diff --git a/mb_qol_inline_recording_tracks.changelog.md b/mb_qol_inline_recording_tracks.changelog.md new file mode 100644 index 000000000..b5dd497f2 --- /dev/null +++ b/mb_qol_inline_recording_tracks.changelog.md @@ -0,0 +1 @@ +- **2022.6.17**: Internal changes: more TypeScript migration ([#499](https://github.com/ROpdebee/mb-userscripts/pull/499)) diff --git a/mb_qol_inline_recording_tracks.meta.js b/mb_qol_inline_recording_tracks.meta.js new file mode 100644 index 000000000..e442601cb --- /dev/null +++ b/mb_qol_inline_recording_tracks.meta.js @@ -0,0 +1,17 @@ +// ==UserScript== +// @name MB: QoL: Inline all recordings' tracks on releases +// @description Display all tracks and releases on which a recording appears from the release page. +// @version 2022.6.17 +// @author ROpdebee +// @license MIT; https://opensource.org/licenses/MIT +// @namespace https://github.com/ROpdebee/mb-userscripts +// @homepageURL https://github.com/ROpdebee/mb-userscripts +// @supportURL https://github.com/ROpdebee/mb-userscripts/issues +// @downloadURL https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_qol_inline_recording_tracks.user.js +// @updateURL https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_qol_inline_recording_tracks.meta.js +// @match *://*.musicbrainz.org/release/* +// @exclude *://*.musicbrainz.org/release/add +// @exclude *://*.musicbrainz.org/release/*/edit* +// @run-at document-end +// @grant none +// ==/UserScript== \ No newline at end of file diff --git a/mb_qol_inline_recording_tracks.metadata.json b/mb_qol_inline_recording_tracks.metadata.json new file mode 100644 index 000000000..2eadd9df4 --- /dev/null +++ b/mb_qol_inline_recording_tracks.metadata.json @@ -0,0 +1 @@ +{"version":"2022.6.17"} \ No newline at end of file diff --git a/mb_qol_inline_recording_tracks.user.js b/mb_qol_inline_recording_tracks.user.js new file mode 100644 index 000000000..1e4319c02 --- /dev/null +++ b/mb_qol_inline_recording_tracks.user.js @@ -0,0 +1,142 @@ +// ==UserScript== +// @name MB: QoL: Inline all recordings' tracks on releases +// @description Display all tracks and releases on which a recording appears from the release page. +// @version 2022.6.17 +// @author ROpdebee +// @license MIT; https://opensource.org/licenses/MIT +// @namespace https://github.com/ROpdebee/mb-userscripts +// @homepageURL https://github.com/ROpdebee/mb-userscripts +// @supportURL https://github.com/ROpdebee/mb-userscripts/issues +// @downloadURL https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_qol_inline_recording_tracks.user.js +// @updateURL https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_qol_inline_recording_tracks.meta.js +// @match *://*.musicbrainz.org/release/* +// @exclude *://*.musicbrainz.org/release/add +// @exclude *://*.musicbrainz.org/release/*/edit* +// @run-at document-end +// @grant none +// ==/UserScript== + +// For original source code, see https://github.com/ROpdebee/mb-userscripts/tree/main/src/mb_qol_inline_recording_tracks +(function () { + 'use strict'; + + /* minified: babel helpers, nativejsx, babel-plugin-transform-async-to-promises, p-throttle */ + var appendChildren=function(t,e){(e=Array.isArray(e)?e:[e]).forEach((function(e){e instanceof HTMLElement?t.appendChild(e):(e||"string"==typeof e)&&t.appendChild(document.createTextNode(e.toString()));}));};function ownKeys(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n);}return r}function _objectSpread2(t){for(var e=1;et.length)&&(e=t.length);for(var r=0,n=new Array(e);r=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,l=!0,a=!1;return {s:function(){r=r.call(t);},n:function(){var t=r.next();return l=t.done,t},e:function(t){a=!0,i=t;},f:function(){try{l||null==r.return||r.return();}finally{if(a)throw i}}}}const _Pact=function(){function t(){}return t.prototype.then=function(e,r){const n=new t,o=this.s;if(o){const t=1&o?e:r;if(t){try{_settle(n,1,t(this.v));}catch(i){_settle(n,2,i);}return n}return this}return this.o=function(t){try{const o=t.v;1&t.s?_settle(n,1,e?e(o):o):r?_settle(n,1,r(o)):_settle(n,2,o);}catch(i){_settle(n,2,i);}},n},t}();function _settle(t,e,r){if(!t.s){if(r instanceof _Pact){if(!r.s)return void(r.o=_settle.bind(null,t,e));1&e&&(e=r.s),r=r.v;}if(r&&r.then)return void r.then(_settle.bind(null,t,e),_settle.bind(null,t,2));t.s=e,t.v=r;const n=t.o;n&&n(t);}}function _async(t){return function(){for(var e=[],r=0;r=n?(a.push(t),0):(a.push(n),n-t)}:function(){const t=Date.now();return t-i>r?(l=1,i=t,0):(l{const e=function e(){const r=this;for(var n=arguments.length,i=new Array(n),l=0;l{a=setTimeout((()=>{e(t.apply(this,i)),o.delete(a);}),u()),o.set(a,r);}))};return e.abort=()=>{var t,e=_createForOfIteratorHelper(o.keys());try{for(e.s();!(t=e.n()).done;){const e=t.value;clearTimeout(e),o.get(e)(new AbortError);}}catch(r){e.e(r);}finally{e.f();}o.clear(),a.splice(0,a.length);},e.isEnabled=!0,e}} + + /* minified: lib */ + function splitChunks(e,t){const n=[];for(let r=0;r[t,e]))]}let LogLevel;!function(e){e[e.DEBUG=0]="DEBUG",e[e.LOG=1]="LOG",e[e.INFO=2]="INFO",e[e.SUCCESS=3]="SUCCESS",e[e.WARNING=4]="WARNING",e[e.ERROR=5]="ERROR";}(LogLevel||(LogLevel={}));const HANDLER_NAMES={[LogLevel.DEBUG]:"onDebug",[LogLevel.LOG]:"onLog",[LogLevel.INFO]:"onInfo",[LogLevel.SUCCESS]:"onSuccess",[LogLevel.WARNING]:"onWarn",[LogLevel.ERROR]:"onError"},DEFAULT_OPTIONS={logLevel:LogLevel.INFO,sinks:[]};class Logger{constructor(e){_defineProperty(this,"_configuration",void 0),this._configuration=_objectSpread2(_objectSpread2({},DEFAULT_OPTIONS),e);}fireHandlers(e,t,n){e{const o=r[HANDLER_NAMES[e]];o&&(n?o.call(r,t,n):o.call(r,t));}));}debug(e){this.fireHandlers(LogLevel.DEBUG,e);}log(e){this.fireHandlers(LogLevel.LOG,e);}info(e){this.fireHandlers(LogLevel.INFO,e);}success(e){this.fireHandlers(LogLevel.SUCCESS,e);}warn(e,t){this.fireHandlers(LogLevel.WARNING,e,t);}error(e,t){this.fireHandlers(LogLevel.ERROR,e,t);}configure(e){Object.assign(this._configuration,e);}get configuration(){return this._configuration}addSink(e){this._configuration.sinks.push(e);}}const LOGGER=new Logger;function logFailure(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"An error occurred";e.catch((e=>{LOGGER.error(t,e);}));}class AssertionError extends Error{}function assert(e,t){if(!e)throw new AssertionError(null!=t?t:"Assertion failed")}function assertNonNull(e,t){assert(null!==e,null!=t?t:"Assertion failed: Expected value to be non-null");}function qs(e,t){const n=qsMaybe(e,t);return assertNonNull(n,"Could not find required element"),n}function qsMaybe(e,t){return (null!=t?t:document).querySelector(e)}function qsa(e,t){return [...(null!=t?t:document).querySelectorAll(e)]}function onDocumentLoaded(e){"loading"!==document.readyState?e():document.addEventListener("DOMContentLoaded",e);}function onReactHydrated(e,t){var n;e.getAttributeNames().some((t=>t.startsWith("_reactListening")&&e.getAttribute(t)))?t():"production"===(null===(n=window.__MB__)||void 0===n?void 0:n.DBDefs.GIT_BRANCH)&&"923237cf73"===window.__MB__.DBDefs.GIT_SHA?window.addEventListener("load",t):e.addEventListener("mb-hydration",t);}function safeParseJSON(e,t){try{return JSON.parse(e)}catch(n){if(t)throw new Error("".concat(t,": ").concat(n));return}}var USERSCRIPT_ID="mb_qol_inline_recording_tracks";function insertStylesheet(e,t){if(void 0===t&&(t="ROpdebee_".concat(USERSCRIPT_ID,"_css")),null!==qsMaybe("style#".concat(t)))return;const n=function(){var n=document.createElement("style");return n.setAttribute("id",t),appendChildren(n,e),n}.call(this);document.head.insertAdjacentElement("beforeend",n);}function parseVersion(e){return e.split(".").map((e=>parseInt(e)))}function versionLessThan(e,t){let n=0;for(;nt[n])return !1;n++;}return e.lengthversionLessThan(n,parseVersion(e.versionAdded))));0!==r.length&&showFeatureNotification(t.name,t.version,r.map((e=>e.description)));}function showFeatureNotification(e,t,n){insertStylesheet(css_248z,"ROpdebee_Update_Banner");const r=function(){var o=document.createElement("div");o.setAttribute("class","banner warning-header");var i=document.createElement("p");o.appendChild(i),appendChildren(i,"".concat(e," was updated to v").concat(t,"! "));var s=document.createElement("a");s.setAttribute("href",CHANGELOG_URL),i.appendChild(s);var a=document.createTextNode("See full changelog here");s.appendChild(a),appendChildren(i,". New features since last update:");var l=document.createElement("div");l.setAttribute("class","ROpdebee_feature_list"),o.appendChild(l);var c=document.createElement("ul");l.appendChild(c),appendChildren(c,n.map((e=>function(){var t=document.createElement("li");return appendChildren(t,e),t}.call(this))));var d=document.createElement("button");return d.setAttribute("class","dismiss-banner remove-item icon"),d.setAttribute("data-banner-name","alert"),d.setAttribute("type","button"),d.addEventListener("click",(()=>{r.remove(),localStorage.setItem(LAST_DISPLAYED_KEY,GM.info.script.version);})),o.appendChild(d),o}.call(this);qs("#page").insertAdjacentElement("beforebegin",r);} + + if (document.location.hostname === 'musicbrainz.org' || document.location.hostname.endsWith('.musicbrainz.org')) { + onDocumentLoaded(maybeDisplayNewFeatures); + } + + const loadRecordingInfo = _async(function (rids) { + const query = rids.map(rid => 'rid:' + rid).join(' OR '); + const url = document.location.origin + '/ws/2/recording?fmt=json&query=' + query; + return _await(throttledFetch(url), function (resp) { + return _await(resp.text(), function (_resp$text) { + const respContent = safeParseJSON(_resp$text, 'Could not parse API response'); + const perRecId = new Map(); + respContent.recordings.forEach(rec => { + perRecId.set(rec.id, rec); + }); + return perRecId; + }); + }); + }); + const throttledFetch = pThrottle({ + limit: 1, + interval: 500 + })(fetch); + function getTrackIndexElement(track, mediumPosition) { + return function () { + var $$a = document.createElement('a'); + $$a.setAttribute('href', '/track/'.concat(track.id)); + var $$b = document.createTextNode('#'); + $$a.appendChild($$b); + appendChildren($$a, mediumPosition.toString()); + var $$d = document.createTextNode('.'); + $$a.appendChild($$d); + appendChildren($$a, track.number); + return $$a; + }.call(this); + } + function getTrackIndexElements(media) { + const tracks = media.flatMap(medium => medium.track.map(track => { + return getTrackIndexElement(track, medium.position); + })); + return insertBetween(tracks, ', '); + } + function getReleaseNameElement(release) { + return function () { + var $$f = document.createElement('a'); + $$f.setAttribute('href', '/release/'.concat(release.id)); + appendChildren($$f, release.title); + return $$f; + }.call(this); + } + function formatRow(release) { + return [ + getReleaseNameElement(release), + ' (', + ...getTrackIndexElements(release.media), + ')' + ]; + } + function insertRows(recordingTd, recordingInfo) { + const rowElements = recordingInfo.releases.map(release => formatRow(release)).map(row => function () { + var $$h = document.createElement('dl'); + $$h.setAttribute('class', 'ars'); + var $$i = document.createElement('dt'); + $$h.appendChild($$i); + var $$j = document.createTextNode('appears on:'); + $$i.appendChild($$j); + var $$k = document.createElement('dd'); + $$h.appendChild($$k); + appendChildren($$k, row); + return $$h; + }.call(this)); + qs('div.ars', recordingTd).insertAdjacentElement('beforebegin', function () { + var $$m = document.createElement('div'); + $$m.setAttribute('class', 'ars ROpdebee_inline_tracks'); + appendChildren($$m, rowElements); + return $$m; + }.call(this)); + } + function loadAndInsert() { + const recAnchors = qsa('table.medium td > a[href^="/recording/"]:first-child, table.medium td > span:first-child > a[href^="/recording/"]:first-child'); + const todo = recAnchors.map(a => [ + a.closest('td'), + a.href.split('/recording/')[1] + ]).filter(_ref => { + let _ref2 = _slicedToArray(_ref, 1), td = _ref2[0]; + return qsMaybe('div.ars.ROpdebee_inline_tracks', td) === null; + }); + const chunks = splitChunks(todo, 20); + logFailure(Promise.all(chunks.map(_async(function (chunk) { + return _await(loadRecordingInfo(chunk.map(_ref3 => { + let _ref4 = _slicedToArray(_ref3, 2), recId = _ref4[1]; + return recId; + })), function (recInfo) { + chunk.forEach(_ref5 => { + let _ref6 = _slicedToArray(_ref5, 2), td = _ref6[0], recId = _ref6[1]; + insertRows(td, recInfo.get(recId)); + }); + }); + })))); + } + onReactHydrated(qs('.tracklist-and-credits'), function () { + var _qs$firstChild; + const button = function () { + var $$o = document.createElement('button'); + $$o.setAttribute('class', 'btn-link'); + $$o.setAttribute('type', 'button'); + $$o.addEventListener('click', loadAndInsert); + var $$p = document.createTextNode('\n Display track info for recordings\n '); + $$o.appendChild($$p); + return $$o; + }.call(this); + (_qs$firstChild = qs('span#medium-toolbox').firstChild) === null || _qs$firstChild === void 0 ? void 0 : _qs$firstChild.before(button, ' | '); + }); + +})();