Extend OpenSubsonic support with Transcode Offset extension

merge-requests/2775/head
Krzysztof S 2024-04-13 22:41:12 +00:00
rodzic d673e77dff
commit 6491a946c8
5 zmienionych plików z 38 dodań i 13 usunięć

Wyświetl plik

@ -916,14 +916,14 @@ class Upload(models.Model):
# Not using reverse because this is slow
return self.listen_url + "&download=false"
def get_transcoded_version(self, format, max_bitrate=None):
def get_transcoded_version(self, format, max_bitrate=None, time_offset=None):
if format:
mimetype = utils.EXTENSION_TO_MIMETYPE[format]
else:
mimetype = self.mimetype or "audio/mpeg"
format = utils.MIMETYPE_TO_EXTENSION[mimetype]
existing_versions = self.versions.filter(mimetype=mimetype)
existing_versions = self.versions.filter(mimetype=mimetype, time_offset=time_offset)
if max_bitrate is not None:
# we don't want to transcode if a 320kbps version is available
# and we're requestiong 300kbps
@ -936,18 +936,24 @@ class Upload(models.Model):
# we found an existing version, no need to transcode again
return existing_versions[0]
return self.create_transcoded_version(mimetype, format, bitrate=max_bitrate)
return self.create_transcoded_version(mimetype, format, bitrate=max_bitrate, time_offset=time_offset)
@transaction.atomic
def create_transcoded_version(self, mimetype, format, bitrate):
def create_transcoded_version(self, mimetype, format, bitrate, time_offset):
# we create the version with an empty file, then
# we'll write to it
f = ContentFile(b"")
bitrate = min(bitrate or 320000, self.bitrate or 320000)
version = self.versions.create(mimetype=mimetype, bitrate=bitrate, size=0)
version = self.versions.create(mimetype=mimetype, bitrate=bitrate, time_offset=time_offset, size=0)
if time_offset is not None:
time_offset_filename = "-" + str(time_offset)
else:
time_offset_filename = ""
# we keep the same name, but we update the extension
new_name = (
os.path.splitext(os.path.basename(self.audio_file.name))[0] + f".{format}"
os.path.splitext(os.path.basename(self.audio_file.name))[0] + time_offset_filename + f".{format}"
)
version.audio_file.save(new_name, f)
utils.transcode_audio(
@ -955,6 +961,7 @@ class Upload(models.Model):
output=version.audio_file,
output_format=utils.MIMETYPE_TO_EXTENSION[mimetype],
bitrate=str(bitrate),
time_offset=time_offset,
)
version.size = version.audio_file.size
version.save(update_fields=["size"])
@ -1002,10 +1009,11 @@ class UploadVersion(models.Model):
accessed_date = models.DateTimeField(null=True, blank=True)
audio_file = models.FileField(upload_to=get_file_path, max_length=255)
bitrate = models.PositiveIntegerField()
time_offset = models.IntegerField(null=True, blank=True)
size = models.IntegerField()
class Meta:
unique_together = ("upload", "mimetype", "bitrate")
unique_together = ("upload", "mimetype", "bitrate", "time_offset")
@property
def filename(self) -> str:

Wyświetl plik

@ -108,7 +108,10 @@ def transcode_file(input, output, input_format=None, output_format="mp3", **kwar
return transcode_audio(audio, output, output_format, **kwargs)
def transcode_audio(audio, output, output_format, **kwargs):
def transcode_audio(audio, output, output_format, time_offset=None, **kwargs):
if time_offset is not None:
audio = audio[time_offset:]
with output.open("wb"):
return audio.export(output, format=output_format, **kwargs)

Wyświetl plik

@ -501,7 +501,7 @@ def get_file_path(audio_file):
return path.encode("utf-8")
def should_transcode(upload, format, max_bitrate=None):
def should_transcode(upload, format, max_bitrate=None, time_offset=None):
if not preferences.get("music__transcoding_enabled"):
return False
format_need_transcoding = True
@ -552,7 +552,7 @@ def record_downloads(f):
@record_downloads
def handle_serve(
upload, user, format=None, max_bitrate=None, proxy_media=True, download=True
upload, user, format=None, max_bitrate=None, time_offset=None, proxy_media=True, download=True
):
f = upload
# we update the accessed_date
@ -593,8 +593,8 @@ def handle_serve(
file_path = get_file_path(f.source.replace("file://", "", 1))
mt = f.mimetype
if should_transcode(f, format, max_bitrate=max_bitrate):
transcoded_version = f.get_transcoded_version(format, max_bitrate=max_bitrate)
if should_transcode(f, format, max_bitrate=max_bitrate, time_offset=time_offset):
transcoded_version = f.get_transcoded_version(format, max_bitrate=max_bitrate, time_offset=time_offset)
transcoded_version.accessed_date = now
transcoded_version.save(update_fields=["accessed_date"])
f = transcoded_version

Wyświetl plik

@ -189,7 +189,10 @@ class SubsonicViewSet(viewsets.GenericViewSet):
)
def get_open_subsonic_extensions(self, request, *args, **kwargs):
data = {
"openSubsonicExtensions": [{"name": "formPost", "versions": [1]}],
"openSubsonicExtensions": [
{"name": "formPost", "versions": [1]},
{"name": "transcodeOffset", "versions": [1]}
],
}
return response.Response(data, status=200)
@ -309,6 +312,15 @@ class SubsonicViewSet(viewsets.GenericViewSet):
if max_bitrate:
max_bitrate = max_bitrate * 1000
time_offset = data.get("timeOffset")
try:
time_offset = int(time_offset) or None
except (TypeError, ValueError):
time_offset = None
if time_offset:
time_offset = time_offset * 1000
format = data.get("format") or None
if max_bitrate and not format:
# specific bitrate requested, but no format specified
@ -323,6 +335,7 @@ class SubsonicViewSet(viewsets.GenericViewSet):
user=request.user,
format=format,
max_bitrate=max_bitrate,
time_offset=time_offset,
# Subsonic clients don't expect 302 redirection unfortunately,
# So we have to proxy media files
proxy_media=True,

Wyświetl plik

@ -1 +1,2 @@
Extend Subsonic API with OpenSubsonic support (#2270)
Extend OpenSubsonic support with Transcode Offset extension