[scrobbles] Fix mood starting, and clean up code
This commit is contained in:
18
vrobbler/apps/moods/migrations/0002_mood_image.py
Normal file
18
vrobbler/apps/moods/migrations/0002_mood_image.py
Normal file
@ -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/"),
|
||||
),
|
||||
]
|
||||
@ -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 ""
|
||||
|
||||
@ -12,6 +12,7 @@ PLAY_AGAIN_MEDIA = {
|
||||
"videogames": "VideoGame",
|
||||
"books": "Book",
|
||||
"boardgames": "BoardGame",
|
||||
"moods": "Mood",
|
||||
}
|
||||
|
||||
MEDIA_END_PADDING_SECONDS = {
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
vrobbler/templates/moods/mood_detail.html
Normal file
39
vrobbler/templates/moods/mood_detail.html
Normal file
@ -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
vrobbler/templates/moods/mood_list.html
Normal file
42
vrobbler/templates/moods/mood_list.html
Normal file
@ -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 %}
|
||||
Reference in New Issue
Block a user