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 @@
- {% if object.youtube_id %}

View on YouTube

{% endif %} + {% if object.description %}

{{object.description}}

{% endif %} + {% if object.genre.all %} +

Genres: {% for tag in object.genre.all %}{{tag.name}} {% endfor %}

+ {% endif %} +
+ {% if object.youtube_id %} +

+ +

+ {% endif %} + {% if object.twitch_id %} +

+ View on Twitch +

+ {% endif %}
+{% if charts %} +
+
+ {% include "scrobbles/_chart_links.html" %} +
+
+{% endif %}

Last scrobbles

@@ -41,6 +82,8 @@ Date Title + With + Rated @@ -48,11 +91,26 @@ {{scrobble.local_timestamp}} {{scrobble.media_obj.title}} + {% firstof scrobble.logdata.with_people|join:", " "Solo" %} + {% firstof scrobble.logdata.rating "Unrated" %} {% endfor %}
+ {% if is_paginated %} + + {% endif %}
{% endblock %} diff --git a/vrobbler/templates/videos/video_detail.html b/vrobbler/templates/videos/video_detail.html index f7769a2..6b02206 100644 --- a/vrobbler/templates/videos/video_detail.html +++ b/vrobbler/templates/videos/video_detail.html @@ -63,6 +63,7 @@ dd {
{% if object.tv_series %}

{{object.tv_series}} - S{{object.season_number}}E{{object.episode_number}}

{% endif %} + {% if object.channel %}
{{object.channel.name}}
{% endif %} {% if object.overview %}

{{object.overview}}

{% endif %} {% if object.plot%}

{{object.plot|safe|linebreaks|truncatewords:160}}

{% endif %}