#193 Add --set/--season overrides for batch episode renaming
--season auto-numbers episodes (E01, E02, ...) across a directory; --set TYPE=VALUE overrides any parsed chunk, e.g. forcing name when release text buries it in noise. Requires splitting SxxExx tokens into separate season/episode chunks (previously one glued value) and rejoining them without a separator in the generated name. Also scopes --season numbering per directory instead of the whole tree, drops leftover unknown text when overriding name on titles with nothing else recognized, and makes extension matching case-insensitive.
This commit is contained in:
@@ -10,7 +10,8 @@ class TestParserParts(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
res,
|
||||
{
|
||||
"episode": ["S04E06"],
|
||||
"season": ["S04"],
|
||||
"episode": ["E06"],
|
||||
"resolution": ["1080p"],
|
||||
"quality": ["WEB-DL"],
|
||||
"episode_name": ["Live", "Bait"],
|
||||
@@ -23,11 +24,13 @@ class TestParserParts(unittest.TestCase):
|
||||
def test_episode_number(self):
|
||||
title = "Vikings.S01E01.720p.BluRay.4xRus.Eng.HDCLUB"
|
||||
res = parse_title(title)
|
||||
self.assertIn("S01E01", res.get("episode", []))
|
||||
self.assertEqual(["S01"], res.get("season"))
|
||||
self.assertEqual(["E01"], res.get("episode"))
|
||||
self.assertEqual(
|
||||
res,
|
||||
{
|
||||
"episode": ["S01E01"],
|
||||
"season": ["S01"],
|
||||
"episode": ["E01"],
|
||||
"resolution": ["720p"],
|
||||
"quality": ["BluRay"],
|
||||
"language": ["4xRus", "Eng"],
|
||||
@@ -59,7 +62,8 @@ class TestParserParts(unittest.TestCase):
|
||||
res,
|
||||
{
|
||||
"name": ["The", "Guild"],
|
||||
"episode": ["s04e06"],
|
||||
"season": ["s04"],
|
||||
"episode": ["e06"],
|
||||
"episode_name": ["Weird", "Respawn", "by", "Swich"],
|
||||
"file_extension": ["mkv"],
|
||||
},
|
||||
@@ -266,7 +270,8 @@ class TestCornerCases(unittest.TestCase):
|
||||
res,
|
||||
{
|
||||
"name": ["The", "IT", "Crowd"],
|
||||
"episode": ["S01E04"],
|
||||
"season": ["S01"],
|
||||
"episode": ["E04"],
|
||||
"episode_name": ["The", "Red", "Door", "HR"],
|
||||
"quality": ["DVDRip"],
|
||||
"edition": ["HQ.Edition"],
|
||||
@@ -284,7 +289,8 @@ class TestCornerCases(unittest.TestCase):
|
||||
{
|
||||
"name": ["The", "Big", "Bang", "Theory"],
|
||||
"year": ["2019"],
|
||||
"episode": ["S12E20"],
|
||||
"season": ["S12"],
|
||||
"episode": ["E20"],
|
||||
"episode_name": ["The", "Decision", "Reverberation"],
|
||||
"unknown": ["NTb", "EniaHD"],
|
||||
"resolution": ["1080p"],
|
||||
@@ -303,7 +309,8 @@ class TestCornerCases(unittest.TestCase):
|
||||
res,
|
||||
{
|
||||
"name": ["The", "Big", "Bang", "Theory"],
|
||||
"episode": ["S04E06"],
|
||||
"season": ["S04"],
|
||||
"episode": ["E06"],
|
||||
"resolution": ["720p"],
|
||||
"quality": ["WEB-DL"],
|
||||
"language": ["eng", "rus"],
|
||||
@@ -318,8 +325,26 @@ class TestCornerCases(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
res,
|
||||
{
|
||||
"episode": ["S27E01"],
|
||||
"season": ["S27"],
|
||||
"episode": ["E01"],
|
||||
"episode_name": ["Every", "Man's", "Dream"],
|
||||
"file_extension": ["mkv"],
|
||||
},
|
||||
)
|
||||
|
||||
def test_season_episode_divided(self):
|
||||
title = "Lethal.Weapon.S01.E18.2016.1080p.WEBRip.Rus.Eng.HDCLUB"
|
||||
res = parse_title(title)
|
||||
self.assertEqual(
|
||||
res,
|
||||
{
|
||||
"season": ["S01"],
|
||||
"episode": ["E18"],
|
||||
"language": ["Rus", "Eng"],
|
||||
"name": ["Lethal", "Weapon"],
|
||||
"quality": ["WEBRip"],
|
||||
"resolution": ["1080p"],
|
||||
"unknown": ["HDCLUB"],
|
||||
"year": ["2016"],
|
||||
},
|
||||
)
|
||||
|
||||
100
tests/test_process_path.py
Normal file
100
tests/test_process_path.py
Normal file
@@ -0,0 +1,100 @@
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from renamer import CliAction, _is_media_file, process_path
|
||||
|
||||
|
||||
class TestIsMediaFile(unittest.TestCase):
|
||||
def test_extension_is_case_insensitive(self):
|
||||
self.assertTrue(_is_media_file("Show.S04E06.MKV"))
|
||||
self.assertTrue(_is_media_file("Show.S04E06.mkv"))
|
||||
self.assertTrue(_is_media_file("Show.S04E06.AVI"))
|
||||
|
||||
def test_unsupported_extension(self):
|
||||
self.assertFalse(_is_media_file("Show.S04E06.txt"))
|
||||
|
||||
|
||||
class TestRenameCaseInsensitiveExtension(unittest.TestCase):
|
||||
def test_uppercase_extension_is_processed(self):
|
||||
with tempfile.TemporaryDirectory() as root:
|
||||
original = "Show S04E06.MKV"
|
||||
open(os.path.join(root, original), "w").close()
|
||||
|
||||
process_path(CliAction.rename, os.path.join(root, original))
|
||||
|
||||
self.assertEqual(os.listdir(root), ["Show.S04E06.MKV"])
|
||||
|
||||
|
||||
class TestSeasonNumbering(unittest.TestCase):
|
||||
def test_scoped_to_directory_not_whole_tree(self):
|
||||
with tempfile.TemporaryDirectory() as root:
|
||||
season_dir = os.path.join(root, "Season 1")
|
||||
extras_dir = os.path.join(root, "Extras")
|
||||
os.mkdir(season_dir)
|
||||
os.mkdir(extras_dir)
|
||||
for name in (
|
||||
"Show.(01.serija.iz.10).mkv",
|
||||
"Show.(02.serija.iz.10).mkv",
|
||||
):
|
||||
open(os.path.join(season_dir, name), "w").close()
|
||||
open(os.path.join(extras_dir, "Blooper.reel.mkv"), "w").close()
|
||||
|
||||
process_path(
|
||||
CliAction.rename, root, overrides={"name": "Show"}, season=1,
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
sorted(os.listdir(season_dir)),
|
||||
["Show.S01E01.mkv", "Show.S01E02.mkv"],
|
||||
)
|
||||
# Extras is a separate directory: it gets its own numbering,
|
||||
# starting fresh at E01, instead of continuing the season's count.
|
||||
self.assertEqual(os.listdir(extras_dir), ["Show.S01E01.mkv"])
|
||||
|
||||
def test_single_file_target_defaults_to_episode_one(self):
|
||||
with tempfile.TemporaryDirectory() as root:
|
||||
original = "Show.(01.serija.iz.10).mkv"
|
||||
open(os.path.join(root, original), "w").close()
|
||||
|
||||
process_path(
|
||||
CliAction.rename, os.path.join(root, original),
|
||||
overrides={"name": "Show"}, season=1,
|
||||
)
|
||||
|
||||
self.assertEqual(os.listdir(root), ["Show.S01E01.mkv"])
|
||||
|
||||
|
||||
class TestSetOverride(unittest.TestCase):
|
||||
def test_name_override_drops_unrecognized_leftovers(self):
|
||||
with tempfile.TemporaryDirectory() as root:
|
||||
# nothing else in the title is recognized (no year/quality/etc),
|
||||
# so it would otherwise land entirely under "unknown"
|
||||
original = "Show.(01.serija.iz.10).mkv"
|
||||
open(os.path.join(root, original), "w").close()
|
||||
|
||||
process_path(
|
||||
CliAction.rename, os.path.join(root, original),
|
||||
overrides={"name": "Show"}, season=1,
|
||||
)
|
||||
|
||||
self.assertEqual(os.listdir(root), ["Show.S01E01.mkv"])
|
||||
|
||||
def test_name_override_preserves_unknown_when_name_was_recognized(self):
|
||||
with tempfile.TemporaryDirectory() as root:
|
||||
original = "The.Walking.Dead.S04E06.1080p.WEB-DL.Rus.Eng.HDCLUB.mkv"
|
||||
open(os.path.join(root, original), "w").close()
|
||||
|
||||
process_path(
|
||||
CliAction.rename, os.path.join(root, original),
|
||||
overrides={"name": "TWD"},
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
os.listdir(root),
|
||||
["TWD.S04E06.1080p.WEB-DL.Rus.Eng.HDCLUB.mkv"],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user