Compare commits
4 Commits
7d708ad8a6
...
0671ab432f
| Author | SHA1 | Date | |
|---|---|---|---|
| 0671ab432f | |||
| 893867419a | |||
| d9dfec81aa | |||
| 948fbc19bf |
10
PROJECT.org
10
PROJECT.org
@ -88,7 +88,7 @@ fetching and simple saving.
|
||||
*** Metadata sources
|
||||
**** Scraper
|
||||
|
||||
* Backlog [3/26] :vrobbler:project:personal:
|
||||
* Backlog [0/22] :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
|
||||
@ -604,7 +604,12 @@ independent of the email flow it was originally creatdd for
|
||||
|
||||
** TODO [#B] Is there way to create unique slugs for media instances :media_types:
|
||||
|
||||
** STRT [#B] Use HTMx to update the Now Playing widget :feature:templates:
|
||||
* Version 57.0 [5/5]
|
||||
** DONE [#A] Scrobble button on some media list pages dont work :bug:scrobbles:
|
||||
:PROPERTIES:
|
||||
:ID: a3a5c707-2e3d-a6b1-0f7f-4c6f7433aa1f
|
||||
:END:
|
||||
** DONE [#B] Use HTMx to update the Now Playing widget :feature:templates:
|
||||
:PROPERTIES:
|
||||
:ID: 5f5631fc-9ee1-d5a5-d0f8-94fea6fbbfa4
|
||||
:END:
|
||||
@ -620,6 +625,7 @@ independent of the email flow it was originally creatdd for
|
||||
:PROPERTIES:
|
||||
:ID: c5fca159-c7e0-5795-7c05-bbc48f539650
|
||||
:END:
|
||||
|
||||
* Version 56.4 [3/3]
|
||||
** DONE [#B] Add ability to do reverse address lookup on lat-long pairs :geolocations:feature:
|
||||
:PROPERTIES:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "vrobbler"
|
||||
version = "56.4"
|
||||
version = "57.0"
|
||||
description = ""
|
||||
authors = ["Colin Powell <colin@unbl.ink>"]
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@ class UserProfileForm(forms.ModelForm):
|
||||
"enable_public_widgets",
|
||||
"widget_custom_css",
|
||||
"home_scrobble_limit",
|
||||
"live_now_playing",
|
||||
"weigh_in_units",
|
||||
]
|
||||
widgets = {
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.29 on 2026-06-22 02:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("profiles", "0038_userprofile_media_type_visibility"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="userprofile",
|
||||
name="live_now_playing",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@ -98,6 +98,8 @@ class UserProfile(TimeStampedModel):
|
||||
|
||||
home_scrobble_limit = models.IntegerField(default=20)
|
||||
|
||||
live_now_playing = models.BooleanField(default=False)
|
||||
|
||||
weigh_in_units = models.CharField(
|
||||
max_length=16,
|
||||
choices=WeighUnit.choices,
|
||||
|
||||
@ -25,16 +25,24 @@ AUTO_FINISH_MEDIA = {
|
||||
}
|
||||
|
||||
PLAY_AGAIN_MEDIA = {
|
||||
"videogames": "VideoGame",
|
||||
"videos": "Video",
|
||||
"music": "Track",
|
||||
"podcasts": "PodcastEpisode",
|
||||
"sports": "SportEvent",
|
||||
"books": "Book",
|
||||
"videogames": "VideoGame",
|
||||
"boardgames": "BoardGame",
|
||||
"moods": "Mood",
|
||||
"bricksets": "BrickSet",
|
||||
"locations": "GeoLocation",
|
||||
"trails": "Trail",
|
||||
"beers": "Beer",
|
||||
"puzzles": "Puzzle",
|
||||
"foods": "Food",
|
||||
"locations": "GeoLocation",
|
||||
"videos": "Video",
|
||||
"tasks": "Task",
|
||||
"webpages": "WebPage",
|
||||
"lifeevents": "LifeEvent",
|
||||
"moods": "Mood",
|
||||
"bricksets": "BrickSet",
|
||||
"channels": "Channel",
|
||||
"birds": "BirdingLocation",
|
||||
"discgolf": "DiscGolfCourse",
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ from tasks.webhooks import EmacsWebhookView, TodoistWebhookView
|
||||
app_name = "scrobbles"
|
||||
|
||||
urlpatterns = [
|
||||
path("now-playing/", views.NowPlayingPartialView.as_view(), name="now-playing-partial"),
|
||||
path("calendar/", views.ScrobbleCalendarView.as_view(), name="calendar"),
|
||||
path("search/", views.ScrobbleSearchView.as_view(), name="search"),
|
||||
path("status/", views.ScrobbleStatusView.as_view(), name="status"),
|
||||
|
||||
@ -361,6 +361,25 @@ class RecentScrobbleList(ListView):
|
||||
return Scrobble.objects.all().order_by("-timestamp")
|
||||
|
||||
|
||||
class NowPlayingPartialView(LoginRequiredMixin, TemplateView):
|
||||
template_name = "scrobbles/_now_playing.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
from scrobbles.constants import EXCLUDE_FROM_NOW_PLAYING
|
||||
|
||||
ctx["now_playing_list"] = list(
|
||||
Scrobble.objects.filter(
|
||||
in_progress=True,
|
||||
is_paused=False,
|
||||
user=self.request.user,
|
||||
)
|
||||
.exclude(media_type__in=EXCLUDE_FROM_NOW_PLAYING)
|
||||
.select_related("track", "video", "podcast_episode")
|
||||
)
|
||||
return ctx
|
||||
|
||||
|
||||
class ScrobbleListView(LoginRequiredMixin, ListView):
|
||||
model = Scrobble
|
||||
paginate_by = 100
|
||||
@ -899,7 +918,7 @@ def scrobble_start(request, media_uuid):
|
||||
if last_scrobble and last_scrobble.logdata:
|
||||
next_page = last_scrobble.logdata.page_end + 1
|
||||
log_data = {"page_start": next_page}
|
||||
media_obj.scrobble_for_user(user_id, log=log_data)
|
||||
scrobble = media_obj.scrobble_for_user(user_id, log=log_data)
|
||||
|
||||
if scrobble:
|
||||
messages.add_message(
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
# Generated by Django 4.2.29 on 2026-06-22 02:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
(
|
||||
"trends",
|
||||
"0002_alter_trendresult_unique_together_trendresult_period_and_more",
|
||||
),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="trendresult",
|
||||
name="period",
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
("last_30", "Last 30 days"),
|
||||
("last_90", "Last 90 days"),
|
||||
("last_year", "Last year"),
|
||||
],
|
||||
default="last_30",
|
||||
max_length=20,
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -15,13 +15,11 @@ def natural_duration(value):
|
||||
seconds = remainder % 60
|
||||
parts = []
|
||||
if days:
|
||||
parts.append(f"{days} day{'s' if days != 1 else ''}")
|
||||
parts.append(f"{days} d")
|
||||
if hours:
|
||||
parts.append(f"{hours} hour{'s' if hours != 1 else ''}")
|
||||
parts.append(f"{hours} hr")
|
||||
if minutes:
|
||||
parts.append(f"{minutes} minute{'s' if minutes != 1 else ''}")
|
||||
parts.append(f"{minutes} min")
|
||||
if seconds or not parts:
|
||||
parts.append(f"{seconds} second{'s' if seconds != 1 else ''}")
|
||||
if len(parts) == 1:
|
||||
return parts[0]
|
||||
return ", ".join(parts[:-1]) + " and " + parts[-1]
|
||||
parts.append(f"{seconds} sec")
|
||||
return ", ".join(parts)
|
||||
|
||||
@ -311,26 +311,13 @@
|
||||
{% endblock %}
|
||||
|
||||
{% if now_playing_list and user.is_authenticated %}
|
||||
<ul>
|
||||
<b>Now playing</b>
|
||||
{% for scrobble in now_playing_list %}
|
||||
<div class="now-playing">
|
||||
{% if scrobble.media_obj.primary_image_url %}<div style="float:left;padding-right:10px;padding-bottom:10px;"><img src="{{scrobble.media_obj.primary_image_url}}" /></div>{% endif %}
|
||||
<p><a href="{{scrobble.get_absolute_url}}">{{scrobble.media_obj}}</a></p>
|
||||
{% if scrobble.logdata %}{% if scrobble.logdata.title%}<p><em>{{scrobble.logdata.title}}</em></p>{% endif %}{% endif %}
|
||||
<p><small>{{scrobble.local_timestamp|naturaltime}} from {{scrobble.source}}</small></p>
|
||||
<div class="progress-bar" style="margin-right:5px;">
|
||||
<span class="progress-bar-fill" style="width: {{scrobble.percent_played}}%;"></span>
|
||||
</div>
|
||||
<p class="action-buttons">
|
||||
<a href="{% url "scrobbles:cancel" scrobble.id %}">Cancel</a>
|
||||
<a class="right" href="{% url "scrobbles:finish" scrobble.id %}">Finish</a>
|
||||
</p>
|
||||
{% if not forloop.last %}<hr/>{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if now_playing_list|length > 1 %}<hr/>{% endif %}
|
||||
{% if user.profile.live_now_playing %}
|
||||
<div id="now-playing-container" hx-get="{% url 'scrobbles:now-playing-partial' %}" hx-trigger="every 7s" hx-swap="innerHTML">
|
||||
{% include "scrobbles/_now_playing.html" %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% include "scrobbles/_now_playing.html" %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if active_imports %}
|
||||
|
||||
21
vrobbler/templates/scrobbles/_now_playing.html
Normal file
21
vrobbler/templates/scrobbles/_now_playing.html
Normal file
@ -0,0 +1,21 @@
|
||||
{% load humanize %}
|
||||
<ul>
|
||||
<b>Now playing</b>
|
||||
{% for scrobble in now_playing_list %}
|
||||
<div class="now-playing">
|
||||
{% if scrobble.media_obj.primary_image_url %}<div style="float:left;padding-right:10px;padding-bottom:10px;"><img src="{{scrobble.media_obj.primary_image_url}}" /></div>{% endif %}
|
||||
<p><a href="{{scrobble.get_absolute_url}}">{{scrobble.media_obj}}</a></p>
|
||||
{% if scrobble.logdata %}{% if scrobble.logdata.title%}<p><em>{{scrobble.logdata.title}}</em></p>{% endif %}{% endif %}
|
||||
<p><small>{{scrobble.local_timestamp|naturaltime}} from {{scrobble.source}}</small></p>
|
||||
<div class="progress-bar" style="margin-right:5px;">
|
||||
<span class="progress-bar-fill" style="width: {{scrobble.percent_played}}%;"></span>
|
||||
</div>
|
||||
<p class="action-buttons">
|
||||
<a href="{% url "scrobbles:cancel" scrobble.id %}">Cancel</a>
|
||||
<a class="right" href="{% url "scrobbles:finish" scrobble.id %}">Finish</a>
|
||||
</p>
|
||||
{% if not forloop.last %}<hr/>{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if now_playing_list|length > 1 %}<hr/>{% endif %}
|
||||
Reference in New Issue
Block a user