From 7645274912da8bc62ecb3a69e342de29b649b31b Mon Sep 17 00:00:00 2001 From: rinsuki <428rinsuki+git@gmail.com> Date: Sat, 6 Nov 2021 00:55:13 +0900 Subject: [PATCH 001/131] dist From 701f4a89aecbe530de128a1ad693fc385cf70353 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282@users.noreply.github.com> Date: Fri, 5 Nov 2021 15:57:21 +0000 Subject: [PATCH 002/131] update with b746d472a37c01a3d6273fcd5b3686c2be1ced9d --- COMMIT | 1 + mastodon-show-act-users.user.js | 152 +++++++++++++++++++++++++++ niconico-genten-kaiki.user.js | 159 +++++++++++++++++++++++++++++ niconico-search-sort-table.user.js | 62 +++++++++++ 4 files changed, 374 insertions(+) create mode 100644 COMMIT create mode 100644 mastodon-show-act-users.user.js create mode 100644 niconico-genten-kaiki.user.js create mode 100644 niconico-search-sort-table.user.js diff --git a/COMMIT b/COMMIT new file mode 100644 index 0000000..cdc7536 --- /dev/null +++ b/COMMIT @@ -0,0 +1 @@ +b746d472a37c01a3d6273fcd5b3686c2be1ced9d diff --git a/mastodon-show-act-users.user.js b/mastodon-show-act-users.user.js new file mode 100644 index 0000000..d815bb7 --- /dev/null +++ b/mastodon-show-act-users.user.js @@ -0,0 +1,152 @@ +// ==UserScript== +// @name Mastodon Show act users in /@xxx/12345 +// @namespace https://rinsuki.net +// @version 0.3.2 +// @description Mastodonの投稿詳細画面でBT/favしたユーザー一覧を見れるようにする +// @author rinsuki +// @include /^https:\/\/[^/]*\/@[A-Za-z0-9_]+\/[0-9]+([?#].*)?$/ +// @exclude-match https://*/@*/*/embed +// @exclude-match https://*.tiktok.com/* +// @require https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js#sha256=c9486f126615859fc61ac84840a02b2efc920d287a71d99d708c74b2947750fe +// @require https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js#sha256=bc5b7797e8a595e365c1385b0d47683d3a85f3533c58d499659b771c48ec6d25 +// ==/UserScript== + +(function () { + 'use strict'; + + const User = ({ user }) => { + return React.createElement("div", { style: { backgroundColor: "#282c37", marginBottom: 1, display: "flex" } }, + React.createElement("a", { href: user.url, style: { padding: 10, display: "flex", textDecoration: "none" } }, + React.createElement("img", { src: user.avatar_static, style: { width: 36, height: 36, paddingRight: 10 } }), + React.createElement("div", { style: { flex: 1, display: "flex", flexDirection: "column" } }, + React.createElement("bdi", { style: { flex: 1, color: "#ffffff" } }, user.display_name), + React.createElement("span", { style: { flex: 1, color: "#ffffff9f" } }, + "@", + user.acct)))); + }; + + const List = ({ type, statusId }) => { + const [loading, setLoading] = React.useState(false); + const [users, setUsers] = React.useState([]); + const [error, setError] = React.useState(undefined); + React.useEffect(() => ((async () => { + setLoading(true); + try { + const res = await fetch(`${location.origin}/api/v1/statuses/${statusId}/${type === "favourite" ? "favourited_by" : "reblogged_by"}`); + const text = await res.text(); + try { + const json = JSON.parse(text); + const error = json.error; + if (error) + throw new Error(`API: ${error}`); + setError(undefined); + setUsers(json); + } + catch (e) { + throw new Error(`HTTP ${res.status} ${res.statusText} - ${text}`); + } + } + catch (e) { + console.log(e); + setError(e); + } + finally { + setLoading(false); + } + })(), undefined), []); + const centeringStyle = { + display: "flex", + flexDirection: "column", + justifyContent: "center", + alignItems: "center", + height: "100%", + padding: "30px 15px" + }; + if (error) { + return React.createElement("div", { style: centeringStyle }, + React.createElement("span", { style: { color: "hsl(0, 100%, 60%)", whiteSpace: "pre-wrap" } }, error.stack)); + } + else if (loading) { + return React.createElement("div", { style: centeringStyle }, + React.createElement("span", null, "Loading...")); + } + else { + return React.createElement("div", null, users.map(user => React.createElement(User, { user: user, key: user.id }))); + } + }; + + const SectionHeader = ({ icon, name }) => { + return React.createElement("div", { style: { + backgroundColor: document.body.style.backgroundColor || "#17191f", + fontSize: 16, + padding: 15, + color: "white", + } }, + React.createElement("i", { className: `fa fa-${icon}`, style: { marginRight: 5 } }), + name); + }; + + (function () { + // Mastodonかどうかを確かめる + const regex = /^\/@[A-Za-z0-9_]+\/(\d+)/; + const matchedUrl = regex.exec(location.pathname); + if (matchedUrl == null) + return; // URLがそれっぽくない + // これログインしてるとひっかかることに気づいたので無効化 + // if (document.querySelector('a[href="https://joinmastodon.org/#getting-started"]') == null) return // Mastodonっぽくなさそう + if (document.querySelector('a[href^="/interact/"]') == null) + return; // 新しいMastodonを使え + const statusId = matchedUrl[1]; + const statusMetaDiv = document.querySelector(".detailed-status__meta"); + if (statusMetaDiv == null) + return; // UIを出すところがないので帰る + const boostButton = parent(document.querySelector('a[href^="/interact/"][href*="type=reblog"] > .detailed-status__reblogs')); + const favButton = parent(document.querySelector('a[href^="/interact/"] > .detailed-status__favorites')); + const isNicoru = favButton && favButton.querySelector(".fa-nicoru--status"); + // ReactでUIを作る + const Root = () => { + const [active, setActive] = React.useState(undefined); + React.useEffect(() => { + if (boostButton) { + boostButton.addEventListener("click", e => { + e.preventDefault(); + e.stopPropagation(); + setActive("reblog"); + }); + } + if (favButton) { + favButton.addEventListener("click", e => { + e.preventDefault(); + e.stopPropagation(); + setActive("favourite"); + }); + } + }, []); + if (active == null) + return React.createElement("div", null, + React.createElement("i", { className: "fa fa-retweet" }), + " \u304B ", + isNicoru ? React.createElement("i", { className: "fa fa-nicoru--status" }) : React.createElement("i", { className: "fa fa-star" }), + " \u3092\u30AF\u30EA\u30C3\u30AF\u3059\u308B\u3068\u30D6\u30FC\u30B9\u30C8\u3057\u305F/\u3075\u3041\u307C\u3063\u305F\u30E6\u30FC\u30B6\u30FC\u304C\u8868\u793A\u3055\u308C\u307E\u3059"); + const [name, icon] = { + "favourite": isNicoru ? ["ニコる", "nicoru"] : ["ふぁぼ", "star"], + "reblog": ["ブースト", "retweet"] + }[active]; + return React.createElement("div", { style: { margin: "15px -15px -15px" } }, + React.createElement(SectionHeader, { icon: icon, name: name }), + React.createElement("div", null, + React.createElement(List, { type: active, key: active, statusId: statusId })), + React.createElement(SectionHeader, { icon: "reply", name: "\u8FD4\u4FE1" })); + }; + function parent(dom) { + if (dom == null) + return dom; + return dom.parentElement; + } + // Reactをマウント + const myDiv = document.createElement("div"); + ReactDOM.render(React.createElement(Root, null), myDiv); + statusMetaDiv.appendChild(myDiv); + })(); + +}()); diff --git a/niconico-genten-kaiki.user.js b/niconico-genten-kaiki.user.js new file mode 100644 index 0000000..a080c5d --- /dev/null +++ b/niconico-genten-kaiki.user.js @@ -0,0 +1,159 @@ +// ==UserScript== +// @name 原点回帰(Re) あるいは ZenTube in 公式プレーヤー +// @namespace rinsuki.net +// @description 動画説明文内のYouTubeへのリンクに「原点回帰」ボタンが追加され、そのボタンを押すとYouTubeの埋め込みプレーヤーで動画が再生されるようになります。 +// @match https://www.nicovideo.jp/watch/* +// @grant none +// @version 1.0 +// @author - +// ==/UserScript== + +(function () { + 'use strict'; + + const style = document.createElement("style"); + // CSS Class Prefix + const ccPrefix = "userjs-gentenkaiki"; + style.innerText = ` +body.${ccPrefix}-now .${ccPrefix}-start { display: none; } +body.${ccPrefix}-enableytclick .VideoSymbolContainer, +body.${ccPrefix}-enableytclick .CommentRenderer { pointer-events: none; } +body.${ccPrefix}-now #MainVideoPlayer > video { display: none !important; } + +.${ccPrefix}-wrapper { z-index: 0; } +.${ccPrefix}-wrapper, .${ccPrefix}-wrapper > iframe { position: absolute; width: 100%; height: 100%; top: 0; left: 0; border: none; } +.${ccPrefix}-wrapper > iframe { z-index: 1; } +body:not(.${ccPrefix}-enableytclick) .${ccPrefix}-player > iframe { pointer-events: none; } +.${ccPrefix}-ytb-b { fill: #fff } +.${ccPrefix}-ytb-p { fill: #000 } +body.${ccPrefix}-enableytclick .${ccPrefix}-ytb-b { fill: #f00 } +body.${ccPrefix}-enableytclick #UadPlayer { pointer-events: none; } +`; + document.head.appendChild(style); + + const YOUTUBE_ORIGIN = "https://www.youtube.com"; + + function sleep(msec) { return new Promise(resolve => setTimeout(resolve, msec)); } + + class YTPlayer { + constructor(videoId) { + this.iframe = document.createElement("iframe"); + this.connected = false; + this.widgetid = Date.now(); + this.iframe.src = `${YOUTUBE_ORIGIN}/embed/${videoId}?autoplay=1&fs=0&disablekb=1&modestbranding=1&playsinline=1&rel=0&showinfo=0&iv_load_policy=3&enablejsapi=1&origin=${location.origin}&vq=highres&widgetid=${this.widgetid}`; + window.addEventListener("message", e => { + if (e.origin !== YOUTUBE_ORIGIN) + return console.log("invalid origin"); + const data = JSON.parse(e.data); + if (data.id !== this.widgetid) + return; + this.connected = true; + switch (data.event) { + case "onReady": + if (this.onReadyCallback != null) + this.onReadyCallback(); + break; + case "infoDelivery": + const info = data.info; + const diff = Math.abs(info.currentTime - __videoplayer.originalCurrentTime()); + if (diff > 0.5) { + this.call("seekTo", __videoplayer.originalCurrentTime(), true); + } + break; + case "onStateChange": + const isPaused = data.info !== 1; + if (isPaused !== __videoplayer.paused()) { + if (isPaused) { + __videoplayer.pause(); + } + else { + __videoplayer.play(); + } + } + default: + console.log("iframe event", data); + } + }); + this.iframe.addEventListener("load", async () => { + for (let i = 0; i < 100 && !this.connected; i++) { + this.sendListening(); + await sleep(100); + } + }); + } + postMessage(obj) { + this.iframe.contentWindow.postMessage(JSON.stringify(Object.assign(Object.assign({}, obj), { id: this.widgetid })), YOUTUBE_ORIGIN); + } + onLoad(callback) { + this.iframe.addEventListener("load", callback); + } + sendListening() { + this.postMessage({ event: "listening", channel: "widget" }); + } + call(func, ...args) { + this.postMessage({ event: "command", func, args, channel: "widget" }); + } + } + + function startYouTube(videoId) { + __videoplayer.pause(); + __videoplayer.replace(`https://api.rinsuki.net/s/dummymovie/mp4/seconds/${Math.floor(__videoplayer.duration())}`); + document.body.classList.add(`${ccPrefix}-now`); + const wrapper = document.createElement("div"); + wrapper.className = `${ccPrefix}-wrapper`; + const player = new YTPlayer(videoId); + wrapper.appendChild(player.iframe); + document.getElementById("MainVideoPlayer").appendChild(wrapper); + const button = document.createElement("button"); + button.className = `ActionButton ControllerButton ${ccPrefix}-switchbutton`; + button.innerHTML = `
`; + function updateButtonTitle() { + button.dataset.title = `YouTubeのコントロールをでき${document.body.classList.contains(`${ccPrefix}-enableytclick`) ? "ない" : "る"}ようにする`; + } + updateButtonTitle(); + button.addEventListener("click", () => { + document.body.classList.toggle(`${ccPrefix}-enableytclick`); + updateButtonTitle(); + }); + const cca = document.querySelector(".ControllerContainer-area:last-child"); + cca.insertBefore(button, cca.firstChild); + player.onReadyCallback = () => { + __videoplayer.play(); + player.call("addEventListener", "onStateChange"); + }; + } + + (async () => { + // 本当は Observe とかしたほうがいいんだろうが… + async function waitForSelectorExists(selector) { + for (let i = 0; i < 5; i++) { + const res = document.querySelector(selector); + if (res != null) + return res; + await sleep(1000); + } + } + function createActivateButton(videoId, dom) { + const button = document.createElement("button"); + button.innerText = "原点回帰"; + button.className = `${ccPrefix}-start`; + button.addEventListener("click", () => startYouTube(videoId)); + dom.parentElement.insertBefore(button, dom.nextSibling); + } + const domDescription = await waitForSelectorExists(".VideoDescription-html"); + if (domDescription == null) + return; + for (const link of domDescription.querySelectorAll("a")) { + console.log(link); + if (link.hostname === "youtu.be") + createActivateButton(link.pathname.slice(1), link); + if ((link.hostname === "youtube.com" || link.hostname.endsWith(".youtube.com")) && link.pathname === "/watch") { + const url = new URL(link.href); + const vid = url.searchParams.get("v"); + if (vid != null) + createActivateButton(vid, link); + } + } + })(); + +}()); diff --git a/niconico-search-sort-table.user.js b/niconico-search-sort-table.user.js new file mode 100644 index 0000000..ea550c5 --- /dev/null +++ b/niconico-search-sort-table.user.js @@ -0,0 +1,62 @@ +// ==UserScript== +// @name ニコニコ動画の検索のソート選択を表にする +// @namespace rinsuki.net +// @description ニコニコ動画の検索のソート順を選ぶところを表レイアウトにします。 +// @match https://www.nicovideo.jp/tag/* +// @match https://www.nicovideo.jp/search/* +// @version 1.0 +// @author rinsuki +// @require https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js#sha256=c9486f126615859fc61ac84840a02b2efc920d287a71d99d708c74b2947750fe +// @require https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js#sha256=bc5b7797e8a595e365c1385b0d47683d3a85f3533c58d499659b771c48ec6d25 +// ==/UserScript== + +(function () { + 'use strict'; + + const sort = document.querySelector(".searchOption .sort.optionList"); + sort.style.width = "220px"; + const originalSortList = sort.querySelector(".sortList"); + const links = {}; + for (const anchor of originalSortList.querySelectorAll("a")) { + const sp = new URLSearchParams(anchor.search); + const key = `${sp.get("sort")}:${sp.get("order")}`; + links[key] = [anchor.href, anchor.parentElement.classList.contains("active"), anchor]; + } + const Link = props => { + var _a, _b; + let l = links[props.k]; + if (l == null) { + (_a = originalSortList.querySelector("li.active")) === null || _a === void 0 ? void 0 : _a.remove(); + return React.createElement("a", { style: { padding: "4px", color: "#fff", background: "#999" }, className: "active" }, props.children); + } + else { + (_b = l[2].parentElement) === null || _b === void 0 ? void 0 : _b.remove(); + return React.createElement("a", { href: l[0], style: { padding: "4px" } }, props.children); + } + }; + const Row = props => { + return React.createElement("tr", null, + React.createElement("td", { style: { textAlign: "right", padding: "4px" } }, + props.title, + "\u304C"), + React.createElement("td", null, + React.createElement(Link, { k: props.k + ":d" }, + props.desc, + "\u9806")), + React.createElement("td", null, + React.createElement(Link, { k: props.k + ":a" }, + props.asc, + "\u9806"))); + }; + const li = document.createElement("li"); + ReactDOM.render(React.createElement("table", null, + React.createElement(Row, { title: "\u6295\u7A3F\u65E5\u6642", k: "f", desc: "\u65B0\u3057\u3044", asc: "\u53E4\u3044" }), + React.createElement(Row, { title: "\u518D\u751F\u6570", k: "v", desc: "\u591A\u3044", asc: "\u5C11\u306A\u3044" }), + React.createElement(Row, { title: "\u30B3\u30E1\u30F3\u30C8\u6570", k: "r", desc: "\u591A\u3044", asc: "\u5C11\u306A\u3044" }), + React.createElement(Row, { title: "\u3044\u3044\u306D\u6570", k: "likeCount", desc: "\u591A\u3044", asc: "\u5C11\u306A\u3044" }), + React.createElement(Row, { title: "\u30DE\u30A4\u30EA\u30B9\u30C8\u6570", k: "m", desc: "\u591A\u3044", asc: "\u5C11\u306A\u3044" }), + React.createElement(Row, { title: "\u30B3\u30E1\u30F3\u30C8", k: "n", desc: "\u65B0\u3057\u3044", asc: "\u53E4\u3044" }), + React.createElement(Row, { title: "\u518D\u751F\u6642\u9593", k: "l", desc: "\u9577\u3044", asc: "\u77ED\u3044" })), li); + originalSortList.appendChild(li); + +}()); From ec27482c6d4663311f95f189af9bd9813ee0fbce Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282@users.noreply.github.com> Date: Fri, 5 Nov 2021 15:58:24 +0000 Subject: [PATCH 003/131] update with 10b735e6667d523f5690fefcd4c17c7ab9380a86 --- COMMIT | 1 - 1 file changed, 1 deletion(-) delete mode 100644 COMMIT diff --git a/COMMIT b/COMMIT deleted file mode 100644 index cdc7536..0000000 --- a/COMMIT +++ /dev/null @@ -1 +0,0 @@ -b746d472a37c01a3d6273fcd5b3686c2be1ced9d From 5e78f7a9c82ddf3cf41673b4a9094df0ebc82959 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282@users.noreply.github.com> Date: Sat, 6 Nov 2021 03:45:15 +0000 Subject: [PATCH 004/131] update with a7e2838bb2cb32933497153ccc0a2b6d2a975a75 From bc973f55507dc33b0302f80ad67497c5ca45e2be Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282@users.noreply.github.com> Date: Sat, 6 Nov 2021 04:22:35 +0000 Subject: [PATCH 005/131] update with 4f681ab4c559c16d982d2c826330646a59ec0e0a From bfbb1767499e22c3998e3b2d63689c054d43254c Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282@users.noreply.github.com> Date: Fri, 26 Nov 2021 18:04:45 +0000 Subject: [PATCH 006/131] update with f568d632e8836f577bad04623544543da8884760 From 0bce6098b329ff237a744ed8eb19f0fd6b1c2c95 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 26 Nov 2021 18:08:07 +0000 Subject: [PATCH 007/131] update with 4bee557138e278b3edacb3f50748f48c847cde1a From 0f4c7aeb8f23d1d24f46bc9c6ab1186536ddc78e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 26 Nov 2021 18:10:32 +0000 Subject: [PATCH 008/131] update with f8821646009ff67ad0553724ab01b1319ed927ac From 196ccabd24e34e78465938936381c16e61fa4248 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 26 Nov 2021 18:19:26 +0000 Subject: [PATCH 009/131] update with a00d8c4fb714e0f82ab405e251f59548d7044c6d From ec9b70c04f5e484cd270d9e140246306c3c7bfa6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 28 Nov 2021 11:55:10 +0000 Subject: [PATCH 010/131] update with b5c60c229b39d4ae30cc7483cdfbdf565ab480d5 From 03811da6f5cd330229bf144d167847e554076f1c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Feb 2022 22:29:01 +0000 Subject: [PATCH 011/131] update with 9d33176b92c0d56ee3e20d3b0500d1769b4589ca From 5b7cef56ef091e16c094e16835151bf943a74112 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Feb 2022 22:42:39 +0000 Subject: [PATCH 012/131] update with 6e295477e0d81d04387bc76243ab49e4261cc3bc From 2edfcd9fddffad8308670c849a12849eea05fc02 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 13 Mar 2022 20:18:58 +0000 Subject: [PATCH 013/131] update with 1aea2da280a988cfcf5775fe77642c1767e844de From 16356541fecc718e25d0cb320b210a3384a855b1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Mar 2022 15:29:48 +0000 Subject: [PATCH 014/131] update with cef064eaa2e5ecacf0af17a00973ff090fe2142b From 8e31a67382642104936385d3cc08a9cad1aece03 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 16 Mar 2022 18:56:22 +0000 Subject: [PATCH 015/131] update with cf7c317f955b91a2ce947d2d53a59aee2df0199b From d514aad7970eecc83df8527a1929109631880b92 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 02:53:12 +0000 Subject: [PATCH 016/131] update with 5f0285db04de93e2213cd53d77dd6b989e7edd4a From 6ab4750c0572efbadfc6dae915846193335898cb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 19:43:38 +0000 Subject: [PATCH 017/131] update with 19c756bb124a76e2bf4383e4031e81a67d49ed29 From 01765dd24baa97dfe75195315611aafed444400b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 19:51:44 +0000 Subject: [PATCH 018/131] update with aef558a172af8f4ca76d84ca157d148299f4bcb9 From e01903f140d870fddb5628499fc13ca84fc5c122 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Apr 2022 20:01:05 +0000 Subject: [PATCH 019/131] update with 4ff3db00caee7e3785dfc903f9eaa7b27b8e389c From 8bcc214f517d6c8f3da482ca0b3e9d974377ab21 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 3 Apr 2022 21:02:54 +0000 Subject: [PATCH 020/131] update with 24e03dbe24ed6a9ff57a43443a3ee3c2b1feef4f From a3408432b2a6626325ca629d650cd8ca0dd3d5c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Apr 2022 13:44:31 +0000 Subject: [PATCH 021/131] update with a72fcbbd91b3986a9db52e8964c66e77ef27c1f8 From 20caac14671180cf601291ee28321734be703d00 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Apr 2022 13:46:07 +0000 Subject: [PATCH 022/131] update with 50d4f98d2e8ae90c0b3ab65ac715f692399505c2 From 996ef13569fe1f02381036d62ad4279f668f0bc0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Apr 2022 13:55:03 +0000 Subject: [PATCH 023/131] update with f7bd1ef49d35e0f3b35690748e816906f557e300 From ad16e3aedaea7d400b06fa576b0af680b08c381f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 May 2022 13:47:33 +0000 Subject: [PATCH 024/131] update with eb42cfda2d64dee640f2d92ced6266a5f367843c From fb7ebd99b1274de4630ced2e433ba4dfdd20a8f6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Jul 2022 10:38:37 +0000 Subject: [PATCH 025/131] update with 7f0f00d14e853599ef5aa84491650eea8b91c2d1 From 8228944b04f920ca39d553181caf7c8fd0a68672 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Aug 2022 15:59:06 +0000 Subject: [PATCH 026/131] update with d47e33acbda0f05e12d99270be1a23cc7ff09b1d From c1700f91daea23fe2f7cc58a48c2a72b3f2fae84 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 6 Aug 2022 22:20:22 +0000 Subject: [PATCH 027/131] update with 9ab6d4c3279795fc8006a0741c2e06baa6705ef6 From 8dd7f39d570327a315cb4450bbd4598e7517fe62 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 29 Aug 2022 08:06:04 +0000 Subject: [PATCH 028/131] update with 12dd5a88f12e80b70ed8c62012ad38839be8f304 --- mb-artist-credit-splitter.user.js | 95 ++++++++++++++++++++++++++++++ niconico-genten-kaiki.user.js | 5 +- niconico-search-sort-table.user.js | 5 +- 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 mb-artist-credit-splitter.user.js diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js new file mode 100644 index 0000000..61de26b --- /dev/null +++ b/mb-artist-credit-splitter.user.js @@ -0,0 +1,95 @@ +// ==UserScript== +// @name MB: Artist Credit Splitter +// @version 1.0 +// @description TODO_WRITE_DESCRIPTION +// @namespace https://rinsuki.net/ +// @author rinsuki +// @match https://musicbrainz.org/* +// @grant none +// ==/UserScript== + +(function () { + 'use strict'; + + function getReactContainer(elem) { + const properties = Object.getOwnPropertyNames(elem); + const name = properties.find(x => x.startsWith("__reactContainer$")); + if (name != null) + return elem[name]; + } + + function waitDOMByObserve(root, check, options) { + const firstRes = check(); + if (firstRes != null) + return Promise.resolve(firstRes); + return new Promise(resolve => { + const observer = new MutationObserver(() => { + const res = check(); + if (res != null) { + observer.disconnect(); + resolve(res); + } + }); + observer.observe(root, { + childList: true, + subtree: options.subtree + }); + }); + } + + const LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT = "copiedArtistCredit"; + (async () => { + const bubble = await waitDOMByObserve(document.body, () => document.querySelector("#artist-credit-bubble"), { subtree: false }); + const buttons = await waitDOMByObserve(bubble, () => bubble.querySelector(".buttons"), { subtree: false }); + const button = document.createElement("button"); + button.type = "button"; + button.style.float = "left"; + button.textContent = "Split Automatically"; + button.addEventListener("click", async () => { + const container = getReactContainer(bubble); + if (container == null) + return alert("Failed to get React container"); + const tbody = bubble.querySelector("tbody"); + if (tbody == null) + return alert("Failed to get tbody"); + const props = container.memoizedState.element.props; + console.log(props); + const currentCredit = props.artistCredit.names.map(name => name.name + name.joinPhrase).join(""); + const RE = /([ ]*(\((CV[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})\)\/?|、| [&&] | feat[.: .: ] *)[ ]*)+/g; + let splittedCredits = []; + let lastIndex = 0; + for (const match of currentCredit.matchAll(RE)) { + splittedCredits.push([currentCredit.slice(lastIndex, match.index), match[0]]); + lastIndex = match.index + match[0].length; + } + if (currentCredit.slice(lastIndex).length > 0) + splittedCredits.push([currentCredit.slice(lastIndex), ""]); + if (!confirm("次のように指定します。よろしいですか?\n\n" + JSON.stringify(splittedCredits, null, 4))) + return; + for (let i = props.artistCredit.names.length; i < splittedCredits.length; i++) { + props.addName(); + await waitDOMByObserve(tbody, () => tbody.childNodes.item(i), { subtree: false }); + } + // localStorage.removeItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT) + // let p = waitLocalStorage(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT) + // props.copyArtistCredit() + // await p + // const stubArtistCredit = JSON.parse(localStorage.getItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT)!) + localStorage.setItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT, JSON.stringify(splittedCredits.map(([name, joinPhrase], i) => { + return { + joinPhrase, + name, + // artist: { + // entityType: "artist", + // uniqueID: stubArtistCredit[i].artist.uniqueID, + // name, + // } + }; + }))); + props.pasteArtistCredit(); + alert("finish!"); + }); + buttons.appendChild(button); + })(); + +}()); diff --git a/niconico-genten-kaiki.user.js b/niconico-genten-kaiki.user.js index a080c5d..d03fddf 100644 --- a/niconico-genten-kaiki.user.js +++ b/niconico-genten-kaiki.user.js @@ -82,7 +82,10 @@ body.${ccPrefix}-enableytclick #UadPlayer { pointer-events: none; } }); } postMessage(obj) { - this.iframe.contentWindow.postMessage(JSON.stringify(Object.assign(Object.assign({}, obj), { id: this.widgetid })), YOUTUBE_ORIGIN); + this.iframe.contentWindow.postMessage(JSON.stringify({ + ...obj, + id: this.widgetid, + }), YOUTUBE_ORIGIN); } onLoad(callback) { this.iframe.addEventListener("load", callback); diff --git a/niconico-search-sort-table.user.js b/niconico-search-sort-table.user.js index ea550c5..9bbf8c0 100644 --- a/niconico-search-sort-table.user.js +++ b/niconico-search-sort-table.user.js @@ -23,14 +23,13 @@ links[key] = [anchor.href, anchor.parentElement.classList.contains("active"), anchor]; } const Link = props => { - var _a, _b; let l = links[props.k]; if (l == null) { - (_a = originalSortList.querySelector("li.active")) === null || _a === void 0 ? void 0 : _a.remove(); + originalSortList.querySelector("li.active")?.remove(); return React.createElement("a", { style: { padding: "4px", color: "#fff", background: "#999" }, className: "active" }, props.children); } else { - (_b = l[2].parentElement) === null || _b === void 0 ? void 0 : _b.remove(); + l[2].parentElement?.remove(); return React.createElement("a", { href: l[0], style: { padding: "4px" } }, props.children); } }; From 61007922a53595ec676626fe873752902bbf046c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Aug 2022 00:43:16 +0000 Subject: [PATCH 029/131] update with 569fe215c40af1cc158dcb834438ad4613e3ea5d --- mb-artist-credit-splitter.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 61de26b..343c0c1 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MB: Artist Credit Splitter // @version 1.0 -// @description TODO_WRITE_DESCRIPTION +// @description いい感じに MusicBrainz のアーティストクレジットを分割します (失敗することもあります) // @namespace https://rinsuki.net/ // @author rinsuki // @match https://musicbrainz.org/* From b4bd3764848ddba7929085dc6e93731f2c5562fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 31 Aug 2022 09:51:54 +0000 Subject: [PATCH 030/131] update with bef562f6e2c3bb4b221376181f4b76d3e4ba9af8 --- mb-artist-credit-splitter.user.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 343c0c1..81d37e4 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name MB: Artist Credit Splitter -// @version 1.0 +// @version 1.0.1 // @description いい感じに MusicBrainz のアーティストクレジットを分割します (失敗することもあります) // @namespace https://rinsuki.net/ // @author rinsuki @@ -55,7 +55,7 @@ const props = container.memoizedState.element.props; console.log(props); const currentCredit = props.artistCredit.names.map(name => name.name + name.joinPhrase).join(""); - const RE = /([ ]*(\((CV[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})\)\/?|、| [&&] | feat[.: .: ] *)[ ]*)+/g; + const RE = /([ ]*(\((CV[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})\)\/?|、| [&&] |[\//]| feat[.: .: ] *)[ ]*)+/g; let splittedCredits = []; let lastIndex = 0; for (const match of currentCredit.matchAll(RE)) { From 9ac0f4529e06f4954200519d308bb54eaedcf91d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 1 Sep 2022 03:26:19 +0000 Subject: [PATCH 031/131] update with 36ebc3524e83bcb8954ccf06bfd6d58e70f69a3b --- mb-artist-credit-splitter.user.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 81d37e4..5521734 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name MB: Artist Credit Splitter -// @version 1.0.1 +// @version 1.0.2 // @description いい感じに MusicBrainz のアーティストクレジットを分割します (失敗することもあります) // @namespace https://rinsuki.net/ // @author rinsuki @@ -44,7 +44,7 @@ const button = document.createElement("button"); button.type = "button"; button.style.float = "left"; - button.textContent = "Split Automatically"; + button.textContent = "USERJS: Split Automatically"; button.addEventListener("click", async () => { const container = getReactContainer(bubble); if (container == null) @@ -55,7 +55,7 @@ const props = container.memoizedState.element.props; console.log(props); const currentCredit = props.artistCredit.names.map(name => name.name + name.joinPhrase).join(""); - const RE = /([ ]*(\((CV[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})\)\/?|、| [&&] |[\//]| feat[.: .: ] *)[ ]*)+/g; + const RE = /([ ]*(CV[.:.:] *|\((CV[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})\)\/?|、| [&&] |[\//]| feat[.: .: ] *)[ ]*)+/g; let splittedCredits = []; let lastIndex = 0; for (const match of currentCredit.matchAll(RE)) { From ac1f2da3b823d9cd2b3c02f6cd2e12eb8d539c60 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Sep 2022 23:01:17 +0000 Subject: [PATCH 032/131] update with c2c49c36a951485ad32e64e75ed564d2e6944939 From d811d37f2164dd59d9d15a3cc512f43cff0b3cee Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 2 Oct 2022 03:04:33 +0000 Subject: [PATCH 033/131] update with e8054cdd9d03b6b623fca367ca2df397dd96fc21 From 3eae1f212bd1fc6d66518900a426656fd0134089 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 2 Oct 2022 03:04:50 +0000 Subject: [PATCH 034/131] update with 544bf9f3079388055367edb37742500a1f6c49bd From 2f13cc0fe1d479ceaf7e786c98307dcdcfe4bbb6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Oct 2022 09:48:51 +0000 Subject: [PATCH 035/131] update with bffe3b949bba2097d74af615bf115cd80f579188 From 76f6822bcef124398987e05a08a039890c08e223 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 05:35:53 +0000 Subject: [PATCH 036/131] update with c196b41959c952406c5dac124df6013336930553 From 907f2cf96dd288a511b296c760b78b4f3acc4d14 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Nov 2022 07:16:35 +0000 Subject: [PATCH 037/131] update with 32e11b8e16b9646e7fc7699976e26550332bc8b0 From f28da7ab08ce15cf4bd89f916e1505c5321156f6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Nov 2022 07:44:09 +0000 Subject: [PATCH 038/131] update with 74b78010fc48886a86529b60cc6ccdfd9764e1a8 From a41cb4aed79bd24f80049f2a225726db59afe9e9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Nov 2022 07:44:28 +0000 Subject: [PATCH 039/131] update with 498b2391899e71b5cda69e22401ee8c1c896ea2f From a4611ead316469db9a62c6dcf74c6adc0450c734 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 Feb 2023 02:37:31 +0000 Subject: [PATCH 040/131] update with 0b29af13b3b492ba2ef493c6630fd72e243f7e4f From 863bf6ee82a133cd478cf933494f8775415f34f9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Feb 2023 14:48:18 +0000 Subject: [PATCH 041/131] update with 47c22697bcf1ce45637e95d07648254b0193c445 From 965f8ab85251370ea4021ae7ed16387dfd28637d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Feb 2023 06:40:50 +0000 Subject: [PATCH 042/131] update with d00ed31e18b656930b8124e4d6febf831f6d4be1 From cea3a5f173582fd8f8f238139a2787dc13971cf2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Feb 2023 13:38:34 +0000 Subject: [PATCH 043/131] update with 8933f932f5569ca94aaec90b8d8532da53535352 From 6f8f1096daa8c8ac4ec03e8c15f5181fdd3b23ff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Feb 2023 13:40:55 +0000 Subject: [PATCH 044/131] update with 27637f6aab52f05b4f9e6ae61dce8381bd94c87c From 40c50106cec1362bb7015fabc4ccd402742dec84 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 21:15:43 +0000 Subject: [PATCH 045/131] update with 07bcc1320688854726a72763c992b890b8d947be --- mb-artist-credit-splitter.user.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 5521734..abaa967 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -37,6 +37,19 @@ }); } + function splitCredit(input) { + const RE = /([ ]*(CV[.:.:] *|[\((](CV[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})[\))]\/?|、| [&&] |[\//]| feat[.: .: ] *)[ ]*)+/g; + const splittedCredits = []; + let lastIndex = 0; + for (const match of input.matchAll(RE)) { + splittedCredits.push([input.slice(lastIndex, match.index), match[0]]); + lastIndex = match.index + match[0].length; + } + if (input.slice(lastIndex).length > 0) + splittedCredits.push([input.slice(lastIndex), ""]); + return splittedCredits; + } + const LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT = "copiedArtistCredit"; (async () => { const bubble = await waitDOMByObserve(document.body, () => document.querySelector("#artist-credit-bubble"), { subtree: false }); @@ -54,16 +67,8 @@ return alert("Failed to get tbody"); const props = container.memoizedState.element.props; console.log(props); - const currentCredit = props.artistCredit.names.map(name => name.name + name.joinPhrase).join(""); - const RE = /([ ]*(CV[.:.:] *|\((CV[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})\)\/?|、| [&&] |[\//]| feat[.: .: ] *)[ ]*)+/g; - let splittedCredits = []; - let lastIndex = 0; - for (const match of currentCredit.matchAll(RE)) { - splittedCredits.push([currentCredit.slice(lastIndex, match.index), match[0]]); - lastIndex = match.index + match[0].length; - } - if (currentCredit.slice(lastIndex).length > 0) - splittedCredits.push([currentCredit.slice(lastIndex), ""]); + const currentCredit = props.artistCredit.names.map(name => name.name + (name.joinPhrase ?? "")).join(""); + const splittedCredits = splitCredit(currentCredit); if (!confirm("次のように指定します。よろしいですか?\n\n" + JSON.stringify(splittedCredits, null, 4))) return; for (let i = props.artistCredit.names.length; i < splittedCredits.length; i++) { From 1a18ef7479c90b3b3cd6430948a0ab011ff07bbd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Apr 2023 21:30:23 +0000 Subject: [PATCH 046/131] update with cffe0b6c286eb9b64261432587b6d1f7883bde54 From dcd4e513053e708a04b7419e6b799dc292db19ea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 19 Aug 2023 01:53:27 +0000 Subject: [PATCH 047/131] update with f3cf35962d6dd2196c4a01840042b638a4220307 From 7d9d65e4c59d88ee1ab3834badd03d0873766f14 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Sep 2023 17:51:26 +0000 Subject: [PATCH 048/131] update with 9431e1e48f6168bb59a6027b39c8a81ee00c9d8e --- mb-artist-credit-splitter.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index abaa967..7156fd8 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -38,7 +38,7 @@ } function splitCredit(input) { - const RE = /([ ]*(CV[.:.:] *|[\((](CV[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})[\))]\/?|、| [&&] |[\//]| feat[.: .: ] *)[ ]*)+/g; + const RE = /([ ]*((?:CV|cv)[.:.:] *|[\((]((?:CV|cv)[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})[\))]\/?|、|(?: & |&)|[\//]| feat[.: .: ] *)[ ]*)+/g; const splittedCredits = []; let lastIndex = 0; for (const match of input.matchAll(RE)) { From 08e1555c595f7ee8db9641fd62b1bc1e8b0cce13 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:21:37 +0000 Subject: [PATCH 049/131] update with 952cb3924c6068dbc4769194f96e67daf5d86af3 --- mb-artist-credit-splitter.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 7156fd8..75a9e4d 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -38,7 +38,7 @@ } function splitCredit(input) { - const RE = /([ ]*((?:CV|cv)[.:.:] *|[\((]((?:CV|cv)[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})[\))]\/?|、|(?: & |&)|[\//]| feat[.: .: ] *)[ ]*)+/g; + const RE = /([ ]*((?:CV|cv)[.:.:] *|[\((]((?:CV|cv)[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})[\))]\/?|[、,\//]|(?: & |&)| feat[.: .: ] *)[ ]*)+/g; const splittedCredits = []; let lastIndex = 0; for (const match of input.matchAll(RE)) { From df0633a37b2a29030e8cb4efe0284acac57d5d62 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:22:51 +0000 Subject: [PATCH 050/131] update with e53f3b362c9f35522cbc60ea572b79b38449e845 --- mb-artist-credit-splitter.user.js | 46 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 75a9e4d..646b859 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -11,9 +11,9 @@ (function () { 'use strict'; - function getReactContainer(elem) { + function getReactProps(elem) { const properties = Object.getOwnPropertyNames(elem); - const name = properties.find(x => x.startsWith("__reactContainer$")); + const name = properties.find(x => x.startsWith("__reactProps$")); if (name != null) return elem[name]; } @@ -38,7 +38,7 @@ } function splitCredit(input) { - const RE = /([ ]*((?:CV|cv)[.:.:] *|[\((]((?:CV|cv)[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})[\))]\/?|[、,\//]|(?: & |&)| feat[.: .: ] *)[ ]*)+/g; + const RE = /([ ]*((?:CV|cv)[.:.:] *|[\((]((?:CV|cv)[.:.:] *)?(?=[^)]{3,})|(?<=[^(]{3})[\))]\/?|[、,\//[]\[\]]|(?: & |&)| feat[.: .: ] *)[ ]*)+/g; const splittedCredits = []; let lastIndex = 0; for (const match of input.matchAll(RE)) { @@ -59,39 +59,37 @@ button.style.float = "left"; button.textContent = "USERJS: Split Automatically"; button.addEventListener("click", async () => { - const container = getReactContainer(bubble); - if (container == null) + const props = getReactProps(bubble); + console.log(props); + if (props == null) return alert("Failed to get React container"); const tbody = bubble.querySelector("tbody"); if (tbody == null) return alert("Failed to get tbody"); - const props = container.memoizedState.element.props; - console.log(props); - const currentCredit = props.artistCredit.names.map(name => name.name + (name.joinPhrase ?? "")).join(""); + const dispatch = props.children[0].props.children.props.dispatch; + dispatch({ type: "copy" }); + await new Promise(resolve => requestAnimationFrame(resolve)); + const currentCredit = JSON.parse(localStorage.getItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT) ?? "").names.map(name => name.name + (name.joinPhrase ?? "")).join(""); const splittedCredits = splitCredit(currentCredit); if (!confirm("次のように指定します。よろしいですか?\n\n" + JSON.stringify(splittedCredits, null, 4))) return; - for (let i = props.artistCredit.names.length; i < splittedCredits.length; i++) { - props.addName(); - await waitDOMByObserve(tbody, () => tbody.childNodes.item(i), { subtree: false }); - } // localStorage.removeItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT) // let p = waitLocalStorage(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT) // props.copyArtistCredit() // await p // const stubArtistCredit = JSON.parse(localStorage.getItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT)!) - localStorage.setItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT, JSON.stringify(splittedCredits.map(([name, joinPhrase], i) => { - return { - joinPhrase, - name, - // artist: { - // entityType: "artist", - // uniqueID: stubArtistCredit[i].artist.uniqueID, - // name, - // } - }; - }))); - props.pasteArtistCredit(); + localStorage.setItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT, JSON.stringify({ names: splittedCredits.map(([name, joinPhrase], i) => { + return { + joinPhrase, + name, + // artist: { + // entityType: "artist", + // uniqueID: stubArtistCredit[i].artist.uniqueID, + // name, + // } + }; + }) })); + dispatch({ type: "paste" }); alert("finish!"); }); buttons.appendChild(button); From 5bf0c9c477021ab91d693c3b8df08d152ea1f2ba Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 18:32:10 +0000 Subject: [PATCH 051/131] update with 5d8416e756e234ce2e0ed03f70880aeb4aefa1d9 From 21ae68c01650bda6c2dc2429f579c6594eb334d9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 18:33:36 +0000 Subject: [PATCH 052/131] update with 0eb7d8ac03896a8df711059695d3e152e3c1f31c From 944fadb99d6aac785fdba9c47918c6695340dd99 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 18:57:05 +0000 Subject: [PATCH 053/131] update with 76e90beb8d26563b25ea52cd1615779076d7d5f8 From 98c1daf47433729b88dd917cde3a7e18cdf41723 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 18:57:19 +0000 Subject: [PATCH 054/131] update with 6bc21dccae08fa40e922ea5701964f4138c99071 From 5a7297ad712d15614b45442f31188bb62a7a5335 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 19:12:02 +0000 Subject: [PATCH 055/131] update with cf3d354f1ce6c0839c3a785645b71671436fb40e From 6e31ab347ad152accffd5c74dee2dfa3dcab1f83 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 19:13:12 +0000 Subject: [PATCH 056/131] update with 0292762097d76795ae17a7056fe4b80816a1663e From d640febe70b52708f50cb1408e1f718659a613df Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 20:18:01 +0000 Subject: [PATCH 057/131] update with c406ef044cbd2686e9732ccca63be8dfddcef2ce From 65afa198e556fbc2c81d51776fb2f2e3db90e351 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 1 Feb 2025 04:07:41 +0000 Subject: [PATCH 058/131] update with 2e5b8fed0ecf6086df6d4e467deeddd506c3fe3f From 5d09b1b72cac051b15cf198bddc1a046464f2a3f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 12 Apr 2025 23:04:28 +0000 Subject: [PATCH 059/131] update with 1c80c7919c1d3e8ec45d2e9059f63f6253c2a765 From a53eb75693d7330c81eca24525e422fd518c9dc2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 12 Apr 2025 23:19:42 +0000 Subject: [PATCH 060/131] update with 640d9ca55f1552748e84cd3082afc688415ecd66 From 5efb6ba9d872ec3fa75d9954b0211233f8536477 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 15 Jun 2025 13:20:27 +0000 Subject: [PATCH 061/131] update with adf8d8f38ffb275a3a48998c6aad4d5f7aca1ee6 --- mb-artist-credit-splitter-by-ai.user.js | 108 ++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 mb-artist-credit-splitter-by-ai.user.js diff --git a/mb-artist-credit-splitter-by-ai.user.js b/mb-artist-credit-splitter-by-ai.user.js new file mode 100644 index 0000000..9e34896 --- /dev/null +++ b/mb-artist-credit-splitter-by-ai.user.js @@ -0,0 +1,108 @@ +// ==UserScript== +// @name MB: Artist Credit Splitter, but Powered by AI +// @version 1.0.2 +// @description OpenRouter でいい感じに MusicBrainz のアーティストクレジットを分割します (失敗することもあります) +// @namespace https://rinsuki.net/ +// @author rinsuki +// @match https://musicbrainz.org/* +// @grant GM_getValue +// @grant GM_setValue +// @grant GM.registerMenuCommand +// @grant GM.xmlHttpRequest +// ==/UserScript== + +(function () { + 'use strict'; + + const SYSTEM_PROMPT = [ + "あなたはアーティストクレジットを分割するプロフェッショナルです。", + "あなたは与えられた文字列を分割し、適切なオブジェクトの配列に変換する必要があります。", + "それぞれのオブジェクトは、`name`と`joinPhrase`の2つのフィールドを持つ必要があります。", + "`name`フィールドにはアーティストの名前を、`joinPhrase`フィールドには次の名前と結合するために使用されるフレーズを含める必要があります。", + "ルール:", + "* 出力は、入力のすべての文字を、そのままの文字で、削除せずに含める必要があります。句読点や括弧も全て含めます (だいたいの場合はそれらは`joinPhrase`に含めることになるでしょう)。文字種を変換してもいけません (例えば `、` を `,` に変換することはできません)。出力が入力と一致しない場合、ユーザーにエラーが表示されます。", + "* 出力は、1つのオブジェクトに付き1人のアーティスト(個人またはグループ)を含める必要があります。つまり、CVクレジットを1つのアーティストオブジェクトに入力することはできません。たとえば、`キャラ (CV. 人物名)`を`name`に設定することは許可されていません。", + "* `joinPharse`に人物名を含めることはできません。CVクレジットなどが含まれる場合は、人物名の部分でオブジェクトを分割し、次のオブジェクトの`name`に代入してください。", + "* 出力はJSON形式をそのまま出力する必要があります (Markdownのコードブロックなどは不要です)。", + ].join("\n"); + const exampleInputs = [ + { role: "user", content: "せるふとぷりん(稲垣好 / 市ノ瀬加那)" }, + { role: "assistant", content: JSON.stringify([{ "name": "せるふ", "joinPhrase": "と" }, { "name": "ぷりん", "joinPhrase": "(" }, { "name": "稲垣好", "joinPhrase": " / " }, { "name": "市ノ瀬加那", "joinPhrase": ")" }]) }, + { role: "user", content: "Triad Primus [渋谷凛(CV:福原綾香)×神谷奈緒(CV:松井恵理子)×北条加蓮(CV:渕上舞)]" }, + { role: "assistant", content: JSON.stringify([ + { "name": "Triad Primus", "joinPhrase": " [" }, + { "name": "渋谷凛", "joinPhrase": "(CV:" }, + { "name": "福原綾香", "joinPhrase": ")×" }, + { "name": "神谷奈緒", "joinPhrase": "(CV:" }, + { "name": "松井恵理子", "joinPhrase": ")×" }, + { "name": "北条加蓮", "joinPhrase": "(CV:" }, + { "name": "渕上舞", "joinPhrase": ")]" }, + ]) }, + ]; + function copiedArtistCreditToPlainString(artistCredit) { + return artistCredit.map(credit => credit.name + credit.joinPhrase).join(""); + } + GM.registerMenuCommand("Split clipboard content", () => { + const inputString = localStorage.getItem("copiedArtistCredit"); + if (inputString == null) { + alert("No copied artist credit found."); + return; + } + const inputJSON = JSON.parse(inputString); + const input = copiedArtistCreditToPlainString(inputJSON.names); + let apiKey = GM_getValue("openrouter_api_key"); + if (apiKey == null) { + apiKey = prompt("Please input your OpenRouter API key") ?? undefined; + if (apiKey == null) { + return; + } + GM_setValue("openrouter_api_key", apiKey); + } + const div = document.createElement("div"); + div.style.position = "fixed"; + div.style.top = "0"; + div.style.left = "0"; + div.style.width = "100%"; + div.style.height = "100%"; + div.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; + div.style.zIndex = "9999"; + div.style.display = "flex"; + div.style.alignItems = "center"; + div.style.justifyContent = "center"; + div.style.color = "white"; + div.style.fontSize = "20px"; + div.textContent = "Asking to the LLM..."; + document.body.appendChild(div); + fetch("https://openrouter.ai/api/v1/chat/completions", { + method: "POST", + headers: { + Authorization: `Bearer ${apiKey}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + model: prompt("model", "google/gemini-2.0-flash-exp:free"), + temperature: 0, + messages: [ + { role: "system", content: SYSTEM_PROMPT }, + ...exampleInputs, + { role: "user", content: input }, + ], + }) + }).then(r => r.json()).then(r => { + div.remove(); + console.log(r); + const generated = JSON.parse(r.choices[0].message.content); + const output = copiedArtistCreditToPlainString(generated); + if (output !== input) { + alert("LLM's output does not match with input. The LLM moment...\n\n" + input + "\n" + output); + return; + } + if (confirm("Done! ... according to the LLM (" + r.model + ").\n\n" + generated.map(a => JSON.stringify(a)).join("\n"))) { + localStorage.setItem("copiedArtistCredit", JSON.stringify({ + names: generated, + })); + } + }); + }); + +}()); From afc2acc6be7a0d59e2cb633ac79ce6f4a8d33d77 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 15 Jun 2025 14:23:30 +0000 Subject: [PATCH 062/131] update with 967440adc0635818aba12dd3f5b39ea650d2bb2a From 11daffb45a82bbb90145f1533aaa587f08b82ee4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 28 Jun 2025 21:56:41 +0000 Subject: [PATCH 063/131] update with b352a489f1e95e331936598f0fee9d0c6492cf5b --- mastodon-show-act-users.user.js | 2 +- mb-artist-credit-splitter-by-ai.user.js | 2 +- mb-artist-credit-splitter.user.js | 2 +- niconico-genten-kaiki.user.js | 2 +- niconico-search-sort-table.user.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mastodon-show-act-users.user.js b/mastodon-show-act-users.user.js index d815bb7..f6f1d19 100644 --- a/mastodon-show-act-users.user.js +++ b/mastodon-show-act-users.user.js @@ -149,4 +149,4 @@ statusMetaDiv.appendChild(myDiv); })(); -}()); +})(); diff --git a/mb-artist-credit-splitter-by-ai.user.js b/mb-artist-credit-splitter-by-ai.user.js index 9e34896..fec64d6 100644 --- a/mb-artist-credit-splitter-by-ai.user.js +++ b/mb-artist-credit-splitter-by-ai.user.js @@ -105,4 +105,4 @@ }); }); -}()); +})(); diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 646b859..ff6040e 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -95,4 +95,4 @@ buttons.appendChild(button); })(); -}()); +})(); diff --git a/niconico-genten-kaiki.user.js b/niconico-genten-kaiki.user.js index d03fddf..9d091b1 100644 --- a/niconico-genten-kaiki.user.js +++ b/niconico-genten-kaiki.user.js @@ -159,4 +159,4 @@ body.${ccPrefix}-enableytclick #UadPlayer { pointer-events: none; } } })(); -}()); +})(); diff --git a/niconico-search-sort-table.user.js b/niconico-search-sort-table.user.js index 9bbf8c0..5d09326 100644 --- a/niconico-search-sort-table.user.js +++ b/niconico-search-sort-table.user.js @@ -58,4 +58,4 @@ React.createElement(Row, { title: "\u518D\u751F\u6642\u9593", k: "l", desc: "\u9577\u3044", asc: "\u77ED\u3044" })), li); originalSortList.appendChild(li); -}()); +})(); From 0ecba855b6016afedca2415950fc989543062ea3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 28 Jun 2025 23:50:30 +0000 Subject: [PATCH 064/131] update with d37e037227adaf599266d3d0ff2a9630de310404 From abeafd78d45a48338ca0a60eb73a050f04499e94 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 29 Jun 2025 08:35:47 +0000 Subject: [PATCH 065/131] update with f460a2866ac3b5167d3f4ccd7b459a9acb070fc8 --- mb-seed-urls-to-release-recordings.user.js | 142 +++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 mb-seed-urls-to-release-recordings.user.js diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js new file mode 100644 index 0000000..3a2bcb8 --- /dev/null +++ b/mb-seed-urls-to-release-recordings.user.js @@ -0,0 +1,142 @@ +// ==UserScript== +// @name MusicBrainz: Seed URLs to Release Recordings +// @namespace https://rinsuki.net +// @match https://musicbrainz.org/release/*/edit-relationships +// @require https://cdn.jsdelivr.net/npm/zod@3.24.4/lib/index.umd.js#sha256=25623a1c332de4571b75a2a6fb8be1fae40180c8fdfd7b4420f09bea727cee1c +// @grant none +// @version 0.1 +// @author rinsuki +// ==/UserScript== + +(function () { + 'use strict'; + + //#region node_modules/.pnpm/typedbrainz@0.1.3/node_modules/typedbrainz/lib/index.js + // SPDX-License-Identifier: MIT + function isReleaseRelationshipEditor(relationshipEditor) { + return relationshipEditor.state?.entity.entityType === "release"; + } + + //#endregion + + const zSeedJSON = Zod.object({ + recordings: Zod.record(Zod.string(), // recording id + Zod.object({ + url: Zod.string().url(), + types: Zod.array(Zod.string()), // link_type UUID + })), + note: Zod.string(), + }); + async function main() { + // check hash + const urlParams = new URLSearchParams(location.hash.slice(1)); + const rawJson = urlParams.get("seed-urls-v1"); + if (rawJson == null) + return; + while (window.MB?.relationshipEditor?.state == null) { + console.log("Waiting for window.MB.relationshipEditor?.state to be defined..."); + await new Promise(resolve => setTimeout(resolve, 100)); + } + console.log("!", window.MB.relationshipEditor); + if (!isReleaseRelationshipEditor(window.MB.relationshipEditor)) { + return; + } + const { linkedEntities, relationshipEditor } = window.MB; + const button = document.createElement("button"); + button.textContent = "Seed URLs to Recordings"; + button.style.zoom = "2"; + button.addEventListener("click", () => { + const json = zSeedJSON.parse(JSON.parse(rawJson)); + console.log(json); + const errors = []; + relationshipEditor.dispatch({ + type: "update-edit-note", + editNote: (relationshipEditor.state.editNoteField.value + "\n" + json.note + "\n''Powered by \"" + GM_info.script.name + "\" script''").trim(), + }); + for (const medium of relationshipEditor.state.entity.mediums) { + console.log(medium); + for (const track of medium.tracks ?? []) { + console.log(track); + if (track.recording.gid in json.recordings) { + const rel = json.recordings[track.recording.gid]; + delete json.recordings[track.recording.gid]; + for (const relType of rel.types) { + let linkTypeID; + if (relType in linkedEntities.link_type && linkedEntities.link_type[relType].type0 === "recording" && linkedEntities.link_type[relType].type1 === "url") { + linkTypeID = linkedEntities.link_type[relType].id; + } + if (linkTypeID == null) { + for (const lt of Object.values(linkedEntities.link_type)) { + if (lt.type0 !== "recording") + continue; + if (lt.type1 !== "url") + continue; + console.log(lt); + if (lt.name === relType) { + linkTypeID = lt.id; + break; + } + } + } + if (linkTypeID == null) { + errors.push(`Failed to find link type ${JSON.stringify(relType)} for recording ${track.recording.gid}`); + continue; + } + // it will be marked as "incomplete" in the UI, but actually working? + // @see https://github.com/metabrainz/musicbrainz-server/blob/e214b4d3c13f7ee6b2eb2f9c186ecab310354a5b/root/static/scripts/relationship-editor/components/RelationshipItem.js#L153-L163 + relationshipEditor.dispatch({ + type: "update-relationship-state", + sourceEntity: track.recording, + oldRelationshipState: null, + newRelationshipState: { + id: relationshipEditor.getRelationshipStateId(null), + linkOrder: 0, + linkTypeID, + _lineage: ["added"], + _original: null, + _status: 1, + attributes: null, + begin_date: null, // TODO: support? + end_date: null, // TODO: support? + editsPending: false, + ended: false, + entity0: track.recording, + entity0_credit: "", + entity1: { + decoded: "", + editsPending: false, + entityType: "url", + gid: "", + name: rel.url, + id: relationshipEditor.getRelationshipStateId(null), + last_updated: null, + href_url: "", + pretty_name: "", + }, + entity1_credit: "test", + }, + batchSelectionCount: undefined, + creditsToChangeForSource: "", + creditsToChangeForTarget: "", + }); + } + } + } + } + for (const remainingRecordingId of Object.keys(json.recordings)) { + errors.push(`Can't find ${remainingRecordingId}, skipped`); + } + if (errors.length === 0) { + alert("All URLs seeded successfully!"); + } + else { + alert("URLs seeded, but with some errors:\n" + errors.map(x => "* " + x).join("\n")); + } + }); + const before = document.querySelector("#content > p"); + before.parentElement.insertBefore(button, before); + console.log("done"); + } + main(); + +})(); From dbf9b48a2184c55479d795c68fbbf05a78b17328 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 29 Jun 2025 08:40:35 +0000 Subject: [PATCH 066/131] update with 5dec80ac1b31b5db2933ba7ab4c12f30354e8a4a --- mb-seed-urls-to-release-recordings.user.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index 3a2bcb8..eb0a12f 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -26,6 +26,7 @@ types: Zod.array(Zod.string()), // link_type UUID })), note: Zod.string(), + version: Zod.number(), }); async function main() { // check hash @@ -47,6 +48,10 @@ button.style.zoom = "2"; button.addEventListener("click", () => { const json = zSeedJSON.parse(JSON.parse(rawJson)); + if (json.version !== 1) { + alert("Unsupported version: " + json.version + ", please update the script (or contacat to seeder's developer)"); + return; + } console.log(json); const errors = []; relationshipEditor.dispatch({ From 9de8916e75b6215eec5a53690747fbc2e0211e9f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 29 Jun 2025 08:43:31 +0000 Subject: [PATCH 067/131] update with a928b88e9cac4b8fbc04d8ac1fff09ba9f3037aa --- mb-seed-urls-to-release-recordings.user.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index eb0a12f..5541c28 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -6,6 +6,7 @@ // @grant none // @version 0.1 // @author rinsuki +// @description Import recording-url relationship to release's recordings. // ==/UserScript== (function () { From f2fb166c494b6eb6b1bd58a080448a5d323dfc86 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 29 Jun 2025 10:36:24 +0000 Subject: [PATCH 068/131] update with 4c75aafc3c88a8c449e337389b0619db7cb7dff4 --- mb-seed-urls-to-release-recordings.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index 5541c28..d42e3b5 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -119,7 +119,7 @@ href_url: "", pretty_name: "", }, - entity1_credit: "test", + entity1_credit: "", }, batchSelectionCount: undefined, creditsToChangeForSource: "", From 3aa5130a9cbf2000e2b05ac10e331f0b70ba74b7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 29 Jun 2025 21:54:25 +0000 Subject: [PATCH 069/131] update with 9327df97a11d41832b747a2396527e971868e0d9 --- mb-seed-urls-to-release-recordings.user.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index d42e3b5..b5afba6 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -4,7 +4,7 @@ // @match https://musicbrainz.org/release/*/edit-relationships // @require https://cdn.jsdelivr.net/npm/zod@3.24.4/lib/index.umd.js#sha256=25623a1c332de4571b75a2a6fb8be1fae40180c8fdfd7b4420f09bea727cee1c // @grant none -// @version 0.1 +// @version 0.1.1 // @author rinsuki // @description Import recording-url relationship to release's recordings. // ==/UserScript== @@ -141,6 +141,7 @@ }); const before = document.querySelector("#content > p"); before.parentElement.insertBefore(button, before); + button.focus(); console.log("done"); } main(); From e1d15b5bc0e6f8f3195a452919c5e15526c965a8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 20:44:52 +0000 Subject: [PATCH 070/131] update with 5cbe0880ae00815b278339af4566399ec1b89196 --- mb-seed-urls-to-release-recordings.user.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index b5afba6..fafc203 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -2,9 +2,10 @@ // @name MusicBrainz: Seed URLs to Release Recordings // @namespace https://rinsuki.net // @match https://musicbrainz.org/release/*/edit-relationships +// @match https://*.musicbrainz.org/release/*/edit-relationships // @require https://cdn.jsdelivr.net/npm/zod@3.24.4/lib/index.umd.js#sha256=25623a1c332de4571b75a2a6fb8be1fae40180c8fdfd7b4420f09bea727cee1c // @grant none -// @version 0.1.1 +// @version 0.1.2 // @author rinsuki // @description Import recording-url relationship to release's recordings. // ==/UserScript== From 35c405b8a1e9f2be523adc48d640fe539309dfde Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 21:25:39 +0000 Subject: [PATCH 071/131] update with 7424f895731fa260f1205bd7f38176f38d66be50 --- mb-seed-urls-to-release-recordings.user.js | 144 ++++++++++++--------- 1 file changed, 83 insertions(+), 61 deletions(-) diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index fafc203..be4cb84 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -5,7 +5,7 @@ // @match https://*.musicbrainz.org/release/*/edit-relationships // @require https://cdn.jsdelivr.net/npm/zod@3.24.4/lib/index.umd.js#sha256=25623a1c332de4571b75a2a6fb8be1fae40180c8fdfd7b4420f09bea727cee1c // @grant none -// @version 0.1.2 +// @version 0.2.0 // @author rinsuki // @description Import recording-url relationship to release's recordings. // ==/UserScript== @@ -22,12 +22,23 @@ //#endregion const zSeedJSON = Zod.object({ + version: Zod.literal(1), recordings: Zod.record(Zod.string(), // recording id Zod.object({ url: Zod.string().url(), types: Zod.array(Zod.string()), // link_type UUID })), note: Zod.string(), + }).or(Zod.object({ + version: Zod.literal(2), + recordings: Zod.record(Zod.string(), // recording id + Zod.array(Zod.object({ + url: Zod.string().url(), + types: Zod.array(Zod.string()), // link_type UUID + }))), + note: Zod.string(), + })); + const zSeedJSONFallback = Zod.object({ version: Zod.number(), }); async function main() { @@ -49,12 +60,13 @@ button.textContent = "Seed URLs to Recordings"; button.style.zoom = "2"; button.addEventListener("click", () => { - const json = zSeedJSON.parse(JSON.parse(rawJson)); - if (json.version !== 1) { - alert("Unsupported version: " + json.version + ", please update the script (or contacat to seeder's developer)"); + const anyJSON = JSON.parse(rawJson); + const baseJSON = zSeedJSONFallback.parse(anyJSON); + if (![1, 2].includes(baseJSON.version)) { + alert(`Unsupported version: ${baseJSON.version}, please update the script (or contact the seeder's developer)`); return; } - console.log(json); + const json = zSeedJSON.parse(anyJSON); const errors = []; relationshipEditor.dispatch({ type: "update-edit-note", @@ -65,67 +77,76 @@ for (const track of medium.tracks ?? []) { console.log(track); if (track.recording.gid in json.recordings) { - const rel = json.recordings[track.recording.gid]; + const rels = json.recordings[track.recording.gid]; delete json.recordings[track.recording.gid]; - for (const relType of rel.types) { - let linkTypeID; - if (relType in linkedEntities.link_type && linkedEntities.link_type[relType].type0 === "recording" && linkedEntities.link_type[relType].type1 === "url") { - linkTypeID = linkedEntities.link_type[relType].id; + const alreadyAddedDomains = new Set(); + for (const rel of Array.isArray(rels) ? rels : [rels]) { + const relUrl = new URL(rel.url); + if (alreadyAddedDomains.has(relUrl.hostname)) { + errors.push(`You can't add multiple same domain URLs for a recording at once! Skipped ${rel.url} for recording ${track.recording.gid}`); + continue; } - if (linkTypeID == null) { - for (const lt of Object.values(linkedEntities.link_type)) { - if (lt.type0 !== "recording") - continue; - if (lt.type1 !== "url") - continue; - console.log(lt); - if (lt.name === relType) { - linkTypeID = lt.id; - break; + alreadyAddedDomains.add(relUrl.hostname); + for (const relType of rel.types) { + let linkTypeID; + if (relType in linkedEntities.link_type && linkedEntities.link_type[relType].type0 === "recording" && linkedEntities.link_type[relType].type1 === "url") { + linkTypeID = linkedEntities.link_type[relType].id; + } + if (linkTypeID == null) { + for (const lt of Object.values(linkedEntities.link_type)) { + if (lt.type0 !== "recording") + continue; + if (lt.type1 !== "url") + continue; + console.log(lt); + if (lt.name === relType) { + linkTypeID = lt.id; + break; + } } } - } - if (linkTypeID == null) { - errors.push(`Failed to find link type ${JSON.stringify(relType)} for recording ${track.recording.gid}`); - continue; - } - // it will be marked as "incomplete" in the UI, but actually working? - // @see https://github.com/metabrainz/musicbrainz-server/blob/e214b4d3c13f7ee6b2eb2f9c186ecab310354a5b/root/static/scripts/relationship-editor/components/RelationshipItem.js#L153-L163 - relationshipEditor.dispatch({ - type: "update-relationship-state", - sourceEntity: track.recording, - oldRelationshipState: null, - newRelationshipState: { - id: relationshipEditor.getRelationshipStateId(null), - linkOrder: 0, - linkTypeID, - _lineage: ["added"], - _original: null, - _status: 1, - attributes: null, - begin_date: null, // TODO: support? - end_date: null, // TODO: support? - editsPending: false, - ended: false, - entity0: track.recording, - entity0_credit: "", - entity1: { - decoded: "", - editsPending: false, - entityType: "url", - gid: "", - name: rel.url, + if (linkTypeID == null) { + errors.push(`Failed to find link type ${JSON.stringify(relType)} for recording ${track.recording.gid}`); + continue; + } + // it will be marked as "incomplete" in the UI, but actually working? + // @see https://github.com/metabrainz/musicbrainz-server/blob/e214b4d3c13f7ee6b2eb2f9c186ecab310354a5b/root/static/scripts/relationship-editor/components/RelationshipItem.js#L153-L163 + relationshipEditor.dispatch({ + type: "update-relationship-state", + sourceEntity: track.recording, + oldRelationshipState: null, + newRelationshipState: { id: relationshipEditor.getRelationshipStateId(null), - last_updated: null, - href_url: "", - pretty_name: "", + linkOrder: 0, + linkTypeID, + _lineage: ["added"], + _original: null, + _status: 1, + attributes: null, + begin_date: null, // TODO: support? + end_date: null, // TODO: support? + editsPending: false, + ended: false, + entity0: track.recording, + entity0_credit: "", + entity1: { + decoded: "", + editsPending: false, + entityType: "url", + gid: "", + name: rel.url, + id: relationshipEditor.getRelationshipStateId(null), + last_updated: null, + href_url: "", + pretty_name: "", + }, + entity1_credit: "", }, - entity1_credit: "", - }, - batchSelectionCount: undefined, - creditsToChangeForSource: "", - creditsToChangeForTarget: "", - }); + batchSelectionCount: undefined, + creditsToChangeForSource: "", + creditsToChangeForTarget: "", + }); + } } } } @@ -134,7 +155,8 @@ errors.push(`Can't find ${remainingRecordingId}, skipped`); } if (errors.length === 0) { - alert("All URLs seeded successfully!"); + button.textContent = "URLs seeded successfully!"; + button.disabled = true; } else { alert("URLs seeded, but with some errors:\n" + errors.map(x => "* " + x).join("\n")); From 27e451e1f6d73521396418148d195d0b6f6dc5ad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Jul 2025 00:49:59 +0000 Subject: [PATCH 072/131] update with bb0ff4aa8e2acc7e4537f546254f585e5ce99553 --- mb-seed-urls-to-release-recordings.user.js | 167 +++++++++++---------- 1 file changed, 88 insertions(+), 79 deletions(-) diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index be4cb84..fb6e18b 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -5,7 +5,7 @@ // @match https://*.musicbrainz.org/release/*/edit-relationships // @require https://cdn.jsdelivr.net/npm/zod@3.24.4/lib/index.umd.js#sha256=25623a1c332de4571b75a2a6fb8be1fae40180c8fdfd7b4420f09bea727cee1c // @grant none -// @version 0.2.0 +// @version 0.2.1 // @author rinsuki // @description Import recording-url relationship to release's recordings. // ==/UserScript== @@ -41,6 +41,53 @@ const zSeedJSONFallback = Zod.object({ version: Zod.number(), }); + + function applyRelationships(relationships, editNote, relationshipEditor) { + relationshipEditor.dispatch({ + type: "update-edit-note", + editNote: (relationshipEditor.state.editNoteField.value + "\n" + editNote + "\n''Powered by \"" + GM_info.script.name + "\" script (" + GM_info.script.version + ")''").trim(), + }); + for (const relationship of relationships) { + // it will be marked as "incomplete" in the UI, but actually working? + // @see https://github.com/metabrainz/musicbrainz-server/blob/e214b4d3c13f7ee6b2eb2f9c186ecab310354a5b/root/static/scripts/relationship-editor/components/RelationshipItem.js#L153-L163 + relationshipEditor.dispatch({ + type: "update-relationship-state", + sourceEntity: relationship.recording, + oldRelationshipState: null, + newRelationshipState: { + id: relationshipEditor.getRelationshipStateId(null), + linkOrder: 0, + linkTypeID: relationship.linkTypeID, + _lineage: ["added"], + _original: null, + _status: 1, + attributes: null, + begin_date: null, // TODO: support? + end_date: null, // TODO: support? + editsPending: false, + ended: false, + entity0: relationship.recording, + entity0_credit: "", + entity1: { + decoded: "", + editsPending: false, + entityType: "url", + gid: "", + name: relationship.url, + id: relationshipEditor.getRelationshipStateId(null), + last_updated: null, + href_url: "", + pretty_name: "", + }, + entity1_credit: "", + }, + batchSelectionCount: undefined, + creditsToChangeForSource: "", + creditsToChangeForTarget: "", + }); + } + } + async function main() { // check hash const urlParams = new URLSearchParams(location.hash.slice(1)); @@ -68,98 +115,60 @@ } const json = zSeedJSON.parse(anyJSON); const errors = []; - relationshipEditor.dispatch({ - type: "update-edit-note", - editNote: (relationshipEditor.state.editNoteField.value + "\n" + json.note + "\n''Powered by \"" + GM_info.script.name + "\" script''").trim(), - }); - for (const medium of relationshipEditor.state.entity.mediums) { - console.log(medium); - for (const track of medium.tracks ?? []) { - console.log(track); - if (track.recording.gid in json.recordings) { - const rels = json.recordings[track.recording.gid]; - delete json.recordings[track.recording.gid]; - const alreadyAddedDomains = new Set(); - for (const rel of Array.isArray(rels) ? rels : [rels]) { - const relUrl = new URL(rel.url); - if (alreadyAddedDomains.has(relUrl.hostname)) { - errors.push(`You can't add multiple same domain URLs for a recording at once! Skipped ${rel.url} for recording ${track.recording.gid}`); - continue; - } - alreadyAddedDomains.add(relUrl.hostname); - for (const relType of rel.types) { - let linkTypeID; - if (relType in linkedEntities.link_type && linkedEntities.link_type[relType].type0 === "recording" && linkedEntities.link_type[relType].type1 === "url") { - linkTypeID = linkedEntities.link_type[relType].id; - } - if (linkTypeID == null) { - for (const lt of Object.values(linkedEntities.link_type)) { - if (lt.type0 !== "recording") - continue; - if (lt.type1 !== "url") - continue; - console.log(lt); - if (lt.name === relType) { - linkTypeID = lt.id; - break; - } - } - } - if (linkTypeID == null) { - errors.push(`Failed to find link type ${JSON.stringify(relType)} for recording ${track.recording.gid}`); + const preparedRelationships = []; + for (const track of relationshipEditor.state.entity.mediums.flatMap(m => m.tracks ?? [])) { + if (!(track.recording.gid in json.recordings)) + continue; + const rels = json.recordings[track.recording.gid]; + delete json.recordings[track.recording.gid]; + const alreadyAddedDomains = new Set(); + for (const rel of Array.isArray(rels) ? rels : [rels]) { + const relUrl = new URL(rel.url); + if (alreadyAddedDomains.has(relUrl.hostname)) { + errors.push(`You can't add multiple same domain URLs for a recording at once! URL: ${rel.url} Recording: ${track.recording.gid}`); + continue; + } + alreadyAddedDomains.add(relUrl.hostname); + for (const relType of rel.types) { + let linkTypeID; + if (relType in linkedEntities.link_type && linkedEntities.link_type[relType].type0 === "recording" && linkedEntities.link_type[relType].type1 === "url") { + linkTypeID = linkedEntities.link_type[relType].id; + } + if (linkTypeID == null) { + for (const lt of Object.values(linkedEntities.link_type)) { + if (lt.type0 !== "recording") continue; + if (lt.type1 !== "url") + continue; + console.log(lt); + if (lt.name === relType) { + linkTypeID = lt.id; + break; } - // it will be marked as "incomplete" in the UI, but actually working? - // @see https://github.com/metabrainz/musicbrainz-server/blob/e214b4d3c13f7ee6b2eb2f9c186ecab310354a5b/root/static/scripts/relationship-editor/components/RelationshipItem.js#L153-L163 - relationshipEditor.dispatch({ - type: "update-relationship-state", - sourceEntity: track.recording, - oldRelationshipState: null, - newRelationshipState: { - id: relationshipEditor.getRelationshipStateId(null), - linkOrder: 0, - linkTypeID, - _lineage: ["added"], - _original: null, - _status: 1, - attributes: null, - begin_date: null, // TODO: support? - end_date: null, // TODO: support? - editsPending: false, - ended: false, - entity0: track.recording, - entity0_credit: "", - entity1: { - decoded: "", - editsPending: false, - entityType: "url", - gid: "", - name: rel.url, - id: relationshipEditor.getRelationshipStateId(null), - last_updated: null, - href_url: "", - pretty_name: "", - }, - entity1_credit: "", - }, - batchSelectionCount: undefined, - creditsToChangeForSource: "", - creditsToChangeForTarget: "", - }); } } + if (linkTypeID == null) { + errors.push(`Failed to find link type ${JSON.stringify(relType)} for recording ${track.recording.gid}`); + continue; + } + preparedRelationships.push({ + recording: track.recording, + url: rel.url, + linkTypeID, + }); } } } for (const remainingRecordingId of Object.keys(json.recordings)) { - errors.push(`Can't find ${remainingRecordingId}, skipped`); + errors.push(`Failed to find recording: ${remainingRecordingId}, you might be need to expand mediums before press seed button.`); } if (errors.length === 0) { + applyRelationships(preparedRelationships, json.note, relationshipEditor); button.textContent = "URLs seeded successfully!"; button.disabled = true; } else { - alert("URLs seeded, but with some errors:\n" + errors.map(x => "* " + x).join("\n")); + alert("Failed to seed urls:\n" + errors.map(x => "* " + x).join("\n")); } }); const before = document.querySelector("#content > p"); From 6cacd5f25fac558c84336a846ca08732c46a3073 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 05:05:25 +0000 Subject: [PATCH 073/131] update with d8cd2a8d9b1a69f9530cce35d8a35c91f925a503 --- mb-artist-credit-splitter-by-ai.user.js | 66 ++++++++++++++++++++++++- niconico-genten-kaiki.user.js | 6 ++- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/mb-artist-credit-splitter-by-ai.user.js b/mb-artist-credit-splitter-by-ai.user.js index fec64d6..d235ba5 100644 --- a/mb-artist-credit-splitter-by-ai.user.js +++ b/mb-artist-credit-splitter-by-ai.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name MB: Artist Credit Splitter, but Powered by AI -// @version 1.0.2 +// @version 1.1.0 // @description OpenRouter でいい感じに MusicBrainz のアーティストクレジットを分割します (失敗することもあります) // @namespace https://rinsuki.net/ // @author rinsuki @@ -42,7 +42,7 @@ function copiedArtistCreditToPlainString(artistCredit) { return artistCredit.map(credit => credit.name + credit.joinPhrase).join(""); } - GM.registerMenuCommand("Split clipboard content", () => { + GM.registerMenuCommand("Split clipboard content (OpenRouter)", () => { const inputString = localStorage.getItem("copiedArtistCredit"); if (inputString == null) { alert("No copied artist credit found."); @@ -104,5 +104,67 @@ } }); }); + GM.registerMenuCommand("Split clipboard content (OpenAI)", () => { + const inputString = localStorage.getItem("copiedArtistCredit"); + if (inputString == null) { + alert("No copied artist credit found."); + return; + } + const inputJSON = JSON.parse(inputString); + const input = copiedArtistCreditToPlainString(inputJSON.names); + let apiKey = GM_getValue("openai_api_key"); + if (apiKey == null) { + apiKey = prompt("Please input your OpenAI API key") ?? undefined; + if (apiKey == null) { + return; + } + GM_setValue("openai_api_key", apiKey); + } + const div = document.createElement("div"); + div.style.position = "fixed"; + div.style.top = "0"; + div.style.left = "0"; + div.style.width = "100%"; + div.style.height = "100%"; + div.style.backgroundColor = "rgba(0, 0, 0, 0.5)"; + div.style.zIndex = "9999"; + div.style.display = "flex"; + div.style.alignItems = "center"; + div.style.justifyContent = "center"; + div.style.color = "white"; + div.style.fontSize = "20px"; + div.textContent = "Asking to the LLM..."; + document.body.appendChild(div); + fetch("https://api.openai.com/v1/chat/completions", { + method: "POST", + headers: { + Authorization: `Bearer ${apiKey}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + model: prompt("model", "gpt-4.1-mini"), + temperature: 0, + messages: [ + { role: "system", content: SYSTEM_PROMPT }, + ...exampleInputs, + { role: "user", content: input }, + ], + }) + }).then(r => r.json()).then(r => { + div.remove(); + console.log(r); + const generated = JSON.parse(r.choices[0].message.content); + const output = copiedArtistCreditToPlainString(generated); + if (output !== input) { + alert("LLM's output does not match with input. The LLM moment...\n\n" + input + "\n" + output); + return; + } + if (confirm("Done! ... according to the LLM (" + r.model + ").\n\n" + generated.map(a => JSON.stringify(a)).join("\n"))) { + localStorage.setItem("copiedArtistCredit", JSON.stringify({ + names: generated, + })); + } + }); + }); })(); diff --git a/niconico-genten-kaiki.user.js b/niconico-genten-kaiki.user.js index 9d091b1..8fbd16d 100644 --- a/niconico-genten-kaiki.user.js +++ b/niconico-genten-kaiki.user.js @@ -36,9 +36,10 @@ body.${ccPrefix}-enableytclick #UadPlayer { pointer-events: none; } function sleep(msec) { return new Promise(resolve => setTimeout(resolve, msec)); } class YTPlayer { + iframe = document.createElement("iframe"); + widgetid; + connected = false; constructor(videoId) { - this.iframe = document.createElement("iframe"); - this.connected = false; this.widgetid = Date.now(); this.iframe.src = `${YOUTUBE_ORIGIN}/embed/${videoId}?autoplay=1&fs=0&disablekb=1&modestbranding=1&playsinline=1&rel=0&showinfo=0&iv_load_policy=3&enablejsapi=1&origin=${location.origin}&vq=highres&widgetid=${this.widgetid}`; window.addEventListener("message", e => { @@ -87,6 +88,7 @@ body.${ccPrefix}-enableytclick #UadPlayer { pointer-events: none; } id: this.widgetid, }), YOUTUBE_ORIGIN); } + onReadyCallback; onLoad(callback) { this.iframe.addEventListener("load", callback); } From 6edd7668f12227fe70c63a34a84d41d9eb8d25ab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 05:46:07 +0000 Subject: [PATCH 074/131] update with 294dd45f2c04343e174a0ca169748d5f02d4363c --- mastodon-show-act-users.user.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/mastodon-show-act-users.user.js b/mastodon-show-act-users.user.js index f6f1d19..ae0cc3b 100644 --- a/mastodon-show-act-users.user.js +++ b/mastodon-show-act-users.user.js @@ -1,14 +1,18 @@ // ==UserScript== -// @name Mastodon Show act users in /@xxx/12345 -// @namespace https://rinsuki.net -// @version 0.3.2 -// @description Mastodonの投稿詳細画面でBT/favしたユーザー一覧を見れるようにする -// @author rinsuki -// @include /^https:\/\/[^/]*\/@[A-Za-z0-9_]+\/[0-9]+([?#].*)?$/ -// @exclude-match https://*/@*/*/embed -// @exclude-match https://*.tiktok.com/* -// @require https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js#sha256=c9486f126615859fc61ac84840a02b2efc920d287a71d99d708c74b2947750fe -// @require https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js#sha256=bc5b7797e8a595e365c1385b0d47683d3a85f3533c58d499659b771c48ec6d25 +// @name Mastodon Show act users in /@xxx/12345 +// @namespace https://rinsuki.net +// @version 0.3.2 +// @description Mastodonの投稿詳細画面でBT/favしたユーザー一覧を見れるようにする +// @author rinsuki +// @include /^https:\/\/[^/]*\/@[A-Za-z0-9_]+\/[0-9]+([?#].*)?$/ +// @exclude-match https://*/@*/*/embed +// @exclude-match https://*.tiktok.com/* +// @require https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js#sha256=c9486f126615859fc61ac84840a02b2efc920d287a71d99d708c74b2947750fe +// @require https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js#sha256=bc5b7797e8a595e365c1385b0d47683d3a85f3533c58d499659b771c48ec6d25 +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { From 151378c54577e3ed46a7c712549eda21707e8b6b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 06:04:17 +0000 Subject: [PATCH 075/131] update with c9d1167f3be6c3e06de9c6cbb4e02b58e78c6fe0 --- mb-artist-credit-splitter-by-ai.user.js | 24 +++++++++++++--------- mb-artist-credit-splitter.user.js | 18 +++++++++------- mb-seed-urls-to-release-recordings.user.js | 22 ++++++++++++-------- niconico-genten-kaiki.user.js | 18 +++++++++------- niconico-search-sort-table.user.js | 22 ++++++++++++-------- 5 files changed, 62 insertions(+), 42 deletions(-) diff --git a/mb-artist-credit-splitter-by-ai.user.js b/mb-artist-credit-splitter-by-ai.user.js index d235ba5..3c9cdde 100644 --- a/mb-artist-credit-splitter-by-ai.user.js +++ b/mb-artist-credit-splitter-by-ai.user.js @@ -1,14 +1,18 @@ // ==UserScript== -// @name MB: Artist Credit Splitter, but Powered by AI -// @version 1.1.0 -// @description OpenRouter でいい感じに MusicBrainz のアーティストクレジットを分割します (失敗することもあります) -// @namespace https://rinsuki.net/ -// @author rinsuki -// @match https://musicbrainz.org/* -// @grant GM_getValue -// @grant GM_setValue -// @grant GM.registerMenuCommand -// @grant GM.xmlHttpRequest +// @name MB: Artist Credit Splitter, but Powered by AI +// @namespace https://rinsuki.net/ +// @version 1.1.0 +// @description OpenRouter でいい感じに MusicBrainz のアーティストクレジットを分割します (失敗することもあります) +// @author rinsuki +// @match https://musicbrainz.org/* +// @grant GM_getValue +// @grant GM_setValue +// @grant GM.registerMenuCommand +// @grant GM.xmlHttpRequest +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index ff6040e..9cdda49 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -1,11 +1,15 @@ // ==UserScript== -// @name MB: Artist Credit Splitter -// @version 1.0.2 -// @description いい感じに MusicBrainz のアーティストクレジットを分割します (失敗することもあります) -// @namespace https://rinsuki.net/ -// @author rinsuki -// @match https://musicbrainz.org/* -// @grant none +// @name MB: Artist Credit Splitter +// @namespace https://rinsuki.net/ +// @version 1.0.2 +// @description いい感じに MusicBrainz のアーティストクレジットを分割します (失敗することもあります) +// @author rinsuki +// @match https://musicbrainz.org/* +// @grant none +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index fb6e18b..662d98a 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -1,13 +1,17 @@ // ==UserScript== -// @name MusicBrainz: Seed URLs to Release Recordings -// @namespace https://rinsuki.net -// @match https://musicbrainz.org/release/*/edit-relationships -// @match https://*.musicbrainz.org/release/*/edit-relationships -// @require https://cdn.jsdelivr.net/npm/zod@3.24.4/lib/index.umd.js#sha256=25623a1c332de4571b75a2a6fb8be1fae40180c8fdfd7b4420f09bea727cee1c -// @grant none -// @version 0.2.1 -// @author rinsuki -// @description Import recording-url relationship to release's recordings. +// @name MusicBrainz: Seed URLs to Release Recordings +// @namespace https://rinsuki.net +// @version 0.2.1 +// @description Import recording-url relationship to release's recordings. +// @author rinsuki +// @match https://musicbrainz.org/release/*/edit-relationships +// @match https://*.musicbrainz.org/release/*/edit-relationships +// @require https://cdn.jsdelivr.net/npm/zod@3.24.4/lib/index.umd.js#sha256=25623a1c332de4571b75a2a6fb8be1fae40180c8fdfd7b4420f09bea727cee1c +// @grant none +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { diff --git a/niconico-genten-kaiki.user.js b/niconico-genten-kaiki.user.js index 8fbd16d..55c484f 100644 --- a/niconico-genten-kaiki.user.js +++ b/niconico-genten-kaiki.user.js @@ -1,11 +1,15 @@ // ==UserScript== -// @name 原点回帰(Re) あるいは ZenTube in 公式プレーヤー -// @namespace rinsuki.net -// @description 動画説明文内のYouTubeへのリンクに「原点回帰」ボタンが追加され、そのボタンを押すとYouTubeの埋め込みプレーヤーで動画が再生されるようになります。 -// @match https://www.nicovideo.jp/watch/* -// @grant none -// @version 1.0 -// @author - +// @name 原点回帰(Re) あるいは ZenTube in 公式プレーヤー +// @namespace rinsuki.net +// @version 1.0 +// @description 動画説明文内のYouTubeへのリンクに「原点回帰」ボタンが追加され、そのボタンを押すとYouTubeの埋め込みプレーヤーで動画が再生されるようになります。 +// @author - +// @match https://www.nicovideo.jp/watch/* +// @grant none +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { diff --git a/niconico-search-sort-table.user.js b/niconico-search-sort-table.user.js index 5d09326..6224ca8 100644 --- a/niconico-search-sort-table.user.js +++ b/niconico-search-sort-table.user.js @@ -1,13 +1,17 @@ // ==UserScript== -// @name ニコニコ動画の検索のソート選択を表にする -// @namespace rinsuki.net -// @description ニコニコ動画の検索のソート順を選ぶところを表レイアウトにします。 -// @match https://www.nicovideo.jp/tag/* -// @match https://www.nicovideo.jp/search/* -// @version 1.0 -// @author rinsuki -// @require https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js#sha256=c9486f126615859fc61ac84840a02b2efc920d287a71d99d708c74b2947750fe -// @require https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js#sha256=bc5b7797e8a595e365c1385b0d47683d3a85f3533c58d499659b771c48ec6d25 +// @name ニコニコ動画の検索のソート選択を表にする +// @namespace rinsuki.net +// @version 1.0 +// @description ニコニコ動画の検索のソート順を選ぶところを表レイアウトにします。 +// @author rinsuki +// @match https://www.nicovideo.jp/tag/* +// @match https://www.nicovideo.jp/search/* +// @require https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js#sha256=c9486f126615859fc61ac84840a02b2efc920d287a71d99d708c74b2947750fe +// @require https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js#sha256=bc5b7797e8a595e365c1385b0d47683d3a85f3533c58d499659b771c48ec6d25 +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { From ebeeeebf73d9f54c1201f6981736db03fad8a716 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 07:42:46 +0000 Subject: [PATCH 076/131] update with 2125010f76d27dd75e9b1a82266c1497dd5b65c7 From 4ad55767378b2f981bb50b402da59b329c3350aa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 08:32:23 +0000 Subject: [PATCH 077/131] update with 200cfaf9065a062f4033cac3b8627e877464dc5d From 884b1fb534ed9b565536adf95c00a4f10d66902e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:24:43 +0000 Subject: [PATCH 078/131] update with 23dad7f8d28e386d2c16b63bad7ebeaffd034143 From ffea8fa2c2ebdaf702c48330bce59b96edbd8d51 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 10:42:11 +0000 Subject: [PATCH 079/131] update with e5e513d14d23b00e32534e3a93348487f5eff3cf --- mastodon-show-act-users.user.js | 1 + mb-artist-credit-splitter-by-ai.user.js | 1 + mb-artist-credit-splitter.user.js | 1 + mb-seed-urls-to-release-recordings.user.js | 2 +- niconico-genten-kaiki.user.js | 1 + niconico-search-sort-table.user.js | 1 + 6 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mastodon-show-act-users.user.js b/mastodon-show-act-users.user.js index ae0cc3b..ecd451b 100644 --- a/mastodon-show-act-users.user.js +++ b/mastodon-show-act-users.user.js @@ -13,6 +13,7 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues + // ==/UserScript== (function () { diff --git a/mb-artist-credit-splitter-by-ai.user.js b/mb-artist-credit-splitter-by-ai.user.js index 3c9cdde..d985f0e 100644 --- a/mb-artist-credit-splitter-by-ai.user.js +++ b/mb-artist-credit-splitter-by-ai.user.js @@ -13,6 +13,7 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues + // ==/UserScript== (function () { diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 9cdda49..533cff0 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -10,6 +10,7 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues + // ==/UserScript== (function () { diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index 662d98a..c49b7c2 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -6,12 +6,12 @@ // @author rinsuki // @match https://musicbrainz.org/release/*/edit-relationships // @match https://*.musicbrainz.org/release/*/edit-relationships -// @require https://cdn.jsdelivr.net/npm/zod@3.24.4/lib/index.umd.js#sha256=25623a1c332de4571b75a2a6fb8be1fae40180c8fdfd7b4420f09bea727cee1c // @grant none // @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues +// @require https://cdn.jsdelivr.net/npm/zod@3.24.4/lib/index.umd.js#sha256-JWI6HDMt5FcbdaKm-4vh-uQBgMj9_XtEIPCb6nJ87hw,sha512-w_o1QQewvZigMuaxE5iaUXhSUomguLoRvf4n95QwcYXWxEs7xdMzM0Wmhqwt-PCcUkmE8prQjzcqDOIMhVD6ew // ==/UserScript== (function () { diff --git a/niconico-genten-kaiki.user.js b/niconico-genten-kaiki.user.js index 55c484f..301549e 100644 --- a/niconico-genten-kaiki.user.js +++ b/niconico-genten-kaiki.user.js @@ -10,6 +10,7 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues + // ==/UserScript== (function () { diff --git a/niconico-search-sort-table.user.js b/niconico-search-sort-table.user.js index 6224ca8..73dd9f9 100644 --- a/niconico-search-sort-table.user.js +++ b/niconico-search-sort-table.user.js @@ -12,6 +12,7 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues + // ==/UserScript== (function () { From cf9ff029bebf81ea47300a0cc7acb6a9fb85e30d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 10:45:34 +0000 Subject: [PATCH 080/131] update with a539618af7f1d2c622065ffec3efc93d05f86a2d --- mastodon-show-act-users.user.js | 1 - mb-artist-credit-splitter-by-ai.user.js | 1 - mb-artist-credit-splitter.user.js | 1 - niconico-genten-kaiki.user.js | 1 - niconico-search-sort-table.user.js | 1 - 5 files changed, 5 deletions(-) diff --git a/mastodon-show-act-users.user.js b/mastodon-show-act-users.user.js index ecd451b..ae0cc3b 100644 --- a/mastodon-show-act-users.user.js +++ b/mastodon-show-act-users.user.js @@ -13,7 +13,6 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues - // ==/UserScript== (function () { diff --git a/mb-artist-credit-splitter-by-ai.user.js b/mb-artist-credit-splitter-by-ai.user.js index d985f0e..3c9cdde 100644 --- a/mb-artist-credit-splitter-by-ai.user.js +++ b/mb-artist-credit-splitter-by-ai.user.js @@ -13,7 +13,6 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues - // ==/UserScript== (function () { diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 533cff0..9cdda49 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -10,7 +10,6 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues - // ==/UserScript== (function () { diff --git a/niconico-genten-kaiki.user.js b/niconico-genten-kaiki.user.js index 301549e..55c484f 100644 --- a/niconico-genten-kaiki.user.js +++ b/niconico-genten-kaiki.user.js @@ -10,7 +10,6 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues - // ==/UserScript== (function () { diff --git a/niconico-search-sort-table.user.js b/niconico-search-sort-table.user.js index 73dd9f9..6224ca8 100644 --- a/niconico-search-sort-table.user.js +++ b/niconico-search-sort-table.user.js @@ -12,7 +12,6 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues - // ==/UserScript== (function () { From 480a4a93c5fe9d7b57580252be6490476e123ab0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 11:03:22 +0000 Subject: [PATCH 081/131] update with 50e57750d03860a79b26dcb23d71012880b09abd --- mastodon-show-act-users.user.js | 4 ++-- niconico-search-sort-table.user.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mastodon-show-act-users.user.js b/mastodon-show-act-users.user.js index ae0cc3b..2572f91 100644 --- a/mastodon-show-act-users.user.js +++ b/mastodon-show-act-users.user.js @@ -7,12 +7,12 @@ // @include /^https:\/\/[^/]*\/@[A-Za-z0-9_]+\/[0-9]+([?#].*)?$/ // @exclude-match https://*/@*/*/embed // @exclude-match https://*.tiktok.com/* -// @require https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js#sha256=c9486f126615859fc61ac84840a02b2efc920d287a71d99d708c74b2947750fe -// @require https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js#sha256=bc5b7797e8a595e365c1385b0d47683d3a85f3533c58d499659b771c48ec6d25 // @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues +// @require https://cdn.jsdelivr.net/npm/react@18.3.1/umd/react.production.min.js#sha256-2Unxw2h67a3O2shSYYZfKbF80nOZfn9rK_xTsvnUxN0,sha512-QVs8Lo43F9lSuBykadDb0oSXDL_BbZ588urWVCRwSIoewQv_Ewg1f84mK3U790bZ0FfhFa1YSQUmIhG-pIRKeg +// @require https://cdn.jsdelivr.net/npm/react-dom@18.3.1/umd/react-dom.production.min.js#sha256-NfT5dPSyvNRNpzljNH-JUuNB-DkJ5EmCJ9Tia5j2bw0,sha512-6a1107rTlA4gYpgHAqbwLAtxmWipBdJFcq8y5S_aTge3Bp-VAklABm2LO-Kg51vOWR9JMZq1Ovjl5tpluNpTeQ // ==/UserScript== (function () { diff --git a/niconico-search-sort-table.user.js b/niconico-search-sort-table.user.js index 6224ca8..5d15f6c 100644 --- a/niconico-search-sort-table.user.js +++ b/niconico-search-sort-table.user.js @@ -6,12 +6,12 @@ // @author rinsuki // @match https://www.nicovideo.jp/tag/* // @match https://www.nicovideo.jp/search/* -// @require https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js#sha256=c9486f126615859fc61ac84840a02b2efc920d287a71d99d708c74b2947750fe -// @require https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js#sha256=bc5b7797e8a595e365c1385b0d47683d3a85f3533c58d499659b771c48ec6d25 // @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues +// @require https://cdn.jsdelivr.net/npm/react@18.3.1/umd/react.production.min.js#sha256-2Unxw2h67a3O2shSYYZfKbF80nOZfn9rK_xTsvnUxN0,sha512-QVs8Lo43F9lSuBykadDb0oSXDL_BbZ588urWVCRwSIoewQv_Ewg1f84mK3U790bZ0FfhFa1YSQUmIhG-pIRKeg +// @require https://cdn.jsdelivr.net/npm/react-dom@18.3.1/umd/react-dom.production.min.js#sha256-NfT5dPSyvNRNpzljNH-JUuNB-DkJ5EmCJ9Tia5j2bw0,sha512-6a1107rTlA4gYpgHAqbwLAtxmWipBdJFcq8y5S_aTge3Bp-VAklABm2LO-Kg51vOWR9JMZq1Ovjl5tpluNpTeQ // ==/UserScript== (function () { From 92dc43420b0e4216044c1147177d5223be8d2018 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 13:14:24 +0000 Subject: [PATCH 082/131] update with 925a4f4ac68339eea3d9098f1a627ca56eead8be --- mastodon-show-act-users.user.js | 4 ++-- mb-seed-urls-to-release-recordings.user.js | 2 +- niconico-search-sort-table.user.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mastodon-show-act-users.user.js b/mastodon-show-act-users.user.js index 2572f91..bf5514d 100644 --- a/mastodon-show-act-users.user.js +++ b/mastodon-show-act-users.user.js @@ -11,8 +11,8 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues -// @require https://cdn.jsdelivr.net/npm/react@18.3.1/umd/react.production.min.js#sha256-2Unxw2h67a3O2shSYYZfKbF80nOZfn9rK_xTsvnUxN0,sha512-QVs8Lo43F9lSuBykadDb0oSXDL_BbZ588urWVCRwSIoewQv_Ewg1f84mK3U790bZ0FfhFa1YSQUmIhG-pIRKeg -// @require https://cdn.jsdelivr.net/npm/react-dom@18.3.1/umd/react-dom.production.min.js#sha256-NfT5dPSyvNRNpzljNH-JUuNB-DkJ5EmCJ9Tia5j2bw0,sha512-6a1107rTlA4gYpgHAqbwLAtxmWipBdJFcq8y5S_aTge3Bp-VAklABm2LO-Kg51vOWR9JMZq1Ovjl5tpluNpTeQ +// @require https://cdn.jsdelivr.net/npm/react@18.3.1/umd/react.production.min.js#sha256-2Unxw2h67a3O2shSYYZfKbF80nOZfn9rK/xTsvnUxN0=,sha512-QVs8Lo43F9lSuBykadDb0oSXDL/BbZ588urWVCRwSIoewQv/Ewg1f84mK3U790bZ0FfhFa1YSQUmIhG+pIRKeg== +// @require https://cdn.jsdelivr.net/npm/react-dom@18.3.1/umd/react-dom.production.min.js#sha256-NfT5dPSyvNRNpzljNH+JUuNB+DkJ5EmCJ9Tia5j2bw0=,sha512-6a1107rTlA4gYpgHAqbwLAtxmWipBdJFcq8y5S/aTge3Bp+VAklABm2LO+Kg51vOWR9JMZq1Ovjl5tpluNpTeQ== // ==/UserScript== (function () { diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index c49b7c2..ab6f4f1 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -11,7 +11,7 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues -// @require https://cdn.jsdelivr.net/npm/zod@3.24.4/lib/index.umd.js#sha256-JWI6HDMt5FcbdaKm-4vh-uQBgMj9_XtEIPCb6nJ87hw,sha512-w_o1QQewvZigMuaxE5iaUXhSUomguLoRvf4n95QwcYXWxEs7xdMzM0Wmhqwt-PCcUkmE8prQjzcqDOIMhVD6ew +// @require https://cdn.jsdelivr.net/npm/zod@3.24.4/lib/index.umd.js#sha256-JWI6HDMt5FcbdaKm+4vh+uQBgMj9/XtEIPCb6nJ87hw=,sha512-w/o1QQewvZigMuaxE5iaUXhSUomguLoRvf4n95QwcYXWxEs7xdMzM0Wmhqwt+PCcUkmE8prQjzcqDOIMhVD6ew== // ==/UserScript== (function () { diff --git a/niconico-search-sort-table.user.js b/niconico-search-sort-table.user.js index 5d15f6c..e7b9da5 100644 --- a/niconico-search-sort-table.user.js +++ b/niconico-search-sort-table.user.js @@ -10,8 +10,8 @@ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues -// @require https://cdn.jsdelivr.net/npm/react@18.3.1/umd/react.production.min.js#sha256-2Unxw2h67a3O2shSYYZfKbF80nOZfn9rK_xTsvnUxN0,sha512-QVs8Lo43F9lSuBykadDb0oSXDL_BbZ588urWVCRwSIoewQv_Ewg1f84mK3U790bZ0FfhFa1YSQUmIhG-pIRKeg -// @require https://cdn.jsdelivr.net/npm/react-dom@18.3.1/umd/react-dom.production.min.js#sha256-NfT5dPSyvNRNpzljNH-JUuNB-DkJ5EmCJ9Tia5j2bw0,sha512-6a1107rTlA4gYpgHAqbwLAtxmWipBdJFcq8y5S_aTge3Bp-VAklABm2LO-Kg51vOWR9JMZq1Ovjl5tpluNpTeQ +// @require https://cdn.jsdelivr.net/npm/react@18.3.1/umd/react.production.min.js#sha256-2Unxw2h67a3O2shSYYZfKbF80nOZfn9rK/xTsvnUxN0=,sha512-QVs8Lo43F9lSuBykadDb0oSXDL/BbZ588urWVCRwSIoewQv/Ewg1f84mK3U790bZ0FfhFa1YSQUmIhG+pIRKeg== +// @require https://cdn.jsdelivr.net/npm/react-dom@18.3.1/umd/react-dom.production.min.js#sha256-NfT5dPSyvNRNpzljNH+JUuNB+DkJ5EmCJ9Tia5j2bw0=,sha512-6a1107rTlA4gYpgHAqbwLAtxmWipBdJFcq8y5S/aTge3Bp+VAklABm2LO+Kg51vOWR9JMZq1Ovjl5tpluNpTeQ== // ==/UserScript== (function () { From 381ffdeded3de2fecfe58c187c5b03dfc73fc3b6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 8 Aug 2025 13:23:01 +0000 Subject: [PATCH 083/131] update with 6d52117ceb5492e4ea70841b9c399b18ef7d19d8 From 36da51652832c8afd9a79ed666f609d160315427 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 9 Aug 2025 04:52:12 +0000 Subject: [PATCH 084/131] update with dc56f3de3713f3033ca644f069a922fb59ec363e From 31ac0cfddd3bcf56f6bc993087eac0afcbb9fb46 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 9 Aug 2025 05:01:38 +0000 Subject: [PATCH 085/131] update with 1973d6a5ff2058fe2ac963eb7994e8780fa7eb43 From ebb16866e5d058d151d7b11d9b9090e203050bb5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 9 Aug 2025 06:21:54 +0000 Subject: [PATCH 086/131] update with fbd810d3d86dbb1ae6a5b0f572b6da3bbc7fe660 --- mastodon-show-act-users.user.js | 6 +++--- niconico-search-sort-table.user.js | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mastodon-show-act-users.user.js b/mastodon-show-act-users.user.js index bf5514d..f8dd615 100644 --- a/mastodon-show-act-users.user.js +++ b/mastodon-show-act-users.user.js @@ -129,9 +129,9 @@ if (active == null) return React.createElement("div", null, React.createElement("i", { className: "fa fa-retweet" }), - " \u304B ", + " か ", isNicoru ? React.createElement("i", { className: "fa fa-nicoru--status" }) : React.createElement("i", { className: "fa fa-star" }), - " \u3092\u30AF\u30EA\u30C3\u30AF\u3059\u308B\u3068\u30D6\u30FC\u30B9\u30C8\u3057\u305F/\u3075\u3041\u307C\u3063\u305F\u30E6\u30FC\u30B6\u30FC\u304C\u8868\u793A\u3055\u308C\u307E\u3059"); + " をクリックするとブーストした/ふぁぼったユーザーが表示されます"); const [name, icon] = { "favourite": isNicoru ? ["ニコる", "nicoru"] : ["ふぁぼ", "star"], "reblog": ["ブースト", "retweet"] @@ -140,7 +140,7 @@ React.createElement(SectionHeader, { icon: icon, name: name }), React.createElement("div", null, React.createElement(List, { type: active, key: active, statusId: statusId })), - React.createElement(SectionHeader, { icon: "reply", name: "\u8FD4\u4FE1" })); + React.createElement(SectionHeader, { icon: "reply", name: "返信" })); }; function parent(dom) { if (dom == null) diff --git a/niconico-search-sort-table.user.js b/niconico-search-sort-table.user.js index e7b9da5..3d29491 100644 --- a/niconico-search-sort-table.user.js +++ b/niconico-search-sort-table.user.js @@ -41,25 +41,25 @@ return React.createElement("tr", null, React.createElement("td", { style: { textAlign: "right", padding: "4px" } }, props.title, - "\u304C"), + "が"), React.createElement("td", null, React.createElement(Link, { k: props.k + ":d" }, props.desc, - "\u9806")), + "順")), React.createElement("td", null, React.createElement(Link, { k: props.k + ":a" }, props.asc, - "\u9806"))); + "順"))); }; const li = document.createElement("li"); ReactDOM.render(React.createElement("table", null, - React.createElement(Row, { title: "\u6295\u7A3F\u65E5\u6642", k: "f", desc: "\u65B0\u3057\u3044", asc: "\u53E4\u3044" }), - React.createElement(Row, { title: "\u518D\u751F\u6570", k: "v", desc: "\u591A\u3044", asc: "\u5C11\u306A\u3044" }), - React.createElement(Row, { title: "\u30B3\u30E1\u30F3\u30C8\u6570", k: "r", desc: "\u591A\u3044", asc: "\u5C11\u306A\u3044" }), - React.createElement(Row, { title: "\u3044\u3044\u306D\u6570", k: "likeCount", desc: "\u591A\u3044", asc: "\u5C11\u306A\u3044" }), - React.createElement(Row, { title: "\u30DE\u30A4\u30EA\u30B9\u30C8\u6570", k: "m", desc: "\u591A\u3044", asc: "\u5C11\u306A\u3044" }), - React.createElement(Row, { title: "\u30B3\u30E1\u30F3\u30C8", k: "n", desc: "\u65B0\u3057\u3044", asc: "\u53E4\u3044" }), - React.createElement(Row, { title: "\u518D\u751F\u6642\u9593", k: "l", desc: "\u9577\u3044", asc: "\u77ED\u3044" })), li); + React.createElement(Row, { title: "投稿日時", k: "f", desc: "新しい", asc: "古い" }), + React.createElement(Row, { title: "再生数", k: "v", desc: "多い", asc: "少ない" }), + React.createElement(Row, { title: "コメント数", k: "r", desc: "多い", asc: "少ない" }), + React.createElement(Row, { title: "いいね数", k: "likeCount", desc: "多い", asc: "少ない" }), + React.createElement(Row, { title: "マイリスト数", k: "m", desc: "多い", asc: "少ない" }), + React.createElement(Row, { title: "コメント", k: "n", desc: "新しい", asc: "古い" }), + React.createElement(Row, { title: "再生時間", k: "l", desc: "長い", asc: "短い" })), li); originalSortList.appendChild(li); })(); From d32a39ff521faa91983dec580405d9c7f30a6172 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 09:33:49 +0000 Subject: [PATCH 087/131] update with 36edba1636c2856a390c05938a6b917a6bc54093 From 2bb7605168ee333a96f08328e1d0a653023ee5d8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 14:24:40 +0000 Subject: [PATCH 088/131] update with c2bf2110522634a8be5ab1b3ae1b8f6bdf8312a4 From 601930006cf87d4da21f2443c2feb92fa4f384a9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 20:27:57 +0000 Subject: [PATCH 089/131] update with 6494c5e0fcb48fb6585d2d08da7f21a37b8e44f4 --- fix-1password-extension-passkey.user.js | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 fix-1password-extension-passkey.user.js diff --git a/fix-1password-extension-passkey.user.js b/fix-1password-extension-passkey.user.js new file mode 100644 index 0000000..c54dfbd --- /dev/null +++ b/fix-1password-extension-passkey.user.js @@ -0,0 +1,40 @@ +// ==UserScript== +// @name 1Password Extension Passkey Fix +// @namespace rinsuki.net +// @version 1.0 +// @match https://accounts.nintendo.com/* +// @grant none +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues +// ==/UserScript== + +(function () { + 'use strict'; + + (() => { + const origPublicKeyCredential = window.PublicKeyCredential; + window.PublicKeyCredential = new Proxy(origPublicKeyCredential, { + get(...args) { + if (args[1] === Symbol.hasInstance) { + return (instance) => { + if (typeof instance !== "object") + return false; + if (instance == null) + return false; + if (!("type" in instance)) + return false; + if (instance.type !== "public-key") + return false; + if (!("id" in instance)) + return false; + if (typeof instance.id !== "string") + return false; + return true; + }; + } + return Reflect.get(...args); + } + }); + })(); + +})(); From d095db25e76fd3bd288d0fa35825572a3cea25bc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 20:31:39 +0000 Subject: [PATCH 090/131] update with 69d945fc46159079cf998ba5c475ca4e4f9b7c68 --- fix-1password-extension-passkey.user.js | 1 + 1 file changed, 1 insertion(+) diff --git a/fix-1password-extension-passkey.user.js b/fix-1password-extension-passkey.user.js index c54dfbd..c66d959 100644 --- a/fix-1password-extension-passkey.user.js +++ b/fix-1password-extension-passkey.user.js @@ -1,6 +1,7 @@ // ==UserScript== // @name 1Password Extension Passkey Fix // @namespace rinsuki.net +// @description Treat 1Password extension's response as a instance of window.PublicKeyCredential // @version 1.0 // @match https://accounts.nintendo.com/* // @grant none From 334e9e91a00647d14879f55f7d0ddfa47b7b09fb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 11 Oct 2025 08:35:05 +0000 Subject: [PATCH 091/131] update with 4a42ca6e2bd1dd25b0511c760636f71842d8c5e8 --- ...-recording-rels-from-other-release.user.js | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 mb-copy-recording-rels-from-other-release.user.js diff --git a/mb-copy-recording-rels-from-other-release.user.js b/mb-copy-recording-rels-from-other-release.user.js new file mode 100644 index 0000000..13c168b --- /dev/null +++ b/mb-copy-recording-rels-from-other-release.user.js @@ -0,0 +1,175 @@ +// ==UserScript== +// @name MB: [WIP] Copy Recording Relationships from Other Release +// @version 0.1.0 +// @grant none +// @namespace https://rinsuki.net +// @author rinsuki +// @match https://*musicbrainz.org/release/*/edit-relationships* +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues +// @require https://cdn.jsdelivr.net/npm/@rinsuki/dom-chef@5.1.1/umd.js#sha256-EvGMVNob2jRhcwSo8gGGJp3mZJENNaQwrpYlNRQlUMc=,sha512-b/5dx8A+dc01RsqR2hF2Na/DFwwn64M4PQFka7dXhcAeXjPfpkgWS9YFs1LHPaEckkxAI7ivhVn6i+OrFivnYQ== +// ==/UserScript== + +(function () { + 'use strict'; + + class HTTPError extends Error { + response; + text; + constructor(response, text) { + super(`HTTP-${response.status}: ${text}`); + this.response = response; + this.text = text; + } + } + async function fetchOkOrThrow(...args) { + const res = await fetch(...args); + if (!res.ok) { + throw new HTTPError(res, await res.text()); + } + return res; + } + + //#region node_modules/.pnpm/typedbrainz@0.1.3/node_modules/typedbrainz/lib/index.js + // SPDX-License-Identifier: MIT + function isReleaseRelationshipEditor(relationshipEditor) { + return relationshipEditor.state?.entity.entityType === "release"; + } + + //#endregion + + /** @jsx h */ + + let abortController = null; + const elm = DOMChef.h("div", null, + DOMChef.h("details", { style: { border: "1px solid #ccc", margin: "1em 0", padding: "0.5em" }, open: true }, + DOMChef.h("summary", { style: { marginBottom: "0.5em" } }, + DOMChef.h("h2", { style: { display: "inline" } }, "Copy Recording Relationships from Other Release")), + DOMChef.h("form", { action: "javascript:", onSubmit: e => { + e.preventDefault(); + const input = e.currentTarget.querySelector("input[type=text]"); + if (input == null) + return; + const mbid = (input.value.match(/(?:^|\/release\/)([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i) ?? [])[1]; + if (!mbid) { + alert("Invalid MBID or URL"); + return; + } + if (abortController) { + abortController.abort(); + } + abortController = new AbortController(); + fetchReleaseAndShowCopyUI(mbid, abortController, e.currentTarget).catch(err => { + console.error(err); + alert(err.message); + }); + } }, + DOMChef.h("input", { type: "text", placeholder: "Release MBID or URL", size: 40 }), + DOMChef.h("input", { type: "submit", value: "Load" })))); + async function fetchReleaseAndShowCopyUI(mbid, ac, form) { + const resRaw = await fetchOkOrThrow(`/ws/js/entity/${mbid}`).catch(e => { throw new Error(`failed to fetch release: ${e.message}`, { cause: e }); }); + const resJSON = await resRaw.json().catch(e => { throw new Error(`failed to fetch release (parsing json): ${e.message}`, { cause: e }); }); + console.log(resJSON); + const elm = DOMChef.h("div", null, + DOMChef.h("h3", null, + "Copy from ", + resJSON.name), + DOMChef.h("form", { action: "javascript:", onSubmit: e => { + e.preventDefault(); + const input = e.currentTarget.querySelector("select"); + if (input == null) + return; + const mediumid = parseInt(input.value, 10); + if (!Number.isSafeInteger(mediumid)) { + alert("Invalid medium ID"); + return; + } + fetchMediumAndShowCopyUI(resJSON.gid, mediumid, ac, e.currentTarget).catch(err => { + console.error(err); + alert(err.message); + }); + } }, + resJSON.mediums + ? DOMChef.h("div", null, + DOMChef.h("select", { size: Math.max(Math.min(2, resJSON.mediums?.length), 5) }, resJSON.mediums.map(medium => { + return DOMChef.h("option", { value: medium.id }, + medium.position, + ". ", + medium.format?.name ?? "(unknown)", + medium.name.length ? ": " + medium.name : ""); + }))) + : "(no mediums)", + DOMChef.h("input", { type: "submit", value: "Load" }))); + if (ac.signal.aborted) + return; + form.nextElementSibling?.remove(); + form.insertAdjacentElement("afterend", elm); + elm.querySelector("select")?.focus(); + } + async function fetchMediumAndShowCopyUI(releasegid, mediumid, ac, form) { + const resRaw = await fetchOkOrThrow(`/ws/js/medium/${mediumid}?inc=recordings+rels`).catch(e => { throw new Error(`failed to fetch medium: ${e.message}`, { cause: e }); }); + const srcMedia = await resRaw.json().catch(e => { throw new Error(`failed to fetch medium (parsing json): ${e.message}`, { cause: e }); }); + const elm = DOMChef.h("div", null, + DOMChef.h("button", { onClick: () => { + if (MB == null) + return; + const relEditor = MB.relationshipEditor; + if (MB.tree == null || !isReleaseRelationshipEditor(relEditor)) + return; + const dstMedia = MB.tree.iterate(relEditor.state.mediums).toArray()[0]; + let copiedSomeRels = false; + for (let i = 0; i < dstMedia[0].tracks.length; i++) { + const srcRec = srcMedia.tracks[i].recording; + const dstRec = dstMedia[0].tracks[i].recording; + for (const rel of srcRec.relationships) { + copiedSomeRels = true; + relEditor.dispatch({ + type: "update-relationship-state", + sourceEntity: dstRec, + oldRelationshipState: null, + newRelationshipState: { + _lineage: [], + _original: null, + editsPending: false, + _status: 1, + entity0: rel.backward ? rel.target : dstRec, + entity1: rel.backward ? dstRec : rel.target, + entity0_credit: rel.entity0_credit, + entity1_credit: rel.entity1_credit, + begin_date: rel.begin_date, + end_date: rel.end_date, + ended: rel.ended, + attributes: MB.tree.fromDistinctAscArray(rel.attributes.map(attribute => { + const type = MB.linkedEntities.link_attribute_type[attribute.type.gid]; + return { + ...attribute, + type, + typeID: type.id, + }; + })), + id: relEditor.getRelationshipStateId(null), + linkTypeID: rel.linkTypeID, + linkOrder: rel.linkOrder, + }, + batchSelectionCount: undefined, + creditsToChangeForSource: "", + creditsToChangeForTarget: "", + }); + } + } + if (copiedSomeRels) { + let editNote = relEditor.state.editNoteField.value; + if (editNote.length) + editNote += "\n"; + editNote += `Relationships Copied from https://musicbrainz.org/release/${releasegid}/disc/${srcMedia.position} (a.k.a. https://musicbrainz.org/medium/${srcMedia.gid} )`; + relEditor.dispatch({ type: "update-edit-note", editNote }); + } + } }, "Copy")); + if (ac.signal.aborted) + return; + form.nextElementSibling?.remove(); + form.insertAdjacentElement("afterend", elm); + } + document.querySelector("#content > div.tabs")?.insertAdjacentElement("afterend", elm); + +})(); From afac9001264be72ebd8bff84fc89f7303580a8b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 14:30:12 +0000 Subject: [PATCH 092/131] update with 044f8513f114b1f4b18fd9ae4e592b70666eb65f --- ...acklist-credits-from-other-credits.user.js | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 mb-match-tracklist-credits-from-other-credits.user.js diff --git a/mb-match-tracklist-credits-from-other-credits.user.js b/mb-match-tracklist-credits-from-other-credits.user.js new file mode 100644 index 0000000..8860d9a --- /dev/null +++ b/mb-match-tracklist-credits-from-other-credits.user.js @@ -0,0 +1,102 @@ +// ==UserScript== +// @name MB: Match Tracklist Credits from Other Credits +// @namespace https://rinsuki.net +// @grant none +// @match https://*musicbrainz.org/release/*/edit +// @match https://*musicbrainz.org/release/add +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues +// ==/UserScript== + +(function () { + 'use strict'; + + function doIt() { + const MB = window.MB; + if (MB == null) + return; + const editor = MB.releaseEditor; + const currentCredits = [ + ...editor.rootField.release().artistCredit().names, + ...Array.from(editor.rootField.release().allTracks()).flatMap(t => t.artistCredit().names), + ]; + const creditMap = new Map(); + for (const credit of currentCredits) { + if (!creditMap.has(credit.name)) { + if (credit.artist != null) { + creditMap.set(credit.name, credit.artist); + } + } + else { + if (credit.artist == null) { + creditMap.set(credit.name, null); + } + else { + const current = creditMap.get(credit.name); + if (current == null) + continue; + if (current.id !== credit.artist.id) { + creditMap.set(credit.name, null); + } + } + } + } + for (const track of editor.rootField.release().allTracks()) { + const names = track.artistCredit().names; + // ループ中にnamesを書き換えるのであえて for-of を使わない + for (let i = 0; i < names.length; i++) { + const name = names[i]; + if (name.artist == null) { + for (const [knownName, knownArtist] of creditMap) { + if (knownArtist == null) + continue; + if (!name.name.startsWith(knownName)) + continue; + const remainName = name.name.slice(knownName.length); + if (remainName === "") { + // 完全一致 + name.artist = knownArtist; + break; + } + name.name = knownName; + name.artist = knownArtist; + name.joinPhrase = remainName + name.joinPhrase; + break; + } + } + let firstArtist = null; + for (const [knownName, knownArtist] of creditMap) { + if (knownArtist == null) + continue; + const i = name.joinPhrase.indexOf(knownName); + if (i === -1) + continue; + if (firstArtist == null || firstArtist[0] > i) { + firstArtist = [i, knownName, knownArtist]; + } + } + if (firstArtist != null) { + const [i, knownName, knownArtist] = firstArtist; + const remainName = name.joinPhrase.slice(i + knownName.length); + name.joinPhrase = name.joinPhrase.slice(0, i); + const newCreditName = { + name: knownName, + artist: knownArtist, + joinPhrase: remainName, + }; + names.splice(i + 1, 0, newCreditName); + } + } + track.artistCredit({ + names: [...names], + }); + } + } + const button = document.createElement("button"); + button.id = "mb-match-tracklist-credits-from-other-credits-button"; + document.getElementById(button.id)?.remove(); + button.textContent = "Match Tracklist Credits from Other Credits"; + button.addEventListener("click", doIt); + document.getElementById("tracklist")?.prepend(button); + +})(); From 079af3d2763a520c5c54e25c25a989322fdc17c7 Mon Sep 17 00:00:00 2001 From: rinsuki <428rinsuki+git@gmail.com> Date: Tue, 21 Oct 2025 08:14:50 +0900 Subject: [PATCH 093/131] rename --- ...er.js => mb-match-tracklist-credits-with-other-credits.user.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename mb-match-tracklist-credits-from-other-credits.user.js => mb-match-tracklist-credits-with-other-credits.user.js (100%) diff --git a/mb-match-tracklist-credits-from-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js similarity index 100% rename from mb-match-tracklist-credits-from-other-credits.user.js rename to mb-match-tracklist-credits-with-other-credits.user.js From 2876fadd44893aecde19cce4e19cacda19cf166e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 20 Oct 2025 23:15:55 +0000 Subject: [PATCH 094/131] update with df8fe121401c555aeebb8536f166588948fda82a From c151f62ad250caefb0466582d5df7b2b8435f06c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 00:21:57 +0000 Subject: [PATCH 095/131] update with 59769795a5aa23b818cbc9614eb3ba47504d32da --- ...acklist-credits-with-other-credits.user.js | 113 +++++++++--------- 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index 8860d9a..40a5c3e 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -1,5 +1,5 @@ // ==UserScript== -// @name MB: Match Tracklist Credits from Other Credits +// @name MB: Match Tracklist Credits with Other Credits // @namespace https://rinsuki.net // @grant none // @match https://*musicbrainz.org/release/*/edit @@ -11,12 +11,63 @@ (function () { 'use strict'; - function doIt() { + function doItForSpecificArtistCredit(creditMap, artistCredit) { + const names = [...artistCredit().names]; + // ループ中にnamesを書き換えるのであえて for-of を使わない + for (let i = 0; i < names.length; i++) { + const name = names[i]; + if (name.artist == null) { + for (const [knownName, knownArtist] of creditMap) { + if (knownArtist == null) + continue; + if (!name.name.startsWith(knownName)) + continue; + const remainName = name.name.slice(knownName.length); + if (remainName === "") { + // 完全一致 + name.artist = knownArtist; + break; + } + name.name = knownName; + name.artist = knownArtist; + name.joinPhrase = remainName + name.joinPhrase; + break; + } + } + let firstArtist = null; + for (const [knownName, knownArtist] of creditMap) { + if (knownArtist == null) + continue; + const i = name.joinPhrase.indexOf(knownName); + if (i === -1) + continue; + if (firstArtist == null || firstArtist[0] > i) { + firstArtist = [i, knownName, knownArtist]; + } + } + if (firstArtist != null) { + const [i, knownName, knownArtist] = firstArtist; + const remainName = name.joinPhrase.slice(i + knownName.length); + name.joinPhrase = name.joinPhrase.slice(0, i); + const newCreditName = { + name: knownName, + artist: knownArtist, + joinPhrase: remainName, + }; + names.splice(i + 1, 0, newCreditName); + } + } + artistCredit({ + names, + }); + } + function doItEntirely() { const MB = window.MB; if (MB == null) return; const editor = MB.releaseEditor; const currentCredits = [ + ...editor.rootField.release().releaseGroup().artistCredit.names, ...editor.rootField.release().artistCredit().names, ...Array.from(editor.rootField.release().allTracks()).flatMap(t => t.artistCredit().names), ]; @@ -41,62 +92,16 @@ } } } + doItForSpecificArtistCredit(creditMap, editor.rootField.release().artistCredit); for (const track of editor.rootField.release().allTracks()) { - const names = track.artistCredit().names; - // ループ中にnamesを書き換えるのであえて for-of を使わない - for (let i = 0; i < names.length; i++) { - const name = names[i]; - if (name.artist == null) { - for (const [knownName, knownArtist] of creditMap) { - if (knownArtist == null) - continue; - if (!name.name.startsWith(knownName)) - continue; - const remainName = name.name.slice(knownName.length); - if (remainName === "") { - // 完全一致 - name.artist = knownArtist; - break; - } - name.name = knownName; - name.artist = knownArtist; - name.joinPhrase = remainName + name.joinPhrase; - break; - } - } - let firstArtist = null; - for (const [knownName, knownArtist] of creditMap) { - if (knownArtist == null) - continue; - const i = name.joinPhrase.indexOf(knownName); - if (i === -1) - continue; - if (firstArtist == null || firstArtist[0] > i) { - firstArtist = [i, knownName, knownArtist]; - } - } - if (firstArtist != null) { - const [i, knownName, knownArtist] = firstArtist; - const remainName = name.joinPhrase.slice(i + knownName.length); - name.joinPhrase = name.joinPhrase.slice(0, i); - const newCreditName = { - name: knownName, - artist: knownArtist, - joinPhrase: remainName, - }; - names.splice(i + 1, 0, newCreditName); - } - } - track.artistCredit({ - names: [...names], - }); + doItForSpecificArtistCredit(creditMap, track.artistCredit); } } const button = document.createElement("button"); - button.id = "mb-match-tracklist-credits-from-other-credits-button"; + button.id = "mb-match-tracklist-credits-with-other-credits-button"; document.getElementById(button.id)?.remove(); - button.textContent = "Match Tracklist Credits from Other Credits"; - button.addEventListener("click", doIt); - document.getElementById("tracklist")?.prepend(button); + button.textContent = "Match Tracklist Credits with Other Credits"; + button.addEventListener("click", doItEntirely); + document.getElementById("release-editor")?.prepend(button); })(); From 96045a0ab5d4024546684f531a07362bb3009339 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 03:39:18 +0000 Subject: [PATCH 096/131] update with c058acc3eaf23d2e994b440caa40ee4bed636b36 --- mb-match-tracklist-credits-with-other-credits.user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index 40a5c3e..3b9d9f6 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -69,7 +69,7 @@ const currentCredits = [ ...editor.rootField.release().releaseGroup().artistCredit.names, ...editor.rootField.release().artistCredit().names, - ...Array.from(editor.rootField.release().allTracks()).flatMap(t => t.artistCredit().names), + ...Array.from(editor.rootField.release().allTracks()).flatMap(t => [...t.artistCredit().names, ...t.recording().artistCredit.names]), ]; const creditMap = new Map(); for (const credit of currentCredits) { From 21ded36a4768a0898a1179677f7f313fbdc6c670 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:56:13 +0000 Subject: [PATCH 097/131] update with ed51e70762b4746d768a01bf7000ba98f4ce8d83 --- ...acklist-credits-with-other-credits.user.js | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index 3b9d9f6..70b491c 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -61,16 +61,32 @@ names, }); } - function doItEntirely() { + async function doItEntirely(withShiftKey) { const MB = window.MB; if (MB == null) return; const editor = MB.releaseEditor; const currentCredits = [ - ...editor.rootField.release().releaseGroup().artistCredit.names, + ...editor.rootField.release().releaseGroup().artistCredit?.names ?? [], ...editor.rootField.release().artistCredit().names, ...Array.from(editor.rootField.release().allTracks()).flatMap(t => [...t.artistCredit().names, ...t.recording().artistCredit.names]), ]; + if (withShiftKey) { + const releaseUrl = prompt("Enter the release URL to copy credits from"); + if (releaseUrl == null || releaseUrl === "") + return; + const mbid = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i.exec(releaseUrl)?.[0]; + if (mbid == null) { + alert("Invalid release URL"); + return; + } + const otherJS = await fetch(`/ws/js/entity/${mbid}`).then(r => r.json()); + if (!("artistCredit" in otherJS)) { + alert("Could not fetch artist credit data"); + return; + } + currentCredits.push(...otherJS.artistCredit.names); + } const creditMap = new Map(); for (const credit of currentCredits) { if (!creditMap.has(credit.name)) { @@ -92,16 +108,20 @@ } } } + console.log(creditMap); doItForSpecificArtistCredit(creditMap, editor.rootField.release().artistCredit); for (const track of editor.rootField.release().allTracks()) { doItForSpecificArtistCredit(creditMap, track.artistCredit); } + alert("Done"); } const button = document.createElement("button"); button.id = "mb-match-tracklist-credits-with-other-credits-button"; document.getElementById(button.id)?.remove(); button.textContent = "Match Tracklist Credits with Other Credits"; - button.addEventListener("click", doItEntirely); + button.addEventListener("click", (e) => { + doItEntirely(e.shiftKey); + }); document.getElementById("release-editor")?.prepend(button); })(); From f8782672ec778173635aacf1560f60f1a6e04cd7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Nov 2025 05:52:36 +0000 Subject: [PATCH 098/131] update with c356fd5bfb74a890c1d3900052931aa1922f4194 --- ...acklist-credits-with-other-credits.user.js | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index 70b491c..6113a92 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -11,44 +11,52 @@ (function () { 'use strict'; + function isArtistExist(artist) { + return artist != null && artist.id != 0; + } function doItForSpecificArtistCredit(creditMap, artistCredit) { const names = [...artistCredit().names]; + console.log(JSON.stringify(names)); // ループ中にnamesを書き換えるのであえて for-of を使わない for (let i = 0; i < names.length; i++) { const name = names[i]; - if (name.artist == null) { + // アーティストIDが不明の場合、先頭一致できる名前を探す + if (!isArtistExist(name.artist)) { for (const [knownName, knownArtist] of creditMap) { - if (knownArtist == null) + if (!isArtistExist(knownArtist)) continue; if (!name.name.startsWith(knownName)) continue; const remainName = name.name.slice(knownName.length); if (remainName === "") { - // 完全一致 + // 完全一致したら設定だけでよい name.artist = knownArtist; break; } + // おまけがある場合はそれをjoinPhraseに回す name.name = knownName; name.artist = knownArtist; name.joinPhrase = remainName + name.joinPhrase; break; } } + // joinPhraseに既知の名前があれば分割する let firstArtist = null; for (const [knownName, knownArtist] of creditMap) { - if (knownArtist == null) + if (!isArtistExist(knownArtist)) continue; - const i = name.joinPhrase.indexOf(knownName); - if (i === -1) + const index = name.joinPhrase.indexOf(knownName); + if (index === -1) continue; - if (firstArtist == null || firstArtist[0] > i) { - firstArtist = [i, knownName, knownArtist]; + if (firstArtist == null || firstArtist[0] > index) { + firstArtist = [index, knownName, knownArtist]; } } + // 既知の名前があった! if (firstArtist != null) { - const [i, knownName, knownArtist] = firstArtist; - const remainName = name.joinPhrase.slice(i + knownName.length); - name.joinPhrase = name.joinPhrase.slice(0, i); + const [index, knownName, knownArtist] = firstArtist; + const remainName = name.joinPhrase.slice(index + knownName.length); + name.joinPhrase = name.joinPhrase.slice(0, index); const newCreditName = { name: knownName, artist: knownArtist, @@ -90,12 +98,12 @@ const creditMap = new Map(); for (const credit of currentCredits) { if (!creditMap.has(credit.name)) { - if (credit.artist != null) { + if (isArtistExist(credit.artist)) { creditMap.set(credit.name, credit.artist); } } else { - if (credit.artist == null) { + if (!isArtistExist(credit.artist)) { creditMap.set(credit.name, null); } else { From 94aa3090aea69f9511b4f6202e725ac3d821304e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 14:11:27 +0000 Subject: [PATCH 099/131] update with 5d25cb7b9110a144086f3a42997389c91dfd494b --- mb-seed-urls-to-release-recordings.user.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index ab6f4f1..58ce0df 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MusicBrainz: Seed URLs to Release Recordings // @namespace https://rinsuki.net -// @version 0.2.1 +// @version 0.2.2 // @description Import recording-url relationship to release's recordings. // @author rinsuki // @match https://musicbrainz.org/release/*/edit-relationships @@ -31,6 +31,7 @@ Zod.object({ url: Zod.string().url(), types: Zod.array(Zod.string()), // link_type UUID + ended: Zod.boolean().optional(), })), note: Zod.string(), }).or(Zod.object({ @@ -39,6 +40,7 @@ Zod.array(Zod.object({ url: Zod.string().url(), types: Zod.array(Zod.string()), // link_type UUID + ended: Zod.boolean().optional(), }))), note: Zod.string(), })); @@ -69,7 +71,7 @@ begin_date: null, // TODO: support? end_date: null, // TODO: support? editsPending: false, - ended: false, + ended: !!relationship.ended, // defaults to false entity0: relationship.recording, entity0_credit: "", entity1: { @@ -159,6 +161,7 @@ recording: track.recording, url: rel.url, linkTypeID, + ended: rel.ended ?? false, }); } } From bc0c8ef3f9447ff046928fe954d795e9cae3a878 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 08:30:04 +0000 Subject: [PATCH 100/131] update with c2163ba7c57f9241cec2d7b4209f39c2a0f2291e --- mastodon-show-act-users.user.js | 2 +- mb-artist-credit-splitter.user.js | 25 ++++++++++++++++--- ...-recording-rels-from-other-release.user.js | 2 +- mb-seed-urls-to-release-recordings.user.js | 2 +- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/mastodon-show-act-users.user.js b/mastodon-show-act-users.user.js index f8dd615..5b90623 100644 --- a/mastodon-show-act-users.user.js +++ b/mastodon-show-act-users.user.js @@ -68,7 +68,7 @@ }; if (error) { return React.createElement("div", { style: centeringStyle }, - React.createElement("span", { style: { color: "hsl(0, 100%, 60%)", whiteSpace: "pre-wrap" } }, error.stack)); + React.createElement("span", { style: { color: "hsl(0, 100%, 60%)", whiteSpace: "pre-wrap" } }, error != null && typeof error === "object" && "stack" in error && typeof error.stack === "string" && error.stack)); } else if (loading) { return React.createElement("div", { style: centeringStyle }, diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 9cdda49..7d2f6d6 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -22,6 +22,23 @@ return elem[name]; } + const LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT = "copiedArtistCredit"; + function getArtistCreditClipboard() { + const str = localStorage.getItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT); + if (str == null) + return undefined; + try { + return JSON.parse(str); + } + catch (e) { + console.warn("Failed to parse artist credit clipboard data", e); + return undefined; + } + } + function setArtistCreditClipboard(artistCredit) { + localStorage.setItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT, JSON.stringify(artistCredit)); + } + function waitDOMByObserve(root, check, options) { const firstRes = check(); if (firstRes != null) @@ -54,7 +71,6 @@ return splittedCredits; } - const LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT = "copiedArtistCredit"; (async () => { const bubble = await waitDOMByObserve(document.body, () => document.querySelector("#artist-credit-bubble"), { subtree: false }); const buttons = await waitDOMByObserve(bubble, () => bubble.querySelector(".buttons"), { subtree: false }); @@ -73,7 +89,7 @@ const dispatch = props.children[0].props.children.props.dispatch; dispatch({ type: "copy" }); await new Promise(resolve => requestAnimationFrame(resolve)); - const currentCredit = JSON.parse(localStorage.getItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT) ?? "").names.map(name => name.name + (name.joinPhrase ?? "")).join(""); + const currentCredit = getArtistCreditClipboard()?.names.map(name => name.name + (name.joinPhrase ?? "")).join("") ?? ""; const splittedCredits = splitCredit(currentCredit); if (!confirm("次のように指定します。よろしいですか?\n\n" + JSON.stringify(splittedCredits, null, 4))) return; @@ -82,17 +98,18 @@ // props.copyArtistCredit() // await p // const stubArtistCredit = JSON.parse(localStorage.getItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT)!) - localStorage.setItem(LOCALSTORAGE_KEY_COPIED_ARTIST_CREDIT, JSON.stringify({ names: splittedCredits.map(([name, joinPhrase], i) => { + setArtistCreditClipboard({ names: splittedCredits.map(([name, joinPhrase], i) => { return { joinPhrase, name, + artist: null, // artist: { // entityType: "artist", // uniqueID: stubArtistCredit[i].artist.uniqueID, // name, // } }; - }) })); + }) }); dispatch({ type: "paste" }); alert("finish!"); }); diff --git a/mb-copy-recording-rels-from-other-release.user.js b/mb-copy-recording-rels-from-other-release.user.js index 13c168b..8213c34 100644 --- a/mb-copy-recording-rels-from-other-release.user.js +++ b/mb-copy-recording-rels-from-other-release.user.js @@ -30,7 +30,7 @@ return res; } - //#region node_modules/.pnpm/typedbrainz@0.1.3/node_modules/typedbrainz/lib/index.js + //#region node_modules/.pnpm/typedbrainz@0.1.4/node_modules/typedbrainz/lib/index.js // SPDX-License-Identifier: MIT function isReleaseRelationshipEditor(relationshipEditor) { return relationshipEditor.state?.entity.entityType === "release"; diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index 58ce0df..b6b56fd 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -17,7 +17,7 @@ (function () { 'use strict'; - //#region node_modules/.pnpm/typedbrainz@0.1.3/node_modules/typedbrainz/lib/index.js + //#region node_modules/.pnpm/typedbrainz@0.1.4/node_modules/typedbrainz/lib/index.js // SPDX-License-Identifier: MIT function isReleaseRelationshipEditor(relationshipEditor) { return relationshipEditor.state?.entity.entityType === "release"; From 3f5a2b95b91997d83fa2ba7d5559a468a4ebb91f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 18 Jan 2026 09:48:28 +0000 Subject: [PATCH 101/131] update with b90296249ad97d394a601a23d66ccb5044fd81be --- mb-copy-recording-rels-to-karaoke.user.js | 100 ++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 mb-copy-recording-rels-to-karaoke.user.js diff --git a/mb-copy-recording-rels-to-karaoke.user.js b/mb-copy-recording-rels-to-karaoke.user.js new file mode 100644 index 0000000..a302dd2 --- /dev/null +++ b/mb-copy-recording-rels-to-karaoke.user.js @@ -0,0 +1,100 @@ +// ==UserScript== +// @name MB: Copy Recording Relationships to Karaoke Recordings +// @version 0.1.0 +// @grant none +// @namespace https://rinsuki.net +// @author rinsuki +// @match https://*musicbrainz.org/release/*/edit-relationships* +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues +// @require https://cdn.jsdelivr.net/npm/@rinsuki/dom-chef@5.1.1/umd.js#sha256-EvGMVNob2jRhcwSo8gGGJp3mZJENNaQwrpYlNRQlUMc=,sha512-b/5dx8A+dc01RsqR2hF2Na/DFwwn64M4PQFka7dXhcAeXjPfpkgWS9YFs1LHPaEckkxAI7ivhVn6i+OrFivnYQ== +// ==/UserScript== + +(function () { + 'use strict'; + + //#region node_modules/.pnpm/typedbrainz@0.1.4/node_modules/typedbrainz/lib/index.js + // SPDX-License-Identifier: MIT + function isReleaseRelationshipEditor(relationshipEditor) { + return relationshipEditor.state?.entity.entityType === "release"; + } + + //#endregion + + /** @jsx h */ + + const KARAOKE_REL_LINK_TYPE_ID = 226; // gid: 39a08d0e-26e4-44fb-ae19-906f5fe9435d + const WORK_REL_LINK_TYPE_ID = 278; // gid: a3005666-a872-32c3-ad06-98af558e99b0 + const WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID = 1261; // gid: 3d984f6e-bbe2-4620-9425-5f32e945b60d + function doIt() { + const MB = window.MB; + if (MB == null) + return alert("MB global not found"); + if (MB.tree == null) + return alert("MB.tree not found"); + const editor = MB.relationshipEditor; + if (editor == null || !isReleaseRelationshipEditor(editor)) + return alert("Relationship editor not found"); + const currentRecordings = new Map(); + for (const medium of MB.tree.iterate(editor.state.mediums)) { + for (const track of MB.tree.iterate(medium[1])) { + currentRecordings.set(track.recording.gid, track.recording); + } + } + for (const recording of currentRecordings.values()) { + const karaokes = recording.relationships.filter(rel => rel.linkTypeID === KARAOKE_REL_LINK_TYPE_ID && rel.entity0_id === recording.id); + for (const karaoke of karaokes) { + const karaokeRecording = karaoke.target; + for (const rel of recording.relationships) { + if (rel.source_type === "recording" && rel.target_type === "recording") + continue; // probably don't want to copy + if (rel.source_type === "url" || rel.target_type === "url") + continue; // probably don't want to copy + const attrs = [...rel.attributes]; + if (rel.linkTypeID === WORK_REL_LINK_TYPE_ID && attrs.find(x => x.typeID === WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID) == null) { + const karaokeAttr = MB.linkedEntities.link_attribute_type[WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID]; + attrs.push({ + type: karaokeAttr, + typeID: WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID, + typeName: karaokeAttr.name, + }); + } + editor.dispatch({ + type: "update-relationship-state", + sourceEntity: rel.backward ? rel.target : karaokeRecording, + oldRelationshipState: null, + newRelationshipState: { + _lineage: [], + _original: null, + editsPending: false, + _status: 1, + entity0: rel.backward ? rel.target : karaokeRecording, + entity1: rel.backward ? karaokeRecording : rel.target, + entity0_credit: rel.entity0_credit, + entity1_credit: rel.entity1_credit, + begin_date: rel.begin_date, + end_date: rel.end_date, + ended: rel.ended, + attributes: MB.tree.fromDistinctAscArray(attrs), + id: editor.getRelationshipStateId(null), + linkTypeID: rel.linkTypeID, + linkOrder: rel.linkOrder, + }, + batchSelectionCount: undefined, + creditsToChangeForSource: "", + creditsToChangeForTarget: "", + }); + } + } + } + let editNote = editor.state.editNoteField.value; + if (editNote.length) + editNote += "\n"; + editNote += "Script: \"" + GM.info.script.name + "\" (" + GM.info.script.version + ")"; + editor.dispatch({ type: "update-edit-note", editNote }); + } + const elm = DOMChef.h("div", null, + DOMChef.h("button", { onClick: () => doIt() }, "Copy Recording Relationships to Karaoke Recordings")); + document.querySelector("#content > div.tabs")?.insertAdjacentElement("afterend", elm); + +})(); From 4eda75cc1c81c7d74fbb305b79e84bd35fcd5e76 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 18 Jan 2026 10:05:05 +0000 Subject: [PATCH 102/131] update with 9691f84d950bbaf65fd8fdd005ad3b9481ea58b9 --- mb-copy-recording-rels-to-karaoke.user.js | 48 ++++++++++++++++------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/mb-copy-recording-rels-to-karaoke.user.js b/mb-copy-recording-rels-to-karaoke.user.js index a302dd2..17a80c5 100644 --- a/mb-copy-recording-rels-to-karaoke.user.js +++ b/mb-copy-recording-rels-to-karaoke.user.js @@ -1,6 +1,6 @@ // ==UserScript== -// @name MB: Copy Recording Relationships to Karaoke Recordings -// @version 0.1.0 +// @name MB: Copy Recording Relationships to Karaoke/Edited Recordings +// @version 0.2.0 // @grant none // @namespace https://rinsuki.net // @author rinsuki @@ -24,8 +24,10 @@ /** @jsx h */ const KARAOKE_REL_LINK_TYPE_ID = 226; // gid: 39a08d0e-26e4-44fb-ae19-906f5fe9435d + const EDITS_REL_LINK_TYPE_ID = 309; // gid: ce01b3ac-dd47-4702-9302-085344f96e84 const WORK_REL_LINK_TYPE_ID = 278; // gid: a3005666-a872-32c3-ad06-98af558e99b0 const WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID = 1261; // gid: 3d984f6e-bbe2-4620-9425-5f32e945b60d + const WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID = 579; // gid: d2b63be6-91ec-426a-987a-30b47f8aae2d function doIt() { const MB = window.MB; if (MB == null) @@ -42,34 +44,50 @@ } } for (const recording of currentRecordings.values()) { - const karaokes = recording.relationships.filter(rel => rel.linkTypeID === KARAOKE_REL_LINK_TYPE_ID && rel.entity0_id === recording.id); - for (const karaoke of karaokes) { - const karaokeRecording = karaoke.target; + console.log(recording); + const subrecordingrels = [ + ...recording.relationships.filter(rel => KARAOKE_REL_LINK_TYPE_ID === rel.linkTypeID && rel.entity0_id === recording.id), + ...recording.relationships.filter(rel => EDITS_REL_LINK_TYPE_ID === rel.linkTypeID && rel.entity1_id === recording.id), + ]; + for (const subrecordingrel of subrecordingrels) { + const subrecording = subrecordingrel.target; for (const rel of recording.relationships) { if (rel.source_type === "recording" && rel.target_type === "recording") continue; // probably don't want to copy if (rel.source_type === "url" || rel.target_type === "url") continue; // probably don't want to copy const attrs = [...rel.attributes]; - if (rel.linkTypeID === WORK_REL_LINK_TYPE_ID && attrs.find(x => x.typeID === WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID) == null) { - const karaokeAttr = MB.linkedEntities.link_attribute_type[WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID]; - attrs.push({ - type: karaokeAttr, - typeID: WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID, - typeName: karaokeAttr.name, - }); + if (subrecordingrel.linkTypeID === KARAOKE_REL_LINK_TYPE_ID) { + if (rel.linkTypeID === WORK_REL_LINK_TYPE_ID && attrs.find(x => x.typeID === WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID) == null) { + const karaokeAttr = MB.linkedEntities.link_attribute_type[WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID]; + attrs.push({ + type: karaokeAttr, + typeID: WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID, + typeName: karaokeAttr.name, + }); + } + } + else if (subrecordingrel.linkTypeID === EDITS_REL_LINK_TYPE_ID) { + if (rel.linkTypeID === WORK_REL_LINK_TYPE_ID && attrs.find(x => x.typeID === WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID) == null) { + const partialAttr = MB.linkedEntities.link_attribute_type[WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID]; + attrs.push({ + type: partialAttr, + typeID: WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID, + typeName: partialAttr.name, + }); + } } editor.dispatch({ type: "update-relationship-state", - sourceEntity: rel.backward ? rel.target : karaokeRecording, + sourceEntity: rel.backward ? rel.target : subrecording, oldRelationshipState: null, newRelationshipState: { _lineage: [], _original: null, editsPending: false, _status: 1, - entity0: rel.backward ? rel.target : karaokeRecording, - entity1: rel.backward ? karaokeRecording : rel.target, + entity0: rel.backward ? rel.target : subrecording, + entity1: rel.backward ? subrecording : rel.target, entity0_credit: rel.entity0_credit, entity1_credit: rel.entity1_credit, begin_date: rel.begin_date, From eb7d790692767ae75a2de721bdd357f6efa037b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 Jan 2026 18:55:13 +0000 Subject: [PATCH 103/131] update with 1b0cc92c027cb5a30688a01608c72a2301982824 --- mb-copy-recording-rels-to-karaoke.user.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mb-copy-recording-rels-to-karaoke.user.js b/mb-copy-recording-rels-to-karaoke.user.js index 17a80c5..5ec5f0c 100644 --- a/mb-copy-recording-rels-to-karaoke.user.js +++ b/mb-copy-recording-rels-to-karaoke.user.js @@ -1,5 +1,6 @@ // ==UserScript== // @name MB: Copy Recording Relationships to Karaoke/Edited Recordings +// @description Copy recording-{artist, work, etc...} relationships to karaoke/edited recordings with one button! // @version 0.2.0 // @grant none // @namespace https://rinsuki.net From befadda773ca049f485d37cb53b69cb4221c8c46 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 12:36:35 +0000 Subject: [PATCH 104/131] update with e9428a09ae053f3baf74a62aa9bda96bb365e8f2 --- mb-match-tracklist-credits-with-other-credits.user.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index 6113a92..7f78824 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -1,6 +1,7 @@ // ==UserScript== // @name MB: Match Tracklist Credits with Other Credits // @namespace https://rinsuki.net +// @version 0.2.0 // @grant none // @match https://*musicbrainz.org/release/*/edit // @match https://*musicbrainz.org/release/add @@ -94,6 +95,12 @@ return; } currentCredits.push(...otherJS.artistCredit.names); + for (const media of otherJS.mediums) { + const otherJS = await fetch(`/ws/js/medium/${media.id}`).then(r => r.json()); + for (const track of otherJS.tracks) { + currentCredits.push(...track.artistCredit.names); + } + } } const creditMap = new Map(); for (const credit of currentCredits) { @@ -104,12 +111,14 @@ } else { if (!isArtistExist(credit.artist)) { + console.warn("?"); creditMap.set(credit.name, null); } else { const current = creditMap.get(credit.name); if (current == null) continue; + console.log(current); if (current.id !== credit.artist.id) { creditMap.set(credit.name, null); } From 3e99d0368a1b53df3bd09e90e42c08ecbdfd1bd6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:55:47 +0000 Subject: [PATCH 105/131] update with c3460482a34e1ee6de06de6382d687eb98579544 From 52fa1755adebaf2fc6863ccd0ccd75602bb87bed Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:47:50 +0000 Subject: [PATCH 106/131] update with 2c741f33ba304d0857da8b82125c368783b0e9fd --- mb-copy-recording-rels-from-other-release.user.js | 2 +- mb-copy-recording-rels-to-karaoke.user.js | 2 +- mb-match-tracklist-credits-with-other-credits.user.js | 4 +++- mb-seed-urls-to-release-recordings.user.js | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mb-copy-recording-rels-from-other-release.user.js b/mb-copy-recording-rels-from-other-release.user.js index 8213c34..fa15041 100644 --- a/mb-copy-recording-rels-from-other-release.user.js +++ b/mb-copy-recording-rels-from-other-release.user.js @@ -30,7 +30,7 @@ return res; } - //#region node_modules/.pnpm/typedbrainz@0.1.4/node_modules/typedbrainz/lib/index.js + //#region node_modules/.pnpm/typedbrainz@0.2.0/node_modules/typedbrainz/lib/index.js // SPDX-License-Identifier: MIT function isReleaseRelationshipEditor(relationshipEditor) { return relationshipEditor.state?.entity.entityType === "release"; diff --git a/mb-copy-recording-rels-to-karaoke.user.js b/mb-copy-recording-rels-to-karaoke.user.js index 5ec5f0c..2711ff6 100644 --- a/mb-copy-recording-rels-to-karaoke.user.js +++ b/mb-copy-recording-rels-to-karaoke.user.js @@ -14,7 +14,7 @@ (function () { 'use strict'; - //#region node_modules/.pnpm/typedbrainz@0.1.4/node_modules/typedbrainz/lib/index.js + //#region node_modules/.pnpm/typedbrainz@0.2.0/node_modules/typedbrainz/lib/index.js // SPDX-License-Identifier: MIT function isReleaseRelationshipEditor(relationshipEditor) { return relationshipEditor.state?.entity.entityType === "release"; diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index 7f78824..d583c94 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -37,7 +37,7 @@ // おまけがある場合はそれをjoinPhraseに回す name.name = knownName; name.artist = knownArtist; - name.joinPhrase = remainName + name.joinPhrase; + name.joinPhrase = remainName + (name.joinPhrase ?? ""); break; } } @@ -46,6 +46,8 @@ for (const [knownName, knownArtist] of creditMap) { if (!isArtistExist(knownArtist)) continue; + if (name.joinPhrase == null) + continue; const index = name.joinPhrase.indexOf(knownName); if (index === -1) continue; diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index b6b56fd..43bfcba 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -17,7 +17,7 @@ (function () { 'use strict'; - //#region node_modules/.pnpm/typedbrainz@0.1.4/node_modules/typedbrainz/lib/index.js + //#region node_modules/.pnpm/typedbrainz@0.2.0/node_modules/typedbrainz/lib/index.js // SPDX-License-Identifier: MIT function isReleaseRelationshipEditor(relationshipEditor) { return relationshipEditor.state?.entity.entityType === "release"; From 84d05b8fac6138d8d697078384572181162edb11 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 06:32:19 +0000 Subject: [PATCH 107/131] update with 03ebbddd60e31642d21fc5cb2a8dcf585510975c --- mb-seed-from-barcode-search.user.js | 101 +++++++++++++++++++++ mb-seed-urls-to-release-recordings.user.js | 8 +- 2 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 mb-seed-from-barcode-search.user.js diff --git a/mb-seed-from-barcode-search.user.js b/mb-seed-from-barcode-search.user.js new file mode 100644 index 0000000..cdbf701 --- /dev/null +++ b/mb-seed-from-barcode-search.user.js @@ -0,0 +1,101 @@ +// ==UserScript== +// @name MB: Barcode Search ++ +// @namespace https://rinsuki.net +// @match https://*musicbrainz.org/search* +// @description Add seed barcode to new release button, highlights barcode-matched releases +// @grant none +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues +// ==/UserScript== + +(function () { + 'use strict'; + + function removeFirstZeroes(str) { + return str.replace(/^0+/, ""); + } + + function main() { + const addReleaseLink = document.querySelector('#content a[href="/release/add"]'); + if (addReleaseLink == null) + return; + const url = new URL(location.href); + const searchQuery = url.searchParams.get('query'); + if (searchQuery == null) + return; + const barcodeMatch = searchQuery.split(" ").find(part => part.startsWith("barcode:")); + if (barcodeMatch == null) + return; + const barcode = barcodeMatch.replace("barcode:", ""); + // add seed button + const form = document.createElement("form"); + form.method = "POST"; + form.action = "/release/add"; + form.style.display = "inline"; + const barcodeInput = document.createElement("input"); + barcodeInput.type = "hidden"; + barcodeInput.name = "barcode"; + barcodeInput.value = barcode; + form.appendChild(barcodeInput); + const seedName = url.searchParams.get("seed.name"); + if (seedName) { + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "name"; + input.value = seedName; + form.appendChild(input); + } + const seedUrl = url.searchParams.get("seed.url"); + if (seedUrl) { + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "urls.0.url"; + input.value = seedUrl; + form.appendChild(input); + } + const addButton = document.createElement("button"); + addButton.type = "submit"; + addButton.textContent = "with barcode " + barcode + " [UserScript]"; + form.appendChild(addButton); + addReleaseLink.insertAdjacentElement("afterend", form); + form.insertAdjacentText("beforebegin", ", or "); + // try to highlight + let focused = false; + for (const td of document.querySelectorAll("td.barcode-cell")) { + if (removeFirstZeroes(barcode) === removeFirstZeroes(td.textContent.trim())) { + td.parentElement.style.border = "2px solid blue"; + const link = td.parentElement.querySelector('a[href*="release/"]:not([href*=cover-art])'); + if (link == null) + continue; + let focusTarget = link; + if (seedUrl) { + const form = document.createElement("form"); + form.method = "POST"; + form.action = link.href + "/edit"; + form.style.display = "inline"; + const input = document.createElement("input"); + input.type = "hidden"; + input.name = "urls.0.url"; + input.value = seedUrl; + form.appendChild(input); + const editButton = document.createElement("button"); + editButton.type = "submit"; + editButton.textContent = "[Add URL UserScript]"; + form.appendChild(editButton); + link.insertAdjacentElement("afterend", form); + form.insertAdjacentText("beforebegin", " "); + focusTarget = editButton; + } + if (!focused) { + focusTarget.focus(); + focused = true; + } + } + } + if (!focused) { + addButton.focus(); + } + } + main(); + +})(); diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index 43bfcba..72d1139 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MusicBrainz: Seed URLs to Release Recordings // @namespace https://rinsuki.net -// @version 0.2.2 +// @version 0.2.3 // @description Import recording-url relationship to release's recordings. // @author rinsuki // @match https://musicbrainz.org/release/*/edit-relationships @@ -108,7 +108,9 @@ if (!isReleaseRelationshipEditor(window.MB.relationshipEditor)) { return; } - const { linkedEntities, relationshipEditor } = window.MB; + const { linkedEntities, relationshipEditor, tree } = window.MB; + if (tree == null) + throw new Error("MB.tree is missing..."); const button = document.createElement("button"); button.textContent = "Seed URLs to Recordings"; button.style.zoom = "2"; @@ -122,7 +124,7 @@ const json = zSeedJSON.parse(anyJSON); const errors = []; const preparedRelationships = []; - for (const track of relationshipEditor.state.entity.mediums.flatMap(m => m.tracks ?? [])) { + for (const track of tree.iterate(relationshipEditor.state.mediums).flatMap(m => tree.iterate(m[1]))) { if (!(track.recording.gid in json.recordings)) continue; const rels = json.recordings[track.recording.gid]; From 813fa539eb9d37ec17578d7e7f6de1e81c5bafe3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 11 Feb 2026 05:06:17 +0000 Subject: [PATCH 108/131] update with 6b674c5f50702d0c39f06cfd378e0213a4d99a61 --- ...acklist-credits-with-other-credits.user.js | 3 +- mb-seed-urls-to-release-recordings.user.js | 86 +++++++++++++++++- mb-split-or-merge-medium.user.js | 90 +++++++++++++++++++ 3 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 mb-split-or-merge-medium.user.js diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index d583c94..10baac8 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MB: Match Tracklist Credits with Other Credits // @namespace https://rinsuki.net -// @version 0.2.0 +// @version 0.2.1 // @grant none // @match https://*musicbrainz.org/release/*/edit // @match https://*musicbrainz.org/release/add @@ -21,6 +21,7 @@ // ループ中にnamesを書き換えるのであえて for-of を使わない for (let i = 0; i < names.length; i++) { const name = names[i]; + name.joinPhrase ??= ""; // アーティストIDが不明の場合、先頭一致できる名前を探す if (!isArtistExist(name.artist)) { for (const [knownName, knownArtist] of creditMap) { diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index 72d1139..4fa8989 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MusicBrainz: Seed URLs to Release Recordings // @namespace https://rinsuki.net -// @version 0.2.3 +// @version 0.2.4 // @description Import recording-url relationship to release's recordings. // @author rinsuki // @match https://musicbrainz.org/release/*/edit-relationships @@ -49,13 +49,97 @@ }); function applyRelationships(relationships, editNote, relationshipEditor) { + const tree = window.MB?.tree; + if (tree == null) + return alert("MB.tree is missing."); relationshipEditor.dispatch({ type: "update-edit-note", editNote: (relationshipEditor.state.editNoteField.value + "\n" + editNote + "\n''Powered by \"" + GM_info.script.name + "\" script (" + GM_info.script.version + ")''").trim(), }); + let alreadyAlerted = false; for (const relationship of relationships) { // it will be marked as "incomplete" in the UI, but actually working? // @see https://github.com/metabrainz/musicbrainz-server/blob/e214b4d3c13f7ee6b2eb2f9c186ecab310354a5b/root/static/scripts/relationship-editor/components/RelationshipItem.js#L153-L163 + // if we already have a same relationship, just change ended flag + const existingRels = relationship.recording.relationships + .filter(r => !r.editsPending) + .filter(r => r.target_type === "url" && r.target.href_url === relationship.url && (relationship.linkTypeID === "all" || r.linkTypeID === relationship.linkTypeID)) + .toSorted((a, b) => { + if (a.ended !== b.ended) { + return (a.ended === relationship.ended) ? -1 : 1; + } + return a.id - b.id; + }); + const dispatchesEdit = []; + const dispatchesRemove = []; + if (existingRels.length > 0) { + let weAlreadyHaveRel = false; + for (const existingRel of existingRels) { + const oldRelationshipState = { + ...existingRel, + _lineage: ["loaded from database"], + _original: null, + _status: 0, + attributes: tree.fromDistinctAscArray(existingRel.attributes), + entity0: relationship.recording, + entity1: existingRel.target, + }; + oldRelationshipState._original = oldRelationshipState; + const isSimpleRel = existingRel.begin_date == null && existingRel.end_date == null; + if (isSimpleRel) { + if (weAlreadyHaveRel) { + dispatchesRemove.push({ + type: "update-relationship-state", + sourceEntity: relationship.recording, + batchSelectionCount: undefined, + creditsToChangeForSource: "", + creditsToChangeForTarget: "", + oldRelationshipState: null, + newRelationshipState: { + ...oldRelationshipState, + _lineage: [...oldRelationshipState._lineage, "removed"], + _status: 3, + }, + }); + continue; + } + weAlreadyHaveRel = true; + } + if (existingRel.ended !== relationship.ended) { + dispatchesEdit.push({ + type: "update-relationship-state", + sourceEntity: relationship.recording, + batchSelectionCount: undefined, + creditsToChangeForSource: "", + creditsToChangeForTarget: "", + oldRelationshipState: null, + newRelationshipState: { + ...oldRelationshipState, + _lineage: [...oldRelationshipState._lineage, "edited"], + _status: 2, + ended: relationship.ended, + } + }); + } + } + if (dispatchesRemove.length) { + for (const d of dispatchesRemove) { + relationshipEditor.dispatch(d); + } + if (!alreadyAlerted) { + alert("Please re-seed after submit these edits."); + alreadyAlerted = true; + } + } + else if (dispatchesEdit.length) { + for (const d of dispatchesEdit) { + relationshipEditor.dispatch(d); + } + } + continue; + } + if (relationship.linkTypeID === "all") + continue; relationshipEditor.dispatch({ type: "update-relationship-state", sourceEntity: relationship.recording, diff --git a/mb-split-or-merge-medium.user.js b/mb-split-or-merge-medium.user.js new file mode 100644 index 0000000..397ae16 --- /dev/null +++ b/mb-split-or-merge-medium.user.js @@ -0,0 +1,90 @@ +// ==UserScript== +// @name MB: Split or Merge Medium (WIP!!) +// @namespace https://rinsuki.net +// @grant none +// @match https://*.musicbrainz.org/release/*/edit +// @match https://*.musicbrainz.org/release/add +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues +// ==/UserScript== + +(function () { + 'use strict'; + + function isMBWithReleaseEditor(mb) { + return mb.releaseEditor !== undefined; + } + + function main() { + const MB = window.MB; + if (MB == null) + return; + if (!isMBWithReleaseEditor(MB)) + return; + const tracklist = document.getElementById("tracklist"); + if (tracklist == null) + return; + const splitButton = document.createElement("button"); + splitButton.textContent = "Split Medium"; + splitButton.addEventListener("click", () => { + if (MB.releaseEditor.rootField.release().mediums().length < 2) { + alert("There must be at least 2 mediums to split (src and dst)."); + return; + } + const srcMediumIndex = prompt([ + "Choose source medium:", + "", + ...Array.from(MB.releaseEditor.rootField.release().mediums().map((m, i) => [ + `${i + 1}: `, + m.name(), + ` (${m.tracks().length} tracks)` + ].join(""))) + ].join("\n")); + if (srcMediumIndex == null) + return; + const index = parseInt(srcMediumIndex, 10) - 1; + if (isNaN(index) || index < 0 || index >= MB.releaseEditor.rootField.release().mediums().length) { + alert("Invalid medium index."); + return; + } + const srcMedium = MB.releaseEditor.rootField.release().mediums()[index]; + const dstMediumIndex = prompt([ + "Choose destination medium:", + "", + ...Array.from(MB.releaseEditor.rootField.release().mediums().map((m, i) => [ + `${i + 1}: `, + m.name(), + ` (${m.tracks().length} tracks)` + ].join(""))) + ].join("\n")); + if (dstMediumIndex == null) + return; + const dstIndex = parseInt(dstMediumIndex, 10) - 1; + if (isNaN(dstIndex) || dstIndex < 0 || dstIndex >= MB.releaseEditor.rootField.release().mediums().length) { + alert("Invalid medium index."); + return; + } + const dstMedium = MB.releaseEditor.rootField.release().mediums()[dstIndex]; + const trackNumber = prompt([ + `Please enter the track number to move from "${srcMedium.position()}. ${srcMedium.name()}" to "${dstMedium.position()}. ${dstMedium.name()}".`, + "", + ...Array.from(srcMedium.tracks().map((t, i) => [ + `${i + 1}: `, + t.name() + ].join(""))) + ].join("\n")); + if (trackNumber == null) + return; + const trackIndex = parseInt(trackNumber, 10) - 1; + if (isNaN(trackIndex) || trackIndex < 0 || trackIndex >= srcMedium.tracks().length) { + alert("Invalid track number."); + return; + } + dstMedium.tracks.unshift(...srcMedium.tracks.splice(trackIndex)); + alert("Moved!"); + }); + tracklist.insertAdjacentElement("afterbegin", splitButton); + } + main(); + +})(); From 01eca5f8c7936c49ac7e7e175801aaa5367cd723 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 10:33:00 +0000 Subject: [PATCH 109/131] update with c6562215355d96ec94b52954f7ff5296a2a4a0f0 --- ytm-dont-suggest-video.user.js | 82 ++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 ytm-dont-suggest-video.user.js diff --git a/ytm-dont-suggest-video.user.js b/ytm-dont-suggest-video.user.js new file mode 100644 index 0000000..a289192 --- /dev/null +++ b/ytm-dont-suggest-video.user.js @@ -0,0 +1,82 @@ +// ==UserScript== +// @name YTM: Don't Suggest Video +// @namespace https://rinsuki.net +// @match https://music.youtube.com/* +// @grant none +// @author rinsuki +// @run-at document-start +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues +// ==/UserScript== + +(function () { + 'use strict'; + + function urlFromFetch(input) { + if (typeof input === "string") + return input; + if ("url" in input) + return input.url; + return input.href; + } + function shouldFilterThisEntry(json) { + if (json.playlistPanelVideoWrapperRenderer) + return true; // たぶん + if (json.automixPreviewVideoRenderer) + return true; + const thumbnails = json.playlistPanelVideoRenderer?.thumbnail?.thumbnails; + if (!Array.isArray(thumbnails)) { + debugger; + return true; // ? + } + for (const thumbnail of thumbnails) { + if (typeof thumbnail.url === "string" && thumbnail.url.includes("i.ytimg.com")) { + // debugger; + return false; + } + } + return true; + } + function handleNextResponse(json) { + let checked = false; + if (Array.isArray(json.continuationContents?.playlistPanelContinuation?.contents)) { + json.continuationContents.playlistPanelContinuation.contents = json.continuationContents.playlistPanelContinuation.contents.filter(shouldFilterThisEntry); + checked = true; + } + if (Array.isArray(json.contents?.singleColumnMusicWatchNextResultsRenderer?.tabbedRenderer?.watchNextTabbedResultsRenderer?.tabs)) { + for (const tab of json.contents.singleColumnMusicWatchNextResultsRenderer.tabbedRenderer.watchNextTabbedResultsRenderer.tabs) { + if (Array.isArray(tab.tabRenderer?.content?.musicQueueRenderer?.content?.playlistPanelRenderer?.contents)) { + tab.tabRenderer.content.musicQueueRenderer.content.playlistPanelRenderer.contents = tab.tabRenderer.content.musicQueueRenderer.content.playlistPanelRenderer.contents.filter(shouldFilterThisEntry); + checked = true; + } + } + } + if (!checked) + debugger; + } + window.fetch = new Proxy(window.fetch, { + apply(target, thisArg, args) { + const promise = Reflect.apply(target, thisArg, args); + const url = urlFromFetch(args[0]); + if (!url.includes("/next")) + return promise; + return promise.then(async (resOriginal) => { + const res = resOriginal.clone(); + try { + const json = await res.json(); + handleNextResponse(json); + return Response.json(json, { + status: resOriginal.status, + statusText: resOriginal.statusText, + headers: resOriginal.headers, + }); + } + catch (e) { + debugger; + return resOriginal; + } + }); + } + }); + +})(); From 700ad2a7de42d2647bff69ae7d1989cac67a462e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 10:42:53 +0000 Subject: [PATCH 110/131] update with a385f5a11945b0309b70590ccf217e853b49e643 --- ytm-dont-suggest-video.user.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ytm-dont-suggest-video.user.js b/ytm-dont-suggest-video.user.js index a289192..70c8ed2 100644 --- a/ytm-dont-suggest-video.user.js +++ b/ytm-dont-suggest-video.user.js @@ -38,21 +38,16 @@ return true; } function handleNextResponse(json) { - let checked = false; if (Array.isArray(json.continuationContents?.playlistPanelContinuation?.contents)) { json.continuationContents.playlistPanelContinuation.contents = json.continuationContents.playlistPanelContinuation.contents.filter(shouldFilterThisEntry); - checked = true; } if (Array.isArray(json.contents?.singleColumnMusicWatchNextResultsRenderer?.tabbedRenderer?.watchNextTabbedResultsRenderer?.tabs)) { for (const tab of json.contents.singleColumnMusicWatchNextResultsRenderer.tabbedRenderer.watchNextTabbedResultsRenderer.tabs) { if (Array.isArray(tab.tabRenderer?.content?.musicQueueRenderer?.content?.playlistPanelRenderer?.contents)) { tab.tabRenderer.content.musicQueueRenderer.content.playlistPanelRenderer.contents = tab.tabRenderer.content.musicQueueRenderer.content.playlistPanelRenderer.contents.filter(shouldFilterThisEntry); - checked = true; } } } - if (!checked) - debugger; } window.fetch = new Proxy(window.fetch, { apply(target, thisArg, args) { From 7bc4ee0264b81d07b36f240258f00eedb84aa54b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 10:49:24 +0000 Subject: [PATCH 111/131] update with 7c882c15e8bbc3740e1fbe5b73124fd01b170cd5 --- ytm-dont-suggest-video.user.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ytm-dont-suggest-video.user.js b/ytm-dont-suggest-video.user.js index 70c8ed2..8b92af4 100644 --- a/ytm-dont-suggest-video.user.js +++ b/ytm-dont-suggest-video.user.js @@ -5,6 +5,8 @@ // @grant none // @author rinsuki // @run-at document-start +// @version 0.1.0 +// @description Hide video-only tracks from "Up Next" tab on YTM. // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== From 361d372a3113c978f9804c935ab99880d121bae6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 22:34:14 +0000 Subject: [PATCH 112/131] update with 2bc66f2f6885065e10d0ab29b13d1611d549b51d --- mb-match-tracklist-credits-with-other-credits.user.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index 10baac8..ccd4a01 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MB: Match Tracklist Credits with Other Credits // @namespace https://rinsuki.net -// @version 0.2.1 +// @version 0.2.2 // @grant none // @match https://*musicbrainz.org/release/*/edit // @match https://*musicbrainz.org/release/add @@ -13,7 +13,7 @@ 'use strict'; function isArtistExist(artist) { - return artist != null && artist.id != 0; + return artist != null && artist.id != 0 && artist.id != null; } function doItForSpecificArtistCredit(creditMap, artistCredit) { const names = [...artistCredit().names]; @@ -122,7 +122,7 @@ if (current == null) continue; console.log(current); - if (current.id !== credit.artist.id) { + if (current.id != null && current.id !== credit.artist.id) { creditMap.set(credit.name, null); } } From 2473f3c914bca3cdf7c83e3b4cbceeef598a7e13 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 22:44:54 +0000 Subject: [PATCH 113/131] update with c8ac831aca0c3dd11c3d33fdcaf76c83a261c3f3 --- mb-match-tracklist-credits-with-other-credits.user.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index ccd4a01..fca9e23 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MB: Match Tracklist Credits with Other Credits // @namespace https://rinsuki.net -// @version 0.2.2 +// @version 0.2.3 // @grant none // @match https://*musicbrainz.org/release/*/edit // @match https://*musicbrainz.org/release/add @@ -138,7 +138,7 @@ const button = document.createElement("button"); button.id = "mb-match-tracklist-credits-with-other-credits-button"; document.getElementById(button.id)?.remove(); - button.textContent = "Match Tracklist Credits with Other Credits"; + button.textContent = "Match Tracklist Credits with Other Credits (Click with Shift key to load from another release)"; button.addEventListener("click", (e) => { doItEntirely(e.shiftKey); }); From 7c1853f8dcd32c83bc9a9a252dcfae62a7c4d3eb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Feb 2026 22:53:23 +0000 Subject: [PATCH 114/131] update with e320048e1de43aea7d7baab8a0984dcfb8f7e2ad --- ...atch-tracklist-credits-with-other-credits.user.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index fca9e23..da091b5 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MB: Match Tracklist Credits with Other Credits // @namespace https://rinsuki.net -// @version 0.2.3 +// @version 0.3.0 // @grant none // @match https://*musicbrainz.org/release/*/edit // @match https://*musicbrainz.org/release/add @@ -83,10 +83,10 @@ ...editor.rootField.release().artistCredit().names, ...Array.from(editor.rootField.release().allTracks()).flatMap(t => [...t.artistCredit().names, ...t.recording().artistCredit.names]), ]; - if (withShiftKey) { - const releaseUrl = prompt("Enter the release URL to copy credits from"); - if (releaseUrl == null || releaseUrl === "") - return; + const releaseUrl = prompt("Enter the release URL to copy credits from (If you keep it empty, it will only match within the current release)"); + if (releaseUrl == null) + return; + if (releaseUrl.length) { const mbid = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i.exec(releaseUrl)?.[0]; if (mbid == null) { alert("Invalid release URL"); @@ -138,7 +138,7 @@ const button = document.createElement("button"); button.id = "mb-match-tracklist-credits-with-other-credits-button"; document.getElementById(button.id)?.remove(); - button.textContent = "Match Tracklist Credits with Other Credits (Click with Shift key to load from another release)"; + button.textContent = "Match Tracklist Credits with Other Credits"; button.addEventListener("click", (e) => { doItEntirely(e.shiftKey); }); From 359c239f64d75d00cbe29449965b191f39c6e48b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 23:39:44 +0000 Subject: [PATCH 115/131] update with eb9962a123370027dc61e76b45f547799b6a5cc0 --- mb-auto-set-media-type-by-url-rels.user.js | 73 ++++++++++++++++++++ mb-clear-tracklist-artist.user.js | 39 +++++++++++ mb-create-release-from-other-release.user.js | 71 +++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 mb-auto-set-media-type-by-url-rels.user.js create mode 100644 mb-clear-tracklist-artist.user.js create mode 100644 mb-create-release-from-other-release.user.js diff --git a/mb-auto-set-media-type-by-url-rels.user.js b/mb-auto-set-media-type-by-url-rels.user.js new file mode 100644 index 0000000..f59c2c2 --- /dev/null +++ b/mb-auto-set-media-type-by-url-rels.user.js @@ -0,0 +1,73 @@ +// ==UserScript== +// @name MB: Automatically Set Media Type by URL Relationships +// @description Automatically set the media type based on the relationships of release's URL. +// @namespace https://rinsuki.net +// @author rinsuki +// @grant none +// @match https://*musicbrainz.org/release/add* +// @match https://*musicbrainz.org/release/*/edit* +// @exclude-match https://*musicbrainz.org/release/*/edit-relationships* +// @run-at document-idle +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues +// ==/UserScript== + +(function () { + 'use strict'; + + const LINK_TYPE_GID_RELEASE_FREE_STREAMING = "08445ccf-7b99-4438-9f9a-fb9ac18099ee"; + const LINK_TYPE_GID_RELEASE_PAID_STREAMING = "320adf26-96fa-4183-9045-1f5f32f833cb"; + const LINK_TYPE_GID_RELEASE_PAID_DOWNLOAD = "98e08c20-8402-4163-8970-53504bb6a1e4"; + + const MEDIUM_FORMAT_DIGITAL_RELEASE = "12"; + + function isMBWithReleaseEditor(mb) { + return mb.releaseEditor !== undefined; + } + + const DIGITAL_MEDIA_TYPES = [ + LINK_TYPE_GID_RELEASE_FREE_STREAMING, + LINK_TYPE_GID_RELEASE_PAID_DOWNLOAD, + LINK_TYPE_GID_RELEASE_PAID_STREAMING, + ]; + function estimateMediumType(MB) { + for (const url of MB.releaseEditor.externalLinks?.externalLinksEditorRef.current?.state.links ?? []) { + if (url.type == null) + continue; + const gid = MB.linkedEntities.link_type[url.type]?.gid; + if (DIGITAL_MEDIA_TYPES.includes(gid)) { + return MEDIUM_FORMAT_DIGITAL_RELEASE; + } + } + } + function doIt(medium, type) { + if (medium.formatID()?.length < 1) { + medium.formatID(type); + } + } + function main() { + const MB = window.MB; + if (!isMBWithReleaseEditor(MB)) + return console.log("You are not on the release editor page."); + MB.releaseEditor.rootField.release().mediums.subscribe(m => { + const type = estimateMediumType(MB); + if (type == null) + return; + for (const medium of m) { + doIt(medium, type); + } + }); + MB.releaseEditor.activeTabID.subscribe(tabID => { + if (tabID !== "#tracklist") + return; + const type = estimateMediumType(MB); + if (type == null) + return; + for (const medium of MB.releaseEditor.rootField.release().mediums()) { + doIt(medium, type); + } + }); + } + main(); + +})(); diff --git a/mb-clear-tracklist-artist.user.js b/mb-clear-tracklist-artist.user.js new file mode 100644 index 0000000..84a30ca --- /dev/null +++ b/mb-clear-tracklist-artist.user.js @@ -0,0 +1,39 @@ +// ==UserScript== +// @name MB: Clear Tracklist Artist +// @namespace https://rinsuki.net +// @match https://*musicbrainz.org/release/add +// @match https://*musicbrainz.org/release/*/edit +// @grant none +// @author rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues +// ==/UserScript== + +(function () { + 'use strict'; + + function isMBWithReleaseEditor(mb) { + return mb.releaseEditor !== undefined; + } + + function main() { + const tracklist = document.getElementById("tracklist"); + if (tracklist == null) + return; + const button = document.createElement("button"); + button.textContent = "Clear Tracklist Artist"; + button.addEventListener("click", () => { + const MB = window.MB; + if (!isMBWithReleaseEditor(MB)) + return alert("You are not on the release editor page."); + if (!confirm("Are you sure you want to clear the tracklist artist?")) + return; + for (const track of MB.releaseEditor.rootField.release().allTracks()) { + track.artistCredit({ names: [] }); + } + }); + tracklist.prepend(button); + } + main(); + +})(); diff --git a/mb-create-release-from-other-release.user.js b/mb-create-release-from-other-release.user.js new file mode 100644 index 0000000..b0360cf --- /dev/null +++ b/mb-create-release-from-other-release.user.js @@ -0,0 +1,71 @@ +// ==UserScript== +// @name MB: Create Release from Other Release +// @match https://*musicbrainz.org/release-group/* +// @grant none +// @namespace https://rinsuki.net +// @author rinsuki +// @description in the release group page +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues +// ==/UserScript== + +(function () { + 'use strict'; + + function main() { + const table = document.querySelector("table.mergeable-table"); + if (table == null) + return; + for (const row of table.rows) { + const link = row.querySelector("td > a[href^='/release/'][href*='-']:has(bdi)"); + if (link == null) + continue; + const button = document.createElement("button"); + button.textContent = "Copy"; + button.addEventListener("click", e => { + e.preventDefault(); + button.textContent = "……"; + button.disabled = true; + fetch(`/ws/js/release/${new URL(link.href).pathname.split("/")[2]}`).then(r => r.json()).then((r) => { + let f = { + name: r.name, + release_group: r.releaseGroup?.gid, + comment: r.comment, + barcode: r.barcode, + language: r.language?.iso_code_3, + script: r.script?.iso_code, + }; + for (let i = 0; i < (r.labels?.length ?? 0); i++) { + f[`labels.${i}.mbid`] = r.labels?.[i].label?.gid; + } + for (let i = 0; i < r.artistCredit.names.length; i++) { + f[`artist_credit.names.${i}.name`] = r.artistCredit.names[i].name; + f[`artist_credit.names.${i}.mbid`] = r.artistCredit.names[i].artist?.gid; + f[`artist_credit.names.${i}.join_phrase`] = r.artistCredit.names[i].joinPhrase; + } + const form = document.createElement("form"); + form.method = "POST"; + form.action = "/release/add"; + for (const [k, v] of Object.entries(f)) { + if (v == null) + continue; + const input = document.createElement("input"); + input.type = "hidden"; + input.name = k; + input.value = v; + form.appendChild(input); + } + document.body.appendChild(form); + form.submit(); + }).catch(e => { + console.error(e); + button.textContent = "Error"; + button.disabled = false; + }); + }); + link.parentElement?.prepend(button); + } + } + main(); + +})(); From eae32e5a3584f63813a865ff9794edc24b77a996 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 20:29:28 +0000 Subject: [PATCH 116/131] update with 39870c0e1f2ec8c1f4696175a601192bd5430b2d --- mb-auto-set-media-type-by-url-rels.user.js | 6 +++--- mb-clear-tracklist-artist.user.js | 4 ++-- mb-copy-recording-rels-from-other-release.user.js | 2 +- mb-copy-recording-rels-to-karaoke.user.js | 2 +- mb-create-release-from-other-release.user.js | 2 +- mb-match-tracklist-credits-with-other-credits.user.js | 4 ++-- mb-seed-from-barcode-search.user.js | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mb-auto-set-media-type-by-url-rels.user.js b/mb-auto-set-media-type-by-url-rels.user.js index f59c2c2..55be179 100644 --- a/mb-auto-set-media-type-by-url-rels.user.js +++ b/mb-auto-set-media-type-by-url-rels.user.js @@ -4,9 +4,9 @@ // @namespace https://rinsuki.net // @author rinsuki // @grant none -// @match https://*musicbrainz.org/release/add* -// @match https://*musicbrainz.org/release/*/edit* -// @exclude-match https://*musicbrainz.org/release/*/edit-relationships* +// @match https://*.musicbrainz.org/release/add* +// @match https://*.musicbrainz.org/release/*/edit* +// @exclude-match https://*.musicbrainz.org/release/*/edit-relationships* // @run-at document-idle // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/mb-clear-tracklist-artist.user.js b/mb-clear-tracklist-artist.user.js index 84a30ca..a22b5a8 100644 --- a/mb-clear-tracklist-artist.user.js +++ b/mb-clear-tracklist-artist.user.js @@ -1,8 +1,8 @@ // ==UserScript== // @name MB: Clear Tracklist Artist // @namespace https://rinsuki.net -// @match https://*musicbrainz.org/release/add -// @match https://*musicbrainz.org/release/*/edit +// @match https://*.musicbrainz.org/release/add +// @match https://*.musicbrainz.org/release/*/edit // @grant none // @author rinsuki // @homepageURL https://github.com/rinsuki/userscripts diff --git a/mb-copy-recording-rels-from-other-release.user.js b/mb-copy-recording-rels-from-other-release.user.js index fa15041..4a030e0 100644 --- a/mb-copy-recording-rels-from-other-release.user.js +++ b/mb-copy-recording-rels-from-other-release.user.js @@ -4,7 +4,7 @@ // @grant none // @namespace https://rinsuki.net // @author rinsuki -// @match https://*musicbrainz.org/release/*/edit-relationships* +// @match https://*.musicbrainz.org/release/*/edit-relationships* // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues // @require https://cdn.jsdelivr.net/npm/@rinsuki/dom-chef@5.1.1/umd.js#sha256-EvGMVNob2jRhcwSo8gGGJp3mZJENNaQwrpYlNRQlUMc=,sha512-b/5dx8A+dc01RsqR2hF2Na/DFwwn64M4PQFka7dXhcAeXjPfpkgWS9YFs1LHPaEckkxAI7ivhVn6i+OrFivnYQ== diff --git a/mb-copy-recording-rels-to-karaoke.user.js b/mb-copy-recording-rels-to-karaoke.user.js index 2711ff6..debe520 100644 --- a/mb-copy-recording-rels-to-karaoke.user.js +++ b/mb-copy-recording-rels-to-karaoke.user.js @@ -5,7 +5,7 @@ // @grant none // @namespace https://rinsuki.net // @author rinsuki -// @match https://*musicbrainz.org/release/*/edit-relationships* +// @match https://*.musicbrainz.org/release/*/edit-relationships* // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues // @require https://cdn.jsdelivr.net/npm/@rinsuki/dom-chef@5.1.1/umd.js#sha256-EvGMVNob2jRhcwSo8gGGJp3mZJENNaQwrpYlNRQlUMc=,sha512-b/5dx8A+dc01RsqR2hF2Na/DFwwn64M4PQFka7dXhcAeXjPfpkgWS9YFs1LHPaEckkxAI7ivhVn6i+OrFivnYQ== diff --git a/mb-create-release-from-other-release.user.js b/mb-create-release-from-other-release.user.js index b0360cf..4ab9a4b 100644 --- a/mb-create-release-from-other-release.user.js +++ b/mb-create-release-from-other-release.user.js @@ -1,6 +1,6 @@ // ==UserScript== // @name MB: Create Release from Other Release -// @match https://*musicbrainz.org/release-group/* +// @match https://*.musicbrainz.org/release-group/* // @grant none // @namespace https://rinsuki.net // @author rinsuki diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index da091b5..bbc0665 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -3,8 +3,8 @@ // @namespace https://rinsuki.net // @version 0.3.0 // @grant none -// @match https://*musicbrainz.org/release/*/edit -// @match https://*musicbrainz.org/release/add +// @match https://*.musicbrainz.org/release/*/edit +// @match https://*.musicbrainz.org/release/add // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== diff --git a/mb-seed-from-barcode-search.user.js b/mb-seed-from-barcode-search.user.js index cdbf701..55fc68b 100644 --- a/mb-seed-from-barcode-search.user.js +++ b/mb-seed-from-barcode-search.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MB: Barcode Search ++ // @namespace https://rinsuki.net -// @match https://*musicbrainz.org/search* +// @match https://*.musicbrainz.org/search* // @description Add seed barcode to new release button, highlights barcode-matched releases // @grant none // @homepageURL https://github.com/rinsuki/userscripts From b9f4d97186fe37b551e9a4ec17bfc106ab6c5e19 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 20:44:37 +0000 Subject: [PATCH 117/131] update with 92345e56d8bd80363b6c6e8348fdf58be3b428f8 --- mb-auto-set-media-type-by-url-rels.user.js | 24 ++++++++++--------- mb-clear-tracklist-artist.user.js | 18 +++++++------- mb-copy-recording-rels-to-karaoke.user.js | 22 +++++++++-------- mb-create-release-from-other-release.user.js | 18 +++++++------- ...acklist-credits-with-other-credits.user.js | 18 +++++++------- mb-seed-from-barcode-search.user.js | 16 +++++++------ ytm-dont-suggest-video.user.js | 22 +++++++++-------- 7 files changed, 76 insertions(+), 62 deletions(-) diff --git a/mb-auto-set-media-type-by-url-rels.user.js b/mb-auto-set-media-type-by-url-rels.user.js index 55be179..84d6a73 100644 --- a/mb-auto-set-media-type-by-url-rels.user.js +++ b/mb-auto-set-media-type-by-url-rels.user.js @@ -1,15 +1,17 @@ // ==UserScript== -// @name MB: Automatically Set Media Type by URL Relationships -// @description Automatically set the media type based on the relationships of release's URL. -// @namespace https://rinsuki.net -// @author rinsuki -// @grant none -// @match https://*.musicbrainz.org/release/add* -// @match https://*.musicbrainz.org/release/*/edit* -// @exclude-match https://*.musicbrainz.org/release/*/edit-relationships* -// @run-at document-idle -// @homepageURL https://github.com/rinsuki/userscripts -// @supportURL https://github.com/rinsuki/userscripts/issues +// @name MB: Automatically Set Media Type by URL Relationships +// @description Automatically set the media type based on the relationships of release's URL. +// @namespace https://rinsuki.net +// @author rinsuki +// @grant none +// @match https://*.musicbrainz.org/release/add* +// @match https://*.musicbrainz.org/release/*/edit* +// @exclude-match https://*.musicbrainz.org/release/*/edit-relationships* +// @run-at document-idle +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { diff --git a/mb-clear-tracklist-artist.user.js b/mb-clear-tracklist-artist.user.js index a22b5a8..24d250a 100644 --- a/mb-clear-tracklist-artist.user.js +++ b/mb-clear-tracklist-artist.user.js @@ -1,12 +1,14 @@ // ==UserScript== -// @name MB: Clear Tracklist Artist -// @namespace https://rinsuki.net -// @match https://*.musicbrainz.org/release/add -// @match https://*.musicbrainz.org/release/*/edit -// @grant none -// @author rinsuki -// @homepageURL https://github.com/rinsuki/userscripts -// @supportURL https://github.com/rinsuki/userscripts/issues +// @name MB: Clear Tracklist Artist +// @namespace https://rinsuki.net +// @match https://*.musicbrainz.org/release/add +// @match https://*.musicbrainz.org/release/*/edit +// @grant none +// @author rinsuki +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { diff --git a/mb-copy-recording-rels-to-karaoke.user.js b/mb-copy-recording-rels-to-karaoke.user.js index debe520..7d789fd 100644 --- a/mb-copy-recording-rels-to-karaoke.user.js +++ b/mb-copy-recording-rels-to-karaoke.user.js @@ -1,14 +1,16 @@ // ==UserScript== -// @name MB: Copy Recording Relationships to Karaoke/Edited Recordings -// @description Copy recording-{artist, work, etc...} relationships to karaoke/edited recordings with one button! -// @version 0.2.0 -// @grant none -// @namespace https://rinsuki.net -// @author rinsuki -// @match https://*.musicbrainz.org/release/*/edit-relationships* -// @homepageURL https://github.com/rinsuki/userscripts -// @supportURL https://github.com/rinsuki/userscripts/issues -// @require https://cdn.jsdelivr.net/npm/@rinsuki/dom-chef@5.1.1/umd.js#sha256-EvGMVNob2jRhcwSo8gGGJp3mZJENNaQwrpYlNRQlUMc=,sha512-b/5dx8A+dc01RsqR2hF2Na/DFwwn64M4PQFka7dXhcAeXjPfpkgWS9YFs1LHPaEckkxAI7ivhVn6i+OrFivnYQ== +// @name MB: Copy Recording Relationships to Karaoke/Edited Recordings +// @description Copy recording-{artist, work, etc...} relationships to karaoke/edited recordings with one button! +// @version 0.2.0 +// @grant none +// @namespace https://rinsuki.net +// @author rinsuki +// @match https://*.musicbrainz.org/release/*/edit-relationships* +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues +// @require https://cdn.jsdelivr.net/npm/@rinsuki/dom-chef@5.1.1/umd.js#sha256-EvGMVNob2jRhcwSo8gGGJp3mZJENNaQwrpYlNRQlUMc=,sha512-b/5dx8A+dc01RsqR2hF2Na/DFwwn64M4PQFka7dXhcAeXjPfpkgWS9YFs1LHPaEckkxAI7ivhVn6i+OrFivnYQ== // ==/UserScript== (function () { diff --git a/mb-create-release-from-other-release.user.js b/mb-create-release-from-other-release.user.js index 4ab9a4b..1bd09ae 100644 --- a/mb-create-release-from-other-release.user.js +++ b/mb-create-release-from-other-release.user.js @@ -1,12 +1,14 @@ // ==UserScript== -// @name MB: Create Release from Other Release -// @match https://*.musicbrainz.org/release-group/* -// @grant none -// @namespace https://rinsuki.net -// @author rinsuki -// @description in the release group page -// @homepageURL https://github.com/rinsuki/userscripts -// @supportURL https://github.com/rinsuki/userscripts/issues +// @name MB: Create Release from Other Release +// @match https://*.musicbrainz.org/release-group/* +// @grant none +// @namespace https://rinsuki.net +// @author rinsuki +// @description in the release group page +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index bbc0665..6076687 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -1,12 +1,14 @@ // ==UserScript== -// @name MB: Match Tracklist Credits with Other Credits -// @namespace https://rinsuki.net -// @version 0.3.0 -// @grant none -// @match https://*.musicbrainz.org/release/*/edit -// @match https://*.musicbrainz.org/release/add -// @homepageURL https://github.com/rinsuki/userscripts -// @supportURL https://github.com/rinsuki/userscripts/issues +// @name MB: Match Tracklist Credits with Other Credits +// @namespace https://rinsuki.net +// @version 0.3.0 +// @grant none +// @match https://*.musicbrainz.org/release/*/edit +// @match https://*.musicbrainz.org/release/add +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { diff --git a/mb-seed-from-barcode-search.user.js b/mb-seed-from-barcode-search.user.js index 55fc68b..32cff5f 100644 --- a/mb-seed-from-barcode-search.user.js +++ b/mb-seed-from-barcode-search.user.js @@ -1,11 +1,13 @@ // ==UserScript== -// @name MB: Barcode Search ++ -// @namespace https://rinsuki.net -// @match https://*.musicbrainz.org/search* -// @description Add seed barcode to new release button, highlights barcode-matched releases -// @grant none -// @homepageURL https://github.com/rinsuki/userscripts -// @supportURL https://github.com/rinsuki/userscripts/issues +// @name MB: Barcode Search ++ +// @namespace https://rinsuki.net +// @match https://*.musicbrainz.org/search* +// @description Add seed barcode to new release button, highlights barcode-matched releases +// @grant none +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { diff --git a/ytm-dont-suggest-video.user.js b/ytm-dont-suggest-video.user.js index 8b92af4..8a84b95 100644 --- a/ytm-dont-suggest-video.user.js +++ b/ytm-dont-suggest-video.user.js @@ -1,14 +1,16 @@ // ==UserScript== -// @name YTM: Don't Suggest Video -// @namespace https://rinsuki.net -// @match https://music.youtube.com/* -// @grant none -// @author rinsuki -// @run-at document-start -// @version 0.1.0 -// @description Hide video-only tracks from "Up Next" tab on YTM. -// @homepageURL https://github.com/rinsuki/userscripts -// @supportURL https://github.com/rinsuki/userscripts/issues +// @name YTM: Don't Suggest Video +// @namespace https://rinsuki.net +// @match https://music.youtube.com/* +// @grant none +// @author rinsuki +// @run-at document-start +// @version 0.1.0 +// @description Hide video-only tracks from "Up Next" tab on YTM. +// @contributionURL https://rinsuki.fanbox.cc/ +// @contributionURL https://github.com/sponsors/rinsuki +// @homepageURL https://github.com/rinsuki/userscripts +// @supportURL https://github.com/rinsuki/userscripts/issues // ==/UserScript== (function () { From 4706df1c552f3408ff6d41e589dd9c27b254e977 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 20:51:37 +0000 Subject: [PATCH 118/131] update with 91d46da50e38ac19984b293b05ee2e901f3b24f9 --- mastodon-show-act-users.user.js | 1 - mb-artist-credit-splitter-by-ai.user.js | 1 - mb-artist-credit-splitter.user.js | 1 - mb-auto-set-media-type-by-url-rels.user.js | 1 - mb-clear-tracklist-artist.user.js | 1 - mb-copy-recording-rels-to-karaoke.user.js | 1 - mb-create-release-from-other-release.user.js | 1 - mb-match-tracklist-credits-with-other-credits.user.js | 1 - mb-seed-from-barcode-search.user.js | 1 - mb-seed-urls-to-release-recordings.user.js | 1 - niconico-genten-kaiki.user.js | 1 - niconico-search-sort-table.user.js | 1 - ytm-dont-suggest-video.user.js | 1 - 13 files changed, 13 deletions(-) diff --git a/mastodon-show-act-users.user.js b/mastodon-show-act-users.user.js index 5b90623..bc26f87 100644 --- a/mastodon-show-act-users.user.js +++ b/mastodon-show-act-users.user.js @@ -7,7 +7,6 @@ // @include /^https:\/\/[^/]*\/@[A-Za-z0-9_]+\/[0-9]+([?#].*)?$/ // @exclude-match https://*/@*/*/embed // @exclude-match https://*.tiktok.com/* -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/mb-artist-credit-splitter-by-ai.user.js b/mb-artist-credit-splitter-by-ai.user.js index 3c9cdde..35026dd 100644 --- a/mb-artist-credit-splitter-by-ai.user.js +++ b/mb-artist-credit-splitter-by-ai.user.js @@ -9,7 +9,6 @@ // @grant GM_setValue // @grant GM.registerMenuCommand // @grant GM.xmlHttpRequest -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 7d2f6d6..062af8d 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -6,7 +6,6 @@ // @author rinsuki // @match https://musicbrainz.org/* // @grant none -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/mb-auto-set-media-type-by-url-rels.user.js b/mb-auto-set-media-type-by-url-rels.user.js index 84d6a73..c111fb5 100644 --- a/mb-auto-set-media-type-by-url-rels.user.js +++ b/mb-auto-set-media-type-by-url-rels.user.js @@ -8,7 +8,6 @@ // @match https://*.musicbrainz.org/release/*/edit* // @exclude-match https://*.musicbrainz.org/release/*/edit-relationships* // @run-at document-idle -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/mb-clear-tracklist-artist.user.js b/mb-clear-tracklist-artist.user.js index 24d250a..f5345ec 100644 --- a/mb-clear-tracklist-artist.user.js +++ b/mb-clear-tracklist-artist.user.js @@ -5,7 +5,6 @@ // @match https://*.musicbrainz.org/release/*/edit // @grant none // @author rinsuki -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/mb-copy-recording-rels-to-karaoke.user.js b/mb-copy-recording-rels-to-karaoke.user.js index 7d789fd..4a421e8 100644 --- a/mb-copy-recording-rels-to-karaoke.user.js +++ b/mb-copy-recording-rels-to-karaoke.user.js @@ -6,7 +6,6 @@ // @namespace https://rinsuki.net // @author rinsuki // @match https://*.musicbrainz.org/release/*/edit-relationships* -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/mb-create-release-from-other-release.user.js b/mb-create-release-from-other-release.user.js index 1bd09ae..ee0fcbd 100644 --- a/mb-create-release-from-other-release.user.js +++ b/mb-create-release-from-other-release.user.js @@ -5,7 +5,6 @@ // @namespace https://rinsuki.net // @author rinsuki // @description in the release group page -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index 6076687..6ce4640 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -5,7 +5,6 @@ // @grant none // @match https://*.musicbrainz.org/release/*/edit // @match https://*.musicbrainz.org/release/add -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/mb-seed-from-barcode-search.user.js b/mb-seed-from-barcode-search.user.js index 32cff5f..3d01492 100644 --- a/mb-seed-from-barcode-search.user.js +++ b/mb-seed-from-barcode-search.user.js @@ -4,7 +4,6 @@ // @match https://*.musicbrainz.org/search* // @description Add seed barcode to new release button, highlights barcode-matched releases // @grant none -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index 4fa8989..df8471e 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -7,7 +7,6 @@ // @match https://musicbrainz.org/release/*/edit-relationships // @match https://*.musicbrainz.org/release/*/edit-relationships // @grant none -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/niconico-genten-kaiki.user.js b/niconico-genten-kaiki.user.js index 55c484f..cb5426d 100644 --- a/niconico-genten-kaiki.user.js +++ b/niconico-genten-kaiki.user.js @@ -6,7 +6,6 @@ // @author - // @match https://www.nicovideo.jp/watch/* // @grant none -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/niconico-search-sort-table.user.js b/niconico-search-sort-table.user.js index 3d29491..0a92070 100644 --- a/niconico-search-sort-table.user.js +++ b/niconico-search-sort-table.user.js @@ -6,7 +6,6 @@ // @author rinsuki // @match https://www.nicovideo.jp/tag/* // @match https://www.nicovideo.jp/search/* -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues diff --git a/ytm-dont-suggest-video.user.js b/ytm-dont-suggest-video.user.js index 8a84b95..f0a7a0f 100644 --- a/ytm-dont-suggest-video.user.js +++ b/ytm-dont-suggest-video.user.js @@ -7,7 +7,6 @@ // @run-at document-start // @version 0.1.0 // @description Hide video-only tracks from "Up Next" tab on YTM. -// @contributionURL https://rinsuki.fanbox.cc/ // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts // @supportURL https://github.com/rinsuki/userscripts/issues From 6693823dad3d78c0362e65ed69d43436d8b7f39b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 23:37:46 +0000 Subject: [PATCH 119/131] update with dc155cf3ec0c00f7454fbfc49d2bc87ba0c4bd5f --- mb-copy-recording-rels-to-karaoke.user.js | 223 ++++++++++++++++------ 1 file changed, 165 insertions(+), 58 deletions(-) diff --git a/mb-copy-recording-rels-to-karaoke.user.js b/mb-copy-recording-rels-to-karaoke.user.js index 4a421e8..5cddbc6 100644 --- a/mb-copy-recording-rels-to-karaoke.user.js +++ b/mb-copy-recording-rels-to-karaoke.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MB: Copy Recording Relationships to Karaoke/Edited Recordings // @description Copy recording-{artist, work, etc...} relationships to karaoke/edited recordings with one button! -// @version 0.2.0 +// @version 0.3.0 // @grant none // @namespace https://rinsuki.net // @author rinsuki @@ -23,6 +23,37 @@ //#endregion + async function resolveRecordingsRelationships(recordings, parent) { + // とりあえず愚直な実装 (TODO: appearsOnを使ってmediumで引くとかもできるはず) + const progressBar = document.createElement("progress"); + progressBar.max = recordings.length; + progressBar.value = 0; + parent.appendChild(progressBar); + recordings.sort((a, b) => { + if (a.relationships != null && b.relationships == null) + return -1; + if (a.relationships == null && b.relationships != null) + return 1; + return 0; + }); + for (const recording of recordings) { + if (recording.relationships != null) { + progressBar.value += 1; + continue; + } + const res = await fetch("/ws/js/entity/" + recording.gid + "?inc=rels"); + if (!res.ok) { + alert(`Failed to fetch recording ${recording.gid}: ${res.status} ${await res.text()}`); + } + const freshRecording = await res.json(); + Object.assign(recording, { + relationships: freshRecording.relationships, + }); + progressBar.value += 1; + } + progressBar.remove(); + } + /** @jsx h */ const KARAOKE_REL_LINK_TYPE_ID = 226; // gid: 39a08d0e-26e4-44fb-ae19-906f5fe9435d @@ -30,7 +61,7 @@ const WORK_REL_LINK_TYPE_ID = 278; // gid: a3005666-a872-32c3-ad06-98af558e99b0 const WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID = 1261; // gid: 3d984f6e-bbe2-4620-9425-5f32e945b60d const WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID = 579; // gid: d2b63be6-91ec-426a-987a-30b47f8aae2d - function doIt() { + async function doIt(button) { const MB = window.MB; if (MB == null) return alert("MB global not found"); @@ -40,71 +71,140 @@ if (editor == null || !isReleaseRelationshipEditor(editor)) return alert("Relationship editor not found"); const currentRecordings = new Map(); + const dstRecordingStates = new Map(); for (const medium of MB.tree.iterate(editor.state.mediums)) { for (const track of MB.tree.iterate(medium[1])) { currentRecordings.set(track.recording.gid, track.recording); + dstRecordingStates.set(track.recording.gid, track); } } - for (const recording of currentRecordings.values()) { - console.log(recording); - const subrecordingrels = [ - ...recording.relationships.filter(rel => KARAOKE_REL_LINK_TYPE_ID === rel.linkTypeID && rel.entity0_id === recording.id), - ...recording.relationships.filter(rel => EDITS_REL_LINK_TYPE_ID === rel.linkTypeID && rel.entity1_id === recording.id), + const recToRecRelationships = new Map(); + for (const dstRecording of Array.from(/* currentRecordings はループ内で変化するので Array.from で確定させる */ currentRecordings.values())) { + console.log("dstrec", dstRecording); + const srcRecordingRels = [ + ...dstRecording.relationships.filter(rel => KARAOKE_REL_LINK_TYPE_ID === rel.linkTypeID && rel.entity1_id === dstRecording.id), + ...dstRecording.relationships.filter(rel => EDITS_REL_LINK_TYPE_ID === rel.linkTypeID && rel.entity0_id === dstRecording.id), ]; - for (const subrecordingrel of subrecordingrels) { - const subrecording = subrecordingrel.target; - for (const rel of recording.relationships) { - if (rel.source_type === "recording" && rel.target_type === "recording") - continue; // probably don't want to copy - if (rel.source_type === "url" || rel.target_type === "url") - continue; // probably don't want to copy - const attrs = [...rel.attributes]; - if (subrecordingrel.linkTypeID === KARAOKE_REL_LINK_TYPE_ID) { - if (rel.linkTypeID === WORK_REL_LINK_TYPE_ID && attrs.find(x => x.typeID === WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID) == null) { - const karaokeAttr = MB.linkedEntities.link_attribute_type[WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID]; - attrs.push({ - type: karaokeAttr, - typeID: WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID, - typeName: karaokeAttr.name, - }); - } + for (const srcRecordingRel of srcRecordingRels) { + const srcRecording = srcRecordingRel.target; + if (srcRecording.entityType !== "recording") + continue; // ? + let obj = recToRecRelationships.get(`${dstRecording.gid}:${srcRecording.gid}`); + if (obj == null) { + obj = { + srcRecordingId: srcRecording.gid, + dstRecordingId: dstRecording.gid, + karaoke: false, + partial: false, + }; + if (!currentRecordings.has(srcRecording.gid)) + currentRecordings.set(srcRecording.gid, srcRecording); + recToRecRelationships.set(`${dstRecording.gid}:${srcRecording.gid}`, obj); + } + if (srcRecordingRel.linkTypeID === KARAOKE_REL_LINK_TYPE_ID) { + obj.karaoke = true; + } + else if (srcRecordingRel.linkTypeID === EDITS_REL_LINK_TYPE_ID) { + obj.partial = true; + } + } + } + await resolveRecordingsRelationships(Array.from(recToRecRelationships.values()) + .map(x => x.srcRecordingId) + .map(gid => currentRecordings.get(gid)), button); + for (const recToRecRel of recToRecRelationships.values()) { + const srcRecording = currentRecordings.get(recToRecRel.srcRecordingId); + const dstRecording = currentRecordings.get(recToRecRel.dstRecordingId); + for (const rel of srcRecording.relationships) { + if (rel.source_type === "recording" && rel.target_type === "recording") + continue; // probably don't want to copy + if (rel.source_type === "url" || rel.target_type === "url") + continue; // probably don't want to copy + const attrs = [...rel.attributes]; + let oldRelationshipState = null; + if (rel.linkTypeID === WORK_REL_LINK_TYPE_ID) { + const neededAttrIds = new Set(); + if (recToRecRel.karaoke) + neededAttrIds.add(WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID); + if (recToRecRel.partial) + neededAttrIds.add(WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID); + for (const neededAttrId of neededAttrIds) { + if (attrs.find(x => x.typeID === neededAttrId) != null) + continue; + const neededAttr = MB.linkedEntities.link_attribute_type[neededAttrId]; + attrs.push({ + type: neededAttr, + typeID: neededAttr.id, + typeName: neededAttr.name, + }); } - else if (subrecordingrel.linkTypeID === EDITS_REL_LINK_TYPE_ID) { - if (rel.linkTypeID === WORK_REL_LINK_TYPE_ID && attrs.find(x => x.typeID === WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID) == null) { - const partialAttr = MB.linkedEntities.link_attribute_type[WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID]; - attrs.push({ - type: partialAttr, - typeID: WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID, - typeName: partialAttr.name, - }); + const dstRecAllRels = dstRecordingStates.get(dstRecording.gid).targetTypeGroups; + const dstRecWorkRelsObj = dstRecAllRels && MB.tree + .iterate(dstRecAllRels) + .find(r => r[0] === "work")?.[1]; + const dstRecWorkRels = dstRecWorkRelsObj && MB.tree + .iterate(dstRecWorkRelsObj) + .flatMap(r => MB.tree.iterate(r.phraseGroups)) + .flatMap(r => MB.tree.iterate(r.relationships)) + .toArray(); + console.log("drwrels", dstRecWorkRels, rel.backward); + const dstRecWorkRel = dstRecWorkRels?.find(r => rel.backward + ? r.entity0.id === rel.target.id && r.entity1.id === dstRecording.id + : r.entity0.id === dstRecording.id && r.entity1.id === rel.target.id); + if (dstRecWorkRel != null) { + const oldRelAttrIds = new Set(MB.tree.iterate(dstRecWorkRel.attributes).map(x => x.typeID)); + if (attrs.every(x => oldRelAttrIds.has(x.typeID))) { + continue; + } + const oldRelAttrNames = MB.tree.iterate(dstRecWorkRel.attributes).map(x => x.typeName).toArray().join(", ") || "(none)"; + const newRelAttrNames = attrs.map(x => x.typeName).join(", ") || "(none)"; + if (confirm([ + `While copying`, + "", + `Relationship to work "${rel.target.name}" (${rel.target.id})`, + `to Destination recording "${dstRecording.name}" (${dstRecording.id})`, + `from Source recording "${srcRecording.name}" (${srcRecording.id})`, + "", + "an existing relationship with the same work but different attributes was found.", + "", + "Do you want to overwrite the existing relationship's attributes with the new ones?", + "", + `${oldRelAttrNames} → ${newRelAttrNames}`, + "", + "OK = Overwrite", + "Cancel = Add New Recording-Work Relationship", + ].join("\n"))) { + oldRelationshipState = dstRecWorkRel; } } - editor.dispatch({ - type: "update-relationship-state", - sourceEntity: rel.backward ? rel.target : subrecording, - oldRelationshipState: null, - newRelationshipState: { - _lineage: [], - _original: null, - editsPending: false, - _status: 1, - entity0: rel.backward ? rel.target : subrecording, - entity1: rel.backward ? subrecording : rel.target, - entity0_credit: rel.entity0_credit, - entity1_credit: rel.entity1_credit, - begin_date: rel.begin_date, - end_date: rel.end_date, - ended: rel.ended, - attributes: MB.tree.fromDistinctAscArray(attrs), - id: editor.getRelationshipStateId(null), - linkTypeID: rel.linkTypeID, - linkOrder: rel.linkOrder, - }, - batchSelectionCount: undefined, - creditsToChangeForSource: "", - creditsToChangeForTarget: "", - }); } + editor.dispatch({ + type: "update-relationship-state", + sourceEntity: rel.backward ? rel.target : dstRecording, + oldRelationshipState, + newRelationshipState: { + _lineage: [], + _original: null, + editsPending: false, + entity0: rel.backward ? rel.target : dstRecording, + entity1: rel.backward ? dstRecording : rel.target, + entity0_credit: rel.entity0_credit, + entity1_credit: rel.entity1_credit, + begin_date: rel.begin_date, + end_date: rel.end_date, + ended: rel.ended, + id: editor.getRelationshipStateId(null), + linkTypeID: rel.linkTypeID, + linkOrder: rel.linkOrder, + // ↑ここまでは新規relationshipを作成する時限定 + ...(oldRelationshipState ? oldRelationshipState : {}), + _status: oldRelationshipState ? (oldRelationshipState._status || 2) : 1, + attributes: MB.tree.fromDistinctAscArray(attrs), + }, + batchSelectionCount: undefined, + creditsToChangeForSource: "", + creditsToChangeForTarget: "", + }); } } let editNote = editor.state.editNoteField.value; @@ -114,7 +214,14 @@ editor.dispatch({ type: "update-edit-note", editNote }); } const elm = DOMChef.h("div", null, - DOMChef.h("button", { onClick: () => doIt() }, "Copy Recording Relationships to Karaoke Recordings")); + DOMChef.h("button", { onClick: (e) => { + const button = e.currentTarget; + button.disabled = true; + doIt(button).finally(() => { + button.querySelector(".only-for-loading")?.remove(); + button.disabled = false; + }); + } }, "Copy Recording Relationships to Karaoke Recordings")); document.querySelector("#content > div.tabs")?.insertAdjacentElement("afterend", elm); })(); From ae40f522ffce2d382491f4ad86b4a496581d4e6b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 01:21:49 +0000 Subject: [PATCH 120/131] update with 516afd63c425163019e1ba932d4b9f83e2ca7f6f --- mb-artist-credit-splitter-by-ai.user.js | 2 +- mb-artist-credit-splitter.user.js | 2 +- ...racklist-credits-with-other-credits.user.js | 18 ++++++++---------- mb-seed-urls-to-release-recordings.user.js | 1 - 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/mb-artist-credit-splitter-by-ai.user.js b/mb-artist-credit-splitter-by-ai.user.js index 35026dd..6b98b5a 100644 --- a/mb-artist-credit-splitter-by-ai.user.js +++ b/mb-artist-credit-splitter-by-ai.user.js @@ -4,7 +4,7 @@ // @version 1.1.0 // @description OpenRouter でいい感じに MusicBrainz のアーティストクレジットを分割します (失敗することもあります) // @author rinsuki -// @match https://musicbrainz.org/* +// @match https://*.musicbrainz.org/* // @grant GM_getValue // @grant GM_setValue // @grant GM.registerMenuCommand diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 062af8d..0520a17 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -4,7 +4,7 @@ // @version 1.0.2 // @description いい感じに MusicBrainz のアーティストクレジットを分割します (失敗することもあります) // @author rinsuki -// @match https://musicbrainz.org/* +// @match https://*.musicbrainz.org/* // @grant none // @contributionURL https://github.com/sponsors/rinsuki // @homepageURL https://github.com/rinsuki/userscripts diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index 6ce4640..be87958 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -115,17 +115,15 @@ } else { if (!isArtistExist(credit.artist)) { - console.warn("?"); - creditMap.set(credit.name, null); + // 同じ名前で、名寄せ済みのクレジットと名寄せされていないクレジットがある → 名寄せされていないクレジットは無視 + continue; } - else { - const current = creditMap.get(credit.name); - if (current == null) - continue; - console.log(current); - if (current.id != null && current.id !== credit.artist.id) { - creditMap.set(credit.name, null); - } + const current = creditMap.get(credit.name); + if (current == null) + continue; + console.log(current); + if (current.id != null && current.id !== credit.artist.id) { + creditMap.set(credit.name, null); } } } diff --git a/mb-seed-urls-to-release-recordings.user.js b/mb-seed-urls-to-release-recordings.user.js index df8471e..21108ef 100644 --- a/mb-seed-urls-to-release-recordings.user.js +++ b/mb-seed-urls-to-release-recordings.user.js @@ -4,7 +4,6 @@ // @version 0.2.4 // @description Import recording-url relationship to release's recordings. // @author rinsuki -// @match https://musicbrainz.org/release/*/edit-relationships // @match https://*.musicbrainz.org/release/*/edit-relationships // @grant none // @contributionURL https://github.com/sponsors/rinsuki From 89a021b84b5b12a4874a83748efcb1fd5da029ee Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 01:28:31 +0000 Subject: [PATCH 121/131] update with 2b84e62f7817e42850d348dd2d10b5734aef63ad --- mb-artist-credit-splitter.user.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/mb-artist-credit-splitter.user.js b/mb-artist-credit-splitter.user.js index 0520a17..7dde702 100644 --- a/mb-artist-credit-splitter.user.js +++ b/mb-artist-credit-splitter.user.js @@ -70,8 +70,7 @@ return splittedCredits; } - (async () => { - const bubble = await waitDOMByObserve(document.body, () => document.querySelector("#artist-credit-bubble"), { subtree: false }); + async function whenBubbleHappens(bubble) { const buttons = await waitDOMByObserve(bubble, () => bubble.querySelector(".buttons"), { subtree: false }); const button = document.createElement("button"); button.type = "button"; @@ -113,6 +112,20 @@ alert("finish!"); }); buttons.appendChild(button); - })(); + } + const observer = new MutationObserver((mutations) => { + for (const mutation of mutations) { + for (const node of mutation.addedNodes) { + if (!(node instanceof HTMLElement)) + continue; + if (node.dataset.floatingUiPortal == null) + continue; + const bubble = node.querySelector("#artist-credit-bubble"); + if (bubble != null && bubble instanceof HTMLElement) + whenBubbleHappens(bubble); + } + } + }); + observer.observe(document.body, { childList: true, subtree: false }); })(); From 08763ee7c8b4586a7e7b43533f4de22f6782625d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 02:22:07 +0000 Subject: [PATCH 122/131] update with 379c5cb9e7ed3f6c5685eb507c1a0e2df6f01fd2 From d99c3ac398914aed6542a3c36284f55d21a1b544 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 02:24:37 +0000 Subject: [PATCH 123/131] update with c4009e93b0c8612c307bba55d2934194dea7be20 From d931283b6c304d1205cd126278e1e0cc8685754f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 04:21:51 +0000 Subject: [PATCH 124/131] update with dab12b6bc6888234f544055296c3bc995ae50afb --- ...acklist-credits-with-other-credits.user.js | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/mb-match-tracklist-credits-with-other-credits.user.js b/mb-match-tracklist-credits-with-other-credits.user.js index be87958..9f76ebf 100644 --- a/mb-match-tracklist-credits-with-other-credits.user.js +++ b/mb-match-tracklist-credits-with-other-credits.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MB: Match Tracklist Credits with Other Credits // @namespace https://rinsuki.net -// @version 0.3.0 +// @version 0.3.1 // @grant none // @match https://*.musicbrainz.org/release/*/edit // @match https://*.musicbrainz.org/release/add @@ -108,23 +108,15 @@ } const creditMap = new Map(); for (const credit of currentCredits) { - if (!creditMap.has(credit.name)) { - if (isArtistExist(credit.artist)) { - creditMap.set(credit.name, credit.artist); - } + if (!isArtistExist(credit.artist)) + continue; + const current = creditMap.get(credit.name); + if (current == null || !isArtistExist(current)) { + creditMap.set(credit.name, credit.artist); } - else { - if (!isArtistExist(credit.artist)) { - // 同じ名前で、名寄せ済みのクレジットと名寄せされていないクレジットがある → 名寄せされていないクレジットは無視 - continue; - } - const current = creditMap.get(credit.name); - if (current == null) - continue; - console.log(current); - if (current.id != null && current.id !== credit.artist.id) { - creditMap.set(credit.name, null); - } + else if (current.id !== credit.artist.id) { + // conflict + creditMap.set(credit.name, null); } } console.log(creditMap); From e4719d1936c6fe64ae1567d201c67d34b708dc1d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 04:52:50 +0000 Subject: [PATCH 125/131] update with bced2e480eb8be20293f6dd5e33ca8be9c44519e --- mb-copy-recording-rels-to-karaoke.user.js | 69 +++++++++++++++-------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/mb-copy-recording-rels-to-karaoke.user.js b/mb-copy-recording-rels-to-karaoke.user.js index 5cddbc6..a46c9f7 100644 --- a/mb-copy-recording-rels-to-karaoke.user.js +++ b/mb-copy-recording-rels-to-karaoke.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name MB: Copy Recording Relationships to Karaoke/Edited Recordings // @description Copy recording-{artist, work, etc...} relationships to karaoke/edited recordings with one button! -// @version 0.3.0 +// @version 0.4.0 // @grant none // @namespace https://rinsuki.net // @author rinsuki @@ -61,7 +61,7 @@ const WORK_REL_LINK_TYPE_ID = 278; // gid: a3005666-a872-32c3-ad06-98af558e99b0 const WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID = 1261; // gid: 3d984f6e-bbe2-4620-9425-5f32e945b60d const WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID = 579; // gid: d2b63be6-91ec-426a-987a-30b47f8aae2d - async function doIt(button) { + async function doIt(button, doReverse) { const MB = window.MB; if (MB == null) return alert("MB global not found"); @@ -82,8 +82,8 @@ for (const dstRecording of Array.from(/* currentRecordings はループ内で変化するので Array.from で確定させる */ currentRecordings.values())) { console.log("dstrec", dstRecording); const srcRecordingRels = [ - ...dstRecording.relationships.filter(rel => KARAOKE_REL_LINK_TYPE_ID === rel.linkTypeID && rel.entity1_id === dstRecording.id), - ...dstRecording.relationships.filter(rel => EDITS_REL_LINK_TYPE_ID === rel.linkTypeID && rel.entity0_id === dstRecording.id), + ...dstRecording.relationships.filter(rel => KARAOKE_REL_LINK_TYPE_ID === rel.linkTypeID && (doReverse ? rel.entity0_id : rel.entity1_id) === dstRecording.id), + ...dstRecording.relationships.filter(rel => EDITS_REL_LINK_TYPE_ID === rel.linkTypeID && (doReverse ? rel.entity1_id : rel.entity0_id) === dstRecording.id), ]; for (const srcRecordingRel of srcRecordingRels) { const srcRecording = srcRecordingRel.target; @@ -112,6 +112,13 @@ await resolveRecordingsRelationships(Array.from(recToRecRelationships.values()) .map(x => x.srcRecordingId) .map(gid => currentRecordings.get(gid)), button); + { + const srcRecordingIds = new Set(Array.from(recToRecRelationships.values()).map(x => x.srcRecordingId)); + const srcRecordingsWithoutRels = Array.from(srcRecordingIds) + .map(gid => currentRecordings.get(gid)) + .filter(rec => rec.relationships == null); + await resolveRecordingsRelationships(srcRecordingsWithoutRels, button); + } for (const recToRecRel of recToRecRelationships.values()) { const srcRecording = currentRecordings.get(recToRecRel.srcRecordingId); const dstRecording = currentRecordings.get(recToRecRel.dstRecordingId); @@ -120,25 +127,35 @@ continue; // probably don't want to copy if (rel.source_type === "url" || rel.target_type === "url") continue; // probably don't want to copy - const attrs = [...rel.attributes]; + let attrs = [...rel.attributes]; let oldRelationshipState = null; if (rel.linkTypeID === WORK_REL_LINK_TYPE_ID) { - const neededAttrIds = new Set(); - if (recToRecRel.karaoke) - neededAttrIds.add(WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID); - if (recToRecRel.partial) - neededAttrIds.add(WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID); - for (const neededAttrId of neededAttrIds) { - if (attrs.find(x => x.typeID === neededAttrId) != null) - continue; - const neededAttr = MB.linkedEntities.link_attribute_type[neededAttrId]; - attrs.push({ - type: neededAttr, - typeID: neededAttr.id, - typeName: neededAttr.name, - }); + if (doReverse) { + const removeAttrIds = new Set(); + if (recToRecRel.karaoke) + removeAttrIds.add(WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID); + if (recToRecRel.partial) + removeAttrIds.add(WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID); + attrs = attrs.filter(x => !removeAttrIds.has(x.typeID)); } - const dstRecAllRels = dstRecordingStates.get(dstRecording.gid).targetTypeGroups; + else { + const neededAttrIds = new Set(); + if (recToRecRel.karaoke) + neededAttrIds.add(WORK_REL_KARAOKE_LINK_ATTR_TYPE_ID); + if (recToRecRel.partial) + neededAttrIds.add(WORK_REL_PARTIAL_LINK_ATTR_TYPE_ID); + for (const neededAttrId of neededAttrIds) { + if (attrs.find(x => x.typeID === neededAttrId) != null) + continue; + const neededAttr = MB.linkedEntities.link_attribute_type[neededAttrId]; + attrs.push({ + type: neededAttr, + typeID: neededAttr.id, + typeName: neededAttr.name, + }); + } + } + const dstRecAllRels = dstRecordingStates.get(dstRecording.gid)?.targetTypeGroups; const dstRecWorkRelsObj = dstRecAllRels && MB.tree .iterate(dstRecAllRels) .find(r => r[0] === "work")?.[1]; @@ -217,11 +234,19 @@ DOMChef.h("button", { onClick: (e) => { const button = e.currentTarget; button.disabled = true; - doIt(button).finally(() => { + doIt(button, false).finally(() => { + button.querySelector(".only-for-loading")?.remove(); + button.disabled = false; + }); + } }, "Copy Recording Relationships to Karaoke/Edited Recordings"), + DOMChef.h("button", { onClick: e => { + const button = e.currentTarget; + button.disabled = true; + doIt(button, true).finally(() => { button.querySelector(".only-for-loading")?.remove(); button.disabled = false; }); - } }, "Copy Recording Relationships to Karaoke Recordings")); + }, style: { marginLeft: "1em" } }, "... or FROM Karaoke/Edited Recordings")); document.querySelector("#content > div.tabs")?.insertAdjacentElement("afterend", elm); })(); From e040c395857a6e74bb8b5cc15fd882cf4ae6545a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 3 May 2026 17:39:44 +0000 Subject: [PATCH 126/131] update with e67155299dce8d1ef5ad373d1aaeb5f3b02c687d --- mb-auto-set-media-type-by-url-rels.user.js | 180 +++++++++++---- ...-recording-rels-from-other-release.user.js | 3 +- mb-copy-recording-rels-to-karaoke.user.js | 6 +- mb-search-release-on-websites.user.js | 218 ++++++++++++++++++ mb-seed-urls-to-release-recordings.user.js | 2 +- 5 files changed, 356 insertions(+), 53 deletions(-) create mode 100644 mb-search-release-on-websites.user.js diff --git a/mb-auto-set-media-type-by-url-rels.user.js b/mb-auto-set-media-type-by-url-rels.user.js index c111fb5..0369ce4 100644 --- a/mb-auto-set-media-type-by-url-rels.user.js +++ b/mb-auto-set-media-type-by-url-rels.user.js @@ -14,61 +14,145 @@ // ==/UserScript== (function () { - 'use strict'; + 'use strict'; - const LINK_TYPE_GID_RELEASE_FREE_STREAMING = "08445ccf-7b99-4438-9f9a-fb9ac18099ee"; - const LINK_TYPE_GID_RELEASE_PAID_STREAMING = "320adf26-96fa-4183-9045-1f5f32f833cb"; - const LINK_TYPE_GID_RELEASE_PAID_DOWNLOAD = "98e08c20-8402-4163-8970-53504bb6a1e4"; + //#region node_modules/.pnpm/weight-balanced-tree@0.16.0/node_modules/weight-balanced-tree/src/utility/getSliceArgs.js + // @flow strict - const MEDIUM_FORMAT_DIGITAL_RELEASE = "12"; + /*:: + import type {ImmutableTree} from '../types.js'; + */ - function isMBWithReleaseEditor(mb) { - return mb.releaseEditor !== undefined; + function getSliceArgs( + tree/*: ImmutableTree