Update API to be more complete

This commit is contained in:
2023-02-20 17:08:54 -05:00
parent 2e98850494
commit cf9da39967
22 changed files with 302 additions and 44 deletions

View File

@ -0,0 +1,14 @@
from books.models import Author, Book
from rest_framework import serializers
class AuthorSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Author
fields = "__all__"
class BookSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Book
fields = "__all__"

View File

@ -0,0 +1,19 @@
from rest_framework import permissions, viewsets
from books.api.serializers import (
AuthorSerializer,
BookSerializer,
)
from books.models import Author, Book
class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all().order_by('-created')
serializer_class = AuthorSerializer
permission_classes = [permissions.IsAuthenticated]
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all().order_by('-created')
serializer_class = BookSerializer
permission_classes = [permissions.IsAuthenticated]

View File

@ -8,7 +8,8 @@ from django.urls import reverse
from django_extensions.db.models import TimeStampedModel from django_extensions.db.models import TimeStampedModel
from scrobbles.mixins import ScrobblableMixin from scrobbles.mixins import ScrobblableMixin
from vrobbler.apps.books.utils import lookup_book_from_openlibrary from books.utils import lookup_book_from_openlibrary
from scrobbles.utils import get_scrobbles_for_media
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
User = get_user_model() User = get_user_model()
@ -66,3 +67,7 @@ class Book(ScrobblableMixin):
logger.warn(f"{self} has no pages, no completion percentage") logger.warn(f"{self} has no pages, no completion percentage")
return 0 return 0
return int(self.pages * (self.COMPLETION_PERCENT / 100)) return int(self.pages * (self.COMPLETION_PERCENT / 100))
def progress_for_user(self, user: User) -> int:
last_scrobble = get_scrobbles_for_media(self, user).last()
return int((last_scrobble.book_pages_read / self.pages) * 100)

View File

View File

@ -0,0 +1,20 @@
from music.models import Album, Artist, Track
from rest_framework import serializers
class ArtistSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Artist
fields = "__all__"
class AlbumSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Album
fields = "__all__"
class TrackSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Track
fields = "__all__"

View File

@ -0,0 +1,26 @@
from rest_framework import permissions, viewsets
from music.api.serializers import (
TrackSerializer,
ArtistSerializer,
AlbumSerializer,
)
from music.models import Artist, Album, Track
class ArtistViewSet(viewsets.ModelViewSet):
queryset = Artist.objects.all().order_by('-created')
serializer_class = ArtistSerializer
permission_classes = [permissions.IsAuthenticated]
class AlbumViewSet(viewsets.ModelViewSet):
queryset = Album.objects.all().order_by('-created')
serializer_class = AlbumSerializer
permission_classes = [permissions.IsAuthenticated]
class TrackViewSet(viewsets.ModelViewSet):
queryset = Track.objects.all().order_by('-created')
serializer_class = TrackSerializer
permission_classes = [permissions.IsAuthenticated]

View File

@ -0,0 +1 @@
#!/usr/bin/env python3

View File

@ -0,0 +1 @@
#!/usr/bin/env python3

View File

@ -0,0 +1,18 @@
from django.contrib.auth import get_user_model
from rest_framework import serializers
User = get_user_model()
from profiles.models import UserProfile
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
exclude = ('password',)
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserProfile
exclude = ('lastfm_password',)

View File

@ -0,0 +1,28 @@
from django.contrib.auth import get_user_model
from rest_framework import permissions, viewsets
from profiles.api.serializers import UserSerializer, UserProfileSerializer
from profiles.models import UserProfile
User = get_user_model()
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
class UserProfileViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = UserProfile.objects.all().order_by('-created')
serializer_class = UserProfileSerializer
permission_classes = [permissions.IsAuthenticated]

View File

View File

@ -0,0 +1,33 @@
from rest_framework import serializers
from scrobbles.models import (
AudioScrobblerTSVImport,
KoReaderImport,
LastFmImport,
Scrobble,
)
class ScrobbleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Scrobble
fields = "__all__"
class KoReaderImportSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = KoReaderImport
fields = "__all__"
class AudioScrobblerTSVImportSerializer(
serializers.HyperlinkedModelSerializer
):
class Meta:
model = AudioScrobblerTSVImport
fields = "__all__"
class LastFmImportSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = LastFmImport
fields = "__all__"

View File

@ -0,0 +1,49 @@
from rest_framework import permissions, viewsets
from scrobbles.api.serializers import (
AudioScrobblerTSVImportSerializer,
KoReaderImportSerializer,
LastFmImportSerializer,
ScrobbleSerializer,
)
from scrobbles.models import (
AudioScrobblerTSVImport,
KoReaderImport,
Scrobble,
LastFmImport,
)
class ScrobbleViewSet(viewsets.ModelViewSet):
queryset = Scrobble.objects.all().order_by('-timestamp')
serializer_class = ScrobbleSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return super().get_queryset().filter(user=self.request.user)
class KoReaderImportViewSet(viewsets.ModelViewSet):
queryset = KoReaderImport.objects.all().order_by('-created')
serializer_class = KoReaderImportSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return super().get_queryset().filter(user=self.request.user)
class AudioScrobblerTSVImportViewSet(viewsets.ModelViewSet):
queryset = AudioScrobblerTSVImport.objects.all().order_by('-created')
serializer_class = AudioScrobblerTSVImportSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return super().get_queryset().filter(user=self.request.user)
class LastFmImportViewSet(viewsets.ModelViewSet):
queryset = LastFmImport.objects.all().order_by('-created')
serializer_class = LastFmImportSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
return super().get_queryset().filter(user=self.request.user)

View File

@ -1,14 +0,0 @@
from rest_framework import serializers
from scrobbles.models import Scrobble, AudioScrobblerTSVImport
class AudioScrobblerTSVImportSerializer(serializers.ModelSerializer):
class Meta:
model = AudioScrobblerTSVImport
fields = ('tsv_file',)
class ScrobbleSerializer(serializers.ModelSerializer):
class Meta:
model = Scrobble
fields = "__all__"

View File

@ -4,7 +4,6 @@ from scrobbles import views
app_name = 'scrobbles' app_name = 'scrobbles'
urlpatterns = [ urlpatterns = [
path('', views.scrobble_endpoint, name='api-list'),
path('finish/<slug:uuid>', views.scrobble_finish, name='finish'), path('finish/<slug:uuid>', views.scrobble_finish, name='finish'),
path('cancel/<slug:uuid>', views.scrobble_cancel, name='cancel'), path('cancel/<slug:uuid>', views.scrobble_cancel, name='cancel'),
path( path(

View File

@ -1,10 +1,14 @@
import logging import logging
from urllib.parse import unquote from urllib.parse import unquote
from django.contrib.auth import get_user_model
from dateutil.parser import ParserError, parse from dateutil.parser import ParserError, parse
from django.conf import settings from django.conf import settings
from django.db import models
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
User = get_user_model()
def convert_to_seconds(run_time: str) -> int: def convert_to_seconds(run_time: str) -> int:
@ -103,3 +107,11 @@ def check_scrobble_for_finish(
if getattr(settings, "KEEP_DETAILED_SCROBBLE_LOGS", False): if getattr(settings, "KEEP_DETAILED_SCROBBLE_LOGS", False):
scrobble.scrobble_log += f"\n{str(scrobble.timestamp)} - {scrobble.playback_position} - {str(scrobble.playback_position_ticks)} - {str(scrobble.percent_played)}%" scrobble.scrobble_log += f"\n{str(scrobble.timestamp)} - {scrobble.playback_position} - {str(scrobble.playback_position_ticks)} - {str(scrobble.percent_played)}%"
scrobble.save(update_fields=['scrobble_log']) scrobble.save(update_fields=['scrobble_log'])
def get_scrobbles_for_media(media_obj, user: User) -> models.QuerySet:
from scrobbles.models import Scrobble
if media_obj.__class__.__name__ == 'Book':
media_query = models.Q(book=media_obj)
return Scrobble.objects.filter(media_query, user=user)

View File

@ -49,10 +49,7 @@ from scrobbles.scrobblers import (
mopidy_scrobble_podcast, mopidy_scrobble_podcast,
mopidy_scrobble_track, mopidy_scrobble_track,
) )
from scrobbles.serializers import ( from scrobbles.api import serializers
AudioScrobblerTSVImportSerializer,
ScrobbleSerializer,
)
from scrobbles.tasks import ( from scrobbles.tasks import (
process_koreader_import, process_koreader_import,
process_lastfm_import, process_lastfm_import,
@ -215,15 +212,6 @@ def lastfm_import(request):
return HttpResponseRedirect(success_url) return HttpResponseRedirect(success_url)
@csrf_exempt
@api_view(['GET'])
def scrobble_endpoint(request):
"""List all Scrobbles, or create a new Scrobble"""
scrobble = Scrobble.objects.all()
serializer = ScrobbleSerializer(scrobble, many=True)
return Response(serializer.data)
@csrf_exempt @csrf_exempt
@permission_classes([IsAuthenticated]) @permission_classes([IsAuthenticated])
@api_view(['POST']) @api_view(['POST'])
@ -293,7 +281,9 @@ def import_audioscrobbler_file(request):
scrobbles_created = [] scrobbles_created = []
# tsv_file = request.FILES[0] # tsv_file = request.FILES[0]
file_serializer = AudioScrobblerTSVImportSerializer(data=request.data) file_serializer = serializers.AudioScrobblerTSVImportSerializer(
data=request.data
)
if file_serializer.is_valid(): if file_serializer.is_valid():
import_file = file_serializer.save() import_file = file_serializer.save()
return Response( return Response(

View File

View File

@ -0,0 +1,14 @@
from videos.models import Series, Video
from rest_framework import serializers
class SeriesSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Series
fields = "__all__"
class VideoSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Video
fields = "__all__"

View File

@ -0,0 +1,19 @@
from rest_framework import permissions, viewsets
from videos.api.serializers import (
SeriesSerializer,
VideoSerializer,
)
from videos.models import Series, Video
class SeriesViewSet(viewsets.ModelViewSet):
queryset = Series.objects.all().order_by('-created')
serializer_class = SeriesSerializer
permission_classes = [permissions.IsAuthenticated]
class VideoViewSet(viewsets.ModelViewSet):
queryset = Video.objects.all().order_by('-created')
serializer_class = VideoSerializer
permission_classes = [permissions.IsAuthenticated]

View File

@ -174,19 +174,14 @@ AUTHENTICATION_BACKENDS = [
REST_FRAMEWORK = { REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.AllowAny",), "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.AllowAny",),
'DEFAULT_AUTHENTICATION_CLASSES': [ 'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.SessionAuthentication',
], ],
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"DEFAULT_FILTER_BACKENDS": [ "DEFAULT_FILTER_BACKENDS": [
"django_filters.rest_framework.DjangoFilterBackend" "django_filters.rest_framework.DjangoFilterBackend"
], ],
'DEFAULT_PARSER_CLASSES': [ "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
'rest_framework.parsers.JSONParser', "PAGE_SIZE": 200,
],
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'vrobbler.negotiation.IgnoreClientContentNegotiation',
"PAGE_SIZE": 100,
} }
LOGIN_REDIRECT_URL = "/" LOGIN_REDIRECT_URL = "/"

View File

@ -3,17 +3,46 @@ from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
from django.urls import include, path from django.urls import include, path
from scrobbles import urls as scrobble_urls from rest_framework import routers
from music import urls as music_urls
from videos import urls as video_urls from vrobbler.apps.books.api.views import AuthorViewSet, BookViewSet
from vrobbler.apps.music import urls as music_urls
from vrobbler.apps.music.api.views import (
AlbumViewSet,
ArtistViewSet,
TrackViewSet,
)
from vrobbler.apps.profiles.api.views import UserProfileViewSet, UserViewSet
from vrobbler.apps.scrobbles import urls as scrobble_urls
from vrobbler.apps.scrobbles.api.views import (
AudioScrobblerTSVImportViewSet,
KoReaderImportViewSet,
LastFmImportViewSet,
ScrobbleViewSet,
)
from vrobbler.apps.videos import urls as video_urls
from vrobbler.apps.videos.api.views import SeriesViewSet, VideoViewSet
router = routers.DefaultRouter()
router.register(r'scrobbles', ScrobbleViewSet)
router.register(r'lastfm-imports', LastFmImportViewSet)
router.register(r'tsv-imports', AudioScrobblerTSVImportViewSet)
router.register(r'koreader-imports', KoReaderImportViewSet)
router.register(r'artist', ArtistViewSet)
router.register(r'album', AlbumViewSet)
router.register(r'tracks', TrackViewSet)
router.register(r'series', SeriesViewSet)
router.register(r'videos', VideoViewSet)
router.register(r'authors', AuthorViewSet)
router.register(r'books', BookViewSet)
router.register(r'users', UserViewSet)
router.register(r'user_profiles', UserProfileViewSet)
urlpatterns = [ urlpatterns = [
path('api/v1/', include(router.urls)),
path('api/v1/auth', include("rest_framework.urls")),
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("accounts/", include("allauth.urls")), path("accounts/", include("allauth.urls")),
# path("api-auth/", include("rest_framework.urls")),
# path("movies/", include(movies, namespace="movies")),
# path("shows/", include(shows, namespace="shows")),
path("api/v1/scrobbles/", include(scrobble_urls, namespace="scrobbles")),
path( path(
'manual/imdb/', 'manual/imdb/',
scrobbles_views.ManualScrobbleView.as_view(), scrobbles_views.ManualScrobbleView.as_view(),