Initial commit
This commit is contained in:
57
src/watfag/parsers/tvboxset/__init__.py
Normal file
57
src/watfag/parsers/tvboxset/__init__.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import importlib
|
||||
from pathlib import Path
|
||||
from pkgutil import iter_modules
|
||||
from typing import Optional
|
||||
|
||||
from parsers.generic import Release, ParserManager
|
||||
from parsers.generic.watfag import *
|
||||
|
||||
|
||||
class TVBoxSetRelease(Release):
|
||||
"""Holds info representing a release of a TV box set."""
|
||||
def __init__(self, unparsed_text, dl_link, **kwargs):
|
||||
super().__init__(unparsed_text, dl_link, **kwargs)
|
||||
self.show_title: str = ""
|
||||
self.seasons: Optional[str] = None
|
||||
self.group: Optional[Group] = None
|
||||
self.group_name: Optional[str] = None
|
||||
self.quality: Optional[Resolution] = None
|
||||
self.source: Optional[Source] = None
|
||||
self.streaming: Optional[StreamingService] = None
|
||||
self.video_codec: Optional[VideoCodec] = None
|
||||
self.audio_codec: Optional[AudioCodec] = None
|
||||
self.audio_layout: Optional[AudioLayout] = None
|
||||
self.dynamic_range: Optional[DynamicRange] = None
|
||||
self.repack: Optional[Repack] = None
|
||||
self.multi: Optional[Multi] = None
|
||||
|
||||
def __str__(self):
|
||||
parts = [f"{self.show_title} (Seasons: {self.seasons})"]
|
||||
for attr in ['quality', 'video_codec', 'audio_codec', 'audio_layout', 'dynamic_range', 'repack', 'multi', 'source']:
|
||||
value = getattr(self, attr)
|
||||
parts.append(f"{attr.capitalize()}: {value if value else 'Unknown'}")
|
||||
if self.streaming:
|
||||
parts.append(f"Streaming: {self.streaming}")
|
||||
parts.append(f"Group: {self.group_name if self.group else 'Unknown'}")
|
||||
if not self.fully_consumed():
|
||||
parts.append(f"Unparsed: {self.metadata_text}")
|
||||
parts.append(f"WATFAG: {self.watfag:.2f}")
|
||||
return " | ".join(parts)
|
||||
|
||||
class TVBoxSetParser:
|
||||
"""
|
||||
This class can be inherited by any parser that is specific to TV box sets.
|
||||
It allows dynamic importing of parser classes and provides a method to run all parsers on a given TV box set release.
|
||||
"""
|
||||
|
||||
class TVBoxSetParserManager(ParserManager):
|
||||
"""Parses TV box set releases."""
|
||||
|
||||
def collect_parsers(self):
|
||||
"""Dynamically imports all TV box set parsers."""
|
||||
super().collect_parsers()
|
||||
package_dir = Path(__file__).parent
|
||||
for _, module_name, _ in iter_modules([package_dir]):
|
||||
importlib.import_module(f"{__package__}.{module_name}")
|
||||
|
||||
self.parsers.extend(TVBoxSetParser.__subclasses__())
|
||||
57
src/watfag/parsers/tvboxset/title_seasons.py
Normal file
57
src/watfag/parsers/tvboxset/title_seasons.py
Normal file
@@ -0,0 +1,57 @@
|
||||
import regex as re
|
||||
|
||||
from parsers.generic.parsers import DataParser
|
||||
from parsers.tvboxset import TVBoxSetParser, TVBoxSetRelease
|
||||
|
||||
patterns = [
|
||||
re.compile( # Show Name S01-S02 (year)
|
||||
r"^(?P<title>.+?)[-_. ]S(?:eason)?s?(?P<season_start>[0-9]{1,2}) ?[-_. ] ?(?:S(?:eason)?)?"
|
||||
r"(?P<season_end>[0-9]{1,2})[-_. ]\(?(1(8|9)|20)\d{2}(?!p|i|(1(8|9)|20)\d{2}|\]|\W(1(8|9)|20)\d{2})\)?",
|
||||
re.IGNORECASE | re.UNICODE
|
||||
),
|
||||
re.compile( # Show Name (year) S01-S02
|
||||
r"^(?<title>.+?)[-_. ]\(?(1(8|9)|20)\d{2}(?!p|i|(1(8|9)|20)\d{2}|\]|\W(1(8|9)|20)\d{2})\)?[-_. ]S(?:eason)?s?"
|
||||
r"(?<season_start>[0-9]{1,2}) ?[-_. ] ?(?:S(?:eason)?)?(?<season_end>[0-9]{1,2})[-_. ]",
|
||||
re.IGNORECASE | re.UNICODE
|
||||
),
|
||||
re.compile( # Show Name (year) S01
|
||||
r"^(?<title>.+?)[-_. ]\(?(1(8|9)|20)\d{2}(?!p|i|(1(8|9)|20)\d{2}|\]|\W(1(8|9)|20)\d{2})\)?[-_. ]"
|
||||
r"S(?:eason)?s? ?(?<season_start>[0-9]{1,2}) ?[-_. ]",
|
||||
re.IGNORECASE | re.UNICODE
|
||||
),
|
||||
re.compile( # Show Name S01 (year)
|
||||
r"^(?<title>.+?)[-_. ]S(?:eason)?s? ?(?<season_start>[0-9]{1,2}) ?[-_. ]"
|
||||
r"\(?(1(8|9)|20)\d{2}(?!p|i|(1(8|9)|20)\d{2}|\]|\W(1(8|9)|20)\d{2})\)?[-_. ]",
|
||||
re.IGNORECASE | re.UNICODE
|
||||
),
|
||||
re.compile( # Show Name (Complete) S01-S02
|
||||
r"^(?<title>.+?)[-_. ](?:Complete[-_. ]?(?:Series[-_. ])?)?\(?S(?:eason)?s?(?<season_start>[0-9]{1,2})"
|
||||
r" ?[-_. ] ?(?:S(?:eason)?)?(?<season_end>[0-9]{1,2})\)?[-_. ](?:Complete[-_. ])?",
|
||||
re.IGNORECASE | re.UNICODE
|
||||
),
|
||||
re.compile( # Nuclear option: Show Name S01
|
||||
r"^(?<title>.+?)[-_. ]S(?:eason)?s? ?(?<season_start>[0-9]{1,2}) ?[-_. ]",
|
||||
re.IGNORECASE | re.UNICODE
|
||||
)
|
||||
]
|
||||
|
||||
class TitleSeasonsParser(DataParser, TVBoxSetParser):
|
||||
def __init__(self, release: TVBoxSetRelease):
|
||||
super().__init__(release)
|
||||
self.priority = 0 # First parser to run
|
||||
|
||||
"""Parses the title and seasons from the unparsed text."""
|
||||
def parse(self) -> bool:
|
||||
for pattern in patterns:
|
||||
match = pattern.match(self.release.original_text)
|
||||
if match:
|
||||
self.release.show_title = match.group("title").replace(".", " ").replace("_", " ").strip() if match.group("title") else ""
|
||||
season_start = int(match.group("season_start")) if match.group("season_start") else 0
|
||||
season_end = int(match.group("season_end")) if "season_end" in match.groupdict() and match.group("season_end") else season_start
|
||||
self.release.seasons = f"{season_start}" if season_start == season_end else f"{season_start}-{season_end}"
|
||||
|
||||
self.release.metadata_text = self.release.original_text[:match.span()[0]] + self.release.original_text[match.span()[1]:]
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
Reference in New Issue
Block a user