YouScrobbler

Scrobbles the currently watching YouTube video to last.fm.

// ==UserScript==
// @name          YouScrobbler
// @namespace     userscripts.org
// @author        http://www.lukash.de
// @description   Scrobbles the currently watching YouTube video to last.fm.
// @identifier    http://userscripts.org/scripts/source/119694.user.js
// @include       http://*.youtube.com/*
// @include       https://*.youtube.com/*
// @include       http://youtube.com/*
// @include       https://youtube.com/*
// @include       *//*.youtube.com/tv*
// @grant         GM_getValue
// @grant         GM_setValue
// @grant         GM_xmlhttpRequest
// @version       1.4.8
// @noframes
// @run-at        document-idle
// ==/UserScript==

/**
 * You can contact me on http://www.lukash.de/youscrobbler or on http://userscripts.org/scripts/show/119694 if you have got suggestions, bugs or other questions
 */

'use strict';

const VERSION = '1.4.8';
const APIKEY = 'd2fcec004903116fe399074783ee62c7';

let lastFmAuthenticationUrl = 'http://www.last.fm/api/auth';
let authenticationSessionUrl = 'http://youscrobbler2.lukash.de/auth';
let scrobbleSongUrl = 'http://youscrobbler2.lukash.de/scrobblesong/';

let currentURL = document.URL;
let loadgif = '<div class="us_loadgif"><img alt="loading" src="data:image/gif;base64,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" /></div>';
let BFather;
let TO1Helper = false;
let isGM;

let trackInfoFromDB = false;

/**
 * --- Content ---
 * 1. Initializing
 * 2. General Functions
 * 3. Appearance
 * 4. Scrobbling and Login
 * 5. Information request
 * 6. Miscellaneous
 * 7. Update
 */


/**
 * --- 1. Initializing ---
 */
function init() {
	isGM = typeof GM_getValue != 'undefined' && typeof GM_getValue('a', 'b') != 'undefined'; //is Greasemonkey or Tampermonkey

    var isGM2 = 'undefined' === typeof GM_info.scriptHandler; //is Greasemonkey

    if (isGM2) {
        alert("-YouScrobbler Alert- \n \n   YouScrobbler doesnt work with Greasemonkey. Please switch to Tampermonkey. \n\n https://addons.mozilla.org/en-US/firefox/addon/tampermonkey/ \n ");
    }

	if (!isLoggedIn()) {
		tryGetAuthToken();
	}

	us_addButton();
}

function updateUrl() {
	return 'http://youscrobbler.lukash.de/currentversion';
}

function us_reset() {
	setTimeout(function() {
		us_closebox();
	}, 0);
	trackInfoFromDB = false;
	currentURL = document.URL;
	document.getElementById('us_temp_info').setAttribute('us_video_id', getYouTubeVideoId());
	document.getElementById('us_temp_info').removeAttribute('artist');
	document.getElementById('us_temp_info').removeAttribute('track');
	document.getElementById('us_temp_info').removeAttribute('autoscrobbleerror');
	document.getElementById('us_temp_info').removeAttribute('scrobbled');
	document.getElementById('us_temp_info').setAttribute('us_leftToPlay', -1);
	document.getElementById('us_temp_info').removeAttribute('is_full_album');
	document.getElementById('us_temp_info').removeAttribute('us_playstart_s');

	// save time page was loaded aka playstart time in ctime and gay format
	let time = new Date();

	us_saveTempData('us_playstart_s', Math.round(time.getTime() / 1000));

	us_abortScrobbling();
	getTrackInfo();
	us_buttonStatus();
}

/**
 * --- 2. General Functions ---
 */
function initPreferences() {
	if (!us_getValue('us_boxPosition')) {
		us_saveValue('us_boxPosition', (screen.availWidth / 1.3) + ';' + 70);
	}
	if ((!us_getValue('us_color')) || (us_getValue('us_color') == 'r')) {
		us_saveValue('us_color', 'red');
	}
	if (!us_getValue('database.id', 0)) {
		us_saveValue('database.id', '');
		us_saveValue('database.artist', '');
		us_saveValue('database.track', '');
	}
	if (!us_getValue('database_additional.id', 0)) {
		us_saveValue('database_additional.id', '');
		us_saveValue('database_additional.albumtitle', '');
		us_saveValue('database_additional.mbid', '');
	}
	if (!us_getValue('database.maxEntries', 0)) {
		us_saveValue('database.maxEntries', 5000);
	}
	if (!us_getValue('scrobble_at', 0)) {
		us_saveValue('scrobble_at', 75);
	}
	if (us_getValue('us_autoscrobble_active', 'nf') == 'nf') {
		us_saveValue('us_autoscrobble_active', 1);
	}
	if (us_getValue('asFailNotification', 'nf') == 'nf') {
		us_saveValue('asFailNotification', false);
	}
	if (us_getValue('scrobblingNotification', 'nf') == 'nf') {
		us_saveValue('scrobblingNotification', true);
	}
	if (us_getValue('us_autocorrect_tracks', 'nf') == 'nf') {
		us_saveValue('us_autocorrect_tracks', true);
	}
}

// Creates a <type id="id">
function createIdElement(type, id) {
	let el = document.createElement(type);
	el.setAttribute('id', id);
	return el;
}

/**
 * Makes an element draggable.
 *
 * Based on https://gist.github.com/remarkablemark/5002d27442600510d454a5aeba370579
 *
 * @param {HTMLElement} element - The element that will be dragged.
 * @param {HTMLElement} handle - The element that will facilitate the dragging.
 * @param {Number} initialPositionX - Initial x position of the element.
 * @param {Number} initialPositionY - Initial y position of the element.
 * @param {Function} persistPosition - Function called after position is changed.
 */
function us_draggable(element, handle, initialPositionX, initialPositionY, persistPosition) {
	let isMouseDown = false;

	// initial mouse X and Y for `mousedown`
	let mouseX;
	let mouseY;
	// element X and Y before and after move
	let elementX = initialPositionX;
	let elementY = initialPositionY;

	// mouse button down over the handle
	handle.addEventListener('mousedown', onMouseDown);

	/**
	 * Listens to `mousedown` event.
	 *
	 * @param {Object} event - The event.
	 */
	function onMouseDown(event) {
		mouseX = event.clientX;
		mouseY = event.clientY;
		isMouseDown = true;
		event.preventDefault();
	}

	// mouse button released
	document.addEventListener('mouseup', onMouseUp);

	/**
	 * Listens to `mouseup` event.
	 */
	function onMouseUp() {
		isMouseDown = false;
		elementX = parseInt(element.style.left) || 0;
		elementY = parseInt(element.style.top) || 0;
		persistPosition(elementX, elementY);
	}

	// need to attach to the entire document
	// in order to take full width and height
	// this ensures the element keeps up with the mouse
	document.addEventListener('mousemove', onMouseMove);

	/**
	 * Listens to `mousemove` event.
	 *
	 * @param {Object} event - The event.
	 */
	function onMouseMove(event) {
		if (!isMouseDown) {
			return;
		}
		let deltaX = event.clientX - mouseX;
		let deltaY = event.clientY - mouseY;
		element.style.left = elementX + deltaX + 'px';
		element.style.top = elementY + deltaY + 'px';
	}
}

function GM_main() {
	window.us_stateChanged = function(state) {
		let playerNode;
		if (document.getElementById('c4-player')) {
			playerNode = document.getElementById('c4-player');
		} else {
			playerNode = document.getElementById('movie_player');
		}
		// get video ID
		let regex = /(\?|%3F|&|%26)v=[^?&#]*/gi;
		let removeRegex = /(\?|%3F|&|%26)v=/gi;
		let matches = document.URL.match(regex);
		let vidId;
		if (matches != null) {
			vidId = matches[0].replace(removeRegex, '');
		} else {
			matches = playerNode.getVideoUrl().match(regex);
			vidId = matches[0].replace(removeRegex, '');
		}
		if (state == 1 && vidId != document.getElementById('us_temp_info').getAttribute('us_video_id')) {
			setTimeout(function() {
				document.getElementById('us_temp_info').setAttribute('us_reset_now', '1');
			}, 1);
		}
		switch (state) {
			case 1:
				document.getElementById('us_temp_info').setAttribute('video_is_playing', '1');
				break;
			case 0:
				document.getElementById('us_temp_info').setAttribute('video_end_reached', 'yes');
				document.getElementById('us_temp_info').setAttribute('video_is_playing', '0');
				break;
			default:
				document.getElementById('us_temp_info').setAttribute('video_is_playing', '0');
		}
		if (document.getElementById('us_temp_info').getAttribute('is_full_album') != 'yes') {
			document.getElementById('us_temp_info').setAttribute('us_secs', playerNode.getDuration());
		}
	};

	window.onYouTubePlayerReady = function() {
		let playerNode;
		if (document.getElementById('c4-player')) {
			playerNode = document.getElementById('c4-player');
		} else {
			playerNode = document.getElementById('movie_player');
		}
		if (playerNode) {
			playerNode.removeEventListener('onStateChange', 'us_stateChanged');
			playerNode.addEventListener('onStateChange', 'us_stateChanged');
			document.getElementById('us_temp_info').setAttribute('us_secs', playerNode.getDuration());
		} else {
			throw new Error('YouScrobbler: Player node not found!');
		}
	};

	if (document.getElementById('us_temp_info').getAttribute('us_secs') == 0) {
		window.onYouTubePlayerReady();
	}
}

function addJS_Node(text, s_URL, funcToRun, runOnLoad) {
	let D = document;
	let scriptNode = D.createElement('script');
	if (runOnLoad) {
		scriptNode.addEventListener('load', runOnLoad);
	}
	scriptNode.type = 'text/javascript';
	if (text) {
		scriptNode.textContent = text;
	}
	if (s_URL) {
		scriptNode.src = s_URL;
	}
	if (funcToRun) {
		scriptNode.textContent = '(' + funcToRun.toString() + ')()';
	}

	let targ = D.getElementsByTagName('head')[0] || D.body || D.documentElement;
	targ.appendChild(scriptNode);
}
/**
 * Value Saving Method - Switcher
 * uses Greasemonkey GM_ or localStorage
 */
function us_saveValue(name, value) {
	if (isGM) {
		GM_setValue(name, value);
	} else {
		localStorage.setItem(name, value);
	}
}

/**
 * Value Getting Method - Switcher
 * uses Greasemonkey GM_ or localStorage
 */
function us_getValue(name, alternative) {
	if (isGM) {
		return (GM_getValue(name, alternative));
	} else {
		return (localStorage.getItem(name, alternative));
	}
}

/**
 * Temporary save data
 * Saved in "us_temp_info" attributes
 */
function us_saveTempData(name, value) {
	document.getElementById('us_temp_info').setAttribute(name, value);
}

/**
 * Get temporary saved data
 * Saved in "us_temp_info" attributes
 */
function us_getTempData(name) {
	if (document.getElementById('us_temp_info').getAttribute(name)) {
		let value = document.getElementById('us_temp_info').getAttribute(name);
		return value;
	} else {
		return 0;
	}
}


/**
 * --- 3. Appearance ---
 */

/**
 * Add the Scrobble Button to Video and Userpages
 */
function us_addButton() {
	let secs = 0;
	let time = new Date();
	let t = Math.round(time.getTime() / 1000);

	let style_el = document.createElement('style');
	let head = document.getElementsByTagName('head')[0];

	style_el.innerHTML = `
		#us_loginbox_form table { text-align: left; table-layout: fixed; }
		#us_loginbox button { background: transparent; border: none; margin: 0; padding: 0; cursor: pointer; }
		.us_box { border-radius: 5px; border: 5px solid #333; background: #fff;
		/* by AshKyd */
		z-index:1000000; position: absolute; width: 300px; }
		.us_box h3 { cursor: move; padding: 4px 8px 4px 10px; margin: 0px; border-bottom: 1px solid #AAA; background-color: #EEE; }
		.us_box h4 { margin-left: 5px; margin-bottom:0px}
		#us_loginbox .round_button { border-radius: 50%; width: 14px; height: 14px; padding: 3px; float: right; margin:1px 3px 0 0; background-image: linear-gradient(to bottom, #b4b4b4 0%, #9d9d9d 50%, #868686 100%); background-color: #9d9d9d; }
		#us_loginbox .round_button:hover { background-image: linear-gradient(to bottom, #828282 0%, #6b6b6b 50%, #545454 100%); background-color: #3e3e3e; }
		#us_loginbox .round_button svg { display: block; width: 8px; height: 8px; }
		#us_box_head > ul, #us_box_head li { float:right; }
		#us_box_head ul { list-style-type:none; margin: 0;}
		.us_settings_grp { height:50px; vertical-align:middle; padding-right:3px;padding-left:5px}
		.us_settings_grp hr { background-color: #EEE; margin: 5px 8px; height: 1px;}
		.us_settings_grp_left { width:155px}
		.us_settings_grp_right { width:135px}
		.us_settings_grp span { vertical-align:middle}
		.us_settings_grp_heading { color:#777;font-size:100%;font-weight:bold; border-bottom:1px solid #ccc; margin-bottom:4px;}
		.us_settings_grp_database { cursor: help;}
		#databaseMaxLength {width: 55px; }
		#scrobbleStatus { font-weight: bold; }
		#scrobble_at {width: 45px; }
		#us_resetlogin { float: left; }
		#us_loginbox_form { text-align: right; padding: 5px; }
		.us_box input[type=text] { height: 16px; border: 1px solid #bbb; margin: 2px 15px 4px 2px; padding: 3px 4px; width: 170px;}
		.us_box input[type=submit] { cursor:pointer; margin: 0 0 0 5px; padding: 0 4px 3px 4px; border-radius: 2px; font-size: 11px; font-weight: bold; color: white; height: 18px; border: 1px solid #3e3e3e; background-color: #6b6b6b; background-image: linear-gradient(to bottom, #828282 0%, #6b6b6b 50%, #545454 100%); }
		.us_box input[type=submit]:hover { background-color: #9d9d9d; background-image: linear-gradient(to bottom, #b4b4b4 0%, #9d9d9d 50%, #868686 100%);}
		.us_box input[type=submit]:active { padding: 1px 4px 2px 4px;}
		.us_hidden { visibility: hidden; overflow: hidden; height: 0px; }
		#us_hiddenform { margin: 0; pading-right: 10px;}
		#us_hiddenform input[type=text] {margin-right:15px}
		#us_loginbox #us_quickchange { position:relative; bottom:40px; width:9px; height:15px; float:right; background-image: url(data:image/gif;base64,R0lGODlhCQAPAPcAAAAAAICAgIGBgYODg4SEhIeHh4iIiImJiYqKiouLi4yMjI6Ojo+Pj5CQkJGRkZeXl5iYmJmZmaCgoKKiogAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAJAA8AAAhMAP8J/PdgoMEDAQoY/DcggEMBAxckgBAgAgIEAhlM+Bfg3wSMBjsuFChyZMmFJ0MubCCB4z8JDgQqIEAxQgICAx3qXJgggIGRBA0GBAA7); }
		#us_loginbox #us_quickchange:focus { background-color: #FFF; outline:none}
		.us_clickable_formdesc {}
		.us_loadgif { text-align: center; padding: 10px 0; }
		.us_loadgif img { border-radius: 5px; border:3px solid #91998E; }
		#us_loginbox #us_more { font: normal normal bold 12pt/12pt Arial; color: #999; text-decoration: none; margin-right: 3px; }
		#us_loginbox #us_more:focus { background:none; outline:none }
		.us_submitbuttons { background-color: #EEE; border-top: 1px solid #AAA; padding: 5px; width: 290px; height: 18px; margin-top: 5px; }
		#scrobbleStatus_parent {float: left; height: 18px; margin-left: 15px; padding-left: 5px; padding-top: 2px; color:#888}
		#us_autoscrobble {vertical-align:middle;}
		.us_submitbuttons_box_left {float: left;}
		.us_error { background-color: #F6D8D8; border: 1px solid #f28494; padding: 5px 3px 5px 3px; width: 90%; margin: 6px auto 10px; }
		.us_done { background-color: #CCFF99; border: 1px solid #99CC00; padding: 5px 3px 5px 3px; width: 90%; margin: 5px auto; }
		.us_infobox { z-index:1000000; background-color: #E8E8E8; border-radius: 5px; padding: 10px; position: fixed; right: 16px; bottom: 9px; border: 1px solid #000000; font-size: 10pt; }
		.us_infobox div { color: #AAAAAA; margin: 1px 5px 0 0; float: left; }
		.us_infobox div img { float: right; margin: -1px -6px 1px 8px; vertical-align: middle;}
		.us_infobox .sep { color:#AAA; }
		.us_trackinfo { color: #47D93D; font-weight: bold; padding-right: 8px; font-size: 11pt; vertical-align: middle;}
		.us_box .us_center { padding: 10px; text-align: center; }
		.us_box .us_left { padding: 10px; text-align: left; }
		#us_submit { float: right; margin-bottom:5px;}
		us_submitbuttons_box_left {border}
		#us_scrobblebutton { float:right; cursor: pointer; margin-left:16px;}
		#us_start_scrobblebutton {padding-left:3px!important} /* Feather check */
		#us_start_scrobblebutton_text { vertical-align: middle; background-repeat: no-repeat; background-position: left center; padding-left: calc(16px + 0.5em); display: inline-block; height: 16px; line-height: 16px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAA00lEQVQ4T61RsQ3CMBB0i6hS0rEAFTMgGjZgEzqmSZEuQ1BGKAVdFkCiQBSukPl76ayPHZAMFCf/392frbe7993sfDy0j+0qlEBmTjJbuSmxAM9fA8L/Ai7rpaLf72INpJrVNYCNLCV470egGec7TQNoGJo6G0APnjU1XBgDrIGwRuocaqu5ArVDQQMFApz8tRpxogf4Ung+BtiXocfJXUEDpwF2BzTyFvB8BbUsAEh/AUPg7UItGBgDCApTPGH5LKAUCLimZAmcLGMhxTcht6GpNy9dy3EY/mhSLAAAAABJRU5ErkJggg==); }
		#us_start_scrobblebutton_text.black_icon { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMjHxIGmVAAAA1ElEQVQ4T62RwQkDIRBFhRy3gTRgG1axthGwjNRgIyGHvQZJNx4TyGa+8GWim+BCDg9n5s98ZTQppSmEsHjv1z3IzF1mj0aSi/DQ4iAv4QaDLXGY/xk45wrzPNcYtJrWiwETWcqac/6AzTi/acWADTHGbgA56oyp4cJqoBuIbqTOIWttAbFBwAYKBDX569KIEzngS9Hz00C/DDlO7goaasVA74CNvAV1voJaZwDaX8AQ6nqhGhpWA0Jhq050vTPYCwzObXGQp3A1soyDBCcljLLEGKc3e8wU7uW1aewAAAAASUVORK5CYII=); }
		#fullAlbumIcon { float: left; height: 16px; width: 16px; cursor: help;}
		#foundInDBIcon { float: left; height: 16px; width: 16px; cursor: help; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwQAADsEBuJFr7QAAABh0RVh0U29mdHdhcmUAUGFpbnQuTkVUIHYzLjM2qefiJQAAAR9JREFUOE+tk92KglAYRXuYeTUfIZUi8e9GykASQUEQBNFuvSgotETssfbMPuAZBmOcmIR99a21ksDF4h1PGIY4Ho/wfR/n8/nXkSFLR/6267qoqgp1XWPuIUOWjgw4joOyLHE6neZ8wZClIwO2baMoClyv19kAGbJ0ZMCyLOR5jtvtNhsgQ5aODJimiSzL0Pf9bIAMWToyYBgG0jQV1b+MLB0Z2Gw2iOP4pdGRgfV6jSiKXhodGVitVjgcDmJJkuDxeDwdbyNHRwZ0XUcQBGJt20JRFFwul8kfytvI0ZEBTdOw3+/Fuq4TgaZpJgHeRo6ODGy3W+x2O7FhGETgfr9PAryNnKqq34Ev8sPzPCyXS/GRUH423shwP97gP1/0J3OEY6rxN9R9AAAAAElFTkSuQmCC);}
		#us_scrobble_on {font-weight:bold; color: #66CC00;}
		#us_scrobble_failed {font-weight:bold; color: #D10404;}
		#us_scrobble_statusbar {background-color: #66CC00; height: 2px; width: 0; opacity: 0.8; margin: 0px; padding-right: 1px; }
		.us_status_failed { background-color: #CC181E; }
		.us_status_hidden { display: none; }
		#us_loginbox .us_status_small {color: #999; font-size:80%}
		.us_box, .us_infobox {visibility: visible; opacity: 1; transition: opacity 0.5s;}
		.us_box_hidden {visibility: hidden; opacity: 0; transition: visibility 0s 0.5s, opacity 0.5s;}
	`;

	// us_start_scrobblebutton
	let button = createIdElement('span', 'us_scrobblebutton');

	// Design check
	if (document.getElementsByTagName('ytd-searchbox')[0]) {
		BFather = document.getElementsByTagName('ytd-searchbox')[0];
		button.setAttribute('class', 'yt-uix-button-group');
		button.style.marginLeft = '50px';
		button.style.padding = '6px 0 6px 0';
		button.style.border = '1px solid var(--ytd-searchbox-legacy-border-color)';
        button.style.background = 'var(--ytd-searchbox-legacy-border-color)';
		button.innerHTML = `
			<input id="us_temp_info" video_is_playing="1" type="hidden" us_secs="${secs}" us_playstart_s="${t}"/>
			<a style="border-radius:2px; 2px; 2px; 2px;padding-right:6px;padding-left:8px!important" class="yt-uix-button yt-uix-sessionlink start yt-uix-button-default" id="us_start_scrobblebutton">
				<span id="us_start_scrobblebutton_text">Scrobble</span>
			</a>
			<div id="us_scrobble_statusbar" class="us_status_hidden"></div>
		`;
		BFather.insertBefore(button, BFather.lastChild);

		document.getElementById('us_scrobble_statusbar').style.position = 'relative';
		document.getElementById('us_scrobble_statusbar').style.top = '6px';
	} else if (document.getElementById('masthead-nav')) {
		BFather = document.getElementById('masthead-nav');
		button.innerHTML = `
			<input id="us_temp_info" video_is_playing="1" type="hidden" us_secs="${secs}" us_playstart_s="${t}" />
			<a class="start" id="us_start_scrobblebutton"><span id="us_start_scrobblebutton_text">Scrobble</span></a>
			<span class="masthead-link-separator">|</span>
		`; // postxanadus
		BFather.insertBefore(button, BFather.firstChild);
	} else if (document.getElementById('yt-masthead-content')) {
		BFather = document.getElementById('yt-masthead-content');
		button.setAttribute('class', 'yt-uix-button-group');
		button.style.float = 'right';
		button.style.marginLeft = '20px';
		button.style.marginTop = '3px';
		button.style.marginRight = '2px';
		button.style.borderTopRightRadius = '2px';
		button.style.borderBottomRightRadius = '2px';
		button.innerHTML = `
			<input id="us_temp_info" video_is_playing="1" type="hidden" us_secs="${secs}" us_playstart_s="${t}" />
			<a style="border-radius:2px; 2px; 2px; 2px;padding-left:6px!important" class="yt-uix-button yt-uix-sessionlink start yt-uix-button-default" id="us_start_scrobblebutton">
				<span id="us_start_scrobblebutton_text">Scrobble</span>
			</a>
			<div id="us_scrobble_statusbar" class="us_status_hidden"></div>
		`;
		BFather.insertBefore(button, BFather.firstChild);
	} else if (document.getElementById('mh')) {
		BFather = document.getElementById('mh');
		button.setAttribute('class', 'ml');
		button.innerHTML = `
			<input id="us_temp_info" video_is_playing="1" type="hidden" us_secs="${secs}" us_playstart_s="${t}" />
			<a class="start" id="us_start_scrobblebutton"><span id="us_start_scrobblebutton_text"> Scrobble</span></a>
		`;
		BFather.insertBefore(button, document.getElementById('se').nextSibling);
	} else {
		setTimeout(function() {
			us_addButton();
		}, 1000);

		return;
	}

	if (us_getValue('us_color') === 'black') {
		document.getElementById('us_start_scrobblebutton_text').classList.add('black_icon');
	}

	head.appendChild(style_el);

	us_buttonStatus();
	document.getElementById('us_temp_info').setAttribute('us_video_id', getYouTubeVideoId());
	addJS_Node(null, null, GM_main);
	setTimeout(function() {
		us_ajax_scanner();
	}, 1000);

	checkFirstRun();
}

function us_buttonStatus() {
	let secs = us_getTempData('us_secs');
	document.getElementById('us_start_scrobblebutton').style.opacity = 1;

	if (secs > 30) {
		document.getElementById('us_scrobblebutton').addEventListener('click', us_toggleBox, true);
		document.getElementById('us_scrobblebutton').title = '';
		document.getElementById('us_start_scrobblebutton').style.opacity = 1;
		// watched seconds till scrobbling
		let time_left_to_scrobble;
		if (us_getTempData('us_leftToPlay', false) == false || us_getTempData('us_leftToPlay') < 0) {
			time_left_to_scrobble = parseInt(us_getTempData('us_secs') * (us_getValue('scrobble_at')) * 0.01);
			us_saveTempData('us_leftToPlay', parseInt(time_left_to_scrobble));
		}
		tryAutoScrobble();
	} else {
		document.getElementById('us_start_scrobblebutton').style.opacity = 0.5;
		document.getElementById('us_scrobblebutton').removeEventListener('click', us_toggleBox);
		if (secs == 0) {
			document.getElementById('us_scrobblebutton').title = 'There is no video to scrobble.';
		} else {
			document.getElementById('us_scrobblebutton').title = 'Video is too short to be scrobbled.';
		}
	}

	if (secs == 0) {
		if (!getYouTubeVideoId()) {
			setTimeout(function() {
				us_buttonStatus();
			}, 5000);
		} else {
			setTimeout(function() {
				us_buttonStatus();
			}, 1000);
		}
	}
}


function us_toggleBox() {
	if (!document.getElementById('us_loginbox') || document.getElementById('us_loginbox').classList.contains('us_box_hidden')) {
		us_showBox(false);
	} else {
		us_closebox();
	}
}

// Show dialog window
// Contains either login form, or scrobble form
function us_showBox(justLoggedIn) {
	// check if scrobblerbox was dropped out of possible screen width and if reset
	let boxPosition = us_getValue('us_boxPosition').split(';');
	let boxPositionX = parseInt(boxPosition[0]);
	let boxPositionY = parseInt(boxPosition[1]);

	let boxPositionChanged = false;
	if (boxPositionX > screen.availWidth || boxPositionX < 0) {
		boxPositionX = screen.availWidth / 1.3;
		boxPositionChanged = true;
	}
	if (boxPositionY > screen.availHeight || boxPositionY < 50) {
		boxPositionY = 75;
		boxPositionChanged = true;
	}
	if (boxPositionChanged) {
		us_saveValue('us_boxPosition', boxPositionX + ';' + boxPositionY);
	}

	// either create loginbox or show it
	if (!document.getElementById('us_loginbox')) {
		let loginbox = createIdElement('div', 'us_loginbox');
		loginbox.classList.add('us_box');
		loginbox.style.left = boxPositionX + 'px';
		loginbox.style.top = boxPositionY + 'px';
		document.body.insertBefore(loginbox, document.body.firstChild);
	} else if (document.getElementById('us_loginbox').classList.contains('us_box_hidden')) {
		let loginbox = document.getElementById('us_loginbox');
		loginbox.style.left = boxPositionX + 'px';
		loginbox.style.top = boxPositionY + 'px';
		loginbox.classList.remove('us_box_hidden');
	}
	if (!isLoggedIn()) {
		let cont = `
			<div id="us_loginbox_form">
				<div class="us_error">You are currently not logged in!</div><br />
				<span>Click Login below to authenticate your account</span><br/><br/>
				<em>Note: You will leave this site and be redirected here after having logged in to Last.fm.</em><br/><br />
			</div>
			<div class="us_submitbuttons"><input id="us_submit" value="Authenticate" type="submit" /></div>
		`;
		us_boxcontent('Login to last.fm', cont);

		document.getElementById('us_submit').addEventListener('click', us_authenticate);
	} else {
		us_scrobbleform(justLoggedIn);
	}

}

/**
 * inserts the scrobbleform into the window
 */
function us_scrobbleform(justLoggedIn) {
	let messageText = '';
	let checkedText = '';
	let databaseFoundText = '';
	let scrobbleStatus = '';
	let feedback = getTrackInfo();

	let artist = decodeURIComponent(us_getTempData('artist'));
	let track = decodeURIComponent(us_getTempData('track'));
	let album = decodeURIComponent(us_getTempData('album'));
	if (artist == 0 && track == 0) {
		artist = '';
		track = '';
	}
	if (album == 0) {
		album = '';
	}
	if (TO1Helper) {
		let restTime;
		if (us_getTempData('us_lefttoplay')) {
			restTime = us_getTempData('us_lefttoplay');
		} else {
			restTime = us_getTempData('us_secs');
		}
		scrobbleStatus = `<div id="scrobbleStatus_parent"> scrobble in <span id="scrobbleStatus">${restTime}</span> sec &nbsp;<button type="button" id="us_abortScrobbling" title="abort scrobbling">×</button></div>`;
	}
	if (us_getTempData('scrobbled') == 1) {
		scrobbleStatus = '<div id="scrobbleStatus_parent">scrobbled</div>';
	}
	if (justLoggedIn) {
		messageText = '<div class="us_done">Successfully logged in</div>';
	}
	let asE = us_getTempData('autoscrobbleError');
	if (asE) {
		if (asE == 'failed') {
			messageText += '<div class="us_error">AutoScrobble failed. Please edit info.</div>';
		}
		if (asE == 'noMusic') {
			messageText += '<div class="us_error">Track not found on Last.fm.</div>';
		}
		if (asE == 'bad') {
			messageText += '<div class="us_error">Video title is not in a valid format to be scrobbled.</div>';
		}
	}
	if (us_getValue('us_autoscrobble_active', 0) == 1) {
		checkedText = ' checked';
	}
	if (((feedback == 'found') && (trackInfoFromDB))) {
		databaseFoundText = '<div id="foundInDBIcon" title="Track information retrieved from personal database"></div>';
	}
	if (us_getTempData('is_full_album') == 'yes') {
		databaseFoundText = `<div id="fullAlbumIcon" title="Video was recognized as a full album">Full Album: Track ${us_getTempData('full_album_track_nr')} of ${us_getTempData('full_album_track_count')}</div>`;
	}

	let cont = `
		<div id="us_loginbox_form">${databaseFoundText}
			${messageText}
			<form name="us_scrobbleform" onSubmit="return false">
				Artist: <input type="text" name="artist" value="${artist}" /><br />
				Track: <input type="text" name="track" value="${track}" /><br/>
				<button id="us_quickchange" title="Artist ↔ Track"></button>
				<button type="button" id="us_more" title="more options">+</button>
				<p id="us_hiddenform" class="us_hidden">
					Album title: <input type="text" name="album" value="${album}" /><br />
				</p>
			</form>
		</div>
		<div class="us_submitbuttons">
			<div class="us_submitbuttons_box_left" title="Activate automatic scrobbling?"><input id="us_autoscrobble" name="us_autoscrobble" type="checkbox"${checkedText}><label for="us_autoscrobble">Auto</label></div>
			${scrobbleStatus}
			<input id="us_submit" value="Scrobble" type="submit" />
		</div>
	`;
	us_boxcontent('Scrobble to last.fm - ' + us_getValue('us_username'), cont);

	document.getElementById('us_quickchange').addEventListener('click', us_quickchange);
	document.getElementById('us_submit').addEventListener('click', us_scrobblenp);
	document.getElementById('us_autoscrobble').addEventListener('click', function() {
		if (this.checked) {
			us_saveValue('us_autoscrobble_active', 1);
		} else {
			us_saveValue('us_autoscrobble_active', 0);
		}
	});
	document.getElementById('us_more').addEventListener('click', us_showmoreform);
	if (document.getElementById('us_abortScrobbling')) {
		document.getElementById('us_abortScrobbling').addEventListener('click', us_abortScrobbling);
	}
	if (us_getValue('us_more_options_show_or_hide') == 'show') {
		us_showmoreform();
	}
}

// little box show info-messages
function us_infoBox(cont) {
	let inbox;
	if (!document.getElementById('us_infobox')) {
		inbox = createIdElement('div', 'us_infobox');
		inbox.classList.add('us_infobox');
		document.body.appendChild(inbox);
	} else {
		inbox = document.getElementById('us_infobox');
		inbox.classList.remove('us_box_hidden');
	}
	inbox.addEventListener('click', us_closeinfobox);
	inbox.style.cursor = 'pointer';
	inbox.title = 'Click to Close';
	inbox.innerHTML = cont;
}


// closes the box with fadeout effect
function us_closebox() {
	let object = document.getElementById('us_loginbox');
	object.classList.add('us_box_hidden');
}

// closes the info-box with fadeout effect
function us_closeinfobox() {
	let object = document.getElementById('us_infobox');
	object.classList.add('us_box_hidden');
}

// shows the optional data fields
function us_showmoreform() {
	let i1 = document.getElementById('us_hiddenform');
	let a = document.getElementById('us_more');

	if (i1.classList.contains('us_hidden')) {
		i1.classList.remove('us_hidden');
		a.innerHTML = '&#8722;';
		us_saveValue('us_more_options_show_or_hide', 'show');
	} else {
		i1.classList.add('us_hidden');
		a.innerHTML = '+';
		us_saveValue('us_more_options_show_or_hide', 'hide');
	}
}

/**
 * Fills window with title and content
 */
function us_boxcontent(title, content) {
	let loginbox = document.getElementById('us_loginbox');
	if (!loginbox) {
		return false;
	}
	if (loginbox.classList.contains('us_box_hidden')) {
		loginbox.classList.remove('us_box_hidden');
	}
	loginbox.innerHTML = `
		<h3 id="us_box_head">${title}<ul>
			<li><button type="button" title="Close" id="us_box_close" class="round_button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M2.4,2.4 21.6,21.6 M2.4,21.6 21.6,2.4" style="fill:none;stroke-width:5;stroke:#fff"/></svg></button></li>
			<li><button type="button" title="Settings" id="us_box_settings" class="round_button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 479.8 479.8" fill="#fff"><path d="m479.3 116.6c-0.4-4.3-3.2-7.9-7.2-9.4-4-1.5-8.5-0.5-11.6 2.6l-62.2 62-69-21.8-21.9-68.7 62.2-62c3-3 4-7.5 2.6-11.6-1.5-4-5.1-6.8-9.4-7.2C324.9-2.7 287.9 10.7 261.2 37.4 224.5 73.9 214.8 127.3 231.9 172.8c-1.9 1.6-3.7 3.3-5.5 5.1L18.5 373.2c-0.1 0.1-0.1 0.1-0.2 0.2-24.4 24.3-24.4 64 0 88.3 24.4 24.3 64 24 88.3-0.3 0.1-0.1 0.2-0.2 0.3-0.3L301.3 252.5c1.8-1.8 3.4-3.6 4.9-5.5 45.7 17.2 99.3 7.5 136-29.1 26.8-26.7 40.4-63.6 37-101.3zM75.3 435.4c-9 9-23.6 9-32.6 0-9-9-9-23.5 0-32.5 9-9 23.6-9 32.6 0 8.9 9 8.9 23.5 0 32.5z"/></svg></button></li>
			<li><button type="button" title="Help" id="us_box_help" class="round_button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#fff"><path d="m10.1 23.9h4v-4h-4zm2-24c-4.4 0-8 3.6-8 8h4c0-2.2 1.8-4 4-4 2.2 0 4 1.8 4 4 0 4-6 3.5-6 10h4c0-4.5 6-5 6-10 0-4.4-3.6-8-8-8z"/></svg></button></li>
		</ul></h3>
		<div>${content}</div>
	`;
	document.getElementById('us_box_close').addEventListener('click', us_closebox);
	document.getElementById('us_box_settings').addEventListener('click', us_settings);
	document.getElementById('us_box_help').addEventListener('click', us_help);

	let position = us_getValue('us_boxPosition').split(';');
	let initialPositionX = parseInt(position[0]);
	let initialPositionY = parseInt(position[1]);
	us_draggable(
		loginbox,
		document.getElementById('us_box_head'),
		initialPositionX,
		initialPositionY,
		function persistPosition(elementX, elementY) {
			us_saveValue('us_boxPosition', elementX + ';' + elementY);
		}
	);
}

/**
 * Show the help-window
 */
function us_help() {
	let cont = `
		<p class="us_left">Documentation, Changelog and more can be found on the <a target="_blank" href="http://www.lukash.de/youscrobbler" title="YouScrobbler on lukash.de">YouScrobbler Website</a>.</p>
		<h4>Feedback</h4>
		<p class="us_left">Suggestions and other Questions can be posted in the <a target="_blank" href="http://www.last.fm/group/YouScrobbler/forum" title="YouScrobbler Forum">Forum</a>.</p>
		<h4>Links</h4>
		<p class="us_left">
			<a target="_blank" href="http://www.lukash.de/youscrobbler" title="YouScrobbler on lukash.de">YouScrobbles Website</a><br/>
			<a target="_blank" href="http://www.last.fm/group/YouScrobbler" title="Last.fm Group">Last.fm Group</a><br/>
			<a target="_blank" href="https://github.com/floblik/YouScrobbler" title="GitHub">GitHub repo</a><br/>
		</p>
	`;
	us_boxcontent('About - YouScrobbler ' + VERSION, cont);
}
/**
 * Show the settings-window
 */
function us_settings() {
	let maxEntries = us_getValue('database.maxEntries', 5000);
	let cont = `
		<div id="us_loginbox_form"><form name="us_settings_form" onSubmit="return false"><table><tr>
			<td class="us_settings_grp us_settings_grp_left">
				<div class="us_settings_grp_heading">General</div>
				<input type="checkbox" id="us_settings_asFailNotification" name="us_settings_asFailNotification"/><label for="us_settings_asFailNotification">error notification</label><br/>
				<input type="checkbox" id="us_settings_scrobblingNotification" name="us_settings_scrobblingNotification"/><label for="us_settings_scrobblingNotification">scrobble notification</label><br/>
				<hr/>
				<label for="scrobble_at">scrobble at </label><select name="scrobble_at" id="scrobble_at">
					<option id="scrobble_at10" value="10">10</option>
					<option id="scrobble_at25" value="25">25</option>
					<option id="scrobble_at50" value="50">50</option>
					<option id="scrobble_at75" value="75">75</option>
					<option id="scrobble_at95" value="95">95</option>
				</select><span>&#37;</span><br/>
				<input type="checkbox" id="us_settings_autoCorrect" name="us_settings_autoCorrect"/><label for="us_settings_autoCorrect">last.fm auto correct</label><br/>
				<hr/>
				<div>
					<input type="radio" id="us_settings_color_red" name="us_settings_color" value="red" /><label for="us_settings_color_red">Red</label>
					<input type="radio" id="us_settings_color_black" name="us_settings_color" value="black" /><label for="us_settings_color_black">Black</label>
				</div>
			</td>
			<td class="us_settings_grp us_settings_grp_right">
				<div class="us_settings_grp_heading us_settings_grp_database" title="Your custom edited track information">Database</div>
				<span>Size: ${us_getValue('database.id').split(' ').length - 1} / <select name="databaseMaxLength" id="databaseMaxLength">
					<option id="databaseMaxLength500" value="500">500</option>
					<option id="databaseMaxLength5000" value="5000">5000</option>
					<option id="databaseMaxLength-1" value="-1">unlimited</option>
				</select></span><br/><br/>
				<div class="us_settings_grp_heading">About</div>
				<span>Version: ${VERSION}</span><br/>
				<span id="us_manualupdate"><button type="button" id="us_manualupdate_link">Check for Update</button></span>
			</td>
		</tr></table></form></div>
		<div class="us_submitbuttons"><input type="submit" id="us_resetlogin" value="Reset Login"/></div>
	`;

	us_boxcontent('Settings', cont);
	let us_settings_color = 'us_settings_color_' + us_getValue('us_color');
	document.getElementById(us_settings_color).setAttribute('checked', 'checked');
	if (us_getValue('asFailNotification', 0) || us_getValue('asFailNotification') == 'yes') {
		document.getElementById('us_settings_asFailNotification').setAttribute('checked', 'checked');
	}
	if (us_getValue('scrobblingNotification')) {
		document.getElementById('us_settings_scrobblingNotification').setAttribute('checked', 'checked');
	}
	if (us_getValue('us_autocorrect_tracks')) {
		document.getElementById('us_settings_autoCorrect').setAttribute('checked', 'checked');
	}
	document.getElementById('databaseMaxLength' + maxEntries.toString()).selected = true;
	if (document.getElementById('scrobble_at' + us_getValue('scrobble_at', 75).toString())) {
		document.getElementById(('scrobble_at' + us_getValue('scrobble_at', 75).toString())).selected = true;
	} else {
		document.getElementById('scrobble_at75').selected = true;
	}
	document.getElementById('us_resetlogin').addEventListener('click', us_resetlogin);
	document.getElementById('us_manualupdate_link').addEventListener('click', function() {
		document.getElementById('us_manualupdate').innerHTML = '<span class="us_status_small">checking</span>';
		updateCheck(true);
	});

	// Save settings
	document.getElementById('us_settings_color_red').addEventListener('change', function() {
		us_saveValue('us_color', 'red');
		document.getElementById('us_start_scrobblebutton_text').classList.remove('black_icon');
	});
	document.getElementById('us_settings_color_black').addEventListener('change', function() {
		us_saveValue('us_color', 'black');
		document.getElementById('us_start_scrobblebutton_text').classList.add('black_icon');
	});
	document.getElementById('us_settings_asFailNotification').addEventListener('change', function() {
		us_saveValue('asFailNotification', document.getElementById('us_settings_asFailNotification').checked);
	});
	document.getElementById('us_settings_autoCorrect').addEventListener('change', function() {
		us_saveValue('us_autocorrect_tracks', document.getElementById('us_settings_autoCorrect').checked);
	});
	document.getElementById('us_settings_scrobblingNotification').addEventListener('change', function() {
		us_saveValue('scrobblingNotification', document.getElementById('us_settings_scrobblingNotification').checked);
	});
	document.getElementById('databaseMaxLength').addEventListener('change', function() {
		let el = document.getElementById('databaseMaxLength');
		let text = el.options[el.selectedIndex].value;
		us_saveValue('database.maxEntries', text);
	});

	document.getElementById('scrobble_at').addEventListener('change', function() {
		let el = document.getElementById('scrobble_at');
		let text = el.options[el.selectedIndex].value;
		us_saveValue('scrobble_at', text);
	});
}


/**
 * --- 4. Scrobbling and Login ---
 */

function isMusicVideo(infoResult) {
	let artist = us_getTempData('artist').replace(' ', '+');
	let track = us_getTempData('track').replace(' ', '+');
	let url = 'http://ws.audioscrobbler.com/2.0/?method=track.getInfo&api_key=' + APIKEY + '&artist=' + artist + '&track=' + track + '&autocorrect=1&format=json';

	GM_xmlhttpRequest({
		method: 'GET',
		url: url,
		onload: function(response) {
			if (response.responseText) {
				let json = JSON.parse(response.responseText);

				if (json.track && (json.track.name != us_getTempData('track') || json.track.artist.name != us_getTempData('artist')) && us_getValue('us_autocorrect_tracks') == 'yes' && !json.error) {
					us_saveTempData('track', json.track.name);
					us_saveTempData('artist', json.track.artist.name);
				}
				if (json && json.track || trackInfoFromDB) {
					tryAutoScrobbleCallback(infoResult, true);
					return true;
				} else
				if (json.error) {
					tryAutoScrobbleCallback('noMusic', false);
					return false;
				}
			}
			tryAutoScrobbleCallback(infoResult, false);
			return false;
		},
		onerror: function() {
			tryAutoScrobbleCallback(infoResult, false);
			return false;
		},
		ontimeout: function() {
			tryAutoScrobbleCallback(infoResult, false);
			return false;
		}
	});
}

/**
 * Tries to AutoScrobble video if user is logged in, its a music video and the trackinfo was found
 */
function tryAutoScrobble() {
	if (us_getValue('us_autoscrobble_active', 0) == 1) {
		let response = getTrackInfo();
		if (us_getTempData('is_full_album') != 'yes') {
			isMusicVideo(response);
		}
	}
}

function tryAutoScrobbleCallback(response, musicVideo) {
	if ((isLoggedIn()) && ((trackInfoFromDB) || ((response == 'found') && (musicVideo)))) {
		// save time page was loaded aka playstart time in ctime and gay format
		let time = new Date();

		us_saveTempData('us_playstart_s', Math.round(time.getTime() / 1000));
		us_scrobble(decodeURIComponent(us_getTempData('artist')), decodeURIComponent(us_getTempData('track')), '', '', 0, 0, 1);
		return;
	} else if (response == 'bad') {
		us_saveTempData('autoscrobbleError', 'bad');
		scrobble_statusbar('failed');
	} else if (!musicVideo) {
		us_saveTempData('autoscrobbleError', 'noMusic');
		scrobble_statusbar('failed');
	} else {
		us_saveTempData('autoscrobbleError', 'failed');
		scrobble_statusbar('failed');
	}
	if (us_getValue('asFailNotification') || us_getValue('asFailNotification') == 'yes') {
		us_showBox();
	}
}

function scrobble_statusbar(status) {
	let statusBar = document.getElementById('us_scrobble_statusbar');
	if (status == 'scrobble') {
		statusBar.classList.remove('us_status_hidden');
		statusBar.classList.remove('us_status_failed');
	} else if (status == 'failed') {
		statusBar.classList.remove('us_status_hidden');
		statusBar.classList.add('us_status_failed');
	} else if (status == 'hide') {
		statusBar.classList.add('us_status_hidden');
	}

}


/**
 * Redirects the user to Last FM to authenticate. When they allow they will
 * be directed back and an authentication token will be added to the URL.
 * adapted from ScrobbleSmurf
 */
function us_authenticate() {
	let tokenRegex = /(\?|&)token=[^&?]*/gi;
	let currentURL = document.URL.replace(tokenRegex, '');
	if (currentURL.indexOf('?') === -1) {
		currentURL = currentURL.replace('&', '?');
	}
	let redirectURL = lastFmAuthenticationUrl + '?api_key=' + APIKEY + '&cb=' + currentURL;
	window.location.href = redirectURL;
}

/**
 * Attempts to get a Last FM token from the URL. If so authenticate the user.
 * adapted from ScrobbleSmurf and edited
 */
function tryGetAuthToken() {
	let url = currentURL;
	let tokenRegex = /(\?|&)token=[^]{32}/gi;
	let matches = url.match(tokenRegex);

	if (matches == null) {
		return;
	}
	let rawToken = matches[0];
	let token = rawToken.substring(7); // 7, based on '?' or '&' and 'token='.

	GM_xmlhttpRequest({
		method: 'GET',
		url: authenticationSessionUrl + '?token=' + token,
		headers: {
			'Accept': 'text/html'
		},
		onload: function(responseDetails) {
			let feedback = responseDetails.responseText;
			if (((feedback.indexOf('api-error')) == -1) && ((feedback.indexOf('token-error')) == -1)) {
				let retrievedData = responseDetails.responseText.split(' - ');
				us_saveValue('us_username', retrievedData[0]);
				us_saveValue('us_sessionKey', retrievedData[1]);
				us_showBox(true);
			} else {
				us_showBox();
				us_resetlogin(feedback);
			}
		}
	});
}

/**
 * Scrobbles a song using the saved track information
 */
function us_scrobble(artist, track, album, mbid, retry, queued, auto, full_album_scrobble) {
	let secs = us_getTempData('us_secs');
	if ((us_getTempData('scrobbled')) == 1 && !queued) {
		us_saveTempData('us_leftToPlay', parseInt(us_getTempData('us_secs') * (us_getValue('scrobble_at')) * 0.01));
		us_saveTempData('scrobbled', 0);
	}
	let args = '?artist=' + encodeURIComponent(artist) + '&sk=' + us_getValue('us_sessionKey') +
	'&timestamp=' + us_getTempData('us_playstart_s') + '&track=' + encodeURIComponent(track) + '&duration=' + secs + '&yt_vid_id=' + getYouTubeVideoId();
	if (album != 0 && album != '') {
		args += '&album=' + encodeURIComponent(album);
	}
	if (mbid != 0 && mbid != '') {
		mbid == false;
		args += '&mbid=' + encodeURIComponent(mbid);
	}
	let time_left_to_scrobble = us_getTempData('us_leftToPlay');

	if (time_left_to_scrobble > 0) {
		TO1Helper = true;
		if (retry == 0) {
			if (auto == 1 && us_getValue('scrobblingNotification')) {
				us_infoBox(`<div><span class="us_trackinfo"><span id="us_artist_display">${artist}</span> <span class="sep"> - </span> <span id="us_track_display">${track}</span></span> ${time_left_to_scrobble} s <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAACXBIWXMAAA7CAAAOwgEVKEqAAAAAGnRFWHRTb2Z0d2FyZQBQYWludC5ORVQgdjMuNS4xMDD0cqEAAAG4SURBVDhPdVNNS0JREL19mAV9LSRo4zqEfoK0FCKiKCyqRQuhFK2HEBRSEPmkTBCjRdHGZRD9idb2uRBavMC30EJwlW4iXnN0roxFA4d7OXPO3LlfSsbnd91z9p4xlq3Zu8Cr35EAhxw0LO+Ml8aTf9WaL0BMYzFZ3jfjdsQ07E0Tc3CcK0DLtlaAWLSmsVLVcZw9QjdhKW6HCwDNw8zloYG2XaT2VfOsWHNYGeYsoYugYBAF+pnrIWShhQdela4kDbRGcU7QZmBUFJgUPHACD7xq296orr8Fi0QOcxKrYHSLAmhfFnDBA69qVTrCvkcIsoPoPwXchBx5THibBShcnAS0OB0thXQBnA06G+Ic4JMFBkUCcOWrl80VKFKEPuYDPALedgG0Q4TeO7aRAL9mLYD3MO8jeHm+SwfY2sJWKaQPcYyTA0hkKim0fk3QV6gPGZiAB155jTeEXkJkx47d0iivVe4dXIA7N34/pEMCbgOt6i39xhS07YeEeG48yKecYKG8GayKrVxBAy08TbOOx/q9/EwXZvkAB3hMGKd5TH4maNnWGfiquY9TI2jN/PnO4JDr/M5K/QDEQYmHdixexAAAAABJRU5ErkJggg==" alt="queued" /></div>`);
				window.setTimeout(function() {
					us_closeinfobox();
				}, 5000);
			} else if (us_getValue('scrobblingNotification')) {
				us_boxcontent('Queued...', `<div class="us_done">This will be scrobbled in ${time_left_to_scrobble} seconds. </div>`);
				window.setTimeout(function() {
					us_closebox();
				}, 3000);
			}
		}
	} else {
		TO1Helper = false;
		if (queued != 1) {
			us_boxcontent('Scrobbling...', loadgif);
		}
		GM_xmlhttpRequest({
			method: 'GET',
			url: scrobbleSongUrl + args,
			onload: function(responseDetails) {
				scrobbleFeedback(responseDetails, artist, track, queued, full_album_scrobble);
			},
			onerror: function() {
				GM_xmlhttpRequest({
					method: 'GET',
					url: scrobbleSongUrl + args,
					onload: function(responseDetails) {
						scrobbleFeedback(responseDetails, artist, track, queued, full_album_scrobble);
					},
					onerror: function() {
						us_infoBox('<div class="us_error">Server error</div>');
						window.setTimeout(function() {
							us_closeinfobox();
						}, 10000);
					}
				});
			}
		});
	}
}


/**
 * Feedback on scrobbling
 */
function scrobbleFeedback(responseDetails, artist, track, queued, full_album_scrobble) {
	let feedback = responseDetails.responseText;
	us_saveTempData('scrobbled', 1);

	if ((feedback.indexOf('<lfm status="ok"')) != -1) {
		TO1Helper = false;
		if (document.getElementById('scrobbleStatus_parent')) {
			document.getElementById('scrobbleStatus_parent').innerHTML = 'scrobbled';
		}
		if (queued != 1) {
			us_boxcontent('OK!', `<div class="us_done"><span class="us_trackinfo"><span id="us_artist_display">${artist}</span> <span class="sep">-</span> <span id="us_track_display">${track}</span></span> scrobbled.</div>`);
			window.setTimeout(function() {
				us_closebox();
			}, 2000);
		} else {
			if (us_getValue('scrobblingNotification', 0)) {
				us_infoBox(`<div><span class="us_trackinfo">${artist} <span class="sep">-</span> ${track}</span> scrobbled. <img src="data:image/gif;base64,R0lGODlhEAAQAKIAAO7w7qrZnHXGZnC6WJLJhE%2BpODGCLbrfsyH5BAAAAAAALAAAAAAQABAAAANhCLrcHmKUEZw6g4YtT8PEERBEcBCFtwyh4L5nsQTD8cLCURCKducKkgxQgACBAIIgYFAUXQ3CYNkkjgS8YIZUpdlYyQxlt9jRxBla9WLIKVlq1eJgMI8KBnnUwDdkLYAMCQA7" alt="done" /></div>`);
				window.setTimeout(function() {
					us_closeinfobox();
				}, 3000);
			}
		}
	} else {
		if (queued != 1) {
			us_boxcontent('Error', `<div class="us_error">${feedback}</div>`);
		} else {
			us_infoBox(`<div class="us_error">Error: ${feedback}</div>`);
			window.setTimeout(function() {
				us_closeinfobox();
			}, 10000);
		}
	}

	if (full_album_scrobble || us_getTempData('is_full_album') == 'yes') {
		TO1Helper = true;
		us_saveTempData('scrobbled', 0);
		scrobble_statusbar('hide');

		us_closebox();

		let global_album = JSON.parse(us_getTempData('global_album'));
		let track_num = parseInt(us_getTempData('full_album_track_nr'));

		us_saveTempData('artist', global_album.tracks.track[track_num].artist.name);
		us_saveTempData('track', global_album.tracks.track[track_num].name);
		us_saveTempData('us_secs', global_album.tracks.track[track_num].duration);

		let album_left_to_play = global_album.tracks.track[track_num].duration - 1;
		let time = new Date();

		us_saveTempData('us_leftToPlay', album_left_to_play);
		us_saveTempData('us_playstart_s', Math.round(time.getTime() / 1000));

		track_num++;
		us_saveTempData('full_album_track_nr', track_num);

		us_scrobble(decodeURIComponent(us_getTempData('artist')), decodeURIComponent(us_getTempData('track')), decodeURIComponent(us_getTempData('album')), decodeURIComponent(us_getTempData('mbid')), 0, 1, 1, 1);
	}
}

/**
 * Temporary save track information from the form
 */
function us_scrobblenp() {
	let formArtist = document.forms[0].elements[0].value;
	let formTrack = document.forms[0].elements[1].value;
	let formAlbum = document.forms[0].elements[2].value;
	if ((formArtist != '') && (formTrack != '')) {
		if (formArtist != decodeURIComponent(us_getTempData('artist')) || formTrack != decodeURIComponent(us_getTempData('track')) || formAlbum != decodeURIComponent(us_getTempData('album'))) {
			saveDatabaseData(getYouTubeVideoId(), formArtist, formTrack, formAlbum);
		}
		us_saveTempData('artist', encodeURIComponent(formArtist));
		us_saveTempData('track', encodeURIComponent(formTrack));
		if (!formAlbum) {
			formAlbum = '';
		} else {
			us_saveTempData('album', encodeURIComponent(formAlbum));
		}

		us_scrobble(decodeURIComponent(us_getTempData('artist')), decodeURIComponent(us_getTempData('track')), formAlbum, '', 0, 0, 0);
	} // empty input
}

/**
 * Abort scrobbling process
 */
function us_abortScrobbling() {
	if (TO1Helper) {
		TO1Helper = false;
		if (document.getElementById('scrobbleStatus_parent')) {
			let element = document.getElementById('scrobbleStatus_parent');
			element.parentNode.removeChild(element);
		}

	}
	scrobble_statusbar('hide');
}

/**
 * Unset the saved login info + show login form, and maybe show errors
 */
function us_resetlogin(error) {
	us_saveValue('us_username', '');
	us_saveValue('us_sessionKey', '');
	let cont = '';
	let resetInfo = '';
	if (!error) {
		resetInfo = '<div class="us_done">Successfully reset the login credentials</div><br />';
	}
	if ((error != '[object MouseEvent]') && (error != '[object XPCNativeWrapper [object MouseEvent]]')) {
		cont = `<p class="us_error">Error: ${error}</p>`;
	}
	cont += `
		<div id="us_loginbox_form">
			${resetInfo}
			<span>Click Login below to authenticate your account</span><br/><br/>
			<em>Note: You will leave this site and be redirected here after having logged in to Last.fm.</em><br/><br />
		</div>
		<div class="us_submitbuttons"><input id="us_submit" value="Authenticate" type="submit" /></div>
	`;
	us_boxcontent('Login to last.fm', cont);
	document.getElementById('us_submit').addEventListener('click', us_authenticate);
}


/**
 * --- 5. Information request ---
 */

/**
 * Check whether user credentials are stored or not.
 */
function isLoggedIn() {
	if ((!us_getValue('us_username', 0)) || (!us_getValue('us_sessionKey', 0))) {
		return false;
	}
	return true;
}

/**
 * Gets the current YouTube video ID from the browser URL.
 */
function getYouTubeVideoId() {
	let regex = /(\?|%3F|&|%26)v=[^?&#]*/gi;
	let removeRegex = /(\?|%3F|&|%26)v=/gi;
	let matches = document.URL.match(regex);

	if (matches == null) {
		let playerNode;

		if (document.getElementById('c4-player')) {
			playerNode = document.getElementById('c4-player');
		} else {
			playerNode = document.getElementById('movie_player');
		}
		matches = playerNode.getVideoUrl().match(regex);

		if (matches == null) {
			return null;
		}
	}
	let vidId = matches[0].replace(removeRegex, '');
	return vidId;
}

/**
 * Detects the track information from the video title and temporarily saves it
 */
function getTrackInfo() {
	let feedback;

	if ((us_getTempData('artist') != 0) || (us_getTempData('track') != 0)) {
		feedback = 'found';
	} else {
		let titleContentOriginal;
		if (location.href.indexOf('youtube.com/user/') != -1) {
			if (document.getElementById('playnav-curvideo-title')) {
				titleContentOriginal = document.getElementById('playnav-curvideo-title').getElementsByTagName('a')[0].textContent;
			} else if (document.getElementsByClassName('channels-featured-video-details tile')[0]) {
				titleContentOriginal = document.getElementsByClassName('channels-featured-video-details tile')[0].getElementsByTagName('a')[0].textContent;
			}
		} else {
			// Feather check
			if (document.getElementsByClassName('title style-scope ytd-video-primary-info-renderer')[0]) {
				titleContentOriginal = document.getElementsByClassName('title style-scope ytd-video-primary-info-renderer')[0].textContent;
			} else if (document.getElementById('eow-title')) {
				titleContentOriginal = document.getElementById('eow-title').textContent;
			} else if (document.getElementById('watch-headline-title')) {
				titleContentOriginal = document.getElementById('watch-headline-title').textContent;
			} else if (document.getElementById('vt')) {
				titleContentOriginal = document.getElementById('vt').textContent;
			}
		}

		// Retrieve track information from database
		if (getDatabaseData() == true) {
			feedback = 'found';
			trackInfoFromDB = true;
		} else {
			// New detection of track information
			// remove (*) and/or [*] to remove unimportant data
			let titleContent = titleContentOriginal.replace(/ *\([^)]*\) */g, ' ');
			titleContent = titleContent.replace(/ *\[[^)]*\] */g, ' ');

			// remove HD info
			titleContent = titleContent.replace(/\W* HD( \W*)?/, '');
			titleContent = titleContent.replace(/\W* HQ( \W*)?/, '');

			// get remix info
			let remixInfo = titleContentOriginal.match(/\([^)]*(?:remix|mix|cover|version|edit|booty?leg)\)/i);

			let musicInfo = titleContent.split(' - ');
			if (musicInfo.length == 1) {
				musicInfo = titleContent.split('-');
			}
			if (musicInfo.length == 1) {
				musicInfo = titleContent.split('‎–');
			}
			if (musicInfo.length == 1) {
				musicInfo = titleContent.split(':');
			}
			if (musicInfo.length == 1) {
				musicInfo = titleContent.split(' "');
			}

			// format feat. info
			for (let i = 0; i < musicInfo.length; i++) {
				musicInfo[i] = musicInfo[i].replace(/ feat. /, ' feat. ');
				musicInfo[i] = musicInfo[i].replace(/ feat /, ' feat. ');
				musicInfo[i] = musicInfo[i].replace(/ ft. /, ' feat. ');
				musicInfo[i] = musicInfo[i].replace(/ ft /, ' feat. ');
			}

			// remove " and ' from musicInfo
			for (let i = 0; i < musicInfo.length; i++) {
				musicInfo[i] = musicInfo[i].replace(/^\s*"|"\s*$/g, '');
				musicInfo[i] = musicInfo[i].replace(/^\s*'|'\s*$/g, '');
			}

			if ((musicInfo.length == 1) || (musicInfo[0] == false) || (musicInfo[1] == false)) {
				musicInfo[0] = '';
				musicInfo[1] = '';
				feedback = 'notFound';
			} else {
				feedback = 'found';
			}

			musicInfo[1] = musicInfo[1].replace(/(\.avi)$/gi, '');
			musicInfo[1] = musicInfo[1].replace(/(\.wmv)$/gi, '');
			musicInfo[1] = musicInfo[1].replace(/(\.mp4)$/gi, '');
			musicInfo[1] = musicInfo[1].replace(/(\.mpeg4)$/gi, '');
			musicInfo[1] = musicInfo[1].replace(/(\.mov)$/gi, '');
			musicInfo[1] = musicInfo[1].replace(/(\.3gpp)$/gi, '');
			musicInfo[1] = musicInfo[1].replace(/(\.flv)$/gi, '');
			musicInfo[1] = musicInfo[1].replace(/(\.webm)$/gi, '');

			// Full Album Video
			if (titleContentOriginal.match(/Full Album/i)) {
				musicInfo[1] = musicInfo[1].replace(/Full Album/i, '');
			} else {
				// move feat. info from artist to track
				if (musicInfo[0].match(/ feat.* .*/)) {
					musicInfo[1] = musicInfo[1] + musicInfo[0].match(/ feat.* .*/);
					musicInfo[0] = musicInfo[0].replace(/ feat.* .*/, '');
				}

				// add remix info
				if (remixInfo && remixInfo.length == 1) {
					musicInfo[1] += ' ' + remixInfo[0];
				}
			}

			// delete spaces
			musicInfo[0] = musicInfo[0].replace(/^\s\s*/, '').replace(/\s\s*$/, '');
			musicInfo[1] = musicInfo[1].replace(/^\s\s*/, '').replace(/\s\s*$/, '');

			if (us_getValue('us_autoscrobble_active', 0) == 1) {
				if ((musicInfo.length != 2)) {
					feedback = 'bad';
				}
			}

			if (!us_getTempData('artist') && musicInfo[0] != 0) {
				us_saveTempData('artist', encodeURIComponent(musicInfo[0]));
			}

			if (!us_getTempData('track') && musicInfo[1] != 0) {
				us_saveTempData('track', encodeURIComponent(musicInfo[1]));
			}
		}

		// Full Album Video
		if (titleContentOriginal.match(/Full Album/i)) {
			us_saveTempData('is_full_album', 'yes');

			getAlbumInfo();
		}
	}
	return feedback;
}

/**
 * Fetch full album info from last.fm API
 */
function getAlbumInfo() {
	let artist = decodeURIComponent(us_getTempData('artist'));
	let albumName = decodeURIComponent(us_getTempData('track'));
	let url = 'http://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=' + APIKEY + '&artist=' + artist.replace(' ', '+') + '&album=' + albumName.replace(' ', '+') + '&format=json';
	GM_xmlhttpRequest({
		method: 'GET',
		url: url,
		onload: function(response) {
		// console.log(response); //TODO: if Album not found -> reset
			if (response.responseText) {
				let json = JSON.parse(response.responseText);
				response = response.responseText;

				let album = json.album;

				if (album.artist == artist && album.name == albumName && album.tracks.track.length > 1) {
					us_saveTempData('is_full_album', 'yes');

					us_saveTempData('global_album', JSON.stringify(album));

					us_saveTempData('full_album_track_nr', 1);
					let track_index = 0;

					us_saveTempData('full_album_track_count', album.tracks.track.length);

					us_saveTempData('us_secs', album.tracks.track[track_index].duration);

					let album_left_to_play = album.tracks.track[track_index].duration - 1;
					us_saveTempData('us_leftToPlay', album_left_to_play);

					us_saveTempData('artist', album.tracks.track[track_index].artist.name);
					us_saveTempData('track', album.tracks.track[track_index].name);

					response = getTrackInfo();
					isMusicVideo(response);
				} else {
					us_saveTempData('is_full_album', 0);
				}
			}
		},
		onerror: function() {
			// alert("failed");
		},
		ontimeout: function() {
		// tryAutoScrobbleCallback(infoResult, false);
		}
	});
}


/**
 * database track information
 */
function getDatabaseData() {
	let id = getYouTubeVideoId();
	if ((us_getValue('database.id', 0) != 0) && (us_getValue('database.id', 0).search(id) != -1)) {
		let ids = us_getValue('database.id', 0).split(' ');
		let artists = us_getValue('database.artist', 0).split(' ');
		let tracks = us_getValue('database.track', 0).split(' ');
		let index = 0;
		for (let i = 0; i < ids.length; i++) {
			if (ids[i] == id) {
				index = i;
				i = ids.length;
			}
		}
		if (!us_getTempData('artist')) {
			us_saveTempData('artist', artists[index]);
		}
		if (!us_getTempData('track')) {
			us_saveTempData('track', tracks[index]);
		}
		ids.splice(ids.length, 0, ids[index]);
		ids.splice(index, 1);
		artists.splice(artists.length, 0, artists[index]);
		artists.splice(index, 1);
		tracks.splice(tracks.length, 0, tracks[index]);
		tracks.splice(index, 1);
		us_saveValue('database.id', ids.join(' '));
		us_saveValue('database.artist', artists.join(' '));
		us_saveValue('database.track', tracks.join(' '));
		// retrieve Album title
		if (us_getValue('database_additional.id', 0).search(id) != -1) {
			let Aids = us_getValue('database_additional.id', 0).split(' ');
			let albumtitles = us_getValue('database_additional.albumtitle', 0).split(' ');
			let index = 0;
			for (let i = 0; i < Aids.length; i++) {
				if (Aids[i] == id) {
					index = i;
					i = Aids.length;
				}
			}
			if (!us_getTempData('album')) {
				us_saveTempData('album', albumtitles[index]);
			}
		}
		return true;
	} else {
		return false;
	}
}

function saveDatabaseData(id, artist, track, album) {
	// Edit existing entry
	if ((us_getValue('database.id', 0) != 0) && (us_getValue('database.id', 0).search(id) != -1)) {
		let ids = us_getValue('database.id', 0).split(' ');
		let artists = us_getValue('database.artist', 0).split(' ');
		let tracks = us_getValue('database.track', 0).split(' ');
		let index = 0;
		for (let i = 0; i < ids.length; i++) {
			if (ids[i] == id) {
				index = i;
				i = ids.length;
			}
		}
		ids.splice(ids.length, 0, ids[index]);
		ids.splice(index, 1);
		artists.splice(artists.length, 0, encodeURIComponent(artist));
		artists.splice(index, 1);
		tracks.splice(tracks.length, 0, encodeURIComponent(track));
		tracks.splice(index, 1);
		us_saveValue('database.id', ids.join(' '));
		us_saveValue('database.artist', artists.join(' '));
		us_saveValue('database.track', tracks.join(' '));
	} else {
	// New entry
		let ids = us_getValue('database.id', 0).split(' ');
		if ((us_getValue('database.maxEntries', 5000) == '-1') || (ids.length < us_getValue('database.maxEntries', 5000))) {
			// New Entry
			us_saveValue('database.id', (us_getValue('database.id', 0) + ' ' + id));
			us_saveValue('database.artist', (us_getValue('database.artist', 0) + ' ' + encodeURIComponent(artist)));
			us_saveValue('database.track', (us_getValue('database.track', 0) + ' ' + encodeURIComponent(track)));
		} else {
			// Already maximum number of entries -> delete oldest and insert new
			let artists = us_getValue('database.artist', 0).split(' ');
			let tracks = us_getValue('database.track', 0).split(' ');
			ids.splice(ids.length, 0, id);
			ids.splice(0, 1);
			artists.splice(artists.length, 0, encodeURIComponent(artist));
			artists.splice(0, 1);
			tracks.splice(tracks.length, 0, encodeURIComponent(track));
			tracks.splice(0, 1);
			us_saveValue('database.id', ids.join(' '));
			us_saveValue('database.artist', artists.join(' '));
			us_saveValue('database.track', tracks.join(' '));
		}
	}

	// Save additional information about the track (Album title)
	if (album != 0) {
		if ((us_getValue('database_additional.id', 0) != 0) && (us_getValue('database_additional.id', 0).search(id) != -1)) {
			let Aids = us_getValue('database_additional.id', 0).split(' ');
			let albumtitles = us_getValue('database_additional.albumtitle', 0).split(' ');
			let index = 0;
			for (let i = 0; i < Aids.length; i++) {
				if (Aids[i] == id) {
					index = i;
					i = Aids.length;
				}
			}
			Aids.splice(Aids.length, 0, id);
			Aids.splice(index, 1);
			albumtitles.splice(albumtitles.length, 0, encodeURIComponent(album));
			albumtitles.splice(index, 1);
			us_saveValue('database_additional.id', Aids.join(' '));
			us_saveValue('database_additional.albumtitle', albumtitles.join(' '));
		} else {
			us_saveValue('database_additional.id', us_getValue('database_additional.id', '') + ' ' + encodeURIComponent(id));
			us_saveValue('database_additional.albumtitle', us_getValue('database_additional.albumtitle', '') + ' ' + encodeURIComponent(album));
		}
	}
}


/**
 * --- 6. Miscellaneous ---
 */
/**
 *
 *
 */
function us_ajax_scanner() {
	let leftToPlay = parseInt(us_getTempData('us_leftToPlay'));
	let secs = parseInt(us_getTempData('us_secs'));
	let scrobble_at = parseInt(us_getValue('scrobble_at'));

	if (!getYouTubeVideoId()) {
		us_saveTempData('us_reset_now', '1');

		setTimeout(function() {
			us_ajax_scanner();
		}, 5000);
	} else {
		setTimeout(function() {
			us_ajax_scanner();
		}, 1000);

		if (us_getTempData('video_is_playing', 0) == 1 && us_getTempData('us_reset_now') != '1' && leftToPlay >= 1 && !us_getTempData('autoscrobleerror')) {
			us_saveTempData('us_leftToPlay', parseInt(leftToPlay - 1));
			if (document.getElementById('scrobbleStatus')) {
				document.getElementById('scrobbleStatus').innerHTML = leftToPlay - 1;
			}

			if (TO1Helper) {
				scrobble_statusbar('scrobble');
				if (us_getTempData('is_full_album') == 'yes') {
					document.getElementById('us_scrobble_statusbar').style.width = Math.round(100 - 100 * ((leftToPlay - 1) / (secs * (100) * 0.01))) + '%';
				} else {
					document.getElementById('us_scrobble_statusbar').style.width = Math.round(100 - 100 * ((leftToPlay - 1) / (secs * ((scrobble_at) * 0.01)))) + '%';
				}
			}
		}

		if (us_getTempData('video_end_reached') == 'yes' && us_getTempData('video_is_playing', 0) == 1) {
			us_saveTempData('us_reset_now', '1');
			us_saveTempData('video_end_reached', 0);
		}

		leftToPlay = us_getTempData('us_leftToPlay');
		if (leftToPlay <= 0 && us_getTempData('scrobbled') != 1 && TO1Helper && secs > 30) {
			leftToPlay = 0;
			TO1Helper = false;
			if (document.getElementById('scrobbleStatus')) {
				document.getElementById('scrobbleStatus_parent').innerHTML = 'submitting...';
			}
			us_scrobble(decodeURIComponent(us_getTempData('artist')), decodeURIComponent(us_getTempData('track')), decodeURIComponent(us_getTempData('album')), decodeURIComponent(us_getTempData('mbid')), 0, 1, 1);
		}
		// check for reset -> ajax youtube change
		if (us_getTempData('us_reset_now') == '1') {
			us_saveTempData('us_reset_now', '0');
			us_reset();
		}
	}
}

/**
 * Quickchange artist ↔ track in scrobble-form
 */
function us_quickchange() {
	let artist = document.forms[0].elements[0].value;
	document.forms[0].elements[0].value = document.forms[0].elements[1].value;
	document.forms[0].elements[1].value = artist;
}

/**
 * Checks if its first run or if updated
 */
function checkFirstRun() {
	let localVersion = us_getValue('us_local_version', 0);
	if (localVersion == 0) {
		initPreferences();
		us_showBox();
		let cont = `
			<div id="us_loginbox_form">
				<h4>Welcome to YouScrobbler!</h4><br/>
				<span>Join the <a target="_blank" href="http://www.last.fm/group/YouScrobbler">Last.fm Group</a> to stay up to date.</span><br/><br/>
				<span>Description and documentation can be found on the <a target="_blank" href="http://www.lukash.de/youscrobbler">Homepage</a>.</span><br/><br/>
			</div>
			<div class="us_submitbuttons"><input id="us_submit" value="Next" type="submit" /></div>
		`;
		us_boxcontent('First Run', cont);
		document.getElementById('us_submit').addEventListener('click', us_showBox);
		us_saveValue('us_local_version', VERSION);
	} else if (localVersion < VERSION) {
		initPreferences();
		us_showBox();
		let cont = `
			<div id="us_loginbox_form">
				<div class="us_done">Welcome to YouScrobbler ${VERSION}.</div><br/>
				<span>Changelog can be found on the <a target="_blank" href="http://www.lukash.de/youscrobbler">Web page</a>.</span><br/><br/><br/>
				<h4>Join the <a target="_blank" href="http://www.last.fm/group/YouScrobbler" title="Last.fm Group">Last.fm Group</a> to stay tuned</h4><br />
			</div>
			<div class="us_submitbuttons"><input id="us_submit" value="Close" type="submit" /></div>
		`;
		us_boxcontent('Successfully Updated', cont);
		document.getElementById('us_submit').addEventListener('click', us_closebox);
		us_saveValue('us_local_version', VERSION);
	}
}


/**
 * --- 7. Update ---
 *
 * Edited version of Script Update Checker (http://userscripts.org/scripts/show/20145)
 */
function updateCheck(forced) {
	let update_interval = 86400000;
	if ((forced) || ((parseInt(us_getValue('us_last_update', '0')) + update_interval) <= parseInt(((new Date()).getTime())))) { // Checks every day (24 h * 60 m * 60 s * 1000 ms)
		try {
			GM_xmlhttpRequest(
				{
					method: 'GET',
					url: updateUrl(),
					onload: function(resp) {
						let local_version, remote_version, response;
						response = resp.responseText;
						us_saveValue('us_last_update', new Date().getTime() + '');
						remote_version = response.split(' ')[0];
						local_version = VERSION;
						let scriptDownloadUrl = 'http://youscrobbler.lukash.de/youscrobbler_' + remote_version.replace(/\./g, '') + '.user.js';
						if (remote_version > local_version) {
							let cont = `
								<div id="us_loginbox_form">
									<span>YouScrobbler ${remote_version} is available.</span><br/>
									<span>Changes are applied after new page load.</span><br/><br/>
									Problem updating?<br/>
									Try via Greasemonkey (Addons/UserScripts)
								</div>
								<div class="us_submitbuttons"><input id="us_submit" value="Install Update" type="submit" /></div>
							`;
							us_boxcontent('Update available', cont);
							document.getElementById('us_submit').addEventListener('click', function() {
								window.open(scriptDownloadUrl, '_blank');
							});
						} else if (forced) {
							document.getElementById('us_manualupdate').firstChild.innerHTML = 'No Update available';
						}
					},
					onerror: function() {
						if ((forced)) {
							let cont = `
								<div id="us_loginbox_form">
									<div class="us_error">Checking for an update has failed. Try again later.</div><br/><br/><br/>
								</div>
								<div class="us_submitbuttons"><input id="us_submit" value="Check again" type="submit" /></div>
							`;
							us_showBox();
							us_boxcontent('Checking for update failed', cont);
							document.getElementById('us_submit').addEventListener('click', function() {
								updateCheck(true);
							});
						} else {
							us_infoBox('<div class="us_error">Checking for update failed</div>');
							window.setTimeout(function() {
								us_closeinfobox();
							}, 5000);
						}
					}
				});
		} catch (err) {
			if ((forced)) {
				let cont = `
					<div id="us_loginbox_form"><div class="us_error">Checking for an Updated has failed. Try again later.<br/>
						Error:<br/>
						${err}
					</div></div><br/><br/><br/>
					<div class="us_submitbuttons"><input id="us_submit" value="Check again" type="submit" /></div>
				`;
				us_showBox();
				us_boxcontent('Checking for update failed', cont);
				document.getElementById('us_submit').addEventListener('click', function() {
					updateCheck(true);
				});
			} else {
				us_infoBox(`<div class="us_error">Checking for update failed: ${err}</div>`);
				window.setTimeout(function() {
					us_closeinfobox();
				}, 5000);
			}
		}
	}
}

if (window.top === window.self) {
	init();

	updateCheck(false);
}