From 20bdfc0dd7bd8f54f315bf4bd611b7bfe2183d79 Mon Sep 17 00:00:00 2001 From: Javanaut Date: Sun, 12 Apr 2026 10:06:01 +0200 Subject: [PATCH] Fix pri lang for rename mode --- requirements/project.md | 2 +- src/ffx/cli.py | 4 +- src/ffx/iso_language.py | 275 ++++++++++++++++++++--------- tests/unit/test_cli_rename_only.py | 125 +++++++++++++ tests/unit/test_iso_language.py | 41 +++++ 5 files changed, 361 insertions(+), 86 deletions(-) create mode 100644 tests/unit/test_cli_rename_only.py create mode 100644 tests/unit/test_iso_language.py diff --git a/requirements/project.md b/requirements/project.md index 8182705..dfcac9c 100644 --- a/requirements/project.md +++ b/requirements/project.md @@ -60,7 +60,7 @@ - optional crop detection and crop application, - optional deinterlacing and denoising, - optional subtitle import from external files, - - rename-only copy mode. + - rename-only move mode. - The system shall support optional TMDB lookups to resolve show names, years, and episode titles when a show ID, season, and episode are available. - The system shall generate output filenames from show metadata, season and episode indices, and episode names using the configured filename template. - The system shall allow CLI overrides for stream languages, stream titles, default and forced tracks, stream order, TMDB show and episode data, output directory, label prefix, and processing resource limits. diff --git a/src/ffx/cli.py b/src/ffx/cli.py index 5507bef..6703198 100755 --- a/src/ffx/cli.py +++ b/src/ffx/cli.py @@ -764,7 +764,7 @@ def checkUniqueDispositions(context, mediaDescriptor: MediaDescriptor): help=CPU_OPTION_HELP, ) -@click.option('--rename-only', is_flag=True, default=False, help='Only renaming, no recoding') +@click.option('--rename-only', is_flag=True, default=False, help='Only renaming and moving, no recoding') def convert(ctx, paths, @@ -1247,7 +1247,7 @@ def convert(ctx, if rename_only: - shutil.copyfile(sourcePath, targetPath) + shutil.move(sourcePath, targetPath) else: fc.runJob(sourcePath, targetPath, diff --git a/src/ffx/iso_language.py b/src/ffx/iso_language.py index cc01000..4dab399 100644 --- a/src/ffx/iso_language.py +++ b/src/ffx/iso_language.py @@ -1,85 +1,196 @@ from enum import Enum import difflib + class IsoLanguage(Enum): - AFRIKAANS = {"name": "Afrikaans", "iso639_1": "af", "iso639_2": ["afr"]} - ALBANIAN = {"name": "Albanian", "iso639_1": "sq", "iso639_2": ["alb"]} - ARABIC = {"name": "Arabic", "iso639_1": "ar", "iso639_2": ["ara"]} - ARMENIAN = {"name": "Armenian", "iso639_1": "hy", "iso639_2": ["arm"]} - AZERBAIJANI = {"name": "Azerbaijani", "iso639_1": "az", "iso639_2": ["aze"]} - BASQUE = {"name": "Basque", "iso639_1": "eu", "iso639_2": ["baq"]} - BELARUSIAN = {"name": "Belarusian", "iso639_1": "be", "iso639_2": ["bel"]} - BOKMAL = {"name": "Bokmål", "iso639_1": "nb", "iso639_2": ["nob"]} # Norwegian Bokmål - BULGARIAN = {"name": "Bulgarian", "iso639_1": "bg", "iso639_2": ["bul"]} - CATALAN = {"name": "Catalan", "iso639_1": "ca", "iso639_2": ["cat"]} - CHINESE = {"name": "Chinese", "iso639_1": "zh", "iso639_2": ["zho", "chi"]} - CROATIAN = {"name": "Croatian", "iso639_1": "hr", "iso639_2": ["hrv"]} - CZECH = {"name": "Czech", "iso639_1": "cs", "iso639_2": ["cze"]} - DANISH = {"name": "Danish", "iso639_1": "da", "iso639_2": ["dan"]} - DUTCH = {"name": "Dutch", "iso639_1": "nl", "iso639_2": ["nld", "dut"]} - ENGLISH = {"name": "English", "iso639_1": "en", "iso639_2": ["eng"]} - ESTONIAN = {"name": "Estonian", "iso639_1": "et", "iso639_2": ["est"]} - FILIPINO = {"name": "Filipino", "iso639_1": "tl", "iso639_2": ["fil"]} # Tagalog - FINNISH = {"name": "Finnish", "iso639_1": "fi", "iso639_2": ["fin"]} - FRENCH = {"name": "French", "iso639_1": "fr", "iso639_2": ["fra", "fre"]} - GALICIAN = {"name": "Galician", "iso639_1": "gl", "iso639_2": ["glg"]} - GEORGIAN = {"name": "Georgian", "iso639_1": "ka", "iso639_2": ["geo"]} - GERMAN = {"name": "German", "iso639_1": "de", "iso639_2": ["deu", "ger"]} - GREEK = {"name": "Greek", "iso639_1": "el", "iso639_2": ["gre"]} - HEBREW = {"name": "Hebrew", "iso639_1": "he", "iso639_2": ["heb"]} - HINDI = {"name": "Hindi", "iso639_1": "hi", "iso639_2": ["hin"]} - HUNGARIAN = {"name": "Hungarian", "iso639_1": "hu", "iso639_2": ["hun"]} - ICELANDIC = {"name": "Icelandic", "iso639_1": "is", "iso639_2": ["ice"]} - INDONESIAN = {"name": "Indonesian", "iso639_1": "id", "iso639_2": ["ind"]} - IRISH = {"name": "Irish", "iso639_1": "ga", "iso639_2": ["gle"]} - ITALIAN = {"name": "Italian", "iso639_1": "it", "iso639_2": ["ita"]} - JAPANESE = {"name": "Japanese", "iso639_1": "ja", "iso639_2": ["jpn"]} - KANNADA = {"name": "Kannada", "iso639_1": "kn", "iso639_2": ["kan"]} - KAZAKH = {"name": "Kazakh", "iso639_1": "kk", "iso639_2": ["kaz"]} - KOREAN = {"name": "Korean", "iso639_1": "ko", "iso639_2": ["kor"]} - LATIN = {"name": "Latin", "iso639_1": "la", "iso639_2": ["lat"]} - LATVIAN = {"name": "Latvian", "iso639_1": "lv", "iso639_2": ["lav"]} - LITHUANIAN = {"name": "Lithuanian", "iso639_1": "lt", "iso639_2": ["lit"]} - MACEDONIAN = {"name": "Macedonian", "iso639_1": "mk", "iso639_2": ["mac"]} - MALAY = {"name": "Malay", "iso639_1": "ms", "iso639_2": ["may"]} - MALAYALAM = {"name": "Malayalam", "iso639_1": "ml", "iso639_2": ["mal"]} - MALTESE = {"name": "Maltese", "iso639_1": "mt", "iso639_2": ["mlt"]} - NORWEGIAN = {"name": "Norwegian", "iso639_1": "no", "iso639_2": ["nor"]} - PERSIAN = {"name": "Persian", "iso639_1": "fa", "iso639_2": ["per"]} - POLISH = {"name": "Polish", "iso639_1": "pl", "iso639_2": ["pol"]} - PORTUGUESE = {"name": "Portuguese", "iso639_1": "pt", "iso639_2": ["por"]} - ROMANIAN = {"name": "Romanian", "iso639_1": "ro", "iso639_2": ["rum"]} - RUSSIAN = {"name": "Russian", "iso639_1": "ru", "iso639_2": ["rus"]} - NORTHERN_SAMI = {"name": "Northern Sami", "iso639_1": "se", "iso639_2": ["sme"]} - SAMOAN = {"name": "Samoan", "iso639_1": "sm", "iso639_2": ["smo"]} - SANGO = {"name": "Sango", "iso639_1": "sg", "iso639_2": ["sag"]} - SANSKRIT = {"name": "Sanskrit", "iso639_1": "sa", "iso639_2": ["san"]} - SARDINIAN = {"name": "Sardinian", "iso639_1": "sc", "iso639_2": ["srd"]} - SERBIAN = {"name": "Serbian", "iso639_1": "sr", "iso639_2": ["srp"]} - SHONA = {"name": "Shona", "iso639_1": "sn", "iso639_2": ["sna"]} - SINDHI = {"name": "Sindhi", "iso639_1": "sd", "iso639_2": ["snd"]} - SINHALA = {"name": "Sinhala", "iso639_1": "si", "iso639_2": ["sin"]} - SLOVAK = {"name": "Slovak", "iso639_1": "sk", "iso639_2": ["slk"]} - SLOVENIAN = {"name": "Slovenian", "iso639_1": "sl", "iso639_2": ["slv"]} - SOMALI = {"name": "Somali", "iso639_1": "so", "iso639_2": ["som"]} - SOUTHERN_SOTHO = {"name": "Southern Sotho", "iso639_1": "st", "iso639_2": ["sot"]} - SPANISH = {"name": "Spanish", "iso639_1": "es", "iso639_2": ["spa"]} - SUNDANESE = {"name": "Sundanese", "iso639_1": "su", "iso639_2": ["sun"]} - SWAHILI = {"name": "Swahili", "iso639_1": "sw", "iso639_2": ["swa"]} - SWATI = {"name": "Swati", "iso639_1": "ss", "iso639_2": ["ssw"]} - SWEDISH = {"name": "Swedish", "iso639_1": "sv", "iso639_2": ["swe"]} - TAGALOG = {"name": "Tagalog", "iso639_1": "tl", "iso639_2": ["tgl"]} - TAMIL = {"name": "Tamil", "iso639_1": "ta", "iso639_2": ["tam"]} - TELUGU = {"name": "Telugu", "iso639_1": "te", "iso639_2": ["tel"]} - THAI = {"name": "Thai", "iso639_1": "th", "iso639_2": ["tha"]} - TURKISH = {"name": "Turkish", "iso639_1": "tr", "iso639_2": ["tur"]} - UKRAINIAN = {"name": "Ukrainian", "iso639_1": "uk", "iso639_2": ["ukr"]} - URDU = {"name": "Urdu", "iso639_1": "ur", "iso639_2": ["urd"]} - VIETNAMESE = {"name": "Vietnamese", "iso639_1": "vi", "iso639_2":[ "vie"]} - WELSH = {"name": "Welsh", "iso639_1": "cy", "iso639_2": ["wel"]} + ABKHAZIAN = {"name": "Abkhazian", "iso639_1": "ab", "iso639_2": ["abk"]} + AFAR = {"name": "Afar", "iso639_1": "aa", "iso639_2": ["aar"]} + AFRIKAANS = {"name": "Afrikaans", "iso639_1": "af", "iso639_2": ["afr"]} + AKAN = {"name": "Akan", "iso639_1": "ak", "iso639_2": ["aka"]} + ALBANIAN = {"name": "Albanian", "iso639_1": "sq", "iso639_2": ["sqi", "alb"]} + AMHARIC = {"name": "Amharic", "iso639_1": "am", "iso639_2": ["amh"]} + ARABIC = {"name": "Arabic", "iso639_1": "ar", "iso639_2": ["ara"]} + ARAGONESE = {"name": "Aragonese", "iso639_1": "an", "iso639_2": ["arg"]} + ARMENIAN = {"name": "Armenian", "iso639_1": "hy", "iso639_2": ["hye", "arm"]} + ASSAMESE = {"name": "Assamese", "iso639_1": "as", "iso639_2": ["asm"]} + AVARIC = {"name": "Avaric", "iso639_1": "av", "iso639_2": ["ava"]} + AVESTAN = {"name": "Avestan", "iso639_1": "ae", "iso639_2": ["ave"]} + AYMARA = {"name": "Aymara", "iso639_1": "ay", "iso639_2": ["aym"]} + AZERBAIJANI = {"name": "Azerbaijani", "iso639_1": "az", "iso639_2": ["aze"]} + BAMBARA = {"name": "Bambara", "iso639_1": "bm", "iso639_2": ["bam"]} + BASHKIR = {"name": "Bashkir", "iso639_1": "ba", "iso639_2": ["bak"]} + BASQUE = {"name": "Basque", "iso639_1": "eu", "iso639_2": ["eus", "baq"]} + BELARUSIAN = {"name": "Belarusian", "iso639_1": "be", "iso639_2": ["bel"]} + BENGALI = {"name": "Bengali", "iso639_1": "bn", "iso639_2": ["ben"]} + BISLAMA = {"name": "Bislama", "iso639_1": "bi", "iso639_2": ["bis"]} + BOKMAL = {"name": "Bokmål", "iso639_1": "nb", "iso639_2": ["nob"]} + BOSNIAN = {"name": "Bosnian", "iso639_1": "bs", "iso639_2": ["bos"]} + BRETON = {"name": "Breton", "iso639_1": "br", "iso639_2": ["bre"]} + BULGARIAN = {"name": "Bulgarian", "iso639_1": "bg", "iso639_2": ["bul"]} + BURMESE = {"name": "Burmese", "iso639_1": "my", "iso639_2": ["mya", "bur"]} + CATALAN = {"name": "Catalan", "iso639_1": "ca", "iso639_2": ["cat"]} + CHAMORRO = {"name": "Chamorro", "iso639_1": "ch", "iso639_2": ["cha"]} + CHECHEN = {"name": "Chechen", "iso639_1": "ce", "iso639_2": ["che"]} + CHICHEWA = {"name": "Chichewa", "iso639_1": "ny", "iso639_2": ["nya"]} + CHINESE = {"name": "Chinese", "iso639_1": "zh", "iso639_2": ["zho", "chi"]} + CHURCH_SLAVIC = {"name": "Church Slavic", "iso639_1": "cu", "iso639_2": ["chu"]} + CHUVASH = {"name": "Chuvash", "iso639_1": "cv", "iso639_2": ["chv"]} + CORNISH = {"name": "Cornish", "iso639_1": "kw", "iso639_2": ["cor"]} + CORSICAN = {"name": "Corsican", "iso639_1": "co", "iso639_2": ["cos"]} + CREE = {"name": "Cree", "iso639_1": "cr", "iso639_2": ["cre"]} + CROATIAN = {"name": "Croatian", "iso639_1": "hr", "iso639_2": ["hrv"]} + CZECH = {"name": "Czech", "iso639_1": "cs", "iso639_2": ["ces", "cze"]} + DANISH = {"name": "Danish", "iso639_1": "da", "iso639_2": ["dan"]} + DIVEHI = {"name": "Divehi", "iso639_1": "dv", "iso639_2": ["div"]} + DUTCH = {"name": "Dutch", "iso639_1": "nl", "iso639_2": ["nld", "dut"]} + DZONGKHA = {"name": "Dzongkha", "iso639_1": "dz", "iso639_2": ["dzo"]} + ENGLISH = {"name": "English", "iso639_1": "en", "iso639_2": ["eng"]} + ESPERANTO = {"name": "Esperanto", "iso639_1": "eo", "iso639_2": ["epo"]} + ESTONIAN = {"name": "Estonian", "iso639_1": "et", "iso639_2": ["est"]} + EWE = {"name": "Ewe", "iso639_1": "ee", "iso639_2": ["ewe"]} + FAROESE = {"name": "Faroese", "iso639_1": "fo", "iso639_2": ["fao"]} + FIJIAN = {"name": "Fijian", "iso639_1": "fj", "iso639_2": ["fij"]} + FINNISH = {"name": "Finnish", "iso639_1": "fi", "iso639_2": ["fin"]} + FRENCH = {"name": "French", "iso639_1": "fr", "iso639_2": ["fra", "fre"]} + FULAH = {"name": "Fulah", "iso639_1": "ff", "iso639_2": ["ful"]} + GALICIAN = {"name": "Galician", "iso639_1": "gl", "iso639_2": ["glg"]} + GANDA = {"name": "Ganda", "iso639_1": "lg", "iso639_2": ["lug"]} + GEORGIAN = {"name": "Georgian", "iso639_1": "ka", "iso639_2": ["kat", "geo"]} + GERMAN = {"name": "German", "iso639_1": "de", "iso639_2": ["deu", "ger"]} + GREEK = {"name": "Greek", "iso639_1": "el", "iso639_2": ["ell", "gre"]} + GUARANI = {"name": "Guarani", "iso639_1": "gn", "iso639_2": ["grn"]} + GUJARATI = {"name": "Gujarati", "iso639_1": "gu", "iso639_2": ["guj"]} + HAITIAN = {"name": "Haitian", "iso639_1": "ht", "iso639_2": ["hat"]} + HAUSA = {"name": "Hausa", "iso639_1": "ha", "iso639_2": ["hau"]} + HEBREW = {"name": "Hebrew", "iso639_1": "he", "iso639_2": ["heb"]} + HERERO = {"name": "Herero", "iso639_1": "hz", "iso639_2": ["her"]} + HINDI = {"name": "Hindi", "iso639_1": "hi", "iso639_2": ["hin"]} + HIRI_MOTU = {"name": "Hiri Motu", "iso639_1": "ho", "iso639_2": ["hmo"]} + HUNGARIAN = {"name": "Hungarian", "iso639_1": "hu", "iso639_2": ["hun"]} + ICELANDIC = {"name": "Icelandic", "iso639_1": "is", "iso639_2": ["isl", "ice"]} + IDO = {"name": "Ido", "iso639_1": "io", "iso639_2": ["ido"]} + IGBO = {"name": "Igbo", "iso639_1": "ig", "iso639_2": ["ibo"]} + INDONESIAN = {"name": "Indonesian", "iso639_1": "id", "iso639_2": ["ind"]} + INTERLINGUA = {"name": "Interlingua", "iso639_1": "ia", "iso639_2": ["ina"]} + INTERLINGUE = {"name": "Interlingue", "iso639_1": "ie", "iso639_2": ["ile"]} + INUKTITUT = {"name": "Inuktitut", "iso639_1": "iu", "iso639_2": ["iku"]} + INUPIAQ = {"name": "Inupiaq", "iso639_1": "ik", "iso639_2": ["ipk"]} + IRISH = {"name": "Irish", "iso639_1": "ga", "iso639_2": ["gle"]} + ITALIAN = {"name": "Italian", "iso639_1": "it", "iso639_2": ["ita"]} + JAPANESE = {"name": "Japanese", "iso639_1": "ja", "iso639_2": ["jpn"]} + JAVANESE = {"name": "Javanese", "iso639_1": "jv", "iso639_2": ["jav"]} + KALAALLISUT = {"name": "Kalaallisut", "iso639_1": "kl", "iso639_2": ["kal"]} + KANNADA = {"name": "Kannada", "iso639_1": "kn", "iso639_2": ["kan"]} + KANURI = {"name": "Kanuri", "iso639_1": "kr", "iso639_2": ["kau"]} + KASHMIRI = {"name": "Kashmiri", "iso639_1": "ks", "iso639_2": ["kas"]} + KAZAKH = {"name": "Kazakh", "iso639_1": "kk", "iso639_2": ["kaz"]} + KHMER = {"name": "Khmer", "iso639_1": "km", "iso639_2": ["khm"]} + KIKUYU = {"name": "Kikuyu", "iso639_1": "ki", "iso639_2": ["kik"]} + KINYARWANDA = {"name": "Kinyarwanda", "iso639_1": "rw", "iso639_2": ["kin"]} + KIRGHIZ = {"name": "Kirghiz", "iso639_1": "ky", "iso639_2": ["kir"]} + KOMI = {"name": "Komi", "iso639_1": "kv", "iso639_2": ["kom"]} + KONGO = {"name": "Kongo", "iso639_1": "kg", "iso639_2": ["kon"]} + KOREAN = {"name": "Korean", "iso639_1": "ko", "iso639_2": ["kor"]} + KUANYAMA = {"name": "Kuanyama", "iso639_1": "kj", "iso639_2": ["kua"]} + KURDISH = {"name": "Kurdish", "iso639_1": "ku", "iso639_2": ["kur"]} + LAO = {"name": "Lao", "iso639_1": "lo", "iso639_2": ["lao"]} + LATIN = {"name": "Latin", "iso639_1": "la", "iso639_2": ["lat"]} + LATVIAN = {"name": "Latvian", "iso639_1": "lv", "iso639_2": ["lav"]} + LIMBURGAN = {"name": "Limburgan", "iso639_1": "li", "iso639_2": ["lim"]} + LINGALA = {"name": "Lingala", "iso639_1": "ln", "iso639_2": ["lin"]} + LITHUANIAN = {"name": "Lithuanian", "iso639_1": "lt", "iso639_2": ["lit"]} + LUBA_KATANGA = {"name": "Luba-Katanga", "iso639_1": "lu", "iso639_2": ["lub"]} + LUXEMBOURGISH = {"name": "Luxembourgish", "iso639_1": "lb", "iso639_2": ["ltz"]} + MACEDONIAN = {"name": "Macedonian", "iso639_1": "mk", "iso639_2": ["mkd", "mac"]} + MALAGASY = {"name": "Malagasy", "iso639_1": "mg", "iso639_2": ["mlg"]} + MALAY = {"name": "Malay", "iso639_1": "ms", "iso639_2": ["msa", "may"]} + MALAYALAM = {"name": "Malayalam", "iso639_1": "ml", "iso639_2": ["mal"]} + MALTESE = {"name": "Maltese", "iso639_1": "mt", "iso639_2": ["mlt"]} + MANX = {"name": "Manx", "iso639_1": "gv", "iso639_2": ["glv"]} + MAORI = {"name": "Maori", "iso639_1": "mi", "iso639_2": ["mri", "mao"]} + MARATHI = {"name": "Marathi", "iso639_1": "mr", "iso639_2": ["mar"]} + MARSHALLESE = {"name": "Marshallese", "iso639_1": "mh", "iso639_2": ["mah"]} + MONGOLIAN = {"name": "Mongolian", "iso639_1": "mn", "iso639_2": ["mon"]} + NAURU = {"name": "Nauru", "iso639_1": "na", "iso639_2": ["nau"]} + NAVAJO = {"name": "Navajo", "iso639_1": "nv", "iso639_2": ["nav"]} + NDONGA = {"name": "Ndonga", "iso639_1": "ng", "iso639_2": ["ndo"]} + NEPALI = {"name": "Nepali", "iso639_1": "ne", "iso639_2": ["nep"]} + NORTH_NDEBELE = {"name": "North Ndebele", "iso639_1": "nd", "iso639_2": ["nde"]} + NORTHERN_SAMI = {"name": "Northern Sami", "iso639_1": "se", "iso639_2": ["sme"]} + NORWEGIAN = {"name": "Norwegian", "iso639_1": "no", "iso639_2": ["nor"]} + NORWEGIAN_NYNORSK = {"name": "Nynorsk", "iso639_1": "nn", "iso639_2": ["nno"]} + OCCITAN = {"name": "Occitan", "iso639_1": "oc", "iso639_2": ["oci"]} + OJIBWA = {"name": "Ojibwa", "iso639_1": "oj", "iso639_2": ["oji"]} + ORIYA = {"name": "Oriya", "iso639_1": "or", "iso639_2": ["ori"]} + OROMO = {"name": "Oromo", "iso639_1": "om", "iso639_2": ["orm"]} + OSSETIAN = {"name": "Ossetian", "iso639_1": "os", "iso639_2": ["oss"]} + PALI = {"name": "Pali", "iso639_1": "pi", "iso639_2": ["pli"]} + PANJABI = {"name": "Panjabi", "iso639_1": "pa", "iso639_2": ["pan"]} + PERSIAN = {"name": "Persian", "iso639_1": "fa", "iso639_2": ["fas", "per"]} + POLISH = {"name": "Polish", "iso639_1": "pl", "iso639_2": ["pol"]} + PORTUGUESE = {"name": "Portuguese", "iso639_1": "pt", "iso639_2": ["por"]} + PUSHTO = {"name": "Pushto", "iso639_1": "ps", "iso639_2": ["pus"]} + QUECHUA = {"name": "Quechua", "iso639_1": "qu", "iso639_2": ["que"]} + ROMANIAN = {"name": "Romanian", "iso639_1": "ro", "iso639_2": ["ron", "rum"]} + ROMANSH = {"name": "Romansh", "iso639_1": "rm", "iso639_2": ["roh"]} + RUNDI = {"name": "Rundi", "iso639_1": "rn", "iso639_2": ["run"]} + RUSSIAN = {"name": "Russian", "iso639_1": "ru", "iso639_2": ["rus"]} + SAMOAN = {"name": "Samoan", "iso639_1": "sm", "iso639_2": ["smo"]} + SANGO = {"name": "Sango", "iso639_1": "sg", "iso639_2": ["sag"]} + SANSKRIT = {"name": "Sanskrit", "iso639_1": "sa", "iso639_2": ["san"]} + SARDINIAN = {"name": "Sardinian", "iso639_1": "sc", "iso639_2": ["srd"]} + SCOTTISH_GAELIC = {"name": "Scottish Gaelic", "iso639_1": "gd", "iso639_2": ["gla"]} + SERBIAN = {"name": "Serbian", "iso639_1": "sr", "iso639_2": ["srp"]} + SHONA = {"name": "Shona", "iso639_1": "sn", "iso639_2": ["sna"]} + SICHUAN_YI = {"name": "Sichuan Yi", "iso639_1": "ii", "iso639_2": ["iii"]} + SINDHI = {"name": "Sindhi", "iso639_1": "sd", "iso639_2": ["snd"]} + SINHALA = {"name": "Sinhala", "iso639_1": "si", "iso639_2": ["sin"]} + SLOVAK = {"name": "Slovak", "iso639_1": "sk", "iso639_2": ["slk", "slo"]} + SLOVENIAN = {"name": "Slovenian", "iso639_1": "sl", "iso639_2": ["slv"]} + SOMALI = {"name": "Somali", "iso639_1": "so", "iso639_2": ["som"]} + SOUTH_NDEBELE = {"name": "South Ndebele", "iso639_1": "nr", "iso639_2": ["nbl"]} + SOUTHERN_SOTHO = {"name": "Southern Sotho", "iso639_1": "st", "iso639_2": ["sot"]} + SPANISH = {"name": "Spanish", "iso639_1": "es", "iso639_2": ["spa"]} + SUNDANESE = {"name": "Sundanese", "iso639_1": "su", "iso639_2": ["sun"]} + SWAHILI = {"name": "Swahili", "iso639_1": "sw", "iso639_2": ["swa"]} + SWATI = {"name": "Swati", "iso639_1": "ss", "iso639_2": ["ssw"]} + SWEDISH = {"name": "Swedish", "iso639_1": "sv", "iso639_2": ["swe"]} + TAGALOG = {"name": "Tagalog", "iso639_1": "tl", "iso639_2": ["tgl"]} + TAHITIAN = {"name": "Tahitian", "iso639_1": "ty", "iso639_2": ["tah"]} + TAJIK = {"name": "Tajik", "iso639_1": "tg", "iso639_2": ["tgk"]} + TAMIL = {"name": "Tamil", "iso639_1": "ta", "iso639_2": ["tam"]} + TATAR = {"name": "Tatar", "iso639_1": "tt", "iso639_2": ["tat"]} + TELUGU = {"name": "Telugu", "iso639_1": "te", "iso639_2": ["tel"]} + THAI = {"name": "Thai", "iso639_1": "th", "iso639_2": ["tha"]} + TIBETAN = {"name": "Tibetan", "iso639_1": "bo", "iso639_2": ["bod", "tib"]} + TIGRINYA = {"name": "Tigrinya", "iso639_1": "ti", "iso639_2": ["tir"]} + TONGA = {"name": "Tonga", "iso639_1": "to", "iso639_2": ["ton"]} + TSONGA = {"name": "Tsonga", "iso639_1": "ts", "iso639_2": ["tso"]} + TSWANA = {"name": "Tswana", "iso639_1": "tn", "iso639_2": ["tsn"]} + TURKISH = {"name": "Turkish", "iso639_1": "tr", "iso639_2": ["tur"]} + TURKMEN = {"name": "Turkmen", "iso639_1": "tk", "iso639_2": ["tuk"]} + TWI = {"name": "Twi", "iso639_1": "tw", "iso639_2": ["twi"]} + UIGHUR = {"name": "Uighur", "iso639_1": "ug", "iso639_2": ["uig"]} + UKRAINIAN = {"name": "Ukrainian", "iso639_1": "uk", "iso639_2": ["ukr"]} + URDU = {"name": "Urdu", "iso639_1": "ur", "iso639_2": ["urd"]} + UZBEK = {"name": "Uzbek", "iso639_1": "uz", "iso639_2": ["uzb"]} + VENDA = {"name": "Venda", "iso639_1": "ve", "iso639_2": ["ven"]} + VIETNAMESE = {"name": "Vietnamese", "iso639_1": "vi", "iso639_2": ["vie"]} + VOLAPUK = {"name": "Volapük", "iso639_1": "vo", "iso639_2": ["vol"]} + WALLOON = {"name": "Walloon", "iso639_1": "wa", "iso639_2": ["wln"]} + WELSH = {"name": "Welsh", "iso639_1": "cy", "iso639_2": ["cym", "wel"]} + WESTERN_FRISIAN = {"name": "Western Frisian", "iso639_1": "fy", "iso639_2": ["fry"]} + WOLOF = {"name": "Wolof", "iso639_1": "wo", "iso639_2": ["wol"]} + XHOSA = {"name": "Xhosa", "iso639_1": "xh", "iso639_2": ["xho"]} + YIDDISH = {"name": "Yiddish", "iso639_1": "yi", "iso639_2": ["yid"]} + YORUBA = {"name": "Yoruba", "iso639_1": "yo", "iso639_2": ["yor"]} + ZHUANG = {"name": "Zhuang", "iso639_1": "za", "iso639_2": ["zha"]} + ZULU = {"name": "Zulu", "iso639_1": "zu", "iso639_2": ["zul"]} - UNDEFINED = {"name": "undefined", "iso639_1": "xx", "iso639_2": ["und"]} + FILIPINO = {"name": "Filipino", "iso639_1": "tl", "iso639_2": ["fil"]} + + UNDEFINED = {"name": "undefined", "iso639_1": "xx", "iso639_2": ["und"]} @staticmethod @@ -88,24 +199,22 @@ class IsoLanguage(Enum): closestMatches = difflib.get_close_matches(label, [l.value["name"] for l in IsoLanguage], n=1) if closestMatches: - foundLangs = [l for l in IsoLanguage if l.value['name'] == closestMatches[0]] + foundLangs = [l for l in IsoLanguage if l.value["name"] == closestMatches[0]] return foundLangs[0] if foundLangs else IsoLanguage.UNDEFINED else: return IsoLanguage.UNDEFINED @staticmethod def findThreeLetter(theeLetter : str): - foundLangs = [l for l in IsoLanguage if str(theeLetter) in l.value['iso639_2']] + foundLangs = [l for l in IsoLanguage if str(theeLetter) in l.value["iso639_2"]] return foundLangs[0] if foundLangs else IsoLanguage.UNDEFINED def label(self): - return str(self.value['name']) + return str(self.value["name"]) def twoLetter(self): - return str(self.value['iso639_1']) + return str(self.value["iso639_1"]) def threeLetter(self): - return str(self.value['iso639_2'][0]) - - + return str(self.value["iso639_2"][0]) diff --git a/tests/unit/test_cli_rename_only.py b/tests/unit/test_cli_rename_only.py new file mode 100644 index 0000000..377c246 --- /dev/null +++ b/tests/unit/test_cli_rename_only.py @@ -0,0 +1,125 @@ +from __future__ import annotations + +import os +from pathlib import Path +import sys +import tempfile +import unittest +from unittest.mock import patch + +from click.testing import CliRunner + + +SRC_ROOT = Path(__file__).resolve().parents[2] / "src" + +if str(SRC_ROOT) not in sys.path: + sys.path.insert(0, str(SRC_ROOT)) + + +from ffx import cli # noqa: E402 + + +class _FakeMediaDescriptor: + def getVideoTracks(self): + return [] + + def getAudioTracks(self): + return [] + + def getSubtitleTracks(self): + return [] + + def getAttachmentTracks(self): + return [] + + +class _FakeFileProperties: + def __init__(self, context, source_path): + self.source_path = source_path + + def getShowId(self): + return -1 + + def getSeason(self): + return -1 + + def getEpisode(self): + return -1 + + def getMediaDescriptor(self): + return _FakeMediaDescriptor() + + def getPattern(self): + return None + + +class _FakeShiftedSeasonController: + def __init__(self, context): + self.context = context + + def shiftSeason(self, show_id, season, episode): + return season, episode + + +class _FakeFfxController: + def __init__(self, *args, **kwargs): + pass + + def runJob(self, *args, **kwargs): + raise AssertionError("runJob should not be called for --rename-only") + + +class RenameOnlyCliTests(unittest.TestCase): + def setUp(self): + self.tempdir = tempfile.TemporaryDirectory() + self.home_dir = Path(self.tempdir.name) / "home" + self.home_dir.mkdir() + self.database_path = Path(self.tempdir.name) / "test.db" + self.source_dir = Path(self.tempdir.name) / "source" + self.source_dir.mkdir() + self.output_dir = Path(self.tempdir.name) / "output" + self.output_dir.mkdir() + self.source_path = self.source_dir / "episode.mkv" + self.source_bytes = b"rename-only-source" + self.source_path.write_bytes(self.source_bytes) + + def tearDown(self): + self.tempdir.cleanup() + + def test_rename_only_moves_source_file_into_output_directory(self): + runner = CliRunner() + + with ( + patch("ffx.file_properties.FileProperties", _FakeFileProperties), + patch("ffx.ffx_controller.FfxController", _FakeFfxController), + patch( + "ffx.shifted_season_controller.ShiftedSeasonController", + _FakeShiftedSeasonController, + ), + ): + result = runner.invoke( + cli.ffx, + [ + "--database-file", + str(self.database_path), + "convert", + "--no-tmdb", + "--no-pattern", + "--rename-only", + "--output-directory", + str(self.output_dir), + str(self.source_path), + ], + env={**os.environ, "HOME": str(self.home_dir)}, + ) + + self.assertEqual(0, result.exit_code, result.output) + + target_path = self.output_dir / "out_episode.mkv" + self.assertFalse(self.source_path.exists()) + self.assertTrue(target_path.exists()) + self.assertEqual(self.source_bytes, target_path.read_bytes()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/unit/test_iso_language.py b/tests/unit/test_iso_language.py new file mode 100644 index 0000000..a5aee99 --- /dev/null +++ b/tests/unit/test_iso_language.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from pathlib import Path +import sys +import unittest + + +SRC_ROOT = Path(__file__).resolve().parents[2] / "src" + +if str(SRC_ROOT) not in sys.path: + sys.path.insert(0, str(SRC_ROOT)) + + +from ffx.iso_language import IsoLanguage # noqa: E402 + + +class IsoLanguageTests(unittest.TestCase): + def test_language_constant_set_covers_iso_639_1_plus_filipino_alias(self): + languages = [language for language in IsoLanguage if language is not IsoLanguage.UNDEFINED] + + self.assertEqual(184, len(languages)) + self.assertEqual(183, len({language.twoLetter() for language in languages})) + + def test_primary_three_letter_code_is_returned_first(self): + self.assertEqual("sqi", IsoLanguage.ALBANIAN.threeLetter()) + self.assertEqual("deu", IsoLanguage.GERMAN.threeLetter()) + self.assertEqual("cym", IsoLanguage.WELSH.threeLetter()) + + def test_secondary_three_letter_codes_still_resolve_to_the_same_language(self): + self.assertIs(IsoLanguage.ALBANIAN, IsoLanguage.findThreeLetter("alb")) + self.assertIs(IsoLanguage.GERMAN, IsoLanguage.findThreeLetter("ger")) + self.assertIs(IsoLanguage.WELSH, IsoLanguage.findThreeLetter("wel")) + + def test_newly_added_languages_and_media_aliases_resolve(self): + self.assertIs(IsoLanguage.ASSAMESE, IsoLanguage.find("Assamese")) + self.assertIs(IsoLanguage.YORUBA, IsoLanguage.findThreeLetter("yor")) + self.assertIs(IsoLanguage.FILIPINO, IsoLanguage.findThreeLetter("fil")) + + +if __name__ == "__main__": + unittest.main()