musicbrainz.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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. });
  40. MusicBrainzModule = this;
  41. }
  42. /**
  43. * Initialises the MusicBrainz module
  44. *
  45. * @returns {Promise} - returns promise (reject, resolve)
  46. */
  47. async initialize() {
  48. DBModule = this.moduleManager.modules.db;
  49. this.genericApiRequestModel = this.GenericApiRequestModel = await DBModule.runJob("GET_MODEL", {
  50. modelName: "genericApiRequest"
  51. });
  52. this.rateLimiter = new RateLimitter(1100);
  53. this.requestTimeout = 5000;
  54. this.axios = axios.create();
  55. }
  56. /**
  57. * Perform MusicBrainz API call
  58. *
  59. * @param {object} payload - object that contains the payload
  60. * @param {string} payload.url - request url
  61. * @param {object} payload.params - request parameters
  62. * @returns {Promise} - returns promise (reject, resolve)
  63. */
  64. async API_CALL(payload) {
  65. const { url, params } = payload;
  66. let genericApiRequest = await MusicBrainzModule.GenericApiRequestModel.findOne({
  67. url,
  68. params
  69. });
  70. if (genericApiRequest) {
  71. if (genericApiRequest._doc.responseData.error) throw new Error(genericApiRequest._doc.responseData.error);
  72. return genericApiRequest._doc.responseData;
  73. }
  74. await MusicBrainzModule.rateLimiter.continue();
  75. MusicBrainzModule.rateLimiter.restart();
  76. const responseData = await new Promise((resolve, reject) => {
  77. MusicBrainzModule.axios
  78. .get(url, {
  79. params,
  80. headers: {
  81. "User-Agent": `Musare/${MUSARE_VERSION} ( https://github.com/Musare/Musare )` // TODO set this in accordance to https://musicbrainz.org/doc/MusicBrainz_API/Rate_Limiting
  82. },
  83. timeout: MusicBrainzModule.requestTimeout
  84. })
  85. .then(({ data: responseData }) => {
  86. resolve(responseData);
  87. })
  88. .catch(err => {
  89. if (err.response.status === 404) {
  90. resolve(err.response.data);
  91. } else reject(err);
  92. });
  93. });
  94. if (responseData.error && responseData.error !== "Not Found") throw new Error(responseData.error);
  95. genericApiRequest = new MusicBrainzModule.GenericApiRequestModel({
  96. url,
  97. params,
  98. responseData,
  99. date: Date.now()
  100. });
  101. genericApiRequest.save();
  102. if (responseData.error) throw new Error(responseData.error);
  103. return responseData;
  104. }
  105. }
  106. export default new _MusicBrainzModule();