jefflix-website/app/api/music/offline/route.ts

122 lines
3.5 KiB
TypeScript

import { NextResponse } from 'next/server'
import { navidromeGet } from '@/lib/navidrome'
const OFFLINE_PLAYLIST_NAME = '__soulsync_offline__'
interface SubsonicPlaylist {
id: string
name: string
songCount: number
coverArt: string
}
interface SubsonicSong {
id: string
title: string
artist: string
album: string
albumId: string
duration: number
coverArt: string
}
interface PlaylistsResult {
playlists?: { playlist?: SubsonicPlaylist[] }
}
interface PlaylistResult {
playlist?: {
id: string
name: string
songCount: number
entry?: SubsonicSong[]
}
}
/** Find or create the offline sync playlist, returning its id + songs */
async function getOrCreateOfflinePlaylist() {
// Find existing
const data = await navidromeGet<PlaylistsResult>('getPlaylists.view')
const existing = (data.playlists?.playlist || []).find(
(p) => p.name === OFFLINE_PLAYLIST_NAME
)
if (existing) {
// Fetch full playlist with entries
const full = await navidromeGet<PlaylistResult>('getPlaylist.view', { id: existing.id })
const songs = (full.playlist?.entry || []).map((s) => ({
id: s.id,
title: s.title,
artist: s.artist,
album: s.album,
albumId: s.albumId,
duration: s.duration,
coverArt: s.coverArt,
}))
return { id: existing.id, songs }
}
// Create it
await navidromeGet('createPlaylist.view', { name: OFFLINE_PLAYLIST_NAME })
// Re-fetch to get its id
const data2 = await navidromeGet<PlaylistsResult>('getPlaylists.view')
const created = (data2.playlists?.playlist || []).find(
(p) => p.name === OFFLINE_PLAYLIST_NAME
)
return { id: created?.id || '', songs: [] }
}
/** GET: return offline playlist id + songs */
export async function GET() {
try {
const result = await getOrCreateOfflinePlaylist()
return NextResponse.json(result)
} catch (error) {
console.error('Offline playlist error:', error)
return NextResponse.json({ error: 'Failed to get offline playlist' }, { status: 502 })
}
}
/** POST: add a song to the offline playlist */
export async function POST(request: Request) {
try {
const { songId } = await request.json()
if (!songId) {
return NextResponse.json({ error: 'songId required' }, { status: 400 })
}
const { id: playlistId } = await getOrCreateOfflinePlaylist()
await navidromeGet('updatePlaylist.view', {
playlistId,
songIdToAdd: songId,
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Add to offline playlist error:', error)
return NextResponse.json({ error: 'Failed to add song' }, { status: 502 })
}
}
/** DELETE: remove a song from the offline playlist by songId */
export async function DELETE(request: Request) {
try {
const { songId } = await request.json()
if (!songId) {
return NextResponse.json({ error: 'songId required' }, { status: 400 })
}
const { id: playlistId, songs } = await getOrCreateOfflinePlaylist()
// Subsonic removeFromPlaylist uses songIndexToRemove (0-based index)
const index = songs.findIndex((s) => s.id === songId)
if (index === -1) {
return NextResponse.json({ error: 'Song not in offline playlist' }, { status: 404 })
}
await navidromeGet('updatePlaylist.view', {
playlistId,
songIndexToRemove: String(index),
})
return NextResponse.json({ success: true })
} catch (error) {
console.error('Remove from offline playlist error:', error)
return NextResponse.json({ error: 'Failed to remove song' }, { status: 502 })
}
}