rely less on music-metadata, fix playlist cover, update container version

AuthorKonata <konata@posteo.jp>
Date
Commit763b5df340809be9b85e62ff46de663801339f84
Parentcc62431
4 files changed, 27 insertions(+), 23 deletions(-)
▾MContainerfile
@@ -1,11 +1,11 @@
FROM docker.io/oven/bun:1.2.9-alpine AS compile
FROM docker.io/oven/bun:1.2.15-alpine AS compile
WORKDIR /app
COPY . .
RUN bun install --cwd server
RUN bun run --cwd server compile
FROM docker.io/alpine:3.21
FROM docker.io/alpine:3.22
RUN apk add catatonit ffmpeg tar libavif-apps
WORKDIR /app
COPY --from=compile /app/server/dist .
▾Mserver/src/index.ts
@@ -232,7 +232,6 @@ const app = setup
set.headers["Content-Type"] = type;
};
if ("path" in fileScanResult) {
set.headers["Content-Type"] = fileScanResult.info.mimeType;
set.status = "OK";
if (query.transcode)
return new Response(
@@ -242,14 +241,15 @@ const app = setup
setContentType,
),
);
set.headers["Content-Type"] = fileScanResult.info.mimeType;
//wrapping in new response discards the accept-range header, which we don't support here
return new Response(Bun.file(fileScanResult.path));
}
if ("content" in fileScanResult) {
set.headers["Content-Type"] = fileScanResult.info.mimeType;
set.status = "OK";
if (query.transcode)
return new Response(await toAvif(fileScanResult.content, fileScanResult.info.mimeType, setContentType));
set.headers["Content-Type"] = fileScanResult.info.mimeType;
return new Response(fileScanResult.content);
}
set.status = fileScanResult.info.status;
▾Mserver/src/utils.ts
@@ -87,31 +87,22 @@ export async function probeFile(filePath: string): Promise<Metadata> {
const cached = probeCache.get(filePath);
if (cached) return cached;
const parsed = await parseFile(filePath, { duration: true, skipCovers: false, mkvUseIndex: true });
const probeData = {
title: parsed.common.title,
artist: parsed.common.artist,
album: parsed.common.album,
genre: parsed.common.genre,
trackNumber: parsed.common.track.no || undefined,
};
const info = await analyzeWithMediainfo(filePath);
// for (const track of info.media?.track || []) {
// console.log(track);
// console.log(track);
// }
const generalTrack = info.media?.track.find((track) => track["@type"] === "General");
const videoTrack = info.media?.track.find((track) => track["@type"] === "Video");
const audioTrack = info.media?.track.find((track) => track["@type"] === "Audio");
const probeResult = {
...probeData,
duration: generalTrack?.Duration || 0,
// title: decodeString(generalTrack?.Title),
// artist: generalTrack?.Album_Performer,
// album: generalTrack?.Album,
// genre: [generalTrack?.Genre || ""],
// trackNumber: generalTrack?.Track_Position,
if (!generalTrack?.Duration) throw new Error();
let probeResult: Metadata = {
duration: generalTrack?.Duration,
title: generalTrack?.Title,
artist: generalTrack?.Performer || generalTrack?.Album_Performer,
album: generalTrack?.Album,
genre: [generalTrack?.Genre || ""],
trackNumber: generalTrack?.Track_Position,
container: generalTrack?.Format,
audioCodec: audioTrack?.Format,
audioBitrate: audioTrack?.BitRate,
@@ -119,6 +110,19 @@ export async function probeFile(filePath: string): Promise<Metadata> {
videoBitrate: videoTrack?.BitRate,
};
//if file has broken encodings in their metadata, the "string" are actually objects, and contain this "@dt" field
//music-metadata can usually parse the correctly anyway, so use the library in that case
if (Object.values(probeResult).some((field) => typeof field === "object" && "@dt" in field)) {
const parsed = await parseFile(filePath, { duration: true, skipCovers: false, mkvUseIndex: true });
probeResult = {
...probeResult,
title: parsed.common.title,
artist: parsed.common.artist,
album: parsed.common.album,
genre: parsed.common.genre,
trackNumber: parsed.common.track.no || undefined,
};
}
probeCache.set(filePath, probeResult);
return probeResult;
}
▾Mwebclient/src/components/Playlist.tsx
@@ -240,7 +240,7 @@ export default function Playlist(props: PlaylistProps) {
props.videoShown() || (!props.options.hidePlaylistCover && (cover.loading || cover())) ? undefined : "none",
}}
>
<Show when={!props.videoShown}>
<Show when={!props.videoShown()}>
<Show when={cover.loading}>
<LoadingScreen />
</Show>