|
1 | 1 | // ==UserScript==
|
2 | 2 | // @name Taiwan ISRC to MusicBrainz
|
3 |
| -// @version 2.0.0 |
| 3 | +// @version 3.0.0 |
4 | 4 | // @namespace http://www.agj.cl/
|
5 |
| -// @description Adds a link to any isrc.ncl.edu.tw record entry page that opens the Add Release form in MusicBrainz, prefilling it with that record's information. |
| 5 | +// @description Adds an “Add to MusicBrainz” button to any Taiwan ISRC website record entry page, which prefills the record submission form on MusicBrainz. |
| 6 | +// @description.zh 在台灣ISRC網站裡的專輯頁上加「Add to MusicBrainz」(加到 MusicBrainz 音樂數據庫)的按鈕。 |
6 | 7 | // @license Unlicense
|
7 | 8 | // @include http*://isrc.ncl.edu.tw/C100/*
|
8 | 9 | // @grant none
|
|
47 | 48 | new Promise((resolve) => {
|
48 | 49 | setTimeout(resolve, milliseconds);
|
49 | 50 | });
|
| 51 | + const checkValue = (value) => (value === undefined ? "" : value); |
| 52 | + const input = (name, value) => |
| 53 | + dom("input", { name: name, value: checkValue(value), type: "text" }); |
| 54 | + const observe = (element, callback) => { |
| 55 | + const observer = new MutationObserver(callback); |
| 56 | + observer.observe(element, { childList: true, subtree: true }); |
| 57 | + }; |
50 | 58 |
|
51 | 59 | onFullLoad(async () => {
|
52 |
| - // Get values. |
53 |
| - |
54 |
| - await waitFor(100); |
55 |
| - |
56 |
| - const values = Array.from(sel(".table").querySelectorAll("tr")).reduce( |
57 |
| - (r, el) => { |
58 |
| - const label = selIn(el, "th").textContent.trim(); |
59 |
| - const value = selIn(el, "td").textContent.trim(); |
60 |
| - if (/表演者/.test(label)) r.artist = value; |
61 |
| - else if (/樂團名稱/.test(label)) r.artist = value; |
62 |
| - else if (/專輯名稱/.test(label)) r.title = value; |
63 |
| - else if (/發行公司/.test(label)) r.label = value; |
64 |
| - else if (/產品編碼/.test(label)) r.cat = value; |
65 |
| - else if (/EAN\/UPC碼/.test(label)) r.barcode = value; |
66 |
| - else if (/發行日期/.test(label)) r.date = value.split("."); |
67 |
| - return r; |
68 |
| - }, |
69 |
| - {} |
70 |
| - ); |
71 |
| - |
72 |
| - values.tracks = Array.from(selAll("#songsTable a")).map((el) => |
73 |
| - el.textContent.trim().replace(/^\[\S+\] \d+[.](.*)$/, "$1") |
74 |
| - ); |
75 |
| - |
76 |
| - // Add submit button. |
77 |
| - |
78 |
| - const checkValue = (value) => (value === undefined ? "" : value); |
79 |
| - const input = (name, value) => |
80 |
| - dom("input", { name: name, value: checkValue(value), type: "text" }); |
| 60 | + // Add elements to DOM. |
81 | 61 |
|
82 | 62 | const button = dom(
|
83 | 63 | "button",
|
|
88 | 68 | "Add to MusicBrainz"
|
89 | 69 | );
|
90 | 70 |
|
91 |
| - const form = dom( |
92 |
| - "form", |
93 |
| - { |
94 |
| - name: "musicbrainz-submit", |
95 |
| - action: "https://musicbrainz.org/release/add", |
96 |
| - method: "post", |
97 |
| - "accept-charset": "utf-8", |
98 |
| - style: "display: none", |
99 |
| - }, |
100 |
| - input("name", values.title), |
101 |
| - input("artist_credit.names.0.name", values.artist), |
102 |
| - input("labels.0.name", values.label), |
103 |
| - input("labels.0.catalog_number", values.cat), |
104 |
| - input("events.0.date.year", values.date[0]), |
105 |
| - input("events.0.date.month", values.date[1]), |
106 |
| - input("events.0.country", "TW"), |
107 |
| - input("barcode", values.barcode), |
108 |
| - input("urls.0.url", window.location.href), |
109 |
| - input("urls.0.link_type", "82"), |
110 |
| - input("language", "cmn"), |
111 |
| - input("script", "Hant"), |
112 |
| - input("status", "official"), |
113 |
| - input("mediums.0.format", "cd"), |
114 |
| - input("edit_note", "From Taiwan ISRC: " + window.location.href) |
115 |
| - ); |
116 |
| - |
117 |
| - const trackCount = counter(); |
118 |
| - const trackInputs = flatten( |
119 |
| - values.tracks.map((title) => { |
120 |
| - const i = trackCount(); |
121 |
| - return [ |
122 |
| - input(`mediums.0.track.${i}.name`, title), |
123 |
| - input(`mediums.0.track.${i}.number`, i + 1), |
124 |
| - ]; |
125 |
| - }) |
126 |
| - ); |
127 |
| - form.append(...trackInputs); |
| 71 | + const form = dom("form", { |
| 72 | + name: "musicbrainz-submit", |
| 73 | + action: "https://musicbrainz.org/release/add", |
| 74 | + method: "post", |
| 75 | + "accept-charset": "utf-8", |
| 76 | + style: "display: none", |
| 77 | + }); |
128 | 78 |
|
129 | 79 | const container = sel(".card-header");
|
130 | 80 | container.insertAdjacentElement("afterbegin", form);
|
|
142 | 92 | `
|
143 | 93 | #musicbrainz-button {
|
144 | 94 | float: right
|
145 |
| - } |
146 |
| -` |
| 95 | + }` |
147 | 96 | )
|
148 | 97 | );
|
| 98 | + |
| 99 | + const table = sel(".table"); |
| 100 | + const songsTable = sel("#songsTable"); |
| 101 | + |
| 102 | + const updateForm = () => { |
| 103 | + // Get values. |
| 104 | + |
| 105 | + const values = Array.from(table.querySelectorAll("tr")).reduce( |
| 106 | + (r, el) => { |
| 107 | + const label = selIn(el, "th").textContent.trim(); |
| 108 | + const value = selIn(el, "td").textContent.trim(); |
| 109 | + console.log({ el, label, value }); |
| 110 | + if (/表演者/.test(label)) r.artist = value; |
| 111 | + else if (/樂團名稱/.test(label)) r.artist = value; |
| 112 | + else if (/專輯名稱/.test(label)) r.title = value; |
| 113 | + else if (/發行公司/.test(label)) r.label = value; |
| 114 | + else if (/產品編碼/.test(label)) r.cat = value; |
| 115 | + else if (/EAN\/UPC碼/.test(label)) r.barcode = value; |
| 116 | + else if (/發行日期/.test(label)) r.date = value.split("."); |
| 117 | + return r; |
| 118 | + }, |
| 119 | + {} |
| 120 | + ); |
| 121 | + |
| 122 | + values.tracks = Array.from(songsTable.querySelectorAll("tr")).map( |
| 123 | + (el) => { |
| 124 | + const [hours, minutes, seconds] = el |
| 125 | + .querySelector("td.text-right") |
| 126 | + .textContent.trim() |
| 127 | + .split(".") |
| 128 | + .map(Number); |
| 129 | + const paddedSeconds = seconds.toString().padStart(2, "0"); |
| 130 | + return { |
| 131 | + title: el |
| 132 | + .querySelector("a") |
| 133 | + .textContent.trim() |
| 134 | + .replace(/^\[\S+\] \d+[.](.*)$/, "$1"), |
| 135 | + length: `${hours * 60 + minutes}:${paddedSeconds}`, |
| 136 | + }; |
| 137 | + } |
| 138 | + ); |
| 139 | + |
| 140 | + console.log({ values }); |
| 141 | + |
| 142 | + // Create form inputs. |
| 143 | + |
| 144 | + const baseInputs = [ |
| 145 | + input("name", values.title), |
| 146 | + input("artist_credit.names.0.name", values.artist), |
| 147 | + input("labels.0.name", values.label), |
| 148 | + input("labels.0.catalog_number", values.cat), |
| 149 | + input("events.0.date.year", values.date[0]), |
| 150 | + input("events.0.date.month", values.date[1]), |
| 151 | + input("events.0.country", "TW"), |
| 152 | + input("barcode", values.barcode), |
| 153 | + input("urls.0.url", window.location.href), |
| 154 | + input("urls.0.link_type", "82"), |
| 155 | + input("language", "cmn"), |
| 156 | + input("script", "Hant"), |
| 157 | + input("status", "official"), |
| 158 | + input("mediums.0.format", "cd"), |
| 159 | + input("edit_note", "From Taiwan ISRC: " + window.location.href), |
| 160 | + ]; |
| 161 | + |
| 162 | + const trackCount = counter(); |
| 163 | + const trackInputs = flatten( |
| 164 | + values.tracks.map(({ title, length }) => { |
| 165 | + const i = trackCount(); |
| 166 | + return [ |
| 167 | + input(`mediums.0.track.${i}.name`, title), |
| 168 | + input(`mediums.0.track.${i}.length`, length), |
| 169 | + input(`mediums.0.track.${i}.number`, i + 1), |
| 170 | + ]; |
| 171 | + }) |
| 172 | + ); |
| 173 | + |
| 174 | + // Replace form contents with new data. |
| 175 | + |
| 176 | + form.textContent = ""; |
| 177 | + form.append(...baseInputs, ...trackInputs); |
| 178 | + }; |
| 179 | + |
| 180 | + updateForm(); |
| 181 | + |
| 182 | + // Listen to changes in data. |
| 183 | + |
| 184 | + observe(table, updateForm); |
| 185 | + observe(songsTable, updateForm); |
149 | 186 | });
|
150 | 187 | })();
|
0 commit comments