Add back TitleNumberFixup rule

This commit is contained in:
2026-03-20 11:17:36 +01:00
parent d4d9359461
commit c9102fa4b3
6 changed files with 96 additions and 8 deletions
+2 -2
View File
@@ -38,7 +38,7 @@ PUBLIC_URL=http://localhost:8901
# Set `verified` to true if you don't wanna manually verify users.
EXTRA_CLAIMS='{"permissions": ["core.read", "core.play"], "verified": false}'
# This is the permissions of the first user (aka the first user is admin)
FIRST_USER_CLAIMS='{"permissions": ["users.read", "users.write", "users.delete", "apikeys.read", "apikeys.write", "core.read", "core.write", "core.play", "scanner.trigger"], "verified": true}'
FIRST_USER_CLAIMS='{"permissions": ["users.read", "users.write", "users.delete", "apikeys.read", "apikeys.write", "core.read", "core.write", "core.play", "scanner.trigger", "scanner.guess"], "verified": true}'
# Guest (meaning unlogged in users) can be:
# unauthorized (they need to connect before doing anything)
@@ -46,7 +46,7 @@ FIRST_USER_CLAIMS='{"permissions": ["users.read", "users.write", "users.delete",
# able to browse & see what you have but not able to play
GUEST_CLAIMS='{"permissions": ["core.read"], "verified": true}'
# or have browse & play permissions
GUEST_CLAIMS='{"permissions": ["core.read", "core.play"], "verified": true}'
# GUEST_CLAIMS='{"permissions": ["core.read", "core.play"], "verified": true}'
# DO NOT change this.
PROTECTED_CLAIMS="permissions,verified"
+1 -1
View File
@@ -103,7 +103,7 @@ kyoo:
# auth settings
auth:
firstUserClaims: '{"permissions": ["users.read", "users.write", "apikeys.read", "apikeys.write", "users.delete", "core.read", "core.write", "core.play", "scanner.trigger"], "verified": true}'
firstUserClaims: '{"permissions": ["users.read", "users.write", "apikeys.read", "apikeys.write", "users.delete", "core.read", "core.write", "core.play", "scanner.trigger", "scanner.guess"], "verified": true}'
guestClaims: '{"permissions": ["core.read"], "verified": true}'
extraClaims: '{"permissions": ["core.read", "core.play"], "verified": false}'
protectedClaims: "permissions,verified"
@@ -259,6 +259,77 @@ class PreferFilenameOverDirectory(Rule):
return to_remove if to_remove else None
class TitleNumberFixup(Rule):
"""Fix titles having numbers in them
Example: '[Erai-raws] Zom 100 - Zombie ni Naru made ni Shitai 100 no Koto - 01 [1080p][Multiple Subtitle][8AFBB298].mkv'
(or '[SubsPlease] Mob Psycho 100 Season 3 - 12 (1080p) [E5058D7B].mkv')
Default:
```json
{
"release_group": "Erai-raws",
"title": "Zom",
"episode": [
100,
1
],
"episode_title": "Zombie ni Naru made ni Shitai",
}
```
Expected:
```json
{
"release_group": "Erai-raws",
"title": "Zom 100",
"episode": 1,
"episode_title": "Zombie ni Naru made ni Shitai 100 no Koto",
}
```
"""
priority = POST_PROCESS
consequence = [RemoveMatch, AppendMatch]
@override
def when(self, matches: Matches, context) -> Any:
episodes: List[Match] = matches.named("episode") # type: ignore
if len(episodes) < 2 or all(x.value == episodes[0].value for x in episodes):
return
to_remove = []
to_add = []
for episode in episodes:
prevs: List[Match] = matches.previous(episode) # type: ignore
title = prevs[0] if prevs and prevs[0].tagged("title") else None
if not title:
continue
# do not fixup if there was a - or any separator between the title and the episode number
hole: List[Match] = matches.holes(title.end, episode.start) # type: ignore
if hole:
continue
to_remove.extend([title, episode])
new_title = copy(title)
new_title.end = episode.end
nmatch: List[Match] = matches.next(episode) # type: ignore
if nmatch:
end = (
nmatch[0].initiator.start
if isinstance(nmatch[0].initiator, Match)
else nmatch[0].start
)
# If an hole was created to parse the episode at the current pos, merge it back into the title
holes: List[Match] = matches.holes(start=episode.end, end=end) # type: ignore
if holes and holes[0].start == episode.end:
new_title.end = holes[0].end
to_add.append(new_title)
return [to_remove, to_add]
class ExpectedTitles(Rule):
"""Fix both alternate names and seasons that are known titles but parsed differently by guessit
-2
View File
@@ -4,8 +4,6 @@ from itertools import zip_longest
from logging import getLogger
from typing import Callable, Literal, cast
from rebulk.match import Match
from ..models.videos import Guess, Video
from .anilist import get_anilist_data, identify_anilist
from .guess.guess import guessit
+19 -3
View File
@@ -2,11 +2,11 @@ from typing import Annotated, Literal
from fastapi import APIRouter, BackgroundTasks, Depends, Security
from scanner.models.request import RequestRet
from scanner.status import StatusService
from ..fsscan import create_scanner
from ..identifiers.identify import identify
from ..jwt import validate_bearer
from ..models.request import RequestRet
from ..status import StatusService
router = APIRouter()
@@ -42,3 +42,19 @@ async def trigger_scan(
await scanner.scan()
tasks.add_task(run)
@router.get(
"/guess",
status_code=200,
response_description="Identify a path",
)
async def get_guess(
path: str,
_: Annotated[None, Security(validate_bearer, scopes=["scanner.guess"])],
):
"""
Identify a video path and return a serie/movie guess.
"""
return await identify(path)
+3
View File
@@ -12,6 +12,9 @@ pkgs.mkShell {
packages = [
pkgs.devspace
(pkgs.writeShellScriptBin "guess" ''
curl "localhost:8901/scanner/guess" -G --data-urlencode "path=$1" -H 'X-API-KEY: admin' | jq
'')
];
# env vars aren't inherited from the `inputsFrom`