diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b68d19c0..d77d91f17 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,14 +13,22 @@ stages: test_api: stage: test image: funkwhale/funkwhale:base + variables: + PIP_CACHE_DIR: "$CI_PROJECT_DIR/pip-cache" + DATABASE_URL: "sqlite://" before_script: + - python3 -m venv --copies virtualenv + - source virtualenv/bin/activate - cd api + - pip install -r requirements/base.txt + - pip install -r requirements/local.txt - pip install -r requirements/test.txt script: - pytest - variables: - DATABASE_URL: "sqlite://" - + cache: + key: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME" + paths: + - "$CI_PROJECT_DIR/pip-cache" tags: - docker diff --git a/CHANGELOG b/CHANGELOG index 6faa5d267..446146c29 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,10 +2,30 @@ Changelog ========= -0.2.5 (unreleased) +0.2.6 (Unreleased) ------------------ + +0.2.5 (2017-12-15) +------------------ + +Features: + +- Import: can now specify search template when querying import sources (#45) +- Login form: now redirect to previous page after login (#2) +- 404: a decent 404 template, at least (#48) + +Bugfixes: + +- Player: better handling of errors when fetching the audio file (#46) +- Csrf: default CSRF_TRUSTED_ORIGINS to ALLOWED_HOSTS to avoid Csrf issues on admin (#49) + +Tech: + +- Django 2 compatibility, lot of packages upgrades (#47) + + 0.2.4 (2017-12-14) ------------------ diff --git a/api/Dockerfile b/api/Dockerfile index bb1942c22..def9a4316 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -8,7 +8,9 @@ COPY ./requirements.apt /requirements.apt RUN apt-get update -qq && grep "^[^#;]" requirements.apt | xargs apt-get install -y -COPY ./requirements /requirements +COPY ./requirements/base.txt /requirements +RUN pip install -r /requirements/base.txt +COPY ./requirements/production.txt /requirements RUN pip install -r /requirements/production.txt COPY . /app diff --git a/api/config/api_urls.py b/api/config/api_urls.py index 5ed4cffdd..13205fe3d 100644 --- a/api/config/api_urls.py +++ b/api/config/api_urls.py @@ -25,22 +25,32 @@ v1_patterns = router.urls v1_patterns += [ url(r'^providers/', - include('funkwhale_api.providers.urls', namespace='providers')), + include( + ('funkwhale_api.providers.urls', 'providers'), + namespace='providers')), url(r'^favorites/', - include('funkwhale_api.favorites.urls', namespace='favorites')), + include( + ('funkwhale_api.favorites.urls', 'favorites'), + namespace='favorites')), url(r'^search$', views.Search.as_view(), name='search'), url(r'^radios/', - include('funkwhale_api.radios.urls', namespace='radios')), + include( + ('funkwhale_api.radios.urls', 'radios'), + namespace='radios')), url(r'^history/', - include('funkwhale_api.history.urls', namespace='history')), + include( + ('funkwhale_api.history.urls', 'history'), + namespace='history')), url(r'^users/', - include('funkwhale_api.users.api_urls', namespace='users')), + include( + ('funkwhale_api.users.api_urls', 'users'), + namespace='users')), url(r'^token/', jwt_views.obtain_jwt_token), url(r'^token/refresh/', jwt_views.refresh_jwt_token), ] urlpatterns = [ - url(r'^v1/', include(v1_patterns, namespace='v1')) + url(r'^v1/', include((v1_patterns, 'v1'), namespace='v1')) ] diff --git a/api/config/settings/common.py b/api/config/settings/common.py index b10a0310c..9804bb9c0 100644 --- a/api/config/settings/common.py +++ b/api/config/settings/common.py @@ -75,7 +75,7 @@ INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS # MIDDLEWARE CONFIGURATION # ------------------------------------------------------------------------------ -MIDDLEWARE_CLASSES = ( +MIDDLEWARE = ( # Make sure djangosecure.middleware.SecurityMiddleware is listed first 'django.contrib.sessions.middleware.SessionMiddleware', 'funkwhale_api.users.middleware.AnonymousSessionMiddleware', diff --git a/api/config/settings/local.py b/api/config/settings/local.py index e8108e98b..e0d497e79 100644 --- a/api/config/settings/local.py +++ b/api/config/settings/local.py @@ -31,7 +31,7 @@ EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND', # django-debug-toolbar # ------------------------------------------------------------------------------ -MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',) +MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware',) # INTERNAL_IPS = ('127.0.0.1', '10.0.2.2',) diff --git a/api/config/settings/production.py b/api/config/settings/production.py index a132076c7..e00983305 100644 --- a/api/config/settings/production.py +++ b/api/config/settings/production.py @@ -36,7 +36,7 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') # # # # Make sure djangosecure.middleware.SecurityMiddleware is listed first -# MIDDLEWARE_CLASSES = SECURITY_MIDDLEWARE + MIDDLEWARE_CLASSES +# MIDDLEWARE = SECURITY_MIDDLEWARE + MIDDLEWARE # # # set this to 60 seconds and then to 518400 when you can prove it works # SECURE_HSTS_SECONDS = 60 @@ -55,6 +55,8 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') # Hosts/domain names that are valid for this site # See https://docs.djangoproject.com/en/1.6/ref/settings/#allowed-hosts ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS') +CSRF_TRUSTED_ORIGINS = ALLOWED_HOSTS + # END SITE CONFIGURATION INSTALLED_APPS += ("gunicorn", ) diff --git a/api/config/settings/test.py b/api/config/settings/test.py index 7d02c417b..105645496 100644 --- a/api/config/settings/test.py +++ b/api/config/settings/test.py @@ -22,8 +22,8 @@ CACHES = { 'LOCATION': '' } } -INSTALLED_APPS += ('kombu.transport.django',) -BROKER_URL = 'django://' + +BROKER_URL = 'memory://' # TESTING # ------------------------------------------------------------------------------ diff --git a/api/config/urls.py b/api/config/urls.py index 8764640d1..8c490a5e6 100644 --- a/api/config/urls.py +++ b/api/config/urls.py @@ -10,9 +10,9 @@ from django.views import defaults as default_views urlpatterns = [ # Django Admin, use {% url 'admin:index' %} - url(settings.ADMIN_URL, include(admin.site.urls)), + url(settings.ADMIN_URL, admin.site.urls), - url(r'^api/', include("config.api_urls", namespace="api")), + url(r'^api/', include(("config.api_urls", 'api'), namespace="api")), url(r'^api/auth/', include('rest_auth.urls')), url(r'^api/auth/registration/', include('funkwhale_api.users.rest_auth_urls')), url(r'^accounts/', include('allauth.urls')), diff --git a/api/funkwhale_api/__init__.py b/api/funkwhale_api/__init__.py index 92ed1ff33..28e4b3203 100644 --- a/api/funkwhale_api/__init__.py +++ b/api/funkwhale_api/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -__version__ = '0.2.4' +__version__ = '0.2.5' __version_info__ = tuple([int(num) if num.isdigit() else num for num in __version__.replace('-', '.', 1).split('.')]) diff --git a/api/funkwhale_api/common/permissions.py b/api/funkwhale_api/common/permissions.py index 3f13b2005..6b61d9839 100644 --- a/api/funkwhale_api/common/permissions.py +++ b/api/funkwhale_api/common/permissions.py @@ -7,5 +7,5 @@ class ConditionalAuthentication(BasePermission): def has_permission(self, request, view): if settings.API_AUTHENTICATION_REQUIRED: - return request.user and request.user.is_authenticated() + return request.user and request.user.is_authenticated return True diff --git a/api/funkwhale_api/contrib/sites/migrations/0001_initial.py b/api/funkwhale_api/contrib/sites/migrations/0001_initial.py index 555d02c42..cf95cec65 100644 --- a/api/funkwhale_api/contrib/sites/migrations/0001_initial.py +++ b/api/funkwhale_api/contrib/sites/migrations/0001_initial.py @@ -25,7 +25,7 @@ class Migration(migrations.Migration): 'ordering': ('domain',), }, managers=[ - (b'objects', django.contrib.sites.models.SiteManager()), + ('objects', django.contrib.sites.models.SiteManager()), ], ), ] diff --git a/api/funkwhale_api/favorites/migrations/0001_initial.py b/api/funkwhale_api/favorites/migrations/0001_initial.py index 0a6f0e5fc..c2bd03182 100644 --- a/api/funkwhale_api/favorites/migrations/0001_initial.py +++ b/api/funkwhale_api/favorites/migrations/0001_initial.py @@ -19,8 +19,8 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(serialize=False, auto_created=True, verbose_name='ID', primary_key=True)), ('creation_date', models.DateTimeField(default=django.utils.timezone.now)), - ('track', models.ForeignKey(related_name='track_favorites', to='music.Track')), - ('user', models.ForeignKey(related_name='track_favorites', to=settings.AUTH_USER_MODEL)), + ('track', models.ForeignKey(related_name='track_favorites', to='music.Track', on_delete=models.CASCADE)), + ('user', models.ForeignKey(related_name='track_favorites', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], options={ 'ordering': ('-creation_date',), diff --git a/api/funkwhale_api/favorites/models.py b/api/funkwhale_api/favorites/models.py index f9c6426e6..899ed9cff 100644 --- a/api/funkwhale_api/favorites/models.py +++ b/api/funkwhale_api/favorites/models.py @@ -5,8 +5,10 @@ from funkwhale_api.music.models import Track class TrackFavorite(models.Model): creation_date = models.DateTimeField(default=timezone.now) - user = models.ForeignKey('users.User', related_name='track_favorites') - track = models.ForeignKey(Track, related_name='track_favorites') + user = models.ForeignKey( + 'users.User', related_name='track_favorites', on_delete=models.CASCADE) + track = models.ForeignKey( + Track, related_name='track_favorites', on_delete=models.CASCADE) class Meta: unique_together = ('track', 'user') diff --git a/api/funkwhale_api/favorites/tests/test_favorites.py b/api/funkwhale_api/favorites/tests/test_favorites.py index 230f030fe..78c64a413 100644 --- a/api/funkwhale_api/favorites/tests/test_favorites.py +++ b/api/funkwhale_api/favorites/tests/test_favorites.py @@ -1,6 +1,6 @@ import json from test_plus.test import TestCase -from django.core.urlresolvers import reverse +from django.urls import reverse from funkwhale_api.music.models import Track, Artist from funkwhale_api.favorites.models import TrackFavorite diff --git a/api/funkwhale_api/history/migrations/0001_initial.py b/api/funkwhale_api/history/migrations/0001_initial.py index 5ddfc26f3..7b6f950ed 100644 --- a/api/funkwhale_api/history/migrations/0001_initial.py +++ b/api/funkwhale_api/history/migrations/0001_initial.py @@ -20,8 +20,8 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), ('end_date', models.DateTimeField(null=True, blank=True, default=django.utils.timezone.now)), ('session_key', models.CharField(null=True, blank=True, max_length=100)), - ('track', models.ForeignKey(related_name='listenings', to='music.Track')), - ('user', models.ForeignKey(blank=True, null=True, related_name='listenings', to=settings.AUTH_USER_MODEL)), + ('track', models.ForeignKey(related_name='listenings', to='music.Track', on_delete=models.CASCADE)), + ('user', models.ForeignKey(blank=True, null=True, related_name='listenings', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], options={ 'ordering': ('-end_date',), diff --git a/api/funkwhale_api/history/models.py b/api/funkwhale_api/history/models.py index 0810ecf81..f7f62de62 100644 --- a/api/funkwhale_api/history/models.py +++ b/api/funkwhale_api/history/models.py @@ -7,8 +7,14 @@ from funkwhale_api.music.models import Track class Listening(models.Model): end_date = models.DateTimeField(default=timezone.now, null=True, blank=True) - track = models.ForeignKey(Track, related_name="listenings") - user = models.ForeignKey('users.User', related_name="listenings", null=True, blank=True) + track = models.ForeignKey( + Track, related_name="listenings", on_delete=models.CASCADE) + user = models.ForeignKey( + 'users.User', + related_name="listenings", + null=True, + blank=True, + on_delete=models.CASCADE) session_key = models.CharField(max_length=100, null=True, blank=True) class Meta: diff --git a/api/funkwhale_api/history/tests/factories.py b/api/funkwhale_api/history/tests/factories.py new file mode 100644 index 000000000..0a411adf0 --- /dev/null +++ b/api/funkwhale_api/history/tests/factories.py @@ -0,0 +1,12 @@ +import factory +from funkwhale_api.music.tests import factories + +from funkwhale_api.users.tests.factories import UserFactory + + +class ListeningFactory(factory.django.DjangoModelFactory): + user = factory.SubFactory(UserFactory) + track = factory.SubFactory(factories.TrackFactory) + + class Meta: + model = 'history.Listening' diff --git a/api/funkwhale_api/history/tests/test_history.py b/api/funkwhale_api/history/tests/test_history.py index 61009615a..5cb45c946 100644 --- a/api/funkwhale_api/history/tests/test_history.py +++ b/api/funkwhale_api/history/tests/test_history.py @@ -1,15 +1,16 @@ import random import json from test_plus.test import TestCase -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.exceptions import ValidationError from django.utils import timezone -from model_mommy import mommy +from funkwhale_api.music.tests.factories import TrackFactory from funkwhale_api.users.models import User from funkwhale_api.history import models + class TestHistory(TestCase): def setUp(self): @@ -17,12 +18,12 @@ class TestHistory(TestCase): self.user = User.objects.create_user(username='test', email='test@test.com', password='test') def test_can_create_listening(self): - track = mommy.make('music.Track') + track = TrackFactory() now = timezone.now() l = models.Listening.objects.create(user=self.user, track=track) def test_anonymous_user_can_create_listening_via_api(self): - track = mommy.make('music.Track') + track = TrackFactory() url = self.reverse('api:v1:history:listenings-list') response = self.client.post(url, { 'track': track.pk, @@ -34,7 +35,7 @@ class TestHistory(TestCase): self.assertIsNotNone(listening.session_key) def test_logged_in_user_can_create_listening_via_api(self): - track = mommy.make('music.Track') + track = TrackFactory() self.client.login(username=self.user.username, password='test') diff --git a/api/funkwhale_api/history/views.py b/api/funkwhale_api/history/views.py index d65a70f87..32bad6060 100644 --- a/api/funkwhale_api/history/views.py +++ b/api/funkwhale_api/history/views.py @@ -22,14 +22,14 @@ class ListeningViewSet(mixins.CreateModelMixin, def get_queryset(self): queryset = super().get_queryset() - if self.request.user.is_authenticated(): + if self.request.user.is_authenticated: return queryset.filter(user=self.request.user) else: return queryset.filter(session_key=self.request.session.session_key) def get_serializer_context(self): context = super().get_serializer_context() - if self.request.user.is_authenticated(): + if self.request.user.is_authenticated: context['user'] = self.request.user else: context['session_key'] = self.request.session.session_key diff --git a/api/funkwhale_api/music/migrations/0001_initial.py b/api/funkwhale_api/music/migrations/0001_initial.py index e78647948..265b81577 100644 --- a/api/funkwhale_api/music/migrations/0001_initial.py +++ b/api/funkwhale_api/music/migrations/0001_initial.py @@ -44,7 +44,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')), ('creation_date', models.DateTimeField(default=django.utils.timezone.now)), - ('submitted_by', models.ForeignKey(related_name='imports', to=settings.AUTH_USER_MODEL)), + ('submitted_by', models.ForeignKey(related_name='imports', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), ], ), migrations.CreateModel( @@ -54,7 +54,7 @@ class Migration(migrations.Migration): ('source', models.URLField()), ('mbid', models.UUIDField(editable=False)), ('status', models.CharField(default='pending', choices=[('pending', 'Pending'), ('finished', 'finished')], max_length=30)), - ('batch', models.ForeignKey(related_name='jobs', to='music.ImportBatch')), + ('batch', models.ForeignKey(related_name='jobs', to='music.ImportBatch', on_delete=models.CASCADE)), ], ), migrations.CreateModel( @@ -64,8 +64,8 @@ class Migration(migrations.Migration): ('mbid', models.UUIDField(editable=False, blank=True, null=True)), ('creation_date', models.DateTimeField(default=django.utils.timezone.now)), ('title', models.CharField(max_length=255)), - ('album', models.ForeignKey(related_name='tracks', blank=True, null=True, to='music.Album')), - ('artist', models.ForeignKey(related_name='tracks', to='music.Artist')), + ('album', models.ForeignKey(related_name='tracks', blank=True, null=True, to='music.Album', on_delete=models.CASCADE)), + ('artist', models.ForeignKey(related_name='tracks', to='music.Artist', on_delete=models.CASCADE)), ], options={ 'abstract': False, @@ -78,12 +78,12 @@ class Migration(migrations.Migration): ('audio_file', models.FileField(upload_to='tracks')), ('source', models.URLField(blank=True, null=True)), ('duration', models.IntegerField(blank=True, null=True)), - ('track', models.ForeignKey(related_name='files', to='music.Track')), + ('track', models.ForeignKey(related_name='files', to='music.Track', on_delete=models.CASCADE)), ], ), migrations.AddField( model_name='album', name='artist', - field=models.ForeignKey(related_name='albums', to='music.Artist'), + field=models.ForeignKey(related_name='albums', to='music.Artist', on_delete=models.CASCADE), ), ] diff --git a/api/funkwhale_api/music/migrations/0009_auto_20160920_1614.py b/api/funkwhale_api/music/migrations/0009_auto_20160920_1614.py index 2046a7127..3a3d93989 100644 --- a/api/funkwhale_api/music/migrations/0009_auto_20160920_1614.py +++ b/api/funkwhale_api/music/migrations/0009_auto_20160920_1614.py @@ -39,11 +39,11 @@ class Migration(migrations.Migration): migrations.AddField( model_name='lyrics', name='work', - field=models.ForeignKey(related_name='lyrics', to='music.Work', blank=True, null=True), + field=models.ForeignKey(related_name='lyrics', to='music.Work', blank=True, null=True, on_delete=models.CASCADE), ), migrations.AddField( model_name='track', name='work', - field=models.ForeignKey(related_name='tracks', to='music.Work', blank=True, null=True), + field=models.ForeignKey(related_name='tracks', to='music.Work', blank=True, null=True, on_delete=models.CASCADE), ), ] diff --git a/api/funkwhale_api/music/models.py b/api/funkwhale_api/music/models.py index 95a47fd4a..b7cf0f25c 100644 --- a/api/funkwhale_api/music/models.py +++ b/api/funkwhale_api/music/models.py @@ -10,7 +10,7 @@ from django.conf import settings from django.db import models from django.core.files.base import ContentFile from django.core.files import File -from django.core.urlresolvers import reverse +from django.urls import reverse from django.utils import timezone from taggit.managers import TaggableManager from versatileimagefield.fields import VersatileImageField @@ -108,7 +108,8 @@ def import_tracks(instance, cleaned_data, raw_data): class Album(APIModelMixin): title = models.CharField(max_length=255) - artist = models.ForeignKey(Artist, related_name='albums') + artist = models.ForeignKey( + Artist, related_name='albums', on_delete=models.CASCADE) release_date = models.DateField(null=True) release_group_id = models.UUIDField(null=True, blank=True) cover = VersatileImageField(upload_to='albums/covers/%Y/%m/%d', null=True, blank=True) @@ -245,7 +246,12 @@ class Work(APIModelMixin): class Lyrics(models.Model): - work = models.ForeignKey(Work, related_name='lyrics', null=True, blank=True) + work = models.ForeignKey( + Work, + related_name='lyrics', + null=True, + blank=True, + on_delete=models.CASCADE) url = models.URLField(unique=True) content = models.TextField(null=True, blank=True) @@ -268,10 +274,21 @@ class Lyrics(models.Model): class Track(APIModelMixin): title = models.CharField(max_length=255) - artist = models.ForeignKey(Artist, related_name='tracks') + artist = models.ForeignKey( + Artist, related_name='tracks', on_delete=models.CASCADE) position = models.PositiveIntegerField(null=True, blank=True) - album = models.ForeignKey(Album, related_name='tracks', null=True, blank=True) - work = models.ForeignKey(Work, related_name='tracks', null=True, blank=True) + album = models.ForeignKey( + Album, + related_name='tracks', + null=True, + blank=True, + on_delete=models.CASCADE) + work = models.ForeignKey( + Work, + related_name='tracks', + null=True, + blank=True, + on_delete=models.CASCADE) musicbrainz_model = 'recording' api = musicbrainz.api.recordings @@ -340,7 +357,8 @@ class Track(APIModelMixin): class TrackFile(models.Model): - track = models.ForeignKey(Track, related_name='files') + track = models.ForeignKey( + Track, related_name='files', on_delete=models.CASCADE) audio_file = models.FileField(upload_to='tracks/%Y/%m/%d', max_length=255) source = models.URLField(null=True, blank=True) duration = models.IntegerField(null=True, blank=True) @@ -376,7 +394,8 @@ class TrackFile(models.Model): class ImportBatch(models.Model): creation_date = models.DateTimeField(default=timezone.now) - submitted_by = models.ForeignKey('users.User', related_name='imports') + submitted_by = models.ForeignKey( + 'users.User', related_name='imports', on_delete=models.CASCADE) class Meta: ordering = ['-creation_date'] @@ -392,9 +411,14 @@ class ImportBatch(models.Model): return 'finished' class ImportJob(models.Model): - batch = models.ForeignKey(ImportBatch, related_name='jobs') + batch = models.ForeignKey( + ImportBatch, related_name='jobs', on_delete=models.CASCADE) track_file = models.ForeignKey( - TrackFile, related_name='jobs', null=True, blank=True) + TrackFile, + related_name='jobs', + null=True, + blank=True, + on_delete=models.CASCADE) source = models.URLField() mbid = models.UUIDField(editable=False) STATUS_CHOICES = ( diff --git a/api/funkwhale_api/music/tests/factories.py b/api/funkwhale_api/music/tests/factories.py index b554e3e14..567e2a765 100644 --- a/api/funkwhale_api/music/tests/factories.py +++ b/api/funkwhale_api/music/tests/factories.py @@ -59,3 +59,30 @@ class ImportJobFactory(factory.django.DjangoModelFactory): class Meta: model = 'music.ImportJob' + + +class WorkFactory(factory.django.DjangoModelFactory): + mbid = factory.Faker('uuid4') + language = 'eng' + nature = 'song' + title = factory.Faker('sentence', nb_words=3) + + class Meta: + model = 'music.Work' + + +class LyricsFactory(factory.django.DjangoModelFactory): + work = factory.SubFactory(WorkFactory) + url = factory.Faker('url') + content = factory.Faker('paragraphs', nb=4) + + class Meta: + model = 'music.Lyrics' + + +class TagFactory(factory.django.DjangoModelFactory): + name = factory.SelfAttribute('slug') + slug = factory.Faker('slug') + + class Meta: + model = 'taggit.Tag' diff --git a/api/funkwhale_api/music/tests/test_api.py b/api/funkwhale_api/music/tests/test_api.py index b7c25424f..2460fa97d 100644 --- a/api/funkwhale_api/music/tests/test_api.py +++ b/api/funkwhale_api/music/tests/test_api.py @@ -1,7 +1,7 @@ import json import unittest from test_plus.test import TestCase -from django.core.urlresolvers import reverse +from django.urls import reverse from funkwhale_api.music import models from funkwhale_api.utils.tests import TMPDirTestCaseMixin diff --git a/api/funkwhale_api/music/tests/test_lyrics.py b/api/funkwhale_api/music/tests/test_lyrics.py index 0ea22371b..9a05e5eb8 100644 --- a/api/funkwhale_api/music/tests/test_lyrics.py +++ b/api/funkwhale_api/music/tests/test_lyrics.py @@ -1,25 +1,26 @@ import json import unittest from test_plus.test import TestCase -from django.core.urlresolvers import reverse -from model_mommy import mommy +from django.urls import reverse from funkwhale_api.music import models from funkwhale_api.musicbrainz import api from funkwhale_api.music import serializers from funkwhale_api.users.models import User +from funkwhale_api.music import lyrics as lyrics_utils from .mocking import lyricswiki +from . import factories from . import data as api_data -from funkwhale_api.music import lyrics as lyrics_utils + + class TestLyrics(TestCase): @unittest.mock.patch('funkwhale_api.music.lyrics._get_html', return_value=lyricswiki.content) def test_works_import_lyrics_if_any(self, *mocks): - lyrics = mommy.make( - models.Lyrics, + lyrics = factories.LyricsFactory( url='http://lyrics.wikia.com/System_Of_A_Down:Chop_Suey!') lyrics.fetch_content() @@ -42,7 +43,7 @@ Is it me you're looking for? content = """Hello Is it me you're looking for?""" - l = mommy.make(models.Lyrics, content=content) + l = factories.LyricsFactory(content=content) expected = "

Hello
Is it me you're looking for?

" self.assertHTMLEqual(expected, l.content_rendered) @@ -54,8 +55,7 @@ Is it me you're looking for?""" @unittest.mock.patch('funkwhale_api.music.lyrics._get_html', return_value=lyricswiki.content) def test_works_import_lyrics_if_any(self, *mocks): - track = mommy.make( - models.Track, + track = factories.TrackFactory( work=None, mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') diff --git a/api/funkwhale_api/music/tests/test_music.py b/api/funkwhale_api/music/tests/test_music.py index cc01bc9ed..5cf9d0cf9 100644 --- a/api/funkwhale_api/music/tests/test_music.py +++ b/api/funkwhale_api/music/tests/test_music.py @@ -2,13 +2,11 @@ from test_plus.test import TestCase import unittest.mock from funkwhale_api.music import models import datetime -from model_mommy import mommy + +from . import factories from . import data as api_data from .cover import binary_data -def prettyprint(d): - import json - print(json.dumps(d, sort_keys=True, indent=4)) class TestMusic(TestCase): @@ -79,9 +77,9 @@ class TestMusic(TestCase): self.assertEqual(track, track2) def test_album_tags_deduced_from_tracks_tags(self): - tag = mommy.make('taggit.Tag') - album = mommy.make('music.Album') - tracks = mommy.make('music.Track', album=album, _quantity=5) + tag = factories.TagFactory() + album = factories.AlbumFactory() + tracks = factories.TrackFactory.create_batch(album=album, size=5) for track in tracks: track.tags.add(tag) @@ -92,10 +90,10 @@ class TestMusic(TestCase): self.assertIn(tag, album.tags) def test_artist_tags_deduced_from_album_tags(self): - tag = mommy.make('taggit.Tag') - artist = mommy.make('music.Artist') - album = mommy.make('music.Album', artist=artist) - tracks = mommy.make('music.Track', album=album, _quantity=5) + tag = factories.TagFactory() + artist = factories.ArtistFactory() + album = factories.AlbumFactory(artist=artist) + tracks = factories.TrackFactory.create_batch(album=album, size=5) for track in tracks: track.tags.add(tag) @@ -108,7 +106,7 @@ class TestMusic(TestCase): @unittest.mock.patch('funkwhale_api.musicbrainz.api.images.get_front', return_value=binary_data) def test_can_download_image_file_for_album(self, *mocks): # client._api.get_image_front('55ea4f82-b42b-423e-a0e5-290ccdf443ed') - album = mommy.make('music.Album', mbid='55ea4f82-b42b-423e-a0e5-290ccdf443ed') + album = factories.AlbumFactory(mbid='55ea4f82-b42b-423e-a0e5-290ccdf443ed') album.get_image() album.save() diff --git a/api/funkwhale_api/music/tests/test_works.py b/api/funkwhale_api/music/tests/test_works.py index 84cb51cde..55714bce2 100644 --- a/api/funkwhale_api/music/tests/test_works.py +++ b/api/funkwhale_api/music/tests/test_works.py @@ -1,23 +1,24 @@ import json import unittest from test_plus.test import TestCase -from django.core.urlresolvers import reverse -from model_mommy import mommy +from django.urls import reverse from funkwhale_api.music import models from funkwhale_api.musicbrainz import api from funkwhale_api.music import serializers +from funkwhale_api.music.tests import factories from funkwhale_api.users.models import User from . import data as api_data + class TestWorks(TestCase): @unittest.mock.patch('funkwhale_api.musicbrainz.api.works.get', return_value=api_data.works['get']['chop_suey']) def test_can_import_work(self, *mocks): - recording = mommy.make( - models.Track, mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') + recording = factories.TrackFactory( + mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' work = models.Work.create_from_api(id=mbid) @@ -36,8 +37,7 @@ class TestWorks(TestCase): @unittest.mock.patch('funkwhale_api.musicbrainz.api.recordings.get', return_value=api_data.tracks['get']['chop_suey']) def test_can_get_work_from_recording(self, *mocks): - recording = mommy.make( - models.Track, + recording = factories.TrackFactory( work=None, mbid='07ca77cf-f513-4e9c-b190-d7e24bbad448') mbid = 'e2ecabc4-1b9d-30b2-8f30-3596ec423dc5' diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py index 72982e4c5..c32fa8f7f 100644 --- a/api/funkwhale_api/music/views.py +++ b/api/funkwhale_api/music/views.py @@ -2,7 +2,7 @@ import os import json import unicodedata import urllib -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import models, transaction from django.db.models.functions import Length from django.conf import settings @@ -102,7 +102,7 @@ class TrackViewSet(TagViewSetMixin, SearchMixin, viewsets.ReadOnlyModelViewSet): queryset = super().get_queryset() filter_favorites = self.request.GET.get('favorites', None) user = self.request.user - if user.is_authenticated() and filter_favorites == 'true': + if user.is_authenticated and filter_favorites == 'true': queryset = queryset.filter(track_favorites__user=user) return queryset diff --git a/api/funkwhale_api/musicbrainz/tests/test_api.py b/api/funkwhale_api/musicbrainz/tests/test_api.py index f962e0f78..b0911f1c5 100644 --- a/api/funkwhale_api/musicbrainz/tests/test_api.py +++ b/api/funkwhale_api/musicbrainz/tests/test_api.py @@ -1,7 +1,7 @@ import json import unittest from test_plus.test import TestCase -from django.core.urlresolvers import reverse +from django.urls import reverse from funkwhale_api.musicbrainz import api from . import data as api_data diff --git a/api/funkwhale_api/playlists/migrations/0001_initial.py b/api/funkwhale_api/playlists/migrations/0001_initial.py index f42ca154e..bc97d8122 100644 --- a/api/funkwhale_api/playlists/migrations/0001_initial.py +++ b/api/funkwhale_api/playlists/migrations/0001_initial.py @@ -22,7 +22,7 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=50)), ('is_public', models.BooleanField(default=False)), ('creation_date', models.DateTimeField(default=django.utils.timezone.now)), - ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='playlists')), + ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='playlists', on_delete=models.CASCADE)), ], ), migrations.CreateModel( @@ -33,9 +33,9 @@ class Migration(migrations.Migration): ('rght', models.PositiveIntegerField(db_index=True, editable=False)), ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), ('position', models.PositiveIntegerField(db_index=True, editable=False)), - ('playlist', models.ForeignKey(to='playlists.Playlist', related_name='playlist_tracks')), - ('previous', mptt.fields.TreeOneToOneField(null=True, to='playlists.PlaylistTrack', related_name='next', blank=True)), - ('track', models.ForeignKey(to='music.Track', related_name='playlist_tracks')), + ('playlist', models.ForeignKey(to='playlists.Playlist', related_name='playlist_tracks', on_delete=models.CASCADE)), + ('previous', mptt.fields.TreeOneToOneField(null=True, to='playlists.PlaylistTrack', related_name='next', blank=True, on_delete=models.CASCADE)), + ('track', models.ForeignKey(to='music.Track', related_name='playlist_tracks', on_delete=models.CASCADE)), ], options={ 'ordering': ('-playlist', 'position'), diff --git a/api/funkwhale_api/playlists/models.py b/api/funkwhale_api/playlists/models.py index 35a30a704..e89dce81c 100644 --- a/api/funkwhale_api/playlists/models.py +++ b/api/funkwhale_api/playlists/models.py @@ -7,7 +7,8 @@ from mptt.models import MPTTModel, TreeOneToOneField class Playlist(models.Model): name = models.CharField(max_length=50) is_public = models.BooleanField(default=False) - user = models.ForeignKey('users.User', related_name="playlists") + user = models.ForeignKey( + 'users.User', related_name="playlists", on_delete=models.CASCADE) creation_date = models.DateTimeField(default=timezone.now) def __str__(self): @@ -21,9 +22,18 @@ class Playlist(models.Model): class PlaylistTrack(MPTTModel): - track = models.ForeignKey('music.Track', related_name='playlist_tracks') - previous = TreeOneToOneField('self', blank=True, null=True, related_name='next') - playlist = models.ForeignKey(Playlist, related_name='playlist_tracks') + track = models.ForeignKey( + 'music.Track', + related_name='playlist_tracks', + on_delete=models.CASCADE) + previous = TreeOneToOneField( + 'self', + blank=True, + null=True, + related_name='next', + on_delete=models.CASCADE) + playlist = models.ForeignKey( + Playlist, related_name='playlist_tracks', on_delete=models.CASCADE) class MPTTMeta: level_attr = 'position' diff --git a/api/funkwhale_api/playlists/tests/test_playlists.py b/api/funkwhale_api/playlists/tests/test_playlists.py index 49025cd11..2f61889ee 100644 --- a/api/funkwhale_api/playlists/tests/test_playlists.py +++ b/api/funkwhale_api/playlists/tests/test_playlists.py @@ -1,11 +1,10 @@ import json from test_plus.test import TestCase -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.exceptions import ValidationError from django.utils import timezone -from model_mommy import mommy - +from funkwhale_api.music.tests import factories from funkwhale_api.users.models import User from funkwhale_api.playlists import models from funkwhale_api.playlists.serializers import PlaylistSerializer @@ -18,7 +17,7 @@ class TestPlayLists(TestCase): self.user = User.objects.create_user(username='test', email='test@test.com', password='test') def test_can_create_playlist(self): - tracks = list(mommy.make('music.Track', _quantity=5)) + tracks = factories.TrackFactory.create_batch(size=5) playlist = models.Playlist.objects.create(user=self.user, name="test") previous = None @@ -49,7 +48,7 @@ class TestPlayLists(TestCase): self.assertEqual(playlist.name, 'test') def test_can_add_playlist_track_via_api(self): - tracks = list(mommy.make('music.Track', _quantity=5)) + tracks = factories.TrackFactory.create_batch(size=5) playlist = models.Playlist.objects.create(user=self.user, name="test") self.client.login(username=self.user.username, password='test') diff --git a/api/funkwhale_api/providers/urls.py b/api/funkwhale_api/providers/urls.py index a6d417e5b..10975da53 100644 --- a/api/funkwhale_api/providers/urls.py +++ b/api/funkwhale_api/providers/urls.py @@ -1,8 +1,11 @@ from django.conf.urls import include, url from funkwhale_api.music import views - urlpatterns = [ - url(r'^youtube/', include('funkwhale_api.providers.youtube.urls', namespace='youtube')), - url(r'^musicbrainz/', include('funkwhale_api.musicbrainz.urls', namespace='musicbrainz')), + url(r'^youtube/', include( + ('funkwhale_api.providers.youtube.urls', 'youtube'), + namespace='youtube')), + url(r'^musicbrainz/', include( + ('funkwhale_api.musicbrainz.urls', 'musicbrainz'), + namespace='musicbrainz')), ] diff --git a/api/funkwhale_api/providers/youtube/tests/test_youtube.py b/api/funkwhale_api/providers/youtube/tests/test_youtube.py index db6bd8413..8a1dd1eb7 100644 --- a/api/funkwhale_api/providers/youtube/tests/test_youtube.py +++ b/api/funkwhale_api/providers/youtube/tests/test_youtube.py @@ -2,7 +2,7 @@ import json from collections import OrderedDict import unittest from test_plus.test import TestCase -from django.core.urlresolvers import reverse +from django.urls import reverse from funkwhale_api.providers.youtube.client import client from . import data as api_data diff --git a/api/funkwhale_api/radios/migrations/0001_initial.py b/api/funkwhale_api/radios/migrations/0001_initial.py index 9ec25805d..46faf749e 100644 --- a/api/funkwhale_api/radios/migrations/0001_initial.py +++ b/api/funkwhale_api/radios/migrations/0001_initial.py @@ -20,7 +20,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), ('radio_type', models.CharField(max_length=50)), ('creation_date', models.DateTimeField(default=django.utils.timezone.now)), - ('user', models.ForeignKey(related_name='radio_sessions', blank=True, to=settings.AUTH_USER_MODEL, null=True)), + ('user', models.ForeignKey(related_name='radio_sessions', blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)), ], ), migrations.CreateModel( @@ -28,8 +28,8 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), ('position', models.IntegerField(default=1)), - ('session', models.ForeignKey(to='radios.RadioSession', related_name='session_tracks')), - ('track', models.ForeignKey(to='music.Track', related_name='radio_session_tracks')), + ('session', models.ForeignKey(to='radios.RadioSession', related_name='session_tracks', on_delete=models.CASCADE)), + ('track', models.ForeignKey(to='music.Track', related_name='radio_session_tracks', on_delete=models.CASCADE)), ], options={ 'ordering': ('session', 'position'), diff --git a/api/funkwhale_api/radios/migrations/0003_auto_20160521_1708.py b/api/funkwhale_api/radios/migrations/0003_auto_20160521_1708.py index 4629d68fe..7c70abc2e 100644 --- a/api/funkwhale_api/radios/migrations/0003_auto_20160521_1708.py +++ b/api/funkwhale_api/radios/migrations/0003_auto_20160521_1708.py @@ -15,7 +15,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='radiosession', name='related_object_content_type', - field=models.ForeignKey(null=True, to='contenttypes.ContentType', blank=True), + field=models.ForeignKey(null=True, to='contenttypes.ContentType', blank=True, on_delete=models.CASCADE), ), migrations.AddField( model_name='radiosession', diff --git a/api/funkwhale_api/radios/models.py b/api/funkwhale_api/radios/models.py index a3a353132..984b34a1f 100644 --- a/api/funkwhale_api/radios/models.py +++ b/api/funkwhale_api/radios/models.py @@ -7,11 +7,20 @@ from django.contrib.contenttypes.models import ContentType from funkwhale_api.music.models import Track class RadioSession(models.Model): - user = models.ForeignKey('users.User', related_name='radio_sessions', null=True, blank=True) + user = models.ForeignKey( + 'users.User', + related_name='radio_sessions', + null=True, + blank=True, + on_delete=models.CASCADE) session_key = models.CharField(max_length=100, null=True, blank=True) radio_type = models.CharField(max_length=50) creation_date = models.DateTimeField(default=timezone.now) - related_object_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, blank=True, null=True) + related_object_content_type = models.ForeignKey( + ContentType, + blank=True, + null=True, + on_delete=models.CASCADE) related_object_id = models.PositiveIntegerField(blank=True, null=True) related_object = GenericForeignKey('related_object_content_type', 'related_object_id') @@ -43,9 +52,11 @@ class RadioSession(models.Model): return registry[self.radio_type](session=self) class RadioSessionTrack(models.Model): - session = models.ForeignKey(RadioSession, related_name='session_tracks') + session = models.ForeignKey( + RadioSession, related_name='session_tracks', on_delete=models.CASCADE) position = models.IntegerField(default=1) - track = models.ForeignKey(Track, related_name='radio_session_tracks') + track = models.ForeignKey( + Track, related_name='radio_session_tracks', on_delete=models.CASCADE) class Meta: ordering = ('session', 'position') diff --git a/api/funkwhale_api/radios/tests/test_radios.py b/api/funkwhale_api/radios/tests/test_radios.py index 5729b4412..ab27d4516 100644 --- a/api/funkwhale_api/radios/tests/test_radios.py +++ b/api/funkwhale_api/radios/tests/test_radios.py @@ -1,16 +1,18 @@ import random import json from test_plus.test import TestCase -from django.core.urlresolvers import reverse +from django.urls import reverse from django.core.exceptions import ValidationError -from model_mommy import mommy from funkwhale_api.radios import radios from funkwhale_api.radios import models from funkwhale_api.favorites.models import TrackFavorite from funkwhale_api.users.models import User from funkwhale_api.music.models import Artist +from funkwhale_api.music.tests import factories +from funkwhale_api.history.tests.factories import ListeningFactory + class TestRadios(TestCase): @@ -55,7 +57,7 @@ class TestRadios(TestCase): self.assertTrue(picks[2] > picks[1]) def test_can_get_choices_for_favorites_radio(self): - tracks = mommy.make('music.Track', _quantity=100) + tracks = factories.TrackFactory.create_batch(size=100) for i in range(20): TrackFavorite.add(track=random.choice(tracks), user=self.user) @@ -73,7 +75,7 @@ class TestRadios(TestCase): self.assertIn(pick, choices) def test_can_use_radio_session_to_filter_choices(self): - tracks = mommy.make('music.Track', _quantity=30) + tracks = factories.TrackFactory.create_batch(size=30) radio = radios.RandomRadio() session = radio.start_session(self.user) @@ -85,7 +87,7 @@ class TestRadios(TestCase): self.assertEqual(len(set(tracks_id)), 30) def test_can_restore_radio_from_previous_session(self): - tracks = mommy.make('music.Track', _quantity=30) + tracks = factories.TrackFactory.create_batch(size=30) radio = radios.RandomRadio() session = radio.start_session(self.user) @@ -115,7 +117,7 @@ class TestRadios(TestCase): self.assertIsNotNone(session.session_key) def test_can_get_track_for_session_from_api(self): - tracks = mommy.make('music.Track', _quantity=1) + tracks = factories.TrackFactory.create_batch(size=1) self.client.login(username=self.user.username, password='test') url = reverse('api:v1:radios:sessions-list') @@ -129,7 +131,7 @@ class TestRadios(TestCase): self.assertEqual(data['track']['id'], tracks[0].id) self.assertEqual(data['position'], 1) - next_track = mommy.make('music.Track') + next_track = factories.TrackFactory() response = self.client.post(url, {'session': session.pk}) data = json.loads(response.content.decode('utf-8')) @@ -148,9 +150,10 @@ class TestRadios(TestCase): radio.start_session(self.user, related_object=self.user) def test_can_start_artist_radio(self): - artist = mommy.make('music.Artist') - wrong_tracks = mommy.make('music.Track', _quantity=30) - good_tracks = mommy.make('music.Track', artist=artist, _quantity=5) + artist = factories.ArtistFactory() + wrong_tracks = factories.TrackFactory.create_batch(size=30) + good_tracks = factories.TrackFactory.create_batch( + artist=artist, size=5) radio = radios.ArtistRadio() session = radio.start_session(self.user, related_object=artist) @@ -159,9 +162,9 @@ class TestRadios(TestCase): self.assertIn(radio.pick(), good_tracks) def test_can_start_tag_radio(self): - tag = mommy.make('taggit.Tag') - wrong_tracks = mommy.make('music.Track', _quantity=30) - good_tracks = mommy.make('music.Track', _quantity=5) + tag = factories.TagFactory() + wrong_tracks = factories.TrackFactory.create_batch(size=30) + good_tracks = factories.TrackFactory.create_batch(size=5) for track in good_tracks: track.tags.add(tag) @@ -172,7 +175,7 @@ class TestRadios(TestCase): self.assertIn(radio.pick(), good_tracks) def test_can_start_artist_radio_from_api(self): - artist = mommy.make('music.Artist') + artist = factories.ArtistFactory() url = reverse('api:v1:radios:sessions-list') response = self.client.post(url, {'radio_type': 'artist', 'related_object_id': artist.id}) @@ -181,10 +184,10 @@ class TestRadios(TestCase): self.assertEqual(session.related_object, artist) def test_can_start_less_listened_radio(self): - history = mommy.make('history.Listening', _quantity=5, user=self.user) + history = ListeningFactory.create_batch(size=5, user=self.user) wrong_tracks = [h.track for h in history] - good_tracks = mommy.make('music.Track', _quantity=30) + good_tracks = factories.TrackFactory.create_batch(size=30) radio = radios.LessListenedRadio() session = radio.start_session(self.user) diff --git a/api/funkwhale_api/radios/urls.py b/api/funkwhale_api/radios/urls.py index 57e47f063..8c31df093 100644 --- a/api/funkwhale_api/radios/urls.py +++ b/api/funkwhale_api/radios/urls.py @@ -6,4 +6,5 @@ router = routers.SimpleRouter() router.register(r'sessions', views.RadioSessionViewSet, 'sessions') router.register(r'tracks', views.RadioSessionTrackViewSet, 'tracks') + urlpatterns = router.urls diff --git a/api/funkwhale_api/radios/views.py b/api/funkwhale_api/radios/views.py index 1ae788fcb..9243d6a90 100644 --- a/api/funkwhale_api/radios/views.py +++ b/api/funkwhale_api/radios/views.py @@ -19,14 +19,14 @@ class RadioSessionViewSet(mixins.CreateModelMixin, def get_queryset(self): queryset = super().get_queryset() - if self.request.user.is_authenticated(): + if self.request.user.is_authenticated: return queryset.filter(user=self.request.user) else: return queryset.filter(session_key=self.request.session.session_key) def get_serializer_context(self): context = super().get_serializer_context() - if self.request.user.is_authenticated(): + if self.request.user.is_authenticated: context['user'] = self.request.user else: context['session_key'] = self.request.session.session_key @@ -44,7 +44,7 @@ class RadioSessionTrackViewSet(mixins.CreateModelMixin, serializer.is_valid(raise_exception=True) session = serializer.validated_data['session'] try: - if request.user.is_authenticated(): + if request.user.is_authenticated: assert request.user == session.user else: assert request.session.session_key == session.session_key diff --git a/api/funkwhale_api/users/middleware.py b/api/funkwhale_api/users/middleware.py index 0f572c203..e3eba95f3 100644 --- a/api/funkwhale_api/users/middleware.py +++ b/api/funkwhale_api/users/middleware.py @@ -1,6 +1,11 @@ -class AnonymousSessionMiddleware(object): - def process_request(self, request): +class AnonymousSessionMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): if not request.session.session_key: request.session.save() + response = self.get_response(request) + return response diff --git a/api/funkwhale_api/users/migrations/0001_initial.py b/api/funkwhale_api/users/migrations/0001_initial.py index 8327d2890..ef9240c91 100644 --- a/api/funkwhale_api/users/migrations/0001_initial.py +++ b/api/funkwhale_api/users/migrations/0001_initial.py @@ -38,7 +38,7 @@ class Migration(migrations.Migration): 'verbose_name_plural': 'users', }, managers=[ - (b'objects', django.contrib.auth.models.UserManager()), + ('objects', django.contrib.auth.models.UserManager()), ], ), ] diff --git a/api/funkwhale_api/users/models.py b/api/funkwhale_api/users/models.py index c5ca896ab..c8d0b534c 100644 --- a/api/funkwhale_api/users/models.py +++ b/api/funkwhale_api/users/models.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals, absolute_import from django.contrib.auth.models import AbstractUser -from django.core.urlresolvers import reverse +from django.urls import reverse from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ diff --git a/api/requirements/base.txt b/api/requirements/base.txt index 3a11afadf..aee122259 100644 --- a/api/requirements/base.txt +++ b/api/requirements/base.txt @@ -1,59 +1,59 @@ # Bleeding edge Django -django==1.11 +django>=2.0,<2.1 # Configuration -django-environ==0.4.0 -django-secure==1.0.1 -whitenoise==2.0.6 - -# Models -django-model-utils==2.3.1 +django-environ>=0.4,<0.5 +whitenoise>=3.3,<3.4 # Images -Pillow==3.0.0 +Pillow>=4.3,<4.4 # For user registration, either via email or social # Well-built with regular release cycles! -django-allauth==0.24.1 +django-allauth>=0.34,<0.35 # Python-PostgreSQL Database Adapter -psycopg2==2.6.1 +psycopg2>=2.7,<=2.8 # Time zones support -pytz==2015.7 +pytz==2017.3 # Redis support -django-redis==4.3.0 -redis>=2.10.0 +django-redis>=4.5,<4.6 +redis>=2.10,<2.11 -celery==3.1.19 +celery>=3.1,<3.2 # Your custom requirements go here -django-cors-headers==2.1.0 +django-cors-headers>=2.1,<2.2 musicbrainzngs==0.6 -youtube_dl>=2015.12.21 -djangorestframework==3.6.3 -djangorestframework-jwt==1.11.0 -django-celery==3.2.1 -django-mptt==0.8.7 -google-api-python-client==1.6.2 -arrow==0.10.0 -django-taggit==0.22.1 -persisting-theory==0.2.1 -django-versatileimagefield==1.7.1 -django-cachalot==1.5.0 -django-filter==1.1 -django-rest-auth==0.9.1 -beautifulsoup4==4.6.0 -Markdown==2.6.8 -ipython==6.1.0 -mutagen==1.38 +youtube_dl>=2017.12.14 +djangorestframework>=3.7,<3.8 +djangorestframework-jwt>=1.11,<1.12 +django-celery>=3.2,<3.3 +django-mptt>=0.9,<0.10 +google-api-python-client>=1.6,<1.7 +arrow>=0.12,<0.13 +persisting-theory>=0.2,<0.3 +django-versatileimagefield>=1.8,<1.9 +django-filter>=1.1,<1.2 +django-rest-auth>=0.9,<0.10 +beautifulsoup4>=4.6,<4.7 +Markdown>=2.6,<2.7 +ipython>=6,<7 +mutagen>=1.39,<1.40 +# Until this is merged +#django-taggit>=0.22,<0.23 +git+https://github.com/jdufresne/django-taggit.git@e8f7f216f04c9781bebc84363ab24d575f948ede # Until this is merged git+https://github.com/EliotBerriot/PyMemoize.git@django +# Until this is merged +#django-cachalot==1.5.0 +git+https://github.com/EliotBerriot/django-cachalot.git@django-2 -django-dynamic-preferences>=1.3,<1.4 +django-dynamic-preferences>=1.5,<1.6 diff --git a/api/requirements/local.txt b/api/requirements/local.txt index 3f681b26d..d8a1561e0 100644 --- a/api/requirements/local.txt +++ b/api/requirements/local.txt @@ -1,15 +1,15 @@ # Local development dependencies go here --r base.txt -coverage==4.0.3 -django_coverage_plugin==1.1 -Sphinx==1.6.2 -django-extensions==1.5.9 -Werkzeug==0.11.2 -django-test-plus==1.0.11 + +coverage>=4.4,<4.5 +django_coverage_plugin>=1.5,<1.6 +Sphinx>=1.6,<1.7 +django-extensions>=1.9,<1.10 +Werkzeug>=0.13,<0.14 +django-test-plus>=1.0.20 factory_boy>=2.8.1 # django-debug-toolbar that works with Django 1.5+ -django-debug-toolbar>=1.5,<1.6 +django-debug-toolbar>=1.9,<1.10 # improved REPL ipdb==0.8.1 diff --git a/api/requirements/production.txt b/api/requirements/production.txt index 10d05fd34..42b66eb15 100644 --- a/api/requirements/production.txt +++ b/api/requirements/production.txt @@ -1,8 +1,5 @@ # Pro-tip: Try not to put anything here. There should be no dependency in # production that isn't in development. --r base.txt - - # WSGI Handler # ------------------------------------------------ diff --git a/api/requirements/test.txt b/api/requirements/test.txt index 7c304bbdb..bde5a2df9 100644 --- a/api/requirements/test.txt +++ b/api/requirements/test.txt @@ -1,9 +1,6 @@ # Test dependencies go here. --r local.txt - -flake8==2.5.0 -model-mommy==1.3.2 +flake8 pytest pytest-django pytest-mock diff --git a/front/src/audio/index.js b/front/src/audio/index.js index 7750ee500..4896b83b0 100644 --- a/front/src/audio/index.js +++ b/front/src/audio/index.js @@ -26,6 +26,7 @@ class Audio { if (options.onEnded) { this.onEnded = options.onEnded } + this.onError = options.onError this.state = { preload: preload, @@ -60,8 +61,12 @@ class Audio { init (src, options = {}) { if (!src) throw Error('src must be required') this.state.startLoad = true - if (this.state.tried === this.state.try) { + if (this.state.tried >= this.state.try) { this.state.failed = true + logger.default.error('Cannot fetch audio', src) + if (this.onError) { + this.onError(src) + } return } this.$Audio = new window.Audio(src) diff --git a/front/src/audio/queue.js b/front/src/audio/queue.js index bde0bf863..8c69638e8 100644 --- a/front/src/audio/queue.js +++ b/front/src/audio/queue.js @@ -140,7 +140,6 @@ class Queue { } else { index = index || this.tracks.length } - console.log('INDEEEEEX', index) tracks.forEach((t) => { self.append(t, index, true) index += 1 @@ -243,7 +242,11 @@ class Queue { rate: 1, loop: false, volume: this.state.volume, - onEnded: this.handleAudioEnded.bind(this) + onEnded: this.handleAudioEnded.bind(this), + onError: function (src) { + self.errored = true + self.next() + } }) this.audio = audio audio.updateHook('playState', function (e) { diff --git a/front/src/components/PageNotFound.vue b/front/src/components/PageNotFound.vue new file mode 100644 index 000000000..3b88a6921 --- /dev/null +++ b/front/src/components/PageNotFound.vue @@ -0,0 +1,34 @@ + + + + + + diff --git a/front/src/components/auth/Login.vue b/front/src/components/auth/Login.vue index 867738759..54e7b82e0 100644 --- a/front/src/components/auth/Login.vue +++ b/front/src/components/auth/Login.vue @@ -43,6 +43,9 @@ import auth from '@/auth' export default { name: 'login', + props: { + next: {type: String} + }, data () { return { // We need to initialize the component with any @@ -69,7 +72,7 @@ export default { } // We need to pass the component's this context // to properly make use of http in the auth service - auth.login(this, credentials, {path: '/library'}, function (response) { + auth.login(this, credentials, {path: this.next}, function (response) { // error callback if (response.status === 400) { self.error = 'invalid_credentials' diff --git a/front/src/components/library/import/ArtistImport.vue b/front/src/components/library/import/ArtistImport.vue index 4f049f52e..870a886e1 100644 --- a/front/src/components/library/import/ArtistImport.vue +++ b/front/src/components/library/import/ArtistImport.vue @@ -12,6 +12,10 @@ +
+ + +