musicbrainz.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import axios from "axios";
  2. import CoreClass from "../core";
  3. import { MUSARE_VERSION } from "..";
  4. class RateLimitter {
  5. /**
  6. * Constructor
  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. * @returns {Promise} - promise that gets resolved when the rate limit allows it
  16. */
  17. continue() {
  18. return new Promise(resolve => {
  19. if (Date.now() - this.dateStarted >= this.timeBetween) resolve();
  20. else setTimeout(resolve, this.dateStarted + this.timeBetween - Date.now());
  21. });
  22. }
  23. /**
  24. * Restart the rate limit timer
  25. */
  26. restart() {
  27. this.dateStarted = Date.now();
  28. }
  29. }
  30. let MusicBrainzModule;
  31. let DBModule;
  32. class _MusicBrainzModule extends CoreClass {
  33. // eslint-disable-next-line require-jsdoc
  34. constructor() {
  35. super("musicbrainz", {
  36. concurrency: 10
  37. });
  38. MusicBrainzModule = this;
  39. }
  40. /**
  41. * Initialises the MusicBrainz module
  42. * @returns {Promise} - returns promise (reject, resolve)
  43. */
  44. async initialize() {
  45. DBModule = this.moduleManager.modules.db;
  46. this.genericApiRequestModel = this.GenericApiRequestModel = await DBModule.runJob("GET_MODEL", {
  47. modelName: "genericApiRequest"
  48. });
  49. this.rateLimiter = new RateLimitter(1100);
  50. this.requestTimeout = 5000;
  51. this.axios = axios.create();
  52. }
  53. /**
  54. * Perform MusicBrainz API call
  55. * @param {object} payload - object that contains the payload
  56. * @param {string} payload.url - request url
  57. * @param {object} payload.params - request parameters
  58. * @returns {Promise} - returns promise (reject, resolve)
  59. */
  60. async API_CALL(payload) {
  61. const { url, params } = payload;
  62. let genericApiRequest = await MusicBrainzModule.GenericApiRequestModel.findOne({
  63. url,
  64. params
  65. });
  66. if (genericApiRequest) {
  67. if (genericApiRequest._doc.responseData.error) throw new Error(genericApiRequest._doc.responseData.error);
  68. return genericApiRequest._doc.responseData;
  69. }
  70. await MusicBrainzModule.rateLimiter.continue();
  71. MusicBrainzModule.rateLimiter.restart();
  72. const responseData = await new Promise((resolve, reject) => {
  73. MusicBrainzModule.axios
  74. .get(url, {
  75. params,
  76. headers: {
  77. "User-Agent": `Musare/${MUSARE_VERSION} ( https://github.com/Musare/Musare )` // TODO set this in accordance to https://musicbrainz.org/doc/MusicBrainz_API/Rate_Limiting
  78. },
  79. timeout: MusicBrainzModule.requestTimeout
  80. })
  81. .then(({ data: responseData }) => {
  82. resolve(responseData);
  83. })
  84. .catch(err => {
  85. if (err.response.status === 404) {
  86. resolve(err.response.data);
  87. } else reject(err);
  88. });
  89. });
  90. if (responseData.error && responseData.error !== "Not Found") throw new Error(responseData.error);
  91. genericApiRequest = new MusicBrainzModule.GenericApiRequestModel({
  92. url,
  93. params,
  94. responseData,
  95. date: Date.now()
  96. });
  97. genericApiRequest.save();
  98. if (responseData.error) throw new Error(responseData.error);
  99. return responseData;
  100. }
  101. }
  102. export default new _MusicBrainzModule();