Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d0c88ce271 | |||
| f8c9df3b9a | |||
| 8b61dab1bc |
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "vrobbler"
|
name = "vrobbler"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
description = ""
|
description = ""
|
||||||
authors = ["Colin Powell <colin@unbl.ink>"]
|
authors = ["Colin Powell <colin@unbl.ink>"]
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ from django.contrib import admin
|
|||||||
|
|
||||||
from music.models import Artist, Album, Track
|
from music.models import Artist, Album, Track
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Album)
|
@admin.register(Album)
|
||||||
class AlbumAdmin(admin.ModelAdmin):
|
class AlbumAdmin(admin.ModelAdmin):
|
||||||
date_hierarchy = "created"
|
date_hierarchy = "created"
|
||||||
@ -9,12 +10,14 @@ class AlbumAdmin(admin.ModelAdmin):
|
|||||||
list_filter = ("year",)
|
list_filter = ("year",)
|
||||||
ordering = ("name",)
|
ordering = ("name",)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Artist)
|
@admin.register(Artist)
|
||||||
class ArtistAdmin(admin.ModelAdmin):
|
class ArtistAdmin(admin.ModelAdmin):
|
||||||
date_hierarchy = "created"
|
date_hierarchy = "created"
|
||||||
list_display = ("name", "musicbrainz_id")
|
list_display = ("name", "musicbrainz_id")
|
||||||
ordering = ("name",)
|
ordering = ("name",)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Track)
|
@admin.register(Track)
|
||||||
class TrackAdmin(admin.ModelAdmin):
|
class TrackAdmin(admin.ModelAdmin):
|
||||||
date_hierarchy = "created"
|
date_hierarchy = "created"
|
||||||
|
|||||||
@ -10,7 +10,9 @@ from datetime import datetime, timedelta
|
|||||||
|
|
||||||
NOW = timezone.now()
|
NOW = timezone.now()
|
||||||
START_OF_TODAY = datetime.combine(NOW.date(), datetime.min.time(), NOW.tzinfo)
|
START_OF_TODAY = datetime.combine(NOW.date(), datetime.min.time(), NOW.tzinfo)
|
||||||
STARTING_DAY_OF_CURRENT_WEEK = NOW.date() - timedelta(days=NOW.today().isoweekday() % 7)
|
STARTING_DAY_OF_CURRENT_WEEK = NOW.date() - timedelta(
|
||||||
|
days=NOW.today().isoweekday() % 7
|
||||||
|
)
|
||||||
STARTING_DAY_OF_CURRENT_MONTH = NOW.date().replace(day=1)
|
STARTING_DAY_OF_CURRENT_MONTH = NOW.date().replace(day=1)
|
||||||
STARTING_DAY_OF_CURRENT_YEAR = NOW.date().replace(month=1, day=1)
|
STARTING_DAY_OF_CURRENT_YEAR = NOW.date().replace(month=1, day=1)
|
||||||
|
|
||||||
@ -18,30 +20,48 @@ STARTING_DAY_OF_CURRENT_YEAR = NOW.date().replace(month=1, day=1)
|
|||||||
def scrobble_counts():
|
def scrobble_counts():
|
||||||
finished_scrobbles_qs = Scrobble.objects.filter(in_progress=False)
|
finished_scrobbles_qs = Scrobble.objects.filter(in_progress=False)
|
||||||
data = {}
|
data = {}
|
||||||
data['today'] = finished_scrobbles_qs.filter(timestamp__gte=START_OF_TODAY).count()
|
data['today'] = finished_scrobbles_qs.filter(
|
||||||
data['week'] = finished_scrobbles_qs.filter(timestamp__gte=STARTING_DAY_OF_CURRENT_WEEK).count()
|
timestamp__gte=START_OF_TODAY
|
||||||
data['month'] = finished_scrobbles_qs.filter(timestamp__gte=STARTING_DAY_OF_CURRENT_MONTH).count()
|
).count()
|
||||||
data['year'] = finished_scrobbles_qs.filter(timestamp__gte=STARTING_DAY_OF_CURRENT_YEAR).count()
|
data['week'] = finished_scrobbles_qs.filter(
|
||||||
|
timestamp__gte=STARTING_DAY_OF_CURRENT_WEEK
|
||||||
|
).count()
|
||||||
|
data['month'] = finished_scrobbles_qs.filter(
|
||||||
|
timestamp__gte=STARTING_DAY_OF_CURRENT_MONTH
|
||||||
|
).count()
|
||||||
|
data['year'] = finished_scrobbles_qs.filter(
|
||||||
|
timestamp__gte=STARTING_DAY_OF_CURRENT_YEAR
|
||||||
|
).count()
|
||||||
data['alltime'] = finished_scrobbles_qs.count()
|
data['alltime'] = finished_scrobbles_qs.count()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def week_of_scrobbles(media: str='tracks') -> dict[str, int]:
|
|
||||||
scrobble_day_dict= {}
|
|
||||||
media_filter = Q(track__isnull=True)
|
|
||||||
|
|
||||||
for day in range(1,8):
|
def week_of_scrobbles(media: str = 'tracks') -> dict[str, int]:
|
||||||
|
scrobble_day_dict = {}
|
||||||
|
media_filter = Q(track__isnull=False)
|
||||||
|
|
||||||
|
for day in range(6, -1, -1):
|
||||||
start = START_OF_TODAY - timedelta(days=day)
|
start = START_OF_TODAY - timedelta(days=day)
|
||||||
end = datetime.combine(start, datetime.max.time(), NOW.tzinfo)
|
end = datetime.combine(start, datetime.max.time(), NOW.tzinfo)
|
||||||
day_of_week = start.strftime('%A')
|
day_of_week = start.strftime('%A')
|
||||||
if media == 'movies':
|
if media == 'movies':
|
||||||
media_filter = Q(video__videotype=Video.VideoType.MOVIE)
|
media_filter = Q(video__videotype=Video.VideoType.MOVIE)
|
||||||
if media == 'series':
|
if media == 'series':
|
||||||
media_filter = Q(video__videotype=Video.VideoType.MOVIE)
|
media_filter = Q(video__videotype=Video.VideoType.TV_EPISODE)
|
||||||
scrobble_day_dict[day_of_week] = Scrobble.objects.filter(media_filter).filter(timestamp__lte=START_OF_TODAY, timestamp__gt=end, in_progress=False).count()
|
scrobble_day_dict[day_of_week] = (
|
||||||
|
Scrobble.objects.filter(media_filter)
|
||||||
|
.filter(
|
||||||
|
timestamp__gte=start,
|
||||||
|
timestamp__lte=end,
|
||||||
|
in_progress=False,
|
||||||
|
)
|
||||||
|
.count()
|
||||||
|
)
|
||||||
|
|
||||||
return scrobble_day_dict
|
return scrobble_day_dict
|
||||||
|
|
||||||
def top_tracks(filter: str="today", limit: int=15) -> List["Track"]:
|
|
||||||
|
def top_tracks(filter: str = "today", limit: int = 15) -> List["Track"]:
|
||||||
time_filter = Q(scrobble__timestamp__gte=START_OF_TODAY)
|
time_filter = Q(scrobble__timestamp__gte=START_OF_TODAY)
|
||||||
if filter == "week":
|
if filter == "week":
|
||||||
time_filter = Q(scrobble__timestamp__gte=STARTING_DAY_OF_CURRENT_WEEK)
|
time_filter = Q(scrobble__timestamp__gte=STARTING_DAY_OF_CURRENT_WEEK)
|
||||||
@ -50,21 +70,36 @@ def top_tracks(filter: str="today", limit: int=15) -> List["Track"]:
|
|||||||
if filter == "year":
|
if filter == "year":
|
||||||
time_filter = Q(scrobble__timestamp__gte=STARTING_DAY_OF_CURRENT_YEAR)
|
time_filter = Q(scrobble__timestamp__gte=STARTING_DAY_OF_CURRENT_YEAR)
|
||||||
|
|
||||||
return Track.objects.annotate(num_scrobbles=Count("scrobble", distinct=True)).filter(time_filter).order_by("-num_scrobbles")[:limit]
|
return (
|
||||||
|
Track.objects.annotate(num_scrobbles=Count("scrobble", distinct=True))
|
||||||
|
.filter(time_filter)
|
||||||
|
.order_by("-num_scrobbles")[:limit]
|
||||||
|
)
|
||||||
|
|
||||||
def top_artists(filter: str="today", limit: int=15) -> List["Artist"]:
|
|
||||||
|
def top_artists(filter: str = "today", limit: int = 15) -> List["Artist"]:
|
||||||
time_filter = Q(track__scrobble__timestamp__gte=START_OF_TODAY)
|
time_filter = Q(track__scrobble__timestamp__gte=START_OF_TODAY)
|
||||||
if filter == "week":
|
if filter == "week":
|
||||||
time_filter = Q(track__scrobble__timestamp__gte=STARTING_DAY_OF_CURRENT_WEEK)
|
time_filter = Q(
|
||||||
|
track__scrobble__timestamp__gte=STARTING_DAY_OF_CURRENT_WEEK
|
||||||
|
)
|
||||||
if filter == "month":
|
if filter == "month":
|
||||||
time_filter = Q(track__scrobble__timestamp__gte=STARTING_DAY_OF_CURRENT_MONTH)
|
time_filter = Q(
|
||||||
|
track__scrobble__timestamp__gte=STARTING_DAY_OF_CURRENT_MONTH
|
||||||
|
)
|
||||||
if filter == "year":
|
if filter == "year":
|
||||||
time_filter = Q(track__scrobble__timestamp__gte=STARTING_DAY_OF_CURRENT_YEAR)
|
time_filter = Q(
|
||||||
|
track__scrobble__timestamp__gte=STARTING_DAY_OF_CURRENT_YEAR
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
Artist.objects.annotate(
|
||||||
|
num_scrobbles=Count("track__scrobble", distinct=True)
|
||||||
|
)
|
||||||
|
.filter(time_filter)
|
||||||
|
.order_by("-num_scrobbles")[:limit]
|
||||||
|
)
|
||||||
|
|
||||||
return Artist.objects.annotate(num_scrobbles=Sum("track__scrobble", distinct=True)).filter(time_filter).order_by("-num_scrobbles")[:limit]
|
|
||||||
|
|
||||||
def artist_scrobble_count(artist_id: int, filter: str = "today") -> int:
|
def artist_scrobble_count(artist_id: int, filter: str = "today") -> int:
|
||||||
return (
|
return Scrobble.objects.filter(track__artist=artist_id).count()
|
||||||
Scrobble.objects.filter(track__artist=artist_id)
|
|
||||||
.count()
|
|
||||||
)
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ class ScrobbleAdmin(admin.ModelAdmin):
|
|||||||
"playback_position",
|
"playback_position",
|
||||||
"in_progress",
|
"in_progress",
|
||||||
)
|
)
|
||||||
list_filter = ("in_progress", "source")
|
list_filter = ("in_progress", "source", "track__artist")
|
||||||
ordering = ("-timestamp",)
|
ordering = ("-timestamp",)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,12 @@ from scrobbles.models import Scrobble
|
|||||||
from scrobbles.serializers import ScrobbleSerializer
|
from scrobbles.serializers import ScrobbleSerializer
|
||||||
from scrobbles.utils import convert_to_seconds
|
from scrobbles.utils import convert_to_seconds
|
||||||
from videos.models import Video
|
from videos.models import Video
|
||||||
from vrobbler.apps.music.aggregators import scrobble_counts, top_tracks, week_of_scrobbles
|
from vrobbler.apps.music.aggregators import (
|
||||||
|
scrobble_counts,
|
||||||
|
top_artists,
|
||||||
|
top_tracks,
|
||||||
|
week_of_scrobbles,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -52,18 +57,25 @@ class RecentScrobbleList(ListView):
|
|||||||
timestamp__gte=last_eight_minutes,
|
timestamp__gte=last_eight_minutes,
|
||||||
timestamp__lte=now,
|
timestamp__lte=now,
|
||||||
)
|
)
|
||||||
data['video_scrobble_list'] = Scrobble.objects.filter(video__isnull=False, in_progress=False).order_by('-timestamp')[:10]
|
data['video_scrobble_list'] = Scrobble.objects.filter(
|
||||||
|
video__isnull=False, in_progress=False
|
||||||
|
).order_by('-timestamp')[:10]
|
||||||
data['top_daily_tracks'] = top_tracks()
|
data['top_daily_tracks'] = top_tracks()
|
||||||
data['top_weekly_tracks'] = top_tracks(filter='week')
|
data['top_weekly_tracks'] = top_tracks(filter='week')
|
||||||
data['top_monthly_tracks'] = top_tracks(filter='month')
|
data['top_monthly_tracks'] = top_tracks(filter='month')
|
||||||
|
|
||||||
|
data['top_daily_artists'] = top_artists()
|
||||||
|
data['top_weekly_artists'] = top_artists(filter='week')
|
||||||
|
data['top_monthly_artists'] = top_artists(filter='month')
|
||||||
|
|
||||||
data["weekly_data"] = week_of_scrobbles()
|
data["weekly_data"] = week_of_scrobbles()
|
||||||
data['counts'] = scrobble_counts()
|
data['counts'] = scrobble_counts()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Scrobble.objects.filter(track__isnull=False, in_progress=False).order_by(
|
return Scrobble.objects.filter(
|
||||||
'-timestamp'
|
track__isnull=False, in_progress=False
|
||||||
)[:25]
|
).order_by('-timestamp')[:25]
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
|
|||||||
@ -6,15 +6,18 @@ from os import environ as env
|
|||||||
|
|
||||||
if not 'DJANGO_SETTINGS_MODULE' in env:
|
if not 'DJANGO_SETTINGS_MODULE' in env:
|
||||||
from vrobbler import settings
|
from vrobbler import settings
|
||||||
|
|
||||||
env.setdefault('DJANGO_SETTINGS_MODULE', settings.__name__)
|
env.setdefault('DJANGO_SETTINGS_MODULE', settings.__name__)
|
||||||
|
|
||||||
|
|
||||||
import django
|
import django
|
||||||
|
|
||||||
django.setup()
|
django.setup()
|
||||||
|
|
||||||
# this line must be after django.setup() for logging configure
|
# this line must be after django.setup() for logging configure
|
||||||
logger = logging.getLogger('vrobbler')
|
logger = logging.getLogger('vrobbler')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# to get configured settings
|
# to get configured settings
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|||||||
@ -280,35 +280,35 @@
|
|||||||
var myChart = new Chart(ctx, {
|
var myChart = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: [
|
labels: [
|
||||||
{% for day in weekly_data.keys %}
|
{% for day in weekly_data.keys %}
|
||||||
"{{day}}"{% if not forloop.last %},{% endif %}
|
"{{day}}"{% if not forloop.last %},{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
],
|
],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
data: [
|
data: [
|
||||||
{% for count in weekly_data.values %}
|
{% for count in weekly_data.values %}
|
||||||
{{count}}{% if not forloop.last %},{% endif %}
|
{{count}}{% if not forloop.last %},{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
],
|
],
|
||||||
lineTension: 0,
|
lineTension: 0,
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
borderColor: '#007bff',
|
borderColor: '#007bf0',
|
||||||
borderWidth: 4,
|
borderWidth: 4,
|
||||||
pointBackgroundColor: '#007bff'
|
pointBackgroundColor: '#007bff'
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
scales: {
|
scales: {
|
||||||
yAxes: [{
|
yAxes: [{
|
||||||
ticks: {
|
ticks: {
|
||||||
beginAtZero: false
|
beginAtZero: false
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
display: false
|
display: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})()
|
})()
|
||||||
|
|||||||
@ -20,80 +20,109 @@
|
|||||||
|
|
||||||
<canvas class="my-4 w-100" id="myChart" width="900" height="380"></canvas>
|
<canvas class="my-4 w-100" id="myChart" width="900" height="380"></canvas>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
<h2>Top this week</h2>
|
<div class="row">
|
||||||
<div class="table-responsive">
|
<div class="col-md">
|
||||||
<table class="table table-striped table-sm">
|
<h2>Top artists this week</h2>
|
||||||
<thead>
|
<div class="table-responsive">
|
||||||
<tr>
|
<table class="table table-striped table-sm">
|
||||||
<th scope="col">#</th>
|
<thead>
|
||||||
<th scope="col">Track</th>
|
<tr>
|
||||||
<th scope="col">Artist</th>
|
<th scope="col">#</th>
|
||||||
<th scope="col">Album</th>
|
<th scope="col">Artist</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for track in top_weekly_tracks %}
|
{% for artist in top_weekly_artists %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{track.num_scrobbles}}</td>
|
<td>{{artist.num_scrobbles}}</td>
|
||||||
<td>{{track.title}}</td>
|
<td>{{artist.name}}</td>
|
||||||
<td>{{track.artist.name}}</td>
|
</tr>
|
||||||
<td>{{track.album.name}}</td>
|
{% endfor %}
|
||||||
</tr>
|
</tbody>
|
||||||
{% endfor %}
|
</table>
|
||||||
</tbody>
|
</div>
|
||||||
</table>
|
</div>
|
||||||
|
<div class="col-lg">
|
||||||
|
<h2>Top tracks this week</h2>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">#</th>
|
||||||
|
<th scope="col">Track</th>
|
||||||
|
<th scope="col">Artist</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for track in top_weekly_tracks %}
|
||||||
|
<tr>
|
||||||
|
<td>{{track.num_scrobbles}}</td>
|
||||||
|
<td>{{track.title}}</td>
|
||||||
|
<td>{{track.artist.name}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg">
|
||||||
|
<h2>Latest listened</h2>
|
||||||
|
<p>Today <b>{{counts.today}}</b> | This Week <b>{{counts.week}}</b> | This Month <b>{{counts.month}}</b> | This Year <b>{{counts.year}}</b> | All Time <b>{{counts.alltime}}</b></p>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Time</th>
|
||||||
|
<th scope="col">Track</th>
|
||||||
|
<th scope="col">Artist</th>
|
||||||
|
<th scope="col">Source</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for scrobble in object_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{scrobble.timestamp|naturaltime}}</td>
|
||||||
|
<td>{{scrobble.track.title}}</td>
|
||||||
|
<td>{{scrobble.track.artist.name}}</td>
|
||||||
|
<td>{{scrobble.source}}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h2>Latest scrobbles</h2>
|
<div class="col-lg">
|
||||||
<p>Today <b>{{counts.today}}</b> | This Week <b>{{counts.week}}</b> | This Month <b>{{counts.month}}</b> | This Year <b>{{counts.year}}</b> | All Time <b>{{counts.alltime}}</b></p>
|
<h2>Latest watched</h2>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-striped table-sm">
|
<table class="table table-striped table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">Time</th>
|
<th scope="col">Time</th>
|
||||||
<th scope="col">Track</th>
|
<th scope="col">Title</th>
|
||||||
<th scope="col">Artist</th>
|
<th scope="col">Series</th>
|
||||||
<th scope="col">Source</th>
|
<th scope="col">Source</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for scrobble in object_list %}
|
{% for scrobble in video_scrobble_list %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{scrobble.timestamp|naturaltime}}</td>
|
<td>{{scrobble.timestamp|naturaltime}}</td>
|
||||||
<td>{{scrobble.track.title}}</td>
|
<td>{% if scrobble.video.tv_series %}E{{scrobble.video.season_number}}S{{scrobble.video.season_number}} -{% endif %} {{scrobble.video.title}}</td>
|
||||||
<td>{{scrobble.track.artist.name}}</td>
|
<td>{% if scrobble.video.tv_series %}{{scrobble.video.tv_series}}{% endif %}</td>
|
||||||
<td>{{scrobble.source}}</td>
|
<td>{{scrobble.source}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<h2>Latest watched</h2>
|
</div>
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-striped table-sm">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">Time</th>
|
|
||||||
<th scope="col">Title</th>
|
|
||||||
<th scope="col">Series</th>
|
|
||||||
<th scope="col">Season & Episode</th>
|
|
||||||
<th scope="col">Source</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for scrobble in video_scrobble_list %}
|
|
||||||
<tr>
|
|
||||||
<td>{{scrobble.timestamp|naturaltime}}</td>
|
|
||||||
<td>{{scrobble.video.title}}</td>
|
|
||||||
<td>{% if scrobble.video.tv_series %}{{scrobble.video.tv_series}}{% endif %}</td>
|
|
||||||
<td>{% if scrobble.video.tv_series %}{{scrobble.video.season_number}}, {{scrobble.video.episode_number}}{% endif %}</td>
|
|
||||||
<td>{{scrobble.source}}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user