diff --git a/.env.dev b/.env.dev index d42cdad02..9923c3148 100644 --- a/.env.dev +++ b/.env.dev @@ -1,4 +1,3 @@ API_AUTHENTICATION_REQUIRED=True -CACHALOT_ENABLED=False RAVEN_ENABLED=false RAVEN_DSN=https://44332e9fdd3d42879c7d35bf8562c6a4:0062dc16a22b41679cd5765e5342f716@sentry.eliotberriot.com/5 diff --git a/.gitignore b/.gitignore index 66ec5a41d..1e1017c8d 100644 --- a/.gitignore +++ b/.gitignore @@ -84,3 +84,5 @@ front/test/unit/coverage front/test/e2e/reports front/selenium-debug.log docs/_build + +data/ diff --git a/CHANGELOG b/CHANGELOG index 19cd704a1..6881b7bf0 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,27 @@ Changelog .. towncrier +0.6.1 (unreleased) +------------------ + +Features: + +- Can now skip acoustid on file import with the --no-acoustid flag (#111) + + +Bugfixes: + +- Added missing batch id in output during import (#112) +- Added some feedback on the play button (#100) +- Smarter pagination which takes a fixed size (#84) + + +Other: + +- Completely removed django-cachalot from the codebase (#110). You can safely + remove the CACHALOT_ENABLED setting from your .env file + + 0.6 (2018-03-04) ---------------- diff --git a/api/compose/django/daphne.sh b/api/compose/django/daphne.sh index 16b4d50b8..4fa304143 100755 --- a/api/compose/django/daphne.sh +++ b/api/compose/django/daphne.sh @@ -1,3 +1,3 @@ #!/bin/bash -eux python /app/manage.py collectstatic --noinput -/usr/local/bin/daphne --root-path=/app -b 0.0.0.0 -p 5000 config.asgi:application +/usr/local/bin/daphne -b 0.0.0.0 -p 5000 config.asgi:application diff --git a/api/compose/django/entrypoint.sh b/api/compose/django/entrypoint.sh index 7e789968b..ac85f1164 100755 --- a/api/compose/django/entrypoint.sh +++ b/api/compose/django/entrypoint.sh @@ -4,16 +4,19 @@ set -e # Since docker-compose relies heavily on environment variables itself for configuration, we'd have to define multiple # environment variables just to support cookiecutter out of the box. That makes no sense, so this little entrypoint # does all this for us. -export CACHE_URL=redis://redis:6379/0 +export CACHE_URL=${CACHE_URL:="redis://redis:6379/0"} -# the official postgres image uses 'postgres' as default user if not set explictly. -if [ -z "$POSTGRES_ENV_POSTGRES_USER" ]; then +if [ -z "$DATABASE_URL" ]; then + # the official postgres image uses 'postgres' as default user if not set explictly. + if [ -z "$POSTGRES_ENV_POSTGRES_USER" ]; then export POSTGRES_ENV_POSTGRES_USER=postgres + fi + export DATABASE_URL=postgres://$POSTGRES_ENV_POSTGRES_USER:$POSTGRES_ENV_POSTGRES_PASSWORD@postgres:5432/$POSTGRES_ENV_POSTGRES_USER fi -export DATABASE_URL=postgres://$POSTGRES_ENV_POSTGRES_USER:$POSTGRES_ENV_POSTGRES_PASSWORD@postgres:5432/$POSTGRES_ENV_POSTGRES_USER - -export CELERY_BROKER_URL=$CACHE_URL +if [ -z "$CELERY_BROKER_URL" ]; then + export CELERY_BROKER_URL=$CACHE_URL +fi # we copy the frontend files, if any so we can serve them from the outside if [ -d "frontend" ]; then diff --git a/api/config/settings/common.py b/api/config/settings/common.py index bff43b233..1def68824 100644 --- a/api/config/settings/common.py +++ b/api/config/settings/common.py @@ -55,7 +55,6 @@ THIRD_PARTY_APPS = ( 'rest_framework', 'rest_framework.authtoken', 'taggit', - 'cachalot', 'rest_auth', 'rest_auth.registration', 'mptt', @@ -310,7 +309,7 @@ CELERY_BROKER_URL = env( "CELERY_BROKER_URL", default=env('CACHE_URL', default=CACHE_DEFAULT)) ########## END CELERY # Location of root django.contrib.admin URL, use {% url 'admin:index' %} -ADMIN_URL = r'^admin/' + # Your common stuff: Below this line define 3rd party library settings CELERY_TASK_DEFAULT_RATE_LIMIT = 1 CELERY_TASK_TIME_LIMIT = 300 @@ -371,9 +370,6 @@ MUSICBRAINZ_CACHE_DURATION = env.int( default=300 ) -CACHALOT_ENABLED = env.bool('CACHALOT_ENABLED', default=True) - - # Custom Admin URL, use {% url 'admin:index' %} ADMIN_URL = env('DJANGO_ADMIN_URL', default='^api/admin/') CSRF_USE_SESSIONS = True diff --git a/api/demo/load-demo-data.sh b/api/demo/load-demo-data.sh index c09c5075e..fab0de8b3 100755 --- a/api/demo/load-demo-data.sh +++ b/api/demo/load-demo-data.sh @@ -6,8 +6,8 @@ python manage.py migrate --noinput echo "Creating demo user..." -cat demo/demo-user.py | python manage.py shell --plain +cat demo/demo-user.py | python manage.py shell -i python echo "Importing demo tracks..." -python manage.py import_files "/music/**/*.ogg" --recursive --noinput +python manage.py import_files "/music/**/*.ogg" --recursive --noinput --username demo diff --git a/api/funkwhale_api/__init__.py b/api/funkwhale_api/__init__.py index c384dc521..841f2a299 100644 --- a/api/funkwhale_api/__init__.py +++ b/api/funkwhale_api/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -__version__ = '0.6' +__version__ = '0.6.1' __version_info__ = tuple([int(num) if num.isdigit() else num for num in __version__.replace('-', '.', 1).split('.')]) diff --git a/api/funkwhale_api/common/auth.py b/api/funkwhale_api/common/auth.py index 6f99b3bba..75839b936 100644 --- a/api/funkwhale_api/common/auth.py +++ b/api/funkwhale_api/common/auth.py @@ -9,6 +9,7 @@ from rest_framework import exceptions from rest_framework_jwt.settings import api_settings from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication +from funkwhale_api.users.models import User class TokenHeaderAuth(BaseJSONWebTokenAuthentication): @@ -40,7 +41,7 @@ class TokenAuthMiddleware: auth = TokenHeaderAuth() try: user, token = auth.authenticate(scope) - except exceptions.AuthenticationFailed: + except (User.DoesNotExist, exceptions.AuthenticationFailed): user = AnonymousUser() scope['user'] = user diff --git a/api/funkwhale_api/music/tasks.py b/api/funkwhale_api/music/tasks.py index cb4a737c9..bf7a847d0 100644 --- a/api/funkwhale_api/music/tasks.py +++ b/api/funkwhale_api/music/tasks.py @@ -1,5 +1,7 @@ from django.core.files.base import ContentFile +from dynamic_preferences.registries import global_preferences_registry + from funkwhale_api.taskapp import celery from funkwhale_api.providers.acoustid import get_acoustid_client from funkwhale_api.providers.audiofile.tasks import import_track_data_from_path @@ -23,21 +25,22 @@ def set_acoustid_on_track_file(track_file): return update(result['id']) -def _do_import(import_job, replace): +def _do_import(import_job, replace, use_acoustid=True): from_file = bool(import_job.audio_file) mbid = import_job.mbid acoustid_track_id = None duration = None track = None - if not mbid and from_file: + manager = global_preferences_registry.manager() + use_acoustid = use_acoustid and manager['providers_acoustid__api_key'] + if not mbid and use_acoustid and from_file: # we try to deduce mbid from acoustid client = get_acoustid_client() match = client.get_best_match(import_job.audio_file.path) - if not match: - raise ValueError('Cannot get match') - duration = match['recordings'][0]['duration'] - mbid = match['recordings'][0]['id'] - acoustid_track_id = match['id'] + if match: + duration = match['recordings'][0]['duration'] + mbid = match['recordings'][0]['id'] + acoustid_track_id = match['id'] if mbid: track, _ = models.Track.get_or_create_from_api(mbid=mbid) else: @@ -77,13 +80,13 @@ def _do_import(import_job, replace): models.ImportJob.objects.filter( status__in=['pending', 'errored']), 'import_job') -def import_job_run(self, import_job, replace=False): +def import_job_run(self, import_job, replace=False, use_acoustid=True): def mark_errored(): import_job.status = 'errored' - import_job.save() + import_job.save(update_fields=['status']) try: - return _do_import(import_job, replace) + return _do_import(import_job, replace, use_acoustid=use_acoustid) except Exception as exc: if not settings.DEBUG: try: diff --git a/api/funkwhale_api/providers/audiofile/management/commands/import_files.py b/api/funkwhale_api/providers/audiofile/management/commands/import_files.py index 17a199473..2fa5e464c 100644 --- a/api/funkwhale_api/providers/audiofile/management/commands/import_files.py +++ b/api/funkwhale_api/providers/audiofile/management/commands/import_files.py @@ -34,6 +34,13 @@ class Command(BaseCommand): default=False, help='Will launch celery tasks for each file to import instead of doing it synchronously and block the CLI', ) + parser.add_argument( + '--no-acoustid', + action='store_true', + dest='no_acoustid', + default=False, + help='Use this flag to completely bypass acoustid completely', + ) parser.add_argument( '--noinput', '--no-input', action='store_false', dest='interactive', help="Do NOT prompt the user for input of any kind.", @@ -81,13 +88,12 @@ class Command(BaseCommand): raise CommandError("Import cancelled.") batch = self.do_import(matching, user=user, options=options) - message = 'Successfully imported {} tracks' if options['async']: message = 'Successfully launched import for {} tracks' self.stdout.write(message.format(len(matching))) self.stdout.write( - "For details, please refer to import batch #".format(batch.pk)) + "For details, please refer to import batch #{}".format(batch.pk)) @transaction.atomic def do_import(self, matching, user, options): @@ -109,7 +115,10 @@ class Command(BaseCommand): job.save() try: - utils.on_commit(import_handler, import_job_id=job.pk) + utils.on_commit( + import_handler, + import_job_id=job.pk, + use_acoustid=not options['no_acoustid']) except Exception as e: self.stdout.write('Error: {}'.format(e)) diff --git a/api/funkwhale_api/templates/404.html b/api/funkwhale_api/templates/404.html deleted file mode 100644 index b9cb60846..000000000 --- a/api/funkwhale_api/templates/404.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Page Not found{% endblock %} - -{% block content %} -

Page Not found

- -

This is not the page you were looking for.

-{% endblock content %} diff --git a/api/funkwhale_api/templates/500.html b/api/funkwhale_api/templates/500.html deleted file mode 100644 index 21df60665..000000000 --- a/api/funkwhale_api/templates/500.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Server Error{% endblock %} - -{% block content %} -

Ooops!!! 500

- -

Looks like something went wrong!

- -

We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.

-{% endblock content %} - - diff --git a/api/funkwhale_api/templates/base.html b/api/funkwhale_api/templates/base.html deleted file mode 100644 index e8788f44a..000000000 --- a/api/funkwhale_api/templates/base.html +++ /dev/null @@ -1,107 +0,0 @@ -{% load staticfiles i18n %} - - - - - {% block title %}funkwhale_api{% endblock title %} - - - - - - - - {% block css %} - - - - - - - - {% endblock %} - - {% block angular %} - - {% endblock %} - - - - - -
- -
- -
- - {% if messages %} - {% for message in messages %} -
{{ message }}
- {% endfor %} - {% endif %} - - {% block content %} -

Use this document as a way to quick start any new project.

- {% endblock content %} - -
- - {% block modal %}{% endblock modal %} - - - - {% block javascript %} - - - - - - - - - - - {% endblock javascript %} - - diff --git a/api/funkwhale_api/templates/pages/about.html b/api/funkwhale_api/templates/pages/about.html deleted file mode 100644 index 63913c188..000000000 --- a/api/funkwhale_api/templates/pages/about.html +++ /dev/null @@ -1 +0,0 @@ -{% extends "base.html" %} \ No newline at end of file diff --git a/api/funkwhale_api/templates/pages/home.html b/api/funkwhale_api/templates/pages/home.html deleted file mode 100644 index 63913c188..000000000 --- a/api/funkwhale_api/templates/pages/home.html +++ /dev/null @@ -1 +0,0 @@ -{% extends "base.html" %} \ No newline at end of file diff --git a/api/requirements/base.txt b/api/requirements/base.txt index d402d3591..d6800f3b5 100644 --- a/api/requirements/base.txt +++ b/api/requirements/base.txt @@ -50,9 +50,6 @@ mutagen>=1.39,<1.40 django-taggit>=0.22,<0.23 # 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.5,<1.6 pyacoustid>=1.1.5,<1.2 diff --git a/api/tests/music/test_tasks.py b/api/tests/music/test_tasks.py index 873ca18f0..5ecf9b9e4 100644 --- a/api/tests/music/test_tasks.py +++ b/api/tests/music/test_tasks.py @@ -9,7 +9,8 @@ from . import data as api_data DATA_DIR = os.path.dirname(os.path.abspath(__file__)) -def test_set_acoustid_on_track_file(factories, mocker): +def test_set_acoustid_on_track_file(factories, mocker, preferences): + preferences['providers_acoustid__api_key'] = 'test' track_file = factories['music.TrackFile'](acoustid_track_id=None) id = 'e475bf79-c1ce-4441-bed7-1e33f226c0a2' payload = { @@ -31,7 +32,7 @@ def test_set_acoustid_on_track_file(factories, mocker): assert str(track_file.acoustid_track_id) == id assert r == id - m.assert_called_once_with('', track_file.audio_file.path, parse=False) + m.assert_called_once_with('test', track_file.audio_file.path, parse=False) def test_set_acoustid_on_track_file_required_high_score(factories, mocker): @@ -48,7 +49,9 @@ def test_set_acoustid_on_track_file_required_high_score(factories, mocker): assert track_file.acoustid_track_id is None -def test_import_job_can_run_with_file_and_acoustid(factories, mocker): +def test_import_job_can_run_with_file_and_acoustid( + preferences, factories, mocker): + preferences['providers_acoustid__api_key'] = 'test' path = os.path.join(DATA_DIR, 'test.ogg') mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed' acoustid_payload = { @@ -88,7 +91,46 @@ def test_import_job_can_run_with_file_and_acoustid(factories, mocker): assert job.source == 'file://' -def test_import_job_can_be_skipped(factories, mocker): +def test_run_import_skipping_accoustid(factories, mocker): + m = mocker.patch('funkwhale_api.music.tasks._do_import') + path = os.path.join(DATA_DIR, 'test.ogg') + job = factories['music.FileImportJob'](audio_file__path=path) + tasks.import_job_run(import_job_id=job.pk, use_acoustid=False) + m.assert_called_once_with(job, False, use_acoustid=False) + + +def test__do_import_skipping_accoustid(factories, mocker): + t = factories['music.Track']() + m = mocker.patch( + 'funkwhale_api.music.tasks.import_track_data_from_path', + return_value=t) + path = os.path.join(DATA_DIR, 'test.ogg') + job = factories['music.FileImportJob']( + mbid=None, + audio_file__path=path) + p = job.audio_file.path + tasks._do_import(job, replace=False, use_acoustid=False) + m.assert_called_once_with(p) + + +def test__do_import_skipping_accoustid_if_no_key( + factories, mocker, preferences): + preferences['providers_acoustid__api_key'] = '' + t = factories['music.Track']() + m = mocker.patch( + 'funkwhale_api.music.tasks.import_track_data_from_path', + return_value=t) + path = os.path.join(DATA_DIR, 'test.ogg') + job = factories['music.FileImportJob']( + mbid=None, + audio_file__path=path) + p = job.audio_file.path + tasks._do_import(job, replace=False, use_acoustid=False) + m.assert_called_once_with(p) + + +def test_import_job_can_be_skipped(factories, mocker, preferences): + preferences['providers_acoustid__api_key'] = 'test' path = os.path.join(DATA_DIR, 'test.ogg') mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed' track_file = factories['music.TrackFile'](track__mbid=mbid) @@ -124,7 +166,8 @@ def test_import_job_can_be_skipped(factories, mocker): assert job.status == 'skipped' -def test_import_job_can_be_errored(factories, mocker): +def test_import_job_can_be_errored(factories, mocker, preferences): + preferences['providers_acoustid__api_key'] = 'test' path = os.path.join(DATA_DIR, 'test.ogg') mbid = '9968a9d6-8d92-4051-8f76-674e157b6eed' track_file = factories['music.TrackFile'](track__mbid=mbid) diff --git a/api/tests/test_import_audio_file.py b/api/tests/test_import_audio_file.py index 2c254afd9..4f3de27db 100644 --- a/api/tests/test_import_audio_file.py +++ b/api/tests/test_import_audio_file.py @@ -54,7 +54,7 @@ def test_management_command_requires_a_valid_username(factories, mocker): def test_import_files_creates_a_batch_and_job(factories, mocker): - m = m = mocker.patch('funkwhale_api.common.utils.on_commit') + m = mocker.patch('funkwhale_api.common.utils.on_commit') user = factories['users.User'](username='me') path = os.path.join(DATA_DIR, 'dummy_file.ogg') call_command( @@ -77,4 +77,24 @@ def test_import_files_creates_a_batch_and_job(factories, mocker): assert job.source == 'file://' + path m.assert_called_once_with( music_tasks.import_job_run.delay, - import_job_id=job.pk) + import_job_id=job.pk, + use_acoustid=True) + + +def test_import_files_skip_acoustid(factories, mocker): + m = mocker.patch('funkwhale_api.common.utils.on_commit') + user = factories['users.User'](username='me') + path = os.path.join(DATA_DIR, 'dummy_file.ogg') + call_command( + 'import_files', + path, + username='me', + async=True, + no_acoustid=True, + interactive=False) + batch = user.imports.latest('id') + job = batch.jobs.first() + m.assert_called_once_with( + music_tasks.import_job_run.delay, + import_job_id=job.pk, + use_acoustid=False) diff --git a/demo/setup.sh b/demo/setup.sh new file mode 100644 index 000000000..1d63cc181 --- /dev/null +++ b/demo/setup.sh @@ -0,0 +1,31 @@ +#!/bin/bash -eux +version="develop" +music_path="/usr/share/music" +demo_path="/srv/funkwhale-demo/demo" + +echo 'Cleaning everything...' +cd $demo_path +docker-compose down -v || echo 'Nothing to stop' +rm -rf /srv/funkwhale-demo/demo/* +mkdir -p $demo_path +echo 'Downloading demo files...' +curl -L -o docker-compose.yml "https://code.eliotberriot.com/funkwhale/funkwhale/raw/$version/deploy/docker-compose.yml" +curl -L -o .env "https://code.eliotberriot.com/funkwhale/funkwhale/raw/$version/deploy/env.prod.sample" + +mkdir data/ +cp -r $music_path data/music + +curl -L -o front.zip "https://code.eliotberriot.com/funkwhale/funkwhale/-/jobs/artifacts/$version/download?job=build_front" +unzip front.zip + +echo "FUNKWHALE_URL=https://demo.funkwhale.audio/" >> .env +echo "DJANGO_SECRET_KEY=demo" >> .env +echo "DJANGO_ALLOWED_HOSTS=demo.funkwhale.audio" >> .env +echo "FUNKWHALE_VERSION=$version" >> .env +echo "FUNKWHALE_API_PORT=5001" >> .env + +docker-compose pull +docker-compose up -d postgres redis +sleep 5 +docker-compose run --rm api demo/load-demo-data.sh +docker-compose up -d diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index e6292812e..69d5e1721 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -20,7 +20,7 @@ services: restart: unless-stopped image: funkwhale/funkwhale:${FUNKWHALE_VERSION:-latest} env_file: .env - command: python manage.py celery worker + command: celery -A funkwhale_api.taskapp worker -l INFO links: - postgres - redis diff --git a/deploy/env.prod.sample b/deploy/env.prod.sample index 037dc4651..e1a381b94 100644 --- a/deploy/env.prod.sample +++ b/deploy/env.prod.sample @@ -31,7 +31,7 @@ FUNKWHALE_API_PORT=5000 # Replace this by the definitive, public domain you will use for # your instance -FUNKWHALE_URL=https.//yourdomain.funwhale +FUNKWHALE_URL=https://yourdomain.funwhale # API/Django configuration diff --git a/deploy/funkwhale-server.service b/deploy/funkwhale-server.service index 0027a80ab..53d3a104b 100644 --- a/deploy/funkwhale-server.service +++ b/deploy/funkwhale-server.service @@ -8,7 +8,7 @@ User=funkwhale # adapt this depending on the path of your funkwhale installation WorkingDirectory=/srv/funkwhale/api EnvironmentFile=/srv/funkwhale/config/.env -ExecStart=/usr/local/bin/daphne -b ${FUNKWHALE_API_IP} -p ${FUNKWHALE_API_PORT} config.asgi:application +ExecStart=/srv/funkwhale/virtualenv/bin/daphne -b ${FUNKWHALE_API_IP} -p ${FUNKWHALE_API_PORT} config.asgi:application [Install] WantedBy=multi-user.target diff --git a/deploy/funkwhale-worker.service b/deploy/funkwhale-worker.service index 2a25c2a1b..cb3c88307 100644 --- a/deploy/funkwhale-worker.service +++ b/deploy/funkwhale-worker.service @@ -8,7 +8,7 @@ User=funkwhale # adapt this depending on the path of your funkwhale installation WorkingDirectory=/srv/funkwhale/api EnvironmentFile=/srv/funkwhale/config/.env -ExecStart=/srv/funkwhale/virtualenv/bin/python manage.py celery worker +ExecStart=/srv/funkwhale/virtualenv/bin/celery -A funkwhale_api.taskapp worker -l INFO [Install] WantedBy=multi-user.target diff --git a/deploy/funkwhale_proxy.conf b/deploy/funkwhale_proxy.conf new file mode 100644 index 000000000..1b1dd0d20 --- /dev/null +++ b/deploy/funkwhale_proxy.conf @@ -0,0 +1,13 @@ +# global proxy conf +proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $scheme; +proxy_set_header X-Forwarded-Host $host:$server_port; +proxy_set_header X-Forwarded-Port $server_port; +proxy_redirect off; + +# websocket support +proxy_http_version 1.1; +proxy_set_header Upgrade $http_upgrade; +proxy_set_header Connection $connection_upgrade; diff --git a/deploy/nginx.conf b/deploy/nginx.conf index 125fbc6d4..1c7b9ae83 100644 --- a/deploy/nginx.conf +++ b/deploy/nginx.conf @@ -48,20 +48,6 @@ server { root /srv/funkwhale/front/dist; - # global proxy conf - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $host:$server_port; - proxy_set_header X-Forwarded-Port $server_port; - proxy_redirect off; - - # websocket support - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - location / { try_files $uri $uri/ @rewrites; } @@ -70,6 +56,7 @@ server { rewrite ^(.+)$ /index.html last; } location /api/ { + include /etc/nginx/funkwhale_proxy.conf; # this is needed if you have file import via upload enabled client_max_body_size 30M; proxy_pass http://funkwhale-api/api/; @@ -89,6 +76,7 @@ server { # Transcoding logic and caching location = /transcode-auth { + include /etc/nginx/funkwhale_proxy.conf; # needed so we can authenticate transcode requests, but still # cache the result internal; @@ -97,14 +85,13 @@ server { if ($request_uri ~* "[^\?]+\?(.*)$") { set $query $1; } - proxy_set_header X-Forwarded-Host $host:$server_port; - proxy_set_header X-Forwarded-Port $server_port; proxy_pass http://funkwhale-api/api/v1/trackfiles/viewable/?$query; proxy_pass_request_body off; proxy_set_header Content-Length ""; } location /api/v1/trackfiles/transcode/ { + include /etc/nginx/funkwhale_proxy.conf; # this block deals with authenticating and caching transcoding # requests. Caching is heavily recommended as transcoding # is a CPU intensive process. diff --git a/docs/configuration.rst b/docs/configuration.rst new file mode 100644 index 000000000..5883a2d17 --- /dev/null +++ b/docs/configuration.rst @@ -0,0 +1,35 @@ +Instance configuration +====================== + +General configuration is achieved using two type of settings. + +Environment variables +--------------------- + +Those are located in your ``.env`` file, which you should have created +during installation. + +Options from this file are heavily commented, and usually target lower level +and technical aspects of your instance, such as database credentials. + +.. note:: + + You should restart all funwhale processes when you change the values + on environment variables. + + +Instance settings +----------------- + +Those settings are stored in database and do not require a restart of your +instance after modification. They typically relate to higher level configuration, +such your instance description, signup policy and so on. + +There is no polished interface for those settings, yet, but you can view update +them using the administration interface provided by Django (the framework funkwhale is built on). + +The URL should be ``/api/admin/dynamic_preferences/globalpreferencemodel/`` (prepend your domain in front of it, of course). + +If you plan to use acoustid and external imports +(e.g. with the youtube backends), you should edit the corresponding +settings in this interface. diff --git a/docs/importing-music.rst b/docs/importing-music.rst index 3fa9e9990..92ff6bfb5 100644 --- a/docs/importing-music.rst +++ b/docs/importing-music.rst @@ -25,6 +25,10 @@ to the ``/music`` directory on the container: For the best results, we recommand tagging your music collection through `Picard `_ in order to have the best quality metadata. +.. note:: + + Autotagging using acoustid is experimental now and can yield unexpected + result. You can disable acoustid by passing the --no-acoustid flag. .. note:: diff --git a/docs/index.rst b/docs/index.rst index ca6504b52..17e9fe7f0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,6 +13,7 @@ Funkwhale is a self-hosted, modern free and open-source music server, heavily in features installation/index + configuration importing-music changelog diff --git a/docs/installation/external_dependencies.rst b/docs/installation/external_dependencies.rst index fa0908545..6641bef00 100644 --- a/docs/installation/external_dependencies.rst +++ b/docs/installation/external_dependencies.rst @@ -43,6 +43,15 @@ you should now be able to open a postgresql shell: sudo -u funkwhale -H psql +Unless you give a superuser access to the database user, you should also +enable some extensions on your database server, as those are required +for funkwhale to work properly: + +.. code-block:: shell + + sudo -u postgres psql -c 'CREATE EXTENSION "unaccent";'' + + Cache setup (Redis) ------------------- diff --git a/docs/installation/index.rst b/docs/installation/index.rst index 218049dd1..2e62c71ec 100644 --- a/docs/installation/index.rst +++ b/docs/installation/index.rst @@ -59,10 +59,11 @@ Ensure you have a recent version of nginx on your server. On debian-like system, apt-get update apt-get install nginx -Then, download our sample virtualhost file: +Then, download our sample virtualhost file and proxy conf: .. parsed-literal:: + curl -L -o /etc/nginx/funkwhale_proxy.conf "https://code.eliotberriot.com/funkwhale/funkwhale/raw/|version|/deploy/funkwhale_proxy.conf" curl -L -o /etc/nginx/sites-enabled/funkwhale.conf "https://code.eliotberriot.com/funkwhale/funkwhale/raw/|version|/deploy/nginx.conf" Ensure static assets and proxy pass match your configuration, and check the configuration is valid with ``nginx -t``. If everything is fine, you can restart your nginx server with ``service nginx restart``. diff --git a/front/src/components/Pagination.vue b/front/src/components/Pagination.vue index 83b386fde..71813a18a 100644 --- a/front/src/components/Pagination.vue +++ b/front/src/components/Pagination.vue @@ -1,23 +1,23 @@ @@ -32,7 +32,38 @@ export default { }, computed: { pages: function () { - return _.range(1, this.maxPage + 1) + let range = 2 + let current = this.current + let beginning = _.range(1, Math.min(this.maxPage, 1 + range)) + let middle = _.range(Math.max(1, current - range + 1), Math.min(this.maxPage, current + range)) + let end = _.range(this.maxPage, Math.max(1, this.maxPage - range)) + let allowed = beginning.concat(middle, end) + allowed = _.uniq(allowed) + allowed = _.sortBy(allowed, [(e) => { return e }]) + let final = [] + allowed.forEach(p => { + let last = final.slice(-1)[0] + let consecutive = true + if (last === 'skip') { + consecutive = false + } else { + if (!last) { + consecutive = true + } else { + consecutive = last + 1 === p + } + } + if (consecutive) { + final.push(p) + } else { + if (p !== 'skip') { + final.push('skip') + final.push(p) + } + } + }) + console.log(final) + return final }, maxPage: function () { return Math.ceil(this.total / this.paginateBy) diff --git a/front/src/components/audio/PlayButton.vue b/front/src/components/audio/PlayButton.vue index 451cdcf01..679f8b795 100644 --- a/front/src/components/audio/PlayButton.vue +++ b/front/src/components/audio/PlayButton.vue @@ -1,6 +1,9 @@