musicbrainz.js 5.2 KB

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