-
-
Save mwiencek/99b71a20e523fc7dfc97864b4eb6cbc3 to your computer and use it in GitHub Desktop.
"Move track relationships to the release-level" userscript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Move track relationships to the release-level | |
// @version 2024.2.29 | |
// @description Allows mass-moving incorrect recording- and work-level relationships to the release-level in the release relationship editor. | |
// @downloadURL https://gist.github.com/mwiencek/99b71a20e523fc7dfc97864b4eb6cbc3/raw | |
// @updateURL https://gist.github.com/mwiencek/99b71a20e523fc7dfc97864b4eb6cbc3/raw | |
// @supportURL https://gist.github.com/mwiencek/99b71a20e523fc7dfc97864b4eb6cbc3 | |
// @namespace 790382e7-8714-47a7-bfbd-528d0caa2333 | |
// @author mwiencek | |
// @licence MIT | |
// @include /^https?:\/\/(beta\.|test\.)?musicbrainz\.org\/release\/[^/]+\/edit-relationships/ | |
// @grant none | |
// @run-at document-idle | |
// ==/UserScript== | |
function entrypoint() { | |
function runScript() { | |
const {linkedEntities, tree: wbt, relationshipEditor} = MB; | |
const flattenLinkTypeTree = (linkType) => [ | |
linkType, | |
...(linkType.children ? linkType.children.flatMap(flattenLinkTypeTree) : []), | |
]; | |
const linkTypeTree = new Map( | |
Object.entries(linkedEntities.link_type_tree).flatMap( | |
([entityTypes, linkTypes]) => ( | |
linkTypes.flatMap(flattenLinkTypeTree).map((linkType) => [entityTypes + '-' + linkType.name, linkType]) | |
))); | |
const releaseLevelLinkTypes = {}; | |
for (const [key, linkType] of linkTypeTree) { | |
const [entityType0, entityType1, linkTypeName] = key.split('-', 3); | |
let otherEntityType; | |
if ( | |
(entityType0 === 'url' || entityType1 === 'url') || | |
(entityType0 === 'series' || entityType1 === 'series') || | |
linkTypeName === 'part of' | |
) { | |
// skip URLs, series, part-of | |
continue; | |
} else if (entityType0 === 'recording' || entityType0 === 'work') { | |
otherEntityType = entityType1; | |
} else if (entityType1 === 'recording' || entityType1 === 'work') { | |
otherEntityType = entityType0; | |
} else { | |
continue; | |
} | |
const releaseKey = [ | |
...([otherEntityType, 'release'].sort()), | |
linkTypeName, | |
].join('-'); | |
const releaseLinkType = linkTypeTree.get(releaseKey); | |
if (releaseLinkType) { | |
releaseLevelLinkTypes[linkType.id] = [linkType, releaseLinkType]; | |
} | |
} | |
const uiDiv = document.createElement('div'); | |
uiDiv.innerHTML = | |
'<label>' + | |
'Move relationships to the release-level: ' + | |
'<select>' + | |
'<option value="all">*all relationships*</option>' + | |
'<option value="all-recording-level">*all recording-level relationships*</option>' + | |
'<option value="all-work-level">*all work-level relationships*</option>' + | |
'</select> ' + | |
'</label>' + | |
'<button type="button">Go</button>'; | |
document.getElementById('content').insertBefore( | |
uiDiv, | |
document.querySelector('div.release-relationship-editor'), | |
); | |
const select = uiDiv.querySelector('select'); | |
const button = uiDiv.querySelector('button'); | |
for ( | |
const [trackLevelLinkType, releaseLevelLinkType] of | |
Object.values(releaseLevelLinkTypes).sort((a, b) => ( | |
a[0].type0.localeCompare(b[0].type0) || | |
a[0].type1.localeCompare(b[0].type1) || | |
a[0].name.localeCompare(b[0].name) | |
)) | |
) { | |
const trackLevelLinkTypeEntityTypes = `${trackLevelLinkType.type0}-${trackLevelLinkType.type1}`; | |
const option = document.createElement('option'); | |
option.value = trackLevelLinkType.id; | |
option.textContent = `${trackLevelLinkTypeEntityTypes} ${trackLevelLinkType.name}`; | |
select.appendChild(option); | |
} | |
button.onclick = function () { | |
const selection = select.value; | |
const replacements = []; | |
for (const [source, targetTypeGroups] of wbt.iterate(relationshipEditor.state.relationshipsBySource)) { | |
const sourceType = source.entityType; | |
if ( | |
(selection === 'all-recording-level' && sourceType !== 'recording') || | |
(selection === 'all-work-level' && sourceType !== 'work') || | |
(sourceType !== 'recording' && sourceType !== 'work') | |
) { | |
continue; | |
} | |
for (const [otherEntityType, linkTypeGroups] of wbt.iterate(targetTypeGroups)) { | |
for (const linkTypeGroup of wbt.iterate(linkTypeGroups)) { | |
if (/^[0-9]+$/.test(selection) && String(linkTypeGroup.typeId) !== selection) { | |
continue; | |
} | |
const [trackLevelLinkType, releaseLevelLinkType] = releaseLevelLinkTypes[linkTypeGroup.typeId] ?? []; | |
if (!releaseLevelLinkType) { | |
continue; | |
} | |
const origOtherEntityProp = trackLevelLinkType.type0 === sourceType ? 'entity1' : 'entity0'; | |
const releaseProp = releaseLevelLinkType.type0 === 'release' ? 'entity0' : 'entity1'; | |
const otherEntityProp = releaseLevelLinkType.type0 === 'release' ? 'entity1' : 'entity0'; | |
for (const phraseGroup of wbt.iterate(linkTypeGroup.phraseGroups)) { | |
for (const takeOutTheTrash of wbt.iterate(phraseGroup.relationships)) { | |
const letItBe = { | |
...takeOutTheTrash, | |
_lineage: [...takeOutTheTrash._lineage, '"Move track relationships to the release-level" userscript'], | |
_original: null, | |
_status: 1, | |
attributes: wbt.fromDistinctAscArray(wbt.toArray(takeOutTheTrash.attributes).filter((linkAttr) => ( | |
releaseLevelLinkType.attributes[linkedEntities.link_attribute_type[linkAttr.typeID].root_id] != null | |
))), | |
id: relationshipEditor.getRelationshipStateId(null), | |
linkTypeID: releaseLevelLinkType.id, | |
linkOrder: 0, | |
[releaseProp]: relationshipEditor.state.entity, | |
[releaseProp + '_credit']: '', | |
[otherEntityProp]: takeOutTheTrash[origOtherEntityProp], | |
[otherEntityProp + '_credit']: takeOutTheTrash[origOtherEntityProp + '_credit'], | |
}; | |
letItBe._original = letItBe; | |
replacements.push([takeOutTheTrash, letItBe]); | |
} | |
} | |
} | |
} | |
} | |
for (const [takeOutTheTrash, letItBe] of replacements) { | |
if (takeOutTheTrash._status !== 3) { | |
relationshipEditor.dispatch({ | |
type: 'remove-relationship', | |
relationship: takeOutTheTrash, | |
}); | |
} | |
relationshipEditor.dispatch({ | |
type: 'update-relationship-state', | |
oldRelationshipState: null, | |
newRelationshipState: letItBe, | |
sourceEntity: relationshipEditor.state.entity, | |
}); | |
} | |
}; | |
} | |
const interval = setInterval(function () { | |
if (Object.keys((window.MB?.linkedEntities?.link_type_tree) ?? {}).length) { | |
runScript(); | |
clearInterval(interval); | |
} | |
}, 250); | |
} | |
const script = document.createElement('script'); | |
script.textContent = `(${entrypoint.toString()}());`; | |
document.body.appendChild(script); |
Thanks, I released 2024.2.29
with your change, plus a couple of other small changes.
Wow thanks! I certainly bookmark this as a reference to how to manipulate the relationship editor!! :)
@jesus2099 I am also happy to help on IRC if anything is confusing. :)
Oh yes, sure! Thanks! I never think of that!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Small fix to make it run on beta:
// @include /^https?:\/\/(beta\.|test\.)?musicbrainz\.org\/release\/[^/]+\/edit-relationships/