浏览代码

Update API to be more complete

Colin Powell 2 年之前
父节点
当前提交
cf9da39967

+ 14 - 0
vrobbler/apps/books/api/serializers.py

@@ -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__"

+ 19 - 0
vrobbler/apps/books/api/views.py

@@ -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]

+ 6 - 1
vrobbler/apps/books/models.py

@@ -8,7 +8,8 @@ from django.urls import reverse
 from django_extensions.db.models import TimeStampedModel
 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__)
 User = get_user_model()
@@ -66,3 +67,7 @@ class Book(ScrobblableMixin):
             logger.warn(f"{self} has no pages, no completion percentage")
             return 0
         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)

+ 0 - 0
vrobbler/apps/music/api/__init__.py


+ 20 - 0
vrobbler/apps/music/api/serializers.py

@@ -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__"

+ 26 - 0
vrobbler/apps/music/api/views.py

@@ -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]

+ 1 - 0
vrobbler/apps/music/serializers.py

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

+ 1 - 0
vrobbler/apps/profiles/api/__init__.py

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

+ 18 - 0
vrobbler/apps/profiles/api/serializers.py

@@ -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',)

+ 28 - 0
vrobbler/apps/profiles/api/views.py

@@ -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]

+ 0 - 0
vrobbler/apps/scrobbles/api/__init__.py


+ 33 - 0
vrobbler/apps/scrobbles/api/serializers.py

@@ -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__"

+ 49 - 0
vrobbler/apps/scrobbles/api/views.py

@@ -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)

+ 0 - 14
vrobbler/apps/scrobbles/serializers.py

@@ -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__"

+ 0 - 1
vrobbler/apps/scrobbles/urls.py

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

+ 12 - 0
vrobbler/apps/scrobbles/utils.py

@@ -1,10 +1,14 @@
 import logging
 from urllib.parse import unquote
 
+from django.contrib.auth import get_user_model
+
 from dateutil.parser import ParserError, parse
 from django.conf import settings
+from django.db import models
 
 logger = logging.getLogger(__name__)
+User = get_user_model()
 
 
 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):
             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'])
+
+
+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)

+ 4 - 14
vrobbler/apps/scrobbles/views.py

@@ -49,10 +49,7 @@ from scrobbles.scrobblers import (
     mopidy_scrobble_podcast,
     mopidy_scrobble_track,
 )
-from scrobbles.serializers import (
-    AudioScrobblerTSVImportSerializer,
-    ScrobbleSerializer,
-)
+from scrobbles.api import serializers
 from scrobbles.tasks import (
     process_koreader_import,
     process_lastfm_import,
@@ -215,15 +212,6 @@ def lastfm_import(request):
     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
 @permission_classes([IsAuthenticated])
 @api_view(['POST'])
@@ -293,7 +281,9 @@ def import_audioscrobbler_file(request):
     scrobbles_created = []
     # tsv_file = request.FILES[0]
 
-    file_serializer = AudioScrobblerTSVImportSerializer(data=request.data)
+    file_serializer = serializers.AudioScrobblerTSVImportSerializer(
+        data=request.data
+    )
     if file_serializer.is_valid():
         import_file = file_serializer.save()
         return Response(

+ 0 - 0
vrobbler/apps/videos/api/__init__.py


+ 14 - 0
vrobbler/apps/videos/api/serializers.py

@@ -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__"

+ 19 - 0
vrobbler/apps/videos/api/views.py

@@ -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]

+ 2 - 7
vrobbler/settings.py

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

+ 36 - 7
vrobbler/urls.py

@@ -3,17 +3,46 @@ from django.conf import settings
 from django.conf.urls.static import static
 from django.contrib import admin
 from django.urls import include, path
-from scrobbles import urls as scrobble_urls
-from music import urls as music_urls
-from videos import urls as video_urls
+from rest_framework import routers
+
+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 = [
+    path('api/v1/', include(router.urls)),
+    path('api/v1/auth', include("rest_framework.urls")),
     path("admin/", admin.site.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(
         'manual/imdb/',
         scrobbles_views.ManualScrobbleView.as_view(),