apis.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. import config from "config";
  2. import async from "async";
  3. import axios from "axios";
  4. import isLoginRequired from "../hooks/loginRequired";
  5. import { hasPermission, useHasPermission } from "../hooks/hasPermission";
  6. // eslint-disable-next-line
  7. import moduleManager from "../../index";
  8. const UtilsModule = moduleManager.modules.utils;
  9. const WSModule = moduleManager.modules.ws;
  10. const YouTubeModule = moduleManager.modules.youtube;
  11. const SpotifyModule = moduleManager.modules.spotify;
  12. export default {
  13. /**
  14. * Fetches a list of songs from Youtube's API
  15. *
  16. * @param {object} session - user session
  17. * @param {string} query - the query we'll pass to youtubes api
  18. * @param {Function} cb - callback
  19. * @returns {{status: string, data: object}} - returns an object
  20. */
  21. searchYoutube: isLoginRequired(function searchYoutube(session, query, cb) {
  22. return YouTubeModule.runJob("SEARCH", { query }, this)
  23. .then(data => {
  24. this.log("SUCCESS", "APIS_SEARCH_YOUTUBE", `Searching YouTube successful with query "${query}".`);
  25. return cb({ status: "success", data });
  26. })
  27. .catch(async err => {
  28. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  29. this.log("ERROR", "APIS_SEARCH_YOUTUBE", `Searching youtube failed with query "${query}". "${err}"`);
  30. return cb({ status: "error", message: err });
  31. });
  32. }),
  33. /**
  34. * Fetches a specific page of search results from Youtube's API
  35. *
  36. * @param {object} session - user session
  37. * @param {string} query - the query we'll pass to youtubes api
  38. * @param {string} pageToken - identifies a specific page in the result set that should be retrieved
  39. * @param {Function} cb - callback
  40. * @returns {{status: string, data: object}} - returns an object
  41. */
  42. searchYoutubeForPage: isLoginRequired(function searchYoutubeForPage(session, query, pageToken, cb) {
  43. return YouTubeModule.runJob("SEARCH", { query, pageToken }, this)
  44. .then(data => {
  45. this.log(
  46. "SUCCESS",
  47. "APIS_SEARCH_YOUTUBE_FOR_PAGE",
  48. `Searching YouTube successful with query "${query}".`
  49. );
  50. return cb({ status: "success", data });
  51. })
  52. .catch(async err => {
  53. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  54. this.log(
  55. "ERROR",
  56. "APIS_SEARCH_YOUTUBE_FOR_PAGE",
  57. `Searching youtube failed with query "${query}". "${err}"`
  58. );
  59. return cb({ status: "error", message: err });
  60. });
  61. }),
  62. /**
  63. * Gets Discogs data
  64. *
  65. * @param session
  66. * @param query - the query
  67. * @param {Function} cb
  68. */
  69. searchDiscogs: useHasPermission("apis.searchDiscogs", function searchDiscogs(session, query, page, cb) {
  70. async.waterfall(
  71. [
  72. next => {
  73. const options = {
  74. params: { q: query, per_page: 20, page },
  75. headers: {
  76. "User-Agent": "Request",
  77. Authorization: `Discogs key=${config.get("apis.discogs.client")}, secret=${config.get(
  78. "apis.discogs.secret"
  79. )}`
  80. }
  81. };
  82. axios
  83. .get("https://api.discogs.com/database/search", options)
  84. .then(res => next(null, res.data))
  85. .catch(err => next(err));
  86. }
  87. ],
  88. async (err, body) => {
  89. if (err) {
  90. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  91. this.log(
  92. "ERROR",
  93. "APIS_SEARCH_DISCOGS",
  94. `Searching discogs failed with query "${query}". "${err}"`
  95. );
  96. return cb({ status: "error", message: err });
  97. }
  98. this.log(
  99. "SUCCESS",
  100. "APIS_SEARCH_DISCOGS",
  101. `User "${session.userId}" searched Discogs succesfully for query "${query}".`
  102. );
  103. return cb({
  104. status: "success",
  105. data: {
  106. results: body.results,
  107. pages: body.pagination.pages
  108. }
  109. });
  110. }
  111. );
  112. }),
  113. /**
  114. *
  115. *
  116. * @param session
  117. * @param trackId - the trackId
  118. * @param {Function} cb
  119. */
  120. getAlternativeMediaSourcesForTracks: useHasPermission(
  121. "spotify.getAlternativeMediaSourcesForTracks",
  122. function getAlternativeMediaSourcesForTracks(session, mediaSources, collectAlternativeMediaSourcesOrigins, cb) {
  123. async.waterfall(
  124. [
  125. next => {
  126. if (!mediaSources) {
  127. next("Invalid mediaSources provided.");
  128. return;
  129. }
  130. next();
  131. },
  132. next => {
  133. this.keepLongJob();
  134. this.publishProgress({
  135. status: "started",
  136. title: "Getting alternative media sources for Spotify tracks",
  137. message: "Starting up",
  138. id: this.toString()
  139. });
  140. // await CacheModule.runJob(
  141. // "RPUSH",
  142. // { key: `longJobs.${session.userId}`, value: this.toString() },
  143. // this
  144. // );
  145. SpotifyModule.runJob(
  146. "GET_ALTERNATIVE_MEDIA_SOURCES_FOR_TRACKS",
  147. {
  148. mediaSources,
  149. collectAlternativeMediaSourcesOrigins
  150. },
  151. this
  152. )
  153. .then(() => next())
  154. .catch(next);
  155. }
  156. ],
  157. async err => {
  158. if (err) {
  159. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  160. this.log(
  161. "ERROR",
  162. "APIS_GET_ALTERNATIVE_SOURCES",
  163. `Getting alternative sources failed for "${mediaSources.join(", ")}". "${err}"`
  164. );
  165. return cb({ status: "error", message: err });
  166. }
  167. this.log(
  168. "SUCCESS",
  169. "APIS_GET_ALTERNATIVE_SOURCES",
  170. `User "${session.userId}" started getting alternatives for "${mediaSources.join(", ")}".`
  171. );
  172. return cb({
  173. status: "success"
  174. });
  175. }
  176. );
  177. }
  178. ),
  179. /**
  180. *
  181. *
  182. * @param session
  183. * @param trackId - the trackId
  184. * @param {Function} cb
  185. */
  186. getAlternativeAlbumSourcesForAlbums: useHasPermission(
  187. "spotify.getAlternativeAlbumSourcesForAlbums",
  188. function getAlternativeAlbumSourcesForAlbums(session, albumIds, collectAlternativeAlbumSourcesOrigins, cb) {
  189. async.waterfall(
  190. [
  191. next => {
  192. if (!albumIds) {
  193. next("Invalid albumIds provided.");
  194. return;
  195. }
  196. next();
  197. },
  198. next => {
  199. this.keepLongJob();
  200. this.publishProgress({
  201. status: "started",
  202. title: "Getting alternative album sources for Spotify albums",
  203. message: "Starting up",
  204. id: this.toString()
  205. });
  206. // await CacheModule.runJob(
  207. // "RPUSH",
  208. // { key: `longJobs.${session.userId}`, value: this.toString() },
  209. // this
  210. // );
  211. SpotifyModule.runJob(
  212. "GET_ALTERNATIVE_ALBUM_SOURCES_FOR_ALBUMS",
  213. { albumIds, collectAlternativeAlbumSourcesOrigins },
  214. this
  215. )
  216. .then(() => next())
  217. .catch(next);
  218. }
  219. ],
  220. async err => {
  221. if (err) {
  222. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  223. this.log(
  224. "ERROR",
  225. "APIS_GET_ALTERNATIVE_ALBUM_SOURCES",
  226. `Getting alternative album sources failed for "${albumIds.join(", ")}". "${err}"`
  227. );
  228. return cb({ status: "error", message: err });
  229. }
  230. this.log(
  231. "SUCCESS",
  232. "APIS_GET_ALTERNATIVE_ALBUM_SOURCES",
  233. `User "${session.userId}" started getting alternative album spirces for "${albumIds.join(
  234. ", "
  235. )}".`
  236. );
  237. return cb({
  238. status: "success"
  239. });
  240. }
  241. );
  242. }
  243. ),
  244. /**
  245. *
  246. *
  247. * @param session
  248. * @param trackId - the trackId
  249. * @param {Function} cb
  250. */
  251. getAlternativeArtistSourcesForArtists: useHasPermission(
  252. "spotify.getAlternativeArtistSourcesForArtists",
  253. function getAlternativeArtistSourcesForArtists(session, artistIds, collectAlternativeArtistSourcesOrigins, cb) {
  254. async.waterfall(
  255. [
  256. next => {
  257. if (!artistIds) {
  258. next("Invalid artistIds provided.");
  259. return;
  260. }
  261. next();
  262. },
  263. next => {
  264. this.keepLongJob();
  265. this.publishProgress({
  266. status: "started",
  267. title: "Getting alternative artist sources for Spotify artists",
  268. message: "Starting up",
  269. id: this.toString()
  270. });
  271. // await CacheModule.runJob(
  272. // "RPUSH",
  273. // { key: `longJobs.${session.userId}`, value: this.toString() },
  274. // this
  275. // );
  276. SpotifyModule.runJob(
  277. "GET_ALTERNATIVE_ARTIST_SOURCES_FOR_ARTISTS",
  278. {
  279. artistIds,
  280. collectAlternativeArtistSourcesOrigins
  281. },
  282. this
  283. )
  284. .then(() => next())
  285. .catch(next);
  286. }
  287. ],
  288. async err => {
  289. if (err) {
  290. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  291. this.log(
  292. "ERROR",
  293. "APIS_GET_ALTERNATIVE_ARTIST_SOURCES",
  294. `Getting alternative artist sources failed for "${artistIds.join(", ")}". "${err}"`
  295. );
  296. return cb({ status: "error", message: err });
  297. }
  298. this.log(
  299. "SUCCESS",
  300. "APIS_GET_ALTERNATIVE_ARTIST_SOURCES",
  301. `User "${session.userId}" started getting alternative artist spirces for "${artistIds.join(
  302. ", "
  303. )}".`
  304. );
  305. return cb({
  306. status: "success"
  307. });
  308. }
  309. );
  310. }
  311. ),
  312. /**
  313. * Joins a room
  314. *
  315. * @param {object} session - user session
  316. * @param {string} room - the room to join
  317. * @param {Function} cb - callback
  318. */
  319. joinRoom(session, room, cb) {
  320. const roomName = room.split(".")[0];
  321. // const roomId = room.split(".")[1];
  322. const rooms = {
  323. home: null,
  324. news: null,
  325. profile: null,
  326. "view-media": null,
  327. "manage-station": null,
  328. // "manage-station": "stations.view",
  329. "edit-song": "songs.update",
  330. "edit-songs": "songs.update",
  331. "import-album": "songs.update",
  332. // "edit-playlist": "playlists.update",
  333. "view-report": "reports.get",
  334. "edit-user": "users.update",
  335. "view-api-request": "youtube.getApiRequest",
  336. "view-punishment": "punishments.get"
  337. };
  338. const join = (status, error) => {
  339. if (status === "success")
  340. WSModule.runJob("SOCKET_JOIN_ROOM", {
  341. socketId: session.socketId,
  342. room
  343. })
  344. .then(() => cb({ status: "success", message: "Successfully joined room." }))
  345. .catch(err => join("error", err.message));
  346. else {
  347. this.log("ERROR", `Joining room failed: ${error}`);
  348. cb({ status: "error", message: error });
  349. }
  350. };
  351. if (rooms[roomName] === null) join("success");
  352. else if (rooms[roomName])
  353. hasPermission(rooms[roomName], session)
  354. .then(() => join("success"))
  355. .catch(err => join("error", err));
  356. else join("error", "Room not found");
  357. },
  358. /**
  359. * Leaves a room
  360. *
  361. * @param {object} session - user session
  362. * @param {string} room - the room to leave
  363. * @param {Function} cb - callback
  364. */
  365. leaveRoom(session, room, cb) {
  366. if (
  367. room === "home" ||
  368. room.startsWith("profile.") ||
  369. room.startsWith("manage-station.") ||
  370. room.startsWith("edit-song.") ||
  371. room.startsWith("view-report.") ||
  372. room === "import-album" ||
  373. room === "edit-songs"
  374. ) {
  375. WSModule.runJob("SOCKET_LEAVE_ROOM", {
  376. socketId: session.socketId,
  377. room
  378. })
  379. .then(() => {})
  380. .catch(err => {
  381. this.log("ERROR", `Leaving room failed: ${err.message}`);
  382. });
  383. }
  384. cb({ status: "success", message: "Successfully left room." });
  385. },
  386. /**
  387. * Joins an admin room
  388. *
  389. * @param {object} session - user session
  390. * @param {string} page - the admin room to join
  391. * @param {Function} cb - callback
  392. */
  393. joinAdminRoom(session, page, cb) {
  394. if (
  395. page === "songs" ||
  396. page === "stations" ||
  397. page === "reports" ||
  398. page === "news" ||
  399. page === "playlists" ||
  400. page === "users" ||
  401. page === "statistics" ||
  402. page === "punishments" ||
  403. page === "youtube" ||
  404. page === "youtubeVideos" ||
  405. page === "youtubeChannels" ||
  406. (config.get("experimental.soundcloud") && (page === "soundcloud" || page === "soundcloudTracks")) ||
  407. page === "import" ||
  408. page === "dataRequests"
  409. ) {
  410. hasPermission(`admin.view.${page}`, session.userId)
  411. .then(() =>
  412. WSModule.runJob("SOCKET_LEAVE_ROOMS", { socketId: session.socketId }).then(() => {
  413. WSModule.runJob(
  414. "SOCKET_JOIN_ROOM",
  415. {
  416. socketId: session.socketId,
  417. room: `admin.${page}`
  418. },
  419. this
  420. ).then(() => cb({ status: "success", message: "Successfully joined admin room." }));
  421. })
  422. )
  423. .catch(() => cb({ status: "error", message: "Failed to join admin room." }));
  424. }
  425. },
  426. /**
  427. * Leaves all rooms
  428. *
  429. * @param {object} session - user session
  430. * @param {Function} cb - callback
  431. */
  432. leaveRooms(session, cb) {
  433. WSModule.runJob("SOCKET_LEAVE_ROOMS", { socketId: session.socketId });
  434. cb({ status: "success", message: "Successfully left all rooms." });
  435. },
  436. /**
  437. * Returns current date
  438. *
  439. * @param {object} session - user session
  440. * @param {Function} cb - callback
  441. */
  442. ping(session, cb) {
  443. cb({ status: "success", message: "Successfully pinged.", data: { date: Date.now() } });
  444. }
  445. };