96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
import Fuse from "fuse.js"
|
|
import { Album } from "../models/album"
|
|
import { Artist } from "../models/artist"
|
|
import { Track } from "../models/track"
|
|
import {
|
|
parseNumber,
|
|
parseQueryString,
|
|
validationResponse,
|
|
} from "../utils/api-validation"
|
|
import { requireAuthenticatedUser } from "../utils/auth"
|
|
|
|
const searchTypes = ["track", "album", "artist", "default"] as const
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
await requireAuthenticatedUser(event)
|
|
|
|
const query = getQuery(event)
|
|
const q = parseQueryString(query.q)
|
|
if ("error" in q) return validationResponse(event, q.error)
|
|
|
|
let type = "default"
|
|
if (query.type !== undefined) {
|
|
const parsedType = parseQueryString(query.type)
|
|
if ("error" in parsedType) return validationResponse(event, parsedType.error)
|
|
if (!searchTypes.includes(parsedType.value as (typeof searchTypes)[number])) {
|
|
return validationResponse(
|
|
event,
|
|
`Invalid enum value. Expected 'track' | 'album' | 'artist' | 'default', received '${parsedType.value}'`,
|
|
)
|
|
}
|
|
type = parsedType.value
|
|
}
|
|
|
|
const parsedLimit = parseNumber(query.limit, 9, 1)
|
|
if ("error" in parsedLimit) return validationResponse(event, parsedLimit.error)
|
|
const limit = parsedLimit.value
|
|
|
|
let results: { tracks: any[]; albums: any[]; artists: any[] } = {
|
|
tracks: [],
|
|
albums: [],
|
|
artists: [],
|
|
}
|
|
|
|
if (type === "track" || type === "default") {
|
|
const rawTracks = await Track.find({})
|
|
.populate(["album", "artists"])
|
|
.lean()
|
|
|
|
const fuse = new Fuse(rawTracks, {
|
|
keys: ["name", "lyrics.text"],
|
|
threshold: 0.3,
|
|
distance: 100,
|
|
ignoreLocation: true,
|
|
})
|
|
|
|
results.tracks = fuse.search(q.value).slice(0, limit).map((result) => result.item)
|
|
}
|
|
|
|
if (type === "album" || type === "default") {
|
|
const rawAlbums = await Album.find({})
|
|
.populate(["artists", "genre"])
|
|
.lean()
|
|
|
|
const fuse = new Fuse(rawAlbums, {
|
|
keys: ["name"],
|
|
threshold: 0.3,
|
|
distance: 100,
|
|
ignoreLocation: true,
|
|
})
|
|
|
|
results.albums = fuse.search(q.value).slice(0, limit).map((result) => result.item)
|
|
}
|
|
|
|
if (type === "artist" || type === "default") {
|
|
const rawArtists = await Artist.find({}).lean()
|
|
|
|
const fuse = new Fuse(rawArtists, {
|
|
keys: ["name"],
|
|
threshold: 0.3,
|
|
distance: 100,
|
|
ignoreLocation: true,
|
|
})
|
|
|
|
results.artists = fuse.search(q.value).slice(0, limit).map((result) => result.item)
|
|
}
|
|
|
|
// Keep the original API's split allocation, including when type selects one category.
|
|
results = {
|
|
tracks: results.tracks.slice(0, Math.ceil(limit / 3)),
|
|
albums: results.albums.slice(0, Math.floor(limit / 3)),
|
|
artists: results.artists.slice(0, Math.floor(limit / 3)),
|
|
}
|
|
|
|
return results
|
|
})
|