-
Notifications
You must be signed in to change notification settings - Fork 92
/
mb_ui_enhancements.user.js
290 lines (268 loc) · 13.3 KB
/
mb_ui_enhancements.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
// ==UserScript==
// @name Musicbrainz UI enhancements
// @description Various UI enhancements for Musicbrainz
// @version 2023.4.23.1
// @downloadURL https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/mb_ui_enhancements.user.js
// @updateURL https://raw.githubusercontent.com/murdos/musicbrainz-userscripts/master/mb_ui_enhancements.user.js
// @icon http://wiki.musicbrainz.org/-/images/3/3d/Musicbrainz_logo.png
// @namespace http://userscripts.org/users/22504
// @include http*://*musicbrainz.org/*
// @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.js
// @require https://raw.github.com/murdos/mbediting.js/master/mbediting.js
// ==/UserScript==
// prevent JQuery conflicts, see http://wiki.greasespot.net/@grant
this.$ = this.jQuery = jQuery.noConflict(true);
$(document).ready(function () {
// Follow the instructions found at https://www.last.fm/api/authentication
// then paste your API Key between the single quotes in the variable below.
LASTFM_APIKEY = '';
// Highlight table rows
$('head').append(
'<style>table.tbl > tbody > tr:hover { background-color: #ffeea8 } table.tbl > tbody > tr:hover > td { background-color: rgba(0, 0, 0, 0) }</style>'
);
let re;
// Top tracks from Last.fm
re = new RegExp('musicbrainz.org/artist/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$', 'i');
if (LASTFM_APIKEY && window.location.href.match(re)) {
$('h2.discography').before('<h2 class="toptracks">Top Last.fm recordings</h2><ul class="toptracks" />');
var mbid = window.location.href.match(re)[1];
let toptracks = $.getJSON(
`https://ws.audioscrobbler.com/2.0/?method=artist.gettoptracks&mbid=${mbid}&api_key=${LASTFM_APIKEY}&format=json`,
function (data) {
$.each(data.toptracks.track, function (index, track) {
if (index >= 5) return true;
let url = track.mbid ? `/recording/${track.mbid}` : track.url;
$('ul.toptracks').append(`<li><a href="${url}">${track.name}</a></li>`);
});
}
);
}
// Fix for http://tickets.musicbrainz.org/browse/MBS-750
re = new RegExp('musicbrainz.org/release/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})', 'i');
if (window.location.href.match(re)) {
if ($('table.medium thead').length == 1) {
let text = $.trim($('table.medium thead').text());
if (text.match(/ 1$/)) {
$('table.medium thead a').text(text.replace(/ 1$/, ''));
}
}
}
// Better fix for http://tickets.musicbrainz.org/browse/MBS-1943
re = new RegExp(
'musicbrainz.org/(artist|release-group|release|recording|work|label)/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})',
'i'
);
if (window.location.href.match(re)) {
$("#sidebar h2:contains('Rating')").before($("#sidebar h2:contains('External links')"));
let pageHasRGLinks = $("#sidebar h2:contains('Release group external links')").length > 0;
$("#sidebar h2:contains('Rating')").before(
$("#sidebar h2:contains('External links')")
.nextAll('ul.external_links')
.filter(function () {
return !pageHasRGLinks || $(this).nextAll("h2:contains('Release group external links')").length > 0;
})
);
$("#sidebar h2:contains('Rating')").before($("#sidebar h2:contains('Release group external links')"));
$("#sidebar h2:contains('Rating')").before($("#sidebar h2:contains('Release group external links')").nextAll('ul.external_links'));
}
// Remove the affiliate section
re = new RegExp(
'musicbrainz.org/(artist|release-group|release|recording|work|label)/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})',
'i'
);
if (window.location.href.match(re)) {
$('#sidebar-affiliates').remove();
}
// Batch merge -> open in a new tab/windows
re = new RegExp(
'musicbrainz.org/artist/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/(recordings|releases|works)',
'i'
);
if (window.location.href.match(re)) {
$('form')
.filter(function () {
return $(this).prop('action').match('merge_queue');
})
.attr('target', '_blank');
}
// Modify link to edits: remove " - <Edit type>" from the link "Edit XXXX - <Edit type>"
re = new RegExp('musicbrainz.org/.*/(open_)?edits', 'i');
if (window.location.href.match(re)) {
$('div.edit-description ~ h2').each(function () {
let parts = $(this).find('a').text().split(' - ');
$(this).find('a').text(parts[0]);
$(this).append(` - ${parts[1]}`);
});
}
// Add direct link to cover art tab for Add cover art edits
re = new RegExp('musicbrainz.org/(.*/(open_)?edits|edit/d+)', 'i');
if (window.location.href.match(re)) {
$("div.edit-description ~ h2:contains('cover art')").each(function () {
$editdetails = $(this).parents('.edit-header').siblings('.edit-details');
mbid = $editdetails
.find("a[href*='musicbrainz.org/release/']")
.attr('href')
.match(/\/release\/(.{36})/)[1];
$editdetails
.find('tbody td.edit-cover-art')
.after(`<tr><th span='2'><a href='/release/${mbid}/cover-art'>See all artworks for this release</a></th></tr>`);
});
}
// Embed Youtube videos
re = new RegExp('musicbrainz.org/recording/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$', 'i');
if (window.location.href.match(re)) {
let $youtube_link = $('#sidebar li.youtube-favicon a');
if ($youtube_link.length > 0) {
let youtube_id = $youtube_link.prop('href').match(/http:\/\/www\.youtube\.com\/watch\?v=(.*)/)[1];
$('table.details').width('60%');
$("h2:contains('Relationships')").after(
`<iframe width="360" height="275" frameborder="0" style="float: right;" src="https://www.youtube.com/embed/${youtube_id}?rel=0" allowfullscreen=""></iframe>`
);
}
}
// When attaching CDTOC, autoselect artist when there's only one result
re = new RegExp('musicbrainz.org/cdtoc/attach.*&filter-artist.query=.*', 'i');
if (window.location.href.match(re)) {
$artists = $('ul.radio-list li');
if ($artists.length == 1) {
$artists.find('input:radio').attr('checked', true);
}
}
// Highlight Year in ISRCs codes
re = new RegExp('musicbrainz.org/artist/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/recordings', 'i');
if (window.location.href.match(re)) {
let isrcColNo; // = ($("#content table.tbl thead th:eq(2)").text() == "Artist") ? 3 : 2;
$('#content table.tbl thead th').each(function (index, th) {
if ($(th).text() == 'ISRCs') {
isrcColNo = index;
return false;
}
});
$('#content table.tbl tbody tr').each(function () {
$(this)
.find(`td:eq(${isrcColNo})`)
.find('li')
.each(function () {
let newHTML = '';
var isrc = $(this).text();
newHTML += `<a href='/isrc/${isrc}'><bdi><code>`;
newHTML += `${isrc.substring(0, 5)}<b>${isrc.substring(5, 7)}</b>${isrc.substring(7)}`;
newHTML += '</code></bdi></a>';
$(this).html(newHTML);
});
});
}
// Display ISRCs and recording comment on release tracklisting
re = new RegExp('musicbrainz.org/release/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})#?$', 'i');
if (window.location.href.match(re)) {
let ISRC_COLUMN_POSITION = 2;
var mbid = window.location.href.match(re)[1];
// Get tracks data from webservice
let wsurl = `/ws/2/release/${mbid}?inc=isrcs+recordings`;
$.getJSON(wsurl, function (data) {
// Store tracks data from webservice in a hash table
let tracks = {};
$.each(data.media, function (index, medium) {
$.each(medium.tracks, function (i, track) {
tracks[track.id] = track;
});
});
// Different behavior depending on the number of mediums
if ($('table.medium').length <= 10) {
// All mediums are already displayed: handle them now
$('table.medium').each(function () {
handleMedium($(this), tracks);
});
} else {
// Each medium will be handled when it's loaded
let HANDLED_ATTRIBUTE = 'ui_enh_isrcs_handled';
$('table.medium').attr(HANDLED_ATTRIBUTE, 'no');
$('table.medium').bind('DOMNodeInserted', function (event) {
$target = $(event.target);
if (
$target.prop('nodeName') == 'TBODY' &&
$target.parent().attr(HANDLED_ATTRIBUTE) == 'no' &&
$target.find('tr.subh').length > 0
) {
$medium = $target.parent();
$medium.attr(HANDLED_ATTRIBUTE, 'pending');
handleMedium($medium, tracks);
$medium.attr(HANDLED_ATTRIBUTE, 'done');
}
});
}
});
function handleMedium($medium, ws_tracks) {
// Extend colspan for medium table header
$medium.find('thead tr').each(function () {
$(this)
.find('th:eq(0)')
.attr('colspan', $(this).find('th:eq(0)').attr('colspan') * 1 + 1);
});
// Table sub-header
$medium
.find(`tbody tr.subh th:nth-last-child(${ISRC_COLUMN_POSITION})`)
.before("<th style='width: 150px;' class='isrc c'> ISRC </th>");
// Handle each track
$medium.find('tbody tr[id]').each(function (index, medium_track) {
track_mbid = $(medium_track).attr('id');
let isrcsLinks = '';
if (Object.prototype.hasOwnProperty.call(ws_tracks, track_mbid)) {
track = ws_tracks[track_mbid];
let recording = track.recording;
// Recording comment
if (recording.disambiguation != '') {
let td_title_index = $(`#${track_mbid}`).find('td:eq(1)').hasClass('video') ? 2 : 1;
$(`#${track_mbid}`)
.find(`td:eq(${td_title_index}) a:eq(0)`)
.after(` <span class="comment">(${recording.disambiguation})</span>`);
}
// ISRCS
if (recording.isrcs.length != 0) {
let links = jQuery.map(recording.isrcs, function (isrc, i) {
return `<a href='/isrc/${isrc}'>${isrc}</a>`;
});
isrcsLinks = links.join(', ');
}
}
$(`#${track_mbid}`)
.find(`td:nth-last-child(${ISRC_COLUMN_POSITION})`)
.before(`<td class='isrc c'><small>${isrcsLinks}</small></td>`);
});
}
}
// Display a-tisket links next to Deezer, Spotify, iTunes and Apple Music links
re = new RegExp('musicbrainz.org/release/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})', 'i');
if (window.location.href.match(re)) {
document.querySelectorAll('div#bottom-credits a').forEach(function (link) {
if (link.href.match(/deezer.com|(music|itunes).apple.com|spotify.com/)) {
let id;
let fragment;
let country;
if (link.href.match(/deezer.com/)) {
id = new URL(link.href).pathname.split('/').slice(-1)[0];
fragment = 'deez';
country = 'GB%2CUS%2CIN';
} else if (link.href.match(/apple.com/)) {
id = new URL(link.href).pathname.split('/', 5).slice(-1)[0].replace('id', '');
fragment = 'itu';
country = new URL(link.href).pathname.split('/', 2)[1];
} else if (link.href.match(/spotify.com/)) {
id = new URL(link.href).pathname.split('/', 5).slice(-1)[0];
fragment = 'spf';
country = 'GB%2CUS%2CIN';
}
let next = link.nextElementSibling.nextElementSibling;
let newlink = document.createElement('a');
newlink.href = `https://atisket.pulsewidth.org.uk/?preferred_countries=${country}&${fragment}_id=${id}&preferred_vendor=${fragment}`;
newlink.text = 'a-tisket';
next.before(document.createTextNode(' ['));
next.before(newlink);
next.before(document.createTextNode(']'));
}
});
}
// Discogs link rollover
// TODO...
// -------------- End of script ------------------------
});