Add ability to manage long plays

This commit is contained in:
2023-03-11 14:11:31 -05:00
parent 6e03cf5075
commit bb2a80e2aa
9 changed files with 179 additions and 38 deletions

View File

@ -12,7 +12,7 @@ from django.core.files.base import ContentFile
from django.db import models
from django.urls import reverse
from django_extensions.db.models import TimeStampedModel
from scrobbles.mixins import ScrobblableMixin
from scrobbles.mixins import LongPlayScrobblableMixin, ScrobblableMixin
from scrobbles.utils import get_scrobbles_for_media
logger = logging.getLogger(__name__)
@ -55,7 +55,7 @@ class Author(TimeStampedModel):
self.headshot.save(fname, ContentFile(r.content), save=True)
class Book(ScrobblableMixin):
class Book(LongPlayScrobblableMixin):
COMPLETION_PERCENT = getattr(settings, "BOOK_COMPLETION_PERCENT", 95)
AVG_PAGE_READING_SECONDS = getattr(
settings, "AVERAGE_PAGE_READING_SECONDS", 60
@ -78,6 +78,16 @@ class Book(ScrobblableMixin):
def __str__(self):
return f"{self.title} by {self.author}"
@property
def subtitle(self):
return f" by {self.author}"
def get_start_url(self):
return reverse("scrobbles:start", kwargs={"uuid": self.uuid})
def get_absolute_url(self):
return reverse("books:book_detail", kwargs={"slug": self.uuid})
def fix_metadata(self, force_update=False):
if not self.openlibrary_id or force_update:
book_dict = lookup_book_from_openlibrary(self.title, self.author)
@ -130,9 +140,6 @@ class Book(ScrobblableMixin):
def author(self):
return self.authors.first()
def get_absolute_url(self):
return reverse("books:book_detail", kwargs={"slug": self.uuid})
@property
def pages_for_completion(self) -> int:
if not self.pages:

View File

@ -1,9 +1,11 @@
import logging
from typing import Dict
from typing import Optional
from uuid import uuid4
from django.db import models
from django.urls import reverse
from django_extensions.db.models import TimeStampedModel
from scrobbles.utils import get_scrobbles_for_media
BNULL = {"blank": True, "null": True}
@ -27,3 +29,19 @@ class ScrobblableMixin(TimeStampedModel):
@classmethod
def find_or_create(cls):
logger.warn("find_or_create() not implemented yet")
class LongPlayScrobblableMixin(ScrobblableMixin):
class Meta:
abstract = True
def get_longplay_finish_url(self):
return reverse("scrobbles:longplay-finish", kwargs={"uuid": self.uuid})
def last_long_play_scrobble_for_user(self, user) -> Optional["Scrobble"]:
return (
get_scrobbles_for_media(self, user)
.filter(long_play_complete=False)
.order_by("-timestamp")
.first()
)

View File

@ -222,21 +222,11 @@ def manual_scrobble_video_game(data_dict: dict, user_id: Optional[int]):
def manual_scrobble_book(data_dict: dict, user_id: Optional[int]):
book = Book.find_or_create(data_dict)
last_scrobble = Scrobble.objects.filter(
book=book,
user_id=user_id,
played_to_completion=True,
long_play_complete=False,
).last()
start_playback_position = 0
if last_scrobble:
start_playback_position = last_scrobble.playback_position or 0
scrobble_dict = {
"user_id": user_id,
"timestamp": timezone.now(),
"playback_position_ticks": int(start_playback_position) * 1000,
"playback_position": start_playback_position,
"playback_position_ticks": None,
"playback_position": 0,
"source": "Vrobbler",
"long_play_complete": False,
}

View File

@ -19,8 +19,14 @@ urlpatterns = [
views.KoReaderImportCreateView.as_view(),
name="koreader-file-upload",
),
path("finish/<slug:uuid>", views.scrobble_finish, name="finish"),
path("cancel/<slug:uuid>", views.scrobble_cancel, name="cancel"),
path("start/<slug:uuid>/", views.scrobble_start, name="start"),
path("finish/<slug:uuid>/", views.scrobble_finish, name="finish"),
path("cancel/<slug:uuid>/", views.scrobble_cancel, name="cancel"),
path(
"long-play-finish/<slug:uuid>/",
views.scrobble_longplay_finish,
name="longplay-finish",
),
path(
"upload/",
views.AudioScrobblerImportCreateView.as_view(),

View File

@ -4,6 +4,7 @@ import logging
from datetime import datetime, timedelta
import pytz
from django.apps import apps
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
@ -31,6 +32,7 @@ from scrobbles.api import serializers
from scrobbles.constants import (
JELLYFIN_AUDIO_ITEM_TYPES,
JELLYFIN_VIDEO_ITEM_TYPES,
LONG_PLAY_MEDIA,
)
from scrobbles.export import export_scrobbles
from scrobbles.forms import ExportScrobbleForm, ScrobbleForm
@ -60,8 +62,8 @@ from sports.thesportsdb import lookup_event_from_thesportsdb
from videogames.howlongtobeat import lookup_game_from_hltb
from videos.imdb import lookup_video_from_imdb
from vrobbler.apps.books.openlibrary import lookup_book_from_openlibrary
from vrobbler.apps.scrobbles.utils import (
from books.openlibrary import lookup_book_from_openlibrary
from scrobbles.utils import (
get_long_plays_completed,
get_long_plays_in_progress,
)
@ -388,6 +390,84 @@ def import_audioscrobbler_file(request):
)
@permission_classes([IsAuthenticated])
@api_view(["GET"])
def scrobble_start(request, uuid):
user = request.user
success_url = request.META.get("HTTP_REFERER")
if not user.is_authenticated:
return HttpResponseRedirect(success_url)
media_obj = None
for app, model in LONG_PLAY_MEDIA.items():
media_model = apps.get_model(app_label=app, model_name=model)
media_obj = media_model.objects.filter(uuid=uuid).first()
if media_obj:
break
if not media_obj:
return
scrobble = None
user_id = request.user.id
if media_obj and media_obj.__class__.__name__ == "Book":
data_dict = {
"title": media_obj.title,
"author": media_obj.author.name,
}
scrobble = manual_scrobble_book(data_dict, user_id)
if media_obj and media_obj.__class__.__name__ == "VideoGame":
data_dict = {"hltb_id": media_obj.hltb_id}
scrobble = manual_scrobble_video_game(data_dict, user_id)
if scrobble:
messages.add_message(
request,
messages.SUCCESS,
f"Scrobble of {scrobble.media_obj} started.",
)
else:
messages.add_message(
request, messages.ERROR, f"Media with uuid {uuid} not found."
)
return HttpResponseRedirect(success_url)
@api_view(["GET"])
def scrobble_longplay_finish(request, uuid):
user = request.user
success_url = request.META.get("HTTP_REFERER")
if not user.is_authenticated:
return HttpResponseRedirect(success_url)
media_obj = None
for app, model in LONG_PLAY_MEDIA.items():
media_model = apps.get_model(app_label=app, model_name=model)
media_obj = media_model.objects.filter(uuid=uuid).first()
if media_obj:
break
if not media_obj:
return
last_scrobble = media_obj.last_long_play_scrobble_for_user(user)
if last_scrobble and last_scrobble.long_play_complete == False:
last_scrobble.long_play_complete = True
last_scrobble.save(update_fields=["long_play_complete"])
messages.add_message(
request,
messages.SUCCESS,
f"Long play of {media_obj} finished.",
)
else:
messages.add_message(
request, messages.ERROR, f"Media with uuid {uuid} not found."
)
return HttpResponseRedirect(success_url)
@permission_classes([IsAuthenticated])
@api_view(["GET"])
def scrobble_finish(request, uuid):

View File

@ -1,5 +1,4 @@
import logging
from typing import Optional
from uuid import uuid4
from django.conf import settings
@ -7,7 +6,7 @@ from django.contrib.auth import get_user_model
from django.db import models
from django.urls import reverse
from django_extensions.db.models import TimeStampedModel
from scrobbles.mixins import ScrobblableMixin
from scrobbles.mixins import LongPlayScrobblableMixin
from scrobbles.utils import get_scrobbles_for_media
from videogames.igdb import lookup_game_id_from_gdb
@ -46,7 +45,7 @@ class VideoGameCollection(TimeStampedModel):
)
class VideoGame(ScrobblableMixin):
class VideoGame(LongPlayScrobblableMixin):
COMPLETION_PERCENT = getattr(settings, "GAME_COMPLETION_PERCENT", 100)
FIELDS_FROM_IGDB = [
@ -89,6 +88,10 @@ class VideoGame(ScrobblableMixin):
def __str__(self):
return self.title
@property
def subtitle(self):
return f" On {self.platforms.first()}"
def get_absolute_url(self):
return reverse(
"videogames:videogame_detail", kwargs={"slug": self.uuid}
@ -101,6 +104,9 @@ class VideoGame(ScrobblableMixin):
slug = self.title.lower().replace(" ", "-")
return f"https://igdb.com/games/{slug}"
def get_start_url(self):
return reverse("scrobbles:start", kwargs={"uuid": self.uuid})
def save(self, **kwargs):
super().save(**kwargs)
self.fix_metadata()