artists.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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)
  154. return next("MusicBrainz data must match the provided identifier.");
  155. return next();
  156. },
  157. next => {
  158. data.createdBy = session.userId;
  159. data.createdAt = Date.now();
  160. artistModel.create(data, next);
  161. }
  162. ],
  163. async (err, artist) => {
  164. if (err) {
  165. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  166. this.log("ERROR", "ARTIST_CREATE", `Creating artist failed. "${err}"`);
  167. return cb({ status: "error", message: err });
  168. }
  169. CacheModule.runJob("PUB", { channel: "artists.create", value: artist });
  170. this.log("SUCCESS", "ARTIST_CREATE", `Created artist successful.`);
  171. return cb({
  172. status: "success",
  173. message: "Successfully created artist"
  174. });
  175. }
  176. );
  177. }),
  178. /**
  179. * Gets a artist item by id
  180. * @param {object} session - the session object automatically added by the websocket
  181. * @param {string} artistId - the artist item id
  182. * @param {Function} cb - gets called with the result
  183. */
  184. async getArtistFromId(session, artistId, cb) {
  185. const artistModel = await DBModule.runJob("GET_MODEL", { modelName: "artist" }, this);
  186. async.waterfall(
  187. [
  188. next => {
  189. artistModel.findById(artistId, next);
  190. }
  191. ],
  192. async (err, artist) => {
  193. if (err) {
  194. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  195. this.log("ERROR", "GET_ARTIST_FROM_ID", `Getting artist item ${artistId} failed. "${err}"`);
  196. return cb({ status: "error", message: err });
  197. }
  198. this.log("SUCCESS", "GET_ARTIST_FROM_ID", `Got artist item ${artistId} successfully.`, false);
  199. return cb({ status: "success", data: { artist } });
  200. }
  201. );
  202. },
  203. /**
  204. * Updates an artist item
  205. * @param {object} session - the session object automatically added by the websocket
  206. * @param {string} artistId - the id of the artist item
  207. * @param {object} item - the artist item object
  208. * @param {Function} cb - gets called with the result
  209. */
  210. update: useHasPermission("artists.update", async function update(session, artistId, item, cb) {
  211. const artistModel = await DBModule.runJob("GET_MODEL", { modelName: "artist" }, this);
  212. async.waterfall(
  213. [
  214. next => {
  215. if (!artistId) return next("Please provide an artist item id to update.");
  216. if (item?.musicbrainzData?.id !== item?.musicbrainzIdentifier)
  217. return next("MusicBrainz data must match the provided identifier.");
  218. return next();
  219. },
  220. next => {
  221. artistModel.updateOne({ _id: artistId }, item, { upsert: true }, err => next(err));
  222. }
  223. ],
  224. async err => {
  225. if (err) {
  226. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  227. this.log(
  228. "ERROR",
  229. "ARTIST_UPDATE",
  230. `Updating artist item "${artistId}" failed for user "${session.userId}". "${err}"`
  231. );
  232. return cb({ status: "error", message: err });
  233. }
  234. CacheModule.runJob("PUB", { channel: "artists.update", value: { ...item, _id: artistId } });
  235. this.log(
  236. "SUCCESS",
  237. "ARTIST_UPDATE",
  238. `Updated artist item "${artistId}" successful for user "${session.userId}".`
  239. );
  240. return cb({
  241. status: "success",
  242. message: "Successfully updated artist item"
  243. });
  244. }
  245. );
  246. }),
  247. getMusicbrainzArtist: useHasPermission(
  248. "artists.update",
  249. async function getMusicbrainzArtist(session, musicbrainzIdentifier, cb) {
  250. const ArtistApiResponse = await MusicBrainzModule.runJob(
  251. "API_CALL",
  252. {
  253. url: `https://musicbrainz.org/ws/2/artist/${musicbrainzIdentifier}/`,
  254. params: {
  255. fmt: "json",
  256. inc: "aliases"
  257. }
  258. },
  259. this
  260. );
  261. return cb({
  262. data: ArtistApiResponse
  263. });
  264. }
  265. ),
  266. getMusicbrainzRelatedUrls: useHasPermission(
  267. "artists.update",
  268. async function getMusicbrainzRelatedUrls(session, musicbrainzIdentifier, cb) {
  269. const ArtistApiResponse = await MusicBrainzModule.runJob(
  270. "API_CALL",
  271. {
  272. url: `https://musicbrainz.org/ws/2/artist/${musicbrainzIdentifier}/`,
  273. params: {
  274. fmt: "json",
  275. inc: "url-rels"
  276. }
  277. },
  278. this
  279. );
  280. return cb({
  281. data: ArtistApiResponse
  282. });
  283. }
  284. ),
  285. getIdFromUrl: useHasPermission("artists.update", async function getIdFromUrl(session, type, url, cb) {
  286. if (type === "youtube") {
  287. YouTubeModule.runJob("GET_CHANNEL_ID", {
  288. url,
  289. disableSearch: false
  290. })
  291. .then(({ channelId }) => {
  292. if (channelId) {
  293. cb({
  294. status: "success",
  295. channelId
  296. });
  297. } else {
  298. cb({
  299. status: "error",
  300. message: "Playlist id not found"
  301. });
  302. }
  303. })
  304. .catch(async err => {
  305. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  306. cb({ status: "error", message: err });
  307. });
  308. } else
  309. cb({
  310. status: "error",
  311. message: "Invalid type"
  312. });
  313. })
  314. };