eS v1
All checks were successful
Deploy App / build (push) Successful in 2m14s
Deploy App / deploy (push) Successful in 14s

This commit is contained in:
valere
2025-10-16 00:42:38 +02:00
parent ce73155cfa
commit 3ad8cb8795
21 changed files with 752 additions and 332 deletions

View File

@@ -1,14 +1,14 @@
import type { Compilation, Artist, Track } from '~/../types/types'
import type { Box, Artist, Track } from '~/../types/types'
// stores/data.ts
import { defineStore } from 'pinia'
import { useUiStore } from '~/store/ui'
export const useDataStore = defineStore('data', {
state: () => ({
compilations: [] as Compilation[], // Store your compilation data here
boxes: [] as Box[], // Store your box data here
artists: [] as Artist[], // Store artist data here
tracks: [] as Track[], // Store track data here
playlistTracks: [] as Track[], // store playslit tracks
isLoaded: false // Remember if data is already loaded
}),
@@ -16,75 +16,76 @@ export const useDataStore = defineStore('data', {
async loadData() {
if (this.isLoaded) return
const { data: compilations } = await useFetch<Compilation[]>('/api/compilations')
const { data: artists } = await useFetch<Artist[]>('/api/artists')
const { data: rawTracks } = await useFetch<Track[]>('/api/tracks')
const { data: playlistTracks } = await useFetch<Track[]>('/api/playlists')
// Stocker les données de base
this.compilations = compilations.value ?? []
this.artists = artists.value ?? []
this.boxes = await $fetch<Box[]>('/api/boxes')
this.artists = await $fetch<Artist[]>('/api/artists')
const compilationTracks = await $fetch<Track[]>('/api/tracks/compilation')
const playlistTracks = await $fetch<Track[]>('/api/tracks/playlist')
// Mapper les tracks pour remplacer l'artistId par l'objet Artist
// Mapper les tracks pour remplacer l'artist avec un objet Artist cohérent
const artistMap = new Map(this.artists.map((a) => [a.id, a]))
this.tracks = (rawTracks.value ?? []).map((track) => ({
...track,
artist: artistMap.get(track.artist) ?? {
id: track.artist,
name: 'Unknown',
url: '',
coverId: ''
}
}))
const allTracks = [...(compilationTracks ?? []), ...(playlistTracks ?? [])]
this.playlistTracks = (playlistTracks.value ?? []).map((track) => ({
...track,
artist: artistMap.get(track.artist) ?? {
id: track.artist,
name: track.artist,
url: '',
coverId: ''
this.tracks = allTracks.map((track) => {
const a = track.artist as unknown
let artistObj: Artist
if (typeof a === 'number') {
artistObj = artistMap.get(a) ?? { id: a, name: String(a), url: '', coverId: '' }
} else if (typeof a === 'string') {
artistObj = { id: 0, name: a, url: '', coverId: '' }
} else if (a && typeof a === 'object' && 'id' in (a as any)) {
const idVal = (a as any).id as number | undefined
artistObj = idVal != null ? artistMap.get(idVal) ?? (a as Artist) : (a as Artist)
} else {
artistObj = { id: 0, name: '', url: '', coverId: '' }
}
}))
return {
...track,
artist: artistObj
}
})
this.isLoaded = true
const uiStore = useUiStore()
uiStore.closeBox()
}
},
getters: {
// Obtenir tous les compilations
getAllCompilations: (state) => state.compilations,
getCompilationById: (state) => {
// Obtenir toutes les boxes
getBoxById: (state) => {
return (id: string) => {
return state.compilations.find((compilation) => compilation.id === id)
return state.boxes.find((box) => box.id === id)
}
},
// Obtenir toutes les pistes d'une compilation donnée
getTracksByCompilationId: (state) => (id: string) => {
return state.tracks.filter((track) => track.compilationId === id)
// Obtenir toutes les pistes d'une box donnée
getTracksByboxId: (state) => (id: string) => {
return state.tracks.filter((track) => track.boxId === id)
},
// Filtrer les artistes selon certains critères
getArtistById: (state) => (id: number) => state.artists.find((artist) => artist.id === id),
// Obtenir toutes les pistes d'un artiste donné
getTracksByArtistId: (state) => (artistId: number) => {
return state.tracks.filter((track) => track.artist.id === artistId)
return state.tracks.filter(
(track) =>
typeof track.artist === 'object' &&
!!track.artist &&
'id' in track.artist &&
(track.artist as Artist).id === artistId
)
},
getFirstTrackOfCompilation() {
return (compilationId: string) => {
const tracks = this.getTracksByCompilationId(compilationId)
return tracks.length > 0 ? tracks[0] : null
}
},
getFirstTrackOfPlaylist() {
return (compilationId: string) => {
const tracks = this.getPlaylistTracksByCompilationId(compilationId)
getFirstTrackOfBox() {
return (box: Box) => {
const tracks = this.getTracksByboxId(box.id)
.slice()
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
return tracks.length > 0 ? tracks[0] : null
}
},
getNextPlaylistTrack: (state) => {
return (track: Track) => {
const tracksInPlaylist = state.playlistTracks
.filter((t) => t.compilationId === track.compilationId)
const tracksInPlaylist = state.tracks
.filter((t) => t.boxId === track.boxId)
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
const index = tracksInPlaylist.findIndex((t) => t.id === track.id)
@@ -95,31 +96,26 @@ export const useDataStore = defineStore('data', {
},
getNextTrack: (state) => {
return (track: Track) => {
// Récupérer toutes les tracks de la même compilation et les trier par ordre
const tracksInCompilation = state.tracks
.filter((t) => t.compilationId === track.compilationId)
// Récupérer toutes les tracks de la même box et les trier par ordre
const tracksInBox = state.tracks
.filter((t) => t.boxId === track.boxId)
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
// Trouver lindex de la track courante
const index = tracksInCompilation.findIndex((t) => t.id === track.id)
const index = tracksInBox.findIndex((t) => t.id === track.id)
// Retourner la track suivante ou null si cest la dernière
return index >= 0 && index < tracksInCompilation.length - 1
? tracksInCompilation[index + 1]
: null
return index >= 0 && index < tracksInBox.length - 1 ? tracksInBox[index + 1] : null
}
},
getPrevTrack: (state) => {
return (track: Track) => {
const tracksInCompilation = state.tracks
.filter((t) => t.compilationId === track.compilationId)
const tracksInBox = state.tracks
.filter((t) => t.boxId === track.boxId)
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
const index = tracksInCompilation.findIndex((t) => t.id === track.id)
return index > 0 ? tracksInCompilation[index - 1] : null
const index = tracksInBox.findIndex((t) => t.id === track.id)
return index > 0 ? tracksInBox[index - 1] : null
}
},
getPlaylistTracksByCompilationId: (state) => (id: string) => {
return state.playlistTracks.filter((track) => track.compilationId === id)
}
}
})

View File

@@ -1,6 +1,6 @@
// ~/store/player.ts
import { defineStore } from 'pinia'
import type { Track } from '~/../types/types'
import type { Track, Box } from '~/../types/types'
import { useDataStore } from '~/store/data'
export const usePlayerStore = defineStore('player', {
@@ -30,22 +30,23 @@ export const usePlayerStore = defineStore('player', {
const track = this.currentTrack
if (!track) return
const dataStore = useDataStore()
if (track.compilationId.length === 6) {
if (track.type === 'playlist') {
const next = dataStore.getNextPlaylistTrack(track)
if (next && next.compilationId === track.compilationId) {
if (next && next.boxId === track.boxId) {
this.playTrack(next)
}
} else {
console.log('ended')
this.currentTrack = null
}
})
},
async playCompilation(compilationId: string) {
if (this.currentTrack?.compilationId === compilationId) {
async playBox(box: Box) {
if (this.currentTrack?.boxId === box.id) {
this.togglePlay()
} else {
const dataStore = useDataStore()
const first = (compilationId.length === 6) ? dataStore.getFirstTrackOfPlaylist(compilationId) : dataStore.getFirstTrackOfCompilation(compilationId)
const first = dataStore.getFirstTrackOfBox(box)
if (first) {
await this.playTrack(first)
}
@@ -53,7 +54,7 @@ export const usePlayerStore = defineStore('player', {
},
async playTrack(track: Track) {
// mettre à jour la piste courante uniquement après avoir géré le toggle
if (this.currentTrack?.id === track.id) {
if (this.currentTrack && this.currentTrack?.id === track.id) {
this.togglePlay()
} else {
this.currentTrack = track
@@ -83,10 +84,18 @@ export const usePlayerStore = defineStore('player', {
const onLoaded = () => resolve()
audio.addEventListener('loadedmetadata', onLoaded, { once: true })
})
// Appliquer le temps de départ
audio.currentTime = wantedStart > 0 ? wantedStart : 0
await new Promise<void>((resolve) => {
const onCanPlay = () => {
if (wantedStart > 0 && audio.currentTime < wantedStart - 0.05) {
audio.currentTime = wantedStart
}
resolve()
}
if (audio.readyState >= 3) return resolve()
audio.addEventListener('canplay', onCanPlay, { once: true })
})
this.isPaused = false
await audio.play()
} catch (err: any) {
@@ -133,61 +142,37 @@ export const usePlayerStore = defineStore('player', {
if (!track) return
const dataStore = useDataStore()
const t = audio.currentTime
// For playlists (id length 6), do NOT forward auto-advance here; rely on 'ended'
if (track.compilationId.length === 6) {
const from = track.start ?? 0
const prevTrack = dataStore.getPrevTrack(track)
if (t < from + 0.05) {
if (prevTrack && prevTrack.compilationId === track.compilationId) {
this.currentTrack = prevTrack
}
}
return
}
// For compilations, forward auto-advance at segment boundary
const nextTrack = dataStore.getNextTrack(track)
const to = (track.order === 0) ? Math.round(this.audio?.duration ?? 0) : nextTrack?.start
if (!to || isNaN(to)) return
if (t >= to - 0.05) {
if (nextTrack && nextTrack.compilationId === track.compilationId) {
this.currentTrack = nextTrack
}
} else {
const from = track.start ?? 0
const prevTrack = dataStore.getPrevTrack(track)
if (t < from + 0.05) {
if (prevTrack && prevTrack.compilationId === track.compilationId) {
this.currentTrack = prevTrack
}
}
}
}
},
getters: {
isCurrentCompilation: (state) => {
return (compilationId: string) => compilationId === state.currentTrack?.compilationId
isCurrentBox: (state) => {
return (boxId: string) => boxId === state.currentTrack?.boxId
},
isPlaylistTrack: () => {
return (track: Track) => {
return track.compilationId.length === 6
return track.type === 'playlist'
}
},
getCurrentTrack: (state) => state.currentTrack,
getCurrentCompilation: (state) => {
getCurrentBox: (state) => {
return state.currentTrack ? state.currentTrack.url : null
},
currentProgression(state) {
getCurrentProgression(state) {
if (!state.audio) return 0
const duration = state.audio.duration
const progression = (state.position / duration) * 100
return isNaN(progression) ? state.progressionLast : progression
},
getCurrentCoverUrl(state) {
const id = state.currentTrack?.coverId
if (!id) return null
return id.startsWith('http') ? id : `https://f4.bcbits.com/img/${id}_4.jpg`
}
}
})

46
app/store/ui.ts Normal file
View File

@@ -0,0 +1,46 @@
import { defineStore } from 'pinia'
import { useDataStore } from '~/store/data'
import type { Box } from '~/../types/types'
export const useUiStore = defineStore('ui', {
state: () => ({
// UI-only state can live here later
}),
actions: {
selectBox(id: string) {
const dataStore = useDataStore()
dataStore.boxes.forEach((box) => {
box.state = box.id === id ? 'box-selected' : 'box-hidden'
})
},
closeBox() {
const selectedBox = this.getSelectedBox
const dataStore = useDataStore()
dataStore.boxes.forEach((box) => {
box.state = 'box-list'
})
// Scroll back to the unselected box in the list
if (selectedBox) this.scrollToBox(selectedBox)
},
scrollToBox(box: Box) {
if (box) {
const boxElement = document.getElementById(box.id)
if (boxElement) {
setTimeout(() => {
boxElement.scrollIntoView({ behavior: 'smooth' })
}, 333)
}
}
}
},
getters: {
getSelectedBox: () => {
const dataStore = useDataStore()
return (dataStore.boxes as Box[]).find((box) => box.state === 'box-selected') || null
}
}
})