This commit has no recorded session.
diff
commit 948fbc19bfd70ec1dabf181a57e4c9e233e554d6Author: Colin Powell <colin@unbl.ink>Date: Sun Jun 21 23:00:09 2026 -0400 [templates] Add HTMX support to Now Playing@@ -88,7 +88,7 @@ fetching and simple saving. *** Metadata sources **** Scraper -* Backlog [3/26] :vrobbler:project:personal:+* Backlog [4/26] :vrobbler:project:personal: ** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :bug:music:scrobbles: :PROPERTIES: :ID: 702462cf-d54b-48c6-8a7c-78b8de751deb@@ -604,7 +604,7 @@ independent of the email flow it was originally creatdd for ** TODO [#B] Is there way to create unique slugs for media instances :media_types: -** STRT [#B] Use HTMx to update the Now Playing widget :feature:templates:+** DONE [#B] Use HTMx to update the Now Playing widget :feature:templates: :PROPERTIES: :ID: 5f5631fc-9ee1-d5a5-d0f8-94fea6fbbfa4 :END:@@ -40,6 +40,7 @@ class UserProfileForm(forms.ModelForm): "enable_public_widgets", "widget_custom_css", "home_scrobble_limit",+ "live_now_playing", "weigh_in_units", ] widgets = {@@ -0,0 +1,18 @@+# Generated by Django 4.2.29 on 2026-06-22 02:44++from django.db import migrations, models+++class Migration(migrations.Migration):++ dependencies = [+ ("profiles", "0038_userprofile_media_type_visibility"),+ ]++ operations = [+ migrations.AddField(+ model_name="userprofile",+ name="live_now_playing",+ field=models.BooleanField(default=False),+ ),+ ]@@ -98,6 +98,8 @@ class UserProfile(TimeStampedModel): home_scrobble_limit = models.IntegerField(default=20) + live_now_playing = models.BooleanField(default=False)+ weigh_in_units = models.CharField( max_length=16, choices=WeighUnit.choices,@@ -5,6 +5,7 @@ from tasks.webhooks import EmacsWebhookView, TodoistWebhookView app_name = "scrobbles" urlpatterns = [+ path("now-playing/", views.NowPlayingPartialView.as_view(), name="now-playing-partial"), path("calendar/", views.ScrobbleCalendarView.as_view(), name="calendar"), path("search/", views.ScrobbleSearchView.as_view(), name="search"), path("status/", views.ScrobbleStatusView.as_view(), name="status"),@@ -361,6 +361,25 @@ class RecentScrobbleList(ListView): return Scrobble.objects.all().order_by("-timestamp") +class NowPlayingPartialView(LoginRequiredMixin, TemplateView):+ template_name = "scrobbles/_now_playing.html"++ def get_context_data(self, **kwargs):+ ctx = super().get_context_data(**kwargs)+ from scrobbles.constants import EXCLUDE_FROM_NOW_PLAYING++ ctx["now_playing_list"] = list(+ Scrobble.objects.filter(+ in_progress=True,+ is_paused=False,+ user=self.request.user,+ )+ .exclude(media_type__in=EXCLUDE_FROM_NOW_PLAYING)+ .select_related("track", "video", "podcast_episode")+ )+ return ctx++ class ScrobbleListView(LoginRequiredMixin, ListView): model = Scrobble paginate_by = 100@@ -0,0 +1,29 @@+# Generated by Django 4.2.29 on 2026-06-22 02:44++from django.db import migrations, models+++class Migration(migrations.Migration):++ dependencies = [+ (+ "trends",+ "0002_alter_trendresult_unique_together_trendresult_period_and_more",+ ),+ ]++ operations = [+ migrations.AlterField(+ model_name="trendresult",+ name="period",+ field=models.CharField(+ choices=[+ ("last_30", "Last 30 days"),+ ("last_90", "Last 90 days"),+ ("last_year", "Last year"),+ ],+ default="last_30",+ max_length=20,+ ),+ ),+ ]@@ -311,26 +311,13 @@ {% endblock %} {% if now_playing_list and user.is_authenticated %}- <ul>- <b>Now playing</b>- {% for scrobble in now_playing_list %}- <div class="now-playing">- {% if scrobble.media_obj.primary_image_url %}<div style="float:left;padding-right:10px;padding-bottom:10px;"><img src="{{scrobble.media_obj.primary_image_url}}" /></div>{% endif %}- <p><a href="{{scrobble.get_absolute_url}}">{{scrobble.media_obj}}</a></p>- {% if scrobble.logdata %}{% if scrobble.logdata.title%}<p><em>{{scrobble.logdata.title}}</em></p>{% endif %}{% endif %}- <p><small>{{scrobble.local_timestamp|naturaltime}} from {{scrobble.source}}</small></p>- <div class="progress-bar" style="margin-right:5px;">- <span class="progress-bar-fill" style="width: {{scrobble.percent_played}}%;"></span>- </div>- <p class="action-buttons">- <a href="{% url "scrobbles:cancel" scrobble.id %}">Cancel</a>- <a class="right" href="{% url "scrobbles:finish" scrobble.id %}">Finish</a>- </p>- {% if not forloop.last %}<hr/>{% endif %}- </div>- {% endfor %}- </ul>- {% if now_playing_list|length > 1 %}<hr/>{% endif %}+ {% if user.profile.live_now_playing %}+ <div id="now-playing-container" hx-get="{% url 'scrobbles:now-playing-partial' %}" hx-trigger="every 7s" hx-swap="innerHTML">+ {% include "scrobbles/_now_playing.html" %}+ </div>+ {% else %}+ {% include "scrobbles/_now_playing.html" %}+ {% endif %} {% endif %} {% if active_imports %}@@ -0,0 +1,21 @@+{% load humanize %}+<ul>+ <b>Now playing</b>+ {% for scrobble in now_playing_list %}+ <div class="now-playing">+ {% if scrobble.media_obj.primary_image_url %}<div style="float:left;padding-right:10px;padding-bottom:10px;"><img src="{{scrobble.media_obj.primary_image_url}}" /></div>{% endif %}+ <p><a href="{{scrobble.get_absolute_url}}">{{scrobble.media_obj}}</a></p>+ {% if scrobble.logdata %}{% if scrobble.logdata.title%}<p><em>{{scrobble.logdata.title}}</em></p>{% endif %}{% endif %}+ <p><small>{{scrobble.local_timestamp|naturaltime}} from {{scrobble.source}}</small></p>+ <div class="progress-bar" style="margin-right:5px;">+ <span class="progress-bar-fill" style="width: {{scrobble.percent_played}}%;"></span>+ </div>+ <p class="action-buttons">+ <a href="{% url "scrobbles:cancel" scrobble.id %}">Cancel</a>+ <a class="right" href="{% url "scrobbles:finish" scrobble.id %}">Finish</a>+ </p>+ {% if not forloop.last %}<hr/>{% endif %}+ </div>+ {% endfor %}+</ul>+{% if now_playing_list|length > 1 %}<hr/>{% endif %}