mirror of
https://git.vectorsigma.ru/public/tubearchivist.git
synced 2026-08-04 20:19:30 +00:00
add drf_spectacular, type and serialize channel app
This commit is contained in:
111
backend/channel/serializers.py
Normal file
111
backend/channel/serializers.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
"""channel serializers"""
|
||||||
|
|
||||||
|
# pylint: disable=abstract-method
|
||||||
|
|
||||||
|
from common.serializers import PaginationSerializer
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelOverwriteSerializer(serializers.Serializer):
|
||||||
|
"""serialize channel overwrites"""
|
||||||
|
|
||||||
|
download_format = serializers.CharField(required=False, allow_null=True)
|
||||||
|
autodelete_days = serializers.IntegerField(required=False, allow_null=True)
|
||||||
|
index_playlists = serializers.BooleanField(required=False, allow_null=True)
|
||||||
|
integrate_sponsorblock = serializers.BooleanField(
|
||||||
|
required=False, allow_null=True
|
||||||
|
)
|
||||||
|
subscriptions_channel_size = serializers.IntegerField(
|
||||||
|
required=False, allow_null=True
|
||||||
|
)
|
||||||
|
subscriptions_live_channel_size = serializers.IntegerField(
|
||||||
|
required=False, allow_null=True
|
||||||
|
)
|
||||||
|
subscriptions_shorts_channel_size = serializers.IntegerField(
|
||||||
|
required=False, allow_null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_internal_value(self, data):
|
||||||
|
"""Override this method to detect unknown fields."""
|
||||||
|
allowed_fields = set(self.fields.keys())
|
||||||
|
input_fields = set(data.keys())
|
||||||
|
|
||||||
|
unknown_fields = input_fields - allowed_fields
|
||||||
|
|
||||||
|
if unknown_fields:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
{"error": f"Unknown fields: {', '.join(unknown_fields)}"}
|
||||||
|
)
|
||||||
|
|
||||||
|
return super().to_internal_value(data)
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelSerializer(serializers.Serializer):
|
||||||
|
"""serialize channel"""
|
||||||
|
|
||||||
|
channel_id = serializers.CharField()
|
||||||
|
channel_active = serializers.BooleanField()
|
||||||
|
channel_banner_url = serializers.CharField()
|
||||||
|
channel_thumb_url = serializers.CharField()
|
||||||
|
channel_tvart_url = serializers.CharField()
|
||||||
|
channel_description = serializers.CharField()
|
||||||
|
channel_last_refresh = serializers.CharField()
|
||||||
|
channel_name = serializers.CharField()
|
||||||
|
channel_overwrites = ChannelOverwriteSerializer(required=False)
|
||||||
|
channel_subs = serializers.IntegerField()
|
||||||
|
channel_subscribed = serializers.BooleanField()
|
||||||
|
channel_tags = serializers.ListField(child=serializers.CharField())
|
||||||
|
channel_views = serializers.IntegerField()
|
||||||
|
_index = serializers.CharField(required=False)
|
||||||
|
_score = serializers.IntegerField(required=False)
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelListSerializer(serializers.Serializer):
|
||||||
|
"""serialize channel list"""
|
||||||
|
|
||||||
|
data = ChannelSerializer(many=True)
|
||||||
|
paginate = PaginationSerializer()
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelListQuerySerializer(serializers.Serializer):
|
||||||
|
"""serialize list query"""
|
||||||
|
|
||||||
|
filter = serializers.ChoiceField(choices=["subscribed"], required=False)
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelUpdateSerializer(serializers.Serializer):
|
||||||
|
"""update channel"""
|
||||||
|
|
||||||
|
channel_subscribed = serializers.BooleanField(required=False)
|
||||||
|
channel_overwrites = ChannelOverwriteSerializer(required=False)
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelAggBucketSerializer(serializers.Serializer):
|
||||||
|
"""serialize channel agg bucket"""
|
||||||
|
|
||||||
|
value = serializers.IntegerField()
|
||||||
|
value_str = serializers.CharField(required=False)
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelAggSerializer(serializers.Serializer):
|
||||||
|
"""serialize channel aggregation"""
|
||||||
|
|
||||||
|
total_items = ChannelAggBucketSerializer()
|
||||||
|
total_size = ChannelAggBucketSerializer()
|
||||||
|
total_duration = ChannelAggBucketSerializer()
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelNavSerializer(serializers.Serializer):
|
||||||
|
"""serialize channel navigation"""
|
||||||
|
|
||||||
|
has_pending = serializers.BooleanField()
|
||||||
|
has_playlists = serializers.BooleanField()
|
||||||
|
has_videos = serializers.BooleanField()
|
||||||
|
has_streams = serializers.BooleanField()
|
||||||
|
has_shorts = serializers.BooleanField()
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelSearchQuerySerializer(serializers.Serializer):
|
||||||
|
"""serialize query parameters for searching"""
|
||||||
|
|
||||||
|
q = serializers.CharField()
|
||||||
@@ -1,10 +1,25 @@
|
|||||||
"""all channel API views"""
|
"""all channel API views"""
|
||||||
|
|
||||||
|
from channel.serializers import (
|
||||||
|
ChannelAggSerializer,
|
||||||
|
ChannelListQuerySerializer,
|
||||||
|
ChannelListSerializer,
|
||||||
|
ChannelNavSerializer,
|
||||||
|
ChannelSearchQuerySerializer,
|
||||||
|
ChannelSerializer,
|
||||||
|
ChannelUpdateSerializer,
|
||||||
|
)
|
||||||
from channel.src.index import YoutubeChannel, channel_overwrites
|
from channel.src.index import YoutubeChannel, channel_overwrites
|
||||||
from channel.src.nav import ChannelNav
|
from channel.src.nav import ChannelNav
|
||||||
|
from common.serializers import ErrorResponseSerializer
|
||||||
from common.src.urlparser import Parser
|
from common.src.urlparser import Parser
|
||||||
from common.views_base import AdminWriteOnly, ApiBaseView
|
from common.views_base import AdminWriteOnly, ApiBaseView
|
||||||
from download.src.subscriptions import ChannelSubscription
|
from download.src.subscriptions import ChannelSubscription
|
||||||
|
from drf_spectacular.utils import (
|
||||||
|
OpenApiParameter,
|
||||||
|
OpenApiResponse,
|
||||||
|
extend_schema,
|
||||||
|
)
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from task.tasks import index_channel_playlists, subscribe_to
|
from task.tasks import index_channel_playlists, subscribe_to
|
||||||
|
|
||||||
@@ -19,26 +34,38 @@ class ChannelApiListView(ApiBaseView):
|
|||||||
valid_filter = ["subscribed"]
|
valid_filter = ["subscribed"]
|
||||||
permission_classes = [AdminWriteOnly]
|
permission_classes = [AdminWriteOnly]
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
responses={
|
||||||
|
200: OpenApiResponse(ChannelListSerializer()),
|
||||||
|
},
|
||||||
|
parameters=[
|
||||||
|
OpenApiParameter(
|
||||||
|
name="filter",
|
||||||
|
description="Filter by Subscribed",
|
||||||
|
type=ChannelListQuerySerializer(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""get request"""
|
"""get request"""
|
||||||
self.data.update(
|
self.data.update(
|
||||||
{"sort": [{"channel_name.keyword": {"order": "asc"}}]}
|
{"sort": [{"channel_name.keyword": {"order": "asc"}}]}
|
||||||
)
|
)
|
||||||
|
|
||||||
query_filter = request.GET.get("filter", False)
|
serializer = ChannelListQuerySerializer(data=request.query_params)
|
||||||
must_list = []
|
serializer.is_valid(raise_exception=True)
|
||||||
if query_filter:
|
validated_data = serializer.validated_data
|
||||||
if query_filter not in self.valid_filter:
|
|
||||||
message = f"invalid url query filter: {query_filter}"
|
|
||||||
print(message)
|
|
||||||
return Response({"message": message}, status=400)
|
|
||||||
|
|
||||||
|
must_list = []
|
||||||
|
query_filter = validated_data.get("filter")
|
||||||
|
if query_filter:
|
||||||
must_list.append({"term": {"channel_subscribed": {"value": True}}})
|
must_list.append({"term": {"channel_subscribed": {"value": True}}})
|
||||||
|
|
||||||
self.data["query"] = {"bool": {"must": must_list}}
|
self.data["query"] = {"bool": {"must": must_list}}
|
||||||
self.get_document_list(request)
|
self.get_document_list(request)
|
||||||
|
serializer = ChannelListSerializer(self.response)
|
||||||
|
|
||||||
return Response(self.response)
|
return Response(serializer.data)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""subscribe/unsubscribe to list of channels"""
|
"""subscribe/unsubscribe to list of channels"""
|
||||||
@@ -81,53 +108,79 @@ class ChannelApiView(ApiBaseView):
|
|||||||
search_base = "ta_channel/_doc/"
|
search_base = "ta_channel/_doc/"
|
||||||
permission_classes = [AdminWriteOnly]
|
permission_classes = [AdminWriteOnly]
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
responses={
|
||||||
|
200: OpenApiResponse(ChannelSerializer()),
|
||||||
|
404: OpenApiResponse(
|
||||||
|
ErrorResponseSerializer(), description="Channel not found"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
def get(self, request, channel_id):
|
def get(self, request, channel_id):
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
"""get request"""
|
"""get channel detail"""
|
||||||
self.get_document(channel_id)
|
self.get_document(channel_id)
|
||||||
return Response(self.response, status=self.status_code)
|
if not self.response:
|
||||||
|
error = ErrorResponseSerializer({"error": "channel not found"})
|
||||||
|
return Response(error.data, status=404)
|
||||||
|
|
||||||
|
response_serializer = ChannelSerializer(self.response)
|
||||||
|
return Response(response_serializer.data, status=self.status_code)
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
request=ChannelUpdateSerializer(),
|
||||||
|
responses={
|
||||||
|
200: OpenApiResponse(ChannelUpdateSerializer()),
|
||||||
|
400: OpenApiResponse(
|
||||||
|
ErrorResponseSerializer(), description="Bad request"
|
||||||
|
),
|
||||||
|
404: OpenApiResponse(
|
||||||
|
ErrorResponseSerializer(), description="Channel not found"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
def post(self, request, channel_id):
|
def post(self, request, channel_id):
|
||||||
"""modify channel overwrites"""
|
"""modify channel"""
|
||||||
self.get_document(channel_id)
|
self.get_document(channel_id)
|
||||||
if not self.response["data"]:
|
if not self.response:
|
||||||
return Response({"error": "channel not found"}, status=404)
|
error = ErrorResponseSerializer({"error": "channel not found"})
|
||||||
|
return Response(error.data, status=404)
|
||||||
|
|
||||||
data = request.data
|
serializer = ChannelUpdateSerializer(data=request.data)
|
||||||
subscribed = data.get("channel_subscribed")
|
serializer.is_valid(raise_exception=True)
|
||||||
|
validated_data = serializer.validated_data
|
||||||
|
|
||||||
|
subscribed = validated_data.get("channel_subscribed")
|
||||||
if subscribed is not None:
|
if subscribed is not None:
|
||||||
channel_sub = ChannelSubscription()
|
ChannelSubscription().change_subscribe(channel_id, subscribed)
|
||||||
json_data = channel_sub.change_subscribe(channel_id, subscribed)
|
|
||||||
return Response(json_data, status=200)
|
|
||||||
|
|
||||||
if "channel_overwrites" not in data:
|
overwrites = validated_data.get("channel_overwrites")
|
||||||
return Response({"error": "invalid payload"}, status=400)
|
if overwrites:
|
||||||
|
channel_overwrites(channel_id, overwrites)
|
||||||
overwrites = data["channel_overwrites"]
|
|
||||||
|
|
||||||
try:
|
|
||||||
json_data = channel_overwrites(channel_id, overwrites)
|
|
||||||
if overwrites.get("index_playlists"):
|
if overwrites.get("index_playlists"):
|
||||||
index_channel_playlists.delay(channel_id)
|
index_channel_playlists.delay(channel_id)
|
||||||
|
|
||||||
except ValueError as err:
|
return Response(serializer.data, status=200)
|
||||||
return Response({"error": str(err)}, status=400)
|
|
||||||
|
|
||||||
return Response(json_data, status=200)
|
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
responses={
|
||||||
|
204: OpenApiResponse(description="Channel deleted"),
|
||||||
|
404: OpenApiResponse(
|
||||||
|
ErrorResponseSerializer(), description="Channel not found"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
def delete(self, request, channel_id):
|
def delete(self, request, channel_id):
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
"""delete channel"""
|
"""delete channel"""
|
||||||
message = {"channel": channel_id}
|
|
||||||
try:
|
try:
|
||||||
YoutubeChannel(channel_id).delete_channel()
|
YoutubeChannel(channel_id).delete_channel()
|
||||||
status_code = 200
|
return Response(status=204)
|
||||||
message.update({"state": "delete"})
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
status_code = 404
|
pass
|
||||||
message.update({"state": "not found"})
|
|
||||||
|
|
||||||
return Response(message, status=status_code)
|
error = ErrorResponseSerializer({"error": "channel not found"})
|
||||||
|
return Response(error.data, status=404)
|
||||||
|
|
||||||
|
|
||||||
class ChannelAggsApiView(ApiBaseView):
|
class ChannelAggsApiView(ApiBaseView):
|
||||||
@@ -137,8 +190,13 @@ class ChannelAggsApiView(ApiBaseView):
|
|||||||
|
|
||||||
search_base = "ta_video/_search"
|
search_base = "ta_video/_search"
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
responses={
|
||||||
|
200: OpenApiResponse(ChannelAggSerializer()),
|
||||||
|
},
|
||||||
|
)
|
||||||
def get(self, request, channel_id):
|
def get(self, request, channel_id):
|
||||||
"""get aggs"""
|
"""get channel aggregations"""
|
||||||
self.data.update(
|
self.data.update(
|
||||||
{
|
{
|
||||||
"query": {
|
"query": {
|
||||||
@@ -152,8 +210,9 @@ class ChannelAggsApiView(ApiBaseView):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.get_aggs()
|
self.get_aggs()
|
||||||
|
serializer = ChannelAggSerializer(self.response)
|
||||||
|
|
||||||
return Response(self.response)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class ChannelNavApiView(ApiBaseView):
|
class ChannelNavApiView(ApiBaseView):
|
||||||
@@ -161,11 +220,17 @@ class ChannelNavApiView(ApiBaseView):
|
|||||||
GET: get channel nav
|
GET: get channel nav
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
responses={
|
||||||
|
200: OpenApiResponse(ChannelNavSerializer()),
|
||||||
|
},
|
||||||
|
)
|
||||||
def get(self, request, channel_id):
|
def get(self, request, channel_id):
|
||||||
"""get nav"""
|
"""get navigation"""
|
||||||
|
|
||||||
nav = ChannelNav(channel_id).get_nav()
|
nav = ChannelNav(channel_id).get_nav()
|
||||||
return Response(nav)
|
serializer = ChannelNavSerializer(nav)
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class ChannelApiSearchView(ApiBaseView):
|
class ChannelApiSearchView(ApiBaseView):
|
||||||
@@ -175,10 +240,31 @@ class ChannelApiSearchView(ApiBaseView):
|
|||||||
|
|
||||||
search_base = "ta_channel/_doc/"
|
search_base = "ta_channel/_doc/"
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
responses={
|
||||||
|
200: OpenApiResponse(ChannelSerializer()),
|
||||||
|
400: OpenApiResponse(description="Bad Request"),
|
||||||
|
404: OpenApiResponse(
|
||||||
|
ErrorResponseSerializer(), description="Channel not found"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
parameters=[
|
||||||
|
OpenApiParameter(
|
||||||
|
name="q",
|
||||||
|
description="Search query string",
|
||||||
|
required=True,
|
||||||
|
type=str,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
"""handle get request, search with s parameter"""
|
"""search for local channel ID"""
|
||||||
|
|
||||||
query = request.GET.get("q")
|
serializer = ChannelSearchQuerySerializer(data=request.query_params)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
validated_data = serializer.validated_data
|
||||||
|
|
||||||
|
query = validated_data.get("q")
|
||||||
if not query:
|
if not query:
|
||||||
message = "missing expected q parameter"
|
message = "missing expected q parameter"
|
||||||
return Response({"message": message, "data": False}, status=400)
|
return Response({"message": message, "data": False}, status=400)
|
||||||
@@ -186,13 +272,16 @@ class ChannelApiSearchView(ApiBaseView):
|
|||||||
try:
|
try:
|
||||||
parsed = Parser(query).parse()[0]
|
parsed = Parser(query).parse()[0]
|
||||||
except (ValueError, IndexError, AttributeError):
|
except (ValueError, IndexError, AttributeError):
|
||||||
message = f"channel not found: {query}"
|
error = ErrorResponseSerializer(
|
||||||
return Response({"message": message, "data": False}, status=404)
|
{"error": f"channel not found: {query}"}
|
||||||
|
)
|
||||||
|
return Response(error.data, status=404)
|
||||||
|
|
||||||
if not parsed["type"] == "channel":
|
if not parsed["type"] == "channel":
|
||||||
message = "expected type channel"
|
error = ErrorResponseSerializer({"error": "expected channel data"})
|
||||||
return Response({"message": message, "data": False}, status=400)
|
return Response(error.data, status=400)
|
||||||
|
|
||||||
self.get_document(parsed["url"])
|
self.get_document(parsed["url"])
|
||||||
|
serializer = ChannelSerializer(self.response)
|
||||||
|
|
||||||
return Response(self.response, status=self.status_code)
|
return Response(serializer.data, status=self.status_code)
|
||||||
|
|||||||
29
backend/common/serializers.py
Normal file
29
backend/common/serializers.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
"""common serializers"""
|
||||||
|
|
||||||
|
# pylint: disable=abstract-method
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorResponseSerializer(serializers.Serializer):
|
||||||
|
"""error message"""
|
||||||
|
|
||||||
|
error = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
|
class PaginationSerializer(serializers.Serializer):
|
||||||
|
"""serialize paginate response"""
|
||||||
|
|
||||||
|
page_size = serializers.IntegerField()
|
||||||
|
page_from = serializers.IntegerField()
|
||||||
|
prev_pages = serializers.ListField(
|
||||||
|
child=serializers.IntegerField(), allow_null=True
|
||||||
|
)
|
||||||
|
current_page = serializers.IntegerField()
|
||||||
|
max_hits = serializers.BooleanField()
|
||||||
|
params = serializers.CharField()
|
||||||
|
last_page = serializers.BooleanField()
|
||||||
|
next_pages = serializers.ListField(
|
||||||
|
child=serializers.IntegerField(), allow_null=True
|
||||||
|
)
|
||||||
|
total_hits = serializers.IntegerField()
|
||||||
@@ -106,7 +106,7 @@ class Pagination:
|
|||||||
page_get = self.page_get
|
page_get = self.page_get
|
||||||
page_from = 0
|
page_from = 0
|
||||||
if page_get in [0, 1]:
|
if page_get in [0, 1]:
|
||||||
prev_pages = False
|
prev_pages = None
|
||||||
elif page_get > 1:
|
elif page_get > 1:
|
||||||
page_from = (page_get - 1) * self.page_size
|
page_from = (page_get - 1) * self.page_size
|
||||||
prev_pages = [
|
prev_pages = [
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
"""base classes to inherit from"""
|
"""base classes to inherit from"""
|
||||||
|
|
||||||
from appsettings.src.config import AppConfig
|
|
||||||
from common.src.env_settings import EnvironmentSettings
|
|
||||||
from common.src.es_connect import ElasticWrap
|
from common.src.es_connect import ElasticWrap
|
||||||
from common.src.index_generic import Pagination
|
from common.src.index_generic import Pagination
|
||||||
from common.src.search_processor import SearchProcess, process_aggs
|
from common.src.search_processor import SearchProcess, process_aggs
|
||||||
@@ -45,13 +43,7 @@ class ApiBaseView(APIView):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.response = {
|
self.response = {}
|
||||||
"data": False,
|
|
||||||
"config": {
|
|
||||||
"enable_cast": EnvironmentSettings.ENABLE_CAST,
|
|
||||||
"downloads": AppConfig().config["downloads"],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
self.data = {"query": {"match_all": {}}}
|
self.data = {"query": {"match_all": {}}}
|
||||||
self.status_code = False
|
self.status_code = False
|
||||||
self.context = False
|
self.context = False
|
||||||
@@ -62,12 +54,12 @@ class ApiBaseView(APIView):
|
|||||||
path = f"{self.search_base}{document_id}"
|
path = f"{self.search_base}{document_id}"
|
||||||
response, status_code = ElasticWrap(path).get()
|
response, status_code = ElasticWrap(path).get()
|
||||||
try:
|
try:
|
||||||
self.response["data"] = SearchProcess(
|
self.response = SearchProcess(
|
||||||
response, match_video_user_progress=progress_match
|
response, match_video_user_progress=progress_match
|
||||||
).process()
|
).process()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print(f"item not found: {document_id}")
|
print(f"item not found: {document_id}")
|
||||||
self.response["data"] = False
|
|
||||||
self.status_code = status_code
|
self.status_code = status_code
|
||||||
|
|
||||||
def initiate_pagination(self, request):
|
def initiate_pagination(self, request):
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ INSTALLED_APPS = [
|
|||||||
"django.contrib.humanize",
|
"django.contrib.humanize",
|
||||||
"rest_framework",
|
"rest_framework",
|
||||||
"rest_framework.authtoken",
|
"rest_framework.authtoken",
|
||||||
|
"drf_spectacular",
|
||||||
"common",
|
"common",
|
||||||
"video",
|
"video",
|
||||||
"channel",
|
"channel",
|
||||||
@@ -295,3 +296,15 @@ CORS_ALLOW_HEADERS = list(default_headers) + [
|
|||||||
# TA application settings
|
# TA application settings
|
||||||
TA_UPSTREAM = "https://github.com/tubearchivist/tubearchivist"
|
TA_UPSTREAM = "https://github.com/tubearchivist/tubearchivist"
|
||||||
TA_VERSION = "v0.5.0-unstable"
|
TA_VERSION = "v0.5.0-unstable"
|
||||||
|
|
||||||
|
# API
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
|
||||||
|
}
|
||||||
|
|
||||||
|
SPECTACULAR_SETTINGS = {
|
||||||
|
"TITLE": "Tube Archivist API",
|
||||||
|
"DESCRIPTION": "API documentation for Tube Archivist backend.",
|
||||||
|
"VERSION": TA_VERSION,
|
||||||
|
"SERVE_INCLUDE_SCHEMA": False,
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ Including another URLconf
|
|||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("api/", include("common.urls")),
|
path("api/", include("common.urls")),
|
||||||
@@ -27,5 +28,11 @@ urlpatterns = [
|
|||||||
path("api/appsettings/", include("appsettings.urls")),
|
path("api/appsettings/", include("appsettings.urls")),
|
||||||
path("api/stats/", include("stats.urls")),
|
path("api/stats/", include("stats.urls")),
|
||||||
path("api/user/", include("user.urls")),
|
path("api/user/", include("user.urls")),
|
||||||
|
path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
|
||||||
|
path(
|
||||||
|
"api/docs/",
|
||||||
|
SpectacularSwaggerView.as_view(url_name="schema"),
|
||||||
|
name="swagger-ui",
|
||||||
|
),
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
-r requirements.txt
|
-r requirements.txt
|
||||||
ipython==8.31.0
|
ipython==8.32.0
|
||||||
pre-commit==4.1.0
|
pre-commit==4.1.0
|
||||||
pylint-django==2.6.1
|
pylint-django==2.6.1
|
||||||
pylint==3.3.3
|
pylint==3.3.4
|
||||||
pytest-django==4.9.0
|
pytest-django==4.9.0
|
||||||
pytest==8.3.4
|
pytest==8.3.4
|
||||||
python-dotenv==1.0.1
|
python-dotenv==1.0.1
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ apprise==1.9.2
|
|||||||
celery==5.4.0
|
celery==5.4.0
|
||||||
django-auth-ldap==5.1.0
|
django-auth-ldap==5.1.0
|
||||||
django-celery-beat==2.7.0
|
django-celery-beat==2.7.0
|
||||||
django-cors-headers==4.6.0
|
django-cors-headers==4.7.0
|
||||||
Django==5.1.5
|
Django==5.1.6
|
||||||
djangorestframework==3.15.2
|
djangorestframework==3.15.2
|
||||||
|
drf-spectacular==0.28.0
|
||||||
Pillow==11.1.0
|
Pillow==11.1.0
|
||||||
redis==5.2.1
|
redis==5.2.1
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
ryd-client==0.0.6
|
ryd-client==0.0.6
|
||||||
uvicorn==0.34.0
|
uvicorn==0.34.0
|
||||||
whitenoise==6.8.2
|
whitenoise==6.9.0
|
||||||
yt-dlp[default]==2025.1.26
|
yt-dlp[default]==2025.1.26
|
||||||
|
|||||||
Reference in New Issue
Block a user