YouScrobbler

Scrobbles the currently watching YouTube video to last.fm.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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);
}