This commit is contained in:
valere
2025-12-31 16:31:53 +01:00
parent afb20fe75f
commit 9001025837
16 changed files with 3508 additions and 20277 deletions

View File

@@ -1,82 +0,0 @@
import fs from 'fs'
import path from 'path'
import { eventHandler } from 'h3'
import { getCardFromDate } from '../../../utils/cards'
export default eventHandler(async (event) => {
const dirPath = path.join(process.cwd(), '/mnt/media/files/music')
const urlPrefix = `https://files.erudi.fr/music`
try {
let allTracks: any[] = []
let files = await fs.promises.readdir(dirPath)
files = files.filter((f) => !f.startsWith('.') && !f.endsWith('.jpg'))
const tracks = files.map((file, index) => {
const EXT_RE = /\.(mp3|flac|wav|opus)$/i
const nameWithoutExt = file.replace(EXT_RE, '')
// On split sur __
const parts = nameWithoutExt.split('__')
let stamp = parts[0] || ''
let artist = parts[1] || ''
let title = parts[2] || ''
title = title.replaceAll('_', ' ')
artist = artist.replaceAll('_', ' ')
// Parser la date depuis le stamp
let year = 2020,
month = 1,
day = 1,
hour = 0
if (stamp.length === 10) {
year = Number(stamp.slice(0, 4))
month = Number(stamp.slice(4, 6))
day = Number(stamp.slice(6, 8))
hour = Number(stamp.slice(8, 10))
}
const date = new Date(year, month - 1, day, hour)
const card = getCardFromDate(date)
const url = `${urlPrefix}/${encodeURIComponent(file)}`
const coverId = `${urlPrefix}/cover/${encodeURIComponent(file).replace(EXT_RE, '.jpg')}`
return {
id: Number(`${year}${index + 1}`),
boxId: `ESPLAYLIST`,
year,
date,
title: title.trim(),
artist: artist.trim(),
url,
coverId,
card,
order: 0,
type: 'playlist'
}
})
tracks.sort((a, b) => b.date.getTime() - a.date.getTime())
// assign a stable order after sort (1..N)
tracks.forEach((t, i) => (t.order = i + 1))
allTracks.push(...tracks)
// Récupérer l'ID depuis les paramètres de la requête
const trackId = event.context.params?.id
// Si un ID est fourni dans l'URL, filtrer pour ne retourner que ce morceau
if (trackId) {
const track = allTracks.find((track) => track.id === Number(trackId))
return track || { error: 'Track not found' }
}
return allTracks
} catch (error) {
return {
success: false,
error: (error as Error).message
}
}
})

View File

@@ -1,455 +1,93 @@
import { eventHandler } from 'h3'
import { eventHandler, getQuery } from 'h3'
import { getDatabase } from '../../utils/database'
export default eventHandler(() => {
const tracks = [
{
order: 1,
boxId: 'ES00',
side: 'A',
title: 'The grinding wheel',
artist: 0,
start: 0,
link: 'https://arakirecords.bandcamp.com/track/the-grinding-wheel',
coverId: 'a3236746052'
},
{
order: 2,
boxId: 'ES00',
side: 'A',
title: 'Bleach',
artist: 1,
start: 392,
link: 'https://the-kundalini-genie.bandcamp.com/track/bleach-2',
coverId: 'a1714786533'
},
{
order: 3,
boxId: 'ES00',
side: 'A',
title: 'Televised mind',
artist: 2,
start: 896,
link: 'https://fontainesdc.bandcamp.com/track/televised-mind',
coverId: 'a3772806156'
},
{
order: 4,
boxId: 'ES00',
side: 'A',
title: 'In it',
artist: 3,
start: 1139,
link: 'https://howlinbananarecords.bandcamp.com/track/in-it',
coverId: 'a1720372066'
},
{
order: 5,
boxId: 'ES00',
side: 'A',
title: 'Bad michel',
artist: 4,
start: 1245,
link: 'https://johnnymafia.bandcamp.com/track/bad-michel-3',
coverId: 'a0984622869'
},
{
order: 6,
boxId: 'ES00',
side: 'A',
title: 'Overall',
artist: 5,
start: 1394,
link: 'https://newcandys.bandcamp.com/track/overall',
coverId: 'a0559661270'
},
{
order: 7,
boxId: 'ES00',
side: 'A',
title: 'Blowup',
artist: 6,
start: 1674,
link: 'https://magicshoppe.bandcamp.com/track/blowup',
coverId: 'a1444895293'
},
{
order: 8,
boxId: 'ES00',
side: 'A',
title: 'Guitar jet',
artist: 7,
start: 1880,
link: 'https://radiomartiko.bandcamp.com/track/guitare-jet',
coverId: 'a1494681687'
},
{
order: 9,
boxId: 'ES00',
side: 'A',
title: 'Intercontinental radio waves',
artist: 8,
start: 2024,
link: 'https://traams.bandcamp.com/track/intercontinental-radio-waves',
coverId: 'a0046738552'
},
{
order: 10,
boxId: 'ES00',
side: 'A',
title: 'Here comes the sun',
artist: 9,
start: 2211,
link: 'https://blue-orchid.bandcamp.com/track/here-come-the-sun',
coverId: 'a4102567047'
},
{
order: 11,
boxId: 'ES00',
side: 'A',
title: 'Like in the movies',
artist: 10,
start: 2560,
link: 'https://bruitblanc.bandcamp.com/track/like-in-the-movies-2',
coverId: 'a2203158939'
},
{
order: 1,
boxId: 'ES00',
side: 'B',
title: "Ce que révèle l'éclipse",
artist: 0,
start: 0,
link: 'https://arakirecords.bandcamp.com/track/ce-que-r-v-le-l-clipse',
coverId: 'a3236746052'
},
{
order: 2,
boxId: 'ES00',
side: 'B',
title: "Bleedin' Gums Mushrool",
artist: 1,
start: 263,
link: 'https://the-kundalini-genie.bandcamp.com/track/bleedin-gums-mushroom',
coverId: 'a1714786533'
},
{
order: 3,
boxId: 'ES00',
side: 'B',
title: 'A lucid dream',
artist: 2,
start: 554,
link: 'https://fontainesdc.bandcamp.com/track/a-lucid-dream',
coverId: 'a3772806156'
},
{
order: 4,
boxId: 'ES00',
side: 'B',
title: 'Lights off',
artist: 3,
start: 781,
link: 'https://howlinbananarecords.bandcamp.com/track/lights-off',
coverId: 'a1720372066'
},
{
order: 5,
boxId: 'ES00',
side: 'B',
title: "I'm sentimental",
artist: 4,
start: 969,
link: 'https://johnnymafia.bandcamp.com/track/im-sentimental-2',
coverId: 'a2333676849'
},
{
order: 6,
boxId: 'ES00',
side: 'B',
title: 'Thrill or trip',
artist: 5,
start: 1128,
link: 'https://newcandys.bandcamp.com/track/thrill-or-trip',
coverId: 'a0559661270'
},
{
order: 7,
boxId: 'ES00',
side: 'B',
title: 'Redhead',
artist: 6,
start: 1303,
link: 'https://magicshoppe.bandcamp.com/track/redhead',
coverId: 'a0594426943'
},
{
order: 8,
boxId: 'ES00',
side: 'B',
title: 'Supersonic twist',
artist: 7,
start: 1584,
link: 'https://open.spotify.com/track/66voQIZAJ3zD3Eju2qtNjF',
coverId: 'a1494681687'
},
{
order: 9,
boxId: 'ES00',
side: 'B',
title: 'Flowers',
artist: 8,
start: 1749,
link: 'https://traams.bandcamp.com/track/flowers',
coverId: 'a3644668199'
},
{
order: 10,
boxId: 'ES00',
side: 'B',
title: 'The shade',
artist: 9,
start: 1924,
link: 'https://blue-orchid.bandcamp.com/track/the-shade',
coverId: 'a0804204790'
},
{
order: 11,
boxId: 'ES00',
side: 'B',
title: 'Like in the movies',
artist: 10,
start: 2186,
link: 'https://bruitblanc.bandcamp.com/track/like-in-the-movies',
coverId: 'a3647322740'
},
{
order: 1,
boxId: 'ES01',
side: 'A',
title: 'He Walked In',
artist: 11,
start: 0,
link: 'https://kidcongothepinkmonkeybirds.bandcamp.com/track/he-walked-in',
coverId: 'a0336300523'
},
{
order: 2,
boxId: 'ES01',
side: 'A',
title: 'The Third Wave',
artist: 12,
start: 841,
link: 'https://firefriend.bandcamp.com/track/the-third-wave',
coverId: 'a2803689859'
},
{
order: 3,
boxId: 'ES01',
side: 'A',
title: 'Broadcaster',
artist: 13,
start: 1104.5,
link: 'https://squiduk.bandcamp.com/track/broadcaster',
coverId: 'a3391719769'
},
{
order: 4,
boxId: 'ES01',
side: 'A',
title: 'Mourn',
artist: 14,
start: 1441,
link: 'https://lysistrata.bandcamp.com/track/mourn-2',
coverId: 'a0872900041'
},
{
order: 5,
boxId: 'ES01',
side: 'A',
title: 'Let it Blow',
artist: 15,
start: 1844.8,
link: 'https://pabloxbroadcastingservices.bandcamp.com/track/let-it-blow',
coverId: 'a4000148031'
},
{
order: 6,
boxId: 'ES01',
side: 'A',
title: 'Sunday Mourning',
artist: 16,
start: 2091.7,
link: 'https://nightbeats.bandcamp.com/track/sunday-mourning',
coverId: 'a0031987121'
},
{
order: 7,
boxId: 'ES01',
side: 'A',
title: '3030 Instrumental',
artist: 17,
start: 2339.3,
link: 'https://delthefunkyhomosapien.bandcamp.com/track/3030',
coverId: 'a1948146136'
},
{
order: 8,
boxId: 'ES01',
side: 'A',
title: 'Immortality Break',
artist: 18,
start: 2530.5,
link: 'https://theaa.bandcamp.com/track/immortality-break',
coverId: 'a2749250329'
},
{
order: 9,
boxId: 'ES01',
side: 'A',
title: 'Lazy Bones',
artist: 19,
start: 2718,
link: 'https://woodenshjips.bandcamp.com/track/lazy-bones',
coverId: 'a1884221104'
},
{
order: 10,
boxId: 'ES01',
side: 'A',
title: 'On the Train of Aches',
artist: 20,
start: 2948,
link: 'https://silasjdirge.bandcamp.com/track/on-the-train-of-aches',
coverId: 'a1124177379'
},
{
order: 11,
boxId: 'ES01',
side: 'A',
title: 'Me',
artist: 21,
start: 3265,
link: 'https://secretcolours.bandcamp.com/track/me',
coverId: 'a1497022499'
},
{
order: 1,
boxId: 'ES01',
side: 'B',
title: 'Lady Hawke Blues',
artist: 11,
start: 0,
link: 'https://kidcongothepinkmonkeybirds.bandcamp.com/track/lady-hawke-blues',
coverId: 'a2532623230'
},
{
order: 2,
boxId: 'ES01',
side: 'B',
title: 'Dreamscapes',
artist: 12,
start: 235,
link: 'https://littlecloudrecords.bandcamp.com/track/dreamscapes',
coverId: 'a3498981203'
},
{
order: 3,
boxId: 'ES01',
side: 'B',
title: 'Crispy Skin',
artist: 13,
start: 644.2,
link: 'https://squiduk.bandcamp.com/track/crispy-skin-2',
coverId: 'a2516727021'
},
{
order: 4,
boxId: 'ES01',
side: 'B',
title: 'The Boy Who Stood Above The Earth',
artist: 14,
start: 1018,
link: 'https://lysistrata.bandcamp.com/track/the-boy-who-stood-above-the-earth-2',
coverId: 'a0350933426'
},
{
order: 5,
boxId: 'ES01',
side: 'B',
title: 'Better Off Alone',
artist: 15,
start: 1698,
link: 'https://pabloxbroadcastingservices.bandcamp.com/track/better-off-alone',
coverId: 'a4000148031'
},
{
order: 6,
boxId: 'ES01',
side: 'B',
title: 'Celebration #1',
artist: 16,
start: 2235,
link: 'https://nightbeats.bandcamp.com/track/celebration-1',
coverId: 'a0031987121'
},
{
order: 7,
boxId: 'ES01',
side: 'B',
title: '3030 Instrumental',
artist: 17,
start: 2458.3,
link: 'https://delthefunkyhomosapien.bandcamp.com/track/3030',
coverId: 'a1948146136'
},
{
order: 8,
boxId: 'ES01',
side: 'B',
title: 'The Emptiness Of Nothingness',
artist: 18,
start: 2864.5,
link: 'https://theaa.bandcamp.com/track/the-emptiness-of-nothingness',
coverId: 'a1053923875'
},
{
order: 9,
boxId: 'ES01',
side: 'B',
title: 'Rising',
artist: 19,
start: 3145,
link: 'https://woodenshjips.bandcamp.com/track/rising',
coverId: 'a1884221104'
},
{
order: 10,
boxId: 'ES01',
side: 'B',
title: 'The Last Time',
artist: 22,
start: 3447,
link: 'https://www.discogs.com/release/12110815-Larry-McNeil-And-The-Blue-Knights-Jealous-Woman',
coverId:
'https://i.discogs.com/Yr05_neEXwzPwKlDeV7dimmTG34atkAMgpxbMBhHBkI/rs:fit/g:sm/q:90/h:600/w:600/czM6Ly9kaXNjb2dz/LWRhdGFiYXNlLWlt/YWdlcy9SLTEyMTEw/ODE1LTE1Mjg1NjU1/NzQtMjcyOC5qcGVn.jpeg'
},
{
order: 11,
boxId: 'ES01',
side: 'B',
title: 'Guajira Con Arpa',
artist: 23,
start: 3586,
link: 'https://elpalmasmusic.bandcamp.com/track/guajira-con-arpa',
coverId: 'a3463036407'
export default eventHandler((event) => {
const db = getDatabase()
const query = getQuery(event)
// Paramètres de pagination
const page = parseInt(query.page as string) || 1
const limit = parseInt(query.limit as string) || 50
const offset = (page - 1) * limit
// Filtres optionnels
const boxId = query.boxId as string | undefined
const side = query.side as string | undefined
// Construction de la requête
let sql = `
SELECT
t.id,
t.box_id,
t.side,
t.track_order,
t.title,
t.artist_id,
t.start,
t.link,
t.cover_id,
t.url,
t.type,
a.name as artist_name
FROM tracks t
LEFT JOIN artists a ON t.artist_id = a.id
WHERE t.type = 'compilation'
`
const params: any[] = []
if (boxId) {
sql += ' AND t.box_id = ?'
params.push(boxId)
}
if (side) {
sql += ' AND t.side = ?'
params.push(side)
}
sql += ' ORDER BY t.box_id, t.side, t.track_order'
sql += ' LIMIT ? OFFSET ?'
params.push(limit, offset)
const tracks = db.prepare(sql).all(...params)
// Compter le total pour la pagination
let countSql = "SELECT COUNT(*) as total FROM tracks WHERE type = 'compilation'"
const countParams: any[] = []
if (boxId) {
countSql += ' AND box_id = ?'
countParams.push(boxId)
}
if (side) {
countSql += ' AND side = ?'
countParams.push(side)
}
const { total } = db.prepare(countSql).get(...countParams) as { total: number }
return {
tracks: tracks.map((track: any) => ({
id: track.id,
boxId: track.box_id,
side: track.side,
order: track.track_order,
title: track.title,
artist: track.artist_id,
artistName: track.artist_name,
start: track.start,
link: track.link,
coverId: track.cover_id,
url: track.url,
type: track.type
})),
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit)
}
]
return tracks.map((track, index) => ({
id: index + 1,
...track,
url: `https://files.erudi.fr/evilspins/${track.boxId}${track.side}.mp3`,
coverId: `https://f4.bcbits.com/img/${track.coverId}_4.jpg`,
type: 'compilation'
}))
}
})

View File

@@ -10,8 +10,25 @@ export default eventHandler(async (event) => {
try {
let allTracks: any[] = []
let files = await fs.promises.readdir(dirPath)
files = files.filter((f) => !f.startsWith('.') && !f.endsWith('.jpg'))
const items = await fs.promises.readdir(dirPath, { withFileTypes: true })
// Process files
const files = items
.filter((item) => item.isFile() && !item.name.startsWith('.') && !item.name.endsWith('.jpg'))
.map((item) => item.name)
// Process folders
const folders = items
.filter((item) => item.isDirectory() && !item.name.startsWith('.'))
.map((folder, index) => ({
id: `folder-${index}`,
boxId: 'ESFOLDER',
title: folder.name.replace(/_/g, ' ').trim(),
type: 'folder',
order: 0,
date: new Date(),
card: getCardFromDate(new Date())
}))
const tracks = files.map((file, index) => {
const EXT_RE = /\.(mp3|flac|wav|opus)$/i
@@ -59,11 +76,14 @@ export default eventHandler(async (event) => {
})
tracks.sort((a, b) => b.date.getTime() - a.date.getTime())
// assign a stable order after sort (1..N)
tracks.forEach((t, i) => (t.order = i + 1))
allTracks.push(...tracks)
// Combine folders and tracks
const allItems = [...folders, ...tracks]
return allTracks
// Sort by date (newest first) and assign order
allItems.sort((a, b) => b.date.getTime() - a.date.getTime())
allItems.forEach((item, i) => (item.order = i + 1))
return allItems
} catch (error) {
return {
success: false,

View File

@@ -0,0 +1,91 @@
import { eventHandler, getQuery } from 'h3'
import { getDatabase } from '../../utils/database'
export default eventHandler((event) => {
const db = getDatabase()
const query = getQuery(event)
const search = (query.search as string) || ''
const page = parseInt(query.page as string) || 1
const limit = parseInt(query.limit as string) || 50
const offset = (page - 1) * limit
// Construction de la requête de recherche
let sql = `
SELECT
t.id,
t.box_id,
t.side,
t.track_order,
t.title,
t.artist_id,
t.start,
t.link,
t.cover_id,
t.url,
t.type,
a.name as artist_name,
a.url as artist_url
FROM tracks t
LEFT JOIN artists a ON t.artist_id = a.id
WHERE 1=1
`
const params: any[] = []
// Recherche par titre ou artiste
if (search) {
sql += ` AND (t.title LIKE ? OR a.name LIKE ?)`
const searchPattern = `%${search}%`
params.push(searchPattern, searchPattern)
}
sql += ' ORDER BY t.box_id, t.track_order'
sql += ' LIMIT ? OFFSET ?'
params.push(limit, offset)
const tracks = db.prepare(sql).all(...params)
// Compter le total
let countSql = `
SELECT COUNT(*) as total
FROM tracks t
LEFT JOIN artists a ON t.artist_id = a.id
WHERE 1=1
`
const countParams: any[] = []
if (search) {
countSql += ` AND (t.title LIKE ? OR a.name LIKE ?)`
const searchPattern = `%${search}%`
countParams.push(searchPattern, searchPattern)
}
const { total } = db.prepare(countSql).get(...countParams) as { total: number }
return {
tracks: tracks.map((track: any) => ({
id: track.id,
boxId: track.box_id,
side: track.side,
order: track.track_order,
title: track.title,
artist: track.artist_id,
artistName: track.artist_name,
artistUrl: track.artist_url,
start: track.start,
link: track.link,
coverId: track.cover_id,
url: track.url,
type: track.type
})),
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit)
},
search
}
})