您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
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 <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 = '−'; 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>%</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') + '×tamp=' + 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); }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址