mirror of
https://github.com/zoriya/Kyoo.git
synced 2026-03-25 19:08:00 -04:00
Add search endpoint on the scanner
This commit is contained in:
parent
dcf7b4e794
commit
49961c341e
@ -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", "scanner.guess"], "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", "scanner.search"], "verified": true}'
|
||||
|
||||
# Guest (meaning unlogged in users) can be:
|
||||
# unauthorized (they need to connect before doing anything)
|
||||
|
||||
@ -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", "scanner.guess"], "verified": true}'
|
||||
firstUserClaims: '{"permissions": ["users.read", "users.write", "apikeys.read", "apikeys.write", "users.delete", "core.read", "core.write", "core.play", "scanner.trigger", "scanner.guess", "scanner.search"], "verified": true}'
|
||||
guestClaims: '{"permissions": ["core.read"], "verified": true}'
|
||||
extraClaims: '{"permissions": ["core.read", "core.play"], "verified": false}'
|
||||
protectedClaims: "permissions,verified"
|
||||
|
||||
@ -18,7 +18,7 @@ from .routers.routes import router
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(_):
|
||||
async def lifespan(app: FastAPI):
|
||||
async with (
|
||||
init_pool() as pool,
|
||||
get_db() as db,
|
||||
@ -26,6 +26,7 @@ async def lifespan(_):
|
||||
TVDB() as tvdb,
|
||||
TheMovieDatabase() as tmdb,
|
||||
):
|
||||
app.state.provider = CompositeProvider(tvdb, tmdb)
|
||||
# there's no way someone else used the same id, right?
|
||||
is_master = await db.fetchval("select pg_try_advisory_lock(198347)")
|
||||
is_http = not is_master and await db.fetchval(
|
||||
@ -39,7 +40,7 @@ async def lifespan(_):
|
||||
processor = RequestProcessor(
|
||||
pool,
|
||||
client,
|
||||
CompositeProvider(tvdb, tmdb),
|
||||
app.state.provider,
|
||||
)
|
||||
scanner = FsScanner(client, RequestCreator(db))
|
||||
tasks = create_task(
|
||||
|
||||
@ -111,7 +111,7 @@ class TheMovieDatabase(Provider):
|
||||
params={
|
||||
"query": title,
|
||||
"year": year,
|
||||
"languages": [str(x) for x in language],
|
||||
"language": next((str(x) for x in language), None),
|
||||
},
|
||||
)
|
||||
)["results"]
|
||||
@ -245,7 +245,7 @@ class TheMovieDatabase(Provider):
|
||||
params={
|
||||
"query": title,
|
||||
"year": year,
|
||||
"languages": [str(x) for x in language],
|
||||
"language": next((str(x) for x in language), None),
|
||||
},
|
||||
)
|
||||
)["results"]
|
||||
|
||||
@ -172,8 +172,22 @@ class TVDB(Provider):
|
||||
return [
|
||||
SearchSerie(
|
||||
slug=x["slug"],
|
||||
name=x["name"],
|
||||
description=x.get("overview"),
|
||||
name=next(
|
||||
(
|
||||
x["translations"][lang.to_alpha3()]
|
||||
for lang in language
|
||||
if "translations" in x and lang.to_alpha3() in x["translations"]
|
||||
),
|
||||
x["name"],
|
||||
),
|
||||
description=next(
|
||||
(
|
||||
x["overviews"][lang.to_alpha3()]
|
||||
for lang in language
|
||||
if "overviews" in x and lang.to_alpha3() in x["overviews"]
|
||||
),
|
||||
x.get("overview"),
|
||||
),
|
||||
start_air=datetime.strptime(x["first_air_time"], "%Y-%m-%d").date()
|
||||
if x.get("first_air_time")
|
||||
else None,
|
||||
|
||||
40
scanner/scanner/routers/dependencies.py
Normal file
40
scanner/scanner/routers/dependencies.py
Normal file
@ -0,0 +1,40 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Header, Request
|
||||
from langcodes import Language
|
||||
|
||||
from ..providers.composite import CompositeProvider
|
||||
|
||||
|
||||
def get_provider(request: Request) -> CompositeProvider:
|
||||
return request.app.state.provider
|
||||
|
||||
|
||||
def get_preferred_languages(
|
||||
accept_language: Annotated[str | None, Header()] = None,
|
||||
) -> list[Language]:
|
||||
if not accept_language:
|
||||
return []
|
||||
|
||||
ret: list[tuple[float, int, Language]] = []
|
||||
for index, item in enumerate(accept_language.split(",")):
|
||||
part = item.strip()
|
||||
if not part:
|
||||
continue
|
||||
|
||||
tag, *params = [x.strip() for x in part.split(";")]
|
||||
if tag == "*":
|
||||
continue
|
||||
|
||||
try:
|
||||
q = next((float(x[2:]) for x in params if x.startswith("q=")), 1)
|
||||
if q <= 0:
|
||||
continue
|
||||
|
||||
language = Language.get(tag)
|
||||
ret.append((q, index, language))
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
ret.sort(key=lambda x: (-x[0], x[1]))
|
||||
return [x for _q, _i, x in ret] + [Language.get("en")]
|
||||
@ -5,8 +5,14 @@ from fastapi import APIRouter, BackgroundTasks, Depends, Security
|
||||
from ..fsscan import create_scanner
|
||||
from ..identifiers.identify import identify
|
||||
from ..jwt import validate_bearer
|
||||
from ..models.movie import SearchMovie
|
||||
from ..models.request import RequestRet
|
||||
from ..models.serie import SearchSerie
|
||||
from ..models.videos import Video
|
||||
from ..providers.composite import CompositeProvider
|
||||
from ..status import StatusService
|
||||
from ..utils import Language
|
||||
from .dependencies import get_preferred_languages, get_provider
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@ -52,9 +58,47 @@ async def trigger_scan(
|
||||
async def get_guess(
|
||||
path: str,
|
||||
_: Annotated[None, Security(validate_bearer, scopes=["scanner.guess"])],
|
||||
):
|
||||
) -> Video:
|
||||
"""
|
||||
Identify a video path and return a serie/movie guess.
|
||||
"""
|
||||
|
||||
return await identify(path)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/movies",
|
||||
status_code=200,
|
||||
response_description="Found movies",
|
||||
)
|
||||
async def get_movies(
|
||||
provider: Annotated[CompositeProvider, Depends(get_provider)],
|
||||
language: Annotated[list[Language], Depends(get_preferred_languages)],
|
||||
_: Annotated[None, Security(validate_bearer, scopes=["scanner.search"])],
|
||||
query: str,
|
||||
year: int | None = None,
|
||||
) -> list[SearchMovie]:
|
||||
"""
|
||||
Search for a movie
|
||||
"""
|
||||
|
||||
return await provider.search_movies(query, year=year, language=language)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/series",
|
||||
status_code=200,
|
||||
response_description="Found series",
|
||||
)
|
||||
async def get_series(
|
||||
provider: Annotated[CompositeProvider, Depends(get_provider)],
|
||||
language: Annotated[list[Language], Depends(get_preferred_languages)],
|
||||
_: Annotated[None, Security(validate_bearer, scopes=["scanner.search"])],
|
||||
query: str,
|
||||
year: int | None = None,
|
||||
) -> list[SearchSerie]:
|
||||
"""
|
||||
Search for a serie
|
||||
"""
|
||||
|
||||
return await provider.search_series(query, year=year, language=language)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user