// ==UserScript== // @name Keygenmusic.tk Improvements // @namespace http://tampermonkey.net/ // @version 1.3.1 // @downloadURL https://gist.github.com/Lordmau5/f9fbf60651c765fc61a50a27ebcf7d4a/raw/userscript.js // @updateURL https://gist.github.com/Lordmau5/f9fbf60651c765fc61a50a27ebcf7d4a/raw/userscript.js // @description Various extra functionality and fixes for the amazing site Keygenmusic.tk (Volume slider, Seekbar, Loop toggle, List fixes) // @author Lordmau5 // @match https://keygenmusic.tk/ // @icon https://www.google.com/s2/favicons?sz=64&domain=keygenmusic.tk // @grant none // @require https://unpkg.com/hyperlist@1.0.0/dist/hyperlist.js // ==/UserScript== /* TODO: - add download button - show current seekbar second when holding down and dragging, so you know where you "drop" - try to make audio not lag when scrolling (async-ify onaudioprocess?) ^ seems to mostly be fixed? */ (function() { 'use strict'; let repeatCount = 0; let sliderValue = 0.5; let currentSong = ''; let seeking = false; function toggleLoop() { if (!window.watcher) return; repeatCount = this.checked ? -1 : 0; window.player.config.repeatCount = repeatCount; window.player.reloadConfig(window.player.config, window.player.currentPlayingNode); } function onSliderInput() { sliderValue = this.value / 100; const slider_text = document.querySelector('#volume_slider_text'); slider_text.innerText = `VOLUME ${this.value}%`; localStorage.setItem('slider_volume', sliderValue); } function updateGainElement() { if (!window.player) return; if (window.player.config) { window.player.config.repeatCount = repeatCount; } if (window.player.gainNode) { window.player.gainNode.gain.value = sliderValue; } } function buildLoopButton() { const div = document.createElement('div'); div.id = 'loop_control'; div.classList.add('center'); div.style = 'margin-top: 15px;'; const strong = document.createElement('strong'); strong.style = 'font-size: 19px; color: #a9b7c6;'; strong.appendChild(document.createTextNode('LOOP')); const switchDiv = document.createElement('div'); switchDiv.classList.add('switch'); const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = 'loop'; checkbox.classList.add('switch-check'); checkbox.onclick = toggleLoop; const label = document.createElement('label'); label.htmlFor = 'loop'; label.classList.add('switch-label'); label.appendChild(document.createTextNode('Check')); label.appendChild(document.createElement('span')); switchDiv.appendChild(checkbox); switchDiv.appendChild(label); div.appendChild(strong); div.appendChild(switchDiv); return div; } function buildVolumeSlider() { /* Get LocalStorage Slider Value */ const _raw_slider_volume = localStorage.getItem('slider_volume'); if (!!_raw_slider_volume) { sliderValue = Math.max(0, parseFloat(_raw_slider_volume)); } const div = document.createElement('div'); div.id = 'volume_control'; div.classList.add('center'); div.style = 'margin-top: 15px;'; const strong = document.createElement('strong'); strong.style = 'font-size: 19px; color: #a9b7c6;'; strong.id = 'volume_slider_text'; strong.appendChild(document.createTextNode('VOLUME 50%')); const innerDiv = document.createElement('div'); const input = document.createElement('input'); input.style = 'width: 30%;'; input.type = 'range'; input.min = '0'; input.max = '100'; input.value = sliderValue * 100; input.oninput = onSliderInput; input.classList.add('volume_slider_input'); innerDiv.appendChild(input); strong.innerText = `VOLUME ${input.value}%`; div.appendChild(strong); div.appendChild(innerDiv); return div; } function onSeekbarInput() { seeking = true; } function onSeekbarChange() { seeking = false; if (!window.player || !window.player.currentPlayingNode || !window.player.currentPlayingNode.modulePtr) return; const seconds = document.querySelector('.seekbar_input').value; window._openmpt_module_set_position_seconds(window.player.currentPlayingNode.modulePtr, seconds); } function onSeekbarMouseUp() { seeking = false; } function buildSeekbar() { const div = document.createElement('div'); div.id = 'seekbar'; div.classList.add('center'); div.style = 'margin-top: 15px;'; const strong = document.createElement('strong'); strong.style = 'font-size: 19px; color: #a9b7c6;'; strong.id = 'seekbar_text'; strong.appendChild(document.createTextNode('00:00 / 00:00')); const innerDiv = document.createElement('div'); const input = document.createElement('input'); input.style = 'width: 30%;'; input.type = 'range'; input.min = '0'; input.max = '0'; input.value = 0; input.oninput = onSeekbarInput; input.onchange = onSeekbarChange; input.mouseup = onSeekbarMouseUp; input.classList.add('seekbar_input'); innerDiv.appendChild(input); div.appendChild(strong); div.appendChild(innerDiv); return div; } function secondsToMinutes(seconds) { seconds = Math.floor(seconds); const minutes = Math.floor(seconds / 60); const remaining_seconds = seconds % 60; return `${minutes.toString().padStart(2, '0')}:${remaining_seconds.toString().padStart(2, '0')}`; } let times_played = 0; function loopObserver() { if (!window.player.currentPlayingNode || !window.player.currentPlayingNode.modulePtr) { times_played = 0; setTimeout(loopObserver, 100); return; } // eslint-disable-next-line const song_length = _openmpt_module_get_duration_seconds(window.player.currentPlayingNode.modulePtr); // eslint-disable-next-line const song_position = _openmpt_module_get_position_seconds(window.player.currentPlayingNode.modulePtr); if (!seeking) { document.querySelector('.seekbar_input').value = Math.floor(song_position % song_length); } const seekbar_text = document.querySelector('#seekbar_text'); seekbar_text.innerText = `${secondsToMinutes(song_position % song_length)} / ${secondsToMinutes(song_length)}`; const played = Math.floor(song_position / song_length); if (played > times_played && repeatCount === -1) { times_played = played; window.user.getTracksPlayed(function (num) { const new_count = parseInt(num, 10) + 1; window.model.write('tracks_played', new_count); window.UI.renderTracksPlayed(new_count); window.watcher.handleTotalPlayed(); }); } setTimeout(loopObserver, 100); } function hookSearch() { if(!window.watcher || !window.watcher.search || !window.search_input) { setTimeout(hookSearch, 100); return; } const old_search = window.watcher.search; const hook_search = function(val) { window.UI.renderPlaylist(window.playList.setSelected(val), val); window.playList.setCurrentPlayList(window.UI.getShuffleBtnStatus()); }; window.watcher.search = hook_search; window.search_input.oninput = function() { hook_search(window.search_input.value); }; console.log('[Hooked] window.watcher.search'); } let playlistObject = false; let playlistConfig = {}; let playlistSongs = []; function hookPlaylist() { if (!window.UI || !window.UI.renderPlaylist || !window.playList.song_lib || !window.playList.song_lib.length) { setTimeout(hookPlaylist, 1); return; } const hookVirtualList = function(playlist) { playlistSongs = playlist; if (window.__hookedVirtualList) { playlistConfig.total = playlistSongs.length; playlistObject.refresh(document.querySelector('#playlist table tbody'), playlistConfig); return; } document.head.insertAdjacentHTML('beforeend', ``); const table = document.querySelector('#playlist table'); const table_body = document.createElement('tbody'); playlistConfig = { itemHeight: 60, total: playlistSongs.length, useFragment: true, overrideScrollPosition() { return document.querySelector('.scroller').scrollTop; }, generate(index) { const song = playlistSongs[index]; // Outer track element const track_element = document.createElement('tr'); track_element.classList.add('playlist_track'); track_element.setAttribute('data-path', song.path); track_element.style.width = '100%'; if (currentSong === song.path) { track_element.classList.add('now_playing'); } /* Track Number */ const track_number = document.createElement('td'); track_number.classList.add('track_number'); track_number.innerText = song.n; /* ------------ */ /* Track */ const track = document.createElement('td'); track.classList.add('track'); const author = document.createElement('span'); author.classList.add('rg'); author.innerText = song.rg; const name = document.createElement('span'); name.classList.add('soft'); name.innerText = song.sn; const metadata = document.createElement('span'); metadata.classList.add('mdt'); metadata.classList.add('time'); metadata.innerHTML = song.mdt || '​'; track.appendChild(author); track.appendChild(document.createElement('br')); track.appendChild(name); track.appendChild(document.createElement('br')); track.appendChild(metadata); /* ----- */ /* Favorite */ const favorite = document.createElement('td'); favorite.setAttribute('title', 'Favorite'); favorite.classList.add('heart'); if (window.playList.isFavoriteSong(song.path)) { favorite.classList.add('favorite'); } const heart_span = document.createElement('span'); heart_span.innerHTML = '❤'; favorite.appendChild(heart_span); /* -------- */ track_element.appendChild(track_number); track_element.appendChild(track); track_element.appendChild(favorite); return track_element; } }; playlistObject = window.HyperList.create(table_body, playlistConfig); table.appendChild(table_body); window.__hookedVirtualList = true; }; const old_renderPlaylist = window.UI.renderPlaylist; const hook_renderPlaylist = function(playlist, listName) { hookVirtualList(playlist); console.log(`Loaded playlist "${listName}" with ${playlist.length} songs.`); }; window.UI.renderPlaylist = hook_renderPlaylist; const table = document.querySelector('#playlist table'); if (table.firstChild) { while(table.firstChild) { table.firstChild.remove(); } } window.UI.renderPlaylist(window.playList.song_lib, ''); window.playList.setSelected(''); const old_scrollToSongInPlaylist = window.UI.scrollToSongInPlaylist; const hook_scrollToSongInPlaylist = function(song) { let id = -1; for (let i = 0; i < window.playList.selected.length; i++) { const _s = window.playList.selected[i]; if (song === _s.path) { id = i; break; } } if (id === -1) return; window.$scroller.scrollTop(id * 60); }; window.UI.scrollToSongInPlaylist = hook_scrollToSongInPlaylist; const old_updateSongInfo = window.watcher.updateSongInfo; const hook_updateSongInfo = function(song) { old_updateSongInfo(song); currentSong = song; document.querySelector('.seekbar_input').value = 0; }; window.watcher.updateSongInfo = hook_updateSongInfo; const observer = new MutationObserver(function(mutationsList, observer) { for(const mutation of mutationsList) { if (mutation.type === 'childList') { for (const tr of mutation.addedNodes) { if(!tr.hasAttribute('data-path')) continue; if(tr.getAttribute('data-path') === currentSong) { tr.classList.add('now_playing'); } else { tr.classList.remove('now_playing'); } } } } }); observer.observe(document.querySelector('#playlist table tbody'), { attributes: false, childList: true, subtree: false }); } function hookShuffle() { if (!window.shuffle || !window.playList || !window.UI) { setTimeout(hookShuffle, 1); return; } window.shuffle.addEventListener('change', () => { localStorage.setItem('shuffle', window.shuffle.checked); }); /* Get LocalStorage Shuffle Value */ const _shuffle_status = localStorage.getItem('shuffle'); if (!!_shuffle_status) { window.shuffle.checked = _shuffle_status === 'true'; window.playList.setCurrentPlayList(window.UI.getShuffleBtnStatus()); } } function inject() { const player_container = document.querySelector('#player .container'); if (!player_container || !window.ChiptuneJsPlayer) { setTimeout(inject, 100); return; } // Add new method to this bad boy window.ChiptuneJsPlayer.prototype.reloadConfig = function(config, processNode) { if (!processNode || !processNode.modulePtr) return; // eslint-disable-next-line _openmpt_module_set_repeat_count(processNode.modulePtr, config.repeatCount); // eslint-disable-next-line _openmpt_module_set_render_param(processNode.modulePtr, OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT, config.stereoSeparation); // eslint-disable-next-line _openmpt_module_set_render_param(processNode.modulePtr, OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH, config.interpolationFilter); }; const old_ChiptuneJsPlayer_play = window.ChiptuneJsPlayer.prototype.play; window.ChiptuneJsPlayer.prototype.play = function(buffer) { old_ChiptuneJsPlayer_play.call(this, buffer); document.querySelector('.seekbar_input').max = Math.floor(window._openmpt_module_get_duration_seconds(window.player.currentPlayingNode.modulePtr)); }; // Hook search hookSearch(); // Hook playlist hookPlaylist(); // Observe loops loopObserver(); // Hook shuffle button hookShuffle(); // Add Loop Button player_container.appendChild(buildLoopButton()); // Add Volume Slider player_container.appendChild(buildVolumeSlider()); // Add Seekbar player_container.appendChild(buildSeekbar()); setInterval(updateGainElement, 10); } setTimeout(inject, 100); })();