This commit has no recorded session.
diff
commit 042a3eb737936082a56598413bff174f12714d5fAuthor: Colin Powell <colin@unbl.ink>Date: Mon Jun 15 15:26:46 2026 -0400 [templates] Add aggregate data@@ -88,7 +88,7 @@ fetching and simple saving. *** Metadata sources **** Scraper -* Backlog [0/20] :vrobbler:project:personal:+* Backlog [1/21] :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@@ -594,7 +594,18 @@ named constants for maintainability. - ~vrobbler/apps/webpages/models.py~ (line 290) -- ="url"= - ~vrobbler/apps/scrobbles/importers/tsv.py~ (line 55) -- ="S"= completion status -** TODO [#C] Show time per scrobble in long play lists and total time playing :templates:longplay:scrobbles:+** DONE [#C] Show time per scrobble in long play lists and total time playing :templates:longplay:scrobbles:+:PROPERTIES:+:ID: b3d16230-8ec5-46db-b166-59e98d0ee06c+:END:++*** Description++Long play time should be show in the table of scrobbles on a media detail page.+The total time spent in a long play that's either no completed yet or completed+should be displayed as well. If completed, the date finished should be shown as+well.+ * Version 52.0 [5/5] ** DONE [#B] Allow marking media as long play complete from detail page :templates:scrobbles:longplay: :PROPERTIES:@@ -206,6 +206,19 @@ class ScrobbleableDetailView(ChartContextMixin, DetailView): context_data["scrobbles"] = page_obj.object_list context_data["is_paginated"] = paginator.num_pages > 1 + media = self.object+ if hasattr(media, "is_long_play_media") and media.is_long_play_media():+ qs = media.scrobble_set.filter(user=self.request.user)+ completed = qs.filter(long_play_complete=True).order_by("-timestamp").first()+ if completed and completed.long_play_seconds:+ context_data["long_play_total_seconds"] = completed.long_play_seconds+ context_data["long_play_finished_date"] = completed.timestamp+ else:+ latest_finished = qs.filter(played_to_completion=True).order_by("-timestamp").first()+ if latest_finished and latest_finished.long_play_seconds:+ context_data["long_play_total_seconds"] = latest_finished.long_play_seconds+ context_data["long_play_finished_date"] = None+ return context_data @@ -1297,6 +1310,24 @@ class ScrobbleDetailView(DetailView): else: context["has_mopidy_uri"] = False + if self.object.is_long_play and fk_field:+ all_scrobbles = Scrobble.objects.filter(+ user=user, **{fk_field: media_obj}+ )+ completed = all_scrobbles.filter(+ long_play_complete=True+ ).order_by("-timestamp").first()+ if completed and completed.long_play_seconds:+ context["long_play_total_seconds"] = completed.long_play_seconds+ context["long_play_finished_date"] = completed.timestamp+ else:+ latest_finished = all_scrobbles.filter(+ played_to_completion=True+ ).order_by("-timestamp").first()+ if latest_finished and latest_finished.long_play_seconds:+ context["long_play_total_seconds"] = latest_finished.long_play_seconds+ context["long_play_finished_date"] = None+ return context @@ -6,6 +6,7 @@ <tr> <th scope="col">Latest</th> <th scope="col">Title</th>+ <th scope="col">Time</th> <th scope="col">Scrobbles</th> <th scope="col">Complete</th> <th scope="col">Start</th>@@ -15,16 +16,19 @@ <tbody> {% for obj in object_list %} {% if obj.title %}+ {% with last=obj.scrobble_set.last %} <tr>- <td><a href="{{obj.scrobble_set.last.get_absolute_url}}">{{obj.scrobble_set.last.local_timestamp}}+ <td><a href="{{last.get_absolute_url}}">{{last.local_timestamp}} <td><a href="{{obj.get_absolute_url}}">{{obj}}</a></td> {% if request.user.is_authenticated %}+ <td>{% if last.long_play_seconds %}{{ last.long_play_seconds|natural_duration }}{% elif last.elapsed_time %}{{ last.elapsed_time|natural_duration }}{% endif %}</td> <td>{{obj.scrobble_count}}</td> <td>{% if obj.scrobble_set.last.long_play_complete == True %}Yes{% else %}No{% endif %}</td> <td><a type="button" class="btn btn-sm btn-primary" href="{{obj.start_url}}">Scrobble</a></td> <td><a type="button" class="btn btn-sm btn-warning" href="{{obj.get_longplay_finish_url}}">Finish</a></td> {% endif %} </tr>+ {% endwith %} {% endif %} {% endfor %} </tbody>@@ -33,7 +33,11 @@ <p><a href="{{object.next_readcomics_url}}">Read next issue</a></p> {% endif %} - <p>{{scrobbles.count}} scrobbles</p>+ <p>+ {{scrobbles.count}} scrobbles+ {% if long_play_total_seconds %} | Total time: {{ long_play_total_seconds|natural_duration }}{% endif %}+ {% if long_play_finished_date %} | Finished: {{ long_play_finished_date|date:"M d, Y" }}{% endif %}+ </p> <p><a href="{{object.resume_start_url}}">Resume reading</a></p> </div>@@ -26,7 +26,11 @@ </div> </div> <div class="row">- <p>{{scrobbles.count}} scrobbles</p>+ <p>+ {{scrobbles.count}} scrobbles+ {% if long_play_total_seconds %} | Total time: {{ long_play_total_seconds|natural_duration }}{% endif %}+ {% if long_play_finished_date %} | Finished: {{ long_play_finished_date|date:"M d, Y" }}{% endif %}+ </p> </div> <div class="row"> <div class="col-md">@@ -264,7 +264,13 @@ <h2 class="mt-4">All scrobbles of this {{ object.media_type|lower }}</h2> {% if related_scrobbles %}-<p class="text-muted">{{ related_scrobbles.paginator.count }} scrobble{{ related_scrobbles.paginator.count|pluralize }}</p>+<p class="text-muted">+ {{ related_scrobbles.paginator.count }} scrobble{{ related_scrobbles.paginator.count|pluralize }}+ {% if object.is_long_play and long_play_total_seconds %}+ | Total time: {{ long_play_total_seconds|natural_duration }}+ {% if long_play_finished_date %} | Finished: {{ long_play_finished_date|date:"M d, Y" }}{% endif %}+ {% endif %}+</p> <table class="table table-striped table-sm"> <thead> <tr>@@ -281,7 +287,11 @@ {% if scrobble.media_type == "Task" and scrobble.logdata.title %}{{ scrobble.media_obj.title }}: {{ scrobble.logdata.title }}{% else %}{{ scrobble.media_obj.title|default:scrobble.media_obj }}{% endif %} </td> <td>- {% if scrobble.playback_position_seconds %}{{ scrobble.playback_position_seconds|natural_duration }}{% endif %}+ {% if scrobble.is_long_play and scrobble.long_play_seconds %}+ {{ scrobble.playback_position_seconds|natural_duration }} ({{ scrobble.long_play_seconds|natural_duration }} total)+ {% elif scrobble.playback_position_seconds %}+ {{ scrobble.playback_position_seconds|natural_duration }}+ {% endif %} </td> </tr> {% endfor %}@@ -50,7 +50,11 @@ {% endif %} <div class="row">- <p>{{scrobbles.count}} scrobbles</p>+ <p>+ {{scrobbles.count}} scrobbles+ {% if long_play_total_seconds %} | Total time: {{ long_play_total_seconds|natural_duration }}{% endif %}+ {% if long_play_finished_date %} | Finished: {{ long_play_finished_date|date:"M d, Y" }}{% endif %}+ </p> <p> <a href="{{object.start_url}}">Play again</a> </p>@@ -26,7 +26,7 @@ {% block lists %} <div class="row"> <div class="col-md">- <div class="table-responsive">{% include "_scrobblable_list.html" %}</div>+ <div class="table-responsive">{% include "_longplay_scrobblable_list.html" %}</div> </div> </div> {% endblock %}@@ -59,7 +59,11 @@ </div> </div> <div class="row">- <p>{{scrobbles.count}} scrobbles</p>+ <p>+ {{scrobbles.count}} scrobbles+ {% if long_play_total_seconds %} | Total time: {{ long_play_total_seconds|natural_duration }}{% endif %}+ {% if long_play_finished_date %} | Finished: {{ long_play_finished_date|date:"M d, Y" }}{% endif %}+ </p> <p> <a href="">Play again</a> </p>@@ -16,7 +16,7 @@ <div class="col-md"> <div class="table-responsive">- {% include "_scrobblable_list.html" %}+ {% include "_longplay_scrobblable_list.html" %} </div> </div> </div>