Explorar o código

feat: reloadTab function

Sv443 hai 5 meses
pai
achega
0e35c6abc1
Modificáronse 6 ficheiros con 63 adicións e 12 borrados
  1. 6 5
      changelog.md
  2. 16 0
      contributing.md
  3. 7 3
      src/features/volume.ts
  4. 9 1
      src/interface.ts
  5. 5 1
      src/types.ts
  6. 20 2
      src/utils/misc.ts

+ 6 - 5
changelog.md

@@ -5,13 +5,14 @@
 ## 2.2.1
 - **Changes:**
   - The above-queue-button container's sticky positioning can now be turned off with a new advanced feature toggle in the config menu
+  - Made finding the preferred locale based on the browser's language settings more reliable
 
-<!-- <details><summary>Click to expand internal and plugin changes</summary>
+<details><summary>Click to expand internal and plugin changes</summary>
 
-- **Internal Changes:**
-  - 
+- **Plugin Changes:**
+  - Added authenticated function `reloadTab()` to keep video time and volume and disable BYTM features like initial tab volume
 
-</details> -->
+</details>
 
 <div class="pr-link-cont">
   <a href="https://github.com/Sv443/BetterYTM/pull/121" rel="noopener noreferrer">See pull request for more info</a>
@@ -41,7 +42,7 @@
     - `en-UK` -> `en-GB`
     - `ja-JA` -> `ja-JP`
   - Enabled Subresource Integrity (SRI) hashes for external resources to increase security
-- **Plugin Changes:**  
+- **Plugin Changes:**
   - Migration guide:
     - Since locale codes now have the format `xx-YY` and two were renamed, all plugins must implement those changes
 

+ 16 - 0
contributing.md

@@ -417,6 +417,7 @@ The usage and example blocks on each are written in TypeScript but can be used i
   - [getDomain()](#getdomain) - Returns the current domain of the page as a constant string (either "yt" or "ytm")
   - [getResourceUrl()](#getresourceurl) - Returns a `blob:` URL provided by the local userscript extension for the specified BYTM resource file
   - [getSessionId()](#getsessionid) - Returns the unique session ID that is generated on every started session
+  - [reloadTab()](#reloadtab) 🔒 - Reloads the current tab while preserving video time and volume and making features like initial tab volume lower priority
 - DOM:
   - [BytmDialog](#bytmdialog) - A class for creating and managing modal, fully customizable dialogs
   - [ExImDialog](#eximdialog) - Subclass of BytmDialog for allowing users to export and import serializable data
@@ -704,6 +705,21 @@ The usage and example blocks on each are written in TypeScript but can be used i
 
 <br>
 
+> ### reloadTab()
+> Usage:
+> ```ts
+> unsafeWindow.BYTM.reloadTab(token: string | undefined): Promise<void>
+> ```
+>  
+> Description:  
+> Reloads the current tab while preserving video time and volume and making features like initial tab volume lower priority.  
+> The tab will be reloaded, whether the video element is queryable in the DOM or not.  
+>   
+> Arguments:  
+> - `token` - The private token that was returned when the plugin was registered (if not provided, the function will always return `undefined`)
+
+<br>
+
 > ### setInnerHtml()
 > Usage:  
 > ```ts

+ 7 - 3
src/features/volume.ts

@@ -36,7 +36,7 @@ export async function initVolumeFeatures() {
 
     // the following are only run once:
 
-    if(getFeature("setInitialTabVolume"))
+    if(getFeature("setInitialTabVolume") || new URL(location.href).searchParams.has("bytm_volume"))
       setInitialTabVolume(sliderElem);
 
     if(typeof getFeature("volumeSliderSize") === "number")
@@ -249,7 +249,8 @@ export async function volumeSharedBetweenTabsDisabled() {
 /** Sets the volume slider to a set volume level when the session starts */
 async function setInitialTabVolume(sliderElem: HTMLInputElement) {
   await waitVideoElementReady();
-  const initialVol = getFeature("initialTabVolumeLevel");
+  const urlVol = new URL(location.href).searchParams.get("bytm_volume");
+  const initialVol = urlVol ? Number(urlVol) / 100 : getFeature("initialTabVolumeLevel");
   if(getFeature("volumeSharedBetweenTabs")) {
     lastCheckedSharedVolume = ignoreVal = initialVol;
     if(getFeature("volumeSharedBetweenTabs"))
@@ -257,5 +258,8 @@ async function setInitialTabVolume(sliderElem: HTMLInputElement) {
   }
   sliderElem.value = String(initialVol);
   sliderElem.dispatchEvent(new Event("change", { bubbles: true }));
-  log(`Set initial tab volume to ${initialVol}%`);
+
+  urlVol && history.replaceState({}, document.title, location.href.replace(/([&?]?bytm_volume=\d+($|&))/g, ""));
+
+  log(`Set initial tab volume to ${initialVol}%${urlVol ? " (from URL)" : "(from configuration)"}`);
 }

+ 9 - 1
src/interface.ts

@@ -1,7 +1,7 @@
 import * as UserUtils from "@sv443-network/userutils";
 import * as compareVersions from "compare-versions";
 import { mode, branch, host, buildNumber, compressionFormat, scriptInfo } from "./constants.js";
-import { getDomain, waitVideoElementReady, getResourceUrl, getSessionId, getVideoTime, log, setLocale, getLocale, hasKey, hasKeyFor, t, tp, type TrLocale, info, error, onInteraction, getThumbnailUrl, getBestThumbnailUrl, fetchVideoVotes, setInnerHtml, getCurrentMediaType, tl, tlp, PluginError, formatNumber } from "./utils/index.js";
+import { getDomain, waitVideoElementReady, getResourceUrl, getSessionId, getVideoTime, log, setLocale, getLocale, hasKey, hasKeyFor, t, tp, type TrLocale, info, error, onInteraction, getThumbnailUrl, getBestThumbnailUrl, fetchVideoVotes, setInnerHtml, getCurrentMediaType, tl, tlp, PluginError, formatNumber, reloadTab } from "./utils/index.js";
 import { addSelectorListener } from "./observers.js";
 import { getFeatures, setFeatures } from "./config.js";
 import { autoLikeStore, featInfo, fetchLyricsUrlTop, getLyricsCacheEntry, sanitizeArtists, sanitizeSong } from "./features/index.js";
@@ -109,6 +109,7 @@ const globalFuncs: InterfaceFunctions = {
   getDomain,
   getResourceUrl,
   getSessionId,
+  reloadTab: reloadTabInterface,
 
   // dom:
   setInnerHtml,
@@ -410,6 +411,13 @@ export function resolveToken(token: string | undefined): string | undefined {
 
 //#region proxy funcs
 
+/** Reloads the tab in a smarter way by preserving the current video's time and volume and making features like initial tab volume stand down if called */
+export async function reloadTabInterface(token: string | undefined) {
+  if(resolveToken(token) === undefined)
+    return;
+  return await reloadTab();
+}
+
 /**
  * Sets the new locale on the BYTM interface  
  * This is an authenticated function so you must pass the session- and plugin-unique token, retreived at registration.

+ 5 - 1
src/types.ts

@@ -4,7 +4,7 @@ import type { scriptInfo } from "./constants.js";
 import type { addSelectorListener } from "./observers.js";
 import type { getResourceUrl, getSessionId, getVideoTime, TrLocale, t, tp, fetchVideoVotes, onInteraction, getThumbnailUrl, getBestThumbnailUrl, getLocale, hasKey, hasKeyFor, getDomain, waitVideoElementReady, setInnerHtml, getCurrentMediaType, tl, tlp, formatNumber } from "./utils/index.js";
 import type { SiteEventsMap } from "./siteEvents.js";
-import type { InterfaceEventsMap, getAutoLikeDataInterface, getFeaturesInterface, getPluginInfo, saveAutoLikeDataInterface, saveFeaturesInterface, setLocaleInterface } from "./interface.js";
+import type { InterfaceEventsMap, getAutoLikeDataInterface, getFeaturesInterface, getPluginInfo, reloadTabInterface, saveAutoLikeDataInterface, saveFeaturesInterface, setLocaleInterface } from "./interface.js";
 import type { BytmDialog, ExImDialog, createCircularBtn, createHotkeyInput, createRipple, createToggleInput, showIconToast, showToast } from "./components/index.js";
 import type { fetchLyricsUrlTop, sanitizeArtists, sanitizeSong } from "./features/lyrics.js";
 import type { getLyricsCacheEntry } from "./features/lyricsCache.js";
@@ -266,6 +266,8 @@ export type PluginItem =
   }
   & Pick<PluginRegisterResult, "events">;
 
+//#region plugin interface
+
 /** All functions exposed by the interface on the global `BYTM` object */
 export type InterfaceFunctions = {
   // meta:
@@ -285,6 +287,8 @@ export type InterfaceFunctions = {
   getResourceUrl: typeof getResourceUrl;
   /** Returns the unique session ID for the current tab */
   getSessionId: typeof getSessionId;
+  /** Smarter version of `location.reload()` that remembers video time and volume and makes other features like initial tab volume stand down if used */
+  reloadTab: typeof reloadTabInterface;
 
   // dom:
   /** Sets the innerHTML property of the provided element to a sanitized version of the provided HTML string */

+ 20 - 2
src/utils/misc.ts

@@ -1,8 +1,8 @@
-import { compress, decompress, fetchAdvanced, openInNewTab, pauseFor, randomId, randRange, type Prettify } from "@sv443-network/userutils";
+import { clamp, compress, decompress, fetchAdvanced, openInNewTab, pauseFor, randomId, randRange, type Prettify } from "@sv443-network/userutils";
 import { marked } from "marked";
 import { branch, compressionFormat, repo, sessionStorageAvailable } from "../constants.js";
 import { type Domain, type NumberLengthFormat, type ResourceKey, type StringGen } from "../types.js";
-import { error, type TrLocale, warn, sendRequest, getLocale, log } from "./index.js";
+import { error, type TrLocale, warn, sendRequest, getLocale, log, getVideoElement, getVideoTime } from "./index.js";
 import { getFeature } from "../config.js";
 import langMapping from "../../assets/locales.json" with { type: "json" };
 import resourcesJson from "../../assets/resources.json" with { type: "json" };
@@ -221,6 +221,24 @@ export function formatNumber(num: number, notation?: NumberLengthFormat): string
   );
 }
 
+/** Reloads the tab. If a video is currently playing, its time and volume will be preserved through the URL parameters `time_continue` and `bytm_volume` */
+export async function reloadTab() {
+  let time = 0, volume = 0;
+
+  if(getVideoElement()) {
+    time = (await getVideoTime() ?? 0) - 0.25;
+    volume = clamp(Math.round(getVideoElement()!.volume * 100), 0, 100);
+  }
+
+  const url = new URL(location.href);
+  if(time > 0)
+    url.searchParams.set("time_continue", String(time));
+  if(volume > 0)
+    url.searchParams.set("bytm_volume", String(volume));
+
+  location.href = url.href;
+}
+
 //#region resources
 
 /**