mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 10:31:47 +00:00
Compare commits
13 Commits
v3.3.5
...
feat/webno
| Author | SHA1 | Date | |
|---|---|---|---|
| 22b74113b6 | |||
| 9da3ad2fb7 | |||
| d45d597136 | |||
| 2495d5da99 | |||
| 33aeafd19c | |||
| 374d0ce5e7 | |||
| 371805334b | |||
| 47dbeff0d0 | |||
| 17652b5b77 | |||
| 9608c2a7fc | |||
| 8abe2823d7 | |||
| dbc7f23ab8 | |||
| 357bd935e4 |
12
changelog.md
12
changelog.md
@ -2,8 +2,20 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
||||
|
||||
#### [v3.3.5](https://github.com/th-ch/youtube-music/compare/v3.3.4...v3.3.5)
|
||||
|
||||
- chore(deps): update dependency node-gyp to v10.1.0 [`#1885`](https://github.com/th-ch/youtube-music/pull/1885)
|
||||
- chore(deps): update dependency @typescript-eslint/eslint-plugin to v7.4.0 [`#1886`](https://github.com/th-ch/youtube-music/pull/1886)
|
||||
- chore(deps): update dependency vite to v5.2.6 [`#1883`](https://github.com/th-ch/youtube-music/pull/1883)
|
||||
- fix(style): resolve #1887 [`#1887`](https://github.com/th-ch/youtube-music/issues/1887)
|
||||
- chore(i18n): Translated using Weblate (Swedish) [`69087bb`](https://github.com/th-ch/youtube-music/commit/69087bbf1fac1ba58e992146deb1d6f1706b1e3c)
|
||||
- chore(i18n): Translated using Weblate (French) [`af78f15`](https://github.com/th-ch/youtube-music/commit/af78f1596ab8db2fa7069fdb1c4f078099ce4446)
|
||||
- Update changelog for v3.3.4 [`62f7d44`](https://github.com/th-ch/youtube-music/commit/62f7d440fab5bdbe9f49a3a5f8c32e7aaf2f28f6)
|
||||
|
||||
#### [v3.3.4](https://github.com/th-ch/youtube-music/compare/v3.3.3...v3.3.4)
|
||||
|
||||
> 24 March 2024
|
||||
|
||||
- Update changelog for v3.3.3 [`9769544`](https://github.com/th-ch/youtube-music/commit/97695444affbacb71dd73ae7107d4c987e285a37)
|
||||
- fix(style): fix fullscreen style and in-app-menu [`ed700c2`](https://github.com/th-ch/youtube-music/commit/ed700c2916cc7e6ccd2010d0c552364af116eb4f)
|
||||
- fix(style): fix miniplayer style [`a8bc539`](https://github.com/th-ch/youtube-music/commit/a8bc53912d1f4137008ecb2d9d5d9d9eb06ee2a8)
|
||||
|
||||
10
package.json
10
package.json
@ -172,6 +172,7 @@
|
||||
"node-html-parser": "6.1.12",
|
||||
"node-id3": "0.2.6",
|
||||
"peerjs": "1.5.2",
|
||||
"reconnecting-websocket": "4.4.0",
|
||||
"semver": "7.6.0",
|
||||
"serve": "14.2.1",
|
||||
"simple-youtube-age-restriction-bypass": "github:organization/Simple-YouTube-Age-Restriction-Bypass#v2.5.9",
|
||||
@ -181,6 +182,7 @@
|
||||
"solid-transition-group": "0.2.3",
|
||||
"ts-morph": "22.0.0",
|
||||
"vudio": "2.1.1",
|
||||
"ws": "8.16.0",
|
||||
"x11": "2.3.0",
|
||||
"youtubei.js": "9.1.0"
|
||||
},
|
||||
@ -192,13 +194,14 @@
|
||||
"@types/howler": "2.2.11",
|
||||
"@types/html-to-text": "9.0.4",
|
||||
"@types/semver": "7.5.8",
|
||||
"@types/ws": "8.5.10",
|
||||
"@typescript-eslint/eslint-plugin": "7.4.0",
|
||||
"bufferutil": "4.0.8",
|
||||
"builtin-modules": "3.3.0",
|
||||
"cross-env": "7.0.3",
|
||||
"del-cli": "5.1.0",
|
||||
"discord-api-types": "0.37.76",
|
||||
"electron": "29.1.5",
|
||||
"electron": "29.1.6",
|
||||
"electron-builder": "24.9.1",
|
||||
"electron-devtools-installer": "3.2.0",
|
||||
"electron-vite": "2.1.0",
|
||||
@ -211,14 +214,13 @@
|
||||
"glob": "10.3.10",
|
||||
"node-gyp": "10.1.0",
|
||||
"playwright": "1.42.1",
|
||||
"rollup": "4.13.0",
|
||||
"rollup": "4.13.1",
|
||||
"typescript": "5.4.3",
|
||||
"utf-8-validate": "6.0.3",
|
||||
"vite": "5.2.6",
|
||||
"vite-plugin-inspect": "0.8.3",
|
||||
"vite-plugin-resolve": "2.5.1",
|
||||
"vite-plugin-solid": "2.10.2",
|
||||
"ws": "8.16.0"
|
||||
"vite-plugin-solid": "2.10.2"
|
||||
},
|
||||
"auto-changelog": {
|
||||
"hideCredit": true,
|
||||
|
||||
392
pnpm-lock.yaml
generated
392
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
1
src/i18n/resources/he.json
Normal file
1
src/i18n/resources/he.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
||||
@ -37,6 +37,14 @@
|
||||
},
|
||||
"when-ready": {
|
||||
"clearing-cache-after-20s": "App-cache wissen"
|
||||
},
|
||||
"window": {
|
||||
"tried-to-render-offscreen": "Venster probeerde buiten het scherm te renderen, venstergrootte={{windowSize}}, schermgrootte={{displaySize}}, positie={{position}}"
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
"hide-menu-enabled": {
|
||||
"detail": "Menu is verborgen, gebruik 'Alt' om het te tonen (of 'Escape' als u het In-App Menu gebruikt)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,13 +207,14 @@
|
||||
"name": "Bloqueador de anúncios"
|
||||
},
|
||||
"album-actions": {
|
||||
"description": "Adiciona os botões Gostei e Não Gostei para ser aplicado a todas as músicas em uma lista de reprodução ou álbum.",
|
||||
"description": "Adiciona os botões Anular Rejeição, Não Gostei, Gostei e Anular o Gosto para ser aplicado a todas as músicas de uma lista de reprodução ou álbum",
|
||||
"name": "Ações no álbum"
|
||||
},
|
||||
"album-color-theme": {
|
||||
"description": "Aplica um tema dinâmico e efeitos visuais com base na paleta de cores do álbum",
|
||||
"menu": {
|
||||
"color-mix-ratio": {
|
||||
"label": "Rácio de mistura das cores",
|
||||
"submenu": {
|
||||
"percent": "Proporção"
|
||||
}
|
||||
@ -577,9 +578,11 @@
|
||||
"name": "Trocador de qualidade do vídeo"
|
||||
},
|
||||
"scrobbler": {
|
||||
"description": "Adicionar suporte para scrobbling (Last.fm, ListenBrainz)",
|
||||
"dialog": {
|
||||
"lastfm": {
|
||||
"auth-failed": {
|
||||
"message": "Falha ao autenticar com a Last.fm\nOculte o pop-up até a próxima reinicialização.",
|
||||
"title": "Falha na autenticação"
|
||||
}
|
||||
}
|
||||
@ -587,9 +590,18 @@
|
||||
"menu": {
|
||||
"lastfm": {
|
||||
"api-settings": "Configurações de API Last.fm"
|
||||
}
|
||||
},
|
||||
"listenbrainz": {
|
||||
"token": "Insira o token de utilizador ListenBrainz"
|
||||
},
|
||||
"scrobble-other-media": "Scrobble outros mídia"
|
||||
},
|
||||
"name": "Scrobbler",
|
||||
"prompt": {
|
||||
"lastfm": {
|
||||
"api-key": "Chave de API Last.fm",
|
||||
"api-secret": "Segredo da API Last.fm"
|
||||
},
|
||||
"listenbrainz": {
|
||||
"token": {
|
||||
"label": "Insira seu token de usuário do ListenBrainz:",
|
||||
|
||||
@ -491,7 +491,7 @@
|
||||
},
|
||||
"no-google-login": {
|
||||
"description": "Убрать из интерфейса кнопки и ссылки для входа через Google",
|
||||
"name": "Нет входа в систему Google"
|
||||
"name": "Без входа в систему Google"
|
||||
},
|
||||
"notifications": {
|
||||
"description": "Показывать уведомления о начале воспроизведения песни (интерактивные уведомления доступны в Windows)",
|
||||
@ -578,7 +578,7 @@
|
||||
"name": "Изменение качества видео"
|
||||
},
|
||||
"scrobbler": {
|
||||
"description": "Добавьте поддержку скробблинга (например, last.fm, Listenbrainz)",
|
||||
"description": "Добавляет поддержку скробблинга (last.fm, Listenbrainz)",
|
||||
"dialog": {
|
||||
"lastfm": {
|
||||
"auth-failed": {
|
||||
|
||||
@ -83,7 +83,7 @@
|
||||
"navigation": {
|
||||
"label": "導覽列",
|
||||
"submenu": {
|
||||
"copy-current-url": "複製目前的網址",
|
||||
"copy-current-url": "複製當前頁面的網址",
|
||||
"go-back": "回到上一頁",
|
||||
"go-forward": "回到下一頁",
|
||||
"quit": "退出",
|
||||
@ -112,7 +112,7 @@
|
||||
"toggle-dev-tools": "切換開發者人員工具"
|
||||
}
|
||||
},
|
||||
"always-on-top": "永遠顯示在最上層",
|
||||
"always-on-top": "最上層顯示",
|
||||
"auto-update": "自動更新",
|
||||
"hide-menu": {
|
||||
"dialog": {
|
||||
@ -128,22 +128,22 @@
|
||||
},
|
||||
"label": "語言",
|
||||
"submenu": {
|
||||
"to-help-translate": "想要協助翻譯?按一下這裡"
|
||||
"to-help-translate": "想協助翻譯?按一下這裡"
|
||||
}
|
||||
},
|
||||
"resume-on-start": "應用啟動時繼續上次播放的歌曲",
|
||||
"single-instance-lock": "單視窗鎖定",
|
||||
"single-instance-lock": "單實例模式",
|
||||
"start-at-login": "開機時啟動",
|
||||
"starting-page": {
|
||||
"label": "啟動頁面",
|
||||
"unset": "不指定"
|
||||
},
|
||||
"tray": {
|
||||
"label": "系統閘圖式",
|
||||
"label": "系統閘",
|
||||
"submenu": {
|
||||
"disabled": "已停用",
|
||||
"enabled-and-hide-app": "啟用並隱藏應用程式",
|
||||
"enabled-and-show-app": "啟用並顯示應用程式",
|
||||
"enabled-and-hide-app": "啟用並最小化應用程式",
|
||||
"enabled-and-show-app": "啟用但持續顯示應用程式",
|
||||
"play-pause-on-click": "點擊時播放/暫停"
|
||||
}
|
||||
},
|
||||
@ -514,7 +514,7 @@
|
||||
"picture-in-picture": {
|
||||
"description": "允許應用程式切換至子母畫面模式",
|
||||
"menu": {
|
||||
"always-on-top": "永遠顯示在最上層",
|
||||
"always-on-top": "最上層顯示",
|
||||
"hotkey": {
|
||||
"label": "快捷鍵",
|
||||
"prompt": {
|
||||
|
||||
145
src/plugins/webnowplaying/index.ts
Normal file
145
src/plugins/webnowplaying/index.ts
Normal file
@ -0,0 +1,145 @@
|
||||
import { net } from 'electron';
|
||||
|
||||
import is from 'electron-is';
|
||||
|
||||
import { createPlugin } from '@/utils';
|
||||
import registerCallback from '@/providers/song-info';
|
||||
import { t } from '@/i18n';
|
||||
|
||||
import { WebSocket } from 'ws';
|
||||
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket';
|
||||
|
||||
import type { RepeatMode } from '@/types/datahost-get-state';
|
||||
|
||||
interface Data {
|
||||
player: string;
|
||||
state: 'PLAYING' | 'PAUSED' | 'STOPPED';
|
||||
title: string;
|
||||
artist: string;
|
||||
album: string;
|
||||
cover: string;
|
||||
duration: string;
|
||||
position: string;
|
||||
volume: number;
|
||||
rating: number;
|
||||
repeat: 'ALL' | 'ONE' | 'NONE';
|
||||
shuffle: boolean;
|
||||
}
|
||||
|
||||
export default createPlugin({
|
||||
name: () => t('plugins.webnowplaying.name'),
|
||||
description: () => t('plugins.webnowplaying.description'),
|
||||
restartNeeded: true,
|
||||
config: {
|
||||
enabled: false,
|
||||
},
|
||||
backend: {
|
||||
liteMode: false,
|
||||
data: {
|
||||
player: 'YouTube Music',
|
||||
state: 'STOPPED',
|
||||
title: '',
|
||||
artist: '',
|
||||
album: '',
|
||||
cover: '',
|
||||
duration: '0:00',
|
||||
// position and volume are fetched in sendUpdate()
|
||||
position: '0:00',
|
||||
volume: 100,
|
||||
rating: 0,
|
||||
repeat: 'NONE',
|
||||
shuffle: false
|
||||
} as Data,
|
||||
start({ ipc }) {
|
||||
const timeInSecondsToString = (timeInSeconds: number) => {
|
||||
const timeInMinutes = Math.floor(timeInSeconds / 60);
|
||||
if (timeInMinutes < 60) return `${timeInMinutes}:${Math.floor(timeInSeconds % 60).toString().padStart(2, '0')}`;
|
||||
|
||||
return `${Math.floor(timeInMinutes / 60)}:${Math.floor(timeInMinutes % 60).toString().padStart(2, '0')}:${Math.floor(timeInSeconds % 60).toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const ws = new ReconnectingWebSocket('ws://localhost:8974', undefined, {
|
||||
WebSocket: WebSocket,
|
||||
maxEnqueuedMessages: 0,
|
||||
});
|
||||
ws.onmessage = () => {
|
||||
|
||||
};
|
||||
|
||||
const post = (data: Data) => {
|
||||
const port = 1608;
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Access-Control-Allow-Headers': '*',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
};
|
||||
const url = `http://127.0.0.1:${port}/`;
|
||||
net
|
||||
.fetch(url, {
|
||||
method: this.liteMode ? 'OPTIONS' : 'POST',
|
||||
headers,
|
||||
keepalive: true,
|
||||
body: this.liteMode ? undefined : JSON.stringify({ data }),
|
||||
})
|
||||
.then(() => {
|
||||
if (this.liteMode) {
|
||||
this.liteMode = false;
|
||||
console.debug(
|
||||
`obs-tuna webserver at port ${port} is now accessible. disable lite mode`,
|
||||
);
|
||||
post(data);
|
||||
}
|
||||
})
|
||||
.catch((error: { code: number; errno: number }) => {
|
||||
if (!this.liteMode && is.dev()) {
|
||||
console.debug(
|
||||
`Error: '${
|
||||
error.code || error.errno
|
||||
}' - when trying to access obs-tuna webserver at port ${port}. enable lite mode`,
|
||||
);
|
||||
this.liteMode = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ipc.on('ytmd:player-api-loaded', () => {
|
||||
ipc.send('ytmd:setup-time-changed-listener');
|
||||
ipc.send('ytmd:setup-repeat-changed-listener');
|
||||
ipc.send('ytmd:setup-volume-changed-listener');
|
||||
});
|
||||
ipc.on('ytmd:time-changed', (t: number) => {
|
||||
if (!this.data.title) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.data.position = timeInSecondsToString(t);
|
||||
post(this.data);
|
||||
});
|
||||
ipc.on('ytmd:repeat-changed', (mode: RepeatMode) => {
|
||||
this.data.repeat = mode;
|
||||
post(this.data);
|
||||
});
|
||||
ipc.on('ytmd:volume-changed', (newVolume: number) => {
|
||||
this.data.volume = newVolume;
|
||||
post(this.data);
|
||||
});
|
||||
|
||||
registerCallback((songInfo) => {
|
||||
if (!songInfo.title && !songInfo.artist) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.data.duration = timeInSecondsToString(songInfo.songDuration);
|
||||
this.data.position = timeInSecondsToString(songInfo.elapsedSeconds ?? 0);
|
||||
this.data.cover = songInfo.imageSrc ?? '';
|
||||
this.data.title = songInfo.title;
|
||||
this.data.artist = songInfo.artist;
|
||||
this.data.state = songInfo.isPaused ? 'PAUSED' : 'PLAYING';
|
||||
this.data.album = songInfo.album ?? '';
|
||||
post(this.data);
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -209,10 +209,19 @@ const registerProvider = (win: BrowserWindow) => {
|
||||
};
|
||||
|
||||
const suffixesToRemove = [
|
||||
' - topic',
|
||||
'vevo',
|
||||
' (performance video)',
|
||||
' (clip official)',
|
||||
// Artist names
|
||||
/\s*(- topic)$/i,
|
||||
/\s*vevo$/i,
|
||||
|
||||
// Video titles
|
||||
/\s*[(|\[]official(.*?)[)|\]]/i, // (Official Music Video), [Official Visualizer], etc...
|
||||
/\s*[(|\[]((lyrics?|visualizer|audio)\s*(video)?)[)|\]]/i,
|
||||
/\s*[(|\[](performance video)[)|\]]/i,
|
||||
/\s*[(|\[](clip official)[)|\]]/i,
|
||||
/\s*[(|\[](video version)[)|\]]/i,
|
||||
/\s*[(|\[](HD|HQ)\s*?(?:audio)?[)|\]]$/i,
|
||||
/\s*[(|\[](live)[)|\]]$/i,
|
||||
/\s*[(|\[]4K\s*?(?:upgrade)?[)|\]]$/i,
|
||||
];
|
||||
|
||||
export function cleanupName(name: string): string {
|
||||
@ -220,15 +229,8 @@ export function cleanupName(name: string): string {
|
||||
return name;
|
||||
}
|
||||
|
||||
name = name.replace(
|
||||
/\((?:official)? ?(?:music)? ?(?:lyrics?)? ?(?:video)?\)$/i,
|
||||
'',
|
||||
);
|
||||
const lowCaseName = name.toLowerCase();
|
||||
for (const suffix of suffixesToRemove) {
|
||||
if (lowCaseName.endsWith(suffix)) {
|
||||
return name.slice(0, -suffix.length);
|
||||
}
|
||||
name = name.replace(suffix, '');
|
||||
}
|
||||
|
||||
return name;
|
||||
|
||||
@ -59,7 +59,7 @@ async function onApiLoaded() {
|
||||
});
|
||||
window.ipcRenderer.on('ytmd:switch-repeat', (_, repeat = 1) => {
|
||||
for (let i = 0; i < repeat; i++) {
|
||||
document.querySelector<HTMLElement & { onRepeatButtonTap: () => void }>('ytmusic-player-bar')?.onRepeatButtonTap();
|
||||
document.querySelector<HTMLElement & { onRepeatButtonClick: () => void }>('ytmusic-player-bar')?.onRepeatButtonClick();
|
||||
}
|
||||
});
|
||||
window.ipcRenderer.on('ytmd:update-volume', (_, volume: number) => {
|
||||
|
||||
Reference in New Issue
Block a user