186 lines
5.3 KiB
TypeScript
186 lines
5.3 KiB
TypeScript
import type {
|
|
SearchResponse,
|
|
AlbumResponse,
|
|
ArtistResponse,
|
|
StreamResponse,
|
|
PlayerTrack,
|
|
Album,
|
|
AlbumDetails,
|
|
} from "./types"
|
|
|
|
let apiBaseUrl = "https://qqdl.site/api"
|
|
|
|
export const setApiBaseUrl = (url: string) => {
|
|
apiBaseUrl = url
|
|
console.log("[v0] API base URL set to:", url)
|
|
}
|
|
|
|
export const getApiBaseUrl = () => apiBaseUrl
|
|
|
|
export const searchMusic = async (query: string): Promise<Album[]> => {
|
|
const url = `${apiBaseUrl}/get-music?q=${encodeURIComponent(query)}&offset=0&type=track`
|
|
console.log("[v0] Searching music:", url)
|
|
|
|
try {
|
|
const response = await fetch(url)
|
|
|
|
if (!response.ok) {
|
|
console.error("[v0] Search failed with status:", response.status, response.statusText)
|
|
throw new Error(`Failed to search music: ${response.status}`)
|
|
}
|
|
|
|
const data: SearchResponse = await response.json()
|
|
console.log("[v0] Search response success:", data.success, "Albums found:", data.data?.albums?.items?.length || 0)
|
|
|
|
if (!data.success) {
|
|
throw new Error("Search failed")
|
|
}
|
|
|
|
return data.data.albums.items
|
|
} catch (error) {
|
|
console.error("[v0] Search error:", error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export const getAlbumDetails = async (albumId: string): Promise<AlbumDetails> => {
|
|
const url = `${apiBaseUrl}/get-album?album_id=${albumId}`
|
|
console.log("[v0] Fetching album:", url)
|
|
|
|
try {
|
|
const response = await fetch(url)
|
|
|
|
if (!response.ok) {
|
|
console.error("[v0] Album fetch failed with status:", response.status, response.statusText)
|
|
throw new Error(`Failed to fetch album details: ${response.status}`)
|
|
}
|
|
|
|
const data: AlbumResponse = await response.json()
|
|
|
|
if (!data.success) {
|
|
throw new Error("Failed to load album")
|
|
}
|
|
|
|
console.log("[v0] Album loaded:", data.data.title, "Tracks:", data.data.tracks.items.length)
|
|
return data.data
|
|
} catch (error) {
|
|
console.error("[v0] Album fetch error:", error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export const getArtistDetails = async (artistId: number) => {
|
|
const url = `${apiBaseUrl}/get-artist?artist_id=${artistId}`
|
|
console.log("[v0] Fetching artist:", url)
|
|
|
|
try {
|
|
const response = await fetch(url)
|
|
|
|
if (!response.ok) {
|
|
console.error("[v0] Artist fetch failed with status:", response.status, response.statusText)
|
|
throw new Error(`Failed to fetch artist details: ${response.status}`)
|
|
}
|
|
|
|
const data: ArtistResponse = await response.json()
|
|
|
|
if (!data.success) {
|
|
throw new Error("Failed to load artist")
|
|
}
|
|
|
|
console.log("[v0] Artist loaded:", data.data.artist.name.display)
|
|
return data.data.artist
|
|
} catch (error) {
|
|
console.error("[v0] Artist fetch error:", error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export const getTrackStreamUrl = async (trackId: string): Promise<string> => {
|
|
const url = `${apiBaseUrl}/download-music?track_id=${trackId}`
|
|
console.log("[v0] Fetching stream URL for track:", trackId, "from:", url)
|
|
|
|
try {
|
|
const response = await fetch(url)
|
|
|
|
console.log("[v0] Stream URL fetch response:", {
|
|
status: response.status,
|
|
statusText: response.statusText,
|
|
headers: {
|
|
contentType: response.headers.get("content-type"),
|
|
contentLength: response.headers.get("content-length"),
|
|
accessControlAllowOrigin: response.headers.get("access-control-allow-origin"),
|
|
},
|
|
})
|
|
|
|
if (!response.ok) {
|
|
console.error("[v0] Stream URL fetch failed with status:", response.status, response.statusText)
|
|
throw new Error(`Failed to fetch stream URL: ${response.status}`)
|
|
}
|
|
|
|
const data: StreamResponse = await response.json()
|
|
console.log("[v0] Stream URL response:", {
|
|
success: data.success,
|
|
hasUrl: !!data.data?.url,
|
|
urlLength: data.data?.url?.length,
|
|
urlStart: data.data?.url?.substring(0, 50),
|
|
})
|
|
|
|
if (!data.success || !data.data?.url) {
|
|
throw new Error("Failed to get stream URL - no URL in response")
|
|
}
|
|
|
|
try {
|
|
const testResponse = await fetch(data.data.url, { method: "HEAD" })
|
|
console.log("[v0] Stream URL accessibility test:", {
|
|
status: testResponse.status,
|
|
statusText: testResponse.statusText,
|
|
contentType: testResponse.headers.get("content-type"),
|
|
cors: testResponse.headers.get("access-control-allow-origin"),
|
|
})
|
|
} catch (testError) {
|
|
console.error("[v0] Stream URL accessibility test failed:", testError)
|
|
}
|
|
|
|
console.log("[v0] Stream URL obtained successfully")
|
|
return data.data.url
|
|
} catch (error) {
|
|
console.error("[v0] Stream URL fetch error:", error)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export const albumToPlayerTracks = (album: AlbumDetails): PlayerTrack[] => {
|
|
return album.tracks.items.map((track) => ({
|
|
id: track.id.toString(),
|
|
title: track.title,
|
|
artistName: album.artist.name,
|
|
albumTitle: album.title,
|
|
artworkUrl: album.image.large,
|
|
duration: track.duration,
|
|
}))
|
|
}
|
|
|
|
export const topTracksToPlayerTracks = (
|
|
topTracks: Array<{
|
|
id: number
|
|
title: string
|
|
duration: number
|
|
album: {
|
|
id: string
|
|
title: string
|
|
image: {
|
|
small: string
|
|
}
|
|
}
|
|
}>,
|
|
artistName: string,
|
|
): PlayerTrack[] => {
|
|
return topTracks.map((track) => ({
|
|
id: track.id.toString(),
|
|
title: track.title,
|
|
artistName,
|
|
albumTitle: track.album.title,
|
|
artworkUrl: track.album.image.small,
|
|
duration: track.duration,
|
|
}))
|
|
}
|