Spotify - Append Artist Name and Title to Copied Link

Spotify - Append Artist Name and Title to Copied Link.

// ==UserScript==
// @name         Spotify - Append Artist Name and Title to Copied Link
// @description  Spotify - Append Artist Name and Title to Copied Link.
// @author       to
// @namespace    https://github.com/to
// @license      MIT
// @version      0.6
//
// @noframes
// @connect      spotify.com
// @connect      song.link
// @match        https://open.spotify.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=spotify.com
//
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        unsafeWindow
// ==/UserScript==

// SpotifyのAPIを利用すると 日本のアーティストが日本語表記になりやすい
const CLIENT_ID = '';
const CLIENT_SECRET = '';
const MARKETS = ['JP', 'US'];

const PREFIX = "";

let useSpotify = !!CLIENT_ID;
let market = MARKETS[0];

// Clipboard APIではcopyイベントが発生しないため 強制的にexecCommandを利用させる
unsafeWindow.navigator.clipboard.write = null;
unsafeWindow.navigator.clipboard.writeText = null;

// マーケットの国を切り替える
// (海外のアーティストがカタカナ表記になってしまうことがあるため)
MARKETS.forEach(m => {
    GM_registerMenuCommand ('Change Market [' + m + ']', () => {
        market = m;
    });
});

if(useSpotify){
    GM_registerMenuCommand ('Use Odesil', () => {
        useSpotify = false;
    });
}

document.addEventListener('copy', e => {
	// URL以外がコピーされたか、または、通常のテキストコピー操作か?
    let url = e.target.value;
	if(!url || !(/^http/.test(url) || !(e.target instanceof HTMLTextAreaElement)))
		return;

    url = url.replace(/\?.+/, '');

    // SpotifyのAPIを利用するか?
    if(useSpotify){
        let ts = url.match(/\/(track|album)\/(.+)/);
        if(ts){
            requestToSpotify(`https://api.spotify.com/v1/${ts[1]}s/${ts[2]}?market=${market}`, r => {
                GM_setClipboard([
                    r.artists.map(a => a.name).join(', '),
                    '-',
                    r.name,
                    url,
                    'https://' + (r.type == 'track' ? 'song' : r.type) + '.link/s/' + r.id].join(' ') + PREFIX);
            });
        }
    } else {
        // JPロケールが適用されていない(USと同じ値が返される)
        GM_xmlhttpRequest({
            url: `https://api.song.link/v1-alpha.1/links?userCountry=${market}&url=${encodeURIComponent(url)}`,
            onload: function(r){
                r = JSON.parse(r.responseText);

                GM_setClipboard([
                    r.entitiesByUniqueId[r.entityUniqueId].artistName,
                    '-',
                    r.entitiesByUniqueId[r.entityUniqueId].title,
                    url,
                    r.pageUrl].join(' ') + PREFIX);
            }});
    }
}, true);

function requestToSpotify(url, callback){
    GM_xmlhttpRequest({
        url: 'https://accounts.spotify.com/api/token',
        method: 'POST',
        data: 'grant_type=client_credentials',
        headers: {
            'Authorization': 'Basic ' + btoa(CLIENT_ID + ':' + CLIENT_SECRET),
            'Content-Type': 'application/x-www-form-urlencoded',
        },
        onload: function(r){
            r = JSON.parse(r.responseText);

            GM_xmlhttpRequest({
                url: url,
                headers: {
                    'Authorization': r.token_type + ' ' + r.access_token,
                    'Content-Type': 'application/json',
                },
                onload: function(r){
                    callback(JSON.parse(r.responseText));
                }});
        }});
}