Repository: raycast/extensions
Pull request #18631: YoutubeMusic: Fixed Issue - remove Like and Like Songs
Author: login: theDeal
Labels: name: extension fix / improvement
description: Label for PRs with extension's fix improvements
name: extension: youtube-music
description: Issues related to the youtube-music extension
Created: 2025-04-16T14:46:42Z | Merged: 2025-04-28T21:10:07Z

## Description

This pull request introduces a new command and resolves an existing issue within the YouTube Music extension.

For Issue: #17508 and #18489

The key changes include:

- A new `remove-like` command to allow users to remove a like from the currently playing track.
- A fix for the existing like functionality to avoid accidental unlikes.
- Minor configuration and documentation updates to improve maintainability and developer experience.

## Screencast

<!-- Optional: Add a brief screencast demonstrating the new command and the fix in action. This significantly speeds up the review process. -->

## Checklist

- [x] I read the [[extension guidelines](https://developers.raycast.com/basics/prepare-an-extension-for-store)](https://developers.raycast.com/basics/prepare-an-extension-for-store)
- [x] I read the [[documentation about publishing](https://developers.raycast.com/basics/publish-an-extension)](https://developers.raycast.com/basics/publish-an-extension)
- [x] I ran `npm run build` and [[tested this distribution build in Raycast](https://developers.raycast.com/basics/prepare-an-extension-for-store#metadata-and-configuration)](https://developers.raycast.com/basics/prepare-an-extension-for-store#metadata-and-configuration)
- [x] I checked that files in the `assets` folder are used by the extension itself
- [x] I checked that assets used in the `README` are located outside the metadata folder if they were not generated with our [[metadata tool](https://developers.raycast.com/basics/prepare-an-extension-for-store#how-to-use-it)](https://developers.raycast.com/basics/prepare-an-extension-for-store#how-to-use-it)

---

## Changelog

### ➕ New Command

- [`[remove-like.tsx](diffhunk://#diff-1f1aff7fe1ce4ec669943ea4c87c907130d59aa22bf444d5bd4212c967c8f109R1-R27)`](diffhunk://#diff-1f1aff7fe1ce4ec669943ea4c87c907130d59aa22bf444d5bd4212c967c8f109R1-R27): Adds a command to remove a like from the current YouTube Music track.

### ✅ Bug Fix

- [`[like.tsx](diffhunk://#diff-4c6114950c25870bdc6f5e795fe515e281ff1d090fdd55adf7192e5549da10f3L5-R24)`](diffhunk://#diff-4c6114950c25870bdc6f5e795fe515e281ff1d090fdd55adf7192e5549da10f3L5-R24): Prevents unintentional unlikes by only clicking the like button if the song is not already liked.

### 📝 Documentation

- [`[CHANGELOG.md](diffhunk://#diff-c6a9049b6dadb806baded8640f09e20d9f1c28b1343c7be00adb4ed731dab40fR3-R7)`](diffhunk://#diff-c6a9049b6dadb806baded8640f09e20d9f1c28b1343c7be00adb4ed731dab40fR3-R7): Added changelog entry for the new command and the like functionality fix.

### ⚙️ Configuration

- [`[.gitignore](diffhunk://#diff-374b38af5af937eaefc1f19359d8d171387c042ea618636552c3e1e24ff2d421R8-R14)`](diffhunk://#diff-374b38af5af937eaefc1f19359d8d171387c042ea618636552c3e1e24ff2d421R8-R14): Ignored `.idea`, `raycast-env.d.ts`, and `.pnpm-lock.yaml`.

### 📦 Package

- [`[package.json](diffhunk://#diff-2d1bcd1bfcee40d787766bef982878d384bd6feb19635d7954a021470c87c974R65-R71)`](diffhunk://#diff-2d1bcd1bfcee40d787766bef982878d384bd6feb19635d7954a021470c87c974R65-R71): Registered the `remove-like` command.



Diff:
diff --git a/extensions/youtube-music/.gitignore b/extensions/youtube-music/.gitignore
index 0b7071fe6b6..538ecd1205a 100644
--- a/extensions/youtube-music/.gitignore
+++ b/extensions/youtube-music/.gitignore
@@ -4,4 +4,11 @@
 /node_modules
 
 # misc
-.DS_Store
\ No newline at end of file
+.DS_Store
+
+.idea
+
+raycast-env.d.ts
+
+
+.pnpm-lock.yaml
\ No newline at end of file
diff --git a/extensions/youtube-music/CHANGELOG.md b/extensions/youtube-music/CHANGELOG.md
index dd77271b3ae..45ecfa5bc26 100644
--- a/extensions/youtube-music/CHANGELOG.md
+++ b/extensions/youtube-music/CHANGELOG.md
@@ -1,5 +1,10 @@
 # YouTube Music Changelog
 
+## [New Command and Fix] - 2025-04-28
+
+- Fixed: Like command no longer removes like when song is already liked
+- Added: New command to remove like from currently playing song
+
 ## [New Commands] = 2024-03-06
 
 Add `fast-forward`, `rewind`, `next-chapter`, `previous-chapter`, youtube.com support.
diff --git a/extensions/youtube-music/package.json b/extensions/youtube-music/package.json
index b9984476cc3..c273433cf97 100644
--- a/extensions/youtube-music/package.json
+++ b/extensions/youtube-music/package.json
@@ -24,6 +24,28 @@
       "default": "Safari",
       "required": true
     },
+    {
+      "name": "urlPreference",
+      "title": "URL Preference",
+      "description": "Choose which YouTube URLs to control",
+      "type": "dropdown",
+      "default": "music",
+      "required": true,
+      "data": [
+        {
+          "title": "YouTube Music Only",
+          "value": "music"
+        },
+        {
+          "title": "YouTube Only",
+          "value": "youtube"
+        },
+        {
+          "title": "Both YouTube and Music",
+          "value": "both"
+        }
+      ]
+    },
     {
       "name": "ff-rew-seconds",
       "title": "Number of seconds to fast forward or rewind.",
@@ -62,6 +84,13 @@
       "description": "Adds current track to \"Liked\" collection in YouTube Music.",
       "mode": "no-view"
     },
+    {
+      "name": "remove-like",
+      "title": "Remove Like",
+      "subtitle": "YouTube Music",
+      "description": "Removes the current track from your liked songs in YouTube Music.",
+      "mode": "no-view"
+    },
     {
       "name": "dislike",
       "title": "Dislike",
diff --git a/extensions/youtube-music/src/dislike.tsx b/extensions/youtube-music/src/dislike.tsx
index 92fc5cbf8c2..cc15816c4ff 100644
--- a/extensions/youtube-music/src/dislike.tsx
+++ b/extensions/youtube-music/src/dislike.tsx
@@ -1,12 +1,34 @@
-import { closeMainWindow } from "@raycast/api";
 import { runJSInYouTubeMusicTab } from "./utils";
+import { closeMainWindow, showHUD } from "@raycast/api";
 
 export default async () => {
-  if (
-    await runJSInYouTubeMusicTab(
-      "(document.querySelector('#like-button-renderer > tp-yt-paper-icon-button.dislike.style-scope.ytmusic-like-button-renderer') || document.querySelector('dislike-button-view-model button')).click();"
-    )
-  ) {
+  const jsCode = `(function() {
+    function clickIfNotPressed(button) {
+      if (!button || button.getAttribute("aria-pressed") === "true") return false;
+      button.click();
+      return true;
+    }
+
+    // YouTube Music
+    const ytmDislike = document.querySelector('ytmusic-like-button-renderer#like-button-renderer yt-button-shape.dislike button');
+    if (ytmDislike) return clickIfNotPressed(ytmDislike);
+
+    // YouTube (normal)
+    const ytDislike = Array.from(document.querySelectorAll('button[aria-label*="Mag ich nicht"]')).find(btn => btn.closest("ytd-menu-renderer"));
+    if (ytDislike) return clickIfNotPressed(ytDislike);
+
+    return false;
+  })();`;
+
+  try {
+    const result = await runJSInYouTubeMusicTab(jsCode);
+    if (result) {
+      await showHUD("Disliked 👎");
+    } else {
+      await showHUD("Already disliked 👀 or not found");
+    }
     await closeMainWindow();
+  } catch (error) {
+    await showHUD("❌ Failed to dislike");
   }
 };
diff --git a/extensions/youtube-music/src/like.tsx b/extensions/youtube-music/src/like.tsx
index de65b809aed..fff944f80b0 100644
--- a/extensions/youtube-music/src/like.tsx
+++ b/extensions/youtube-music/src/like.tsx
@@ -1,12 +1,34 @@
-import { closeMainWindow } from "@raycast/api";
 import { runJSInYouTubeMusicTab } from "./utils";
+import { closeMainWindow, showHUD } from "@raycast/api";
 
 export default async () => {
-  if (
-    await runJSInYouTubeMusicTab(
-      "(document.querySelector('#like-button-renderer > tp-yt-paper-icon-button.like.style-scope.ytmusic-like-button-renderer') || document.querySelector('like-button-view-model button')).click();"
-    )
-  ) {
+  const jsCode = `(function() {
+    function clickIfNotPressed(button) {
+      if (!button || button.getAttribute("aria-pressed") === "true") return false;
+      button.click();
+      return true;
+    }
+
+    // YouTube Music
+    const ytmLike = document.querySelector('ytmusic-like-button-renderer#like-button-renderer yt-button-shape.like button');
+    if (ytmLike) return clickIfNotPressed(ytmLike);
+
+    // YouTube (normal)
+    const ytLike = Array.from(document.querySelectorAll('button[aria-label*="mag das Video"]')).find(btn => btn.closest("ytd-menu-renderer"));
+    if (ytLike) return clickIfNotPressed(ytLike);
+
+    return false;
+  })();`;
+
+  try {
+    const result = await runJSInYouTubeMusicTab(jsCode);
+    if (result) {
+      await showHUD("Liked ❤️");
+    } else {
+      await showHUD("Already liked 👍 or not found");
+    }
     await closeMainWindow();
+  } catch (error) {
+    await showHUD("❌ Failed to like");
   }
 };
diff --git a/extensions/youtube-music/src/next-track.tsx b/extensions/youtube-music/src/next-track.tsx
index e65d1d3286f..eecf0b6615c 100644
--- a/extensions/youtube-music/src/next-track.tsx
+++ b/extensions/youtube-music/src/next-track.tsx
@@ -1,12 +1,52 @@
-import { closeMainWindow } from "@raycast/api";
+import { closeMainWindow, showHUD } from "@raycast/api";
 import { runJSInYouTubeMusicTab } from "./utils";
 
 export default async () => {
-  if (
-    await runJSInYouTubeMusicTab(
-      "(document.querySelector('#left-controls > div > tp-yt-paper-icon-button.next-button.style-scope.ytmusic-player-bar') || document.querySelector('.ytp-next-button')).click();"
-    )
-  ) {
+  try {
+    const result = await runJSInYouTubeMusicTab(`
+      (function () {
+        const isYouTubeMusic = window.location.hostname.includes("music.youtube.com");
+
+        // ---- YouTube Music ----
+        if (isYouTubeMusic) {
+          const nextButton = document.querySelector("ytmusic-player-bar .next-button #button");
+          if (nextButton) {
+            nextButton.click();
+            return "ytmusic-next";
+          }
+          return "ytmusic-fail";
+        }
+
+        // ---- YouTube (normal) ----
+        const ytNextButton = document.querySelector(".ytp-next-button");
+        if (ytNextButton && !ytNextButton.disabled) {
+          ytNextButton.click();
+          return "youtube-next";
+        }
+
+        return "youtube-fail";
+      })();
+    `);
+
+    switch (result) {
+      case "ytmusic-next":
+        await showHUD("⏭️ Next Song (YT Music)");
+        break;
+      case "youtube-next":
+        await showHUD("⏭️ Next Video");
+        break;
+      case "ytmusic-fail":
+        await showHUD("❌ No Next Button (YT Music)");
+        break;
+      case "youtube-fail":
+        await showHUD("❌ No Next Video Button");
+        break;
+      default:
+        await showHUD("❌ Unknown Error");
+    }
+
     await closeMainWindow();
+  } catch (error) {
+    await showHUD("❌ Failed to trigger next command");
   }
 };
diff --git a/extensions/youtube-music/src/previous-track.tsx b/extensions/youtube-music/src/previous-track.tsx
index 6a68dbc3eed..348388a9fc1 100644
--- a/extensions/youtube-music/src/previous-track.tsx
+++ b/extensions/youtube-music/src/previous-track.tsx
@@ -1,12 +1,59 @@
-import { closeMainWindow } from "@raycast/api";
+import { closeMainWindow, showHUD } from "@raycast/api";
 import { runJSInYouTubeMusicTab } from "./utils";
 
 export default async () => {
-  if (
-    await runJSInYouTubeMusicTab(
-      "document.querySelector('#left-controls > div > tp-yt-paper-icon-button.previous-button.style-scope.ytmusic-player-bar').click();"
-    )
-  ) {
+  try {
+    const result = await runJSInYouTubeMusicTab(`
+      (function () {
+        const isYouTubeMusic = window.location.hostname.includes("music.youtube.com");
+        const video = document.querySelector("video");
+
+        if (!video) return "no-video";
+
+        // ---- YouTube Music ----
+        if (isYouTubeMusic) {
+          const previousBtn = document.querySelector("ytmusic-player-bar .previous-button #button");
+          if (previousBtn) {
+            previousBtn.click();
+            return "ytmusic-prev";
+          }
+          return "ytmusic-fail";
+        }
+
+        // ---- YouTube (normal) ----
+        if (video.currentTime > 2) {
+          video.currentTime = 0;
+          return "youtube-restart";
+        } else {
+          history.back();
+          return "youtube-back";
+        }
+      })();
+    `);
+
+    // Feedback je nach Result
+    switch (result) {
+      case "ytmusic-prev":
+        await showHUD("⏮️ Previous Song (YT Music)");
+        break;
+      case "youtube-restart":
+        await showHUD("🔁 Restarted Video");
+        break;
+      case "youtube-back":
+        await showHUD("⬅️ Back to Previous Video");
+        break;
+      case "ytmusic-fail":
+        await showHUD("❌ No previous button found (YT Music)");
+        break;
+      case "no-video":
+        await showHUD("❌ No video element found");
+        break;
+      default:
+        await showHUD("❌ Unknown state");
+    }
+
     await closeMainWindow();
+  } catch (error) {
+    await showHUD("❌ Failed to run previous command");
   }
 };
diff --git a/extensions/youtube-music/src/remove-like.tsx b/extensions/youtube-music/src/remove-like.tsx
new file mode 100644
index 00000000000..b244553e469
--- /dev/null
+++ b/extensions/youtube-music/src/remove-like.tsx
@@ -0,0 +1,48 @@
+import { closeMainWindow, showHUD } from "@raycast/api";
+import { runJSInYouTubeMusicTab } from "./utils";
+
+export default async () => {
+  try {
+    const result = await runJSInYouTubeMusicTab(`
+      (() => {
+        const isYTM = location.hostname.includes("music.youtube.com");
+
+        if (isYTM) {
+          const likeButton = document.querySelector(
+            "ytmusic-like-button-renderer[like-status='LIKE'] #button-shape-like[aria-pressed='true'] > button"
+          );
+          if (likeButton) {
+            likeButton.click();
+            return "ytmusic-removed";
+          }
+          return "ytmusic-none";
+        }
+
+        // YouTube – neues UI: segmented-like-dislike-button-view-model
+        const newLikeButton = document.querySelector(
+          "segmented-like-dislike-button-view-model like-button-view-model button[aria-pressed='true']"
+        );
+        if (newLikeButton) {
+          newLikeButton.click();
+          return "youtube-removed";
+        }
+
+        return "youtube-none";
+      })();
+    `);
+
+    const messages = {
+      "ytmusic-removed": "👍🏻 Removed Like (YT Music)",
+      "ytmusic-none": "⚠️ Kein Like gesetzt (YT Music)",
+      "youtube-removed": "👍🏻 Removed Like (YouTube)",
+      "youtube-none": "⚠️ Kein Like gesetzt (YouTube)",
+    };
+
+    await showHUD(
+      typeof result === "string" ? messages[result as keyof typeof messages] : "❌ Fehler beim Entfernen des Likes"
+    );
+    await closeMainWindow();
+  } catch (e) {
+    await showHUD("❌ Fehler beim Entfernen des Likes");
+  }
+};
diff --git a/extensions/youtube-music/src/utils.ts b/extensions/youtube-music/src/utils.ts
index debcce85d9c..ab0a502f059 100644
--- a/extensions/youtube-music/src/utils.ts
+++ b/extensions/youtube-music/src/utils.ts
@@ -1,57 +1,101 @@
 import { Application, Toast, getPreferenceValues, showToast } from "@raycast/api";
 import { runAppleScript } from "run-applescript";
 
-// Extension should work with any Chromium or Firefox (not sure) based browser, but it impossible to check every such browser.
-// So i cannot confirm that extension will work with any else browser except that listed below.
 type SupportedBrowsers = "Safari" | "Chrome" | "YouTube Music" | "Microsoft Edge";
+type UrlPreference = "music" | "youtube" | "both";
+
+interface Preferences {
+  browser: Application;
+  urlPreference: UrlPreference;
+}
 
 interface OsaError {
   stderr: string;
 }
 
+/**
+ * Escapes JavaScript so it can safely be inserted into AppleScript.
+ */
+function escapeJS(js: string): string {
+  return js
+    .replace(/\\/g, "\\\\") // escape backslashes
+    .replace(/"/g, '\\"') // escape double quotes
+    .replace(/\n/g, "\\n"); // escape newlines
+}
+
+/**
+ * Generates the AppleScript command to run JavaScript in a browser tab.
+ */
 function runJS(browser: SupportedBrowsers | string, code: string): string {
-  if (browser === "Safari") {
-    return `do javascript "${code}"`;
+  const escaped = escapeJS(code);
+  return browser === "Safari" ? `do javascript "${escaped}"` : `execute javascript "${escaped}"`;
+}
+
+/**
+ * Returns the URL-matching AppleScript condition based on user preferences.
+ */
+function getUrlCondition(preference: UrlPreference): string {
+  switch (preference) {
+    case "music":
+      return 'URL contains "music.youtube.com"';
+    case "youtube":
+      return 'URL contains "youtube.com" and URL does not contain "music.youtube.com"';
+    case "both":
+      return '(URL contains "music.youtube.com" or (URL contains "youtube.com" and URL does not contain "music.youtube.com"))';
+    default:
+      return 'URL contains "music.youtube.com"';
   }
-  return `execute javascript "${code}"`;
 }
 
-export async function runJSInYouTubeMusicTab(code: string) {
-  const browser = getPreferenceValues<{ browser: Application }>().browser;
+/**
+ * Executes JavaScript inside a matching YouTube or YouTube Music tab in the selected browser.
+ */
+export async function runJSInYouTubeMusicTab(code: string): Promise<string | false> {
+  const preferences = getPreferenceValues<Preferences>();
+  const { browser, urlPreference } = preferences;
 
   try {
-    const jsResult = await runAppleScript(`
-		    tell application "${browser.name}"
-		        repeat with w in (every window)
-		            repeat with t in (every tab whose URL contains "music.youtube.com" or URL contains "youtube.com") of w
-		              tell t
-		                 return ${runJS(browser.name, code)}
-		              end tell
-		            end repeat
-		        end repeat
-		    end tell
-		    return "false"
-		`);
+    const result = await runAppleScript(`
+      tell application "${browser.name}"
+        repeat with w in (every window)
+          repeat with t in (every tab whose ${getUrlCondition(urlPreference)}) of w
+            tell t
+              try
+                return ${runJS(browser.name, code)}
+              on error errMsg
+                return "JS Error: " & errMsg
+              end try
+            end tell
+          end repeat
+        end repeat
+      end tell
+      return "false"
+    `);
 
-    if (jsResult === "false") {
+    if (result === "false") {
       await showToast({
         style: Toast.Style.Failure,
-        title: "The YouTube Music tab was not found",
-        message: "Try to check selected browser in extension preferences.",
+        title: "No matching YouTube tab found",
+        message: "Check your browser and URL preference in settings.",
       });
-
       return false;
     }
 
-    return jsResult;
+    return result;
   } catch (e) {
     const message = (e as OsaError).stderr;
 
     if (message.includes("Allow JavaScript from Apple Events")) {
       await showToast({
         style: Toast.Style.Failure,
-        title: "Cannot run JavaScript in selected browser.",
-        message: `Enable the 'Allow JavaScript from Apple Events' option in ${browser.name}'s Develop menu.`,
+        title: "JavaScript not allowed",
+        message: `Enable "Allow JavaScript from Apple Events" in ${browser.name}'s Develop menu.`,
+      });
+    } else {
+      await showToast({
+        style: Toast.Style.Failure,
+        title: "AppleScript execution failed",
+        message,
       });
     }
 
@@ -62,20 +106,12 @@ export async function runJSInYouTubeMusicTab(code: string) {
 export const goToChapter = {
   next: `(function() {
     const activeChapter = document.querySelector('ytd-macro-markers-list-item-renderer[active]');
-    const nextChapter = activeChapter ? activeChapter.nextElementSibling : null;
-    if (nextChapter) {
-      nextChapter.querySelector('a').click();
-    } else {
-      console.log('No next chapter found.');
-    }
+    const nextChapter = activeChapter?.nextElementSibling;
+    nextChapter?.querySelector('a')?.click();
   })();`,
   previous: `(function(){
     const activeChapter = document.querySelector('ytd-macro-markers-list-item-renderer[active]');
-    const previousChapter = activeChapter ? activeChapter.previousElementSibling : null;
-    if (previousChapter) {
-      previousChapter.querySelector('a').click();
-    } else {
-      console.log('No previous chapter found.');
-    }
+    const previousChapter = activeChapter?.previousElementSibling;
+    previousChapter?.querySelector('a')?.click();
   })();`,
 };


Reviews:
user: login: greptile-apps[bot]
body: 
## PR Summary

This PR adds a new remove-like command and fixes an issue where the like command would unintentionally remove likes from already-liked songs in YouTube Music.

- The CHANGELOG.md entry needs correction: should be `## [New Command and Fix] - {PR_MERGE_DATE}` and fix spelling ("allready" to "already")
- New `remove-like.tsx` properly checks like status before taking action using `like-status` attribute
- Fixed `like.tsx` to prevent unintentional unlikes by checking `like-status !== 'LIKE'` before clicking
- Both commands use proper error handling with `runJSInYouTubeMusicTab` and `closeMainWindow`

<sub>💡 (1/5) You can manually trigger the bot by mentioning @greptileai in a comment!</sub>

<sub>5 file(s) reviewed, 5 comment(s)</sub>
<sub>[Edit PR Review Bot Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews&utm_content=extensions_18631)</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-16T14:50:12Z
user: login: andreaselia
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T21:09:01Z

Review comments:
diff_hunk: @@ -2,11 +2,26 @@ import { closeMainWindow } from "@raycast/api";
 import { runJSInYouTubeMusicTab } from "./utils";
 
 export default async () => {
-  if (
-    await runJSInYouTubeMusicTab(
-      "(document.querySelector('#like-button-renderer > tp-yt-paper-icon-button.like.style-scope.ytmusic-like-button-renderer') || document.querySelector('like-button-view-model button')).click();"
-    )
-  ) {
+  const jsCode = `(function() {
+    const likeRenderer = document.querySelector('ytmusic-like-button-renderer#like-button-renderer');
+    if (!likeRenderer) return false;
+
+    const likeStatus = likeRenderer.getAttribute('like-status');
+    
+    // Only click the like button if the song is not already liked
+    if (likeStatus !== 'LIKE') {
+      const likeButton = likeRenderer.querySelector('yt-button-shape.like button');
+      if (likeButton) {
+        likeButton.click();
+        return true;
+      }
+    }
+    return false;
+  })();`;
+
+  const result = await runJSInYouTubeMusicTab(jsCode);
+
+  if (result) {
     await closeMainWindow();
   }
path: extensions/youtube-music/src/like.tsx
user: login: greptile-apps[bot]
body: style: Check if runJSInYouTubeMusicTab() throws any errors

```suggestion
  try {
    const result = await runJSInYouTubeMusicTab(jsCode);

    if (result) {
      await closeMainWindow();
    }
  } catch (error) {
    showFailureToast(error, { title: "Failed to like song" });
  }
```
created_at: 2025-04-16T14:48:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,5 +1,10 @@
 # YouTube Music Changelog
 
+## [New Comand and Fix ] = 2025-04-15
path: extensions/youtube-music/CHANGELOG.md
user: login: greptile-apps[bot]
body: syntax: Title format should be `## [New Command and Fix] - {PR_MERGE_DATE}`

```suggestion
## [New Command and Fix] - {PR_MERGE_DATE}
```
created_at: 2025-04-16T14:48:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,5 +1,10 @@
 # YouTube Music Changelog
 
+## [New Comand and Fix ] = 2025-04-15
+
+We fixed: If you like a Song and it was allready liked the like will be not removed, 
+New feature: If you want to unlike a song just type `remove-like`.
path: extensions/youtube-music/CHANGELOG.md
user: login: greptile-apps[bot]
body: style: Entry text contains spelling errors and unclear formatting

```suggestion
- Fixed: Like command no longer removes like when song is already liked
- Added: New command to remove like from currently playing song
```
created_at: 2025-04-16T14:48:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4,4 +4,11 @@
 /node_modules
 
 # misc
-.DS_Store
\ No newline at end of file
+.DS_Store
+
+.idea
+
+raycast-env.d.ts
+
+
path: extensions/youtube-music/.gitignore
user: login: greptile-apps[bot]
body: style: Remove extra blank line before .pnpm-lock.yaml
created_at: 2025-04-16T14:48:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,27 @@
+import { closeMainWindow } from "@raycast/api";
+import { runJSInYouTubeMusicTab } from "./utils";
+
+export default async () => {
+  const jsCode = `(function() {
+    const likeRenderer = document.querySelector('ytmusic-like-button-renderer#like-button-renderer');
+    if (!likeRenderer) return false;
+
+    const likeStatus = likeRenderer.getAttribute('like-status');
+
+    // Only click the like button if the song is already liked (to unlike it)
+    if (likeStatus === 'LIKE') {
+      const likeButton = likeRenderer.querySelector('yt-button-shape.like button');
+      if (likeButton) {
+        likeButton.click();
+        return true;
+      }
+    }
+    return false;
+  })();`;
+
+  const result = await runJSInYouTubeMusicTab(jsCode);
+
+  if (result) {
+    await closeMainWindow();
+  }
path: extensions/youtube-music/src/remove-like.tsx
user: login: greptile-apps[bot]
body: style: Consider wrapping runJSInYouTubeMusicTab in try/catch to handle potential browser communication errors

```suggestion
  try {
    const result = await runJSInYouTubeMusicTab(jsCode);

    if (result) {
      await closeMainWindow();
    }
  } catch (error) {
    showFailureToast(error, { title: "Failed to remove like" });
  }
```
created_at: 2025-04-16T14:49:11Z
author_association: CONTRIBUTOR

PR comments:
user: login: raycastbot
created_at: 2025-04-16T14:47:00Z
body: Thank you for your first  contribution! :tada:

🔔 @danieldbird @AlexStrNik @zach-fuller @gnosticdev you might want to have a look.

You can use [this guide](https://developers.raycast.com/basics/review-pullrequest) to learn how to check out the Pull Request locally in order to test it.

You can expect an initial review within five business days.
author_association: COLLABORATOR
user: login: theDeal
created_at: 2025-04-16T15:37:53Z
body: Now also Issue #18489 is fixed with this pull request...
author_association: CONTRIBUTOR
user: login: andreaselia
created_at: 2025-04-17T10:19:48Z
body: @danieldbird do you want to check this? 😄 
author_association: CONTRIBUTOR
user: login: theDeal
created_at: 2025-04-17T10:48:08Z
body: This pull request introduces several enhancements and fixes to the YouTube Music extension, including new features, improved functionality, and code refactoring. The key changes involve adding a new "Remove Like" command, enhancing existing commands for better user experience and error handling, introducing a new preference for URL control, and refactoring utility functions for improved maintainability.

### New Features and Commands:
* Added a new "Remove Like" command to allow users to remove a like from the currently playing song. This includes appropriate feedback messages for success or failure. (`extensions/youtube-music/src/remove-like.tsx`)
* Introduced a new "URL Preference" dropdown in the settings to let users choose which YouTube URLs (Music, YouTube, or both) the extension should control. (`extensions/youtube-music/package.json`)

### Enhancements to Existing Commands:
* Updated the "Like" and "Dislike" commands to provide better feedback through HUD messages, handle edge cases more gracefully, and support both YouTube Music and YouTube. (`extensions/youtube-music/src/like.tsx`, `extensions/youtube-music/src/dislike.tsx`) [[1]](diffhunk://#diff-4c6114950c25870bdc6f5e795fe515e281ff1d090fdd55adf7192e5549da10f3L1-R32) [[2]](diffhunk://#diff-815437ae4b163a2fb86db52b327c14b771690b19eeed74a030cec87b9049a45bL1-R32)
* Improved the "Next Track" and "Previous Track" commands to support both YouTube Music and YouTube, with detailed feedback for different scenarios, such as no next/previous button or restarting a video. (`extensions/youtube-music/src/next-track.tsx`, `extensions/youtube-music/src/previous-track.tsx`) [[1]](diffhunk://#diff-3eddf650c8936c2382615f134329e4cedb58631bcba88a122e6481acaad8da4dL1-R50) [[2]](diffhunk://#diff-72846826ea3036e01482b1b70565a0cd4e3b35c189deb562342d2d578d676278L1-R57)

### Utility Function Refactoring:
* Refactored the `runJSInYouTubeMusicTab` function to support the new "URL Preference" setting, improve error handling, and make the code more modular by introducing helper functions like `escapeJS` and `getUrlCondition`. (`extensions/youtube-music/src/utils.ts`)
* Simplified the "goToChapter" utility function by replacing verbose logic with optional chaining for cleaner and more concise code. (`extensions/youtube-music/src/utils.ts`)

### Miscellaneous:
* Added `.idea`, `raycast-env.d.ts`, and `.pnpm-lock.yaml` to `.gitignore` for better repository hygiene. (`extensions/youtube-music/.gitignore`)
* Updated the changelog to document the new "Remove Like" command and the fix for the "Like" command. (`extensions/youtube-music/CHANGELOG.md`)
author_association: CONTRIBUTOR
user: login: theDeal
created_at: 2025-04-20T09:54:34Z
body: @danieldbird Are you currently available to review Git contributions? The extension is presently non-functional, and this pull request provides a fix along with new functionality. @raycastbot Could you please review and consider merging this update to restore the extension's functionality?
author_association: CONTRIBUTOR
user: login: theDeal
created_at: 2025-04-26T12:54:00Z
body: ??
author_association: CONTRIBUTOR
user: login: andreaselia
created_at: 2025-04-28T21:08:09Z
body: Hey @theDeal 👋 

Thanks for the PR, I'm going to merge it as I see no issues with this fix and small feature addition.

@danieldbird please let me know if you see any issues here when you get a moment.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-28T21:10:54Z
body: <!-- add-pr-comment:add-pr-comment -->

**Published to the Raycast Store:**
https://raycast.com/danieldbird/youtube-music
author_association: CONTRIBUTOR
user: login: raycastbot
created_at: 2025-04-28T21:10:56Z
body: :tada: :tada: :tada:

Such a great contribution deserves a reward, but unfortunately we couldn't find your Raycast account based on your GitHub username (@theDeal).
Please [link your GitHub account to your Raycast account](https://www.raycast.com/settings/account) to receive your credits and soon be able to exchange them for some swag.
author_association: COLLABORATOR
user: login: andreaselia
created_at: 2025-04-28T21:11:12Z
body: @theDeal just curious, what's your Raycast handle? Also, would you like to add yourself as a contributor to the `package.json` for the extension in another PR?
author_association: CONTRIBUTOR
user: login: theDeal
created_at: 2025-04-29T05:08:19Z
body: Thanks @andreaselia for checking it out and merging! 🙂
If you notice any unexpected behavior, please let me know as soon as possible. ;)
author_association: CONTRIBUTOR