ratings.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. import async from "async";
  2. import { isAdminRequired, isLoginRequired } from "./hooks";
  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 SongsModule = moduleManager.modules.songs;
  10. const ActivitiesModule = moduleManager.modules.activities;
  11. const RatingsModule = moduleManager.modules.ratings;
  12. CacheModule.runJob("SUB", {
  13. channel: "ratings.like",
  14. cb: data => {
  15. WSModule.runJob("EMIT_TO_ROOM", {
  16. room: `song.${data.youtubeId}`,
  17. args: [
  18. "event:ratings.liked",
  19. {
  20. data: { youtubeId: data.youtubeId, likes: data.likes, dislikes: data.dislikes }
  21. }
  22. ]
  23. });
  24. WSModule.runJob("SOCKETS_FROM_USER", { userId: data.userId }).then(sockets => {
  25. sockets.forEach(socket => {
  26. socket.dispatch("event:ratings.updated", {
  27. data: {
  28. youtubeId: data.youtubeId,
  29. liked: true,
  30. disliked: false
  31. }
  32. });
  33. });
  34. });
  35. }
  36. });
  37. CacheModule.runJob("SUB", {
  38. channel: "ratings.dislike",
  39. cb: data => {
  40. WSModule.runJob("EMIT_TO_ROOM", {
  41. room: `song.${data.youtubeId}`,
  42. args: [
  43. "event:ratings.disliked",
  44. {
  45. data: { youtubeId: data.youtubeId, likes: data.likes, dislikes: data.dislikes }
  46. }
  47. ]
  48. });
  49. WSModule.runJob("SOCKETS_FROM_USER", { userId: data.userId }).then(sockets => {
  50. sockets.forEach(socket => {
  51. socket.dispatch("event:ratings.updated", {
  52. data: {
  53. youtubeId: data.youtubeId,
  54. liked: false,
  55. disliked: true
  56. }
  57. });
  58. });
  59. });
  60. }
  61. });
  62. CacheModule.runJob("SUB", {
  63. channel: "ratings.unlike",
  64. cb: data => {
  65. WSModule.runJob("EMIT_TO_ROOM", {
  66. room: `song.${data.youtubeId}`,
  67. args: [
  68. "event:ratings.unliked",
  69. {
  70. data: { youtubeId: data.youtubeId, likes: data.likes, dislikes: data.dislikes }
  71. }
  72. ]
  73. });
  74. WSModule.runJob("SOCKETS_FROM_USER", { userId: data.userId }).then(sockets => {
  75. sockets.forEach(socket => {
  76. socket.dispatch("event:ratings.updated", {
  77. data: {
  78. youtubeId: data.youtubeId,
  79. liked: false,
  80. disliked: false
  81. }
  82. });
  83. });
  84. });
  85. }
  86. });
  87. CacheModule.runJob("SUB", {
  88. channel: "ratings.undislike",
  89. cb: data => {
  90. WSModule.runJob("EMIT_TO_ROOM", {
  91. room: `song.${data.youtubeId}`,
  92. args: [
  93. "event:ratings.undisliked",
  94. {
  95. data: { youtubeId: data.youtubeId, likes: data.likes, dislikes: data.dislikes }
  96. }
  97. ]
  98. });
  99. WSModule.runJob("SOCKETS_FROM_USER", { userId: data.userId }).then(sockets => {
  100. sockets.forEach(socket => {
  101. socket.dispatch("event:ratings.updated", {
  102. data: {
  103. youtubeId: data.youtubeId,
  104. liked: false,
  105. disliked: false
  106. }
  107. });
  108. });
  109. });
  110. }
  111. });
  112. export default {
  113. /**
  114. * Recalculates all ratings
  115. *
  116. * @param {object} session - the session object automatically added by the websocket
  117. * @param cb
  118. */
  119. recalculateAll: isAdminRequired(async function recalculateAll(session, cb) {
  120. async.waterfall(
  121. [
  122. next => {
  123. RatingsModule.runJob("RECALCULATE_ALL_RATINGS", {}, this)
  124. .then(() => {
  125. next();
  126. })
  127. .catch(err => {
  128. next(err);
  129. });
  130. }
  131. ],
  132. async err => {
  133. if (err) {
  134. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  135. this.log("ERROR", "RATINGS_RECALCULATE_ALL", `Failed to recalculate all ratings. "${err}"`);
  136. return cb({ status: "error", message: err });
  137. }
  138. this.log("SUCCESS", "RATINGS_RECALCULATE_ALL", `Recalculated all ratings successfully.`);
  139. return cb({ status: "success", message: "Successfully recalculated all ratings." });
  140. }
  141. );
  142. }),
  143. /**
  144. * Like
  145. *
  146. * @param session
  147. * @param youtubeId - the youtube id
  148. * @param cb
  149. */
  150. like: isLoginRequired(async function like(session, youtubeId, cb) {
  151. const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);
  152. async.waterfall(
  153. [
  154. next => {
  155. SongsModule.runJob(
  156. "ENSURE_SONG_EXISTS_BY_YOUTUBE_ID",
  157. {
  158. youtubeId
  159. },
  160. this
  161. )
  162. .then(response => {
  163. const { song } = response;
  164. const { _id, title, artists, thumbnail, duration, verified } = song;
  165. next(null, {
  166. _id,
  167. youtubeId,
  168. title,
  169. artists,
  170. thumbnail,
  171. duration,
  172. verified
  173. });
  174. })
  175. .catch(next);
  176. },
  177. (song, next) => userModel.findOne({ _id: session.userId }, (err, user) => next(err, song, user)),
  178. (song, user, next) => {
  179. if (!user) return next("User does not exist.");
  180. return this.module
  181. .runJob(
  182. "RUN_ACTION2",
  183. {
  184. session,
  185. namespace: "playlists",
  186. action: "removeSongFromPlaylist",
  187. args: [youtubeId, user.dislikedSongsPlaylist]
  188. },
  189. this
  190. )
  191. .then(res => {
  192. if (res.status === "error" && res.message !== "Song wasn't in playlist.")
  193. return next("Unable to remove song from the 'Disliked Songs' playlist.");
  194. return next(null, song, user.likedSongsPlaylist);
  195. })
  196. .catch(err => next(err));
  197. },
  198. (song, likedSongsPlaylist, next) =>
  199. this.module
  200. .runJob(
  201. "RUN_ACTION2",
  202. {
  203. session,
  204. namespace: "playlists",
  205. action: "addSongToPlaylist",
  206. args: [false, youtubeId, likedSongsPlaylist]
  207. },
  208. this
  209. )
  210. .then(res => {
  211. if (res.status === "error") {
  212. if (res.message === "That song is already in the playlist")
  213. return next("You have already liked this song.");
  214. return next("Unable to add song to the 'Liked Songs' playlist.");
  215. }
  216. return next(null, song);
  217. })
  218. .catch(err => next(err)),
  219. (song, next) => {
  220. RatingsModule.runJob("RECALCULATE_RATINGS", { youtubeId })
  221. .then(ratings => next(null, song, ratings))
  222. .catch(err => next(err));
  223. }
  224. ],
  225. async (err, song, ratings) => {
  226. if (err) {
  227. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  228. this.log(
  229. "ERROR",
  230. "RATINGS_LIKE",
  231. `User "${session.userId}" failed to like song ${youtubeId}. "${err}"`
  232. );
  233. return cb({ status: "error", message: err });
  234. }
  235. const { likes, dislikes } = ratings;
  236. if (song._id) SongsModule.runJob("UPDATE_SONG", { songId: song._id });
  237. CacheModule.runJob("PUB", {
  238. channel: "ratings.like",
  239. value: JSON.stringify({
  240. youtubeId,
  241. userId: session.userId,
  242. likes,
  243. dislikes
  244. })
  245. });
  246. ActivitiesModule.runJob("ADD_ACTIVITY", {
  247. userId: session.userId,
  248. type: "song__like",
  249. payload: {
  250. message: `Liked song <youtubeId>${song.title} by ${song.artists.join(", ")}</youtubeId>`,
  251. youtubeId,
  252. thumbnail: song.thumbnail
  253. }
  254. });
  255. return cb({
  256. status: "success",
  257. message: "You have successfully liked this song."
  258. });
  259. }
  260. );
  261. }),
  262. /**
  263. * Dislike
  264. *
  265. * @param session
  266. * @param youtubeId - the youtube id
  267. * @param cb
  268. */
  269. dislike: isLoginRequired(async function dislike(session, youtubeId, cb) {
  270. const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);
  271. async.waterfall(
  272. [
  273. next => {
  274. SongsModule.runJob(
  275. "ENSURE_SONG_EXISTS_BY_YOUTUBE_ID",
  276. {
  277. youtubeId
  278. },
  279. this
  280. )
  281. .then(response => {
  282. const { song } = response;
  283. const { _id, title, artists, thumbnail, duration, verified } = song;
  284. next(null, {
  285. _id,
  286. youtubeId,
  287. title,
  288. artists,
  289. thumbnail,
  290. duration,
  291. verified
  292. });
  293. })
  294. .catch(next);
  295. },
  296. (song, next) => userModel.findOne({ _id: session.userId }, (err, user) => next(err, song, user)),
  297. (song, user, next) => {
  298. if (!user) return next("User does not exist.");
  299. return this.module
  300. .runJob(
  301. "RUN_ACTION2",
  302. {
  303. session,
  304. namespace: "playlists",
  305. action: "removeSongFromPlaylist",
  306. args: [youtubeId, user.likedSongsPlaylist]
  307. },
  308. this
  309. )
  310. .then(res => {
  311. if (res.status === "error" && res.message !== "Song wasn't in playlist.")
  312. return next("Unable to remove song from the 'Liked Songs' playlist.");
  313. return next(null, song, user.dislikedSongsPlaylist);
  314. })
  315. .catch(err => next(err));
  316. },
  317. (song, dislikedSongsPlaylist, next) =>
  318. this.module
  319. .runJob(
  320. "RUN_ACTION2",
  321. {
  322. session,
  323. namespace: "playlists",
  324. action: "addSongToPlaylist",
  325. args: [false, youtubeId, dislikedSongsPlaylist]
  326. },
  327. this
  328. )
  329. .then(res => {
  330. if (res.status === "error") {
  331. if (res.message === "That song is already in the playlist")
  332. return next("You have already disliked this song.");
  333. return next("Unable to add song to the 'Disliked Songs' playlist.");
  334. }
  335. return next(null, song);
  336. })
  337. .catch(err => next(err)),
  338. (song, next) => {
  339. RatingsModule.runJob("RECALCULATE_RATINGS", { youtubeId })
  340. .then(ratings => next(null, song, ratings))
  341. .catch(err => next(err));
  342. }
  343. ],
  344. async (err, song, ratings) => {
  345. if (err) {
  346. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  347. this.log(
  348. "ERROR",
  349. "RATINGS_DISLIKE",
  350. `User "${session.userId}" failed to dislike song ${youtubeId}. "${err}"`
  351. );
  352. return cb({ status: "error", message: err });
  353. }
  354. const { likes, dislikes } = ratings;
  355. if (song._id) SongsModule.runJob("UPDATE_SONG", { songId: song._id });
  356. CacheModule.runJob("PUB", {
  357. channel: "ratings.dislike",
  358. value: JSON.stringify({
  359. youtubeId,
  360. userId: session.userId,
  361. likes,
  362. dislikes
  363. })
  364. });
  365. ActivitiesModule.runJob("ADD_ACTIVITY", {
  366. userId: session.userId,
  367. type: "song__dislike",
  368. payload: {
  369. message: `Disliked song <youtubeId>${song.title} by ${song.artists.join(", ")}</youtubeId>`,
  370. youtubeId,
  371. thumbnail: song.thumbnail
  372. }
  373. });
  374. return cb({
  375. status: "success",
  376. message: "You have successfully disliked this song."
  377. });
  378. }
  379. );
  380. }),
  381. /**
  382. * Undislike
  383. *
  384. * @param session
  385. * @param youtubeId - the youtube id
  386. * @param cb
  387. */
  388. undislike: isLoginRequired(async function undislike(session, youtubeId, cb) {
  389. const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);
  390. async.waterfall(
  391. [
  392. next => {
  393. SongsModule.runJob(
  394. "ENSURE_SONG_EXISTS_BY_YOUTUBE_ID",
  395. {
  396. youtubeId
  397. },
  398. this
  399. )
  400. .then(response => {
  401. const { song } = response;
  402. const { _id, title, artists, thumbnail, duration, verified } = song;
  403. next(null, {
  404. _id,
  405. youtubeId,
  406. title,
  407. artists,
  408. thumbnail,
  409. duration,
  410. verified
  411. });
  412. })
  413. .catch(next);
  414. },
  415. (song, next) => userModel.findOne({ _id: session.userId }, (err, user) => next(err, song, user)),
  416. (song, user, next) => {
  417. if (!user) return next("User does not exist.");
  418. return this.module
  419. .runJob(
  420. "RUN_ACTION2",
  421. {
  422. session,
  423. namespace: "playlists",
  424. action: "removeSongFromPlaylist",
  425. args: [youtubeId, user.dislikedSongsPlaylist]
  426. },
  427. this
  428. )
  429. .then(res => {
  430. if (res.status === "error")
  431. return next("Unable to remove song from the 'Disliked Songs' playlist.");
  432. return next(null, song, user.likedSongsPlaylist);
  433. })
  434. .catch(err => next(err));
  435. },
  436. (song, likedSongsPlaylist, next) => {
  437. this.module
  438. .runJob(
  439. "RUN_ACTION2",
  440. {
  441. session,
  442. namespace: "playlists",
  443. action: "removeSongFromPlaylist",
  444. args: [youtubeId, likedSongsPlaylist]
  445. },
  446. this
  447. )
  448. .then(res => {
  449. if (res.status === "error" && res.message !== "Song wasn't in playlist.")
  450. return next("Unable to remove song from the 'Liked Songs' playlist.");
  451. return next(null, song);
  452. })
  453. .catch(err => next(err));
  454. },
  455. (song, next) => {
  456. RatingsModule.runJob("RECALCULATE_RATINGS", { youtubeId })
  457. .then(ratings => next(null, song, ratings))
  458. .catch(err => next(err));
  459. }
  460. ],
  461. async (err, song, ratings) => {
  462. if (err) {
  463. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  464. this.log(
  465. "ERROR",
  466. "RATINGS_UNDISLIKE",
  467. `User "${session.userId}" failed to undislike song ${youtubeId}. "${err}"`
  468. );
  469. return cb({ status: "error", message: err });
  470. }
  471. const { likes, dislikes } = ratings;
  472. if (song._id) SongsModule.runJob("UPDATE_SONG", { songId: song._id });
  473. CacheModule.runJob("PUB", {
  474. channel: "ratings.undislike",
  475. value: JSON.stringify({
  476. youtubeId,
  477. userId: session.userId,
  478. likes,
  479. dislikes
  480. })
  481. });
  482. ActivitiesModule.runJob("ADD_ACTIVITY", {
  483. userId: session.userId,
  484. type: "song__undislike",
  485. payload: {
  486. message: `Removed <youtubeId>${song.title} by ${song.artists.join(
  487. ", "
  488. )}</youtubeId> from your Disliked Songs`,
  489. youtubeId,
  490. thumbnail: song.thumbnail
  491. }
  492. });
  493. return cb({
  494. status: "success",
  495. message: "You have successfully undisliked this song."
  496. });
  497. }
  498. );
  499. }),
  500. /**
  501. * Unlike
  502. *
  503. * @param session
  504. * @param youtubeId - the youtube id
  505. * @param cb
  506. */
  507. unlike: isLoginRequired(async function unlike(session, youtubeId, cb) {
  508. const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);
  509. async.waterfall(
  510. [
  511. next => {
  512. SongsModule.runJob(
  513. "ENSURE_SONG_EXISTS_BY_YOUTUBE_ID",
  514. {
  515. youtubeId
  516. },
  517. this
  518. )
  519. .then(response => {
  520. const { song } = response;
  521. const { _id, title, artists, thumbnail, duration, verified } = song;
  522. next(null, {
  523. _id,
  524. youtubeId,
  525. title,
  526. artists,
  527. thumbnail,
  528. duration,
  529. verified
  530. });
  531. })
  532. .catch(next);
  533. },
  534. (song, next) => userModel.findOne({ _id: session.userId }, (err, user) => next(err, song, user)),
  535. (song, user, next) => {
  536. if (!user) return next("User does not exist.");
  537. return this.module
  538. .runJob(
  539. "RUN_ACTION2",
  540. {
  541. session,
  542. namespace: "playlists",
  543. action: "removeSongFromPlaylist",
  544. args: [youtubeId, user.dislikedSongsPlaylist]
  545. },
  546. this
  547. )
  548. .then(res => {
  549. if (res.status === "error" && res.message !== "Song wasn't in playlist.")
  550. return next("Unable to remove song from the 'Disliked Songs' playlist.");
  551. return next(null, song, user.likedSongsPlaylist);
  552. })
  553. .catch(err => next(err));
  554. },
  555. (song, likedSongsPlaylist, next) => {
  556. this.module
  557. .runJob(
  558. "RUN_ACTION2",
  559. {
  560. session,
  561. namespace: "playlists",
  562. action: "removeSongFromPlaylist",
  563. args: [youtubeId, likedSongsPlaylist]
  564. },
  565. this
  566. )
  567. .then(res => {
  568. if (res.status === "error")
  569. return next("Unable to remove song from the 'Liked Songs' playlist.");
  570. return next(null, song);
  571. })
  572. .catch(err => next(err));
  573. },
  574. (song, next) => {
  575. RatingsModule.runJob("RECALCULATE_RATINGS", { youtubeId })
  576. .then(ratings => next(null, song, ratings))
  577. .catch(err => next(err));
  578. }
  579. ],
  580. async (err, song, ratings) => {
  581. if (err) {
  582. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  583. this.log(
  584. "ERROR",
  585. "RATINGS_UNLIKE",
  586. `User "${session.userId}" failed to unlike song ${youtubeId}. "${err}"`
  587. );
  588. return cb({ status: "error", message: err });
  589. }
  590. const { likes, dislikes } = ratings;
  591. if (song._id) SongsModule.runJob("UPDATE_SONG", { songId: song._id });
  592. CacheModule.runJob("PUB", {
  593. channel: "ratings.unlike",
  594. value: JSON.stringify({
  595. youtubeId,
  596. userId: session.userId,
  597. likes,
  598. dislikes
  599. })
  600. });
  601. ActivitiesModule.runJob("ADD_ACTIVITY", {
  602. userId: session.userId,
  603. type: "song__unlike",
  604. payload: {
  605. message: `Removed <youtubeId>${song.title} by ${song.artists.join(
  606. ", "
  607. )}</youtubeId> from your Liked Songs`,
  608. youtubeId,
  609. thumbnail: song.thumbnail
  610. }
  611. });
  612. return cb({
  613. status: "success",
  614. message: "You have successfully unliked this song."
  615. });
  616. }
  617. );
  618. }),
  619. /**
  620. * Get ratings
  621. *
  622. * @param session
  623. * @param youtubeId - the youtube id
  624. * @param cb
  625. */
  626. getRatings: isLoginRequired(async function getRatings(session, youtubeId, cb) {
  627. async.waterfall(
  628. [
  629. next => {
  630. RatingsModule.runJob("GET_RATINGS", { youtubeId }, this)
  631. .then(res => next(null, res.ratings))
  632. .catch(next);
  633. },
  634. (ratings, next) => {
  635. next(null, {
  636. likes: ratings.likes,
  637. dislikes: ratings.dislikes
  638. });
  639. }
  640. ],
  641. async (err, ratings) => {
  642. if (err) {
  643. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  644. this.log(
  645. "ERROR",
  646. "RATINGS_GET_RATINGS",
  647. `User "${session.userId}" failed to get ratings for ${youtubeId}. "${err}"`
  648. );
  649. return cb({ status: "error", message: err });
  650. }
  651. const { likes, dislikes } = ratings;
  652. return cb({
  653. status: "success",
  654. data: {
  655. likes,
  656. dislikes
  657. }
  658. });
  659. }
  660. );
  661. }),
  662. /**
  663. * Gets user's own ratings
  664. *
  665. * @param session
  666. * @param youtubeId - the youtube id
  667. * @param cb
  668. */
  669. getOwnRatings: isLoginRequired(async function getOwnRatings(session, youtubeId, cb) {
  670. const playlistModel = await DBModule.runJob("GET_MODEL", { modelName: "playlist" }, this);
  671. async.waterfall(
  672. [
  673. next => {
  674. SongsModule.runJob(
  675. "ENSURE_SONG_EXISTS_BY_YOUTUBE_ID",
  676. {
  677. youtubeId
  678. },
  679. this
  680. )
  681. .then(() => next())
  682. .catch(next);
  683. },
  684. next =>
  685. playlistModel.findOne(
  686. { createdBy: session.userId, displayName: "Liked Songs" },
  687. (err, playlist) => {
  688. if (err) return next(err);
  689. if (!playlist) return next("'Liked Songs' playlist does not exist.");
  690. let isLiked = false;
  691. Object.values(playlist.songs).forEach(song => {
  692. // song is found in 'liked songs' playlist
  693. if (song.youtubeId === youtubeId) isLiked = true;
  694. });
  695. return next(null, isLiked);
  696. }
  697. ),
  698. (isLiked, next) =>
  699. playlistModel.findOne(
  700. { createdBy: session.userId, displayName: "Disliked Songs" },
  701. (err, playlist) => {
  702. if (err) return next(err);
  703. if (!playlist) return next("'Disliked Songs' playlist does not exist.");
  704. const ratings = { isLiked, isDisliked: false };
  705. Object.values(playlist.songs).forEach(song => {
  706. // song is found in 'disliked songs' playlist
  707. if (song.youtubeId === youtubeId) ratings.isDisliked = true;
  708. });
  709. return next(null, ratings);
  710. }
  711. )
  712. ],
  713. async (err, ratings) => {
  714. if (err) {
  715. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  716. this.log(
  717. "ERROR",
  718. "RATINGS_GET_OWN_RATINGS",
  719. `User "${session.userId}" failed to get ratings for ${youtubeId}. "${err}"`
  720. );
  721. return cb({ status: "error", message: err });
  722. }
  723. const { isLiked, isDisliked } = ratings;
  724. return cb({
  725. status: "success",
  726. data: {
  727. youtubeId,
  728. liked: isLiked,
  729. disliked: isDisliked
  730. }
  731. });
  732. }
  733. );
  734. })
  735. };