2023-08-10 11:27:31 -04:00
|
|
|
from django.http import Http404, HttpResponse
|
2023-06-03 11:10:48 -04:00
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from ninja import Schema
|
2023-08-10 11:27:31 -04:00
|
|
|
|
2023-06-03 11:10:48 -04:00
|
|
|
from common.api import *
|
2023-08-10 11:27:31 -04:00
|
|
|
|
2022-12-17 23:03:19 -05:00
|
|
|
from .common import *
|
2023-08-10 11:27:31 -04:00
|
|
|
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 *
|
2022-12-07 19:09:05 -05:00
|
|
|
|
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
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-02 21:54:48 -04:00
|
|
|
"/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-08 19:21:02 -04:00
|
|
|
items, num_pages, count, _ = query_index(
|
2023-07-12 01:11:15 -04:00
|
|
|
query,
|
|
|
|
page=page,
|
|
|
|
categories=[category] if category else None,
|
|
|
|
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
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-02 21:54:48 -04:00
|
|
|
"/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.
|
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.
|
2023-06-02 21:54:48 -04:00
|
|
|
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.
|
2023-06-02 21:54:48 -04:00
|
|
|
"""
|
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
|
|
|
|
|
|
|
|
2023-06-03 01:43:03 -04:00
|
|
|
def _get_item(cls, uuid, response):
|
2023-06-29 08:19:41 -04:00
|
|
|
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"}
|
2023-06-29 08:19:41 -04:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-03 01:43:03 -04:00
|
|
|
"/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)
|
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-03 01:43:03 -04:00
|
|
|
"/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)
|
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-03 01:43:03 -04:00
|
|
|
"/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)
|
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-03 01:43:03 -04:00
|
|
|
"/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)
|
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-19 21:32:11 -04:00
|
|
|
"/tv/episode/{uuid}",
|
|
|
|
response={200: TVEpisodeSchema, 302: RedirectedResult, 404: Result},
|
|
|
|
auth=None,
|
|
|
|
)
|
|
|
|
def get_tv_episode(request, uuid: str, response: HttpResponse):
|
|
|
|
return _get_item(TVEpisode, uuid, response)
|
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-03 01:43:03 -04:00
|
|
|
"/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)
|
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-03 01:43:03 -04:00
|
|
|
"/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)
|
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-03 01:43:03 -04:00
|
|
|
"/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)
|
2023-06-06 13:21:27 -04:00
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-06 13:21:27 -04:00
|
|
|
"/performance/{uuid}",
|
|
|
|
response={200: PerformanceSchema, 302: RedirectedResult, 404: Result},
|
|
|
|
auth=None,
|
|
|
|
)
|
|
|
|
def get_performance(request, uuid: str, response: HttpResponse):
|
|
|
|
return _get_item(Performance, uuid, response)
|
|
|
|
|
|
|
|
|
2023-11-02 21:28:11 +01:00
|
|
|
@api.api_operation(
|
2024-01-20 01:50:36 -05:00
|
|
|
["GET"],
|
2023-06-06 13:21:27 -04:00
|
|
|
"/performance/production/{uuid}",
|
|
|
|
response={200: PerformanceProductionSchema, 302: RedirectedResult, 404: Result},
|
|
|
|
auth=None,
|
|
|
|
)
|
|
|
|
def get_performance_production(request, uuid: str, response: HttpResponse):
|
|
|
|
return _get_item(PerformanceProduction, uuid, response)
|