2023-06-03 11:10:48 -04:00
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from django.http import HttpResponse
|
|
|
|
from django.http import Http404
|
|
|
|
from ninja import Schema
|
|
|
|
from common.api import *
|
2022-12-17 23:03:19 -05:00
|
|
|
from .models import *
|
|
|
|
from .common import *
|
|
|
|
from .sites import *
|
2023-06-03 11:10:48 -04:00
|
|
|
from .search.models import enqueue_fetch, query_index
|
2022-12-07 19:09:05 -05:00
|
|
|
|
2023-02-15 15:45:57 -05:00
|
|
|
|
|
|
|
class SearchResult(Schema):
|
2023-06-03 11:10:48 -04:00
|
|
|
data: List[ItemSchema]
|
|
|
|
pages: int
|
|
|
|
count: int
|
2023-02-15 15:45:57 -05:00
|
|
|
|
|
|
|
|
2023-06-02 21:54:48 -04:00
|
|
|
@api.get(
|
|
|
|
"/catalog/search",
|
|
|
|
response={200: SearchResult, 400: Result},
|
|
|
|
summary="Search items in catalog",
|
|
|
|
auth=None,
|
|
|
|
)
|
2023-06-03 11:10:48 -04:00
|
|
|
def search_item(
|
|
|
|
request, query: str, category: AvailableItemCategory | None = None, page: int = 1
|
|
|
|
):
|
|
|
|
"""
|
|
|
|
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:
|
2023-06-02 21:54:48 -04:00
|
|
|
return 400, {"message": "Invalid query"}
|
2023-06-03 11:10:48 -04:00
|
|
|
items, num_pages, count = query_index(
|
|
|
|
query, page=page, category=category, prepare_external=False
|
|
|
|
)
|
|
|
|
return 200, {"data": items, "pages": num_pages, "count": count}
|
2023-02-15 15:45:57 -05:00
|
|
|
|
|
|
|
|
2023-06-02 21:54:48 -04:00
|
|
|
@api.get(
|
|
|
|
"/catalog/fetch",
|
2023-06-03 11:10:48 -04:00
|
|
|
response={200: ItemSchema, 202: Result, 404: Result},
|
2023-06-02 21:54:48 -04:00
|
|
|
summary="Fetch item from URL of a supported site",
|
|
|
|
auth=None,
|
|
|
|
)
|
2022-12-18 20:28:39 -05:00
|
|
|
def fetch_item(request, url: str):
|
2023-06-02 21:54:48 -04:00
|
|
|
"""
|
|
|
|
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.
|
|
|
|
Wait 10 seconds or longer, call with same input again, it may return the actual fetched item.
|
|
|
|
Some site may take ~90 seconds to fetch.
|
|
|
|
"""
|
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
|
|
|
|
enqueue_fetch(url, False)
|
|
|
|
return 202, {"message": "Fetch in progress"}
|
2022-12-17 23:03:19 -05:00
|
|
|
|
|
|
|
|
2023-06-03 01:43:03 -04:00
|
|
|
def _get_item(cls, uuid, response):
|
|
|
|
item = cls.get_by_url(uuid)
|
|
|
|
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"}
|
|
|
|
return item
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/book/{uuid}",
|
|
|
|
response={200: EditionSchema, 302: RedirectedResult, 404: Result},
|
|
|
|
auth=None,
|
|
|
|
)
|
|
|
|
def get_book(request, uuid: str, response: HttpResponse):
|
|
|
|
return _get_item(Edition, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/movie/{uuid}",
|
|
|
|
response={200: MovieSchema, 302: RedirectedResult, 404: Result},
|
|
|
|
auth=None,
|
|
|
|
)
|
|
|
|
def get_movie(request, uuid: str, response: HttpResponse):
|
|
|
|
return _get_item(Movie, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/tv/{uuid}",
|
|
|
|
response={200: TVShowSchema, 302: RedirectedResult, 404: Result},
|
|
|
|
auth=None,
|
|
|
|
)
|
|
|
|
def get_tv_show(request, uuid: str, response: HttpResponse):
|
|
|
|
return _get_item(TVShow, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/tv/season/{uuid}",
|
|
|
|
response={200: TVSeasonSchema, 302: RedirectedResult, 404: Result},
|
|
|
|
auth=None,
|
|
|
|
)
|
|
|
|
def get_tv_season(request, uuid: str, response: HttpResponse):
|
|
|
|
return _get_item(TVSeason, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/podcast/{uuid}",
|
|
|
|
response={200: PodcastSchema, 302: RedirectedResult, 404: Result},
|
|
|
|
auth=None,
|
|
|
|
)
|
|
|
|
def get_podcast(request, uuid: str, response: HttpResponse):
|
|
|
|
return _get_item(Podcast, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/album/{uuid}",
|
|
|
|
response={200: AlbumSchema, 302: RedirectedResult, 404: Result},
|
|
|
|
auth=None,
|
|
|
|
)
|
|
|
|
def get_album(request, uuid: str, response: HttpResponse):
|
|
|
|
return _get_item(Album, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/game/{uuid}",
|
|
|
|
response={200: GameSchema, 302: RedirectedResult, 404: Result},
|
|
|
|
auth=None,
|
|
|
|
)
|
|
|
|
def get_game(request, uuid: str, response: HttpResponse):
|
|
|
|
return _get_item(Game, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
# Legacy API will be removed soon
|
|
|
|
|
|
|
|
|
2023-06-03 11:10:48 -04:00
|
|
|
class SearchResultLegacy(Schema):
|
|
|
|
items: List[ItemSchema]
|
|
|
|
pages: int
|
|
|
|
|
|
|
|
|
2023-06-02 21:54:48 -04:00
|
|
|
@api.post(
|
|
|
|
"/catalog/search",
|
|
|
|
response={200: SearchResult, 400: Result},
|
|
|
|
summary="This method is deprecated, will be removed by Aug 1 2023; use GET instead",
|
|
|
|
auth=None,
|
|
|
|
deprecated=True,
|
|
|
|
)
|
|
|
|
def search_item_legacy(
|
|
|
|
request, query: str, category: AvailableItemCategory | None = None
|
|
|
|
):
|
|
|
|
query = query.strip()
|
|
|
|
if not query:
|
|
|
|
return 400, {"message": "Invalid query"}
|
|
|
|
result = Indexer.search(query, page=1, category=category)
|
|
|
|
return 200, {"items": result.items}
|
2022-12-17 23:03:19 -05:00
|
|
|
|
2023-02-15 15:45:57 -05:00
|
|
|
|
2023-06-02 21:54:48 -04:00
|
|
|
@api.post(
|
|
|
|
"/catalog/fetch",
|
|
|
|
response={200: ItemSchema, 202: Result},
|
|
|
|
summary="This method is deprecated, will be removed by Aug 1 2023; use GET instead",
|
|
|
|
auth=None,
|
|
|
|
deprecated=True,
|
|
|
|
)
|
|
|
|
def fetch_item_legacy(request, url: str):
|
|
|
|
site = SiteManager.get_site_by_url(url)
|
|
|
|
if not site:
|
|
|
|
raise Http404(url)
|
|
|
|
item = site.get_item()
|
|
|
|
if item:
|
|
|
|
return 200, item
|
|
|
|
enqueue_fetch(url, False)
|
|
|
|
return 202, {"message": "Fetch in progress"}
|
2023-02-15 15:45:57 -05:00
|
|
|
|
|
|
|
|
2023-06-02 21:54:48 -04:00
|
|
|
@api.get(
|
|
|
|
"/movie/{uuid}/",
|
|
|
|
response={200: MovieSchema, 302: RedirectedResult, 404: Result},
|
2023-06-03 01:43:03 -04:00
|
|
|
summary="This method is deprecated, will be removed by Aug 1 2023",
|
2023-06-02 21:54:48 -04:00
|
|
|
auth=None,
|
2023-06-03 01:43:03 -04:00
|
|
|
deprecated=True,
|
2023-06-02 21:54:48 -04:00
|
|
|
)
|
2023-06-03 01:43:03 -04:00
|
|
|
def get_movie_legacy(request, uuid: str, response: HttpResponse):
|
2023-06-02 21:54:48 -04:00
|
|
|
return _get_item(Movie, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/tv/{uuid}/",
|
|
|
|
response={200: TVShowSchema, 302: RedirectedResult, 404: Result},
|
2023-06-03 01:43:03 -04:00
|
|
|
summary="This method is deprecated, will be removed by Aug 1 2023",
|
2023-06-02 21:54:48 -04:00
|
|
|
auth=None,
|
2023-06-03 01:43:03 -04:00
|
|
|
deprecated=True,
|
2023-06-02 21:54:48 -04:00
|
|
|
)
|
2023-06-03 01:43:03 -04:00
|
|
|
def get_tv_show_legacy(request, uuid: str, response: HttpResponse):
|
2023-06-02 21:54:48 -04:00
|
|
|
return _get_item(TVShow, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/tvseason/{uuid}/",
|
|
|
|
response={200: TVSeasonSchema, 302: RedirectedResult, 404: Result},
|
2023-06-03 01:43:03 -04:00
|
|
|
summary="This method is deprecated, will be removed by Aug 1 2023",
|
2023-06-02 21:54:48 -04:00
|
|
|
auth=None,
|
|
|
|
deprecated=True,
|
|
|
|
)
|
|
|
|
def get_tv_season_legacy(request, uuid: str, response: HttpResponse):
|
|
|
|
return _get_item(TVSeason, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/podcast/{uuid}/",
|
|
|
|
response={200: PodcastSchema, 302: RedirectedResult, 404: Result},
|
2023-06-03 01:43:03 -04:00
|
|
|
summary="This method is deprecated, will be removed by Aug 1 2023",
|
2023-06-02 21:54:48 -04:00
|
|
|
auth=None,
|
2023-06-03 01:43:03 -04:00
|
|
|
deprecated=True,
|
2023-06-02 21:54:48 -04:00
|
|
|
)
|
2023-06-03 01:43:03 -04:00
|
|
|
def get_podcast_legacy(request, uuid: str, response: HttpResponse):
|
2023-06-02 21:54:48 -04:00
|
|
|
return _get_item(Podcast, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/album/{uuid}/",
|
|
|
|
response={200: AlbumSchema, 302: RedirectedResult, 404: Result},
|
2023-06-03 01:43:03 -04:00
|
|
|
summary="This method is deprecated, will be removed by Aug 1 2023",
|
2023-06-02 21:54:48 -04:00
|
|
|
auth=None,
|
2023-06-03 01:43:03 -04:00
|
|
|
deprecated=True,
|
2023-06-02 21:54:48 -04:00
|
|
|
)
|
2023-06-03 01:43:03 -04:00
|
|
|
def get_album_legacy(request, uuid: str, response: HttpResponse):
|
2023-06-02 21:54:48 -04:00
|
|
|
return _get_item(Album, uuid, response)
|
|
|
|
|
|
|
|
|
|
|
|
@api.get(
|
|
|
|
"/game/{uuid}/",
|
|
|
|
response={200: GameSchema, 302: RedirectedResult, 404: Result},
|
2023-06-03 01:43:03 -04:00
|
|
|
summary="This method is deprecated, will be removed by Aug 1 2023",
|
2023-06-02 21:54:48 -04:00
|
|
|
auth=None,
|
2023-06-03 01:43:03 -04:00
|
|
|
deprecated=True,
|
2023-06-02 21:54:48 -04:00
|
|
|
)
|
2023-06-03 01:43:03 -04:00
|
|
|
def get_game_legacy(request, uuid: str, response: HttpResponse):
|
2023-06-02 21:54:48 -04:00
|
|
|
return _get_item(Game, uuid, response)
|