Source code for flexget.components.parsing.parsers.parser_common

from __future__ import annotations

import re
from string import capwords
from typing import TYPE_CHECKING

from loguru import logger

from flexget.utils.qualities import Quality

if TYPE_CHECKING:
    import datetime

logger = logger.bind(name='parser')

SERIES_ID_TYPES = ['ep', 'date', 'sequence', 'id']


[docs] def clean_value(name: str) -> str: for char in '[]()_,.': name = name.replace(char, ' ') # if there are no spaces if name.find(' ') == -1: name = name.replace('-', ' ') # MovieParser.strip_spaces return ' '.join(name.split())
[docs] def old_assume_quality(guessed_quality: Quality, assumed_quality: Quality) -> Quality: if assumed_quality: if not guessed_quality: return assumed_quality if assumed_quality.resolution: guessed_quality.resolution = assumed_quality.resolution if assumed_quality.source: guessed_quality.source = assumed_quality.source if assumed_quality.codec: guessed_quality.codec = assumed_quality.codec if assumed_quality.audio: guessed_quality.audio = assumed_quality.audio return guessed_quality
[docs] def remove_dirt(name: str) -> str: return re.sub(r'[_.,\[\]\(\): ]+', ' ', name).strip().lower() if name else name
[docs] def normalize_name(name: str) -> str: return capwords(name)
[docs] class MovieParseResult: def __init__( self, data: str | None = None, name: str | None = None, year: int | None = None, quality: Quality = None, proper_count: int = 0, release_group: str | None = None, valid: bool = True, ) -> None: self.name: str = name self.data: str = data self.year: int | None = year self.quality: Quality = quality if quality is not None else Quality() self.proper_count: int = proper_count self.release_group: str | None = release_group self.valid: bool = valid @property def identifier(self) -> str: if self.name: return (f'{self.name} {self.year}').strip().lower() if self.year else self.name.lower() return None @property def proper(self) -> bool: return self.proper_count > 0 @property def fields(self) -> dict: """Return a dict of all parser fields.""" return { 'id': self.identifier, 'movie_parser': self, 'movie_name': self.name, 'movie_year': self.year, 'proper': self.proper, 'proper_count': self.proper_count, 'release_group': self.release_group, } def __str__(self) -> str: valid = 'OK' if self.valid else 'INVALID' return ( f'<MovieParseResult(data={self.data},name={self.name},year={self.year},' f'id={self.identifier},quality={self.quality},proper={self.proper_count},' f'release_group={self.release_group},status={valid})>' )
[docs] class SeriesParseResult: def __init__( self, data: str | None = None, name: str | None = None, identified_by: str | None = None, id_type: str | None = None, id: tuple[int, int] | str | int | datetime.date | None = None, episodes: int = 1, season_pack: bool = False, strict_name: bool = False, quality: Quality = None, proper_count: int = 0, special: bool = False, group: str | None = None, valid: bool = True, ) -> None: self.name: str = name self.data: str = data self.episodes: int = episodes self.season_pack: bool = season_pack self.identified_by: str = identified_by self.id: tuple[int, int] | str | int | datetime.date = id self.id_type: str = id_type self.quality: Quality = quality if quality is not None else Quality() self.proper_count: int = proper_count self.special: bool = special self.group: str | None = group self.valid: bool = valid self.strict_name: bool = strict_name @property def proper(self) -> bool: return self.proper_count > 0 @property def season(self) -> int | None: match self.id_type: case 'ep': return self.id[0] case 'date': return self.id.year case 'sequence': return 0 case _: return None @property def episode(self) -> int | None: if self.id_type == 'ep': return self.id[1] if self.id_type == 'sequence': return self.id return None @property def identifiers(self) -> list[str]: """Return all identifiers this parser represents. (for packs).""" # Currently 'ep' is the only id type that supports packs if not self.valid: raise RuntimeError('Series flagged invalid') if self.id_type == 'ep': return ( [f'S{self.season:02d}'] if self.season_pack else [f'S{self.season:02d}E{self.episode + x:02d}' for x in range(self.episodes)] ) if self.id_type == 'date': return [self.id.strftime('%Y-%m-%d')] if self.id is None: raise RuntimeError('Series is missing identifier') return [self.id] @property def identifier(self) -> str: """Return String identifier for parsed episode. Example: S01E02 (will be the first identifier if this is a pack) """ return self.identifiers[0] @property def pack_identifier(self) -> str: """Return a combined identifier for the whole pack if this has more than one episode.""" # Currently only supports ep mode return ( f'S{self.season:02d}E{self.episode:02d}-E{self.episode + self.episodes - 1:02d}' if self.id_type == 'ep' and self.episodes > 1 else self.identifier ) def __str__(self) -> str: valid = 'OK' if self.valid else 'INVALID' return ( f'<SeriesParseResult(data={self.data},name={self.name},id={self.id!s},season={self.season},' f'season_pack={self.season_pack},episode={self.episode},quality={self.quality},' f'proper={self.proper_count},special={self.special},status={valid})>' )