Compare commits

2 Commits

Author SHA1 Message Date
74753d94be Added another group regex 2026-03-20 13:21:07 -04:00
02741c1e77 Fixed imports and refactored some attributes 2026-03-20 13:05:45 -04:00
21 changed files with 68 additions and 72 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/test.xml /test.xml
/uv.lock /uv.lock
/main.py

View File

@@ -1 +1 @@
__version__ = "1.0.0" __version__ = "1.0.1"

View File

@@ -3,8 +3,9 @@ from pathlib import Path
from pkgutil import iter_modules from pkgutil import iter_modules
from typing import Optional, Type from typing import Optional, Type
from parsers.generic.parsers import DataParser from watfag.parsers.generic.parsers import DataParser
from parsers.generic.watfag import WATFAG, SeedStatus from watfag.parsers.generic.watfag import *
from watfag.parsers.generic.watfag import WATFAG
class Release: class Release:
@@ -23,6 +24,18 @@ class Release:
self.seed_status: Optional[SeedStatus] = None self.seed_status: Optional[SeedStatus] = None
self.parser_results: dict[str, bool] = {} # Stores which parsers have been run and their results. self.parser_results: dict[str, bool] = {} # Stores which parsers have been run and their results.
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 __lt__(self, other): def __lt__(self, other):
return self.watfag < other.watfag return self.watfag < other.watfag
@@ -63,6 +76,7 @@ class GenericParser:
class ParserManager: class ParserManager:
"""Manages and runs parsers on releases.""" """Manages and runs parsers on releases."""
def __init__(self): def __init__(self):
self.parsers: list[Type[DataParser]] = [] self.parsers: list[Type[DataParser]] = []
self.collect_parsers() self.collect_parsers()

View File

@@ -1,8 +1,8 @@
import regex as re import regex as re
from parsers.generic import GenericParser from watfag.parsers.generic import GenericParser
from parsers.generic.parsers import DataParser from watfag.parsers.generic.parsers import DataParser
from parsers.generic.watfag import AudioCodec, AudioLayout from watfag.parsers.generic.watfag import AudioCodec, AudioLayout
patterns = [ patterns = [
re.compile( re.compile(

View File

@@ -1,11 +1,12 @@
import regex as re import regex as re
from parsers.generic import GenericParser from watfag.parsers.generic import GenericParser
from parsers.generic.watfag import Group from watfag.parsers.generic.watfag import Group
from parsers.generic.parsers import DataParser from watfag.parsers.generic.parsers import DataParser
patterns = [ patterns = [
re.compile(r"(?:-| - )(?P<group>[a-zA-Z0-9 &]*)\)?$", re.UNICODE), re.compile(r"(?:-| - )(?P<group>[a-zA-Z0-9 &]*)\)?$", re.UNICODE),
re.compile(r"\[(?P<group>[a-zA-Z0-9 &]*)\]?$", re.UNICODE),
re.compile(r"(?: )\[?(?P<group>[a-zA-Z0-9]*?)]?\)?$", re.UNICODE) re.compile(r"(?: )\[?(?P<group>[a-zA-Z0-9]*?)]?\)?$", re.UNICODE)
] ]

View File

@@ -1,8 +1,8 @@
import regex as re import regex as re
from parsers.generic import GenericParser from watfag.parsers.generic import GenericParser
from parsers.generic.parsers import CheckParser from watfag.parsers.generic.parsers import CheckParser
from parsers.generic.watfag import DynamicRange from watfag.parsers.generic.watfag import DynamicRange
checks = { checks = {
re.compile(r"hybrid|do?vi? ?hdr(?:10)?[\+p]?", re.IGNORECASE): DynamicRange.HYBRID, re.compile(r"hybrid|do?vi? ?hdr(?:10)?[\+p]?", re.IGNORECASE): DynamicRange.HYBRID,

View File

@@ -1,8 +1,8 @@
import regex as re import regex as re
from parsers.generic import GenericParser from watfag.parsers.generic import GenericParser
from parsers.generic.parsers import CheckParser from watfag.parsers.generic.parsers import CheckParser
from parsers.generic.watfag import Multi from watfag.parsers.generic.watfag import Multi
checks = { checks = {
re.compile(r"multi", re.IGNORECASE): Multi.MULTI re.compile(r"multi", re.IGNORECASE): Multi.MULTI

View File

@@ -3,7 +3,7 @@ from typing import Optional, TYPE_CHECKING
from regex import Pattern from regex import Pattern
if TYPE_CHECKING: if TYPE_CHECKING:
from parsers.generic import WATFAG, Release from watfag.parsers.generic import WATFAG, Release
class DataParser: class DataParser:

View File

@@ -1,8 +1,8 @@
import regex as re import regex as re
from parsers.generic import GenericParser from watfag.parsers.generic import GenericParser
from parsers.generic.parsers import CheckParser from watfag.parsers.generic.parsers import CheckParser
from parsers.generic.watfag import Repack from watfag.parsers.generic.watfag import Repack
checks = { checks = {
re.compile(r"repack", re.IGNORECASE): Repack.REPACK, re.compile(r"repack", re.IGNORECASE): Repack.REPACK,

View File

@@ -1,8 +1,8 @@
import regex as re import regex as re
from parsers.generic import GenericParser from watfag.parsers.generic import GenericParser
from parsers.generic.parsers import CheckParser from watfag.parsers.generic.parsers import CheckParser
from parsers.generic.watfag import Resolution from watfag.parsers.generic.watfag import Resolution
checks = { checks = {
re.compile(r"2160p", re.IGNORECASE): Resolution.UHD, re.compile(r"2160p", re.IGNORECASE): Resolution.UHD,

View File

@@ -1,8 +1,8 @@
import regex as re import regex as re
from parsers.generic import GenericParser from watfag.parsers.generic import GenericParser
from parsers.generic.parsers import CheckParser from watfag.parsers.generic.parsers import CheckParser
from parsers.generic.watfag import SeedStatus from watfag.parsers.generic.watfag import SeedStatus
class SeederParser(CheckParser, GenericParser): class SeederParser(CheckParser, GenericParser):
def parse(self) -> bool: def parse(self) -> bool:

View File

@@ -1,8 +1,8 @@
import regex as re import regex as re
from parsers.generic import GenericParser from watfag.parsers.generic import GenericParser
from parsers.generic.parsers import CheckParser from watfag.parsers.generic.parsers import CheckParser
from parsers.generic.watfag import Source from watfag.parsers.generic.watfag import Source
checks = { checks = {
re.compile(r"remux", re.IGNORECASE): Source.REMUX, re.compile(r"remux", re.IGNORECASE): Source.REMUX,

View File

@@ -1,8 +1,8 @@
import regex as re import regex as re
from parsers.generic import GenericParser from watfag.parsers.generic import GenericParser
from parsers.generic.parsers import CheckParser from watfag.parsers.generic.parsers import CheckParser
from parsers.generic.watfag import StreamingService from watfag.parsers.generic.watfag import StreamingService
checks = { checks = {
re.compile(r"ATVP"): StreamingService.ATVP, re.compile(r"ATVP"): StreamingService.ATVP,

View File

@@ -1,8 +1,8 @@
import regex as re import regex as re
from parsers.generic import GenericParser from watfag.parsers.generic import GenericParser
from parsers.generic.parsers import CheckParser from watfag.parsers.generic.parsers import CheckParser
from parsers.generic.watfag import VideoCodec from watfag.parsers.generic.watfag import VideoCodec
checks = { checks = {
re.compile(r"avc|[hx][\. -]?264", re.IGNORECASE): VideoCodec.AVC, re.compile(r"avc|[hx][\. -]?264", re.IGNORECASE): VideoCodec.AVC,

View File

@@ -189,6 +189,7 @@ class Group(WATFAG):
""" """
FLUX = "FLUX", 10 # Very good WEB-DL releases and fast FLUX = "FLUX", 10 # Very good WEB-DL releases and fast
HONE = "HONE", 10 # High quality re-encodes HONE = "HONE", 10 # High quality re-encodes
TAOE = "TAoE", 10 # High quality re-encodes
PHOCIS = "PHOCiS", 8 # Same as FLUX PHOCIS = "PHOCiS", 8 # Same as FLUX
LEGION = "LEGi0N", 8 # Same as FLUX LEGION = "LEGi0N", 8 # Same as FLUX
AOC = "AOC", 1 # Often low quality CAM releases. While fast, not worth it for most movies. AOC = "AOC", 1 # Often low quality CAM releases. While fast, not worth it for most movies.

View File

@@ -3,32 +3,22 @@ from pathlib import Path
from pkgutil import iter_modules from pkgutil import iter_modules
from typing import Optional from typing import Optional
from parsers.generic import Release, ParserManager from watfag.parsers.generic import Release, ParserManager
from parsers.generic.watfag import *
class MovieRelease(Release): class MovieRelease(Release):
"""Holds info representing a release of a movie.""" """Holds info representing a release of a movie."""
def __init__(self, unparsed_text, dl_link, **kwargs): def __init__(self, unparsed_text, dl_link, **kwargs):
super().__init__(unparsed_text, dl_link, **kwargs) super().__init__(unparsed_text, dl_link, **kwargs)
self.title: str = "" self.title: str = ""
self.year: int = 0 self.year: int = 0
self.edition: Optional[str] = None self.edition: 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): def __str__(self):
parts = [f"{self.title} ({self.year})" + (f" [{self.edition}]" if self.edition else "")] parts = [f"{self.title} ({self.year})" + (f" [{self.edition}]" if self.edition else "")]
for attr in ['quality', 'video_codec', 'audio_codec', 'audio_layout', 'dynamic_range', 'repack', 'multi', 'source']: for attr in ['quality', 'video_codec', 'audio_codec', 'audio_layout', 'dynamic_range', 'repack', 'multi',
'source']:
value = getattr(self, attr) value = getattr(self, attr)
parts.append(f"{attr.capitalize()}: {value if value else 'Unknown'}") parts.append(f"{attr.capitalize()}: {value if value else 'Unknown'}")
if self.streaming: if self.streaming:
@@ -39,12 +29,14 @@ class MovieRelease(Release):
parts.append(f"WATFAG: {self.watfag:.2f}") parts.append(f"WATFAG: {self.watfag:.2f}")
return " | ".join(parts) return " | ".join(parts)
class MovieParser: class MovieParser:
""" """
This class can be inherited by any parser that is specific to movies. This class can be inherited by any parser that is specific to movies.
It allows dynamic importing of parser classes and provides a method to run all parsers on a given movie release. It allows dynamic importing of parser classes and provides a method to run all parsers on a given movie release.
""" """
class MovieParserManager(ParserManager): class MovieParserManager(ParserManager):
"""Parses movie releases.""" """Parses movie releases."""

View File

@@ -1,8 +1,8 @@
import regex as re import regex as re
from parsers.generic.parsers import DataParser from watfag.parsers.generic.parsers import DataParser
from parsers.movie import MovieParser, MovieRelease from watfag.parsers.movie import MovieParser, MovieRelease
from parsers.movie.title_year import edition_regex from watfag.parsers.movie.title_year import edition_regex
class EditionParser(DataParser, MovieParser): class EditionParser(DataParser, MovieParser):

View File

@@ -1,7 +1,7 @@
import regex as re import regex as re
from parsers.generic.parsers import DataParser from watfag.parsers.generic.parsers import DataParser
from parsers.movie import MovieParser, MovieRelease from watfag.parsers.movie import MovieParser, MovieRelease
# Shamelessly stolen from Radarr: https://github.com/Radarr/Radarr/blob/develop/src/NzbDrone.Core/Parser/Parser.cs # Shamelessly stolen from Radarr: https://github.com/Radarr/Radarr/blob/develop/src/NzbDrone.Core/Parser/Parser.cs
edition_regex = r"\(?\b(?P<edition>(((Recut.|Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Extended|Despecialized|(Special|Rouge|Final|Assembly|Imperial|Diamond|Signature|Hunter|Rekall)(?=(.(Cut|Edition|Version)))|\d{2,3}(th)?.Anniversary)(?:.(Cut|Edition|Version))?(.(Extended|Uncensored|Remastered|Unrated|Uncut|Open.?Matte|IMAX|Fan.?Edit))?|((Uncensored|Remastered|Unrated|Uncut|Open?.Matte|IMAX|Fan.?Edit|Restored|((2|3|4)in1))))))\b\)?" edition_regex = r"\(?\b(?P<edition>(((Recut.|Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Extended|Despecialized|(Special|Rouge|Final|Assembly|Imperial|Diamond|Signature|Hunter|Rekall)(?=(.(Cut|Edition|Version)))|\d{2,3}(th)?.Anniversary)(?:.(Cut|Edition|Version))?(.(Extended|Uncensored|Remastered|Unrated|Uncut|Open.?Matte|IMAX|Fan.?Edit))?|((Uncensored|Remastered|Unrated|Uncut|Open?.Matte|IMAX|Fan.?Edit|Restored|((2|3|4)in1))))))\b\)?"

View File

@@ -3,8 +3,7 @@ from pathlib import Path
from pkgutil import iter_modules from pkgutil import iter_modules
from typing import Optional from typing import Optional
from parsers.generic import Release, ParserManager from watfag.parsers.generic import Release, ParserManager
from parsers.generic.watfag import *
class TVBoxSetRelease(Release): class TVBoxSetRelease(Release):
@@ -13,17 +12,6 @@ class TVBoxSetRelease(Release):
super().__init__(unparsed_text, dl_link, **kwargs) super().__init__(unparsed_text, dl_link, **kwargs)
self.show_title: str = "" self.show_title: str = ""
self.seasons: Optional[str] = None 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): def __str__(self):
parts = [f"{self.show_title} (Seasons: {self.seasons})"] parts = [f"{self.show_title} (Seasons: {self.seasons})"]
@@ -46,7 +34,6 @@ class TVBoxSetParser:
class TVBoxSetParserManager(ParserManager): class TVBoxSetParserManager(ParserManager):
"""Parses TV box set releases.""" """Parses TV box set releases."""
def collect_parsers(self): def collect_parsers(self):
"""Dynamically imports all TV box set parsers.""" """Dynamically imports all TV box set parsers."""
super().collect_parsers() super().collect_parsers()

View File

@@ -1,7 +1,7 @@
import regex as re import regex as re
from parsers.generic.parsers import DataParser from watfag.parsers.generic.parsers import DataParser
from parsers.tvboxset import TVBoxSetParser, TVBoxSetRelease from watfag.parsers.tvboxset import TVBoxSetParser, TVBoxSetRelease
patterns = [ patterns = [
re.compile( # Show Name S01-S02 (year) re.compile( # Show Name S01-S02 (year)

View File

@@ -2,9 +2,9 @@ from xml.etree import ElementTree
from httpx import AsyncClient from httpx import AsyncClient
from parsers.generic import Release from watfag.parsers.generic import Release
from parsers.movie import MovieRelease, MovieParserManager from watfag.parsers.movie import MovieRelease, MovieParserManager
from parsers.tvboxset import TVBoxSetRelease, TVBoxSetParserManager from watfag.parsers.tvboxset import TVBoxSetRelease, TVBoxSetParserManager
class Jackett: class Jackett: