// ==UserScript== // @name MB: QoL: Seed the batch recording comments script // @description Seed the recording comments for the batch recording comments userscripts with live and DJ-mix data. // @version 2022.8.8 // @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_seed_recording_disambiguation.user.js // @updateURL https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_qol_seed_recording_disambiguation.meta.js // @include /^https?://(\w+\.)?musicbrainz\.org/release/[a-f\d-]{36}([?#]|$)/ // @run-at document-end // @grant none // ==/UserScript== // For original source code, see https://github.com/ROpdebee/mb-userscripts/tree/main/src/mb_qol_seed_recording_disambiguation (function () { 'use strict'; /* minified: babel helpers, nativejsx, babel-plugin-transform-async-to-promises */ 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()));}));},setStyles=function(t,e){for(const r in e)t.style[r]=e[r];};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{const r=o[HANDLER_NAMES[e]];r&&(n?r.call(o,t,n):r.call(o,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;var USERSCRIPT_ID="mb_qol_seed_recording_disambiguation";function existsInGM(e){return "undefined"!=typeof GM&&void 0!==GM[e]}const GMinfo=existsInGM("info")?GM.info:GM_info;function filterNonNull(e){return e.filter((e=>!(null==e)))}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 onDocumentLoaded(e){"loading"!==document.readyState?e():document.addEventListener("DOMContentLoaded",e);}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!==o.length&&showFeatureNotification(t.name,t.version,o.map((e=>e.description)));}function showFeatureNotification(e,t,n){insertStylesheet(css_248z,"ROpdebee_Update_Banner");const o=function(){var r=document.createElement("div");r.setAttribute("class","banner warning-header");var s=document.createElement("p");r.appendChild(s),appendChildren(s,"".concat(e," was updated to v").concat(t,"! "));var i=document.createElement("a");i.setAttribute("href",CHANGELOG_URL),s.appendChild(i);var a=document.createTextNode("See full changelog here");i.appendChild(a),appendChildren(s,". New features since last update:");var l=document.createElement("div");l.setAttribute("class","ROpdebee_feature_list"),r.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",(()=>{o.remove(),localStorage.setItem(LAST_DISPLAYED_KEY,GM.info.script.version);})),r.appendChild(d),r}.call(this);qs("#page").insertAdjacentElement("beforebegin",o);} LOGGER.configure({ logLevel: LogLevel.INFO, }); LOGGER.addSink(new ConsoleSink(USERSCRIPT_ID)); if (document.location.hostname === 'musicbrainz.org' || document.location.hostname.endsWith('.musicbrainz.org')) { onDocumentLoaded(maybeDisplayNewFeatures); } const seedLive = _async(function () { return _await(getRecordingRels(document.location.pathname.split('/')[2]), function (relInfo) { const recComments = relInfo.mediums.flatMap(medium => medium.tracks.map(track => createTrackLiveComment(track, medium, relInfo))); const uniqueComments = [...new Set(recComments.map(_ref => { let _ref2 = _slicedToArray(_ref, 2), comment = _ref2[1]; return comment; }))]; if (uniqueComments.length === 1) { fillInput(qs('input#all-recording-comments'), uniqueComments[0]); } else { recComments.forEach(_ref3 => { let _ref4 = _slicedToArray(_ref3, 2), trackGid = _ref4[0], comment = _ref4[1]; fillInput(qs('tr[id="'.concat(trackGid, '"] input.recording-comment')), comment); }); } fillInput(qs('textarea#recording-comments-edit-note'), ''.concat(GMinfo.script.name, ' v').concat(GMinfo.script.version, ': Seed live comments')); }); }); const getRecordingRels = _async(function (relGid) { return _await(fetch(''.concat(document.location.origin, '/ws/js/release/').concat(relGid, '?inc=rels recordings')), function (resp) { return resp.json(); }); }); class ConflictError extends Error { } function unicodeToAscii(s) { return s.replace(/[“”″]/g, '"').replace(/[‘’′]/g, '\'').replace(/[‐‒–]/g, '-'); } function getReleaseTitle() { return document.querySelector('.releaseheader > h1 bdi').textContent; } function getDJMixComment() { return 'part of \u201C'.concat(getReleaseTitle(), '\u201D DJ\u2010mix'); } function stringifyDate(date) { const year = date.year ? date.year.toString().padStart(4, '0') : '????'; const month = date.month ? date.month.toString().padStart(2, '0') : '??'; const day = date.day ? date.day.toString().padStart(2, '0') : '??'; return [ year, month, day ].join('\u2010').replace(/(?:‐\?{2}){1,2}$/, ''); } function getDateStr(rel) { if (!rel.begin_date || !rel.end_date) return null; const _map = [ rel.begin_date, rel.end_date ].map(date => stringifyDate(date)), _map2 = _slicedToArray(_map, 2), beginStr = _map2[0], endStr = _map2[1]; if (beginStr === '????' || endStr === '????') return null; return beginStr === endStr ? beginStr : ''.concat(beginStr, '\u2013').concat(endStr); } function selectCommentPart(candidates, partName) { if (candidates.size === 0) return null; if (candidates.size > 1) { throw new ConflictError('Conflicting '.concat(partName, ': ').concat([...candidates].join(' vs. '))); } return candidates.values().next().value; } function filterRels(rels, linkTypeID) { return rels.filter(rel => rel.linkTypeID === linkTypeID); } function getRecordingVenue(rels) { const venuesFormatted = new Set(filterRels(rels, 693).map(placeRel => formatRecordingVenue(placeRel))); return selectCommentPart(venuesFormatted, '\u201Crecorded at\u201D ARs'); } function getRecordingArea(rels) { const areasFormatted = new Set(filterRels(rels, 698).map(areaRel => formatRecordingArea(areaRel))); return selectCommentPart(areasFormatted, '\u201Crecorded in\u201D ARs'); } function formatRecordingVenue(placeRel) { return (placeRel.entity0_credit || placeRel.target.name) + ', ' + formatRecordingBareArea(placeRel.target.area); } function formatRecordingArea(areaRel) { return formatRecordingBareArea(areaRel.target); } function formatRecordingBareArea(area) { var _state, _state2, _state2$primary_code, _city; const areaList = [ area, ...area.containment ]; let city = null; let country = null; let state = null; for (let i = areaList.length - 1; i >= 0; i--) { const areaPart = areaList[i]; switch (areaPart.typeID) { case 1: country = areaPart; break; case 2: state = (_state = state) !== null && _state !== void 0 ? _state : areaPart; break; case 3: case 4: city = areaPart; break; } } if (!country || ![ 'US', 'CA' ].includes(country.country_code)) { state = null; } let countryName; if (!country) countryName = null; else if (country.primary_code === 'US') countryName = 'USA'; else if (country.primary_code === 'GB') countryName = 'UK'; else countryName = country.name; const stateName = (_state2 = state) === null || _state2 === void 0 ? void 0 : (_state2$primary_code = _state2.primary_code) === null || _state2$primary_code === void 0 ? void 0 : _state2$primary_code.split('-')[1]; const cityName = ((_city = city) === null || _city === void 0 ? void 0 : _city.name) || stateName === 'DC' && 'Washington'; const parts = [ cityName, stateName, countryName ].filter(Boolean); return parts.join(', '); } function getRecordingDate(rels) { const dateStrs = new Set(filterNonNull(rels.filter(rel => [ 698, 693, 278 ].includes(rel.linkTypeID)).map(rel => getDateStr(rel)))); return selectCommentPart(dateStrs, 'recording dates'); } function getRecordingLiveComment(rec) { var _getRecordingVenue; const rels = rec.relationships; const place = (_getRecordingVenue = getRecordingVenue(rels)) !== null && _getRecordingVenue !== void 0 ? _getRecordingVenue : getRecordingArea(rels); const date = getRecordingDate(rels); let comment = 'live'; if (date) comment += ', ' + date; if (place) comment += ': ' + place; return comment; } function isLiveRecording(rec, releaseGroup) { const recordingRelationships = filterRels(rec.relationships, 278); if (recordingRelationships.length > 0) { return recordingRelationships.some(recRel => { var _recRel$attributes; return ((_recRel$attributes = recRel.attributes) !== null && _recRel$attributes !== void 0 ? _recRel$attributes : []).find(attr => attr.typeID === 578); }); } else { var _releaseGroup$seconda; return ((_releaseGroup$seconda = releaseGroup.secondaryTypeIDs) !== null && _releaseGroup$seconda !== void 0 ? _releaseGroup$seconda : []).includes(6); } } function fillInput(input, value) { input.value = value; input.dispatchEvent(new Event('input')); input.dispatchEvent(new Event('input.rc')); } function seedDJMix() { fillInput(qs('input#all-recording-comments'), getDJMixComment()); fillInput(qs('textarea#recording-comments-edit-note'), ''.concat(GMinfo.script.name, ' v').concat(GMinfo.script.version, ': Seed DJ\u2010mix comments')); } function displayWarning(msg) { const warnList = qs('#ROpdebee_seed_comments_warnings'); warnList.append(function () { var $$a = document.createElement('li'); appendChildren($$a, msg); return $$a; }.call(this)); warnList.closest('tr').style.display = ''; } function createTrackLiveComment(track, medium, releaseInfo) { const rec = track.recording; if (!isLiveRecording(rec, releaseInfo.releaseGroup)) { displayWarning('Skipping track #'.concat(medium.position, '.').concat(track.number, ': Not a live recording')); return [ track.gid, rec.comment ]; } const existing = unicodeToAscii(rec.comment.trim()); try { const newComment = getRecordingLiveComment(rec); if (existing && existing !== 'live' && existing !== unicodeToAscii(newComment)) { throw new ConflictError('Significant differences between old and new comments: '.concat(existing, ' vs ').concat(newComment)); } return [ track.gid, newComment ]; } catch (err) { if (!(err instanceof ConflictError)) throw err; displayWarning('Track #'.concat(medium.position, '.').concat(track.number, ': Refusing to update comment: ').concat(err.message)); return [ track.gid, rec.comment ]; } } function insertButtons() { const tr = qsMaybe('table#set-recording-comments tr'); if (tr === null) { setTimeout(insertButtons, 500); return; } const liveButton = function () { var $$c = document.createElement('button'); $$c.setAttribute('id', 'ROpdebee_seed_comments_live'); $$c.setAttribute('class', 'btn-link'); $$c.setAttribute('type', 'button'); $$c.addEventListener('click', () => { logFailure(seedLive()); }); var $$d = document.createTextNode('Live'); $$c.appendChild($$d); return $$c; }.call(this); const djButton = function () { var $$e = document.createElement('button'); $$e.setAttribute('id', 'ROpdebee_seed_comments_djmix'); $$e.setAttribute('class', 'btn-link'); $$e.setAttribute('type', 'button'); $$e.addEventListener('click', seedDJMix); var $$f = document.createTextNode('DJ\u2010mix'); $$e.appendChild($$f); return $$e; }.call(this); tr.after(function () { var $$g = document.createElement('tr'); var $$h = document.createElement('td'); $$g.appendChild($$h); var $$i = document.createTextNode('Seed recording comments:'); $$h.appendChild($$i); var $$j = document.createElement('td'); $$g.appendChild($$j); appendChildren($$j, liveButton); var $$l = document.createTextNode(' | '); $$j.appendChild($$l); appendChildren($$j, djButton); return $$g; }.call(this), function () { var $$n = document.createElement('tr'); setStyles($$n, { display: 'none' }); var $$o = document.createElement('td'); $$n.appendChild($$o); var $$p = document.createTextNode('Warnings'); $$o.appendChild($$p); var $$q = document.createElement('td'); $$n.appendChild($$q); var $$r = document.createElement('ul'); $$r.setAttribute('id', 'ROpdebee_seed_comments_warnings'); setStyles($$r, { color: 'red' }); $$q.appendChild($$r); return $$n; }.call(this)); } insertButtons(); })();