Skip to content

th-ch/youtube-music

Repository files navigation

YTMD

GitHub release GitHub license eslint code style Build status GitHub All Releases AUR Known Vulnerabilities

Screenshot

  • Native look & feel extension, aims at keeping the original interface one click

Content

Translation

You can help with translation on Hosted Weblate.

translation status translation status 2

Download

You can check out the latest release to quickly find the latest version.

Arch Linux

Install the youtube-music-bin package from the AUR. For AUR installation instructions, take a look at this wiki page.

macOS

You can install the app using Homebrew (see the cask definition):

brew install th-ch/youtube-music/youtube-music

If you install the app manually and get an error "is damaged and can’t be opened." when launching the app, run the following in the Terminal:

/usr/bin/xattr -cr /Applications/YouTube\ Music.app

Windows

You can use the Scoop package manager to install the youtube-music package from the extras bucket.

scoop bucket add extras
scoop install extras/youtube-music

Alternately you can use Winget, Windows 11s official CLI package manager to install the th-ch.YouTubeMusic package.

Note: Microsoft Defender SmartScreen might block the installation since it is from an "unknown publisher". This is also true for the manual installation when trying to run the executable(.exe) after a manual download here on github (same file).

winget install th-ch.YouTubeMusic

How to install without a network connection? (in Windows)

  • Download the *.nsis.7z file for your device architecture in release page.
    • x64 for 64-bit Windows
    • ia32 for 32-bit Windows
    • arm64 for ARM64 Windows
  • Download installer in release page. (*-Setup.exe)
  • Place them in the same directory.
  • Run the installer.

Themes

You can load CSS files to change the look of the application (Options > Visual Tweaks > Themes).

Some predefined themes are available in https://github.com/kerichdev/themes-for-ytmdesktop-player.

Dev

git clone https://github.com/th-ch/youtube-music
cd youtube-music
pnpm install --frozen-lockfile
pnpm dev

Build your own plugins

Using plugins, you can:

  • manipulate the app - the BrowserWindow from electron is passed to the plugin handler
  • change the front by manipulating the HTML/CSS

Creating a plugin

Create a folder in src/plugins/YOUR-PLUGIN-NAME:

  • index.ts: the main file of the plugin
import style from './style.css?inline'; // import style as inline

import { createPlugin } from '@/utils';

export default createPlugin({
  name: 'Plugin Label',
  restartNeeded: true, // if value is true, ytmusic show restart dialog
  config: {
    enabled: false,
  }, // your custom config
  stylesheets: [style], // your custom style,
  menu: async ({ getConfig, setConfig }) => {
    // All *Config methods are wrapped Promise<T>
    const config = await getConfig();
    return [
      {
        label: 'menu',
        submenu: [1, 2, 3].map((value) => ({
          label: `value ${value}`,
          type: 'radio',
          checked: config.value === value,
          click() {
            setConfig({ value });
          },
        })),
      },
    ];
  },
  backend: {
    start({ window, ipc }) {
      window.maximize();

      // you can communicate with renderer plugin
      ipc.handle('some-event', () => {
        return 'hello';
      });
    },
    // it fired when config changed
    onConfigChange(newConfig) { /* ... */ },
    // it fired when plugin disabled
    stop(context) { /* ... */ },
  },
  renderer: {
    async start(context) {
      console.log(await context.ipc.invoke('some-event'));
    },
    // Only renderer available hook
    onPlayerApiReady(api: YoutubePlayer, context: RendererContext) {
      // set plugin config easily
      context.setConfig({ myConfig: api.getVolume() });
    },
    onConfigChange(newConfig) { /* ... */ },
    stop(_context) { /* ... */ },
  },
  preload: {
    async start({ getConfig }) {
      const config = await getConfig();
    },
    onConfigChange(newConfig) {},
    stop(_context) {},
  },
});

Common use cases

  • injecting custom CSS: create a style.css file in the same folder then:
// index.ts
import style from './style.css?inline'; // import style as inline

import { createPlugin } from '@/utils';

export default createPlugin({
  name: 'Plugin Label',
  restartNeeded: true, // if value is true, ytmusic will show a restart dialog
  config: {
    enabled: false,
  }, // your custom config
  stylesheets: [style], // your custom style
  renderer() {} // define renderer hook
});
  • If you want to change the HTML:
import { createPlugin } from '@/utils';

export default createPlugin({
  name: 'Plugin Label',
  restartNeeded: true, // if value is true, ytmusic will show the restart dialog
  config: {
    enabled: false,
  }, // your custom config
  renderer() {
    console.log('hello from renderer');
  } // define renderer hook
});
  • communicating between the front and back: can be done using the ipcMain module from electron. See index.ts file and example in sponsorblock plugin.

Build

  1. Clone the repo
  2. Follow this guide to install pnpm
  3. Run pnpm install --frozen-lockfile to install dependencies
  4. Run pnpm build:OS
  • pnpm dist:win - Windows
  • pnpm dist:linux - Linux (amd64)
  • pnpm dist:linux:deb-arm64 - Linux (arm64 for Debian)
  • pnpm dist:linux:rpm-arm64 - Linux (arm64 for Fedora)
  • pnpm dist:mac - macOS (amd64)
  • pnpm dist:mac:arm64 - macOS (arm64)

Builds the app for macOS, Linux, and Windows, using electron-builder.

Production Preview

pnpm start

Tests

pnpm test

Uses Playwright to test the app.

License

MIT © th-ch

FAQ

Why apps menu isn't showing up?

If Hide Menu option is on - you can show the menu with the alt key (or ` [backtick] if using the in-app-menu plugin)