// ==UserScript== // @name AnimePahe Improvements // @namespace https://gist.github.com/Ellivers/f7716b6b6895802058c367963f3a2c51 // @match https://animepahe.com/* // @match https://animepahe.org/* // @match https://animepahe.ru/* // @match https://kwik.*/e/* // @match https://kwik.*/f/* // @grant GM_getValue // @grant GM_setValue // @version 4.1.0 // @author Ellivers // @license MIT // @description Improvements and additions for the AnimePahe site // @downloadURL https://update.greasyfork.icu/scripts/520048/AnimePahe%20Improvements.user.js // @updateURL https://update.greasyfork.icu/scripts/520048/AnimePahe%20Improvements.meta.js // ==/UserScript== /* How to install: * Get the Violentmonkey browser extension (Tampermonkey is largely untested, but seems to work as well). * For the GitHub Gist page, click the "Raw" button on this page. * For Greasy Fork, click "Install this script". * I highly suggest using an ad blocker (uBlock Origin is recommended) Feature list: * Automatically redirects to the correct session when a tab with an old session is loaded. No more having to search for the anime and find the episode again! * Saves your watch progress of each video, so you can resume right where you left off. * Bookmark anime and view it in a bookmark menu. * Add ongoing anime to an episode feed to easily check when new episodes are out. * Quickly visit the download page for a video, instead of having to wait 5 seconds when clicking the download link. * Find collections of anime series in the search results, with the series listed in release order. * Jump directly to the next anime's first episode from the previous anime's last episode, and the other way around. * Hide all episode thumbnails on the site, for those who are extra wary of spoilers (and for other reasons). * Saved data can be viewed and deleted in the "Manage Data" menu. * Reworked anime index page. You can now: * Find anime with your desired genre, theme, type, demographic, status and season. * Search among these filter results. * Open a random anime within the specified filters. * Automatically finds a relevant cover for the top of anime pages. * Adds points in the video player progress bar for opening, ending, and other highlights (only available for some anime). * Adds a button to skip openings and endings when they start (only available for some anime). * Frame-by-frame controls on videos, using ',' and '.' * Skip 10 seconds on videos at a time, using 'J' and 'L' * Changes the video 'loop' keybind to Shift + L * Press Shift + N to go to the next episode, and Shift + P to go to the previous one. * Speed up or slow down a video by holding Ctrl and: * Scrolling up/down * Pressing the up/down keys * You can also hold shift to make the speed change more gradual. * Remembers the selected speed for each anime. * Enables you to see images from the video while hovering over the progress bar. * Allows you to also use numpad number keys to seek through videos. * Theatre mode for a better non-fullscreen video experience on larger screens. * Instantly loads the video instead of having to click a button to load it. * Adds an "Auto-Play Video" option to automatically play the video (on some browsers, you may need to allow auto-playing for this to work). * Adds an "Auto-Play Next" option to automatically go to the next episode when the current one is finished. * Focuses on the video player when loading the page, so you don't have to click on it to use keyboard controls. * Adds an option to automatically choose the highest quality available when loading the video. * Adds a button (in the settings menu) to reset the video player. * Shows the dates of when episodes were added. * And more! */ const baseUrl = window.location.toString(); const initialStorage = getStorage(); function getDefaultData() { return { version: 2, linkList:[], videoTimes:[], bookmarks:[], notifications: { lastUpdated: Date.now(), anime: [], episodes: [] }, badCovers: [], settings: { autoDelete:true, hideThumbnails:false, theatreMode:false, bestQuality:true, autoDownload:true, autoPlayNext:false, autoPlayVideo:false, seekThumbnails:true, seekPoints:true, skipButton:true, reduceMotion:false }, videoSpeed: [] }; } function upgradeData(data, fromver) { if (fromver === undefined) { fromver = 0; } const defaultVer = getDefaultData().version; if (fromver >= defaultVer) return; console.log(`[AnimePahe Improvements] Upgrading data from version ${fromver}`); /* Changes: * V1: * autoPlay -> autoPlayNext * v2: * autoDelete -> settings.autoDelete * hideThumbnails -> settings.hideThumbnails * theatreMode -> settings.theatreMode * bestQuality -> settings.bestQuality * autoDownload -> settings.autoDownload * autoPlayNext -> settings.autoPlayNext * autoPlayVideo -> settings.autoPlayVideo * +videoSpeed */ const upgradeFunctions = [ () => { // for V0 data.autoPlayNext = data.autoPlay; delete data.autoPlay; }, () => { // for V1 const settings = {}; settings.autoDelete = data.autoDelete; settings.hideThumbnails = data.hideThumbnails; settings.theatreMode = data.theatreMode; settings.bestQuality = data.bestQuality; settings.autoDownload = data.autoDownload; settings.autoPlayNext = data.autoPlayNext; settings.autoPlayVideo = data.autoPlayVideo; data.settings = settings; delete data.autoDelete; delete data.hideThumbnails; delete data.theatreMode; delete data.bestQuality; delete data.autoDownload; delete data.autoPlayNext; delete data.autoPlayVideo; } ] for (let i = fromver; i < defaultVer; i++) { const fn = upgradeFunctions[i]; if (fn !== undefined) fn(); } data.version = defaultVer; } function getStorage() { const defa = getDefaultData(); const res = GM_getValue('anime-link-tracker', defa); const oldVersion = res.version; for (const key of Object.keys(defa)) { if (res[key] !== undefined) continue; res[key] = defa[key]; } for (const key of Object.keys(defa.settings)) { if (res.settings[key] !== undefined) continue; res.settings[key] = defa.settings[key]; } if (oldVersion !== defa.version) { upgradeData(res, oldVersion); saveData(res); } return res; } function saveData(data) { GM_setValue('anime-link-tracker', data); } function secondsToHMS(secs) { const mins = Math.floor(secs/60); const hrs = Math.floor(mins/60); const newSecs = Math.floor(secs % 60); return `${hrs > 0 ? hrs + ':' : ''}${mins % 60}:${newSecs.toString().length > 1 ? '' : '0'}${newSecs % 60}`; } function getStoredTime(name, ep, storage, id = undefined) { if (id !== undefined) { return storage.videoTimes.find(a => a.episodeNum === ep && a.animeId === id); } else return storage.videoTimes.find(a => a.animeName === name && a.episodeNum === ep); } function applyCssSheet(cssString) { $("head").append(''); const sheet = $("#anitracker-style")[0].sheet; const rules = cssString.split(/^\}/mg).map(a => a.replace(/\n/gm,'') + '}'); for (let i = 0; i < rules.length - 1; i++) { sheet.insertRule(rules[i], i); } } const kwikDLPageRegex = /^https:\/\/kwik\.\w+\/f\//; // Video player improvements if (/^https:\/\/kwik\.\w+/.test(baseUrl)) { if (typeof $ !== "undefined" && $() !== null) anitrackerKwikLoad(window.location.origin + window.location.pathname); else { const scriptElem = document.querySelector('head > link:nth-child(12)'); if (scriptElem == null) { const h1 = document.querySelector('h1'); // Some bug that the kwik DL page had before // (You're not actually blocked when this happens) if (!kwikDLPageRegex.test(baseUrl) && h1.textContent == "Sorry, you have been blocked") { h1.textContent = "Oops, page failed to load."; document.querySelector('h2').textContent = "This doesn't mean you're blocked. Try playing from another page instead."; } return; } scriptElem.onload(() => {anitrackerKwikLoad(window.location.origin + window.location.pathname)}); } function anitrackerKwikLoad(url) { if (kwikDLPageRegex.test(url)) { if (initialStorage.settings.autoDownload === false) return; $(`