diff --git a/PROJECT.org b/PROJECT.org
index 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-a5aa7c4a1f2c
diff --git a/vrobbler/apps/videos/models.py b/vrobbler/apps/videos/models.py
index 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.py
index 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.html
index 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;
+}
{% endblock %}
@@ -29,9 +49,30 @@
{{object.description}}
{% endif %} + {% if object.genre.all %} +Genres: {% for tag in object.genre.all %}{{tag.name}} {% endfor %}
+ {% endif %} ++ View on Twitch +
+ {% endif %}{{object.overview}}
{% endif %} {% if object.plot%}{{object.plot|safe|linebreaks|truncatewords:160}}
{% endif %}