musicbrainz.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import axios from "axios";
  2. import CoreClass from "../core";
  3. class RateLimitter {
  4. /**
  5. * Constructor
  6. *
  7. * @param {number} timeBetween - The time between each allowed MusicBrainz request
  8. */
  9. constructor(timeBetween) {
  10. this.dateStarted = Date.now();
  11. this.timeBetween = timeBetween;
  12. }
  13. /**
  14. * Returns a promise that resolves whenever the ratelimit of a MusicBrainz request is done
  15. *
  16. * @returns {Promise} - promise that gets resolved when the rate limit allows it
  17. */
  18. continue() {
  19. return new Promise(resolve => {
  20. if (Date.now() - this.dateStarted >= this.timeBetween) resolve();
  21. else setTimeout(resolve, this.dateStarted + this.timeBetween - Date.now());
  22. });
  23. }
  24. /**
  25. * Restart the rate limit timer
  26. */
  27. restart() {
  28. this.dateStarted = Date.now();
  29. }
  30. }
  31. let MusicBrainzModule;
  32. let DBModule;
  33. class _MusicBrainzModule extends CoreClass {
  34. // eslint-disable-next-line require-jsdoc
  35. constructor() {
  36. super("musicbrainz", {
  37. concurrency: 10
  38. // priorities: {
  39. // GET_PLAYLIST: 11
  40. // }
  41. });
  42. MusicBrainzModule = this;
  43. }
  44. /**
  45. * Initialises the activities module
  46. *
  47. * @returns {Promise} - returns promise (reject, resolve)
  48. */
  49. async initialize() {
  50. DBModule = this.moduleManager.modules.db;
  51. this.genericApiRequestModel = this.GenericApiRequestModel = await DBModule.runJob("GET_MODEL", {
  52. modelName: "genericApiRequest"
  53. });
  54. // this.youtubeVideoModel = this.YoutubeVideoModel = await DBModule.runJob("GET_MODEL", {
  55. // modelName: "youtubeVideo"
  56. // });
  57. // return new Promise(resolve => {
  58. // CacheModule.runJob("SUB", {
  59. // channel: "youtube.removeYoutubeApiRequest",
  60. // cb: requestId => {
  61. // WSModule.runJob("EMIT_TO_ROOM", {
  62. // room: `view-api-request.${requestId}`,
  63. // args: ["event:youtubeApiRequest.removed"]
  64. // });
  65. // WSModule.runJob("EMIT_TO_ROOM", {
  66. // room: "admin.youtube",
  67. // args: ["event:admin.youtubeApiRequest.removed", { data: { requestId } }]
  68. // });
  69. // }
  70. // });
  71. // CacheModule.runJob("SUB", {
  72. // channel: "youtube.removeVideos",
  73. // cb: videoIds => {
  74. // const videos = Array.isArray(videoIds) ? videoIds : [videoIds];
  75. // videos.forEach(videoId => {
  76. // WSModule.runJob("EMIT_TO_ROOM", {
  77. // room: `view-youtube-video.${videoId}`,
  78. // args: ["event:youtubeVideo.removed"]
  79. // });
  80. // WSModule.runJob("EMIT_TO_ROOM", {
  81. // room: "admin.youtubeVideos",
  82. // args: ["event:admin.youtubeVideo.removed", { data: { videoId } }]
  83. // });
  84. // WSModule.runJob("EMIT_TO_ROOMS", {
  85. // rooms: ["import-album", "edit-songs"],
  86. // args: ["event:admin.youtubeVideo.removed", { videoId }]
  87. // });
  88. // });
  89. // }
  90. // });
  91. this.rateLimiter = new RateLimitter(1100);
  92. // this.requestTimeout = config.get("apis.youtube.requestTimeout");
  93. this.requestTimeout = 5000;
  94. this.axios = axios.create();
  95. // this.axios.defaults.raxConfig = {
  96. // instance: this.axios,
  97. // retry: config.get("apis.youtube.retryAmount"),
  98. // noResponseRetries: config.get("apis.youtube.retryAmount")
  99. // };
  100. // rax.attach(this.axios);
  101. // this.youtubeApiRequestModel
  102. // .find(
  103. // { date: { $gte: new Date() - 2 * 24 * 60 * 60 * 1000 } },
  104. // { date: true, quotaCost: true, _id: false }
  105. // )
  106. // .sort({ date: 1 })
  107. // .exec((err, youtubeApiRequests) => {
  108. // if (err) console.log("Couldn't load YouTube API requests.");
  109. // else {
  110. // this.apiCalls = youtubeApiRequests;
  111. // resolve();
  112. // }
  113. // });
  114. // resolve();
  115. // });
  116. }
  117. /**
  118. * Perform MusicBrainz API call
  119. *
  120. * @param {object} payload - object that contains the payload
  121. * @param {object} payload.url - request url
  122. * @param {object} payload.params - request parameters
  123. * @param {object} payload.quotaCost - request quotaCost
  124. * @returns {Promise} - returns promise (reject, resolve)
  125. */
  126. async API_CALL(payload) {
  127. const { url, params } = payload;
  128. let genericApiRequest = await MusicBrainzModule.GenericApiRequestModel.findOne({
  129. url,
  130. params
  131. });
  132. if (genericApiRequest) {
  133. if (genericApiRequest._doc.responseData.error) throw new Error(genericApiRequest._doc.responseData.error);
  134. return genericApiRequest._doc.responseData;
  135. }
  136. await MusicBrainzModule.rateLimiter.continue();
  137. MusicBrainzModule.rateLimiter.restart();
  138. const responseData = await new Promise((resolve, reject) => {
  139. MusicBrainzModule.axios
  140. .get(url, {
  141. params,
  142. headers: {
  143. "User-Agent": "Musare/3.9.0-fork ( https://git.kvos.dev/kris/MusareFork )" // TODO set this in accordance to https://musicbrainz.org/doc/MusicBrainz_API/Rate_Limiting
  144. },
  145. timeout: MusicBrainzModule.requestTimeout
  146. })
  147. .then(({ data: responseData }) => {
  148. resolve(responseData);
  149. })
  150. .catch(err => {
  151. if (err.response.status === 404) {
  152. resolve(err.response.data);
  153. } else reject(err);
  154. });
  155. });
  156. if (responseData.error && responseData.error !== "Not Found") throw new Error(responseData.error);
  157. genericApiRequest = new MusicBrainzModule.GenericApiRequestModel({
  158. url,
  159. params,
  160. responseData,
  161. date: Date.now()
  162. });
  163. genericApiRequest.save();
  164. if (responseData.error) throw new Error(responseData.error);
  165. return responseData;
  166. }
  167. }
  168. export default new _MusicBrainzModule();