//@ts-check import JadefinIntegrity from '../JadefinIntegrity.js'; import JadefinMod from "../JadefinMod.js"; import JadefinModules from "../JadefinModules.js"; import JadefinUtils from "../JadefinUtils.js"; export default JadefinIntegrity("VolumeBoost", import.meta.url, () => new (class VolumeBoost extends JadefinMod { audioCtx = new AudioContext(); audioBypass = this.audioCtx.createGain(); audioGain = this.audioCtx.createGain(); audioComp = this.audioCtx.createDynamicsCompressor(); audioCompGain = this.audioCtx.createGain(); _currentGain = 1; constructor() { super(); this.audioBypass.gain.value = 1; this.audioGain.gain.value = 0; this.audioComp.knee.value = 40; this.audioComp.ratio.value = 12; this.audioComp.attack.value = 0; this.audioComp.release.value = 0.25; this.audioCompGain.gain.value = 0; this.audioBypass.connect(this.audioCtx.destination); this.audioGain.connect(this.audioComp).connect(this.audioCompGain).connect(this.audioCtx.destination); document.addEventListener("click", e => this.audioCtxResume(), true); } get currentGain() { return this._currentGain; } set currentGain(value) { this.log.i(`Changing gain to ${value}`); const diff = Math.abs(this._currentGain - value); const timeNow = this.audioCtx.currentTime; const time = this.audioCtx.currentTime + Math.min(0.5 * diff, 2); if (diff != 0) { this.audioBypass.gain.setValueAtTime(this.audioBypass.gain.value, timeNow); this.audioGain.gain.setValueAtTime(this.audioGain.gain.value, timeNow); this.audioCompGain.gain.setValueAtTime(this.audioCompGain.gain.value, timeNow); this.audioBypass.gain.linearRampToValueAtTime((value <= 1) ? value : 0, time); this.audioGain.gain.linearRampToValueAtTime((value <= 1) ? 0 : value, time); this.audioCompGain.gain.linearRampToValueAtTime((value <= 1) ? 0 : 1, time); } this._currentGain = value; this.connect(); } async init(name, url) { await super.init(name, url); this.initHookActionSheetShow(); document.addEventListener("viewshow", () => { if (JadefinUtils.routePathIsVideo) { this.log.i("Navigating to video"); this.audioCtxResume(); if (this._connectRepeat) { clearInterval(this._connectRepeat); } clearInterval(this._connectRepeat); this._connectRepeat = setInterval(() => this.connect(), 100); } }); this.log.i("Ready"); } initHookActionSheetShow() { const orig = this._actionSheetShowOrig = JadefinModules.actionSheet.show.bind(JadefinModules.actionSheet); JadefinModules.actionSheet.show = (options) => { const optionsOrig = Object.assign({}, options, { items: options.items.slice() }); this.log.v("actionSheet.show(...)"); this.log.dir(options); // Options menu during playback if (options.items.length >= 6 && options.items[0].id == "aspectratio" && options.items[1].id == "playbackrate" && options.items[2].id == "quality" && options.items[3].id == "repeatmode" && options.items[4].id == "suboffset" && options.items[5].id == "stats" && true ) { options.items.splice(4, 0, {id: "custom-volumeboost", name: "Volume Boost", asideText: `${this.currentGain}x`}); return new Promise((resolve, reject) => { JadefinModules.actionSheet.show({ positionTo: options.positionTo, items: [ {id: "all", name: "Advanced..."}, {divider: true}, ...options.items.slice(4), ] }).then(id => { if (id == "all") { orig(Object.assign({}, options, { items: [ ...options.items.slice(0, 4), {divider: true}, {id: "back", name: "Back..."}, ] })).then(id => { if (id == "back") { JadefinModules.actionSheet.show(optionsOrig).then(resolve).catch(reject); } else { resolve(id); } }).catch(reject); return; } if (id == "custom-volumeboost") { reject(); JadefinModules.actionSheet.show({ positionTo: options.positionTo, items: [ {id: "1", name: "1x", selected: this.currentGain == 1}, {id: "2", name: "2x", selected: this.currentGain == 2}, {id: "3", name: "3x", selected: this.currentGain == 3}, {id: "4", name: "4x", selected: this.currentGain == 4}, ] }).then(id => { if (!id) { return; } this.currentGain = parseInt(id); }); return; } resolve(id); }).catch(reject); }); } return orig(options); }; } connect() { const video = JadefinUtils.video; if (!video) { return; } const last = this.video; const lastSrc = this.videoSrc; if (video != last) { lastSrc?.disconnect(); this.videoSrc = null; } this.video = video; if (!this.videoSrc) { this.log.i("Connected to video element"); this.log.dir(this.video); this.videoSrc = this.audioCtx.createMediaElementSource(this.video); this.videoSrc.connect(this.audioBypass); this.videoSrc.connect(this.audioGain); } if (this._connectRepeat) { clearInterval(this._connectRepeat); } } audioCtxResume() { if (this.audioCtx.state === "suspended") { this.audioCtx.resume(); } } })());