Merge branch 'master' into develop
This commit is contained in:
commit
c58c74d653
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/bash -eux
|
#!/bin/bash -eux
|
||||||
python /app/manage.py collectstatic --noinput
|
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-}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production")
|
||||||
|
os.environ.setdefault("ASGI_THREADS", "5")
|
||||||
|
|
||||||
import django # noqa
|
import django # noqa
|
||||||
|
|
||||||
|
|
|
@ -270,7 +270,7 @@ DATABASES = {
|
||||||
"default": env.db("DATABASE_URL")
|
"default": env.db("DATABASE_URL")
|
||||||
}
|
}
|
||||||
DATABASES["default"]["ATOMIC_REQUESTS"] = True
|
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 = {
|
MIGRATION_MODULES = {
|
||||||
# see https://github.com/jazzband/django-oauth-toolkit/issues/634
|
# see https://github.com/jazzband/django-oauth-toolkit/issues/634
|
||||||
|
|
|
@ -72,7 +72,7 @@ def clean_id3_pictures(apic):
|
||||||
|
|
||||||
def get_mp4_tag(f, k):
|
def get_mp4_tag(f, k):
|
||||||
if k == "pictures":
|
if k == "pictures":
|
||||||
return f.get("covr")
|
return f.get("covr", [])
|
||||||
raw_value = f.get(k, None)
|
raw_value = f.get(k, None)
|
||||||
|
|
||||||
if not raw_value:
|
if not raw_value:
|
||||||
|
|
|
@ -320,6 +320,10 @@ def get_file_path(audio_file):
|
||||||
)
|
)
|
||||||
path = "/music" + audio_file.replace(prefix, "", 1)
|
path = "/music" + audio_file.replace(prefix, "", 1)
|
||||||
if path.startswith("http://") or path.startswith("https://"):
|
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")
|
return (settings.PROTECT_FILES_PATH + "/media/" + path).encode("utf-8")
|
||||||
# needed to serve files with % or ? chars
|
# needed to serve files with % or ? chars
|
||||||
path = urllib.parse.quote(path)
|
path = urllib.parse.quote(path)
|
||||||
|
|
|
@ -219,6 +219,11 @@ def test_can_get_metadata_from_m4a_file(field, value):
|
||||||
assert data.get(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():
|
def test_can_get_metadata_from_flac_file_not_crash_if_empty():
|
||||||
path = os.path.join(DATA_DIR, "sample.flac")
|
path = os.path.join(DATA_DIR, "sample.flac")
|
||||||
data = metadata.Metadata(path)
|
data = metadata.Metadata(path)
|
||||||
|
|
|
@ -247,6 +247,18 @@ def test_serve_file_in_place_nginx_encode_url(
|
||||||
assert response["X-Accel-Redirect"] == expected
|
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(
|
@pytest.mark.parametrize(
|
||||||
"proxy,serve_path,expected",
|
"proxy,serve_path,expected",
|
||||||
[
|
[
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Added a retry option for failed uploads (#942)
|
|
@ -0,0 +1 @@
|
||||||
|
Fix import crash when importing M4A file with no embedded cover (#946)
|
|
@ -0,0 +1 @@
|
||||||
|
Fixed style glitches in dropdowns
|
|
@ -0,0 +1 @@
|
||||||
|
Reduce the number of simultaneous DB connections under some deployment scenario
|
|
@ -0,0 +1 @@
|
||||||
|
Fix audio serving issues under S3/nginx when signatures are enabled
|
|
@ -47,6 +47,7 @@ Funkwhale packages are available for the following platforms:
|
||||||
- `YunoHost 3 <https://yunohost.org/>`_: https://github.com/YunoHost-Apps/funkwhale_ynh (kindly maintained by `@Jibec <https://github.com/Jibec>`_)
|
- `YunoHost 3 <https://yunohost.org/>`_: https://github.com/YunoHost-Apps/funkwhale_ynh (kindly maintained by `@Jibec <https://github.com/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)
|
- 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 <https://github.com/mmai/funkwhale-nixos>`_ (kindly maintained by @mmai)
|
- `NixOS <https://github.com/mmai/funkwhale-nixos>`_ (kindly maintained by @mmai)
|
||||||
|
- `Helm chart <https://gitlab.com/ananace/charts/>`_ to install Funkwhale on Kubenertes (kindly maintained by @ananace)
|
||||||
|
|
||||||
Project architecture
|
Project architecture
|
||||||
--------------------
|
--------------------
|
||||||
|
|
|
@ -91,9 +91,20 @@
|
||||||
<table class="ui unstackable table">
|
<table class="ui unstackable table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th><translate translate-context="Content/Library/Table.Label">Filename</translate></th>
|
<th class="ten wide"><translate translate-context="Content/Library/Table.Label">Filename</translate></th>
|
||||||
<th><translate translate-context="Content/*/*/Noun">Size</translate></th>
|
<th><translate translate-context="Content/*/*/Noun">Size</translate></th>
|
||||||
<th><translate translate-context="*/*/*">Status</translate></th>
|
<th><translate translate-context="*/*/*">Status</translate></th>
|
||||||
|
<th><translate translate-context="*/*/*">Actions</translate></th>
|
||||||
|
</tr>
|
||||||
|
<tr v-if="retryableFiles.length > 1">
|
||||||
|
<th class="ten wide"></th>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
<th>
|
||||||
|
<button class="ui right floated small basic button" @click.prevent="retry(retryableFiles)">
|
||||||
|
<translate translate-context="Content/Library/Table">Retry failed uploads</translate>
|
||||||
|
</button>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -113,9 +124,20 @@
|
||||||
<translate translate-context="Content/Library/Table" key="2">Uploading…</translate>
|
<translate translate-context="Content/Library/Table" key="2">Uploading…</translate>
|
||||||
({{ parseInt(file.progress) }}%)
|
({{ parseInt(file.progress) }}%)
|
||||||
</span>
|
</span>
|
||||||
<template v-else>
|
<span v-else class="ui label"><translate translate-context="Content/Library/*/Short" key="3">Pending</translate></span>
|
||||||
<span class="ui label"><translate translate-context="Content/Library/*/Short" key="3">Pending</translate></span>
|
</td>
|
||||||
<button class="ui tiny basic red icon button" @click.prevent="$refs.upload.remove(file)"><i class="delete icon"></i></button>
|
<td>
|
||||||
|
<template v-if="file.error">
|
||||||
|
<button
|
||||||
|
class="ui tiny basic icon right floated button"
|
||||||
|
:title="labels.retry"
|
||||||
|
@click.prevent="retry([file])"
|
||||||
|
v-if="retryableFiles.indexOf(file) > -1">
|
||||||
|
<i class="redo icon"></i>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="!file.success">
|
||||||
|
<button class="ui tiny basic red icon right floated button" @click.prevent="$refs.upload.remove(file)"><i class="delete icon"></i></button>
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -251,6 +273,13 @@ export default {
|
||||||
self.uploads.objects[event.upload.uuid] = event.upload;
|
self.uploads.objects[event.upload.uuid] = event.upload;
|
||||||
self.needsRefresh = true
|
self.needsRefresh = true
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
retry (files) {
|
||||||
|
files.forEach((file) => {
|
||||||
|
this.$refs.upload.update(file, {error: '', progress: '0.00'})
|
||||||
|
})
|
||||||
|
this.$refs.upload.active = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -274,6 +303,7 @@ export default {
|
||||||
server,
|
server,
|
||||||
network,
|
network,
|
||||||
timeout,
|
timeout,
|
||||||
|
retry: this.$pgettext('*/*/*/Verb', "Retry"),
|
||||||
extension: this.$gettextInterpolate(extension, {
|
extension: this.$gettextInterpolate(extension, {
|
||||||
extensions: this.supportedExtensions.join(", ")
|
extensions: this.supportedExtensions.join(", ")
|
||||||
})
|
})
|
||||||
|
@ -295,6 +325,11 @@ export default {
|
||||||
return f.error;
|
return f.error;
|
||||||
}).length;
|
}).length;
|
||||||
},
|
},
|
||||||
|
retryableFiles () {
|
||||||
|
return this.files.filter(f => {
|
||||||
|
return f.error;
|
||||||
|
});
|
||||||
|
},
|
||||||
processableFiles() {
|
processableFiles() {
|
||||||
return (
|
return (
|
||||||
this.uploads.pending +
|
this.uploads.pending +
|
||||||
|
@ -342,7 +377,9 @@ export default {
|
||||||
uploadedSize () {
|
uploadedSize () {
|
||||||
let uploaded = 0
|
let uploaded = 0
|
||||||
this.files.forEach((f) => {
|
this.files.forEach((f) => {
|
||||||
|
if (!f.error) {
|
||||||
uploaded += f.size * (f.progress / 100)
|
uploaded += f.size * (f.progress / 100)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return uploaded
|
return uploaded
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue