diff --git a/README.md b/README.md index a04954838..749be92aa 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ # Bilibili-Evolved 增强哔哩哔哩Web端体验的油猴脚本. -(未完成) + +作为[原脚本](bilibili-touch.js)的工程化形式,工程化目前未完成,完成之前可直接以使用原脚本. diff --git a/bilibili-evolved.js b/bilibili-evolved.js index a3004ac0d..a9073f0ec 100644 --- a/bilibili-evolved.js +++ b/bilibili-evolved.js @@ -8,6 +8,7 @@ // @match *://*.bilibili.com // @grant unsafeWindow // @require https://static.hdslb.com/js/jquery.min.js +// @require https://raw.githubusercontent.com/the1812/Bilibili-Evolved/master/utils/common.min.js // ==/UserScript== (self$ => @@ -71,386 +72,17 @@ // [New Styles -> Override Nav Bar] // show top banner showBanner: true - // [Not used] - // (Experimental) use dominant color of banner image - // useDominantColor: false }; for (const key in userSettings) { settings[key] = userSettings[key]; } - const $ = unsafeWindow.$ || self$; - const waitForQuery = () => - { - const MaxRetry = settings.maxQueryRetry; - let retry = 0; - const tryQuery = (query, condition, action, failed) => - { - if (retry >= MaxRetry) - { - if (failed) - { - failed(); - } - } - else - { - const result = query(); - if (condition(result)) - { - action(result); - } - else - { - retry++; - setTimeout(() => tryQuery(query, condition, action, failed), settings.queryInterval); - } - } - }; - return tryQuery; - }; const getEventHandler = (element, event) => { return element.data("events")[event][0].handler; }; - const getPosition = element => - { - let x = 0; - let y = 0; - while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) - { - x += element.offsetLeft - element.scrollLeft; - y += element.offsetTop - element.scrollTop; - element = element.offsetParent; - } - return { x: x, y: y }; - }; - class Swiper - { - constructor(element) - { - this.action = new SwipeAction(element); - this.onTouchStart = undefined; - this.onTouchEnd = undefined; - this._direction = null; - - element.addEventListener("touchstart", e => - { - this._xDown = e.touches[0].clientX; - this._yDown = e.touches[0].clientY; - if (this.onTouchStart) - { - this.onTouchStart(e); - } - }); - element.addEventListener("touchmove", e => - { - if (!this._xDown || !this._yDown) - { - return; - } - const xUp = e.touches[0].clientX; - const yUp = e.touches[0].clientY; - const elementPosition = getPosition(element); - const position = { - x: (e.touches[0].pageX - elementPosition.x) / element.clientWidth, - y: (e.touches[0].pageY - elementPosition.y) / element.clientHeight, - width: element.clientWidth, - height: element.clientHeight - }; - - const xDiff = this._xDown - xUp; - const yDiff = this._yDown - yUp; - - if (!this._direction) - { - let direction = ""; - if (Math.abs(xDiff) > Math.abs(yDiff)) - { - direction = "horizontal"; - } - else - { - direction = "vertical"; - } - this._direction = direction; - e.preventDefault(); - } - else - { - if (this._direction === "vertical") - { - this.action.startAction(this._direction, yDiff, position); - } - else if (this._direction === "horizontal") - { - this.action.startAction(this._direction, -xDiff, position); - } - e.preventDefault(); - } - - }); - element.addEventListener("touchend", e => - { - this._xDown = null; - this._yDown = null; - this._direction = null; - if (this.onTouchEnd) - { - this.onTouchEnd(e); - } - }); - } - } - class SwipeAction - { - constructor(element) - { - this.lowSpeedForward = undefined; - this.lowSpeedBackward = undefined; - this.mediumSpeedForward = undefined; - this.mediumSpeedBackward = undefined; - this.highSpeedForward = undefined; - this.highSpeedBackward = undefined; - - this.lowVolumeUp = undefined; - this.lowVolumeDown = undefined; - this.mediumVolumeUp = undefined; - this.mediumVolumeDown = undefined; - this.highVolumeUp = undefined; - this.highVolumeDown = undefined; - - this.speedCancel = undefined; - this.volumeCancel = undefined; - this.minSwipeDistance = 20; - this.onActionStart = undefined; - this.onActionEnd = undefined; - - this._element = element; - this._touchStart = false; - this._startPosition = null; - this._lastAction = null; - element.addEventListener("touchstart", () => - { - this._touchStart = true; - }); - element.addEventListener("touchend", () => - { - this._startPosition = null; - this.onActionEnd && this.onActionEnd(this._lastAction); - this._lastAction = null; - }); - } - - startAction(direction, distance, position) - { - if (this._touchStart) - { - this.onActionStart && this.onActionStart(); - this._startPosition = position; - this._touchStart = false; - } - if (direction === "vertical") - { - if (Math.abs(distance) < this.minSwipeDistance) - { - this.volumeCancel && this.volumeCancel(); - this._lastAction = null; - } - else - { - let volumeFactor = 0; - let upHandler = undefined; - let downHandler = undefined; - if (this._startPosition.x < 1 / 3) - { - volumeFactor = 0.4; - upHandler = this.lowVolumeUp; - downHandler = this.lowVolumeDown; - } - else if (this._startPosition.x >= 1 / 3 && this._startPosition.x <= 2 / 3) - { - volumeFactor = 1; - upHandler = this.mediumVolumeUp; - downHandler = this.mediumVolumeDown; - } - else - { - volumeFactor = 2; - upHandler = this.highVolumeUp; - downHandler = this.highVolumeDown; - } - - if (distance > 0) - { - const volumeChange = Math.round( - volumeFactor * 100 * (distance - this.minSwipeDistance) / (1.5 * position.height) - ); - upHandler && upHandler(volumeChange); - this._lastAction = { - type: "volume", - volume: volumeChange - }; - } - else - { - const volumeChange = Math.round( - volumeFactor * 100 * (distance + this.minSwipeDistance) / (1.5 * position.height) - ); - downHandler && downHandler(volumeChange); - this._lastAction = { - type: "volume", - volume: volumeChange - }; - } - } - } - else if (direction === "horizontal") - { - if (position.y < 1 / 3 && (position.x < 0.1 || position.x > 0.9) || - Math.abs(distance) < this.minSwipeDistance) - { - this.speedCancel && this.speedCancel(); - this._lastAction = null; - } - else - { - let speedFactor = 0; - let forwardHandler = undefined; - let backwardHandler = undefined; - if (this._startPosition.y < 1 / 3) - { - speedFactor = 0.05; - forwardHandler = this.lowSpeedForward; - backwardHandler = this.lowSpeedBackward; - } - else if (this._startPosition.y >= 1 / 3 && this._startPosition.y <= 2 / 3) - { - speedFactor = 0.2; - forwardHandler = this.mediumSpeedForward; - backwardHandler = this.mediumSpeedBackward; - } - else - { - speedFactor = 1; - forwardHandler = this.highSpeedForward; - backwardHandler = this.highSpeedBackward; - } - - if (distance > 0) - { - const seconds = (distance - this.minSwipeDistance) * speedFactor; - forwardHandler && forwardHandler(seconds); - this._lastAction = { - type: "playback", - seconds: seconds - }; - } - else - { - const seconds = (distance + this.minSwipeDistance) * speedFactor; - backwardHandler && backwardHandler(seconds); - this._lastAction = { - type: "playback", - seconds: seconds - }; - } - } - } - } - } - class VideoShot - { - constructor(totalTime) - { - this.interval = totalTime < 5 * 100 ? 5 : totalTime / 100; - this.firstShot = { - css: { - backgroundPosition: "-160px 0px" - }, - x: -160, - y: 0, - position: 2 - }; - this.lastShot = { - css: { - backgroundPosition: "-1440px -810px" - }, - x: -1440, - y: -810, - position: 100 - }; - - this.imageXLength = 10; - this.imageYLength = 10; - this.imageXSize = 160; - this.imageYSize = 90; - this.imageUrl = null; - this.imageIndex = []; - } - init(input) - { - const matches = input.prop("value").match(/aid=([\d]+)&cid=([\d]+)/); - const aid = matches[1]; - const cid = matches[2]; - $.ajax({ - type: "GET", - url: `https://api.bilibili.com/x/player/videoshot?aid=${aid}&cid=${cid}&index=1`, - success: obj => - { - const data = obj.data; - this.imageXLength = data.img_x_len; - this.imageYLength = data.img_y_len; - this.imageXSize = data.img_x_size; - this.imageYSize = data.img_y_size; - this.imageIndex = data.index; - this.imageUrl = data.image[0]; - - this.firstShot = { - css: {}, - x: -this.imageXSize, - y: 0, - position: 1 - }; - this.firstShot.css.backgroundPosition = `${this.firstShot.x}px ${this.firstShot.y}px`; - this.lastShot = { - css: {}, - x: -this.imageXSize * (this.imageXLength - 1), - y: -this.imageYSize * (this.imageYLength - 1), - position: 99 - }; - this.lastShot.css.backgroundPosition = `${this.lastShot.x}px ${this.lastShot.y}px`; - - - } - }); - } - _getStyle(position) - { - if (position <= this.firstShot.position) - { - return this.firstShot.css; - } - else if (position >= this.lastShot.position) - { - return this.lastShot.css; - } - else - { - const x = -160 * (position % 10 - 1); - const y = -90 * (position >= 100 ? 9 : Math.trunc(position / 10)); - return { - backgroundPosition: `${x}px ${y}px` - }; - } - } - getStyle(time) - { - const position = Math.round(time / interval); - return this._getStyle(position); - } - } + $(document).ready(() => { diff --git a/bilibili-touch.js b/bilibili-touch.js new file mode 100644 index 000000000..438304a73 --- /dev/null +++ b/bilibili-touch.js @@ -0,0 +1,3039 @@ +// ==UserScript== +// @name Bilibili Touch +// @namespace http://tampermonkey.net/ +// @version 3.4 +// @description Touch optimizations and UI improvements for Bilibili +// @author Grant Howard +// @match *://*.bilibili.com/* +// @match *://*.bilibili.com +// @grant unsafeWindow +// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAH0mlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIiB4bXA6Q3JlYXRlRGF0ZT0iMjAxOC0wMi0yNVQxNDo1NzozOCswODowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOC0wMi0yNVQxNDo1NzozOCswODowMCIgeG1wOk1vZGlmeURhdGU9IjIwMTgtMDItMjVUMTQ6NTc6MzgrMDg6MDAiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODY3MDMzY2UtZjdlMy0wYTRiLWE5YWItODE3ZTI2ZmNlYTMyIiB4bXBNTTpEb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6YWFhN2UzZTQtM2MzOS0yOTQ4LWI1OTgtYTEzM2ZjMTMxNDMyIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6YjRjNGFjZWUtZjQyYS0yMTQwLTlmMzgtY2NlZTc3YmY2ZTM1IiBkYzpmb3JtYXQ9ImltYWdlL3BuZyIgcGhvdG9zaG9wOkhpc3Rvcnk9IjIwMTgtMDItMjVUMTQ6NTc6MTArMDg6MDAmI3g5O+aWh+S7tiDmnKrmoIfpopgtMiDlt7LmiZPlvIAmI3hBO+W7uueriyYjeDk75paw5bu6OiDmlofmoaMmI3hBO0ZBTFNFJiN4QTvmqKHlvI86IFJHQiDpopzoibLmqKHlvI8mI3hBO+WuveW6pjogMi41IOiLseWvuCYjeEE76auY5bqmOiAyLjUg6Iux5a+4JiN4QTvmr4/oi7Hlr7gg5YiG6L6o546HOiA3MiYjeEE75YOP57Sg6ZW/5a695q+UOiAxJiN4QTvloavlhYU6IOmAj+aYjiYjeEE75rex5bqmOiA4JiN4QTvphY3nva7mlofku7Y6IOKAnG5vbmXigJ0mI3hBO+WPguiAg+e6vzog5pegJiN4QTsyMTcmI3hBOyYjeEE757KY6LS0JiN4QTvnspjotLQmI3g5O+a2iOmZpOmUr+m9vzog5pegJiN4QTvkuLo6IOWDj+e0oCYjeEE7JiN4QTsyMDE4LTAyLTI1VDE0OjU3OjM4KzA4OjAwJiN4OTvmlofku7YgQzpcVXNlcnNcVGhlMThcUGljdHVyZXNcR3JhcGhpY3NcYmlsaWJpbGkgbG9nbyBzbWFsbC5wbmcg5bey5a2Y5YKoJiN4QTvlrZjlgqgmI3g5OyIgcGhvdG9zaG9wOkNvbG9yTW9kZT0iMyI+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6YjRjNGFjZWUtZjQyYS0yMTQwLTlmMzgtY2NlZTc3YmY2ZTM1IiBzdEV2dDp3aGVuPSIyMDE4LTAyLTI1VDE0OjU3OjM4KzA4OjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgKFdpbmRvd3MpIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIg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 +// @require https://static.hdslb.com/js/jquery.min.js +// @require https://raw.githubusercontent.com/briangonzalez/rgbaster.js/master/rgbaster.min.js +// ==/UserScript== + +(self$ => +{ + const colors = { + red: "#E53935", + pink: "#F06292", + purple: "#AB47BC", + deepPurple: "#7E57C2", + indigo: "#7986CB", + blue: "#1E88E5", + lightBlue: "#00A0D8", + cyan: "#00ACC1", + teal: "#26A69A", + green: "#66BB6A", + lightGreen: "#8BC34A", + lime: "#CDDC39", + yellow: "#FFEB3B", + amber: "#FFC107", + orange: "#FF9800", + deepOrange: "#FF5722", + brown: "#795548", + grey: "#757575", + blueGrey: "#607D8B" + }; + // User settings will overwrite default settings below + const userSettings = { + customStyleColor: colors.pink, + showBanner: false, + useDarkMode: true + }; + const settings = { + // remove ads + removeAds: true, + // max retry count used for query elements + maxQueryRetry: 30, + // query retry interval (ms) + queryInterval: 500, + // touch support for nav bar (not compatible with Edge) + touchNavBar: true, + // (Experimental) touch support for video player + touchVideoPlayer: true, + // redirect to original sites in watchlater list + watchLaterRedirect: true, + // auto expand danmaku list + expandDanmakuList: true, + // use new styles for nav bar and player + useNewStyles: true, + // [New Styles] + // set theme color (must in #rrggbb format, not compatible with Edge) + customStyleColor: colors.pink, + // [New Styles] + // set background blur opacity of nav bar + blurBackgroundOpacity: 0.382, + // [New Styles] + // (Not Implemented) use dark mode + useDarkMode: true, + // [New Styles] + // (Experimental) use new nav bar in old sites + overrideNavBar: true, + // [New Styles -> Override Nav Bar] + // show top banner + showBanner: true + // [Not used] + // (Experimental) use dominant color of banner image + // useDominantColor: false + }; + for (const key in userSettings) + { + settings[key] = userSettings[key]; + } + + const $ = unsafeWindow.$ || self$; + const waitForQuery = () => + { + const MaxRetry = settings.maxQueryRetry; + let retry = 0; + const tryQuery = (query, condition, action, failed) => + { + if (retry >= MaxRetry) + { + if (failed) + { + failed(); + } + } + else + { + const result = query(); + if (condition(result)) + { + action(result); + } + else + { + retry++; + setTimeout(() => tryQuery(query, condition, action, failed), settings.queryInterval); + } + } + }; + return tryQuery; + }; + const getEventHandler = (element, event) => + { + return element.data("events")[event][0].handler; + }; + const getPosition = element => + { + let x = 0; + let y = 0; + while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) + { + x += element.offsetLeft - element.scrollLeft; + y += element.offsetTop - element.scrollTop; + element = element.offsetParent; + } + return { x: x, y: y }; + }; + class Swiper + { + constructor(element) + { + this.action = new SwipeAction(element); + this.onTouchStart = undefined; + this.onTouchEnd = undefined; + this._direction = null; + + element.addEventListener("touchstart", e => + { + this._xDown = e.touches[0].clientX; + this._yDown = e.touches[0].clientY; + if (this.onTouchStart) + { + this.onTouchStart(e); + } + }); + element.addEventListener("touchmove", e => + { + if (!this._xDown || !this._yDown) + { + return; + } + const xUp = e.touches[0].clientX; + const yUp = e.touches[0].clientY; + const elementPosition = getPosition(element); + const position = { + x: (e.touches[0].pageX - elementPosition.x) / element.clientWidth, + y: (e.touches[0].pageY - elementPosition.y) / element.clientHeight, + width: element.clientWidth, + height: element.clientHeight + }; + + const xDiff = this._xDown - xUp; + const yDiff = this._yDown - yUp; + + if (!this._direction) + { + let direction = ""; + if (Math.abs(xDiff) > Math.abs(yDiff)) + { + direction = "horizontal"; + } + else + { + direction = "vertical"; + } + this._direction = direction; + e.preventDefault(); + } + else + { + if (this._direction === "vertical") + { + this.action.startAction(this._direction, yDiff, position); + } + else if (this._direction === "horizontal") + { + this.action.startAction(this._direction, -xDiff, position); + } + e.preventDefault(); + } + + }); + element.addEventListener("touchend", e => + { + this._xDown = null; + this._yDown = null; + this._direction = null; + if (this.onTouchEnd) + { + this.onTouchEnd(e); + } + }); + } + } + class SwipeAction + { + constructor(element) + { + this.lowSpeedForward = undefined; + this.lowSpeedBackward = undefined; + this.mediumSpeedForward = undefined; + this.mediumSpeedBackward = undefined; + this.highSpeedForward = undefined; + this.highSpeedBackward = undefined; + + this.lowVolumeUp = undefined; + this.lowVolumeDown = undefined; + this.mediumVolumeUp = undefined; + this.mediumVolumeDown = undefined; + this.highVolumeUp = undefined; + this.highVolumeDown = undefined; + + this.speedCancel = undefined; + this.volumeCancel = undefined; + this.minSwipeDistance = 20; + this.onActionStart = undefined; + this.onActionEnd = undefined; + + this._element = element; + this._touchStart = false; + this._startPosition = null; + this._lastAction = null; + element.addEventListener("touchstart", () => + { + this._touchStart = true; + }); + element.addEventListener("touchend", () => + { + this._startPosition = null; + this.onActionEnd && this.onActionEnd(this._lastAction); + this._lastAction = null; + }); + } + + startAction(direction, distance, position) + { + if (this._touchStart) + { + this.onActionStart && this.onActionStart(); + this._startPosition = position; + this._touchStart = false; + } + if (direction === "vertical") + { + if (Math.abs(distance) < this.minSwipeDistance) + { + this.volumeCancel && this.volumeCancel(); + this._lastAction = null; + } + else + { + let volumeFactor = 0; + let upHandler = undefined; + let downHandler = undefined; + if (this._startPosition.x < 1 / 3) + { + volumeFactor = 0.4; + upHandler = this.lowVolumeUp; + downHandler = this.lowVolumeDown; + } + else if (this._startPosition.x >= 1 / 3 && this._startPosition.x <= 2 / 3) + { + volumeFactor = 1; + upHandler = this.mediumVolumeUp; + downHandler = this.mediumVolumeDown; + } + else + { + volumeFactor = 2; + upHandler = this.highVolumeUp; + downHandler = this.highVolumeDown; + } + + if (distance > 0) + { + const volumeChange = Math.round( + volumeFactor * 100 * (distance - this.minSwipeDistance) / (1.5 * position.height) + ); + upHandler && upHandler(volumeChange); + this._lastAction = { + type: "volume", + volume: volumeChange + }; + } + else + { + const volumeChange = Math.round( + volumeFactor * 100 * (distance + this.minSwipeDistance) / (1.5 * position.height) + ); + downHandler && downHandler(volumeChange); + this._lastAction = { + type: "volume", + volume: volumeChange + }; + } + } + } + else if (direction === "horizontal") + { + if (position.y < 1 / 3 && (position.x < 0.1 || position.x > 0.9) || + Math.abs(distance) < this.minSwipeDistance) + { + this.speedCancel && this.speedCancel(); + this._lastAction = null; + } + else + { + let speedFactor = 0; + let forwardHandler = undefined; + let backwardHandler = undefined; + if (this._startPosition.y < 1 / 3) + { + speedFactor = 0.05; + forwardHandler = this.lowSpeedForward; + backwardHandler = this.lowSpeedBackward; + } + else if (this._startPosition.y >= 1 / 3 && this._startPosition.y <= 2 / 3) + { + speedFactor = 0.2; + forwardHandler = this.mediumSpeedForward; + backwardHandler = this.mediumSpeedBackward; + } + else + { + speedFactor = 1; + forwardHandler = this.highSpeedForward; + backwardHandler = this.highSpeedBackward; + } + + if (distance > 0) + { + const seconds = (distance - this.minSwipeDistance) * speedFactor; + forwardHandler && forwardHandler(seconds); + this._lastAction = { + type: "playback", + seconds: seconds + }; + } + else + { + const seconds = (distance + this.minSwipeDistance) * speedFactor; + backwardHandler && backwardHandler(seconds); + this._lastAction = { + type: "playback", + seconds: seconds + }; + } + } + } + } + } + class VideoShot + { + constructor(totalTime) + { + this.interval = totalTime < 5 * 100 ? 5 : totalTime / 100; + this.firstShot = { + css: { + backgroundPosition: "-160px 0px" + }, + x: -160, + y: 0, + position: 2 + }; + this.lastShot = { + css: { + backgroundPosition: "-1440px -810px" + }, + x: -1440, + y: -810, + position: 100 + }; + + this.imageXLength = 10; + this.imageYLength = 10; + this.imageXSize = 160; + this.imageYSize = 90; + this.imageUrl = null; + this.imageIndex = []; + } + init(input) + { + const matches = input.prop("value").match(/aid=([\d]+)&cid=([\d]+)/); + const aid = matches[1]; + const cid = matches[2]; + $.ajax({ + type: "GET", + url: `https://api.bilibili.com/x/player/videoshot?aid=${aid}&cid=${cid}&index=1`, + success: obj => + { + const data = obj.data; + this.imageXLength = data.img_x_len; + this.imageYLength = data.img_y_len; + this.imageXSize = data.img_x_size; + this.imageYSize = data.img_y_size; + this.imageIndex = data.index; + this.imageUrl = data.image[0]; + + this.firstShot = { + css: {}, + x: -this.imageXSize, + y: 0, + position: 1 + }; + this.firstShot.css.backgroundPosition = `${this.firstShot.x}px ${this.firstShot.y}px`; + this.lastShot = { + css: {}, + x: -this.imageXSize * (this.imageXLength - 1), + y: -this.imageYSize * (this.imageYLength - 1), + position: 99 + }; + this.lastShot.css.backgroundPosition = `${this.lastShot.x}px ${this.lastShot.y}px`; + + + } + }); + } + _getStyle(position) + { + if (position <= this.firstShot.position) + { + return this.firstShot.css; + } + else if (position >= this.lastShot.position) + { + return this.lastShot.css; + } + else + { + const x = -160 * (position % 10 - 1); + const y = -90 * (position >= 100 ? 9 : Math.trunc(position / 10)); + return { + backgroundPosition: `${x}px ${y}px` + }; + } + } + getStyle(time) + { + const position = Math.round(time / interval); + return this._getStyle(position); + } + } + + $(document).ready(() => + { + // Remove ads + if (settings.removeAds) + { + waitForQuery()( + () => $("#slide_ad"), + it => it.length > 0, + it => it.css("display", "none") + ); + waitForQuery()( + () => $("#home_popularize"), + it => it.length > 0, + it => it.css("display", "none") + ); + waitForQuery()( + () => $(".gg-floor-module"), + it => it.length > 0, + it => it.css("display", "none") + ); + } + + // Touch nav bar + if (settings.touchNavBar) + { + waitForQuery()( + () => $("ul.fr>li.nav-item").not(".profile-info"), + it => it.length === 6, + navItems => + { + const navTouch = (_, nav) => + { + const $nav = $(nav); + $nav.css("cursor", "pointer"); + const a = $nav.find("a.t"); + a.removeAttr("href"); + }; + navItems.each(navTouch); + } + ); + } + + // Touch video player + const touchVideoPlayer = () => + { + waitForQuery()( + () => $(".bilibili-player-video-web-fullscreen"), + it => it.length > 0, + fullscreenButton => + { + if (!fullscreenButton.hasClass("bilibili-player-video-btn") && + $(".bilibili-player-video-btn-fullscreen").data("events")) + { + const clickHandler = getEventHandler( + $(".bilibili-player-video-btn-fullscreen"), "click"); + fullscreenButton + .detach() + .insertAfter(".bilibili-player-video-btn-widescreen") + .addClass("bilibili-player-video-btn") + .on("click", clickHandler); + } + } + ); + waitForQuery()( + () => $(".bilibili-player-iconfont,.bilibili-player-video-quality-menu"), + it => it.length > 0, + icons => icons.unbind("click") + ); + waitForQuery()( + () => $(".bilibili-player-video"), + it => it.length > 0 && $("video").length > 0, + player => + { + if ($(".touch-video-box").length === 0) + { + $(".bilibili-player-video-subtitle").before(`