sessions / b26470c2792792c960cf9a468ad0e14568bf148a

/home/powellc/src/code.unbl.ink/secstate/vrobbler

This commit has no recorded session.

diff

ignore whitespace

commit b26470c2792792c960cf9a468ad0e14568bf148aAuthor: Colin Powell <colin@unbl.ink>Date:   Wed Jun 17 10:59:42 2026 -0400    [videos] Fix channel templatesdiff --git a/PROJECT.org b/PROJECT.orgindex 5edbb6e..179e0da 100644--- a/PROJECT.org+++ b/PROJECT.org@@ -88,7 +88,7 @@ fetching and simple saving. *** Metadata sources **** Scraper -* Backlog [2/22] :vrobbler:project:personal:+* Backlog [3/23] :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@@ -590,6 +590,10 @@ We should rename `email_scrobble_board_game` to reflect the fact that it's just a helper method to create board game scrobbles given a json blob. It's independent of the email flow it was originally creatdd for +** DONE [#A] Update youtube video detail pages with links to channel :videos:templates:+:PROPERTIES:+:ID:       8b87cb42-09e5-a3f5-136f-182f967fa81f+:END: ** DONE [#A] Concurrent reading trend does not consolidate on single book :trends:reading: :PROPERTIES: :ID:       fe220f55-7e0d-2a17-2477-a5aa7c4a1f2cdiff --git a/vrobbler/apps/videos/models.py b/vrobbler/apps/videos/models.pyindex cbaa30e..c64c87a 100644--- a/vrobbler/apps/videos/models.py+++ b/vrobbler/apps/videos/models.py@@ -80,6 +80,28 @@ class Channel(ScrobblableMixin):     def title(self):         return self.name +    @property+    def safe_cover_image_url(self) -> str:+        if self.cover_image:+            try:+                if self.cover_image.storage.exists(self.cover_image.name):+                    return self.cover_medium.url+            except Exception:+                pass+        return "/static/images/not-found.jpg"++    @property+    def youtube_url(self) -> str:+        if self.youtube_id:+            return YOUTUBE_CHANNEL_URL + self.youtube_id+        return ""++    @property+    def twitch_url(self) -> str:+        if self.twitch_id:+            return f"https://www.twitch.tv/{self.twitch_id}"+        return ""+     def save_image_from_url(self, url: str, force_update: bool = False):         if not self.cover_image or (force_update and url):             r = requests.get(url)@@ -95,7 +117,7 @@ class Channel(ScrobblableMixin):             played_query = models.Q()         return Scrobble.objects.filter(             played_query,-            channel=self,+            models.Q(channel=self) | models.Q(video__channel=self),             user=user_id,         ).order_by("-timestamp") diff --git a/vrobbler/apps/videos/views.py b/vrobbler/apps/videos/views.pyindex d0a5702..af9fb28 100644--- a/vrobbler/apps/videos/views.py+++ b/vrobbler/apps/videos/views.py@@ -1,5 +1,7 @@ import datetime+ from django.contrib.auth.mixins import LoginRequiredMixin+from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator from django.utils import timezone from django.views import generic from scrobbles.models import Scrobble@@ -44,15 +46,31 @@ class SeriesDetailView(LoginRequiredMixin, ChartContextMixin, generic.DetailView         return context_data  -class ChannelDetailView(LoginRequiredMixin, generic.DetailView):+class ChannelDetailView(LoginRequiredMixin, ChartContextMixin, generic.DetailView):     model = Channel     slug_field = "uuid"     template_name = "videos/channel_detail.html"+    paginate_by = 50      def get_context_data(self, **kwargs):         user_id = self.request.user.id         context_data = super().get_context_data(**kwargs)-        context_data["scrobbles"] = self.object.scrobbles_for_user(user_id)++        scrobbles = self.object.scrobbles_for_user(user_id)+        paginator = Paginator(scrobbles, self.paginate_by)+        page_number = self.request.GET.get("page")++        try:+            page_obj = paginator.page(page_number)+        except PageNotAnInteger:+            page_obj = paginator.page(1)+        except EmptyPage:+            page_obj = paginator.page(paginator.num_pages)++        context_data["page_obj"] = page_obj+        context_data["scrobbles"] = page_obj.object_list+        context_data["is_paginated"] = paginator.num_pages > 1+         return context_data  diff --git a/vrobbler/templates/videos/channel_detail.html b/vrobbler/templates/videos/channel_detail.htmlindex 95dba56..102e3e2 100644--- a/vrobbler/templates/videos/channel_detail.html+++ b/vrobbler/templates/videos/channel_detail.html@@ -19,6 +19,26 @@      color:white;      background:rgba(0,0,0,0.4); }+dl {+    display: flex;+    flex-flow: row wrap;+    padding-right:20px;+    border:none;+}+dt {+    flex-basis: 20%;+    padding: 5px;+    background: #3cf;+    text-align: right;+    color: #fff;+}+dd {+    flex-basis: 70%;+    flex-grow: 1;+    margin: 0;+    padding: 5px;+    border:none;+} </style> {% endblock %} @@ -29,9 +49,30 @@         <img src="{{ object.safe_cover_image_url }}" width="400px" />     </div>     <div class="summary">-        {% if object.youtube_id %}<p><a href="{{object.youtube_url}}" target="_blank">View on YouTube</a></p>{% endif %}+        {% if object.description %}<p><em>{{object.description}}</em></p>{% endif %}+        {% if object.genre.all %}+        <p>Genres: {% for tag in object.genre.all %}<span class="badge bg-secondary">{{tag.name}}</span> {% endfor %}</p>+        {% endif %}+        <hr />+        {% if object.youtube_id %}+        <p style="float:right;">+            <a href="{{object.youtube_url}}" target="_blank"><img src="{% static "images/youtube_logo.png" %}" width=35></a>+        </p>+        {% endif %}+        {% if object.twitch_id %}+        <p style="float:right;">+            <a href="{{object.twitch_url}}" target="_blank">View on Twitch</a>+        </p>+        {% endif %}+    </div>+</div>+{% if charts %}+<div class="row">+    <div class="col-md">+        {% include "scrobbles/_chart_links.html" %}     </div> </div>+{% endif %} <div class="row">     <div class="col-md">         <h3>Last scrobbles</h3>@@ -41,6 +82,8 @@                     <tr>                         <th scope="col">Date</th>                         <th scope="col">Title</th>+                        <th scope="col">With</th>+                        <th scope="col">Rated</th>                     </tr>                 </thead>                 <tbody>@@ -48,11 +91,26 @@                     <tr>                         <td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>                         <td><a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a></td>+                        <td>{% firstof scrobble.logdata.with_people|join:", " "Solo" %}</td>+                        <td>{% firstof scrobble.logdata.rating "Unrated" %}</td>                     </tr>                     {% endfor %}                 </tbody>             </table>         </div>+        {% if is_paginated %}+        <nav>+            <ul class="pagination">+                {% if page_obj.has_previous %}+                <li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a></li>+                {% endif %}+                <li class="page-item disabled"><span class="page-link">Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span></li>+                {% if page_obj.has_next %}+                <li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a></li>+                {% endif %}+            </ul>+        </nav>+        {% endif %}     </div> </div> {% endblock %}diff --git a/vrobbler/templates/videos/video_detail.html b/vrobbler/templates/videos/video_detail.htmlindex f7769a2..6b02206 100644--- a/vrobbler/templates/videos/video_detail.html+++ b/vrobbler/templates/videos/video_detail.html@@ -63,6 +63,7 @@ dd {     </div>     <div class="summary">     {% if object.tv_series %}<h4><a href="{{object.tv_series.get_absolute_url}}">{{object.tv_series}}</a> - S{{object.season_number}}E{{object.episode_number}}</h4>{% endif %}+        {% if object.channel %}<h5><a href="{{object.channel.get_absolute_url}}">{{object.channel.name}}</a></h5>{% endif %}         {% if object.overview %}<p><em>{{object.overview}}</em></p>{% endif %}         {% if object.plot%}<p>{{object.plot|safe|linebreaks|truncatewords:160}}</p>{% endif %}         <hr />