kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
Drop python 3.8 and 3.9, support python 3.13
rodzic
75c4b3a7ff
commit
78856cc32a
|
@ -232,7 +232,7 @@ test_api:
|
||||||
image: $CI_REGISTRY/funkwhale/ci/python-funkwhale-api:$PYTHON_VERSION
|
image: $CI_REGISTRY/funkwhale/ci/python-funkwhale-api:$PYTHON_VERSION
|
||||||
parallel:
|
parallel:
|
||||||
matrix:
|
matrix:
|
||||||
- PYTHON_VERSION: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
- PYTHON_VERSION: ["3.10", "3.11", "3.12", "3.13"]
|
||||||
services:
|
services:
|
||||||
- name: postgres:15-alpine
|
- name: postgres:15-alpine
|
||||||
command:
|
command:
|
||||||
|
@ -540,8 +540,8 @@ package:
|
||||||
artifacts: true
|
artifacts: true
|
||||||
- job: build_front
|
- job: build_front
|
||||||
artifacts: true
|
artifacts: true
|
||||||
# - job: build_tauri
|
# - job: build_tauri
|
||||||
# artifacts: true
|
# artifacts: true
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_COMMIT_BRANCH =~ /(stable|develop)/
|
- if: $CI_COMMIT_BRANCH =~ /(stable|develop)/
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM alpine:3.19 AS requirements
|
FROM alpine:3.21 AS requirements
|
||||||
|
|
||||||
RUN set -eux; \
|
RUN set -eux; \
|
||||||
apk add --no-cache \
|
apk add --no-cache \
|
||||||
|
@ -12,7 +12,7 @@ RUN set -eux; \
|
||||||
poetry export --without-hashes --extras typesense > requirements.txt; \
|
poetry export --without-hashes --extras typesense > requirements.txt; \
|
||||||
poetry export --without-hashes --with dev > dev-requirements.txt;
|
poetry export --without-hashes --with dev > dev-requirements.txt;
|
||||||
|
|
||||||
FROM alpine:3.19 AS builder
|
FROM alpine:3.21 AS builder
|
||||||
|
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
@ -37,11 +37,11 @@ RUN set -eux; \
|
||||||
openssl-dev \
|
openssl-dev \
|
||||||
postgresql-dev \
|
postgresql-dev \
|
||||||
zlib-dev \
|
zlib-dev \
|
||||||
py3-cryptography=41.0.7-r0 \
|
py3-cryptography \
|
||||||
py3-lxml=4.9.3-r1 \
|
py3-lxml \
|
||||||
py3-pillow=10.3.0-r0 \
|
py3-pillow \
|
||||||
py3-psycopg2=2.9.9-r0 \
|
py3-psycopg2 \
|
||||||
py3-watchfiles=0.19.0-r1 \
|
py3-watchfiles \
|
||||||
python3-dev
|
python3-dev
|
||||||
|
|
||||||
# Create virtual env
|
# Create virtual env
|
||||||
|
@ -61,11 +61,11 @@ RUN --mount=type=cache,target=~/.cache/pip; \
|
||||||
# to install the deps using pip.
|
# to install the deps using pip.
|
||||||
grep -Ev 'cryptography|lxml|pillow|psycopg2|watchfiles' /requirements.txt \
|
grep -Ev 'cryptography|lxml|pillow|psycopg2|watchfiles' /requirements.txt \
|
||||||
| pip3 install -r /dev/stdin \
|
| pip3 install -r /dev/stdin \
|
||||||
cryptography==41.0.7 \
|
cryptography \
|
||||||
lxml==4.9.3 \
|
lxml \
|
||||||
pillow==10.2.0 \
|
pillow \
|
||||||
psycopg2==2.9.9 \
|
psycopg2 \
|
||||||
watchfiles==0.19.0
|
watchfiles
|
||||||
|
|
||||||
ARG install_dev_deps=0
|
ARG install_dev_deps=0
|
||||||
RUN --mount=type=cache,target=~/.cache/pip; \
|
RUN --mount=type=cache,target=~/.cache/pip; \
|
||||||
|
@ -73,14 +73,14 @@ RUN --mount=type=cache,target=~/.cache/pip; \
|
||||||
if [ "$install_dev_deps" = "1" ] ; then \
|
if [ "$install_dev_deps" = "1" ] ; then \
|
||||||
grep -Ev 'cryptography|lxml|pillow|psycopg2|watchfiles' /dev-requirements.txt \
|
grep -Ev 'cryptography|lxml|pillow|psycopg2|watchfiles' /dev-requirements.txt \
|
||||||
| pip3 install -r /dev/stdin \
|
| pip3 install -r /dev/stdin \
|
||||||
cryptography==41.0.7 \
|
cryptography \
|
||||||
lxml==4.9.3 \
|
lxml \
|
||||||
pillow==10.2.0 \
|
pillow \
|
||||||
psycopg2==2.9.9 \
|
psycopg2 \
|
||||||
watchfiles==0.19.0; \
|
watchfiles; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
FROM alpine:3.19 AS production
|
FROM alpine:3.21 AS production
|
||||||
|
|
||||||
ENV PYTHONDONTWRITEBYTECODE=1
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
@ -97,11 +97,11 @@ RUN set -eux; \
|
||||||
libpq \
|
libpq \
|
||||||
libxml2 \
|
libxml2 \
|
||||||
libxslt \
|
libxslt \
|
||||||
py3-cryptography=41.0.7-r0 \
|
py3-cryptography \
|
||||||
py3-lxml=4.9.3-r1 \
|
py3-lxml \
|
||||||
py3-pillow=10.3.0-r0 \
|
py3-pillow \
|
||||||
py3-psycopg2=2.9.9-r0 \
|
py3-psycopg2 \
|
||||||
py3-watchfiles=0.19.0-r1 \
|
py3-watchfiles \
|
||||||
python3 \
|
python3 \
|
||||||
tzdata
|
tzdata
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import urllib.parse
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
import pydub
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.contenttypes.fields import GenericRelation
|
from django.contrib.contenttypes.fields import GenericRelation
|
||||||
from django.contrib.postgres.indexes import GinIndex
|
from django.contrib.postgres.indexes import GinIndex
|
||||||
|
@ -939,6 +938,12 @@ class Upload(models.Model):
|
||||||
if self.source and self.source.startswith("file://"):
|
if self.source and self.source.startswith("file://"):
|
||||||
return open(self.source.replace("file://", "", 1), "rb")
|
return open(self.source.replace("file://", "", 1), "rb")
|
||||||
|
|
||||||
|
def get_audio_file_path(self):
|
||||||
|
if self.audio_file:
|
||||||
|
return self.audio_file.path
|
||||||
|
if self.source and self.source.startswith("file://"):
|
||||||
|
return self.source.replace("file://", "", 1)
|
||||||
|
|
||||||
def get_audio_data(self):
|
def get_audio_data(self):
|
||||||
audio_file = self.get_audio_file()
|
audio_file = self.get_audio_file()
|
||||||
if not audio_file:
|
if not audio_file:
|
||||||
|
@ -952,14 +957,6 @@ class Upload(models.Model):
|
||||||
"size": self.get_file_size(),
|
"size": self.get_file_size(),
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_audio_segment(self):
|
|
||||||
input = self.get_audio_file()
|
|
||||||
if not input:
|
|
||||||
return
|
|
||||||
|
|
||||||
audio = pydub.AudioSegment.from_file(input)
|
|
||||||
return audio
|
|
||||||
|
|
||||||
def get_quality(self):
|
def get_quality(self):
|
||||||
extension_to_mimetypes = utils.get_extension_to_mimetype_dict()
|
extension_to_mimetypes = utils.get_extension_to_mimetype_dict()
|
||||||
|
|
||||||
|
@ -1083,8 +1080,8 @@ class Upload(models.Model):
|
||||||
)
|
)
|
||||||
version.audio_file.save(new_name, f)
|
version.audio_file.save(new_name, f)
|
||||||
utils.transcode_audio(
|
utils.transcode_audio(
|
||||||
audio=self.get_audio_segment(),
|
audio_file_path=self.get_audio_file_path(),
|
||||||
output=version.audio_file,
|
output_path=version.audio_file.path,
|
||||||
output_format=utils.MIMETYPE_TO_EXTENSION[mimetype],
|
output_format=utils.MIMETYPE_TO_EXTENSION[mimetype],
|
||||||
bitrate=str(bitrate),
|
bitrate=str(bitrate),
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,10 +4,10 @@ import pathlib
|
||||||
|
|
||||||
import magic
|
import magic
|
||||||
import mutagen
|
import mutagen
|
||||||
import pydub
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
|
from ffmpeg import FFmpeg
|
||||||
|
|
||||||
from funkwhale_api.common import throttling
|
from funkwhale_api.common import throttling
|
||||||
from funkwhale_api.common.search import get_fts_query # noqa
|
from funkwhale_api.common.search import get_fts_query # noqa
|
||||||
|
@ -114,15 +114,10 @@ def get_actor_from_request(request):
|
||||||
return actor
|
return actor
|
||||||
|
|
||||||
|
|
||||||
def transcode_file(input, output, input_format=None, output_format="mp3", **kwargs):
|
def transcode_audio(audio_file_path, output_path, output_format="mp3", **kwargs):
|
||||||
with input.open("rb"):
|
FFmpeg().input(audio_file_path).output(
|
||||||
audio = pydub.AudioSegment.from_file(input, format=input_format)
|
output_path, format=output_format, **kwargs
|
||||||
return transcode_audio(audio, output, output_format, **kwargs)
|
).option("y").execute()
|
||||||
|
|
||||||
|
|
||||||
def transcode_audio(audio, output, output_format, **kwargs):
|
|
||||||
with output.open("wb"):
|
|
||||||
return audio.export(output, format=output_format, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def increment_downloads_count(upload, user, wsgi_request):
|
def increment_downloads_count(upload, user, wsgi_request):
|
||||||
|
|
Plik diff jest za duży
Load Diff
|
@ -25,7 +25,7 @@ exclude = ["tests"]
|
||||||
funkwhale-manage = 'funkwhale_api.main:main'
|
funkwhale-manage = 'funkwhale_api.main:main'
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.8,<3.13"
|
python = "^3.10,<3.14"
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
dj-rest-auth = "5.0.2"
|
dj-rest-auth = "5.0.2"
|
||||||
|
@ -46,7 +46,7 @@ djangorestframework = "==3.14.0"
|
||||||
drf-spectacular = "==0.26.5"
|
drf-spectacular = "==0.26.5"
|
||||||
markdown = "==3.4.4"
|
markdown = "==3.4.4"
|
||||||
persisting-theory = "==1.0"
|
persisting-theory = "==1.0"
|
||||||
psycopg2-binary = "==2.9.9"
|
psycopg2-binary = "==2.9.10"
|
||||||
redis = "==5.0.1"
|
redis = "==5.0.1"
|
||||||
|
|
||||||
# Django LDAP
|
# Django LDAP
|
||||||
|
@ -73,12 +73,12 @@ bleach = "==6.1.0"
|
||||||
boto3 = "==1.26.161"
|
boto3 = "==1.26.161"
|
||||||
click = "==8.1.7"
|
click = "==8.1.7"
|
||||||
cryptography = "==41.0.7"
|
cryptography = "==41.0.7"
|
||||||
feedparser = "==6.0.10"
|
feedparser = "==6.0.11"
|
||||||
|
python-ffmpeg = "==2.0.12"
|
||||||
liblistenbrainz = "==0.5.5"
|
liblistenbrainz = "==0.5.5"
|
||||||
musicbrainzngs = "==0.7.1"
|
musicbrainzngs = "==0.7.1"
|
||||||
mutagen = "==1.46.0"
|
mutagen = "==1.46.0"
|
||||||
pillow = "==10.2.0"
|
pillow = "==11.1.0"
|
||||||
pydub = "==0.25.1"
|
|
||||||
pyld = "==2.0.3"
|
pyld = "==2.0.3"
|
||||||
python-magic = "==0.4.27"
|
python-magic = "==0.4.27"
|
||||||
requests = "==2.31.0"
|
requests = "==2.31.0"
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import tempfile
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -114,25 +113,6 @@ def test_get_dirs_and_files(path, expected, tmpdir):
|
||||||
assert utils.browse_dir(root_path, path) == expected
|
assert utils.browse_dir(root_path, path) == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"name, expected",
|
|
||||||
[
|
|
||||||
("sample.flac", {"bitrate": 128000, "length": 0}),
|
|
||||||
("test.mp3", {"bitrate": 16000, "length": 268}),
|
|
||||||
("test.ogg", {"bitrate": 128000, "length": 1}),
|
|
||||||
("test.opus", {"bitrate": 128000, "length": 1}),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
def test_transcode_file(name, expected):
|
|
||||||
path = pathlib.Path(os.path.join(DATA_DIR, name))
|
|
||||||
with tempfile.NamedTemporaryFile() as dest:
|
|
||||||
utils.transcode_file(path, pathlib.Path(dest.name))
|
|
||||||
with open(dest.name, "rb") as f:
|
|
||||||
result = {k: round(v) for k, v in utils.get_audio_file_data(f).items()}
|
|
||||||
|
|
||||||
assert result == expected
|
|
||||||
|
|
||||||
|
|
||||||
def test_custom_s3_domain(factories, settings):
|
def test_custom_s3_domain(factories, settings):
|
||||||
"""See #2220"""
|
"""See #2220"""
|
||||||
settings.AWS_S3_CUSTOM_DOMAIN = "my.custom.domain.tld"
|
settings.AWS_S3_CUSTOM_DOMAIN = "my.custom.domain.tld"
|
||||||
|
|
|
@ -621,8 +621,6 @@ def test_listen_transcode_in_place(
|
||||||
source="file://" + os.path.join(DATA_DIR, "test.ogg"),
|
source="file://" + os.path.join(DATA_DIR, "test.ogg"),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert upload.get_audio_segment()
|
|
||||||
|
|
||||||
url = reverse("api:v1:listen-detail", kwargs={"uuid": upload.track.uuid})
|
url = reverse("api:v1:listen-detail", kwargs={"uuid": upload.track.uuid})
|
||||||
handle_serve = mocker.spy(views, "handle_serve")
|
handle_serve = mocker.spy(views, "handle_serve")
|
||||||
response = logged_in_api_client.get(url, {"to": "mp3"})
|
response = logged_in_api_client.get(url, {"to": "mp3"})
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Drop python 3.8 and 3.9 (#2282)
|
Ładowanie…
Reference in New Issue