Add album cover image to track
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

This commit is contained in:
apatil 2025-05-07 20:46:13 +01:00
parent c1e1966977
commit 7627433107
4 changed files with 196 additions and 5 deletions

159
package-lock.json generated
View File

@ -17,6 +17,7 @@
"formidable": "^3.5.4",
"gray-matter": "^4.0.3",
"highlight.js": "^11.11.1",
"music-metadata": "^11.2.1",
"next": "15.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
@ -1551,6 +1552,30 @@
"tslib": "^2.8.0"
}
},
"node_modules/@tokenizer/inflate": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz",
"integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==",
"license": "MIT",
"dependencies": {
"debug": "^4.4.0",
"fflate": "^0.8.2",
"token-types": "^6.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/@tokenizer/token": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
"license": "MIT"
},
"node_modules/@tybys/wasm-util": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
@ -2787,7 +2812,6 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
@ -3932,6 +3956,12 @@
"reusify": "^1.0.4"
}
},
"node_modules/fflate": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
"license": "MIT"
},
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@ -3945,6 +3975,24 @@
"node": ">=16.0.0"
}
},
"node_modules/file-type": {
"version": "20.5.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz",
"integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==",
"license": "MIT",
"dependencies": {
"@tokenizer/inflate": "^0.2.6",
"strtok3": "^10.2.0",
"token-types": "^6.0.0",
"uint8array-extras": "^1.4.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sindresorhus/file-type?sponsor=1"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@ -4516,6 +4564,26 @@
"node": ">=0.10.0"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "BSD-3-Clause"
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@ -5350,7 +5418,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.8"
@ -5887,6 +5954,35 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/music-metadata": {
"version": "11.2.1",
"resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-11.2.1.tgz",
"integrity": "sha512-on3rRUrDXZOcWUb+bwRPQw4eZVD4Lmn1kFIa893xbM2pWi3kMTtz1EGA86TVBRgvk3Rcw5CftmTKY/wm1eI2Pg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/Borewit"
},
{
"type": "buymeacoffee",
"url": "https://buymeacoffee.com/borewit"
}
],
"license": "MIT",
"dependencies": {
"@tokenizer/token": "^0.3.0",
"content-type": "^1.0.5",
"debug": "^4.4.0",
"file-type": "^20.4.1",
"media-typer": "^1.1.0",
"strtok3": "^10.2.2",
"token-types": "^6.0.0",
"uint8array-extras": "^1.4.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@ -6289,6 +6385,19 @@
"node": ">=8"
}
},
"node_modules/peek-readable": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-7.0.0.tgz",
"integrity": "sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -7356,6 +7465,23 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/strtok3": {
"version": "10.2.2",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.2.2.tgz",
"integrity": "sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==",
"license": "MIT",
"dependencies": {
"@tokenizer/token": "^0.3.0",
"peek-readable": "^7.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/styled-jsx": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
@ -7478,6 +7604,23 @@
"node": ">=0.6"
}
},
"node_modules/token-types": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz",
"integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==",
"license": "MIT",
"dependencies": {
"@tokenizer/token": "^0.3.0",
"ieee754": "^1.2.1"
},
"engines": {
"node": ">=14.16"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Borewit"
}
},
"node_modules/trim-lines": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
@ -7650,6 +7793,18 @@
"node": ">=14.17"
}
},
"node_modules/uint8array-extras": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz",
"integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/unbox-primitive": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",

View File

@ -18,6 +18,7 @@
"formidable": "^3.5.4",
"gray-matter": "^4.0.3",
"highlight.js": "^11.11.1",
"music-metadata": "^11.2.1",
"next": "15.3.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",

View File

@ -0,0 +1,23 @@
import { NextRequest } from 'next/server';
import { resolve } from 'path';
import * as mm from 'music-metadata';
export async function GET(req: NextRequest, { params }: { params: { track: string } }) {
try {
const trackName = decodeURIComponent(params.track);
const filePath = resolve(process.cwd(), 'public/music', trackName);
const metadata = await mm.parseFile(filePath);
const picture = metadata.common.picture?.[0];
if (!picture) {
return new Response('No cover found', { status: 404 });
}
return new Response(picture.data, {
headers: {
'Content-Type': picture.format,
'Content-Length': picture.data.length.toString(),
},
});
} catch (err) {
console.log(err);
return new Response('Error retrieving cover', { status: 500 });
}
}

View File

@ -1,14 +1,21 @@
'use client';
import { Box, Typography, IconButton, Slider, useTheme } from '@mui/material';
import { Box, Typography, IconButton, Slider, useTheme, CardMedia } from '@mui/material';
import { PlayArrow, Pause, VolumeUp } from '@mui/icons-material';
import { useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
export default function AudioPlayer({ src, title }: { src: string; title: string }) {
const audioRef = useRef<HTMLAudioElement | null>(null);
const [playing, setPlaying] = useState(false);
const [volume, setVolume] = useState(100);
const [coverUrl, setCoverUrl] = useState<string | null>(null);
const theme = useTheme();
const filename = src.split('/').pop();
useEffect(() => {
if (filename)
setCoverUrl(`/api/music/${encodeURIComponent(filename)}/cover`)
}, [filename])
const togglePlay = () => {
const audio = audioRef.current;
@ -44,6 +51,11 @@ export default function AudioPlayer({ src, title }: { src: string; title: string
mx: 'auto',
}}
>
{
coverUrl && (
<CardMedia component={"img"} image={`${coverUrl}`} alt='Album cover' sx={{ width: '100%', height: 200, objectFit: 'cover', borderRadius: 2, mb: 2 }} />
)
}
<Typography variant="subtitle1" gutterBottom>{title}</Typography>
<Box display="flex" alignItems="center" gap={1}>
<IconButton onClick={togglePlay}>
@ -52,7 +64,7 @@ export default function AudioPlayer({ src, title }: { src: string; title: string
<VolumeUp />
<Slider
value={volume}
onChange={handleVolumeChange}
onChange={(e, val) => handleVolumeChange(e, val)}
aria-label="Volume"
sx={{ width: 100 }}
/>