use new items list standart

This commit is contained in:
SrGooglo 2025-02-05 02:51:35 +00:00
parent 3990ef45c9
commit 9c9fa4c18b
13 changed files with 295 additions and 177 deletions

View File

@ -12,6 +12,7 @@ const AllowedUpdateFields = [
export default class Release { export default class Release {
static async create(payload) { static async create(payload) {
console.log(payload)
if (!payload.title) { if (!payload.title) {
throw new OperationError(400, "Release title is required") 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 // 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") { if (typeof item !== "string") {
item = item._id item = item._id
} }

View File

@ -1,66 +1,96 @@
import { Track, TrackLike } from "@db_models" import { Track, TrackLike } from "@db_models"
export default async (track_id, { user_id = null, onlyList = false } = {}) => { async function fullfillData(list, { user_id = null }) {
if (!track_id) { if (!Array.isArray(list)) {
throw new OperationError(400, "Missing track_id") list = [list]
} }
const isMultiple = Array.isArray(track_id) || track_id.includes(",") const trackIds = list.map((track) => {
return track._id
})
if (isMultiple) { // if user_id is provided, fetch likes
const track_ids = Array.isArray(track_id) ? track_id : track_id.split(",") if (user_id) {
const tracksLikes = await TrackLike.find({
user_id: user_id,
track_id: { $in: trackIds },
})
const tracks = await Track.find({ list = list.map(async (track) => {
_id: { $in: track_ids } const trackLike = tracksLikes.find((trackLike) => {
}).lean() return trackLike.track_id.toString() === track._id.toString()
})
if (user_id) { if (trackLike) {
const trackLikes = await TrackLike.find({ track.liked_at = trackLike.created_at
user_id: user_id, track.liked = true
track_id: { $in: track_ids } }
})
// FIXME: this could be a performance issue when there are a lot of likes return track
// 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 (track) { list = await Promise.all(list)
track.liked_at = trackLike.created_at }
track.liked = true
}
}
}
if (onlyList) { // process some metadata
return tracks list = list.map(async (track) => {
} if (track.metadata) {
if (track.metadata.bitrate && track.metadata.bitrate > 9000) {
track.metadata.lossless = true
}
}
return { return track
total_count: await Track.countDocuments({ _id: { $in: track_ids } }), })
list: tracks,
}
}
const track = await Track.findOne({ list = await Promise.all(list)
_id: track_id
}).lean()
if (!track) { return list
throw new OperationError(404, "Track not found") }
}
export default async (track_id, { user_id = null, onlyList = false } = {}) => {
if (user_id) { if (!track_id) {
const trackLike = await TrackLike.findOne({ throw new OperationError(400, "Missing track_id")
user_id: user_id, }
track_id: track_id,
}) const isMultiple = Array.isArray(track_id) || track_id.includes(",")
if (trackLike) { if (isMultiple) {
track.liked_at = trackLike.created_at const track_ids = Array.isArray(track_id)
track.liked = true ? track_id
} : track_id.split(",")
}
let tracks = await Track.find({
return track _id: { $in: track_ids },
}).lean()
tracks = await fullfillData(tracks, {
user_id,
})
if (onlyList) {
return tracks
}
return {
total_count: await Track.countDocuments({
_id: { $in: track_ids },
}),
list: tracks,
}
}
let track = await Track.findOne({
_id: track_id,
}).lean()
if (!track) {
throw new OperationError(404, "Track not found")
}
track = await fullfillData(track, {
user_id,
})
return track[0]
} }

View File

@ -9,7 +9,7 @@ export default async (user_id, track_id, to) => {
throw new OperationError(400, "Missing track_id") 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) { if (!track) {
throw new OperationError(404, "Track not found") throw new OperationError(404, "Track not found")

View File

@ -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) const targetSocket = await global.websocket.find.socketByUserId(user_id)
if (targetSocket) { if (targetSocket) {

View File

@ -1,23 +1,23 @@
import { MusicRelease, Track } from "@db_models" import { MusicRelease, Track } from "@db_models"
export default async (req) => { 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({ let result = await MusicRelease.find({
...searchQuery, ...searchQuery,
public: true, public: true,
}) })
.limit(limit) .limit(limit)
.skip(trim) .skip(trim)
.sort({ created_at: order === "desc" ? -1 : 1 }) .sort({ created_at: order === "desc" ? -1 : 1 })
return { return {
total_length: total_length, total_length: total_length,
has_more: total_length > trim + result.length, has_more: total_length > trim + result.length,
items: result, items: result,
} }
} }

View File

@ -1,16 +1,16 @@
export default { export default {
middlewares: ["withAuthentication"], middlewares: ["withAuthentication"],
fn: async (req) => { fn: async (req) => {
const { keywords, limit = 10, offset = 0 } = req.query 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 total_length = 0
let result = [] let result = []
return { return {
total_length: total_length, total_length: total_length,
items: result, items: result,
} }
} },
} }

View File

@ -6,9 +6,8 @@ export default {
const { track_id } = req.params const { track_id } = req.params
const { video_source, lrc, sync_audio_at } = req.body const { video_source, lrc, sync_audio_at } = req.body
let track = await Track.findOne({ // check if track exists
_id: track_id, let track = await Track.findById(track_id).catch(() => null)
})
if (!track) { if (!track) {
throw new OperationError(404, "Track not found") throw new OperationError(404, "Track not found")
@ -22,13 +21,14 @@ export default {
track_id: track_id, track_id: track_id,
video_source: video_source, video_source: video_source,
lrc: lrc, lrc: lrc,
track: track,
}) })
// check if trackLyric exists
let trackLyric = await TrackLyric.findOne({ let trackLyric = await TrackLyric.findOne({
track_id: track_id track_id: track_id
}).lean() })
// if trackLyric exists, update it, else create it
if (!trackLyric) { if (!trackLyric) {
trackLyric = new TrackLyric({ trackLyric = new TrackLyric({
track_id: track_id, track_id: track_id,

View File

@ -1,73 +1,93 @@
import { import { TrackLike } from "@db_models"
TrackLike,
} from "@db_models"
import TrackClass from "@classes/track" 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 // A endpoint to fetch track & playlists & releases likes
// //
export default { export default {
middlewares: ["withAuthentication"], middlewares: ["withAuthentication"],
fn: async (req) => { fn: async (req) => {
const user_id = req.auth.session.user_id const userId = req.auth.session.user_id
const { limit, offset } = req.query const { limit = 50, offset = 0 } = req.query
const [ const activeHandlers = Object.values(HANDLERS)
totalTrackLikes,
totalReleasesLikes,
totalPlaylistsLikes,
] = await Promise.all([
TrackLike.countDocuments({ user_id }),
0,
0,
])
let [ const results = await Promise.all(
trackLikes, activeHandlers.map((handler) =>
releasesLikes, getLikedItemsFromHandler(handler, userId, { limit, offset }),
playlistsLikes ),
] = await Promise.all([ )
TrackLike.find({
user_id
})
.limit(limit)
.skip(offset),
[],
[],
])
let [ return activeHandlers.reduce((response, handler, index) => {
Tracks, response[handler.type] = results[index]
Releases, return response
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,
}
}
} }

View File

@ -16,6 +16,20 @@ export default {
.limit(req.query.limit ?? 20) .limit(req.query.limit ?? 20)
.sort({ created_at: -1 }) .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 // filter undefined tracks_ids
activities = activities.filter((activity) => { activities = activities.filter((activity) => {
return activity.payload && activity.payload.track_id return activity.payload && activity.payload.track_id

View File

@ -2,33 +2,33 @@ import { MusicRelease, Track } from "@db_models"
import TrackClass from "@classes/track" import TrackClass from "@classes/track"
export default { export default {
middlewares: ["withOptionalAuthentication"], middlewares: ["withOptionalAuthentication"],
fn: async (req) => { fn: async (req) => {
const { release_id } = req.params const { release_id } = req.params
const { limit = 50, offset = 0 } = req.query const { limit = 50, offset = 0 } = req.query
let release = await MusicRelease.findOne({ let release = await MusicRelease.findOne({
_id: release_id _id: release_id,
}) })
if (!release) { if (!release) {
throw new OperationError(404, "Release not found") throw new OperationError(404, "Release not found")
} }
release = release.toObject() release = release.toObject()
const totalTracks = await Track.countDocuments({ const totalTracks = await Track.countDocuments({
_id: release.list _id: release.list,
}) })
const tracks = await TrackClass.get(release.list, { const tracks = await TrackClass.get(release.list, {
user_id: req.auth?.session?.user_id, user_id: req.auth?.session?.user_id,
onlyList: true onlyList: true,
}) })
release.listLength = totalTracks release.listLength = totalTracks
release.list = tracks release.items = tracks
return release return release
} },
} }

View File

@ -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
}

View File

@ -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
}
}

View File

@ -8,7 +8,9 @@ export default {
let results = [] let results = []
for await (const item of req.body.list) { for await (const item of req.body.list) {
requiredFields(["title", "source"], item) if (!item.source || !item.title) {
continue
}
const track = await TrackClass.create({ const track = await TrackClass.create({
...item, ...item,