GMT: Music Release Search Links

try to take over the world!

当前为 2021-03-25 提交的版本,查看 最新版本

// ==UserScript==
// @name         GMT: Music Release Search Links
// @namespace    https://greasyfork.org/users/321857-anakunda
// @version      1.01.0
// @description  try to take over the world!
// @author       Anakunda
// @copyright    2021, Anakunda (https://greasyfork.org/users/321857-anakunda)
// @license      GPL-3.0-or-later
// @match        https://*/torrents.php?id=*
// @connect      *
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_registerMenuCommand
// ==/UserScript==

(function() {
  'use strict';

	if (document.querySelector('div.sidebar > div.box_artists') == null) return; // not a music category
	const header = document.querySelector('div#content div.header');
	if (header == null) throw 'Unexpected page structure';
	['GM_getValue', 'GM_setValue', 'GM_listValues']
		.forEach(fn => { if (!(fn in window)) throw 'GM extensions not available' });
	const defaultSearchLinks = {
		'Orpheus': 'https://orpheus.network/torrents.php?action=advanced&artistname=${artists}&groupname=${album}',
		'RuTracker': 'https://rutracker.org/forum/tracker.php?nm=${artists_quoted}+${album_quoted}',
		'Google': 'https://www.google.com/search?q=${artists_quoted}+${album_quoted}',
		'Google (Images)': 'https://www.google.com/search?q=${artists_quoted}+${album_quoted}&tbm=isch',
		'Discogs': 'https://www.discogs.com/search/?type=all&title=${album}&artist=${artists}',
		'MusicBrainz': 'https://musicbrainz.org/taglookup/index?tag-lookup.artist=${artists}&tag-lookup.release=${album}',
		'AllMusic': 'https://www.allmusic.com/search/all/${artists_quoted}%20${album_quoted}',
		'Apple Music': 'https://music.apple.com/search?term=${artists_quoted}+${album_quoted}',
		'Deezer': 'https://www.deezer.com/search/${artists_quoted}%20${album_quoted}/album',
		'Spotify': 'https://open.spotify.com/search/${artists_quoted}%20${album_quoted}',
		'Tidal': 'https://listen.tidal.com/search/albums?q=${artists_quoted}+${album_quoted}',
		'Bandcamp': 'https://bandcamp.com/search?q=${album_quoted}',
		'Qobuz': 'https://www.qobuz.com/search?q=${artist}+${album}&i=boutique',
		'HighResAudio': 'https://www.highresaudio.com/en/search/?artist=${artists_quoted}&album=${album_quoted}&sort=-releaseDate',
		'Mora': 'https://mora.jp/search/top?keyWord=${artists_quoted}+${album_quoted}&onlyHires=1',
		'e-onkyo': 'https://www.e-onkyo.com/search/search.aspx?q=${artists_quoted}+${album_quoted}',
		'7digital': 'https://uk.7digital.com/search?q=${artists_quoted}+${album_quoted}',
		'Amazon Music': 'https://music.amazon.com/search/${artists_quoted}%20${album_quoted}',
		'YouTube Music': 'https://music.youtube.com/search?q=${artists_quoted}%20${album_quoted}',
		'Presto Jazz': 'https://www.prestomusic.com/jazz/search?search_query=${artists_quoted}%20${album_quoted}',
		'Presto Classical': 'https://www.prestomusic.com/classical/search?search_query=${artists_quoted}%20${album_quoted}',
		'ProStudioMasters': 'https://www.prostudiomasters.com/search?cs=1&q=${artists_quoted}+${album_quoted}',
		'Juno Download': 'https://www.junodownload.com/search/?solrorder=relevancy&q%5Ball%5D%5B%5D=${artists_quoted}%20${album_quoted}',
		'BeatPort': 'https://www.beatport.com/search?q=${artists_quoted}+${album_quoted}',
		'Last.fm': 'https://www.last.fm/search?q=${artists_quoted}+${album_quoted}',
		'OTOTOY': 'https://ototoy.jp/find/?q=${album_quoted}',
		'Netease': 'https://music.163.com/#/search/m/?s=${artists_quoted}%20${album_quoted}&type=10',
		'QQ音乐': 'https://y.qq.com/portal/search.html#t=album&w=${artists_quoted}%20${album_quoted}',
	};
	let keys = GM_listValues(), searchLinks;
	if (keys.length > 0) {
		searchLinks = { };
		for (let key of keys) searchLinks[key] = GM_getValue(key);
	} else {
		searchLinks = defaultSearchLinks;
		for (let key of Object.keys(searchLinks)) GM_setValue(key, searchLinks[key]);
	}
	//console.debug('searchLinks:', searchLinks);
	if (Object.keys(searchLinks) <= 0) return;
	let title = header.querySelector('h2 > span:last-of-type');
	if (title != null) title = title.textContent.trim(); else throw 'Unexpected page structure';
	let albumArtist = header.querySelector('h2 > a');
	if (albumArtist != null) albumArtist = albumArtist.textContent.trim();
	let year = header.querySelector('h2');
	year = year != null && /\[(\d{4})\]/.test(year.lastChild) ? parseInt(RegExp.$1) : '';
	const mainArtists = Array.from(document.querySelectorAll('ul#artist_list > li.artist_main > a[href]'))
		.map(a => a.textContent.trim());
	const artist = mainArtists.length > 0 ? encodeURIComponent(mainArtists[0]) : '',
				artist_quoted = mainArtists.length > 0 ? encodeURIComponent('"' + mainArtists[0] + '"') : '',
				artists = encodeURIComponent(mainArtists.join(' ')),
				artists_quoted = encodeURIComponent(mainArtists.map(artist => '"' + artist + '"').join(' ')),
				album_artist = albumArtist ? encodeURIComponent(albumArtist) : '',
				album_artist_quoted = albumArtist ? encodeURIComponent('"' + albumArtist + '"') : '',
				album = encodeURIComponent(title),
				album_quoted = encodeURIComponent('"' + title + '"');

	const contextId = 'context-1d19ca90-5242-418a-b6d3-d9a9fd5e5cfc';
	if (typeof GM_deleteValue == 'function') {
		var menu = document.createElement('menu'), caller;
		menu.type = 'context';
		menu.id = contextId;
		menu.innerHTML = '<menuitem label="Remove this link" icon="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsSAAALEgHS3X78AAADaElEQVQ4y0XTT0xcVRQG8O+ce+fNAOUN7QiFFoHSNq2SIDQ2QGfQ0hYtSatpuvFPjK0mxtDWRF24MG7UbVeasDGmiTrWgG2NjRlaUyAsirrQoCZVF5QZhsKUP/NmKMwM993jwlG/zfdtT3J+IGYCwPE3zye+f//d6UrmMAAwkUI5/+6qilD4q7Z90x9tjyQIYAIIAHjo1ZfjMn5dNm9+LdfeOjdZQeSCCEzETMQAUBl03C/6j0wmD0Vl7kCnfFgXiQNgFT/32ndnTp84lVnK+A+8rHTubW7piNT2Xfvp5xEDFABIheLwZ6dPjR4u+odWvKxvQ0Hpcd32RrFdvD1cXV9az8Ffz4sqFdT8fNr0P76/O372hYQDbHFEtnz+4vOJJ6C7M/fmTSjoKBYrOuAgEnDqqZI5HH/97LcDPe29CytLJmCtLpV8U1dVo6+M3R6DdnCirr4vOTFhKiortEBMrVJ6ODU3eSGVOkkAUKmU++WZ50aPdu7pvp/NmwAcbQzZ6mA12VUPmbFb4hAYzCbCrK+m01ODs8mni0BOMZEqiRS++eX3kQ5365PtO3c059dLVvuai+tFyf34A3SxwBxQ9iHF6mo6fXtwNjlQBHIMKCWAMBEbSOHqb3fie0NVsUdrd+za9MWaP+6wZBZJBQO2CsSXU+mJ88nUUwZYY4AtYDUAQAQgghWB2SiCfIGZT8OfS0E7DhiAIoEAsOXfkHIrJlIWZEMk4Uv9x248+9iBWHZ12dq//mSGWMUkmsDCsNFtNbsaoY6MetkRHyj8d0KI4H7S1zc68Ehbz8pazmBmRmFjw1bpAAUJJAxRAi5AzMEat7mBAodveN6wDxRUiBAeisWun9zfFl1ZXzOcWdT+6rKJBIPqcjI9/msufze6raa1ADEk0EWI6drqNkVEx2553hX9aTQ6fnTPvo7lnGf0xgNtlpZMJODo4dT81Nup5DMA4LC6+VJTQ3ceviGBzlnfvNJc3yuWxzmTzy+YnAdd3CB/6b4fUUoPp+9NXZi9e9yA1gyw9sbszPFLyYUpl5UWgh9gULZgsVjaXAAB/F7r7ngyGpPF7i75uKll0gFcACiLYwAIgNyLD7dOer09MnuwSwYjO+MAGGWS/EFdQ2KosWU6RPQPZ+B/zuUdJApfbNw9/U5dUwJlzn8DiOOFPhubkGUAAAAASUVORK5CYII=" /><menuitem label="-" />';
		menu.onclick = function(evt) {
			if (!(caller instanceof HTMLAnchorElement)) throw 'Invoker not set';
			if (!confirm('Are you sure to remove ' + caller.textContent + ' from search bar?')) return false;
			GM_deleteValue(caller.textContent);
			if (caller.nextSibling != null && caller.nextSibling.nodeType == 3) caller.nextSibling.remove();
			else if (caller.previousSibling.textContent == divisor) caller.previousSibling.remove();
			caller.remove();
			alert('Site was removed. To restore it, use reset command from script\'s submenu');
		};
		document.body.append(menu);
	}

	const searchBox = document.createElement('div'), divisor = ' | ';
	searchBox.className = 'searchbox';
	searchBox.style = 'text-align: center; padding-bottom: 5px; margin-top: -1px;';
	(searchBox.build = function() {
		this.textContent = 'Lookup on: ';
		for (let key in searchLinks) {
			if (this.lastChild.nodeName == 'A') this.append(divisor);
			let a = document.createElement('a');
			a.textContent = key;
			a.href = eval('`' + searchLinks[key] + '`');
			a.target = '_blank';
			if (typeof GM_deleteValue == 'function') {
				a.oncontextmenu = evt => { caller = evt.currentTarget };
				a.setAttribute('contextmenu', contextId);
			}
			this.append(a);
		}
	}.bind(searchBox))();
	header.append(searchBox);

	if (typeof GM_registerMenuCommand == 'function' && typeof GM_deleteValue == 'function')
		GM_registerMenuCommand('Reset links to default', function() {
			if (!confirm('Are you sure to discard current configuration?')) return;
			for (var key of GM_listValues()) GM_deleteValue(key);
			searchLinks = defaultSearchLinks;
			for (key of Object.keys(searchLinks)) GM_setValue(key, searchLinks[key]);
			searchBox.build();
		}, 'R');
})();