Преглед на файлове

fix history entries merging for YouTube Shorts

extractVideoId() only checked the `v` query param, and background.js's
updateHistory() bypassed it entirely with its own inline parsing — both
returned null for /shorts/<id> URLs, so every Shorts view got written
to the same history[null] entry, overwriting title/position each time.

extractVideoId() now falls back to parsing the id out of the /shorts/
path, cleanVideoUrl() preserves the /shorts/<id> form, and
updateHistory() goes through the shared helper instead of duplicating
the parsing logic.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Brandon Wong преди 3 седмици
родител
ревизия
4e25952b6b
променени са 3 файла, в които са добавени 50 реда и са изтрити 9 реда
  1. 27 0
      2026-07-26-claude-fix-queue-history-tracking.md
  2. 1 2
      background.js
  3. 22 7
      shared/playlist-utils.js

Файловите разлики са ограничени, защото са твърде много
+ 27 - 0
2026-07-26-claude-fix-queue-history-tracking.md


+ 1 - 2
background.js

@@ -143,8 +143,7 @@ async function updateTracking(message) {
 async function updateHistory(message) {
   const { history: currentHistory } =
     await browser.storage.local.get("history");
-  const q = new URL(message.url);
-  const v = q.searchParams.get("v");
+  const v = PlaylistUtils.extractVideoId(message.url);
   if (currentHistory[v]) {
     const { [v]: existing, ...rest } = currentHistory;
 

+ 22 - 7
shared/playlist-utils.js

@@ -3,30 +3,45 @@
 
 const PlaylistUtils = {
   /**
-   * Extract video ID from YouTube URL
+   * Extract video ID from YouTube URL. Handles both standard watch URLs
+   * (`/watch?v=<id>`) and Shorts URLs (`/shorts/<id>`, which carry the ID
+   * in the path rather than a `v` query param).
    * @param {string} url - YouTube URL
    * @returns {string|null} - Video ID or null if not found
    */
   extractVideoId(url) {
     try {
       const urlObj = new URL(url);
-      return urlObj.searchParams.get("v");
+      const vParam = urlObj.searchParams.get("v");
+      if (vParam) {
+        return vParam;
+      }
+      const shortsMatch = urlObj.pathname.match(/^\/shorts\/([^/?]+)/);
+      return shortsMatch ? shortsMatch[1] : null;
     } catch (e) {
       return null;
     }
   },
 
   /**
-   * Normalize a YouTube watch URL down to just the `v` (video ID) param,
-   * stripping queue/playlist params like `list`, `index`, and `pp` so that
+   * Normalize a YouTube video URL down to just its video ID, stripping
+   * queue/playlist params like `list`, `index`, and `pp` so that
    * navigating to the stored URL later doesn't drop the user back into
-   * whatever queue or playlist they originally watched it from.
+   * whatever queue or playlist they originally watched it from. Shorts
+   * URLs keep the `/shorts/<id>` form rather than being rewritten to
+   * `/watch?v=<id>`.
    * @param {string} url - YouTube URL
-   * @returns {string} - Clean watch URL, or the original url if no video ID is found
+   * @returns {string} - Clean URL, or the original url if no video ID is found
    */
   cleanVideoUrl(url) {
     const videoId = this.extractVideoId(url);
-    return videoId ? `https://www.youtube.com/watch?v=${videoId}` : url;
+    if (!videoId) {
+      return url;
+    }
+    const isShorts = new URL(url).pathname.startsWith("/shorts/");
+    return isShorts
+      ? `https://www.youtube.com/shorts/${videoId}`
+      : `https://www.youtube.com/watch?v=${videoId}`;
   },
 
   /**