Forráskód Böngészése

Merge branch 'develop' of https://github.com/Sv443/BetterYTM into develop

Sv443 1 éve
szülő
commit
74e2cd7e5f
10 módosított fájl, 757 hozzáadás és 178 törlés
  1. 1 0
      .gitignore
  2. 9 7
      README.md
  3. 0 0
      dist/BetterYTM.user.js
  4. 624 143
      package-lock.json
  5. 7 5
      package.json
  6. 6 4
      src/BetterYTM.user.ts
  7. 39 3
      src/features/layout.ts
  8. 37 0
      src/tools/serve.ts
  9. 30 14
      src/utils.ts
  10. 4 2
      tsconfig.json

+ 1 - 0
.gitignore

@@ -1,4 +1,5 @@
 node_modules/
+out/
 test.js
 test.ts
 *.map

+ 9 - 7
README.md

@@ -46,13 +46,15 @@ Once you have the extension, click this button to install the userscript:
 ### Development:
 | Command | Description |
 | --- | --- |
-| `npm i` | Run once to install dependencies |
-| `npm run lint` | Lints the userscript with ESLint |
-| `npm run build` | Builds the userscript |
-| `npm run watch` | Watches, rebuilds and serves the userscript on port 8710, so it can be updated live if set up correctly in the userscript manager |
-
-When using ViolentMonkey, after running the command `npm run watch`, open [`http://localhost:8710/BetterYTM.user.js`](http://localhost:8710/BetterYTM.user.js) and select the `Track local file` option.  
-This makes it so the userscript automatically updates when the code changes.
+| `npm i` | Run once to install dependencies |
+| `npm run lint` | Lints the userscript with ESLint |
+| `npm run build` | Builds the userscript |
+| `npm run watch` | Watches, rebuilds and serves the userscript on port 8710, so it can be updated live if set up correctly in the userscript manager. Configure request logging and more in `src/tools/serve.ts` |
+<!-- first column uses non-breaking space U+00A0 (' ') -->
+
+When using ViolentMonkey, after running the command `npm run watch`, open [`http://localhost:8710/dist/BetterYTM.user.js`](http://localhost:8710/dist/BetterYTM.user.js) and select the `Track local file` option.  
+This makes it so the userscript automatically updates when the code changes.  
+Note: the tab needs to stay open on Firefox or the script will not update itself.
 
 
 <br><br>

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
dist/BetterYTM.user.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 624 - 143
package-lock.json


+ 7 - 5
package.json

@@ -10,9 +10,10 @@
     "test": "npm run node-ts -- ./test.ts",
     "build": "webpack",
     "post-build": "npm run node-ts -- ./src/tools/post-build.ts",
-    "serve": "http-server -s -c 5 -p 8710 .",
+    "serve": "npm run node-ts -- ./src/tools/serve.ts",
+    "serve-old": "http-server -s -c 5 -p 8710 .",
     "watch": "nodemon --exec \"npm run build && npm run serve\"",
-    "lint": "eslint .",
+    "lint": "tsc --noEmit && eslint .",
     "node-ts": "node --no-warnings=ExperimentalWarning --enable-source-maps --loader ts-node/esm"
   },
   "repository": {
@@ -31,14 +32,15 @@
     "@billjs/event-emitter": "^1.0.3"
   },
   "devDependencies": {
+    "@types/express": "^4.17.17",
     "@types/greasemonkey": "^4.0.4",
     "@types/marked": "^5.0.0",
     "@types/node": "^20.2.4",
-    "@typescript-eslint/eslint-plugin": "^5.59.7",
+    "@typescript-eslint/eslint-plugin": "^5.59.8",
     "@typescript-eslint/parser": "^5.59.7",
     "eslint": "^7.32.0",
+    "express": "^4.18.2",
     "html-loader": "^4.2.0",
-    "http-server": "^14.1.1",
     "markdown-loader": "^8.0.0",
     "nodemon": "^2.0.22",
     "ts-loader": "^9.4.3",
@@ -63,4 +65,4 @@
       "dev/*"
     ]
   }
-}
+}

+ 6 - 4
src/BetterYTM.user.ts

@@ -1,9 +1,9 @@
 import { getFeatures } from "./config";
 import { dbg, info } from "./constants";
-import { getDomain } from "./utils";
+import { getDomain, initSiteEvents } from "./utils";
 import {
   // layout
-  addMediaCtrlGeniusBtn, addQueueButtons, addWatermark,
+  addMediaCtrlGeniusBtn, initQueueButtons, addWatermark,
   preInitLayout, removeUpgradeTab, setVolSliderSize,
   setVolSliderStep,
   // lyrics
@@ -40,6 +40,8 @@ import {
 
     dbg && console.log(`BetterYTM: Initializing features for domain '${domain}'`);
 
+    initSiteEvents();
+
     try {
       if(domain === "ytm") {
         if(features.arrowKeySupport)
@@ -52,10 +54,10 @@ import {
           addWatermark();
 
         if(features.geniusLyrics)
-          await addMediaCtrlGeniusBtn();
+          addMediaCtrlGeniusBtn();
 
         if(features.queueButtons)
-          await addQueueButtons();
+          initQueueButtons();
 
         if(typeof features.volumeSliderSize === "number")
           setVolSliderSize();

+ 39 - 3
src/features/layout.ts

@@ -1,6 +1,6 @@
 import { dbg, info, triesInterval, triesLimit } from "../constants";
 import { getFeatures } from "../config";
-import { addGlobalStyle, insertAfter } from "../utils";
+import { addGlobalStyle, insertAfter, siteEvents } from "../utils";
 import type { FeatureConfig } from "../types";
 import { openMenu } from "./menu";
 
@@ -103,6 +103,42 @@ export function setVolSliderStep() {
 
 //#MARKER queue buttons
 
-export function addQueueButtons() {
-  void "TODO";
+export function initQueueButtons() {
+  siteEvents.on("queueChanged", (evt) => {
+    for(const queueItm of ((evt.data as HTMLElement).childNodes as NodeListOf<HTMLElement>)) {
+      if(!queueItm.dataset["bytm-has-queue-btns"])
+        addQueueButtons(queueItm);
+    }
+  });
+
+  const queueBtnsStyle = `\
+.side-panel.modular ytmusic-player-queue-item .song-info.ytmusic-player-queue-item {
+  position: relative;
+}
+.side-panel.modular ytmusic-player-queue-item .song-info .bytm-queue-btn-container {
+  display: none;
+  position: absolute;
+  right: 0;
+}
+.side-panel.modular ytmusic-player-queue-item:hover .song-info .bytm-queue-btn-container {
+  display: inline-block;
+}`;
+  addGlobalStyle(queueBtnsStyle, "queue-btns");
+
+  const queueItems = document.querySelectorAll("#contents.ytmusic-player-queue > ytmusic-player-queue-item");
+  if(queueItems.length === 0)
+    return;
+
+  queueItems.forEach(itm => addQueueButtons(itm as HTMLElement));
+}
+
+function addQueueButtons(queueItem: HTMLElement) {
+  console.log("Add queue btns:", queueItem);
+
+  const queueBtnsCont = document.createElement("div");
+  queueBtnsCont.className = "bytm-queue-btn-container";
+  queueBtnsCont.innerText = "ayo";
+
+  const songInfo = queueItem.querySelector(".song-info")!;
+  songInfo.appendChild(queueBtnsCont);
 }

+ 37 - 0
src/tools/serve.ts

@@ -0,0 +1,37 @@
+import express, { NextFunction, Request, Response } from "express";
+const app = express();
+
+/** HTTP port of the dev server */
+const devServerPort = 8710;
+/** Whether to log requests to the console */
+const enableLogging = false;
+/** The folder where the packed userscript resides ("." if in the root dir) */
+const packedScriptDir = "dist";
+/** Name of the packed userscript file */
+const packedScriptName = "BetterYTM.user.js";
+/** Whether to make a bell sound (in some terminals) when the userscript is ready to be fetched */
+const ringBell = true;
+
+app.use((_req, _res, next) => {
+  enableLogging && process.stdout.write("*");
+  next();
+});
+
+app.use((err: unknown, _req: Request, _res: Response, _next: NextFunction) => {
+  if(typeof err === "string" || err instanceof Error)
+    console.error("\x1b[31mError in dev server:\x1b[0m\n", err);
+});
+
+app.use(express.static(packedScriptDir, {
+  etag: false,
+  maxAge: 5_000,
+}));
+
+app.listen(devServerPort, "0.0.0.0", () => {
+  console.log(`The dev server is running.\nUserscript is served at \x1b[34m\x1b[4mhttp://localhost:${devServerPort}/${packedScriptName}\x1b[0m`);
+  if(enableLogging)
+    process.stdout.write("\nRequests: ");
+  else
+    console.log("\x1b[2m(request logging is disabled)\x1b[0m");
+  ringBell && process.stdout.write("\u0007");
+});

+ 30 - 14
src/utils.ts

@@ -2,6 +2,8 @@ import { EventEmitter, EventHandler } from "@billjs/event-emitter";
 import { dbg } from "./constants";
 import type { Domain } from "./types";
 
+//#SECTION BYTM-specific
+
 /**
  * Returns the current domain as a constant string representation
  * @throws Throws if script runs on an unexpected website
@@ -41,6 +43,8 @@ export function getVideoTime() {
   }
 }
 
+//#SECTION DOM
+
 /**
  * Inserts `afterNode` as a sibling just after the provided `beforeNode`
  * @param beforeNode
@@ -59,7 +63,7 @@ export function insertAfter(beforeNode: HTMLElement, afterNode: HTMLElement) {
  */
 export function addGlobalStyle(style: string, ref?: string) {
   if(typeof ref !== "string" || ref.length === 0)
-    ref = String(Math.floor(Math.random() * 10000));
+    ref = String(Math.floor(Math.random() * 10_000));
 
   const styleElem = document.createElement("style");
   styleElem.id = `betterytm-style-${ref}`;
@@ -69,21 +73,33 @@ export function addGlobalStyle(style: string, ref?: string) {
   dbg && console.log(`BetterYTM: Inserted global style with ref '${ref}':`, styleElem);
 }
 
-interface SiteEvents extends EventEmitter {
-  /** Emitted whenever the site "changes" in a major way */
-  on(event: "change", handler: EventHandler): boolean;
+//#SECTION site events
+
+export interface SiteEvents extends EventEmitter {
+  /** Emitted whenever a song is added to or removed from the queue */
+  on(event: "queueChanged", listener: EventHandler): boolean;
 }
 
-class SiteEvents extends EventEmitter {
-  constructor() {
-    super();
-    document.addEventListener("DOMContentLoaded", this.initObserver);
-  }
+export const siteEvents = new EventEmitter() as SiteEvents;
 
-  initObserver() {
-    void "TODO: observe";
-  }
+let observers: MutationObserver[] = [];
+
+export function initSiteEvents() {
+  const queueObserver = new MutationObserver(([ record ]) => {
+    if(record.addedNodes.length > 0 || record.removedNodes.length > 0)
+      siteEvents.fire("queueChanged", record.target);
+  });
+  // only observe added or removed elements
+  queueObserver.observe(document.querySelector(".side-panel.modular #contents.ytmusic-player-queue")!, {
+    childList: true,
+  });
+
+  observers = [
+    queueObserver,
+  ];
 }
 
-/** Emits all site-specific events */
-export const siteEvents = new SiteEvents();
+export function removeObservers() {
+  observers.forEach((observer) => observer.disconnect());
+  observers = [];
+}

+ 4 - 2
tsconfig.json

@@ -1,9 +1,9 @@
 {
   "compilerOptions": {
     "module": "ESNext",
-    "moduleResolution": "bundler",
+    "moduleResolution": "node",
     "target": "ES2016",
-    "outDir": ".",
+    "outDir": "dist/out",
     "lib": [
       "DOM",
       "DOM.Iterable"
@@ -23,6 +23,8 @@
   },
   "exclude": [
     "**/*.js",
+    "dist/**",
+    "rollup.config.cjs",
   ],
   "typeAcquisition": {
     "enable": true,

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott