From 9c9fa4c18b6e026c4d2f871be31171235e917843 Mon Sep 17 00:00:00 2001 From: SrGooglo Date: Wed, 5 Feb 2025 02:51:35 +0000 Subject: [PATCH] use new items list standart --- .../services/music/classes/release/index.js | 3 +- .../music/classes/track/methods/get.js | 136 +++++++++------- .../classes/track/methods/isFavourite.js | 2 +- .../classes/track/methods/toggleFavourite.js | 2 - .../services/music/routes/music/feed/get.js | 32 ++-- .../music/routes/music/feed/my/get.js | 24 +-- .../routes/music/lyrics/[track_id]/put.js | 10 +- .../music/routes/music/my/folder/get.js | 146 ++++++++++-------- .../music/routes/music/recently/get.js | 14 ++ .../music/releases/[release_id]/data/get.js | 46 +++--- .../music/tracks/[track_id]/override/get.js | 17 ++ .../music/tracks/[track_id]/override/put.js | 36 +++++ .../services/music/routes/music/tracks/put.js | 4 +- 13 files changed, 295 insertions(+), 177 deletions(-) create mode 100644 packages/server/services/music/routes/music/tracks/[track_id]/override/get.js create mode 100644 packages/server/services/music/routes/music/tracks/[track_id]/override/put.js diff --git a/packages/server/services/music/classes/release/index.js b/packages/server/services/music/classes/release/index.js index c7fead1f..d1c10676 100644 --- a/packages/server/services/music/classes/release/index.js +++ b/packages/server/services/music/classes/release/index.js @@ -12,6 +12,7 @@ const AllowedUpdateFields = [ export default class Release { static async create(payload) { + console.log(payload) if (!payload.title) { throw new OperationError(400, "Release title is required") } @@ -21,7 +22,7 @@ export default class Release { } // ensure list is an array of strings with tracks ids only - playload.list = playload.list.map((item) => { + payload.list = payload.list.map((item) => { if (typeof item !== "string") { item = item._id } diff --git a/packages/server/services/music/classes/track/methods/get.js b/packages/server/services/music/classes/track/methods/get.js index 9fd4aa92..751976bc 100644 --- a/packages/server/services/music/classes/track/methods/get.js +++ b/packages/server/services/music/classes/track/methods/get.js @@ -1,66 +1,96 @@ import { Track, TrackLike } from "@db_models" +async function fullfillData(list, { user_id = null }) { + if (!Array.isArray(list)) { + list = [list] + } + + const trackIds = list.map((track) => { + return track._id + }) + + // if user_id is provided, fetch likes + if (user_id) { + const tracksLikes = await TrackLike.find({ + user_id: user_id, + track_id: { $in: trackIds }, + }) + + list = list.map(async (track) => { + const trackLike = tracksLikes.find((trackLike) => { + return trackLike.track_id.toString() === track._id.toString() + }) + + if (trackLike) { + track.liked_at = trackLike.created_at + track.liked = true + } + + return track + }) + + list = await Promise.all(list) + } + + // process some metadata + list = list.map(async (track) => { + if (track.metadata) { + if (track.metadata.bitrate && track.metadata.bitrate > 9000) { + track.metadata.lossless = true + } + } + + return track + }) + + list = await Promise.all(list) + + return list +} + export default async (track_id, { user_id = null, onlyList = false } = {}) => { - if (!track_id) { - throw new OperationError(400, "Missing track_id") - } + if (!track_id) { + throw new OperationError(400, "Missing track_id") + } - const isMultiple = Array.isArray(track_id) || track_id.includes(",") + const isMultiple = Array.isArray(track_id) || track_id.includes(",") - if (isMultiple) { - const track_ids = Array.isArray(track_id) ? track_id : track_id.split(",") + if (isMultiple) { + const track_ids = Array.isArray(track_id) + ? track_id + : track_id.split(",") - const tracks = await Track.find({ - _id: { $in: track_ids } - }).lean() + let tracks = await Track.find({ + _id: { $in: track_ids }, + }).lean() - if (user_id) { - const trackLikes = await TrackLike.find({ - user_id: user_id, - track_id: { $in: track_ids } - }) + tracks = await fullfillData(tracks, { + user_id, + }) - // FIXME: this could be a performance issue when there are a lot of likes - // Array.find may not be a good idea - for (const trackLike of trackLikes) { - const track = tracks.find(track => track._id.toString() === trackLike.track_id.toString()) + if (onlyList) { + return tracks + } - if (track) { - track.liked_at = trackLike.created_at - track.liked = true - } - } - } + return { + total_count: await Track.countDocuments({ + _id: { $in: track_ids }, + }), + list: tracks, + } + } - if (onlyList) { - return tracks - } + let track = await Track.findOne({ + _id: track_id, + }).lean() - return { - total_count: await Track.countDocuments({ _id: { $in: track_ids } }), - list: tracks, - } - } + if (!track) { + throw new OperationError(404, "Track not found") + } - const track = await Track.findOne({ - _id: track_id - }).lean() + track = await fullfillData(track, { + user_id, + }) - if (!track) { - throw new OperationError(404, "Track not found") - } - - if (user_id) { - const trackLike = await TrackLike.findOne({ - user_id: user_id, - track_id: track_id, - }) - - if (trackLike) { - track.liked_at = trackLike.created_at - track.liked = true - } - } - - return track -} \ No newline at end of file + return track[0] +} diff --git a/packages/server/services/music/classes/track/methods/isFavourite.js b/packages/server/services/music/classes/track/methods/isFavourite.js index 6d4b1ffa..15620b96 100644 --- a/packages/server/services/music/classes/track/methods/isFavourite.js +++ b/packages/server/services/music/classes/track/methods/isFavourite.js @@ -9,7 +9,7 @@ export default async (user_id, track_id, to) => { throw new OperationError(400, "Missing track_id") } - const track = await Track.findById(track_id) + const track = await Track.findById(track_id).catch(() => null) if (!track) { throw new OperationError(404, "Track not found") diff --git a/packages/server/services/music/classes/track/methods/toggleFavourite.js b/packages/server/services/music/classes/track/methods/toggleFavourite.js index 6b4d24bf..3f60fff9 100644 --- a/packages/server/services/music/classes/track/methods/toggleFavourite.js +++ b/packages/server/services/music/classes/track/methods/toggleFavourite.js @@ -45,8 +45,6 @@ export default async (user_id, track_id, to) => { } } - console.log(global.websocket.find) - const targetSocket = await global.websocket.find.socketByUserId(user_id) if (targetSocket) { diff --git a/packages/server/services/music/routes/music/feed/get.js b/packages/server/services/music/routes/music/feed/get.js index 8916bba0..c7214737 100644 --- a/packages/server/services/music/routes/music/feed/get.js +++ b/packages/server/services/music/routes/music/feed/get.js @@ -1,23 +1,23 @@ import { MusicRelease, Track } from "@db_models" export default async (req) => { - const { limit = 10, trim = 0, order = "desc" } = req.query + const { limit = 10, trim = 0, order = "desc" } = req.query - const searchQuery = {} + const searchQuery = {} - const total_length = await MusicRelease.countDocuments(searchQuery) + const total_length = await MusicRelease.countDocuments(searchQuery) - let result = await MusicRelease.find({ - ...searchQuery, - public: true, - }) - .limit(limit) - .skip(trim) - .sort({ created_at: order === "desc" ? -1 : 1 }) + let result = await MusicRelease.find({ + ...searchQuery, + public: true, + }) + .limit(limit) + .skip(trim) + .sort({ created_at: order === "desc" ? -1 : 1 }) - return { - total_length: total_length, - has_more: total_length > trim + result.length, - items: result, - } -} \ No newline at end of file + return { + total_length: total_length, + has_more: total_length > trim + result.length, + items: result, + } +} diff --git a/packages/server/services/music/routes/music/feed/my/get.js b/packages/server/services/music/routes/music/feed/my/get.js index ede398c2..cc946bb8 100644 --- a/packages/server/services/music/routes/music/feed/my/get.js +++ b/packages/server/services/music/routes/music/feed/my/get.js @@ -1,16 +1,16 @@ export default { - middlewares: ["withAuthentication"], - fn: async (req) => { - const { keywords, limit = 10, offset = 0 } = req.query + middlewares: ["withAuthentication"], + fn: async (req) => { + const { keywords, limit = 10, offset = 0 } = req.query - const user_id = req.auth.session.user_id + const user_id = req.auth.session.user_id - let total_length = 0 - let result = [] + let total_length = 0 + let result = [] - return { - total_length: total_length, - items: result, - } - } -} \ No newline at end of file + return { + total_length: total_length, + items: result, + } + }, +} diff --git a/packages/server/services/music/routes/music/lyrics/[track_id]/put.js b/packages/server/services/music/routes/music/lyrics/[track_id]/put.js index 3f050237..97288a21 100644 --- a/packages/server/services/music/routes/music/lyrics/[track_id]/put.js +++ b/packages/server/services/music/routes/music/lyrics/[track_id]/put.js @@ -6,9 +6,8 @@ export default { const { track_id } = req.params const { video_source, lrc, sync_audio_at } = req.body - let track = await Track.findOne({ - _id: track_id, - }) + // check if track exists + let track = await Track.findById(track_id).catch(() => null) if (!track) { throw new OperationError(404, "Track not found") @@ -22,13 +21,14 @@ export default { track_id: track_id, video_source: video_source, lrc: lrc, - track: track, }) + // check if trackLyric exists let trackLyric = await TrackLyric.findOne({ track_id: track_id - }).lean() + }) + // if trackLyric exists, update it, else create it if (!trackLyric) { trackLyric = new TrackLyric({ track_id: track_id, diff --git a/packages/server/services/music/routes/music/my/folder/get.js b/packages/server/services/music/routes/music/my/folder/get.js index d48e8ac1..c7ccb1ad 100644 --- a/packages/server/services/music/routes/music/my/folder/get.js +++ b/packages/server/services/music/routes/music/my/folder/get.js @@ -1,73 +1,93 @@ -import { - TrackLike, -} from "@db_models" +import { TrackLike } from "@db_models" import TrackClass from "@classes/track" +const HANDLERS = { + track: { + model: TrackLike, + class: TrackClass, + type: "tracks", + idField: "track_id", + }, + // release: { + // model: ReleaseLike, + // class: ReleaseClass, + // type: 'releases', + // idField: 'release_id' + // }, + // playlist: { + // model: PlaylistLike, + // class: PlaylistClass, + // type: 'playlists', + // idField: 'playlist_id' + // }, +} + +async function getLikedItemsFromHandler(config, userId, pagination) { + try { + // obtain ids data and total items + const [total, likes] = await Promise.all([ + config.model.countDocuments({ user_id: userId }), + config.model + .find({ user_id: userId }) + .sort({ created_at: -1 }) + .limit(pagination.limit) + .skip(pagination.offset), + ]) + + const likedAtMap = new Map() + const itemIds = [] + + for (const like of likes) { + const itemId = like[config.idField] + + likedAtMap.set(itemId, like.created_at) + itemIds.push(itemId) + } + + // fetch track data + let processedItems = await config.class.get(itemIds, { + onlyList: true, + minimalData: true, + }) + + // mix with likes data + processedItems = processedItems.map((item) => { + item.liked = true + item.liked_at = likedAtMap.get(item._id.toString()) + return item + }) + + return { + items: processedItems, + total_items: total, + } + } catch (error) { + console.error(`Error processing ${config.type}:`, error) + return { items: [], total_items: 0 } + } +} + // // A endpoint to fetch track & playlists & releases likes // export default { - middlewares: ["withAuthentication"], - fn: async (req) => { - const user_id = req.auth.session.user_id - const { limit, offset } = req.query + middlewares: ["withAuthentication"], + fn: async (req) => { + const userId = req.auth.session.user_id + const { limit = 50, offset = 0 } = req.query - const [ - totalTrackLikes, - totalReleasesLikes, - totalPlaylistsLikes, - ] = await Promise.all([ - TrackLike.countDocuments({ user_id }), - 0, - 0, - ]) + const activeHandlers = Object.values(HANDLERS) - let [ - trackLikes, - releasesLikes, - playlistsLikes - ] = await Promise.all([ - TrackLike.find({ - user_id - }) - .limit(limit) - .skip(offset), - [], - [], - ]) + const results = await Promise.all( + activeHandlers.map((handler) => + getLikedItemsFromHandler(handler, userId, { limit, offset }), + ), + ) - let [ - Tracks, - Releases, - Playlists, - ] = await Promise.all([ - TrackClass.get(trackLikes.map(trackLike => trackLike.track_id), { - user_id, - onlyList: true, - }), - [], - [], - ]) - - Tracks = Tracks.sort((a, b) => b.liked_at - a.liked_at) - // Releases = Releases.sort((a, b) => b.liked_at - a.liked_at) - // Playlists = Playlists.sort((a, b) => b.liked_at - a.liked_at) - - return { - tracks: { - list: Tracks, - total_items: totalTrackLikes, - }, - releases: { - list: Releases, - total_items: totalReleasesLikes, - }, - playlists: { - list: Playlists, - total_items: totalPlaylistsLikes, - }, - total_length: totalTrackLikes + totalReleasesLikes + totalPlaylistsLikes, - } - } -} \ No newline at end of file + return activeHandlers.reduce((response, handler, index) => { + response[handler.type] = results[index] + return response + }, {}) + }, +} diff --git a/packages/server/services/music/routes/music/recently/get.js b/packages/server/services/music/routes/music/recently/get.js index e95a0d1b..baeee052 100644 --- a/packages/server/services/music/routes/music/recently/get.js +++ b/packages/server/services/music/routes/music/recently/get.js @@ -16,6 +16,20 @@ export default { .limit(req.query.limit ?? 20) .sort({ created_at: -1 }) + // filter tracks has different service than comtymusic + activities = activities.map((activity) => { + if (activity.payload.service && activity.payload.service !== "default") { + return null + } + + return activity + }) + + // filter null & undefined tracks + activities = activities.filter((activity) => { + return activity + }) + // filter undefined tracks_ids activities = activities.filter((activity) => { return activity.payload && activity.payload.track_id diff --git a/packages/server/services/music/routes/music/releases/[release_id]/data/get.js b/packages/server/services/music/routes/music/releases/[release_id]/data/get.js index 4c8e76f8..366321a8 100644 --- a/packages/server/services/music/routes/music/releases/[release_id]/data/get.js +++ b/packages/server/services/music/routes/music/releases/[release_id]/data/get.js @@ -2,33 +2,33 @@ import { MusicRelease, Track } from "@db_models" import TrackClass from "@classes/track" export default { - middlewares: ["withOptionalAuthentication"], - fn: async (req) => { - const { release_id } = req.params - const { limit = 50, offset = 0 } = req.query + middlewares: ["withOptionalAuthentication"], + fn: async (req) => { + const { release_id } = req.params + const { limit = 50, offset = 0 } = req.query - let release = await MusicRelease.findOne({ - _id: release_id - }) + let release = await MusicRelease.findOne({ + _id: release_id, + }) - if (!release) { - throw new OperationError(404, "Release not found") - } + if (!release) { + throw new OperationError(404, "Release not found") + } - release = release.toObject() + release = release.toObject() - const totalTracks = await Track.countDocuments({ - _id: release.list - }) + const totalTracks = await Track.countDocuments({ + _id: release.list, + }) - const tracks = await TrackClass.get(release.list, { - user_id: req.auth?.session?.user_id, - onlyList: true - }) + const tracks = await TrackClass.get(release.list, { + user_id: req.auth?.session?.user_id, + onlyList: true, + }) - release.listLength = totalTracks - release.list = tracks + release.listLength = totalTracks + release.items = tracks - return release - } -} \ No newline at end of file + return release + }, +} diff --git a/packages/server/services/music/routes/music/tracks/[track_id]/override/get.js b/packages/server/services/music/routes/music/tracks/[track_id]/override/get.js new file mode 100644 index 00000000..fec598b3 --- /dev/null +++ b/packages/server/services/music/routes/music/tracks/[track_id]/override/get.js @@ -0,0 +1,17 @@ +import { TrackOverride } from "@db_models" + +export default async (req) => { + const { track_id } = req.params + const { service } = req.query + + const trackOverride = await TrackOverride.findOne({ + track_id: track_id, + service: service, + }) + + if (!trackOverride) { + throw new OperationError(404, "Track override not found") + } + + return trackOverride.override +} \ No newline at end of file diff --git a/packages/server/services/music/routes/music/tracks/[track_id]/override/put.js b/packages/server/services/music/routes/music/tracks/[track_id]/override/put.js new file mode 100644 index 00000000..7f22fe7b --- /dev/null +++ b/packages/server/services/music/routes/music/tracks/[track_id]/override/put.js @@ -0,0 +1,36 @@ +import { TrackOverride } from "@db_models" + +export default { + middlewares: ["withAuthentication", "onlyAdmin"], + fn: async (req) => { + const { track_id } = req.params + const { service, override } = req.body + + let trackOverride = await TrackOverride.findOne({ + track_id: track_id, + service: service, + }).catch(() => null) + + if (!trackOverride) { + trackOverride = new TrackOverride({ + track_id: track_id, + service: service, + override: override, + }) + + await trackOverride.save() + } else { + trackOverride = await TrackOverride.findOneAndUpdate( + { + track_id: track_id, + service: service, + }, + { + override: override, + }, + ) + } + + return trackOverride.override + } +} \ No newline at end of file diff --git a/packages/server/services/music/routes/music/tracks/put.js b/packages/server/services/music/routes/music/tracks/put.js index 74e305a5..d7720e47 100644 --- a/packages/server/services/music/routes/music/tracks/put.js +++ b/packages/server/services/music/routes/music/tracks/put.js @@ -8,7 +8,9 @@ export default { let results = [] for await (const item of req.body.list) { - requiredFields(["title", "source"], item) + if (!item.source || !item.title) { + continue + } const track = await TrackClass.create({ ...item,