artists.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. import async from "async";
  2. import { useHasPermission } from "../hooks/hasPermission";
  3. // eslint-disable-next-line
  4. import moduleManager from "../../index";
  5. const DBModule = moduleManager.modules.db;
  6. const UtilsModule = moduleManager.modules.utils;
  7. const WSModule = moduleManager.modules.ws;
  8. const CacheModule = moduleManager.modules.cache;
  9. const MusicBrainzModule = moduleManager.modules.musicbrainz;
  10. const YouTubeModule = moduleManager.modules.youtube;
  11. CacheModule.runJob("SUB", {
  12. channel: "artists.create",
  13. cb: artist => {
  14. WSModule.runJob("EMIT_TO_ROOM", {
  15. room: "admin.artists",
  16. args: ["event:admin.artists.created", { data: { artist } }]
  17. });
  18. }
  19. });
  20. CacheModule.runJob("SUB", {
  21. channel: "artists.remove",
  22. cb: artistId => {
  23. WSModule.runJob("EMIT_TO_ROOM", {
  24. room: "admin.artists",
  25. args: ["event:admin.artists.deleted", { data: { artistId } }]
  26. });
  27. }
  28. });
  29. CacheModule.runJob("SUB", {
  30. channel: "artists.update",
  31. cb: artist => {
  32. WSModule.runJob("EMIT_TO_ROOM", {
  33. room: "admin.artists",
  34. args: ["event:admin.artists.updated", { data: { artist } }]
  35. });
  36. }
  37. });
  38. export default {
  39. /**
  40. * Gets artist items, used in the admin artist page by the AdvancedTable component
  41. * @param {object} session - the session object automatically added by the websocket
  42. * @param page - the page
  43. * @param pageSize - the size per page
  44. * @param properties - the properties to return for each artist item
  45. * @param sort - the sort object
  46. * @param queries - the queries array
  47. * @param operator - the operator for queries
  48. * @param cb
  49. */
  50. getData: useHasPermission(
  51. "admin.view.artists",
  52. async function getSet(session, page, pageSize, properties, sort, queries, operator, cb) {
  53. async.waterfall(
  54. [
  55. next => {
  56. DBModule.runJob(
  57. "GET_DATA",
  58. {
  59. page,
  60. pageSize,
  61. properties,
  62. sort,
  63. queries,
  64. operator,
  65. modelName: "artist",
  66. blacklistedProperties: [],
  67. specialProperties: {
  68. createdBy: [
  69. {
  70. $addFields: {
  71. createdByOID: {
  72. $convert: {
  73. input: "$createdBy",
  74. to: "objectId",
  75. onError: "unknown",
  76. onNull: "unknown"
  77. }
  78. }
  79. }
  80. },
  81. {
  82. $lookup: {
  83. from: "users",
  84. localField: "createdByOID",
  85. foreignField: "_id",
  86. as: "createdByUser"
  87. }
  88. },
  89. {
  90. $unwind: {
  91. path: "$createdByUser",
  92. preserveNullAndEmptyArrays: true
  93. }
  94. },
  95. {
  96. $addFields: {
  97. createdByUsername: {
  98. $ifNull: ["$createdByUser.username", "unknown"]
  99. }
  100. }
  101. },
  102. {
  103. $project: {
  104. createdByOID: 0,
  105. createdByUser: 0
  106. }
  107. }
  108. ]
  109. },
  110. specialQueries: {
  111. createdBy: newQuery => ({
  112. $or: [newQuery, { createdByUsername: newQuery.createdBy }]
  113. })
  114. }
  115. },
  116. this
  117. )
  118. .then(response => {
  119. next(null, response);
  120. })
  121. .catch(err => {
  122. next(err);
  123. });
  124. }
  125. ],
  126. async (err, response) => {
  127. if (err && err !== true) {
  128. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  129. this.log("ERROR", "ARTISTS_GET_DATA", `Failed to get data from artists. "${err}"`);
  130. return cb({ status: "error", message: err });
  131. }
  132. this.log("SUCCESS", "ARTISTS_GET_DATA", `Got data from artists successfully.`);
  133. return cb({
  134. status: "success",
  135. message: "Successfully got data from artists.",
  136. data: response
  137. });
  138. }
  139. );
  140. }
  141. ),
  142. /**
  143. * Creates an artist item
  144. * @param {object} session - the session object automatically added by the websocket
  145. * @param {object} data - the object of the artist data
  146. * @param {Function} cb - gets called with the result
  147. */
  148. create: useHasPermission("artists.create", async function create(session, data, cb) {
  149. const artistModel = await DBModule.runJob("GET_MODEL", { modelName: "artist" }, this);
  150. async.waterfall(
  151. [
  152. next => {
  153. if (data?.musicbrainzData?.id !== data?.musicbrainzIdentifier) return next("MusicBrainz data must match the provided identifier.");
  154. return next();
  155. },
  156. next => {
  157. data.createdBy = session.userId;
  158. data.createdAt = Date.now();
  159. artistModel.create(data, next);
  160. }
  161. ],
  162. async (err, artist) => {
  163. if (err) {
  164. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  165. this.log("ERROR", "ARTIST_CREATE", `Creating artist failed. "${err}"`);
  166. return cb({ status: "error", message: err });
  167. }
  168. CacheModule.runJob("PUB", { channel: "artists.create", value: artist });
  169. this.log("SUCCESS", "ARTIST_CREATE", `Created artist successful.`);
  170. return cb({
  171. status: "success",
  172. message: "Successfully created artist"
  173. });
  174. }
  175. );
  176. }),
  177. /**
  178. * Gets a artist item by id
  179. * @param {object} session - the session object automatically added by the websocket
  180. * @param {string} artistId - the artist item id
  181. * @param {Function} cb - gets called with the result
  182. */
  183. async getArtistFromId(session, artistId, cb) {
  184. const artistModel = await DBModule.runJob("GET_MODEL", { modelName: "artist" }, this);
  185. async.waterfall(
  186. [
  187. next => {
  188. artistModel.findById(artistId, next);
  189. }
  190. ],
  191. async (err, artist) => {
  192. if (err) {
  193. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  194. this.log("ERROR", "GET_ARTIST_FROM_ID", `Getting artist item ${artistId} failed. "${err}"`);
  195. return cb({ status: "error", message: err });
  196. }
  197. this.log("SUCCESS", "GET_ARTIST_FROM_ID", `Got artist item ${artistId} successfully.`, false);
  198. return cb({ status: "success", data: { artist } });
  199. }
  200. );
  201. },
  202. /**
  203. * Updates an artist item
  204. * @param {object} session - the session object automatically added by the websocket
  205. * @param {string} artistId - the id of the artist item
  206. * @param {object} item - the artist item object
  207. * @param {Function} cb - gets called with the result
  208. */
  209. update: useHasPermission("artists.update", async function update(session, artistId, item, cb) {
  210. const artistModel = await DBModule.runJob("GET_MODEL", { modelName: "artist" }, this);
  211. async.waterfall(
  212. [
  213. next => {
  214. if (!artistId) return next("Please provide an artist item id to update.");
  215. if (item?.musicbrainzData?.id !== item?.musicbrainzIdentifier) return next("MusicBrainz data must match the provided identifier.");
  216. return next();
  217. },
  218. next => {
  219. artistModel.updateOne({ _id: artistId }, item, { upsert: true }, err => next(err));
  220. }
  221. ],
  222. async err => {
  223. if (err) {
  224. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  225. this.log(
  226. "ERROR",
  227. "ARTIST_UPDATE",
  228. `Updating artist item "${artistId}" failed for user "${session.userId}". "${err}"`
  229. );
  230. return cb({ status: "error", message: err });
  231. }
  232. CacheModule.runJob("PUB", { channel: "artists.update", value: { ...item, _id: artistId } });
  233. this.log(
  234. "SUCCESS",
  235. "ARTIST_UPDATE",
  236. `Updated artist item "${artistId}" successful for user "${session.userId}".`
  237. );
  238. return cb({
  239. status: "success",
  240. message: "Successfully updated artist item"
  241. });
  242. }
  243. );
  244. }),
  245. getMusicbrainzArtist: useHasPermission("artists.update", async function getMusicbrainzArtist(session, musicbrainzIdentifier, cb) {
  246. const ArtistApiResponse = await MusicBrainzModule.runJob(
  247. "API_CALL",
  248. {
  249. url: `https://musicbrainz.org/ws/2/artist/${musicbrainzIdentifier}/`,
  250. params: {
  251. fmt: "json",
  252. inc: "aliases"
  253. }
  254. },
  255. this
  256. );
  257. return cb({
  258. data: ArtistApiResponse,
  259. });
  260. }),
  261. getMusicbrainzRelatedUrls: useHasPermission("artists.update", async function getMusicbrainzRelatedUrls(session, musicbrainzIdentifier, cb) {
  262. const ArtistApiResponse = await MusicBrainzModule.runJob(
  263. "API_CALL",
  264. {
  265. url: `https://musicbrainz.org/ws/2/artist/${musicbrainzIdentifier}/`,
  266. params: {
  267. fmt: "json",
  268. inc: "url-rels"
  269. }
  270. },
  271. this
  272. );
  273. return cb({
  274. data: ArtistApiResponse,
  275. });
  276. }),
  277. getIdFromUrl: useHasPermission("artists.update", async function getIdFromUrl(session, type, url, cb) {
  278. if (type === "youtube") {
  279. YouTubeModule.runJob("GET_CHANNEL_ID", {
  280. url,
  281. disableSearch: false,
  282. }).then(({ channelId }) => {
  283. if (channelId) {
  284. cb({
  285. status: "success",
  286. channelId,
  287. });
  288. } else {
  289. cb({
  290. status: "error",
  291. message: "Playlist id not found",
  292. });
  293. }
  294. }).catch(async err => {
  295. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  296. cb({ status: "error", message: err });
  297. });
  298. } else cb({
  299. status: "error",
  300. message: "Invalid type"
  301. });
  302. }),
  303. };