Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fd6e0f49b6 | |||
| 93299a1abd | |||
| a58ddebd23 | |||
| 41cdb96e94 | |||
| 5a8e828b81 | |||
| c84a3072be | |||
| 0bd7ed4463 | |||
| ee232aa103 |
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "vrobbler"
|
||||
version = "0.11.4"
|
||||
version = "0.11.5"
|
||||
description = ""
|
||||
authors = ["Colin Powell <colin@unbl.ink>"]
|
||||
|
||||
|
||||
@ -8,8 +8,18 @@ from scrobbles.admin import ScrobbleInline
|
||||
@admin.register(Album)
|
||||
class AlbumAdmin(admin.ModelAdmin):
|
||||
date_hierarchy = "created"
|
||||
list_display = ("name", "year", "musicbrainz_id")
|
||||
list_filter = ("year",)
|
||||
list_display = (
|
||||
"name",
|
||||
"year",
|
||||
"primary_artist",
|
||||
"theaudiodb_genre",
|
||||
"theaudiodb_mood",
|
||||
"musicbrainz_id",
|
||||
)
|
||||
list_filter = (
|
||||
"theaudiodb_score",
|
||||
"theaudiodb_genre",
|
||||
)
|
||||
ordering = ("name",)
|
||||
filter_horizontal = [
|
||||
'artists',
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-02 19:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('music', '0011_artist_thumbnail'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='allmusic_id',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='discogs_id',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='rateyourmusic_id',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='theaudiodb_description',
|
||||
field=models.TextField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='theaudiodb_genre',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='theaudiodb_id',
|
||||
field=models.CharField(
|
||||
blank=True, max_length=255, null=True, unique=True
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='theaudiodb_mood',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='theaudiodb_score',
|
||||
field=models.IntegerField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='theaudiodb_score_votes',
|
||||
field=models.IntegerField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='theaudiodb_speed',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='theaudiodb_style',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='theaudiodb_theme',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='wikidata_id',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='wikipedia_slug',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-02 19:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('music', '0012_album_allmusic_id_album_discogs_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='album',
|
||||
name='theaudiodb_score',
|
||||
field=models.FloatField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.1.5 on 2023-03-02 19:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('music', '0013_alter_album_theaudiodb_score'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='album',
|
||||
name='theaudiodb_year_released',
|
||||
field=models.IntegerField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
@ -13,6 +13,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from django_extensions.db.models import TimeStampedModel
|
||||
from scrobbles.mixins import ScrobblableMixin
|
||||
from scrobbles.theaudiodb import lookup_artist_from_tadb
|
||||
from vrobbler.apps.scrobbles.theaudiodb import lookup_album_from_tadb
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
BNULL = {"blank": True, "null": True}
|
||||
@ -86,6 +87,21 @@ class Album(TimeStampedModel):
|
||||
musicbrainz_releasegroup_id = models.CharField(max_length=255, **BNULL)
|
||||
musicbrainz_albumartist_id = models.CharField(max_length=255, **BNULL)
|
||||
cover_image = models.ImageField(upload_to="albums/", **BNULL)
|
||||
theaudiodb_id = models.CharField(max_length=255, unique=True, **BNULL)
|
||||
theaudiodb_description = models.TextField(**BNULL)
|
||||
theaudiodb_year_released = models.IntegerField(**BNULL)
|
||||
theaudiodb_score = models.FloatField(**BNULL)
|
||||
theaudiodb_score_votes = models.IntegerField(**BNULL)
|
||||
theaudiodb_genre = models.CharField(max_length=255, **BNULL)
|
||||
theaudiodb_style = models.CharField(max_length=255, **BNULL)
|
||||
theaudiodb_mood = models.CharField(max_length=255, **BNULL)
|
||||
theaudiodb_speed = models.CharField(max_length=255, **BNULL)
|
||||
theaudiodb_theme = models.CharField(max_length=255, **BNULL)
|
||||
allmusic_id = models.CharField(max_length=255, **BNULL)
|
||||
rateyourmusic_id = models.CharField(max_length=255, **BNULL)
|
||||
wikipedia_slug = models.CharField(max_length=255, **BNULL)
|
||||
discogs_id = models.CharField(max_length=255, **BNULL)
|
||||
wikidata_id = models.CharField(max_length=255, **BNULL)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@ -112,6 +128,15 @@ class Album(TimeStampedModel):
|
||||
def primary_artist(self):
|
||||
return self.artists.first()
|
||||
|
||||
def scrape_theaudiodb(self) -> None:
|
||||
artist = self.primary_artist.name
|
||||
album_data = lookup_album_from_tadb(self.name, artist)
|
||||
if not album_data.get('theaudiodb_id'):
|
||||
logger.info(f"No data for {self} found in TheAudioDB")
|
||||
return
|
||||
|
||||
Album.objects.filter(pk=self.pk).update(**album_data)
|
||||
|
||||
def fix_metadata(self):
|
||||
if (
|
||||
not self.musicbrainz_albumartist_id
|
||||
@ -159,6 +184,7 @@ class Album(TimeStampedModel):
|
||||
or self.cover_image == 'default-image-replace-me'
|
||||
):
|
||||
self.fetch_artwork()
|
||||
self.scrape_theaudiodb()
|
||||
|
||||
def fetch_artwork(self, force=False):
|
||||
if not self.cover_image and not force:
|
||||
@ -197,9 +223,27 @@ class Album(TimeStampedModel):
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def mb_link(self):
|
||||
def mb_link(self) -> str:
|
||||
return f"https://musicbrainz.org/release/{self.musicbrainz_id}"
|
||||
|
||||
@property
|
||||
def allmusic_link(self) -> str:
|
||||
if self.allmusic_id:
|
||||
return f"https://www.allmusic.com/artist/{self.allmusic_id}"
|
||||
return ""
|
||||
|
||||
@property
|
||||
def wikipedia_link(self):
|
||||
if self.wikipedia_slug:
|
||||
return f"https://www.wikipedia.org/en/{self.wikipedia_slug}"
|
||||
return ""
|
||||
|
||||
@property
|
||||
def tadb_link(self):
|
||||
if self.theaudiodb_id:
|
||||
return f"https://www.theaudiodb.com/album/{self.theaudiodb_id}"
|
||||
return ""
|
||||
|
||||
|
||||
class Track(ScrobblableMixin):
|
||||
COMPLETION_PERCENT = getattr(settings, 'MUSIC_COMPLETION_PERCENT', 90)
|
||||
|
||||
@ -8,6 +8,7 @@ from scrobbles.stats import get_scrobble_count_qs
|
||||
|
||||
class TrackListView(generic.ListView):
|
||||
model = Track
|
||||
paginate_by = 200
|
||||
|
||||
def get_queryset(self):
|
||||
return get_scrobble_count_qs(user=self.request.user).order_by(
|
||||
@ -30,10 +31,18 @@ class TrackDetailView(generic.DetailView):
|
||||
|
||||
class ArtistListView(generic.ListView):
|
||||
model = Artist
|
||||
paginate_by = 100
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().order_by("name")
|
||||
|
||||
def get_context_data(self, *, object_list=None, **kwargs):
|
||||
context_data = super().get_context_data(
|
||||
object_list=object_list, **kwargs
|
||||
)
|
||||
context_data['view'] = self.request.GET.get('view')
|
||||
return context_data
|
||||
|
||||
|
||||
class ArtistDetailView(generic.DetailView):
|
||||
model = Artist
|
||||
@ -49,6 +58,17 @@ class ArtistDetailView(generic.DetailView):
|
||||
|
||||
class AlbumListView(generic.ListView):
|
||||
model = Album
|
||||
paginate_by = 50
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().order_by("name")
|
||||
|
||||
def get_context_data(self, *, object_list=None, **kwargs):
|
||||
context_data = super().get_context_data(
|
||||
object_list=object_list, **kwargs
|
||||
)
|
||||
context_data['view'] = self.request.GET.get('view')
|
||||
return context_data
|
||||
|
||||
|
||||
class AlbumDetailView(generic.DetailView):
|
||||
|
||||
12
vrobbler/apps/scrobbles/static/css/bootstrap.min.css
vendored
Normal file
12
vrobbler/apps/scrobbles/static/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
0
vrobbler/apps/scrobbles/templatetags/__init__.py
Normal file
0
vrobbler/apps/scrobbles/templatetags/__init__.py
Normal file
10
vrobbler/apps/scrobbles/templatetags/urlreplace.py
Normal file
10
vrobbler/apps/scrobbles/templatetags/urlreplace.py
Normal file
@ -0,0 +1,10 @@
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def urlreplace(context, **kwargs):
|
||||
query = context['request'].GET.copy()
|
||||
query.update(kwargs)
|
||||
return query.urlencode()
|
||||
@ -5,14 +5,15 @@ import requests
|
||||
from django.conf import settings
|
||||
|
||||
THEAUDIODB_API_KEY = getattr(settings, "THEAUDIODB_API_KEY")
|
||||
SEARCH_URL = f"https://www.theaudiodb.com/api/v1/json/{THEAUDIODB_API_KEY}/search.php?s="
|
||||
ARTIST_SEARCH_URL = f"https://www.theaudiodb.com/api/v1/json/{THEAUDIODB_API_KEY}/search.php?s="
|
||||
ALBUM_SEARCH_URL = f"https://www.theaudiodb.com/api/v1/json/{THEAUDIODB_API_KEY}/searchalbum.php?s="
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def lookup_artist_from_tadb(name: str) -> dict:
|
||||
artist_info = {}
|
||||
response = requests.get(SEARCH_URL + name)
|
||||
response = requests.get(ARTIST_SEARCH_URL + name)
|
||||
|
||||
if response.status_code != 200:
|
||||
logger.warn(f"Bad response from TADB: {response.status_code}")
|
||||
@ -26,9 +27,49 @@ def lookup_artist_from_tadb(name: str) -> dict:
|
||||
if results['artists']:
|
||||
artist = results['artists'][0]
|
||||
|
||||
artist_info['biography'] = artist['strBiographyEN']
|
||||
artist_info['genre'] = artist['strGenre']
|
||||
artist_info['mood'] = artist['strMood']
|
||||
artist_info['thumb_url'] = artist['strArtistThumb']
|
||||
artist_info['biography'] = artist.get('strBiographyEN')
|
||||
artist_info['genre'] = artist.get('strGenre')
|
||||
artist_info['mood'] = artist.get('strMood')
|
||||
artist_info['thumb_url'] = artist.get('strArtistThumb')
|
||||
|
||||
return artist_info
|
||||
|
||||
|
||||
def lookup_album_from_tadb(name: str, artist: str) -> dict:
|
||||
album_info = {}
|
||||
response = requests.get(''.join([ALBUM_SEARCH_URL, artist, "&a=", name]))
|
||||
|
||||
if response.status_code != 200:
|
||||
logger.warn(f"Bad response from TADB: {response.status_code}")
|
||||
return {}
|
||||
|
||||
if not response.content:
|
||||
logger.warn(f"Bad content from TADB: {response.content}")
|
||||
return {}
|
||||
|
||||
results = json.loads(response.content)
|
||||
if results['album']:
|
||||
album = results['album'][0]
|
||||
|
||||
album_info['theaudiodb_id'] = album.get('idAlbum')
|
||||
album_info['theaudiodb_description'] = album.get('strDescriptionEN')
|
||||
album_info['theaudiodb_genre'] = album.get('strGenre')
|
||||
album_info['theaudiodb_style'] = album.get('strStyle')
|
||||
album_info['theaudiodb_mood'] = album.get('strMood')
|
||||
album_info['theaudiodb_speed'] = album.get('strSpeed')
|
||||
album_info['theaudiodb_theme'] = album.get('strTheme')
|
||||
album_info['theaudiodb_year_released'] = album.get('intYearReleased')
|
||||
album_info['allmusic_id'] = album.get('strAllMusicID')
|
||||
album_info['wikipedia_slug'] = album.get('strWikipediaID')
|
||||
album_info['discogs_id'] = album.get('strDiscogsID')
|
||||
album_info['wikidata_id'] = album.get('strWikidataID')
|
||||
album_info['rateyourmusic_id'] = album.get('strRateYourMusicID')
|
||||
|
||||
if album.get('intScore'):
|
||||
album_info['theaudiodb_score'] = float(album.get('intScore'))
|
||||
if album.get('intScoreVotes'):
|
||||
album_info['theaudiodb_score_votes'] = int(
|
||||
album.get('intScoreVotes')
|
||||
)
|
||||
|
||||
return album_info
|
||||
|
||||
@ -231,11 +231,10 @@ USE_TZ = True
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
||||
|
||||
STATIC_URL = "static/"
|
||||
STATIC_URL = "/static/"
|
||||
STATIC_ROOT = os.getenv(
|
||||
"VROBBLER_STATIC_ROOT", os.path.join(PROJECT_ROOT, "static")
|
||||
)
|
||||
|
||||
MEDIA_URL = "/media/"
|
||||
MEDIA_ROOT = os.getenv(
|
||||
"VROBBLER_MEDIA_ROOT", os.path.join(PROJECT_ROOT, "media")
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" type="image/png" href="{% static 'images/favicon.ico' %}"/>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
|
||||
@ -211,6 +212,7 @@
|
||||
Dashboard
|
||||
</a>
|
||||
</li>
|
||||
{% if user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="/charts/">
|
||||
<span data-feather="bar-chart"></span>
|
||||
@ -229,6 +231,12 @@
|
||||
Artists
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/albums/">
|
||||
<span data-feather="music"></span>
|
||||
Albums
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/movies/">
|
||||
<span data-feather="film"></span>
|
||||
@ -241,7 +249,6 @@
|
||||
TV Shows
|
||||
</a>
|
||||
</li>
|
||||
{% if user.is_authenticated %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/admin/">
|
||||
<span data-feather="key"></span>
|
||||
@ -260,13 +267,13 @@
|
||||
{% for scrobble in now_playing_list %}
|
||||
<div>
|
||||
{% if scrobble.media_obj.album.cover_image %}
|
||||
<td><img src="{{scrobble.track.album.cover_image.url}}" width=120 height=120
|
||||
style="border:1px solid black; " /></td><br/>
|
||||
<td><img src="{{scrobble.track.album.cover_image.url}}" width=120 height=120 style="border:1px solid black; " /></td><br/>
|
||||
{% endif %}
|
||||
{{scrobble.media_obj.title}}<br/>
|
||||
{% if scrobble.media_obj.subtitle %}<em>{{scrobble.media_obj.subtitle}}</em><br/>{% endif %}
|
||||
<small>{{scrobble.timestamp|naturaltime}}<br/>
|
||||
from {{scrobble.source}}</small>
|
||||
<a href="{{scrobble.media_obj.get_absolute_url}}">{{scrobble.media_obj.title}}</a><br/>
|
||||
{% if scrobble.media_obj.subtitle %}
|
||||
<em><a href="{{scrobble.media_obj.subtitle.get_absolute_url}}">{{scrobble.media_obj.subtitle}}</a></em><br/>
|
||||
{% endif %}
|
||||
<small>{{scrobble.timestamp|naturaltime}}<br/> from {{scrobble.source}}</small>
|
||||
<div class="progress-bar" style="margin-right:5px;">
|
||||
<span class="progress-bar-fill" style="width: {{scrobble.percent_played}}%;"></span>
|
||||
</div>
|
||||
|
||||
@ -12,6 +12,13 @@
|
||||
<img src="{{object.cover_image.url}}" width=300 height=300 />
|
||||
</p>
|
||||
{% endif %}
|
||||
<div style="float:left; width:600px; margin-left:10px; ">
|
||||
{% if object.theaudiodb_description %}
|
||||
<p>{{object.theaudiodb_description|safe|linebreaks|truncatewords:160}}</p>
|
||||
<hr/>
|
||||
{% endif %}
|
||||
<p><a href="{{album.mb_link}}">Musicbrainz</a> {% if album.tadb_link %}| <a href="{{album.tadb_link}}">TheAudioDB</a>{% endif %} {% if album.allmusic_link %}| <a href="{{album.allmusic_link}}">AllMusic</a>{% endif %}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p>{{object.scrobbles.count}} scrobbles</p>
|
||||
|
||||
@ -1,11 +1,82 @@
|
||||
{% extends "base_list.html" %}
|
||||
{% load urlreplace %}
|
||||
|
||||
{% block title %}Albums{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
{% for album in object_list %}
|
||||
<dl style="width: 130px; float: left; margin-right:10px;">
|
||||
<dd><img src="{{album.cover_image.url}}" width=120 height=120 /></dd>
|
||||
</dl>
|
||||
{% endfor %}
|
||||
|
||||
<div class="row">
|
||||
<p class="view">
|
||||
<span class="view-links">
|
||||
{% if view == 'grid' %}
|
||||
View as <a href="?{% urlreplace view='list' %}">List</a>
|
||||
{% else %}
|
||||
View as <a href="?{% urlreplace view='grid' %}">Grid</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
<p class="pagination">
|
||||
<span class="page-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?{% urlreplace page=page_obj.previous_page_number %}">prev</a>
|
||||
{% endif %}
|
||||
<span class="page-current">
|
||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
|
||||
</span>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?{% urlreplace page=page_obj.next_page_number %}">next</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
<hr />
|
||||
|
||||
{% if view == 'grid' %}
|
||||
<div>
|
||||
{% for album in object_list %}
|
||||
{% if album.cover_image %}
|
||||
<dl style="width: 130px; float: left; margin-right:10px;">
|
||||
<dd><img src="{{album.cover_image.url}}" width=120 height=120 /></dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-md">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Album</th>
|
||||
<th scope="col">Artist</th>
|
||||
<th scope="col">Scrobbles</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for album in object_list %}
|
||||
<tr>
|
||||
<td><a href="{{album.get_absolute_url}}">{{album}}</a></td>
|
||||
<td><a href="{{album.artist.get_absolute_url}}">{{album.artist}}</a></td>
|
||||
<td>{{album.scrobbles.count}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="pagination" style="margin-bottom:50px;">
|
||||
<span class="page-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?{% urlreplace page=page_obj.previous_page_number %}">prev</a>
|
||||
{% endif %}
|
||||
<span class="page-current">
|
||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
|
||||
</span>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?{% urlreplace page=page_obj.next_page_number %}">next</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -4,12 +4,11 @@
|
||||
{% block title %}{{object.name}}{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
|
||||
<div class="row">
|
||||
|
||||
{% if object.thumbnail %}
|
||||
<p style="float:left; width:302px; padding:0; border: 1px solid #ccc">
|
||||
<img src="{{artist.thumbnail.url}}" width=300 height=300 />
|
||||
<p style="float:left; width:300px; margin-right:10px;">
|
||||
<img style="border:1px solid #ccc;" src="{{artist.thumbnail.url}}" width=300 height=300 />
|
||||
</p>
|
||||
{% else %}
|
||||
{% if object.album_set.first.cover_image %}
|
||||
@ -18,6 +17,13 @@
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div style="float:left; width:600px; margin-left:10px; ">
|
||||
{% if artist.biography %}
|
||||
<p>{{artist.biography|safe|linebreaks|truncatewords:160}}</p>
|
||||
<hr/>
|
||||
{% endif %}
|
||||
<p><a href="{{artist.mb_link}}">Musicbrainz</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p>{{artist.scrobbles.count}} scrobbles</p>
|
||||
|
||||
@ -1,9 +1,45 @@
|
||||
{% extends "base_list.html" %}
|
||||
{% load urlreplace %}
|
||||
|
||||
{% block title %}Artists{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
<div class="row">
|
||||
<p class="view">
|
||||
<span class="view-links">
|
||||
{% if view == 'grid' %}
|
||||
View as <a href="?{% urlreplace view='list' %}">List</a>
|
||||
{% else %}
|
||||
View as <a href="?{% urlreplace view='grid' %}">Grid</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
<p class="pagination">
|
||||
<span class="page-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?{% urlreplace page=page_obj.previous_page_number %}">prev</a>
|
||||
{% endif %}
|
||||
<span class="page-current">
|
||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
|
||||
</span>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?{% urlreplace page=page_obj.next_page_number %}">next</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
<hr />
|
||||
|
||||
{% if view == 'grid' %}
|
||||
<div>
|
||||
{% for artist in object_list %}
|
||||
{% if artist.thumbnail %}
|
||||
<dl style="width: 130px; float: left; margin-right:10px;">
|
||||
<dd><img src="{{artist.thumbnail.url}}" width=120 height=120 /></dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-md">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
@ -26,5 +62,20 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="pagination" style="margin-bottom:50px;">
|
||||
<span class="page-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?{% urlreplace page=page_obj.previous_page_number %}">prev</a>
|
||||
{% endif %}
|
||||
<span class="page-current">
|
||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
|
||||
</span>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?{% urlreplace page=page_obj.next_page_number %}">next</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -3,10 +3,57 @@
|
||||
{% block title %}Tracks{% endblock %}
|
||||
|
||||
{% block lists %}
|
||||
<h2>All time</h2>
|
||||
{% for track in object_list %}
|
||||
<ul>
|
||||
<li><a href="{{track.get_absolute_url}}">{{track}}</a></li>
|
||||
</ul>
|
||||
{% endfor %}
|
||||
<div class="row">
|
||||
<p class="pagination">
|
||||
<span class="page-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
|
||||
{% endif %}
|
||||
<span class="page-current">
|
||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
|
||||
</span>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?page={{ page_obj.next_page_number }}">next</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
<hr />
|
||||
|
||||
<div class="col-md">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Track</th>
|
||||
<th scope="col">Artist</th>
|
||||
<th scope="col">Scrobbles</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for track in object_list %}
|
||||
<tr>
|
||||
<td><a href="{{track.get_absolute_url}}">{{track}}</a></td>
|
||||
<td><a href="{{track.artist.get_absolute_url}}">{{track.artist}}</a></td>
|
||||
<td>{{track.scrobble_set.count}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pagination" style="margin-bottom:50px;">
|
||||
<span class="page-links">
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
|
||||
{% endif %}
|
||||
<span class="page-current">
|
||||
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
|
||||
</span>
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?page={{ page_obj.next_page_number }}">next</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import scrobbles.views as scrobbles_views
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib import admin
|
||||
from django.urls import include, path
|
||||
from rest_framework import routers
|
||||
|
||||
import vrobbler.apps.scrobbles.views as scrobbles_views
|
||||
from vrobbler.apps.books.api.views import AuthorViewSet, BookViewSet
|
||||
from vrobbler.apps.music import urls as music_urls
|
||||
from vrobbler.apps.music.api.views import (
|
||||
@ -23,7 +21,6 @@ from vrobbler.apps.scrobbles.api.views import (
|
||||
from vrobbler.apps.sports.api.views import (
|
||||
LeagueViewSet,
|
||||
PlayerViewSet,
|
||||
RoundViewSet,
|
||||
SeasonViewSet,
|
||||
SportEventViewSet,
|
||||
SportViewSet,
|
||||
@ -65,11 +62,3 @@ urlpatterns = [
|
||||
"", scrobbles_views.RecentScrobbleList.as_view(), name="vrobbler-home"
|
||||
),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(
|
||||
settings.MEDIA_URL, document_root=settings.MEDIA_ROOT
|
||||
)
|
||||
urlpatterns += static(
|
||||
settings.STATIC_URL, document_root=settings.STATIC_ROOT
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user