diff --git a/api/compose/django/server.sh b/api/compose/django/server.sh
index 0e4c737af..9c7709516 100755
--- a/api/compose/django/server.sh
+++ b/api/compose/django/server.sh
@@ -1,3 +1,3 @@
#!/bin/bash -eux
python /app/manage.py collectstatic --noinput
-gunicorn config.asgi:application -w ${FUNKWHALE_WEB_WORKERS-1} -k uvicorn.workers.UvicornWorker -b 0.0.0.0:5000
+gunicorn config.asgi:application -w ${FUNKWHALE_WEB_WORKERS-1} -k uvicorn.workers.UvicornWorker -b 0.0.0.0:5000 ${GUNICORN_ARGS-}
diff --git a/api/config/asgi.py b/api/config/asgi.py
index b4a8105de..d578a3bff 100644
--- a/api/config/asgi.py
+++ b/api/config/asgi.py
@@ -1,6 +1,7 @@
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
+os.environ.setdefault("ASGI_THREADS", "5")
import django # noqa
diff --git a/api/config/settings/common.py b/api/config/settings/common.py
index a20081dd2..43a7a29f5 100644
--- a/api/config/settings/common.py
+++ b/api/config/settings/common.py
@@ -270,7 +270,7 @@ DATABASES = {
"default": env.db("DATABASE_URL")
}
DATABASES["default"]["ATOMIC_REQUESTS"] = True
-DATABASES["default"]["CONN_MAX_AGE"] = env("DB_CONN_MAX_AGE", default=60 * 60)
+DATABASES["default"]["CONN_MAX_AGE"] = env("DB_CONN_MAX_AGE", default=60 * 5)
MIGRATION_MODULES = {
# see https://github.com/jazzband/django-oauth-toolkit/issues/634
diff --git a/api/funkwhale_api/music/metadata.py b/api/funkwhale_api/music/metadata.py
index 801ea6a9b..d22f637fd 100644
--- a/api/funkwhale_api/music/metadata.py
+++ b/api/funkwhale_api/music/metadata.py
@@ -72,7 +72,7 @@ def clean_id3_pictures(apic):
def get_mp4_tag(f, k):
if k == "pictures":
- return f.get("covr")
+ return f.get("covr", [])
raw_value = f.get(k, None)
if not raw_value:
diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py
index c0effbac2..9de785116 100644
--- a/api/funkwhale_api/music/views.py
+++ b/api/funkwhale_api/music/views.py
@@ -320,6 +320,10 @@ def get_file_path(audio_file):
)
path = "/music" + audio_file.replace(prefix, "", 1)
if path.startswith("http://") or path.startswith("https://"):
+ protocol, remainder = path.split("://", 1)
+ hostname, r_path = remainder.split("/", 1)
+ r_path = urllib.parse.quote(r_path)
+ path = protocol + "://" + hostname + "/" + r_path
return (settings.PROTECT_FILES_PATH + "/media/" + path).encode("utf-8")
# needed to serve files with % or ? chars
path = urllib.parse.quote(path)
diff --git a/api/tests/music/test_metadata.py b/api/tests/music/test_metadata.py
index 9a3826ce3..6c9b03846 100644
--- a/api/tests/music/test_metadata.py
+++ b/api/tests/music/test_metadata.py
@@ -219,6 +219,11 @@ def test_can_get_metadata_from_m4a_file(field, value):
assert data.get(field) == value
+def test_get_pictures_m4a_empty():
+ pictures = metadata.get_mp4_tag({}, "pictures")
+ assert metadata.clean_mp4_pictures(pictures) == []
+
+
def test_can_get_metadata_from_flac_file_not_crash_if_empty():
path = os.path.join(DATA_DIR, "sample.flac")
data = metadata.Metadata(path)
diff --git a/api/tests/music/test_views.py b/api/tests/music/test_views.py
index 0339437d4..22f7da635 100644
--- a/api/tests/music/test_views.py
+++ b/api/tests/music/test_views.py
@@ -247,6 +247,18 @@ def test_serve_file_in_place_nginx_encode_url(
assert response["X-Accel-Redirect"] == expected
+def test_serve_s3_nginx_encode_url(mocker, settings):
+ settings.PROTECT_FILE_PATH = "/_protected/media"
+ settings.REVERSE_PROXY_TYPE = "nginx"
+ audio_file = mocker.Mock(url="https://s3.storage.example/path/to/mp3?aws=signature")
+
+ expected = (
+ b"/_protected/media/https://s3.storage.example/path/to/mp3%3Faws%3Dsignature"
+ )
+
+ assert views.get_file_path(audio_file) == expected
+
+
@pytest.mark.parametrize(
"proxy,serve_path,expected",
[
diff --git a/changes/changelog.d/942.enhancement b/changes/changelog.d/942.enhancement
new file mode 100644
index 000000000..66a1bfff3
--- /dev/null
+++ b/changes/changelog.d/942.enhancement
@@ -0,0 +1 @@
+Added a retry option for failed uploads (#942)
diff --git a/changes/changelog.d/946.bugfix b/changes/changelog.d/946.bugfix
new file mode 100644
index 000000000..9f6998306
--- /dev/null
+++ b/changes/changelog.d/946.bugfix
@@ -0,0 +1 @@
+Fix import crash when importing M4A file with no embedded cover (#946)
diff --git a/changes/changelog.d/css-dropdown.bugfix b/changes/changelog.d/css-dropdown.bugfix
new file mode 100644
index 000000000..259731bdd
--- /dev/null
+++ b/changes/changelog.d/css-dropdown.bugfix
@@ -0,0 +1 @@
+Fixed style glitches in dropdowns
diff --git a/changes/changelog.d/db.enhancement b/changes/changelog.d/db.enhancement
new file mode 100644
index 000000000..da25ea858
--- /dev/null
+++ b/changes/changelog.d/db.enhancement
@@ -0,0 +1 @@
+Reduce the number of simultaneous DB connections under some deployment scenario
diff --git a/changes/changelog.d/s3-proxy.bugfix b/changes/changelog.d/s3-proxy.bugfix
new file mode 100644
index 000000000..0afe65caf
--- /dev/null
+++ b/changes/changelog.d/s3-proxy.bugfix
@@ -0,0 +1 @@
+Fix audio serving issues under S3/nginx when signatures are enabled
diff --git a/docs/installation/index.rst b/docs/installation/index.rst
index 1b097cd48..047f8739f 100644
--- a/docs/installation/index.rst
+++ b/docs/installation/index.rst
@@ -47,6 +47,7 @@ Funkwhale packages are available for the following platforms:
- `YunoHost 3 `_: https://github.com/YunoHost-Apps/funkwhale_ynh (kindly maintained by `@Jibec `_)
- ArchLinux (as an AUR package): if you'd rather use a package, check out this alternative installation method on ArchLinux: https://wiki.archlinux.org/index.php/Funkwhale (package and wiki kindly maintained by getzee)
- `NixOS `_ (kindly maintained by @mmai)
+- `Helm chart `_ to install Funkwhale on Kubenertes (kindly maintained by @ananace)
Project architecture
--------------------
diff --git a/front/src/components/library/FileUpload.vue b/front/src/components/library/FileUpload.vue
index d88efd5ab..9e86600b3 100644
--- a/front/src/components/library/FileUpload.vue
+++ b/front/src/components/library/FileUpload.vue
@@ -91,9 +91,20 @@
- Filename |
+ Filename |
Size |
Status |
+ Actions |
+
+
+ |
+ |
+ |
+
+
+ |
@@ -113,9 +124,20 @@
Uploading…
({{ parseInt(file.progress) }}%)
-
- Pending
-
+ Pending
+
+
+
+
+
+
+
|
@@ -251,6 +273,13 @@ export default {
self.uploads.objects[event.upload.uuid] = event.upload;
self.needsRefresh = true
});
+ },
+ retry (files) {
+ files.forEach((file) => {
+ this.$refs.upload.update(file, {error: '', progress: '0.00'})
+ })
+ this.$refs.upload.active = true;
+
}
},
computed: {
@@ -274,6 +303,7 @@ export default {
server,
network,
timeout,
+ retry: this.$pgettext('*/*/*/Verb', "Retry"),
extension: this.$gettextInterpolate(extension, {
extensions: this.supportedExtensions.join(", ")
})
@@ -295,6 +325,11 @@ export default {
return f.error;
}).length;
},
+ retryableFiles () {
+ return this.files.filter(f => {
+ return f.error;
+ });
+ },
processableFiles() {
return (
this.uploads.pending +
@@ -342,7 +377,9 @@ export default {
uploadedSize () {
let uploaded = 0
this.files.forEach((f) => {
- uploaded += f.size * (f.progress / 100)
+ if (!f.error) {
+ uploaded += f.size * (f.progress / 100)
+ }
})
return uploaded
}