lib.itmens/catalog/api.py

240 lines
6.3 KiB
Python
Raw Normal View History

2024-05-28 13:18:40 -04:00
from enum import Enum
from typing import List
2024-04-06 00:13:50 -04:00
2025-01-08 00:28:30 -05:00
from django.core.cache import cache
2024-01-28 09:04:35 -05:00
from django.http import HttpResponse
2025-01-08 00:28:30 -05:00
from django.utils import timezone
2023-06-03 11:10:48 -04:00
from ninja import Schema
2023-06-03 11:10:48 -04:00
from common.api import *
2022-12-17 23:03:19 -05:00
from .common import *
from .models import *
from .search.models import enqueue_fetch, get_fetch_lock, query_index
2022-12-17 23:03:19 -05:00
from .sites import *
2023-02-15 15:45:57 -05:00
class SearchResult(Schema):
2023-07-01 10:41:20 -04:00
data: List[
EditionSchema
| MovieSchema
| TVShowSchema
| TVSeasonSchema
| AlbumSchema
| PodcastSchema
| GameSchema
| PerformanceSchema
]
2023-06-03 11:10:48 -04:00
pages: int
count: int
2023-02-15 15:45:57 -05:00
2024-05-28 13:18:40 -04:00
class SearchableItemCategory(Enum):
Book = "book"
Movie = "movie"
TV = "tv"
Movie_And_TV = "movie,tv"
Music = "music"
Game = "game"
Podcast = "podcast"
Performance = "performance"
2025-01-08 00:28:30 -05:00
class Gallery(Schema):
name: str
items: List[ItemSchema]
2024-01-28 07:57:39 -05:00
@api.get(
"/catalog/search",
response={200: SearchResult, 400: Result},
summary="Search items in catalog",
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
)
2023-06-03 11:10:48 -04:00
def search_item(
2024-05-28 13:18:40 -04:00
request, query: str, category: SearchableItemCategory | None = None, page: int = 1
2023-06-03 11:10:48 -04:00
):
"""
Search items in catalog
count and pages are estimated, the actual data may be less
unlike the web search, this does not show external results,
nor does it parse a url to fetch an item. to do that, use /catalog/fetch.
"""
2023-02-15 15:45:57 -05:00
query = query.strip()
if not query:
return 400, {"message": "Invalid query"}
2024-05-28 13:18:40 -04:00
categories = category.value.split(",") if category else None
2023-06-08 19:21:02 -04:00
items, num_pages, count, _ = query_index(
2023-07-12 01:11:15 -04:00
query,
page=page,
2024-05-28 13:18:40 -04:00
categories=categories,
2023-07-12 01:11:15 -04:00
prepare_external=False,
2023-06-03 11:10:48 -04:00
)
return 200, {"data": items, "pages": num_pages, "count": count}
2023-02-15 15:45:57 -05:00
2024-01-28 07:57:39 -05:00
@api.get(
"/catalog/fetch",
2023-06-03 11:10:48 -04:00
response={200: ItemSchema, 202: Result, 404: Result},
summary="Fetch item from URL of a supported site",
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
)
2022-12-18 20:28:39 -05:00
def fetch_item(request, url: str):
"""
Convert a URL from a supported site (e.g. https://m.imdb.com/title/tt2852400/) to an item.
If the item is not available in the catalog, HTTP 202 will be returned.
2023-12-04 09:11:32 -05:00
Wait 15 seconds or longer, call with same input again, it may return the actual fetched item.
Some site may take ~90 seconds to fetch.
2023-12-04 09:11:32 -05:00
If not getting the item after 120 seconds, please stop and consider the URL is not available.
"""
2022-12-17 23:03:19 -05:00
site = SiteManager.get_site_by_url(url)
if not site:
2023-06-03 11:10:48 -04:00
return 404, {"message": "URL not supported"}
2023-02-15 16:22:32 -05:00
item = site.get_item()
if item:
return 200, item
2023-12-04 09:11:32 -05:00
if get_fetch_lock(request.user, url):
2023-07-08 17:30:56 -04:00
enqueue_fetch(url, False)
2023-02-15 16:22:32 -05:00
return 202, {"message": "Fetch in progress"}
2022-12-17 23:03:19 -05:00
2025-01-08 00:28:30 -05:00
@api.get(
"/catalog/gallery/",
response={200: list[Gallery]},
summary="Trending items in catalog",
auth=None,
tags=["catalog"],
)
def trending_items(request):
"""
Returns a list of galleries, each gallery is a list of items
"""
gallery_list = cache.get("public_gallery", [])
# rotate every 6 minutes
rot = timezone.now().minute // 6
for gallery in gallery_list:
i = rot * len(gallery["items"]) // 10
gallery["items"] = gallery["items"][i:] + gallery["items"][:i]
return 200, gallery_list
2023-06-03 01:43:03 -04:00
def _get_item(cls, uuid, response):
item = Item.get_by_url(uuid)
2023-06-03 01:43:03 -04:00
if not item:
return 404, {"message": "Item not found"}
if item.merged_to_item:
response["Location"] = item.merged_to_item.api_url
return 302, {"message": "Item merged", "url": item.merged_to_item.api_url}
if item.is_deleted:
return 404, {"message": "Item not found"}
if item.__class__ != cls:
response["Location"] = item.api_url
return 302, {"message": "Item recasted", "url": item.api_url}
2023-06-03 01:43:03 -04:00
return item
2024-01-28 07:57:39 -05:00
@api.get(
2023-06-03 01:43:03 -04:00
"/book/{uuid}",
response={200: EditionSchema, 302: RedirectedResult, 404: Result},
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
2023-06-03 01:43:03 -04:00
)
def get_book(request, uuid: str, response: HttpResponse):
return _get_item(Edition, uuid, response)
2024-01-28 07:57:39 -05:00
@api.get(
2023-06-03 01:43:03 -04:00
"/movie/{uuid}",
response={200: MovieSchema, 302: RedirectedResult, 404: Result},
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
2023-06-03 01:43:03 -04:00
)
def get_movie(request, uuid: str, response: HttpResponse):
return _get_item(Movie, uuid, response)
2024-01-28 07:57:39 -05:00
@api.get(
2023-06-03 01:43:03 -04:00
"/tv/{uuid}",
response={200: TVShowSchema, 302: RedirectedResult, 404: Result},
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
2023-06-03 01:43:03 -04:00
)
def get_tv_show(request, uuid: str, response: HttpResponse):
return _get_item(TVShow, uuid, response)
2024-01-28 07:57:39 -05:00
@api.get(
2023-06-03 01:43:03 -04:00
"/tv/season/{uuid}",
response={200: TVSeasonSchema, 302: RedirectedResult, 404: Result},
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
2023-06-03 01:43:03 -04:00
)
def get_tv_season(request, uuid: str, response: HttpResponse):
return _get_item(TVSeason, uuid, response)
2024-01-28 07:57:39 -05:00
@api.get(
2023-06-19 21:32:11 -04:00
"/tv/episode/{uuid}",
response={200: TVEpisodeSchema, 302: RedirectedResult, 404: Result},
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
2023-06-19 21:32:11 -04:00
)
def get_tv_episode(request, uuid: str, response: HttpResponse):
return _get_item(TVEpisode, uuid, response)
2024-01-28 07:57:39 -05:00
@api.get(
2023-06-03 01:43:03 -04:00
"/podcast/{uuid}",
response={200: PodcastSchema, 302: RedirectedResult, 404: Result},
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
2023-06-03 01:43:03 -04:00
)
def get_podcast(request, uuid: str, response: HttpResponse):
return _get_item(Podcast, uuid, response)
2024-01-28 07:57:39 -05:00
@api.get(
2023-06-03 01:43:03 -04:00
"/album/{uuid}",
response={200: AlbumSchema, 302: RedirectedResult, 404: Result},
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
2023-06-03 01:43:03 -04:00
)
def get_album(request, uuid: str, response: HttpResponse):
return _get_item(Album, uuid, response)
2024-01-28 07:57:39 -05:00
@api.get(
2023-06-03 01:43:03 -04:00
"/game/{uuid}",
response={200: GameSchema, 302: RedirectedResult, 404: Result},
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
2023-06-03 01:43:03 -04:00
)
def get_game(request, uuid: str, response: HttpResponse):
return _get_item(Game, uuid, response)
2023-06-06 13:21:27 -04:00
2024-01-28 07:57:39 -05:00
@api.get(
2023-06-06 13:21:27 -04:00
"/performance/{uuid}",
response={200: PerformanceSchema, 302: RedirectedResult, 404: Result},
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
2023-06-06 13:21:27 -04:00
)
def get_performance(request, uuid: str, response: HttpResponse):
return _get_item(Performance, uuid, response)
2024-01-28 07:57:39 -05:00
@api.get(
2023-06-06 13:21:27 -04:00
"/performance/production/{uuid}",
response={200: PerformanceProductionSchema, 302: RedirectedResult, 404: Result},
auth=None,
2024-01-28 07:57:39 -05:00
tags=["catalog"],
2023-06-06 13:21:27 -04:00
)
def get_performance_production(request, uuid: str, response: HttpResponse):
return _get_item(PerformanceProduction, uuid, response)