This commit is contained in:
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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 %}
|
||||
|
||||
@ -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 />
|
||||
|
||||
Reference in New Issue
Block a user