Sfoglia il codice sorgente

[scrobbles] Fix mood starting, and clean up code

Colin Powell 9 mesi fa
parent
commit
0ce19527a2

+ 18 - 0
vrobbler/apps/moods/migrations/0002_mood_image.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.2.13 on 2024-08-10 20:43
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("moods", "0001_initial"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="mood",
+            name="image",
+            field=models.ImageField(blank=True, null=True, upload_to="moods/"),
+        ),
+    ]

+ 29 - 1
vrobbler/apps/moods/models.py

@@ -1,10 +1,12 @@
 import logging
-from uuid import uuid4
 
 from django.contrib.auth import get_user_model
 from django.db import models
 from django.urls import reverse
+from imagekit.models import ImageSpecField
+from imagekit.processors import ResizeToFit
 from scrobbles.mixins import ScrobblableMixin
+
 from vrobbler.apps.scrobbles.dataclasses import MoodLogData
 
 logger = logging.getLogger(__name__)
@@ -14,6 +16,19 @@ User = get_user_model()
 
 class Mood(ScrobblableMixin):
     description = models.TextField(**BNULL)
+    image = models.ImageField(upload_to="moods/", **BNULL)
+    image_small = ImageSpecField(
+        source="thumbnail",
+        processors=[ResizeToFit(100, 100)],
+        format="JPEG",
+        options={"quality": 60},
+    )
+    image_medium = ImageSpecField(
+        source="thumbnail",
+        processors=[ResizeToFit(300, 300)],
+        format="JPEG",
+        options={"quality": 75},
+    )
 
     def __str__(self):
         if self.title:
@@ -23,6 +38,19 @@ class Mood(ScrobblableMixin):
     def get_absolute_url(self):
         return reverse("moods:mood-detail", kwargs={"slug": self.uuid})
 
+    def get_start_url(self):
+        return reverse("scrobbles:start", kwargs={"uuid": self.uuid})
+
+    @property
+    def subtitle(self) -> str:
+        return ""
+
     @property
     def logdata_cls(self):
         return MoodLogData
+
+    @property
+    def primary_image_url(self) -> str:
+        if self.image:
+            return self.image.url
+        return ""

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

@@ -12,6 +12,7 @@ PLAY_AGAIN_MEDIA = {
     "videogames": "VideoGame",
     "books": "Book",
     "boardgames": "BoardGame",
+    "moods": "Mood",
 }
 
 MEDIA_END_PADDING_SECONDS = {

+ 39 - 4
vrobbler/apps/scrobbles/mixins.py

@@ -2,12 +2,14 @@ import logging
 from typing import Optional
 from uuid import uuid4
 
+from django.apps import apps
 from django.db import models
 from django.urls import reverse
+from django.utils import timezone
 from django_extensions.db.models import TimeStampedModel
-from taggit.managers import TaggableManager
 from scrobbles.utils import get_scrobbles_for_media
-from taggit.models import TagBase, GenericTaggedItemBase
+from taggit.managers import TaggableManager
+from taggit.models import GenericTaggedItemBase, TagBase
 
 BNULL = {"blank": True, "null": True}
 
@@ -33,6 +35,7 @@ class ObjectWithGenres(GenericTaggedItemBase):
 
 class ScrobblableMixin(TimeStampedModel):
     SECONDS_TO_STALE = 1600
+    COMPLETION_PERCENT = 100
 
     uuid = models.UUIDField(default=uuid4, editable=False, **BNULL)
     title = models.CharField(max_length=255, **BNULL)
@@ -44,21 +47,53 @@ class ScrobblableMixin(TimeStampedModel):
     class Meta:
         abstract = True
 
+    def basic_scrobble_data(self, user_id) -> dict:
+        return {
+            "user_id": user_id,
+            "timestamp": timezone.now(),
+            "playback_position_seconds": 0,
+            "source": "Vrobbler",
+        }
+
+    def scrobble_for_user(self, user_id):
+        Scrobble = apps.get_model("scrobbles", "Scrobble")
+        scrobble_data = self.basic_scrobble_data(user_id)
+        logger.info(
+            "[scrobble_for_user] called",
+            extra={
+                "webpage_id": self.id,
+                "user_id": user_id,
+                "scrobble_data": scrobble_data,
+                "media_type": Scrobble.MediaType.WEBPAGE,
+            },
+        )
+        return Scrobble.create_or_update(self, user_id, scrobble_data)
+
     @property
     def primary_image_url(self) -> str:
         logger.warn(f"Not implemented yet")
         return ""
 
-    def fix_metadata(self):
+    @property
+    def logdata_cls(self) -> None:
+        logger.warn("logdata_cls() not implemented yet")
+        return None
+
+    @property
+    def subtitle(self) -> str:
+        return ""
+
+    def fix_metadata(self) -> None:
         logger.warn("fix_metadata() not implemented yet")
 
     @classmethod
-    def find_or_create(cls):
+    def find_or_create(cls) -> None:
         logger.warn("find_or_create() not implemented yet")
 
     def scrobble(self, user_id, **kwargs):
         """Given a user ID and a dictionary of data, attempts to scrobble it"""
         from scrobbles.models import Scrobble
+
         Scrobble.create_or_update(self, user_id, **kwargs)
 
 

+ 1 - 1
vrobbler/apps/scrobbles/models.py

@@ -904,7 +904,7 @@ class Scrobble(TimeStampedModel):
             .order_by("-timestamp")
             .first()
         )
-        source = scrobble_data.get("source")
+        source = scrobble_data.get("source", "Vrobbler")
         mtype = media.__class__.__name__
         mopidy_status = scrobble_data.get("mopidy_status", None)
 

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

@@ -69,12 +69,13 @@ class ScrobbleableListView(ListView):
         if not self.request.user.is_anonymous:
             queryset = queryset.annotate(
                 scrobble_count=Count("scrobble"),
-                filter=Q(user=self.request.user),
+                filter=Q(scrobble__user=self.request.user),
             ).order_by("-scrobble_count")
         else:
             queryset = queryset.annotate(
                 scrobble_count=Count("scrobble")
             ).order_by("-scrobble_count")
+        return queryset
 
 
 class ScrobbleableDetailView(DetailView):
@@ -85,8 +86,8 @@ class ScrobbleableDetailView(DetailView):
         context_data = super().get_context_data(**kwargs)
         context_data["scrobbles"] = list()
         if not self.request.user.is_anonymous:
-            context_data["scrobbles"] = self.object.scrobbles(
-                self.request.user
+            context_data["scrobbles"] = self.object.scrobble_set.filter(
+                user=self.request.user
             )
         return context_data
 
@@ -429,6 +430,10 @@ def import_audioscrobbler_file(request):
 @permission_classes([IsAuthenticated])
 @api_view(["GET"])
 def scrobble_start(request, uuid):
+    logger.info(
+        "[scrobble_start] called",
+        extra={"request": request, "uuid": uuid},
+    )
     user = request.user
     success_url = request.META.get("HTTP_REFERER")
 
@@ -443,19 +448,17 @@ def scrobble_start(request, uuid):
             break
 
     if not media_obj:
+        logger.info(
+            "[scrobble_start] media object not found",
+            extra={"uuid": uuid, "user_id": user.id},
+        )
+        # TODO Log that we couldn't find a media obj to scrobble
         return
 
     scrobble = None
     user_id = request.user.id
     if media_obj:
-        if media_obj.__class__.__name__ == Scrobble.MediaType.BOOK:
-            scrobble = manual_scrobble_book(media_obj.openlibrary_id, user_id)
-        if media_obj.__class__.__name__ == Scrobble.MediaType.VIDEO_GAME:
-            scrobble = manual_scrobble_video_game(media_obj.hltb_id, user_id)
-        if media_obj.__class__.__name__ == Scrobble.MediaType.BOARD_GAME:
-            scrobble = manual_scrobble_board_game(media_obj.bggeek_id, user_id)
-        if media_obj.__class__.__name__ == Scrobble.MediaType.WEBPAGE:
-            scrobble = manual_scrobble_webpage(media_obj.url, user_id)
+        media_obj.scrobble_for_user(user_id)
 
     if scrobble:
         messages.add_message(

+ 22 - 0
vrobbler/apps/webpages/models.py

@@ -89,6 +89,28 @@ class WebPage(ScrobblableMixin):
     def subtitle(self):
         return self.domain
 
+    @property
+    def primary_image_url(self) -> str:
+        # TODO Figure out how to add a preview?
+        return ""
+
+    def scrobble_for_user(self, user_id):
+        Scrobble = apps.get_model("scrobbles", "Scrobble")
+        scrobble_data = self.basic_scrobble_data(user_id)
+        logger.info(
+            "[scrobble_for_user] called for webpage",
+            extra={
+                "webpage_id": self.id,
+                "user_id": user_id,
+                "scrobble_data": scrobble_data,
+                "media_type": Scrobble.MediaType.WEBPAGE,
+            },
+        )
+        scrobble = Scrobble.create_or_update(self, user_id, scrobble_data)
+        # TODO Possibly make this async?
+        scrobble.push_to_archivebox()
+        return scrobble
+
     def scrobbles(self, user):
         Scrobble = apps.get_model("scrobbles", "Scrobble")
         return Scrobble.objects.filter(user=user, web_page=self).order_by(

+ 39 - 0
vrobbler/templates/moods/mood_detail.html

@@ -0,0 +1,39 @@
+{% extends "base_list.html" %}
+{% load static %}
+
+{% block title %}{{object.title}}{% endblock %}
+
+{% block lists %}
+<div class="row webpage">
+    <div class="webpage-metadata">
+      <p>{{object.description}}</p>
+    </div>
+    {% if object.extract %}
+    <div class="webpage-body" id="article">
+      {{object.extract|linebreaks}}
+    </div>
+    {% endif %}
+    <hr/>
+</div>
+<div class="row">
+    <div class="col-md">
+        <h3>Last scrobbles</h3>
+        <div class="table-responsive">
+            <table class="table table-striped table-sm">
+            <thead>
+                <tr>
+                    <th scope="col">Date</th>
+                </tr>
+            </thead>
+            <tbody>
+                {% for scrobble in object.scrobble_set.all %}
+                <tr>
+                    <td>{{scrobble.timestamp}}</td>
+                </tr>
+                {% endfor %}
+            </tbody>
+            </table>
+        </div>
+    </div>
+</div>
+{% endblock %}

+ 42 - 0
vrobbler/templates/moods/mood_list.html

@@ -0,0 +1,42 @@
+{% extends "base_list.html" %}
+
+{% block title %}Moods{% endblock %}
+
+{% block head_extra %}
+<style>
+ dl { width: 210px; float:left; margin-right: 10px; }
+ dt a { color:white; text-decoration: none; font-size:smaller; }
+ img { height:200px; width: 200px; object-fit: cover; }
+ dd .right { float:right; }
+</style>
+{% endblock  %}
+
+{% block lists %}
+<div class="row">
+
+    <div class="col-md">
+        <div class="table-responsive">
+            <table class="table table-striped table-sm">
+                <thead>
+                    <tr>
+                        <th scope="col">Title</th>
+                        <th scope="col">Scrobbles</th>
+                        <th scope="col">Start</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    {% for mood in object_list %}
+                    <tr>
+                        <td><a href="{{mood.get_absolute_url}}">{{mood}}</a></td>
+                        {% if request.user.is_authenticated %}
+                        <td>{{mood.scrobble_count}}</td>
+                        <td><a type="button" class="btn btn-sm btn-primary" href="{{mood.get_start_url}}">Scrobble</a></td>
+                        {% endif %}
+                    </tr>
+                    {% endfor %}
+                </tbody>
+            </table>
+        </div>
+    </div>
+</div>
+{% endblock %}