mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
188 lines
4.4 KiB
Python
188 lines
4.4 KiB
Python
# Read that for examples/rules: https://github.com/pymedusa/Medusa/blob/master/medusa/name_parser/rules/rules.py
|
|
|
|
from logging import getLogger
|
|
from typing import Any, List, Optional, cast
|
|
from rebulk import Rule, RemoveMatch, AppendMatch, POST_PROCESS
|
|
from rebulk.match import Matches, Match
|
|
from copy import copy
|
|
|
|
logger = getLogger(__name__)
|
|
|
|
|
|
class UnlistTitles(Rule):
|
|
"""Join titles to a single string instead of a list
|
|
|
|
Example: '/media/series/Demon Slayer - Kimetsu no Yaiba/Season 4/Demon Slayer - Kimetsu no Yaiba - S04E10 - Love Hashira Mitsuri Kanroji WEBDL-1080p.mkv'
|
|
Default:
|
|
```json
|
|
{
|
|
"title": [
|
|
"Demon Slayer",
|
|
"Kimetsu no Yaiba"
|
|
],
|
|
"season": 4,
|
|
"episode_title": "Demon Slayer",
|
|
"alternative_title": "Kimetsu no Yaiba",
|
|
"episode": 10,
|
|
"source": "Web",
|
|
"screen_size": "1080p",
|
|
"container": "mkv",
|
|
"mimetype": "video/x-matroska",
|
|
"type": "episode"
|
|
}
|
|
```
|
|
Expected:
|
|
```json
|
|
{
|
|
"title": "Demon Slayer - Kimetsu no Yaiba",
|
|
"season": 4,
|
|
"episode_title": "Demon Slayer",
|
|
"alternative_title": "Kimetsu no Yaiba",
|
|
"episode": 10,
|
|
"source": "Web",
|
|
"screen_size": "1080p",
|
|
"container": "mkv",
|
|
"mimetype": "video/x-matroska",
|
|
"type": "episode"
|
|
}
|
|
```
|
|
"""
|
|
|
|
priority = POST_PROCESS
|
|
consequence = [RemoveMatch, AppendMatch]
|
|
|
|
def when(self, matches: Matches, context) -> Any:
|
|
fileparts: List[Match] = matches.markers.named("path") # type: ignore
|
|
|
|
for part in fileparts:
|
|
titles: List[Match] = matches.range(
|
|
part.start, part.end, lambda x: x.name == "title"
|
|
) # type: ignore
|
|
|
|
if not titles or len(titles) <= 1:
|
|
continue
|
|
|
|
title = copy(titles[0])
|
|
for nmatch in titles[1:]:
|
|
# Check if titles are next to each other, if they are not ignore it.
|
|
next: List[Match] = matches.next(title) # type: ignore
|
|
if not next or next[0] != nmatch:
|
|
logger.warn(f"Ignoring potential part of title: {nmatch.value}")
|
|
continue
|
|
title.end = nmatch.end
|
|
|
|
return [titles, [title]]
|
|
|
|
|
|
class MultipleSeasonRule(Rule):
|
|
"""Understand `abcd Season 2 - 5.mkv` as S2E5
|
|
|
|
Example: '[Erai-raws] Spy x Family Season 2 - 08 [1080p][Multiple Subtitle][00C44E2F].mkv'
|
|
Default:
|
|
```json
|
|
{
|
|
"title": "Spy x Family",
|
|
"season": [
|
|
2,
|
|
3,
|
|
4,
|
|
5,
|
|
6,
|
|
7,
|
|
8
|
|
],
|
|
}
|
|
```
|
|
Expected:
|
|
```json
|
|
{
|
|
"title": "Spy x Family",
|
|
"season": 2,
|
|
"episode": 8,
|
|
}
|
|
```
|
|
"""
|
|
|
|
priority = POST_PROCESS
|
|
consequence = [RemoveMatch, AppendMatch]
|
|
|
|
def when(self, matches: Matches, context) -> Any:
|
|
seasons: List[Match] = matches.named("season") # type: ignore
|
|
|
|
if not seasons:
|
|
return
|
|
|
|
# Only apply this rule if all seasons are due to the same match
|
|
initiator: Optional[Match] = seasons[0].initiator
|
|
if not initiator or any(
|
|
True for match in seasons if match.initiator != initiator
|
|
):
|
|
return
|
|
|
|
value: str = initiator.value # type: ignore
|
|
if "-" not in value:
|
|
return
|
|
|
|
new_season, *new_episodes = (x.strip() for x in value.split("-"))
|
|
to_remove = [x for x in seasons if cast(Match, x.parent).value != new_season]
|
|
to_add = []
|
|
|
|
try:
|
|
episodes = [int(x) for x in new_episodes]
|
|
parents: List[Match] = [match.parent for match in to_remove] # type: ignore
|
|
for episode in episodes:
|
|
smatch = next(
|
|
x
|
|
for x in parents
|
|
if int(str(x.value).replace("-", "").strip()) == episode
|
|
)
|
|
match = copy(smatch)
|
|
match.name = "episode"
|
|
match.value = episode
|
|
to_add.append(match)
|
|
except (ValueError, StopIteration):
|
|
return
|
|
|
|
return [to_remove, to_add]
|
|
|
|
|
|
class SeasonYearDedup(Rule):
|
|
"""Remove "season" when it's the same as "year"
|
|
|
|
Example: "One Piece (1999) 152.mkv"
|
|
Default:
|
|
```json
|
|
{
|
|
"title": "One Piece",
|
|
"year": 1999,
|
|
"season": 1999,
|
|
"episode": 152,
|
|
"container": "mkv",
|
|
"mimetype": "video/x-matroska",
|
|
"type": "episode"
|
|
}
|
|
```
|
|
Expected:
|
|
```json
|
|
{
|
|
"title": "One Piece",
|
|
"year": 1999,
|
|
"episode": 152,
|
|
"container": "mkv",
|
|
"mimetype": "video/x-matroska",
|
|
"type": "episode"
|
|
}
|
|
```
|
|
"""
|
|
|
|
# This rules does the opposite of the YearSeason rule of guessit (with POST_PROCESS priority)
|
|
# To overide it, we need the -1. (rule: https://github.com/guessit-io/guessit/blob/develop/guessit/rules/processors.py#L195)
|
|
priority = POST_PROCESS - 1
|
|
consequence = RemoveMatch
|
|
|
|
def when(self, matches: Matches, context) -> Any:
|
|
season: List[Match] = matches.named("season") # type: ignore
|
|
year: List[Match] = matches.named("year") # type: ignore
|
|
if len(season) == 1 and len(year) == 1 and season[0].value == year[0].value:
|
|
return season
|