Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f7981de65 | |||
| aff31e4c1d | |||
| c7c808023a | |||
| 898e275809 | |||
| 1aa28f978d | |||
| c21c98f6c4 | |||
| 54883d34c0 | |||
| 0cddc2dfb4 | |||
| b1deaee4e6 | |||
| 78dc7be703 | |||
| 79adf3d3f5 | |||
| da288f20cd | |||
| 0812a6b2cb | |||
| 507a65cfde | |||
| 90b523095a | |||
| e1f50955f1 | |||
| c3a7a2d204 | |||
| 31c6fd3265 | |||
| 7c92b33f91 | |||
| ef0f82d516 | |||
| 041333d1da | |||
| 6f867415f8 | |||
| 43c6d7168b | |||
| 7a282e23f5 | |||
| 902243b969 | |||
| 6f578eca6c | |||
| 6b75e787c4 | |||
| 3c02ec930b | |||
| 499a35bf50 | |||
| a7ac9951a6 | |||
| 25d9f8ab7e | |||
| 9263d88cee | |||
| 389b6db432 | |||
| 142d2986b6 | |||
| 4134e3a234 | |||
| 3185ca3c43 | |||
| ca378790f1 | |||
| d9050b35ba | |||
| bfa1c7879b | |||
| e7ca11d1bc | |||
| dba9b58a6a | |||
| 2a64d05b71 | |||
| ce5ec2a763 | |||
| 3576d85ac9 | |||
| 970eb9e19e | |||
| dfc242084e |
@@ -3,6 +3,8 @@
|
|||||||
db.sqlite3
|
db.sqlite3
|
||||||
*.pyc
|
*.pyc
|
||||||
*/__pycache__/
|
*/__pycache__/
|
||||||
*/migrations/
|
|
||||||
albums/
|
albums/
|
||||||
wallpapers/
|
wallpapers/
|
||||||
|
frontend/node_modules/
|
||||||
|
frontend/.next/
|
||||||
|
frontend/src/data/types.ts
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
SPOTIFY_ENCODED_ID=<Spotify-API-Token>
|
SPOTIFY_ENCODED_ID=<Spotify-API-Token>
|
||||||
OIDC_RP_CLIENT_ID=<OIDC-Client-ID>
|
OIDC_RP_CLIENT_ID=<OIDC-Client-ID>
|
||||||
OIDC_RP_CLIENT_SECRET=<OIDC-Client-Secret>
|
OIDC_RP_CLIENT_SECRET=<OIDC-Client-Secret>
|
||||||
|
OIDC_RP_SIGN_ALGO=RS256
|
||||||
OIDC_OP_AUTHORIZATION_ENDPOINT=https://auth.yourwebsite.com/application/o/authorize/
|
OIDC_OP_AUTHORIZATION_ENDPOINT=https://auth.yourwebsite.com/application/o/authorize/
|
||||||
OIDC_OP_TOKEN_ENDPOINT=https://auth.yourwebsite.com/application/o/token/
|
OIDC_OP_TOKEN_ENDPOINT=https://auth.yourwebsite.com/application/o/token/
|
||||||
OIDC_OP_USER_ENDPOINT=https://auth.yourwebsite.com/application/o/userinfo/
|
OIDC_OP_USER_ENDPOINT=https://auth.yourwebsite.com/application/o/userinfo/
|
||||||
|
OIDC_OP_JWKS_ENDPOINT=https://auth.yourwebsite.com/application/o/name/jwks/
|
||||||
AI_ENDPOINT=http://localhost:11434/api/generate
|
AI_ENDPOINT=http://localhost:11434/api/generate
|
||||||
AI_MODEL=myModel
|
AI_MODEL=myModel
|
||||||
DATE_OPEN=<ISO-formatted date>
|
DATE_OPEN=<ISO-formatted date>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
from rest_framework.authentication import BaseAuthentication
|
||||||
|
from rest_framework.exceptions import AuthenticationFailed
|
||||||
|
from mozilla_django_oidc.auth import OIDCAuthenticationBackend
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
class OIDCBearerTokenAuthentication(BaseAuthentication):
|
||||||
|
def authenticate(self, request):
|
||||||
|
auth = request.META.get('HTTP_AUTHORIZATION', '')
|
||||||
|
if not auth.startswith('Bearer '):
|
||||||
|
return None
|
||||||
|
token = auth.split(' ')[1]
|
||||||
|
|
||||||
|
backend = OIDCAuthenticationBackend()
|
||||||
|
|
||||||
|
try:
|
||||||
|
claims = backend.verify_token(token)
|
||||||
|
except Exception as e:
|
||||||
|
raise AuthenticationFailed(f'Invalid token: {e}')
|
||||||
|
|
||||||
|
user = backend.filter_users_by_claims(claims).first()
|
||||||
|
if not user:
|
||||||
|
raise AuthenticationFailed(f'Unknown user: {claims}')
|
||||||
|
return (user, None)
|
||||||
@@ -13,6 +13,7 @@ SECRET_KEY = 'oefjei1lwe918Alkfwuf3ionwu-@kt)6m1e)ah$&^_i9y!qffhm-a$#m6+++'
|
|||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
ALLOWED_HOSTS = ['*']
|
||||||
|
CORS_ALLOW_ALL_ORIGINS = True
|
||||||
|
|
||||||
CURRENT_HOST = 'http://127.0.0.1:8000' # Needs to be set for spotify callback to work.
|
CURRENT_HOST = 'http://127.0.0.1:8000' # Needs to be set for spotify callback to work.
|
||||||
|
|
||||||
@@ -26,12 +27,15 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'django_htmx',
|
'django_htmx',
|
||||||
'rest_framework'
|
'rest_framework',
|
||||||
|
'corsheaders',
|
||||||
|
'drf_spectacular',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
"corsheaders.middleware.CorsMiddleware",
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
@@ -88,12 +92,20 @@ AUTHENTICATION_BACKENDS = (
|
|||||||
|
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||||
|
'muzak.auth.OIDCBearerTokenAuthentication',
|
||||||
'mozilla_django_oidc.contrib.drf.OIDCAuthentication',
|
'mozilla_django_oidc.contrib.drf.OIDCAuthentication',
|
||||||
'rest_framework.authentication.SessionAuthentication',
|
'rest_framework.authentication.SessionAuthentication',
|
||||||
],
|
],
|
||||||
'DEFAULT_PERMISSION_CLASSES': [
|
'DEFAULT_PERMISSION_CLASSES': [
|
||||||
'rest_framework.permissions.IsAuthenticated',
|
'rest_framework.permissions.IsAuthenticated',
|
||||||
]
|
],
|
||||||
|
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
|
||||||
|
}
|
||||||
|
|
||||||
|
SPECTACULAR_SETTINGS = {
|
||||||
|
'TITLE': 'HDcon Muzak',
|
||||||
|
'DESCRIPTION': 'Gewoon een leuk tooltje om samen met je vrienden een muzieklijst samen te stellen.',
|
||||||
|
'VERSION': '1.0.0',
|
||||||
}
|
}
|
||||||
|
|
||||||
OIDC_RP_CLIENT_ID = os.getenv('OIDC_RP_CLIENT_ID')
|
OIDC_RP_CLIENT_ID = os.getenv('OIDC_RP_CLIENT_ID')
|
||||||
@@ -101,6 +113,8 @@ OIDC_RP_CLIENT_SECRET = os.getenv('OIDC_RP_CLIENT_SECRET')
|
|||||||
OIDC_OP_AUTHORIZATION_ENDPOINT = os.getenv('OIDC_OP_AUTHORIZATION_ENDPOINT')
|
OIDC_OP_AUTHORIZATION_ENDPOINT = os.getenv('OIDC_OP_AUTHORIZATION_ENDPOINT')
|
||||||
OIDC_OP_TOKEN_ENDPOINT = os.getenv('OIDC_OP_TOKEN_ENDPOINT')
|
OIDC_OP_TOKEN_ENDPOINT = os.getenv('OIDC_OP_TOKEN_ENDPOINT')
|
||||||
OIDC_OP_USER_ENDPOINT = os.getenv('OIDC_OP_USER_ENDPOINT')
|
OIDC_OP_USER_ENDPOINT = os.getenv('OIDC_OP_USER_ENDPOINT')
|
||||||
|
OIDC_RP_SIGN_ALGO = os.getenv('OIDC_RP_SIGN_ALGO')
|
||||||
|
OIDC_OP_JWKS_ENDPOINT = os.getenv('OIDC_OP_JWKS_ENDPOINT')
|
||||||
LOGIN_REDIRECT_URL = "/"
|
LOGIN_REDIRECT_URL = "/"
|
||||||
LOGOUT_REDIRECT_URL = "/"
|
LOGOUT_REDIRECT_URL = "/"
|
||||||
LOGIN_URL = "/"
|
LOGIN_URL = "/"
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-04 22:50
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Artist',
|
||||||
|
fields=[
|
||||||
|
('id', models.CharField(max_length=128, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=255)),
|
||||||
|
('popularity', models.SmallIntegerField(blank=True, null=True)),
|
||||||
|
('genres', models.JSONField()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Album',
|
||||||
|
fields=[
|
||||||
|
('id', models.CharField(max_length=128, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=255)),
|
||||||
|
('album_type', models.CharField(max_length=255)),
|
||||||
|
('total_tracks', models.PositiveSmallIntegerField(null=True)),
|
||||||
|
('image', models.ImageField(upload_to='albums/')),
|
||||||
|
('artists', models.ManyToManyField(to='playlist.artist')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Track',
|
||||||
|
fields=[
|
||||||
|
('id', models.CharField(max_length=128, primary_key=True, serialize=False)),
|
||||||
|
('title', models.CharField(max_length=255)),
|
||||||
|
('explicit', models.BooleanField(default=False)),
|
||||||
|
('duration_ms', models.PositiveIntegerField(null=True)),
|
||||||
|
('popularity', models.PositiveIntegerField(null=True)),
|
||||||
|
('banter', models.TextField()),
|
||||||
|
('album', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='playlist.album')),
|
||||||
|
('artists', models.ManyToManyField(to='playlist.artist')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-05 21:06
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='track',
|
||||||
|
name='old',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-05 21:11
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0002_track_old'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='track',
|
||||||
|
old_name='title',
|
||||||
|
new_name='name',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-05 22:11
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0003_rename_title_track_name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='artist',
|
||||||
|
name='genres',
|
||||||
|
field=models.JSONField(null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='track',
|
||||||
|
name='banter',
|
||||||
|
field=models.TextField(blank=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-07 12:26
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0004_alter_artist_genres_alter_track_banter'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Vote',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('points', models.IntegerField()),
|
||||||
|
('track', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='playlist.track')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'unique_together': {('user', 'track')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-07 19:48
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0005_vote'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='track',
|
||||||
|
name='banter_done',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-12 13:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0006_track_banter_done'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='album',
|
||||||
|
name='image_url',
|
||||||
|
field=models.URLField(null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-21 13:26
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0007_album_image_url'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Background',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('image', models.ImageField(upload_to='wallpapers/')),
|
||||||
|
('repeat', models.BooleanField(default=False)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Profile',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('background', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='playlist.background')),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-21 13:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0008_background_profile'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='background',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(default='Wallpaper', max_length=128),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-21 14:38
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0009_background_name'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='background',
|
||||||
|
name='color',
|
||||||
|
field=models.CharField(blank=True, max_length=32),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-21 14:38
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0010_background_color'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='background',
|
||||||
|
name='image',
|
||||||
|
field=models.ImageField(null=True, upload_to='wallpapers/'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-21 14:39
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0011_alter_background_image'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='background',
|
||||||
|
name='image',
|
||||||
|
field=models.ImageField(blank=True, null=True, upload_to='wallpapers/'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-26 18:38
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0012_alter_background_image'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='profile',
|
||||||
|
name='last_voted',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='playlist.track'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-26 20:46
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0013_profile_last_voted'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='profile',
|
||||||
|
name='quota',
|
||||||
|
field=models.FloatField(default=10.0),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='profile',
|
||||||
|
name='quota_last_updated',
|
||||||
|
field=models.DateTimeField(auto_now=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-02-26 22:17
|
||||||
|
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0014_profile_quota_profile_quota_last_updated'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='profile',
|
||||||
|
name='quota_last_updated',
|
||||||
|
field=models.DateTimeField(default=django.utils.timezone.now),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-03-08 23:53
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0015_alter_profile_quota_last_updated'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='profile',
|
||||||
|
name='spt_access_token',
|
||||||
|
field=models.CharField(default='', max_length=128),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='profile',
|
||||||
|
name='spt_refresh_token',
|
||||||
|
field=models.CharField(default='', max_length=128),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Playlist',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255)),
|
||||||
|
('tracks', models.ManyToManyField(to='playlist.track')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-03-08 23:56
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0016_profile_spt_access_token_profile_spt_refresh_token_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='profile',
|
||||||
|
name='spt_expires',
|
||||||
|
field=models.DateTimeField(default=None, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 5.0.1 on 2024-03-13 08:38
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0017_profile_spt_expires'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='profile',
|
||||||
|
name='spt_access_token',
|
||||||
|
field=models.CharField(default='', max_length=128, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='profile',
|
||||||
|
name='spt_refresh_token',
|
||||||
|
field=models.CharField(default='', max_length=128, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.4 on 2024-07-20 06:58
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0018_alter_profile_spt_access_token_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='vote',
|
||||||
|
name='skipped',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 5.0.4 on 2024-07-21 07:59
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0019_vote_skipped'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='track',
|
||||||
|
name='nominated_by',
|
||||||
|
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='profile',
|
||||||
|
name='spt_access_token',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=256, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='profile',
|
||||||
|
name='spt_refresh_token',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=256, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Generated by Django 5.0.4 on 2024-07-22 20:32
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0020_track_nominated_by_alter_profile_spt_access_token_and_more'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='track',
|
||||||
|
name='from_playlist',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='track',
|
||||||
|
name='album',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='playlist.album'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='track',
|
||||||
|
name='banter',
|
||||||
|
field=models.TextField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='track',
|
||||||
|
name='duration_ms',
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='track',
|
||||||
|
name='nominated_by',
|
||||||
|
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='track',
|
||||||
|
name='popularity',
|
||||||
|
field=models.PositiveIntegerField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.0.4 on 2024-07-23 11:29
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('playlist', '0021_track_from_playlist_alter_track_album_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='background',
|
||||||
|
name='cover',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -104,6 +104,11 @@ class Track(models.Model):
|
|||||||
artists = list(map(lambda a:a.name, self.artists.all()))
|
artists = list(map(lambda a:a.name, self.artists.all()))
|
||||||
return " & ".join(artists)
|
return " & ".join(artists)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def album_cover(self):
|
||||||
|
if self.album:
|
||||||
|
return self.album.image_url
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name_sanitized(self):
|
def name_sanitized(self):
|
||||||
return re.match("[^\-\(]*\w", self.name)[0]
|
return re.match("[^\-\(]*\w", self.name)[0]
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
from os import extsep
|
||||||
|
from django.db.models.functions import Extract
|
||||||
|
from playlist.models import Album
|
||||||
|
from rest_framework import serializers
|
||||||
|
from drf_spectacular.utils import extend_schema_serializer, OpenApiExample
|
||||||
|
|
||||||
|
@extend_schema_serializer(
|
||||||
|
examples = [
|
||||||
|
OpenApiExample(
|
||||||
|
name='Album Example',
|
||||||
|
value = {
|
||||||
|
"url": "http://muzak.hoekveen.net/api/album/7MejfRSNnrpcLZIxkeZDqR",
|
||||||
|
"name": "Leftoverture (Expanded Edition)",
|
||||||
|
"album_type": "album",
|
||||||
|
"total_tracks": 10,
|
||||||
|
"image": "http://127.0.0.1:8001/albums/Leftoverture_Expanded_Edition.png",
|
||||||
|
"image_url": "https://i.scdn.co/image/ab67616d0000b2731be40e44db112e123e5e8b51",
|
||||||
|
"artists": [
|
||||||
|
"http://127.0.0.1:8001/api/artist/2hl0xAkS2AIRAu23TVMBG1"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
OpenApiExample(
|
||||||
|
name = 'Multiple artist single',
|
||||||
|
value = {
|
||||||
|
"url": "http://127.0.0.1:8001/api/album/1pHD8AFu4z1CvuTPjZFOFi",
|
||||||
|
"name": "ラビリンス",
|
||||||
|
"album_type": "single",
|
||||||
|
"total_tracks": 4,
|
||||||
|
"image": None,
|
||||||
|
"image_url": "https://i.scdn.co/image/ab67616d0000b2731607d3aa3a69ca0e1ffbe26b",
|
||||||
|
"artists": [
|
||||||
|
"http://127.0.0.1:8001/api/artist/4ZX8Wr8KHHrW7radu6IwYG",
|
||||||
|
"http://127.0.0.1:8001/api/artist/4d2zOuYJHBPJTpVblHEKJb"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
class AlbumSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Album
|
||||||
|
fields = '__all__'
|
||||||
|
extra_kwargs = {
|
||||||
|
'url': {'view_name': 'api-album', 'lookup_field': 'id'},
|
||||||
|
'artists': {'view_name': 'api-artist', 'lookup_field': 'id'},
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
from playlist.models import Artist
|
||||||
|
from rest_framework import serializers
|
||||||
|
from drf_spectacular.utils import extend_schema_serializer, OpenApiExample
|
||||||
|
|
||||||
|
@extend_schema_serializer(
|
||||||
|
examples=[
|
||||||
|
OpenApiExample(
|
||||||
|
'Daft Punk',
|
||||||
|
value = {
|
||||||
|
"id": "4tZwfgrHOc3mvqYlEYSvVi",
|
||||||
|
"name": "Daft Punk",
|
||||||
|
"url": "http://127.0.0.1:8001/api/artist/4tZwfgrHOc3mvqYlEYSvVi"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
OpenApiExample(
|
||||||
|
'コリッキー',
|
||||||
|
value = {
|
||||||
|
"id": "307y5sbPNvRpXjBcZgS25q",
|
||||||
|
"name": "コリッキー",
|
||||||
|
"url": "http://127.0.0.1:8001/api/artist/307y5sbPNvRpXjBcZgS25q"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
class ArtistSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Artist
|
||||||
|
fields = ['id', 'name', 'url']
|
||||||
|
extra_kwargs = {
|
||||||
|
'url': {'view_name': 'api-artist', 'lookup_field': 'id'},
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
from playlist.models import Track
|
||||||
|
from rest_framework import serializers
|
||||||
|
from drf_spectacular.utils import extend_schema_serializer, OpenApiExample
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_serializer(
|
||||||
|
examples = [
|
||||||
|
OpenApiExample(
|
||||||
|
'Song with banter',
|
||||||
|
value={
|
||||||
|
"url": "http://127.0.0.1:8001/api/track/0AzD1FEuvkXP1verWfaZdv",
|
||||||
|
"name": "Cbat",
|
||||||
|
"explicit": False,
|
||||||
|
"duration_ms": 171720,
|
||||||
|
"popularity": 0,
|
||||||
|
"banter": "Als je denkt dat \"Cbat\" van Hudson Mohawke de soundtrack is voor het onhandig manoeuvreren van een stelletje klunzen die elkaar constant in de weg zitten, dan heb je waarschijnlijk gelijk. Dit nummer, waarin de beats net zo chaotisch zijn als de liefdesperikelen van een tiener op een eerste date, is het perfecte liedje voor iedereen die zich ooit heeft afgevraagd hoe je een kat kunt imiteren zonder dat je daadwerkelijk een kat bent.",
|
||||||
|
"banter_done": True,
|
||||||
|
"old": False,
|
||||||
|
"from_playlist": None,
|
||||||
|
"nominated_by": "http://127.0.0.1:8001/api/user/1",
|
||||||
|
"album": "http://127.0.0.1:8001/api/album/0d99LxnQpiPLgSGDRuU9HT",
|
||||||
|
"artists": [
|
||||||
|
"http://127.0.0.1:8001/api/artist/6olWbKW2VLhFCHfOi0iEDb"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
OpenApiExample(
|
||||||
|
'Song from old playlist',
|
||||||
|
description='While this track is from an old playlist, old is still set to False as it has been nominated again this year.',
|
||||||
|
value={
|
||||||
|
"url": "http://127.0.0.1:8001/api/track/4QGUlo1swUpXduW23JM57S",
|
||||||
|
"name": "You Bring On The Sun",
|
||||||
|
"explicit": False,
|
||||||
|
"duration_ms": 215400,
|
||||||
|
"popularity": 43,
|
||||||
|
"banter": None,
|
||||||
|
"banter_done": False,
|
||||||
|
"old": False,
|
||||||
|
"from_playlist": "HD Con 2020 A",
|
||||||
|
"nominated_by": None,
|
||||||
|
"album": "http://127.0.0.1:8001/api/album/01QglfxMtbN2EVEyGWpPyQ",
|
||||||
|
"artists": [
|
||||||
|
"http://127.0.0.1:8001/api/artist/0gcMPgunYh4rX1UOdvZKBn"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
class TrackSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
artist = serializers.CharField()
|
||||||
|
album_cover = serializers.CharField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Track
|
||||||
|
fields = '__all__'
|
||||||
|
extra_kwargs = {
|
||||||
|
'url': {'view_name': 'api-track', 'lookup_field': 'id'},
|
||||||
|
'artists': {'view_name': 'api-artist', 'lookup_field': 'id'},
|
||||||
|
'album': {'view_name': 'api-album', 'lookup_field': 'id'},
|
||||||
|
'nominated_by': {'view_name': 'api-user', 'lookup_field': 'id'},
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from django.contrib.auth.models import User
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
class UserSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['id', 'username', 'email']
|
||||||
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 556 B After Width: | Height: | Size: 556 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 462 B |
|
Before Width: | Height: | Size: 419 B After Width: | Height: | Size: 419 B |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 451 B |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 498 B After Width: | Height: | Size: 498 B |
|
Before Width: | Height: | Size: 749 B After Width: | Height: | Size: 749 B |
|
Before Width: | Height: | Size: 411 B After Width: | Height: | Size: 411 B |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
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 |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 394 B After Width: | Height: | Size: 394 B |
|
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 524 B |
|
Before Width: | Height: | Size: 683 B After Width: | Height: | Size: 683 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 239 B After Width: | Height: | Size: 239 B |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 393 B After Width: | Height: | Size: 393 B |
|
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 458 B |