import async from "async"; import { useHasPermission } from "../hooks/hasPermission"; // eslint-disable-next-line import moduleManager from "../../index"; const DBModule = moduleManager.modules.db; const UtilsModule = moduleManager.modules.utils; const WSModule = moduleManager.modules.ws; const CacheModule = moduleManager.modules.cache; const MusicBrainzModule = moduleManager.modules.musicbrainz; const YouTubeModule = moduleManager.modules.youtube; CacheModule.runJob("SUB", { channel: "artists.create", cb: artist => { WSModule.runJob("EMIT_TO_ROOM", { room: "admin.artists", args: ["event:admin.artists.created", { data: { artist } }] }); } }); CacheModule.runJob("SUB", { channel: "artists.remove", cb: artistId => { WSModule.runJob("EMIT_TO_ROOM", { room: "admin.artists", args: ["event:admin.artists.deleted", { data: { artistId } }] }); } }); CacheModule.runJob("SUB", { channel: "artists.update", cb: artist => { WSModule.runJob("EMIT_TO_ROOM", { room: "admin.artists", args: ["event:admin.artists.updated", { data: { artist } }] }); } }); export default { /** * Gets artist items, used in the admin artist page by the AdvancedTable component * @param {object} session - the session object automatically added by the websocket * @param page - the page * @param pageSize - the size per page * @param properties - the properties to return for each artist item * @param sort - the sort object * @param queries - the queries array * @param operator - the operator for queries * @param cb */ getData: useHasPermission( "admin.view.artists", async function getSet(session, page, pageSize, properties, sort, queries, operator, cb) { async.waterfall( [ next => { DBModule.runJob( "GET_DATA", { page, pageSize, properties, sort, queries, operator, modelName: "artist", blacklistedProperties: [], specialProperties: { createdBy: [ { $addFields: { createdByOID: { $convert: { input: "$createdBy", to: "objectId", onError: "unknown", onNull: "unknown" } } } }, { $lookup: { from: "users", localField: "createdByOID", foreignField: "_id", as: "createdByUser" } }, { $unwind: { path: "$createdByUser", preserveNullAndEmptyArrays: true } }, { $addFields: { createdByUsername: { $ifNull: ["$createdByUser.username", "unknown"] } } }, { $project: { createdByOID: 0, createdByUser: 0 } } ] }, specialQueries: { createdBy: newQuery => ({ $or: [newQuery, { createdByUsername: newQuery.createdBy }] }) } }, this ) .then(response => { next(null, response); }) .catch(err => { next(err); }); } ], async (err, response) => { if (err && err !== true) { err = await UtilsModule.runJob("GET_ERROR", { error: err }, this); this.log("ERROR", "ARTISTS_GET_DATA", `Failed to get data from artists. "${err}"`); return cb({ status: "error", message: err }); } this.log("SUCCESS", "ARTISTS_GET_DATA", `Got data from artists successfully.`); return cb({ status: "success", message: "Successfully got data from artists.", data: response }); } ); } ), /** * Creates an artist item * @param {object} session - the session object automatically added by the websocket * @param {object} data - the object of the artist data * @param {Function} cb - gets called with the result */ create: useHasPermission("artists.create", async function create(session, data, cb) { const artistModel = await DBModule.runJob("GET_MODEL", { modelName: "artist" }, this); async.waterfall( [ next => { if (data?.musicbrainzData?.id !== data?.musicbrainzIdentifier) return next("MusicBrainz data must match the provided identifier."); return next(); }, next => { data.createdBy = session.userId; data.createdAt = Date.now(); artistModel.create(data, next); } ], async (err, artist) => { if (err) { err = await UtilsModule.runJob("GET_ERROR", { error: err }, this); this.log("ERROR", "ARTIST_CREATE", `Creating artist failed. "${err}"`); return cb({ status: "error", message: err }); } CacheModule.runJob("PUB", { channel: "artists.create", value: artist }); this.log("SUCCESS", "ARTIST_CREATE", `Created artist successful.`); return cb({ status: "success", message: "Successfully created artist" }); } ); }), /** * Gets a artist item by id * @param {object} session - the session object automatically added by the websocket * @param {string} artistId - the artist item id * @param {Function} cb - gets called with the result */ async getArtistFromId(session, artistId, cb) { const artistModel = await DBModule.runJob("GET_MODEL", { modelName: "artist" }, this); async.waterfall( [ next => { artistModel.findById(artistId, next); } ], async (err, artist) => { if (err) { err = await UtilsModule.runJob("GET_ERROR", { error: err }, this); this.log("ERROR", "GET_ARTIST_FROM_ID", `Getting artist item ${artistId} failed. "${err}"`); return cb({ status: "error", message: err }); } this.log("SUCCESS", "GET_ARTIST_FROM_ID", `Got artist item ${artistId} successfully.`, false); return cb({ status: "success", data: { artist } }); } ); }, /** * Updates an artist item * @param {object} session - the session object automatically added by the websocket * @param {string} artistId - the id of the artist item * @param {object} item - the artist item object * @param {Function} cb - gets called with the result */ update: useHasPermission("artists.update", async function update(session, artistId, item, cb) { const artistModel = await DBModule.runJob("GET_MODEL", { modelName: "artist" }, this); async.waterfall( [ next => { if (!artistId) return next("Please provide an artist item id to update."); if (item?.musicbrainzData?.id !== item?.musicbrainzIdentifier) return next("MusicBrainz data must match the provided identifier."); return next(); }, next => { artistModel.updateOne({ _id: artistId }, item, { upsert: true }, err => next(err)); } ], async err => { if (err) { err = await UtilsModule.runJob("GET_ERROR", { error: err }, this); this.log( "ERROR", "ARTIST_UPDATE", `Updating artist item "${artistId}" failed for user "${session.userId}". "${err}"` ); return cb({ status: "error", message: err }); } CacheModule.runJob("PUB", { channel: "artists.update", value: { ...item, _id: artistId } }); this.log( "SUCCESS", "ARTIST_UPDATE", `Updated artist item "${artistId}" successful for user "${session.userId}".` ); return cb({ status: "success", message: "Successfully updated artist item" }); } ); }), getMusicbrainzArtist: useHasPermission( "artists.update", async function getMusicbrainzArtist(session, musicbrainzIdentifier, cb) { const ArtistApiResponse = await MusicBrainzModule.runJob( "API_CALL", { url: `https://musicbrainz.org/ws/2/artist/${musicbrainzIdentifier}/`, params: { fmt: "json", inc: "aliases" } }, this ); return cb({ data: ArtistApiResponse }); } ), getMusicbrainzRelatedUrls: useHasPermission( "artists.update", async function getMusicbrainzRelatedUrls(session, musicbrainzIdentifier, cb) { const ArtistApiResponse = await MusicBrainzModule.runJob( "API_CALL", { url: `https://musicbrainz.org/ws/2/artist/${musicbrainzIdentifier}/`, params: { fmt: "json", inc: "url-rels" } }, this ); return cb({ data: ArtistApiResponse }); } ), getIdFromUrl: useHasPermission("artists.update", async function getIdFromUrl(session, type, url, cb) { if (type === "youtube") { YouTubeModule.runJob("GET_CHANNEL_ID", { url, disableSearch: false }) .then(({ channelId }) => { if (channelId) { cb({ status: "success", channelId }); } else { cb({ status: "error", message: "Playlist id not found" }); } }) .catch(async err => { err = await UtilsModule.runJob("GET_ERROR", { error: err }, this); cb({ status: "error", message: err }); }); } else cb({ status: "error", message: "Invalid type" }); }) };