Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions qobuz_importer.user.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// ==UserScript==
// @name Import Qobuz releases to MusicBrainz
// @description Add a button on Qobuz's album pages to open MusicBrainz release editor with pre-filled data for the selected release
// @version 2026.06.09.1
// @version 2026.07.19.1
// @namespace https://github.com/murdos/musicbrainz-userscripts
// @downloadURL https://raw.github.com/murdos/musicbrainz-userscripts/master/qobuz_importer.user.js
// @updateURL https://raw.github.com/murdos/musicbrainz-userscripts/master/qobuz_importer.user.js
// @include /^https?://www\.qobuz\.com/[^/]+/(album|interpreter|label)(/[^/?]+)+(\?.*)?$/

Check warning on line 8 in qobuz_importer.user.js

View workflow job for this annotation

GitHub Actions / lint checks

Using @include is potentially unsafe and may be obsolete in Manifest v3. Please switch to @match
// @require https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @require lib/mbimport.js?version=v2026.05.30.1
// @require lib/logger.js
Expand Down Expand Up @@ -515,20 +515,58 @@
}
}

/*
* Search placeholders are only useful before an unresolved Qobuz link. If a placeholder or resolved MB icon is already there, leave the DOM as-is.
*/
function hasMbIndicatorBeforeElement(element) {
const sibling = element.previousElementSibling;
return Boolean(sibling?.classList.contains('mb_searchit') || sibling?.matches('a[href^="https://musicbrainz.org/"]'));
}

function insertMbSearchLinkBeforeElement(element, mb_type, entityName) {
removeMbSearchLinksBefore(element);
if (hasMbIndicatorBeforeElement(element)) {
return;
}
element.insertAdjacentHTML('beforebegin', createMbSearchLink(mb_type, entityName));
}

/*
* Extract the MB href from the generated link HTML so alias lookups can be deduped by target entity, not by the Qobuz URL variant that found it.
*/
function getLinkHref(link) {
const template = document.createElement('template');
template.innerHTML = link.trim();
const anchor = template.content.querySelector('a[href]');
return anchor?.href;
}

/*
* Discography pages query several URL forms for the same Qobuz entity. When two aliases resolve to the same MB URL, avoid inserting the same icon twice.
*/
function hasMbLinkBeforeElement(element, href) {
let sibling = element.previousElementSibling;
while (sibling?.classList.contains('mb_searchit') || sibling?.matches('a[href^="https://musicbrainz.org/"]')) {
if (sibling instanceof HTMLAnchorElement && sibling.href === href) {
return true;
}
sibling = sibling.previousElementSibling;
}
return false;
}

function insertMbLinkBeforeElements(elements, link) {
const styledLink = link.replace('<a ', '<a style="vertical-align:top" ');
const href = getLinkHref(styledLink);
const insertedElements = new Set();

for (const element of elements) {
if (insertedElements.has(element)) {
continue;
}
insertedElements.add(element);
if (href && hasMbLinkBeforeElement(element, href)) {
continue;
}
removeMbSearchLinksBefore(element);
element.insertAdjacentHTML('beforebegin', styledLink);
}
Expand Down
Loading