Drop python 3.8 and 3.9, support python 3.13

merge-requests/2823/head
petitminion 2025-01-13 21:47:18 +00:00
rodzic 75c4b3a7ff
commit 78856cc32a
9 zmienionych plików z 1426 dodań i 1369 usunięć

Wyświetl plik

@ -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)/

Wyświetl plik

@ -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

Wyświetl plik

@ -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),
) )

Wyświetl plik

@ -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):

2676
api/poetry.lock wygenerowano

Plik diff jest za duży Load Diff

Wyświetl plik

@ -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"

Wyświetl plik

@ -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"

Wyświetl plik

@ -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"})

Wyświetl plik

@ -0,0 +1 @@
Drop python 3.8 and 3.9 (#2282)