mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 18:41:47 +00:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b3cbe8e01 | |||
| 67a89e8ed4 | |||
| 464a2b94ea | |||
| 9357a15116 | |||
| ee820bb01c | |||
| 6b81735811 | |||
| 8ce91b143a | |||
| 116dbad9bc | |||
| 977af3d617 | |||
| 6da8defc73 | |||
| 0e93a963e1 | |||
| 1e98b2e75a | |||
| 6f5f13a840 | |||
| 822bcedadf | |||
| 2b6aea82c3 | |||
| 4f4efb407e | |||
| 6159e0e652 | |||
| 3957e06174 | |||
| c78f823b9b | |||
| 1be3bb360e | |||
| ba2afd2652 | |||
| 5e283c9ea5 | |||
| ddb1c56111 | |||
| ebd167f3f2 | |||
| 178a62b9d3 | |||
| f98a2cf766 | |||
| fdbe6f7331 | |||
| 57c2cdc91e | |||
| 0f5074f8ab | |||
| 661396226d | |||
| 36f27fe2e6 | |||
| adf1ce4bc7 | |||
| 43b4b8df5e | |||
| 4a8440c281 | |||
| 32fe9fcffe | |||
| a9896845da | |||
| a59aa07334 | |||
| e07d7395e7 | |||
| 9bb6f32ece | |||
| ccb19a0dc9 | |||
| 64fb6c2597 | |||
| 73c3e355fe | |||
| fc7a504643 | |||
| 764dc0f895 | |||
| 9f33f49ec4 | |||
| 87ae6d29bb | |||
| 093c8e3ca6 | |||
| fec26a010d | |||
| 5d8aaccc55 | |||
| cda03078a9 | |||
| 9c139b96f4 | |||
| 9b2816c156 | |||
| b1b8847134 | |||
| bf9e698288 | |||
| 28e8a1c5dd | |||
| 18e0b1b863 |
69
changelog.md
69
changelog.md
@ -2,8 +2,77 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
||||
|
||||
#### [v3.4.1](https://github.com/th-ch/youtube-music/compare/v3.4.0...v3.4.1)
|
||||
|
||||
- fix(mpris): fix mpris position [`#2225`](https://github.com/th-ch/youtube-music/issues/2225)
|
||||
- fix(deb): fix depends [`#1983`](https://github.com/th-ch/youtube-music/issues/1983)
|
||||
- fix: fix touchbar icon [`#2183`](https://github.com/th-ch/youtube-music/issues/2183)
|
||||
- fix: fix "Starting page" [`#1822`](https://github.com/th-ch/youtube-music/issues/1822)
|
||||
- fix: fix album actions [`#2202`](https://github.com/th-ch/youtube-music/issues/2202)
|
||||
- fix: fix playback slider [`#2045`](https://github.com/th-ch/youtube-music/issues/2045)
|
||||
- chore(i18n): Translated using Weblate (Spanish) [`91bee48`](https://github.com/th-ch/youtube-music/commit/91bee4880ed2c6fdd887814a2620877d89bea311)
|
||||
- Bump version to 3.4.1 [`02e2fb6`](https://github.com/th-ch/youtube-music/commit/02e2fb6a83844f439f760e72cdcb935b86000df2)
|
||||
|
||||
#### [v3.4.0](https://github.com/th-ch/youtube-music/compare/v3.3.12...v3.4.0)
|
||||
|
||||
> 14 July 2024
|
||||
|
||||
- fix(deps): update dependency i18next to v23.12.1 [`#2230`](https://github.com/th-ch/youtube-music/pull/2230)
|
||||
- feat(downloader): New option to download on finish [`#1964`](https://github.com/th-ch/youtube-music/pull/1964)
|
||||
- chore(deps): update typescript-eslint monorepo to v8.0.0-alpha.42 [`#2228`](https://github.com/th-ch/youtube-music/pull/2228)
|
||||
- chore(deps): update dependency eslint to v9.7.0 [`#2226`](https://github.com/th-ch/youtube-music/pull/2226)
|
||||
- chore(deps): update dependency @babel/runtime to v7.24.8 [`#2221`](https://github.com/th-ch/youtube-music/pull/2221)
|
||||
- chore(deps): update dependency node-gyp to v10.2.0 [`#2216`](https://github.com/th-ch/youtube-music/pull/2216)
|
||||
- chore(deps): update dependency ws to v8.18.0 [`#2217`](https://github.com/th-ch/youtube-music/pull/2217)
|
||||
- chore(deps): update dependency glob to v11 [`#2219`](https://github.com/th-ch/youtube-music/pull/2219)
|
||||
- chore(deps): update dependency esbuild to v0.23.0 [`#2215`](https://github.com/th-ch/youtube-music/pull/2215)
|
||||
- chore(deps): update dependency electron to v31.2.0 [`#2214`](https://github.com/th-ch/youtube-music/pull/2214)
|
||||
- fix(deps): update dependency youtubei.js to v10.1.0 [`#2218`](https://github.com/th-ch/youtube-music/pull/2218)
|
||||
- chore(deps): update playwright monorepo to v1.45.1 [`#2212`](https://github.com/th-ch/youtube-music/pull/2212)
|
||||
- chore(deps): update typescript-eslint monorepo to v8.0.0-alpha.41 [`#2213`](https://github.com/th-ch/youtube-music/pull/2213)
|
||||
- chore(deps): update dependency rollup to v4.18.1 [`#2210`](https://github.com/th-ch/youtube-music/pull/2210)
|
||||
- chore(deps): update dependency eslint to v9.6.0 [`#2192`](https://github.com/th-ch/youtube-music/pull/2192)
|
||||
- chore(deps): update dependency vite to v5.3.3 [`#2211`](https://github.com/th-ch/youtube-music/pull/2211)
|
||||
- chore(deps): update dependency glob to v10.4.5 [`#2205`](https://github.com/th-ch/youtube-music/pull/2205)
|
||||
- chore(deps): update dependency discord-api-types to v0.37.92 [`#2204`](https://github.com/th-ch/youtube-music/pull/2204)
|
||||
- fix(deps): update dependency solid-js to v1.8.18 [`#2189`](https://github.com/th-ch/youtube-music/pull/2189)
|
||||
- chore(deps): update dependency typescript to v5.5.3 [`#2206`](https://github.com/th-ch/youtube-music/pull/2206)
|
||||
- chore(deps): update dependency electron to v31.1.0 [`#2190`](https://github.com/th-ch/youtube-music/pull/2190)
|
||||
- chore(deps): update typescript-eslint monorepo to v8.0.0-alpha.40 [`#2193`](https://github.com/th-ch/youtube-music/pull/2193)
|
||||
- fix(deps): update dependency @floating-ui/dom to v1.6.7 [`#2196`](https://github.com/th-ch/youtube-music/pull/2196)
|
||||
- chore(deps): update dependency vite to v5.3.2 [`#2188`](https://github.com/th-ch/youtube-music/pull/2188)
|
||||
- chore(deps): update dependency discord-api-types to v0.37.91 [`#2187`](https://github.com/th-ch/youtube-music/pull/2187)
|
||||
- chore(deps): update typescript-eslint monorepo to v8.0.0-alpha.34 [`#2184`](https://github.com/th-ch/youtube-music/pull/2184)
|
||||
- fix(deps): update dependency @floating-ui/dom to v1.6.6 [`#2182`](https://github.com/th-ch/youtube-music/pull/2182)
|
||||
- chore(deps): update playwright monorepo to v1.45.0 [`#2181`](https://github.com/th-ch/youtube-music/pull/2181)
|
||||
- fix(deps): update dependency ts-morph to v23 [`#2180`](https://github.com/th-ch/youtube-music/pull/2180)
|
||||
- chore(deps): update dependency electron-vite to v2.3.0 [`#2178`](https://github.com/th-ch/youtube-music/pull/2178)
|
||||
- fix(deps): update dependency conf to v13.0.1 [`#2175`](https://github.com/th-ch/youtube-music/pull/2175)
|
||||
- chore(deps): update dependency glob to v10.4.2 [`#2168`](https://github.com/th-ch/youtube-music/pull/2168)
|
||||
- chore(deps): update dependency discord-api-types to v0.37.90 [`#2167`](https://github.com/th-ch/youtube-music/pull/2167)
|
||||
- chore(deps): update dependency typescript to v5.5.2 [`#2173`](https://github.com/th-ch/youtube-music/pull/2173)
|
||||
- chore(deps): update dependency electron to v31.0.2 [`#2170`](https://github.com/th-ch/youtube-music/pull/2170)
|
||||
- chore(deps): update dependency ws to v8.17.1 [`#2164`](https://github.com/th-ch/youtube-music/pull/2164)
|
||||
- chore(deps): update dependency eslint to v9.5.0 [`#2162`](https://github.com/th-ch/youtube-music/pull/2162)
|
||||
- fix(deps): update dependency youtubei.js to v10 [`#2136`](https://github.com/th-ch/youtube-music/pull/2136)
|
||||
- chore(deps): update dependency discord-api-types to v0.37.89 [`#2153`](https://github.com/th-ch/youtube-music/pull/2153)
|
||||
- chore(deps): update dependency vite to v5.3.1 [`#2154`](https://github.com/th-ch/youtube-music/pull/2154)
|
||||
- fix(deps): update dependency electron-store to v10 [`#2157`](https://github.com/th-ch/youtube-music/pull/2157)
|
||||
- fix(deps): update dependency conf to v13 [`#2156`](https://github.com/th-ch/youtube-music/pull/2156)
|
||||
- chore(deps): update dependency electron to v31.0.1 [`#2148`](https://github.com/th-ch/youtube-music/pull/2148)
|
||||
- chore(deps): update dependency discord-api-types to v0.37.88 [`#2138`](https://github.com/th-ch/youtube-music/pull/2138)
|
||||
- chore(deps): update typescript-eslint monorepo to v8.0.0-alpha.30 [`#2139`](https://github.com/th-ch/youtube-music/pull/2139)
|
||||
- chore(deps): update dependency electron to v31 [`#2141`](https://github.com/th-ch/youtube-music/pull/2141)
|
||||
- chore(deps): update dependency esbuild to v0.21.5 [`#2135`](https://github.com/th-ch/youtube-music/pull/2135)
|
||||
- chore(deps): update typescript-eslint monorepo to v8.0.0-alpha.29 [`#2132`](https://github.com/th-ch/youtube-music/pull/2132)
|
||||
- fix: rollback eslint version to v8 [`45931a2`](https://github.com/th-ch/youtube-music/commit/45931a25b08ab8a406f9e102486585311fd14bf9)
|
||||
- chore(i18n): Translated using Weblate (Filipino) [`8a20566`](https://github.com/th-ch/youtube-music/commit/8a20566e0f2736f72d46282188ada69df1d7076a)
|
||||
- chore(i18n): Translated using Weblate (Slovenian) [`40f0b9b`](https://github.com/th-ch/youtube-music/commit/40f0b9b852dcd9146e1c1e6c741b5baaf55ac079)
|
||||
|
||||
#### [v3.3.12](https://github.com/th-ch/youtube-music/compare/v3.3.11...v3.3.12)
|
||||
|
||||
> 8 June 2024
|
||||
|
||||
- hotfix: Revert "chore(deps): update dependencies `@cliqz/adblocker-electron`, `@cliqz/adblocker-electron-preload`" [`3c4abc1`](https://github.com/th-ch/youtube-music/commit/3c4abc14187e51f7e47c1ae71b3513f6d8c9912a)
|
||||
- Update changelog for v3.3.11 [`de22444`](https://github.com/th-ch/youtube-music/commit/de224444c2a6d9030aa22a3b263ceacbc4b41914)
|
||||
- Bump version to 3.3.12 [`89ed7d2`](https://github.com/th-ch/youtube-music/commit/89ed7d2345001fea59514944f4c1d56d2b7bd888)
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { resolve, dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { UserConfig } from 'vite';
|
||||
import { defineConfig, defineViteConfig } from 'electron-vite';
|
||||
import builtinModules from 'builtin-modules';
|
||||
import viteResolve from 'vite-plugin-resolve';
|
||||
import Inspect from 'vite-plugin-inspect';
|
||||
import solidPlugin from 'vite-plugin-solid';
|
||||
|
||||
import { pluginVirtualModuleGenerator } from './vite-plugins/plugin-importer.mjs';
|
||||
import pluginLoader from './vite-plugins/plugin-loader.mjs';
|
||||
|
||||
import type { UserConfig } from 'vite';
|
||||
import { i18nImporter } from './vite-plugins/i18n-importer.mjs';
|
||||
import solidPlugin from 'vite-plugin-solid';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
@ -52,7 +52,10 @@ export default defineConfig({
|
||||
|
||||
if (mode === 'development') {
|
||||
commonConfig.plugins?.push(
|
||||
Inspect({ build: true, outputDir: join(__dirname, '.vite-inspect/backend') }),
|
||||
Inspect({
|
||||
build: true,
|
||||
outputDir: join(__dirname, '.vite-inspect/backend'),
|
||||
}),
|
||||
);
|
||||
return commonConfig;
|
||||
}
|
||||
@ -96,7 +99,10 @@ export default defineConfig({
|
||||
|
||||
if (mode === 'development') {
|
||||
commonConfig.plugins?.push(
|
||||
Inspect({ build: true, outputDir: join(__dirname, '.vite-inspect/preload') }),
|
||||
Inspect({
|
||||
build: true,
|
||||
outputDir: join(__dirname, '.vite-inspect/preload'),
|
||||
}),
|
||||
);
|
||||
return commonConfig;
|
||||
}
|
||||
@ -143,7 +149,10 @@ export default defineConfig({
|
||||
|
||||
if (mode === 'development') {
|
||||
commonConfig.plugins?.push(
|
||||
Inspect({ build: true, outputDir: join(__dirname, '.vite-inspect/renderer') }),
|
||||
Inspect({
|
||||
build: true,
|
||||
outputDir: join(__dirname, '.vite-inspect/renderer'),
|
||||
}),
|
||||
);
|
||||
return commonConfig;
|
||||
}
|
||||
|
||||
49
package.json
49
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "youtube-music",
|
||||
"productName": "YouTube Music",
|
||||
"version": "3.4.1",
|
||||
"version": "3.5.0",
|
||||
"description": "YouTube Music Desktop App - including custom plugins",
|
||||
"main": "./dist/main/index.js",
|
||||
"license": "MIT",
|
||||
@ -145,11 +145,11 @@
|
||||
"xml2js": "0.6.2",
|
||||
"node-fetch": "3.3.2",
|
||||
"@electron/universal": "2.0.1",
|
||||
"@babel/runtime": "7.24.8"
|
||||
"@babel/runtime": "7.25.0"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"vudio@2.1.1": "patches/vudio@2.1.1.patch",
|
||||
"@xhayper/discord-rpc@1.1.2": "patches/@xhayper__discord-rpc@1.1.2.patch",
|
||||
"@xhayper/discord-rpc@1.1.4": "patches/@xhayper__discord-rpc@1.1.4.patch",
|
||||
"app-builder-lib@24.13.3": "patches/app-builder-lib@24.13.3.patch"
|
||||
}
|
||||
},
|
||||
@ -160,64 +160,65 @@
|
||||
"@electron/remote": "2.1.2",
|
||||
"@ffmpeg.wasm/core-mt": "0.12.0",
|
||||
"@ffmpeg.wasm/main": "0.12.0",
|
||||
"@floating-ui/dom": "1.6.7",
|
||||
"@floating-ui/dom": "1.6.8",
|
||||
"@foobar404/wave": "2.0.5",
|
||||
"@jellybrick/electron-better-web-request": "1.0.4",
|
||||
"@jellybrick/mpris-service": "2.1.4",
|
||||
"@xhayper/discord-rpc": "1.1.2",
|
||||
"@skyra/jaro-winkler": "^1.1.1",
|
||||
"@xhayper/discord-rpc": "1.1.4",
|
||||
"async-mutex": "0.5.0",
|
||||
"butterchurn": "3.0.0-beta.4",
|
||||
"butterchurn-presets": "3.0.0-beta.4",
|
||||
"color": "4.2.3",
|
||||
"conf": "13.0.1",
|
||||
"custom-electron-prompt": "1.5.7",
|
||||
"custom-electron-prompt": "1.5.8",
|
||||
"dbus-next": "0.10.2",
|
||||
"deepmerge-ts": "7.0.3",
|
||||
"deepmerge-ts": "7.1.0",
|
||||
"electron-debug": "4.0.0",
|
||||
"electron-is": "3.0.0",
|
||||
"electron-localshortcut": "3.2.1",
|
||||
"electron-store": "10.0.0",
|
||||
"electron-unhandled": "4.0.1",
|
||||
"electron-updater": "6.2.1",
|
||||
"electron-updater": "6.3.2",
|
||||
"fast-average-color": "9.4.0",
|
||||
"fast-equals": "5.0.1",
|
||||
"filenamify": "6.0.0",
|
||||
"howler": "2.2.4",
|
||||
"html-to-text": "9.0.5",
|
||||
"i18next": "23.12.1",
|
||||
"i18next": "23.12.2",
|
||||
"keyboardevent-from-electron-accelerator": "2.0.0",
|
||||
"keyboardevents-areequal": "0.2.2",
|
||||
"node-html-parser": "6.1.13",
|
||||
"node-id3": "0.2.6",
|
||||
"peerjs": "1.5.4",
|
||||
"semver": "7.6.2",
|
||||
"semver": "7.6.3",
|
||||
"serve": "14.2.3",
|
||||
"simple-youtube-age-restriction-bypass": "github:organization/Simple-YouTube-Age-Restriction-Bypass#v2.5.9",
|
||||
"solid-floating-ui": "0.3.1",
|
||||
"solid-js": "1.8.18",
|
||||
"solid-js": "1.8.19",
|
||||
"solid-styled-components": "0.28.5",
|
||||
"solid-transition-group": "0.2.3",
|
||||
"ts-morph": "23.0.0",
|
||||
"vudio": "2.1.1",
|
||||
"x11": "2.3.0",
|
||||
"youtubei.js": "10.1.0"
|
||||
"youtubei.js": "10.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.45.1",
|
||||
"@playwright/test": "1.45.3",
|
||||
"@total-typescript/ts-reset": "0.5.1",
|
||||
"@types/color": "3.0.6",
|
||||
"@types/electron-localshortcut": "3.1.3",
|
||||
"@types/howler": "2.2.11",
|
||||
"@types/html-to-text": "9.0.4",
|
||||
"@types/semver": "7.5.8",
|
||||
"@typescript-eslint/eslint-plugin": "7.16.0",
|
||||
"@typescript-eslint/parser": "7.16.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.18.0",
|
||||
"@typescript-eslint/parser": "7.18.0",
|
||||
"bufferutil": "4.0.8",
|
||||
"builtin-modules": "4.0.0",
|
||||
"cross-env": "7.0.3",
|
||||
"del-cli": "5.1.0",
|
||||
"discord-api-types": "0.37.92",
|
||||
"electron": "31.2.0",
|
||||
"discord-api-types": "0.37.93",
|
||||
"electron": "31.3.1",
|
||||
"electron-builder": "24.13.3",
|
||||
"electron-devtools-installer": "3.2.0",
|
||||
"electron-vite": "2.3.0",
|
||||
@ -226,16 +227,16 @@
|
||||
"eslint-import-resolver-exports": "1.0.0-beta.5",
|
||||
"eslint-import-resolver-typescript": "3.6.1",
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eslint-plugin-prettier": "5.1.3",
|
||||
"eslint-plugin-prettier": "5.2.1",
|
||||
"glob": "11.0.0",
|
||||
"node-gyp": "10.2.0",
|
||||
"playwright": "1.45.1",
|
||||
"rollup": "4.18.1",
|
||||
"typescript": "5.5.3",
|
||||
"playwright": "1.45.3",
|
||||
"rollup": "4.19.1",
|
||||
"typescript": "5.5.4",
|
||||
"utf-8-validate": "6.0.4",
|
||||
"vite": "5.3.3",
|
||||
"vite-plugin-inspect": "0.8.4",
|
||||
"vite-plugin-resolve": "2.5.1",
|
||||
"vite": "5.3.5",
|
||||
"vite-plugin-inspect": "0.8.5",
|
||||
"vite-plugin-resolve": "2.5.2",
|
||||
"vite-plugin-solid": "2.10.2",
|
||||
"ws": "8.18.0"
|
||||
},
|
||||
|
||||
@ -5,13 +5,13 @@ index 40db5dfbd8a4455ce2987d8115eca9882e1f9f14..414fc6986b9c0cc288908eb0107b90c4
|
||||
@@ -25,11 +25,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.6.2",
|
||||
- "ws": "^8.15.1"
|
||||
"axios": "^1.7.2",
|
||||
- "ws": "^8.18.0"
|
||||
- },
|
||||
- "optionalDependencies": {
|
||||
- "bufferutil": "^4.0.8",
|
||||
- "utf-8-validate": "^6.0.3"
|
||||
+ "ws": "^8.16.0"
|
||||
- "utf-8-validate": "^6.0.4"
|
||||
+ "ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^14.*",
|
||||
756
pnpm-lock.yaml
generated
756
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
717
src/i18n/resources/ca.json
Normal file
717
src/i18n/resources/ca.json
Normal file
@ -0,0 +1,717 @@
|
||||
{
|
||||
"common": {
|
||||
"console": {
|
||||
"plugins": {
|
||||
"execute-failed": "Ha fallat l'execució de l'extensió {{pluginName}}::{{contextName}}",
|
||||
"executed-at-ms": "L'extensió {{pluginName}}::{{contextName}} s'ha executat als {{ms}}ms",
|
||||
"initialize-failed": "Ha fallat la inicialització de l'extensió «{{pluginName}}»",
|
||||
"load-all": "Carregant totes les extensions",
|
||||
"load-failed": "Error al carregar l'extensió «{{pluginName}}»",
|
||||
"loaded": "L'extensió «{{pluginName}}» s'ha carregat",
|
||||
"unload-failed": "Error al deshabilitar l'extensió «{{pluginName}}»",
|
||||
"unloaded": "Extensió «{{pluginName}}» deshabilitada"
|
||||
}
|
||||
}
|
||||
},
|
||||
"language": {
|
||||
"code": "ca",
|
||||
"local-name": "català",
|
||||
"name": "Catalan"
|
||||
},
|
||||
"main": {
|
||||
"console": {
|
||||
"did-finish-load": {
|
||||
"dev-tools": "Càrrega finalitzada. S'han obert les DevTools"
|
||||
},
|
||||
"i18n": {
|
||||
"loaded": "i18n carregat"
|
||||
},
|
||||
"second-instance": {
|
||||
"receive-command": "Comanda rebuda a través del protocol: «{{command}}»"
|
||||
},
|
||||
"theme": {
|
||||
"css-file-not-found": "L'arxiu CSS «{{cssFile}}» no existeix, s'ha ignorat"
|
||||
},
|
||||
"unresponsive": {
|
||||
"details": "Error sense resposta!\n{{error}}"
|
||||
},
|
||||
"when-ready": {
|
||||
"clearing-cache-after-20s": "Netejant la memòria cau de l'aplicació"
|
||||
},
|
||||
"window": {
|
||||
"tried-to-render-offscreen": "La finestra s'ha intentat mostrar fora de la pantalla, windowSize={{windowSize}}, displaySize={{displaySize}}, position={{position}}"
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
"hide-menu-enabled": {
|
||||
"detail": "El menú es troba amagat, premi «Alt» per mostrar-lo (o «Escapament» si utilitza el menú integrat In-App)",
|
||||
"message": "S'ha habilitat l'amagament del menú",
|
||||
"title": "Amagament del menú habilitat"
|
||||
},
|
||||
"need-to-restart": {
|
||||
"buttons": {
|
||||
"later": "Més tard",
|
||||
"restart-now": "Reinicia ara"
|
||||
},
|
||||
"detail": "L'extensió «{{pluginName}}» requereix reiniciar l'aplicació per fer tenir efecte",
|
||||
"message": "\"{{pluginName}}\" necessita reiniciar-se",
|
||||
"title": "Es requereix reiniciar"
|
||||
},
|
||||
"unresponsive": {
|
||||
"buttons": {
|
||||
"quit": "Marxar",
|
||||
"relaunch": "Rellançar",
|
||||
"wait": "Espera"
|
||||
},
|
||||
"detail": "Ho sentim per les molèsties! si us plau, tria què fer:",
|
||||
"message": "L'aplicació ha deixat de respondre",
|
||||
"title": "La finestra ha deixat de respondre"
|
||||
},
|
||||
"update-available": {
|
||||
"buttons": {
|
||||
"disable": "Deshabilita les actualitzacions",
|
||||
"download": "Descarrega",
|
||||
"ok": "D'acord"
|
||||
},
|
||||
"detail": "Hi ha una nova versió disponible i pot ser descarregada a {{downloadLink}}",
|
||||
"message": "Hi ha una nova versió disponible",
|
||||
"title": "Actualització disponible"
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"about": "Quant a",
|
||||
"navigation": {
|
||||
"label": "Navegació",
|
||||
"submenu": {
|
||||
"copy-current-url": "Copia l'URL actual",
|
||||
"go-back": "Anar enrere",
|
||||
"go-forward": "Anar endavant",
|
||||
"quit": "Sortir",
|
||||
"restart": "Reinicia l'aplicació"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"label": "Opcions",
|
||||
"submenu": {
|
||||
"advanced-options": {
|
||||
"label": "Opcions avançades",
|
||||
"submenu": {
|
||||
"auto-reset-app-cache": "Reinicialitza la memòria cau de l'aplicació quan es reiniciï",
|
||||
"disable-hardware-acceleration": "Deshabilita l'acceleració per hardware",
|
||||
"edit-config-json": "Edita el config.json",
|
||||
"override-user-agent": "Sobreescriu l'agent d'usuari (User-Agent)",
|
||||
"restart-on-config-changes": "Reinicia quan es canviï la configuració",
|
||||
"set-proxy": {
|
||||
"label": "Definir proxy",
|
||||
"prompt": {
|
||||
"label": "Introduir l'adreça del proxy: (deixar en blanc per deshabilitar)",
|
||||
"placeholder": "Exemple: SOCKS5://127.0.0.1:9999",
|
||||
"title": "Definir proxy"
|
||||
}
|
||||
},
|
||||
"toggle-dev-tools": "Commuta les DevTools"
|
||||
}
|
||||
},
|
||||
"always-on-top": "Mostra sempre per sobre",
|
||||
"auto-update": "Actualitza automàticament",
|
||||
"hide-menu": {
|
||||
"dialog": {
|
||||
"message": "El menú s'amagarà la següent vegada que s'iniciï l'aplicació, prem «Alt» per mostrar-lo (o accent obert « ` » si utilitza el menú integrat In-App)",
|
||||
"title": "Amagament del menú habilitat"
|
||||
},
|
||||
"label": "Amaga el menú"
|
||||
},
|
||||
"language": {
|
||||
"dialog": {
|
||||
"message": "L'idioma es canviarà un cop es reiniciï",
|
||||
"title": "Idioma canviat"
|
||||
},
|
||||
"label": "Idioma",
|
||||
"submenu": {
|
||||
"to-help-translate": "Vols ajudar a traduir? Clica aquí"
|
||||
}
|
||||
},
|
||||
"resume-on-start": "Reprèn l'última cançó quan s'inicia l'aplicació",
|
||||
"single-instance-lock": "Bloqueja en una única instància",
|
||||
"start-at-login": "Obre a l'iniciar sessió",
|
||||
"starting-page": {
|
||||
"label": "Pàgina d'inici",
|
||||
"unset": "Sense establir"
|
||||
},
|
||||
"tray": {
|
||||
"label": "Safata d'icones",
|
||||
"submenu": {
|
||||
"disabled": "Deshabilitat",
|
||||
"enabled-and-hide-app": "Mostra la icona i amaga l'aplicació",
|
||||
"enabled-and-show-app": "Mostra la icona i mostra l'aplicació",
|
||||
"play-pause-on-click": "Reproduir/Pausar en clicar"
|
||||
}
|
||||
},
|
||||
"visual-tweaks": {
|
||||
"label": "Configuració visual",
|
||||
"submenu": {
|
||||
"like-buttons": {
|
||||
"default": "Per defecte",
|
||||
"force-show": "Força que es mostri",
|
||||
"hide": "Amaga",
|
||||
"label": "Botons de «m'agrada»"
|
||||
},
|
||||
"remove-upgrade-button": "Elimina el botó «Actualitza a Music Premium»",
|
||||
"theme": {
|
||||
"dialog": {
|
||||
"button": {
|
||||
"cancel": "Cancel·la",
|
||||
"remove": "Elimina"
|
||||
},
|
||||
"remove-theme": "De debó vols eliminar el tema personalitzat?",
|
||||
"remove-theme-message": "Això eliminarà el tema personalitzat"
|
||||
},
|
||||
"label": "Tema",
|
||||
"submenu": {
|
||||
"import-css-file": "Importa un arxiu CSS personalitzat",
|
||||
"no-theme": "Cap tema"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"enabled": "Habilitat",
|
||||
"label": "Extensions",
|
||||
"new": "NOU"
|
||||
},
|
||||
"view": {
|
||||
"label": "Mostra",
|
||||
"submenu": {
|
||||
"force-reload": "Força recàrrega",
|
||||
"reload": "Recarrega",
|
||||
"reset-zoom": "Mida real",
|
||||
"toggle-fullscreen": "Commuta la pantalla completa",
|
||||
"zoom-in": "Apropa el zoom",
|
||||
"zoom-out": "Allunya el zoom"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tray": {
|
||||
"next": "Següent",
|
||||
"play-pause": "Reprodueix/Pausa",
|
||||
"previous": "Anterior",
|
||||
"quit": "Tanca",
|
||||
"restart": "Reinicia l'aplicació",
|
||||
"show": "Mostra la finestra",
|
||||
"tooltip": {
|
||||
"default": "YouTube Music",
|
||||
"with-song-info": "YouTube Music: {{artist}} - {{title}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"ad-speedup": {
|
||||
"description": "Si es reprodueix un anunci, silencia l'àudio i el reprodueix a la velocitat 16x",
|
||||
"name": "Accelera els anuncis"
|
||||
},
|
||||
"adblocker": {
|
||||
"description": "Bloqueja tots els anuncis i el seguiment",
|
||||
"menu": {
|
||||
"blocker": "Bloquejador"
|
||||
},
|
||||
"name": "Bloquejador d'anuncis"
|
||||
},
|
||||
"album-actions": {
|
||||
"description": "Afegeix botons de «no m'agrada / retirar el no m'agrada» i «m'agrada / retirar el m'agrada» per aplicar-ho a totes les cançons en una llista de reproducció o àlbum",
|
||||
"name": "Accions de l'àlbum"
|
||||
},
|
||||
"album-color-theme": {
|
||||
"description": "Aplica un tema dinàmic i efectes visuals basats en la paleta de colors de l'àlbum",
|
||||
"menu": {
|
||||
"color-mix-ratio": {
|
||||
"label": "Proporció de la barreja de colors",
|
||||
"submenu": {
|
||||
"percent": "{{ratio}}%"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Tema de color de l'àlbum"
|
||||
},
|
||||
"ambient-mode": {
|
||||
"description": "Aplica un efecte d'il·luminació que projecta colors difusos del vídeo al fons de la pantalla",
|
||||
"menu": {
|
||||
"blur-amount": {
|
||||
"label": "Quantitat de desenfocament",
|
||||
"submenu": {
|
||||
"pixels": "{{blurAmount}} píxels"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"label": "Buffer",
|
||||
"submenu": {
|
||||
"buffer": "{{buffer}}"
|
||||
}
|
||||
},
|
||||
"opacity": {
|
||||
"label": "Opacitat",
|
||||
"submenu": {
|
||||
"percent": "{{opacity}}%"
|
||||
}
|
||||
},
|
||||
"quality": {
|
||||
"label": "Qualitat",
|
||||
"submenu": {
|
||||
"pixels": "{{quality}} píxels"
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"label": "Mida",
|
||||
"submenu": {
|
||||
"percent": "{{size}}%"
|
||||
}
|
||||
},
|
||||
"smoothness-transition": {
|
||||
"label": "Transició suau",
|
||||
"submenu": {
|
||||
"during": "Durant {{interpolationTime}} s"
|
||||
}
|
||||
},
|
||||
"use-fullscreen": {
|
||||
"label": "Utilitza en pantalla completa"
|
||||
}
|
||||
},
|
||||
"name": "Mode ambient"
|
||||
},
|
||||
"audio-compressor": {
|
||||
"description": "Aplica compressió a l'àudio (baixa el volum de les parts més sorolloses de la senyal d'àudio i puja el volum de les parts més fluixes)",
|
||||
"name": "Compressor d'àudio"
|
||||
},
|
||||
"blur-nav-bar": {
|
||||
"description": "Desenfoca i aplica transparència a la barra de navegació",
|
||||
"name": "Desenfoca la barra de navegació"
|
||||
},
|
||||
"bypass-age-restrictions": {
|
||||
"description": "Esquiva la verificació d'edat de YouTube",
|
||||
"name": "Esquiva les restriccions d'edat"
|
||||
},
|
||||
"captions-selector": {
|
||||
"description": "Selector de subtítols per les pistes d'àudio de YouTube Music",
|
||||
"menu": {
|
||||
"autoload": "Selecciona automàticament l'últim subtítol emprat",
|
||||
"disable-captions": "Sense subtítols per defecte"
|
||||
},
|
||||
"name": "Selector de subtítols",
|
||||
"prompt": {
|
||||
"selector": {
|
||||
"label": "Idioma actual dels subtítols: {{language}}",
|
||||
"none": "Cap",
|
||||
"title": "Selecciona l'idioma dels subtítols"
|
||||
}
|
||||
},
|
||||
"templates": {
|
||||
"title": "Obra el selector de subtítols"
|
||||
}
|
||||
},
|
||||
"compact-sidebar": {
|
||||
"description": "Sempre mostrar la barra lateral en mode compacte",
|
||||
"name": "Barra lateral compacta"
|
||||
},
|
||||
"crossfade": {
|
||||
"description": "Transició creuada (crossfade) entre cançons",
|
||||
"menu": {
|
||||
"advanced": "Avançat"
|
||||
},
|
||||
"name": "Transició creuada [Beta]",
|
||||
"prompt": {
|
||||
"options": {
|
||||
"multi-input": {
|
||||
"fade-in-duration": "Durada de la transició d'entrada (ms)",
|
||||
"fade-out-duration": "Durada de la transició de sortida (ms)",
|
||||
"fade-scaling": {
|
||||
"label": "Escala de la transició",
|
||||
"linear": "Linear",
|
||||
"logarithmic": "Logarítmica"
|
||||
},
|
||||
"seconds-before-end": "Transiciona N segons abans del final"
|
||||
},
|
||||
"title": "Opcions de transició creuada"
|
||||
}
|
||||
}
|
||||
},
|
||||
"disable-autoplay": {
|
||||
"description": "Fa que la cançó comenci en mode «pausat»",
|
||||
"menu": {
|
||||
"apply-once": "Tan sols s'aplica a l'inici"
|
||||
},
|
||||
"name": "Deshabilita la reproducció automàtica"
|
||||
},
|
||||
"discord": {
|
||||
"backend": {
|
||||
"already-connected": "S'ha intentat connectar amb una connexió activa",
|
||||
"connected": "Connectat a Discord",
|
||||
"disconnected": "Desconnectat de Discord"
|
||||
},
|
||||
"description": "Mostra als teus amics allò que escoltes a l'estat d'activitat",
|
||||
"menu": {
|
||||
"auto-reconnect": "Reconnecta automàticament",
|
||||
"clear-activity": "Esborra l'activitat",
|
||||
"clear-activity-after-timeout": "Esborra l'activitat al cap d'un temps",
|
||||
"connected": "Connectat",
|
||||
"disconnected": "Desconnectat",
|
||||
"hide-duration-left": "Amaga la durada restant",
|
||||
"hide-github-button": "Amaga el botó de l'enllaç a GitHub",
|
||||
"play-on-youtube-music": "Reprodueix a YouTube Music",
|
||||
"set-inactivity-timeout": "Estableix temps d'espera d'inactivitat"
|
||||
},
|
||||
"name": "Estat d'activitat de Discord",
|
||||
"prompt": {
|
||||
"set-inactivity-timeout": {
|
||||
"label": "Introdueix el temps d'espera d'inactivitat en segons:",
|
||||
"title": "Estableix el temps d'espera d'inactivitat"
|
||||
}
|
||||
}
|
||||
},
|
||||
"downloader": {
|
||||
"backend": {
|
||||
"dialog": {
|
||||
"error": {
|
||||
"buttons": {
|
||||
"ok": "D'acord"
|
||||
},
|
||||
"message": "Caram! Ho sentim, ha fallat la descàrrega…",
|
||||
"title": "Error a la descàrrega!"
|
||||
},
|
||||
"start-download-playlist": {
|
||||
"buttons": {
|
||||
"ok": "D'acord"
|
||||
},
|
||||
"detail": "({{playlistSize}} cançons)",
|
||||
"message": "Descarregant llista de reproducció {{playlistTitle}}",
|
||||
"title": "Descàrrega començada"
|
||||
}
|
||||
},
|
||||
"feedback": {
|
||||
"conversion-progress": "Conversió: {{percent}}%",
|
||||
"converting": "Convertint…",
|
||||
"done": "Fet: {{filePath}}",
|
||||
"download-info": "Descarregant {{artist}} - {{title}} [{{videoId}}",
|
||||
"download-progress": "Descàrrega: {{percent}}%",
|
||||
"downloading": "Descarregant…",
|
||||
"downloading-counter": "Descarregant {{current}}/{{total}}…",
|
||||
"downloading-playlist": "Descarregant la llista de reproducció «{{playlistTitle}}» - {{playlistSize}} cançons ({{playlistId}})",
|
||||
"error-while-downloading": "Error al descarregar «{{author}} - {{title}}»: {{error}}",
|
||||
"folder-already-exists": "La carpeta {{playlistFolder}} ja existeix",
|
||||
"getting-playlist-info": "Obtenint la informació de la llista de reproducció…",
|
||||
"loading": "Carregant…",
|
||||
"playlist-has-only-one-song": "La llista de reproducció té un sol element, descarregant-lo directament",
|
||||
"playlist-id-not-found": "No s'ha trobat cap ID de llista de reproducció",
|
||||
"playlist-is-empty": "La llista de reproducció és buida",
|
||||
"playlist-is-mix-or-private": "Error obtenint la informació de la llista de reproducció: assegura't que no és una llista de reproducció privada o de «Mixos per a tu»\n\n{{error}}",
|
||||
"preparing-file": "Preparant arxiu…",
|
||||
"saving": "Desant…",
|
||||
"trying-to-get-playlist-id": "Intentant obtenir l'ID de la llista de reproducció: {{playlistId}}",
|
||||
"video-id-not-found": "Vídeo no trobat",
|
||||
"writing-id3": "Escrivint les etiquetes ID3…"
|
||||
}
|
||||
},
|
||||
"description": "Descarrega el MP3 / àudio d'origen directament des de la interfície",
|
||||
"menu": {
|
||||
"choose-download-folder": "Tria la carpeta de descàrrega",
|
||||
"download-finish-settings": {
|
||||
"label": "Descarrega en finalitzar",
|
||||
"prompt": {
|
||||
"last-percent": "Desprès del x percent",
|
||||
"last-seconds": "Últims x segons",
|
||||
"title": "Configura quan descarregar"
|
||||
},
|
||||
"submenu": {
|
||||
"advanced": "Avançat",
|
||||
"enabled": "Habilitat",
|
||||
"mode": "Mode de temps",
|
||||
"percent": "Percentatge",
|
||||
"seconds": "Segons"
|
||||
}
|
||||
},
|
||||
"download-playlist": "Descarrega la llista de reproducció",
|
||||
"presets": "Configuracions predefinides",
|
||||
"skip-existing": "Omet els arxius existents"
|
||||
},
|
||||
"name": "Descàrregues",
|
||||
"renderer": {
|
||||
"can-not-update-progress": "No es pot actualitzar el progrés"
|
||||
},
|
||||
"templates": {
|
||||
"button": "Descarrega"
|
||||
}
|
||||
},
|
||||
"exponential-volume": {
|
||||
"description": "Fa que el control lliscant del volum sigui exponencial per que sigui més fàcil seleccionar volums més baixos.",
|
||||
"name": "Volum exponencial"
|
||||
},
|
||||
"in-app-menu": {
|
||||
"description": "Fa que la barra de menú superior tingui un elegant aspecte fosc o basat en el color de l'àlbum",
|
||||
"menu": {
|
||||
"hide-dom-window-controls": "Amaga els controls de la finestra del DOM"
|
||||
},
|
||||
"name": "Menú integrat In-App"
|
||||
},
|
||||
"lumiastream": {
|
||||
"description": "Afegeix suport pel Lumia Stream",
|
||||
"name": "Lumia Stream [Beta]"
|
||||
},
|
||||
"lyrics-genius": {
|
||||
"description": "Afegeix suport per la lletra de la majoria de cançons",
|
||||
"menu": {
|
||||
"romanized-lyrics": "Lletra romanitzada"
|
||||
},
|
||||
"name": "Lletres de Genius",
|
||||
"renderer": {
|
||||
"fetched-lyrics": "S'ha buscat la lletra a Genius"
|
||||
}
|
||||
},
|
||||
"music-together": {
|
||||
"description": "Comparteix una llista de reproducció amb els demés. Quan l'amfitrió reprodueix una cançó, la resta també sentiran la mateixa",
|
||||
"dialog": {
|
||||
"enter-host": "Introdueix l'ID de l'amfitrió"
|
||||
},
|
||||
"internal": {
|
||||
"save": "Desa",
|
||||
"track-source": "Origen de la pista",
|
||||
"unknown-user": "Usuari desconegut"
|
||||
},
|
||||
"menu": {
|
||||
"click-to-copy-id": "Copia l'ID d'amfitrió",
|
||||
"close": "Tanca el Music Together",
|
||||
"connected-users": "Usuaris connectats",
|
||||
"disconnect": "Desconnecta el Music Together",
|
||||
"empty-user": "No hi ha usuaris connectats",
|
||||
"host": "Amfitrió de Music Together",
|
||||
"join": "Uneix-te a Music Together",
|
||||
"permission": {
|
||||
"all": "Permet que els convidats controlin la llista de reproducció i el reproductor",
|
||||
"host-only": "Tan sols l'amfitrió pot controlar la llista de reproducció i el reproductor",
|
||||
"playlist": "Permet que els convidats controlin la llista de reproducció"
|
||||
},
|
||||
"set-permission": "Canvia els permisos de control",
|
||||
"status": {
|
||||
"disconnected": "Desconnectat",
|
||||
"guest": "Connectat com a convidat",
|
||||
"host": "Connectat com amfitrió"
|
||||
}
|
||||
},
|
||||
"name": "Music Together [Beta]",
|
||||
"toast": {
|
||||
"add-song-failed": "Error al afegir la cançó",
|
||||
"closed": "Music Together tancat",
|
||||
"disconnected": "Music Together desconnectat",
|
||||
"host-failed": "No s'ha pogut començar el Music Together",
|
||||
"id-copied": "L'ID d'amfitrió s'ha copiat al porta-retalls",
|
||||
"id-copy-failed": "Error al copiar l'ID d'amfitrió al porta-retalls",
|
||||
"join-failed": "Error al unir-se al Music Together",
|
||||
"joined": "T'has unit al Music Together",
|
||||
"permission-changed": "Els permisos de Music Together han canviat a «{{permission}}»",
|
||||
"remove-song-failed": "Error al eliminar la cançó",
|
||||
"user-connected": "{{name}} s'ha unit al Music Together",
|
||||
"user-disconnected": "{{name}} s'ha desconnectat del Music Together"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"description": "Fletxes de navegació Següent / Enrere integrades directament a la interfície, com al teu navegador preferit",
|
||||
"name": "Navegació"
|
||||
},
|
||||
"no-google-login": {
|
||||
"description": "Elimina els botons d'inici de sessió de Google de la interfície",
|
||||
"name": "Sense inici de sessió de Google"
|
||||
},
|
||||
"notifications": {
|
||||
"description": "Mostra una notificació quan una cançó es comença a reproduir (les notificacions interactives estan disponibles a Windows)",
|
||||
"menu": {
|
||||
"interactive": "Notificacions interactives",
|
||||
"interactive-settings": {
|
||||
"label": "Configuració interactiva",
|
||||
"submenu": {
|
||||
"hide-button-text": "Amaga text del botó",
|
||||
"refresh-on-play-pause": "Recarrega al Reproduir/Pausar",
|
||||
"tray-controls": "Obra/Tanca en clicar a la safata"
|
||||
}
|
||||
},
|
||||
"priority": "Prioritat de les notificacions",
|
||||
"toast-style": "Estil dels missatges emergents",
|
||||
"unpause-notification": "Mostra notificació en reprendre la reproducció"
|
||||
},
|
||||
"name": "Notificacions"
|
||||
},
|
||||
"picture-in-picture": {
|
||||
"description": "Permet commutar el mode d'imatge en imatge (PiP)",
|
||||
"menu": {
|
||||
"always-on-top": "Mostra sempre a sobre",
|
||||
"hotkey": {
|
||||
"label": "Drecera del teclat",
|
||||
"prompt": {
|
||||
"keybind-options": {
|
||||
"hotkey": "Drecera del teclat"
|
||||
},
|
||||
"label": "Tria una drecera per commutar el mode d'imatge en imatge (PiP)",
|
||||
"title": "Drecera del mode imatge en imatge (PiP)"
|
||||
}
|
||||
},
|
||||
"save-window-position": "Desa la posició de la finestra",
|
||||
"save-window-size": "Desa la mida de la finestra",
|
||||
"use-native-pip": "Utilitza l'imatge en imatge (PiP) nativa del navegador"
|
||||
},
|
||||
"name": "Imatge en imatge (PiP)",
|
||||
"templates": {
|
||||
"button": "Imatge en imatge (PiP)"
|
||||
}
|
||||
},
|
||||
"playback-speed": {
|
||||
"description": "Escolta-ho ràpid, escolta-ho lent! Afegeix un control lliscant per canviar la velocitat de la cançó",
|
||||
"name": "Velocitat de la reproducció",
|
||||
"templates": {
|
||||
"button": "Velocitat"
|
||||
}
|
||||
},
|
||||
"precise-volume": {
|
||||
"description": "Controla el volum de manera precisa a través de la rodeta del ratolí / dreceres del teclat, amb una interfície personalitzada i passos de volum personalitzats",
|
||||
"menu": {
|
||||
"arrows-shortcuts": "Controls locals de tecles de fletxa",
|
||||
"custom-volume-steps": "Estableix passos de volum personalitzats",
|
||||
"global-shortcuts": "Dreceres de teclat globals"
|
||||
},
|
||||
"name": "Volum precís",
|
||||
"prompt": {
|
||||
"global-shortcuts": {
|
||||
"keybind-options": {
|
||||
"decrease": "Baixa el volum",
|
||||
"increase": "Puja el volum"
|
||||
},
|
||||
"label": "Tria les dreceres globals de volum:",
|
||||
"title": "Dreceres globals de volum"
|
||||
},
|
||||
"volume-steps": {
|
||||
"label": "Tria els passos d'augment o disminució del volum",
|
||||
"title": "Passos de volum"
|
||||
}
|
||||
}
|
||||
},
|
||||
"quality-changer": {
|
||||
"backend": {
|
||||
"dialog": {
|
||||
"quality-changer": {
|
||||
"detail": "Qualitat actual: {{quality}}",
|
||||
"message": "Tria la qualitat del vídeo:",
|
||||
"title": "Tria la qualitat del vídeo"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Permet canviar la qualitat del vídeo amb un botó que s'hi mostra a sobre",
|
||||
"name": "Canvia la qualitat del vídeo"
|
||||
},
|
||||
"scrobbler": {
|
||||
"description": "Afegeix suport per scrobbling (Last.fm, ListenBrainz, etc.)",
|
||||
"dialog": {
|
||||
"lastfm": {
|
||||
"auth-failed": {
|
||||
"message": "Error al autenticar amb Last.fm\nAmaga la finestra emergent fins el següent reinici.",
|
||||
"title": "Error d'autenticació"
|
||||
}
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"lastfm": {
|
||||
"api-settings": "Configuració de l'API de Last.fm"
|
||||
},
|
||||
"listenbrainz": {
|
||||
"token": "Introduir token d'usuari de ListenBrainz"
|
||||
},
|
||||
"scrobble-other-media": "Scrobble amb altres mitjans"
|
||||
},
|
||||
"name": "Scrobbler",
|
||||
"prompt": {
|
||||
"lastfm": {
|
||||
"api-key": "Clau d'API de Last.fm",
|
||||
"api-secret": "Clau secreta de l'API de Last.fm"
|
||||
},
|
||||
"listenbrainz": {
|
||||
"token": {
|
||||
"label": "Introdueix el teu token de ListenBrainz:",
|
||||
"title": "Token de ListenBrainz"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"shortcuts": {
|
||||
"description": "Permet l'ús de dreceres globals del teclat per la reproducció (reproduir/pausar/següent/anterior) i desactivar l'OSD dels mitjans en sobreescriure les tecles de control multimèdia, habilita el Ctrl/CMD + F per buscar, habilita el suport MPRIS a Linux per tecles de control multimèdia, i dreceres de teclat personalitzades per usuaris avançats",
|
||||
"menu": {
|
||||
"override-media-keys": "Sobreescriu les tecles de control multimèdia",
|
||||
"set-keybinds": "Estableix controls globals de les cançons"
|
||||
},
|
||||
"name": "Dreceres i MPRIS",
|
||||
"prompt": {
|
||||
"keybind": {
|
||||
"keybind-options": {
|
||||
"next": "Següent",
|
||||
"play-pause": "Reproduir / Pausar",
|
||||
"previous": "Anterior"
|
||||
},
|
||||
"label": "Tria combinacions de tecles per controlar les cançons:",
|
||||
"title": "Dreceres globals"
|
||||
}
|
||||
}
|
||||
},
|
||||
"skip-disliked-songs": {
|
||||
"description": "Omet cançons amb «No m'agrada»",
|
||||
"name": "Omet cançons amb «No m'agrada»"
|
||||
},
|
||||
"skip-silences": {
|
||||
"description": "Omet automàticament les seccions amb silenci a les cançons",
|
||||
"name": "Omet els silencis"
|
||||
},
|
||||
"sponsorblock": {
|
||||
"description": "Omet automàticament els segments dels vídeos que no son música, com la intro o el final",
|
||||
"name": "SponsorBlock"
|
||||
},
|
||||
"taskbar-mediacontrol": {
|
||||
"description": "Controla la reproducció des de la barra de tasques del Windows",
|
||||
"name": "Control multimèdia a la barra de tasques"
|
||||
},
|
||||
"touchbar": {
|
||||
"description": "Afegeix un giny a la Touch Bar per usuaris de macOS",
|
||||
"name": "TouchBar"
|
||||
},
|
||||
"tuna-obs": {
|
||||
"description": "Integració amb l'extensió «Tuna» del OBS",
|
||||
"name": "Tuna OBS"
|
||||
},
|
||||
"video-toggle": {
|
||||
"description": "Afegeix un botó per commutar entre el mode de vídeo o de cançó. Opcionalment, es pot eliminar la pestanya de vídeo per complet",
|
||||
"menu": {
|
||||
"align": {
|
||||
"label": "Alineament",
|
||||
"submenu": {
|
||||
"left": "Esquerra",
|
||||
"middle": "Mig",
|
||||
"right": "Dreta"
|
||||
}
|
||||
},
|
||||
"force-hide": "Força amagar la pestanya de vídeo",
|
||||
"mode": {
|
||||
"label": "Mode",
|
||||
"submenu": {
|
||||
"custom": "Commutador personalitzat",
|
||||
"disabled": "Deshabilitat",
|
||||
"native": "Commutador nadiu"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Commutador de vídeo",
|
||||
"templates": {
|
||||
"button": "Cançó"
|
||||
}
|
||||
},
|
||||
"visualizer": {
|
||||
"description": "Afegeix un visualitzador al reproductor",
|
||||
"menu": {
|
||||
"visualizer-type": "Tipus de visualitzador"
|
||||
},
|
||||
"name": "Visualitzador"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -207,6 +207,10 @@
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"ad-speedup": {
|
||||
"description": "Pokud se přehraje reklama tak ztlumí zvuk a nastaví rychlost přehrávání na 16x",
|
||||
"name": "Zrychlovač Reklam"
|
||||
},
|
||||
"adblocker": {
|
||||
"description": "Blokuje všechny reklamy a sledování ihned od začátku",
|
||||
"menu": {
|
||||
@ -410,6 +414,11 @@
|
||||
"description": "Stahuje MP3 / source audio přímo z rozhraní",
|
||||
"menu": {
|
||||
"choose-download-folder": "Vybrat složku pro stahování",
|
||||
"download-finish-settings": {
|
||||
"submenu": {
|
||||
"advanced": "Pokoročile"
|
||||
}
|
||||
},
|
||||
"download-playlist": "Stáhnout seznam písniček",
|
||||
"presets": "Předvolby",
|
||||
"skip-existing": "Přeskočit existující soubory"
|
||||
|
||||
@ -207,6 +207,10 @@
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"ad-speedup": {
|
||||
"description": "Wenn eine Werbung spielt, stummt es das Audio und setzt die Wiedergabegeschwindigkeit auf 16x",
|
||||
"name": "Werbungsbeschleunigung"
|
||||
},
|
||||
"adblocker": {
|
||||
"description": "Blockiere jegliche Werbung und Tracker",
|
||||
"menu": {
|
||||
@ -410,24 +414,24 @@
|
||||
"description": "Lädt MP3-/Original-Audio direkt von der Schnittstelle herunter",
|
||||
"menu": {
|
||||
"choose-download-folder": "Downloadordner wählen",
|
||||
"download-playlist": "Wiedergabeliste herunterladen",
|
||||
"presets": "Voreinstellungen",
|
||||
"skip-existing": "Vorhandene Dateien überspringen",
|
||||
"download-finish-settings": {
|
||||
"label": "Song am Ende runterladen",
|
||||
"prompt": {
|
||||
"last-percent": "Nach x Prozent",
|
||||
"last-seconds": "Letzten x Sekunden",
|
||||
"title": "Konfiguriere wann runtergeladen werden soll"
|
||||
},
|
||||
"submenu": {
|
||||
"advanced": "Erweitert",
|
||||
"enabled": "Aktiviert",
|
||||
"mode": "Zeitmodus",
|
||||
"seconds": "Sekunden",
|
||||
"percent": "Prozent",
|
||||
"advanced": "Erweitert"
|
||||
"seconds": "Sekunden"
|
||||
}
|
||||
},
|
||||
"prompt": {
|
||||
"title": "Konfiguriere wann runtergeladen werden soll",
|
||||
"last-seconds": "Letzten x Sekunden",
|
||||
"last-percent": "Nach x Prozent"
|
||||
}
|
||||
}
|
||||
"download-playlist": "Wiedergabeliste herunterladen",
|
||||
"presets": "Voreinstellungen",
|
||||
"skip-existing": "Vorhandene Dateien überspringen"
|
||||
},
|
||||
"name": "Downloader",
|
||||
"renderer": {
|
||||
|
||||
@ -214,6 +214,10 @@
|
||||
},
|
||||
"name": "Ad Blocker"
|
||||
},
|
||||
"ad-speedup": {
|
||||
"name": "Ad Speedup",
|
||||
"description": "If an ad play it mutes the audio and sets playback speed to 16x"
|
||||
},
|
||||
"album-actions": {
|
||||
"description": "Adds Undislike, Dislike, Like, and Unlike buttons to apply this to all songs in a playlist or album",
|
||||
"name": "Album Actions"
|
||||
@ -664,6 +668,23 @@
|
||||
"description": "Automatically Skips non-music parts like intro/outro or parts of music videos where the song isn't playing",
|
||||
"name": "SponsorBlock"
|
||||
},
|
||||
"synced-lyrics": {
|
||||
"description": "Provides synced lyrics to songs, using providers like LRClib.",
|
||||
"name": "Synced Lyrics",
|
||||
"errors": {
|
||||
"fetch": "⚠️ - An error occurred while fetching the lyrics. Please try again later.",
|
||||
"not-found": "⚠️ - No lyrics found for this song."
|
||||
},
|
||||
"warnings": {
|
||||
"instrumental": "⚠️ - This is an instrumental song",
|
||||
"inexact": "⚠️ - The lyrics for this song may not be exact",
|
||||
"duration-mismatch": "⚠️ - The lyrics may be out of sync due to a duration mismatch."
|
||||
},
|
||||
"refetch-btn": {
|
||||
"normal": "Refetch lyrics",
|
||||
"fetching": "Fetching..."
|
||||
}
|
||||
},
|
||||
"taskbar-mediacontrol": {
|
||||
"description": "Control playback from your Windows taskbar",
|
||||
"name": "Taskbar Media Control"
|
||||
|
||||
@ -207,6 +207,10 @@
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"ad-speedup": {
|
||||
"description": "Si se reproduce un anuncio, silencia el audio y fija la velocidad de reproducción en 16x",
|
||||
"name": "Aumento de la velocidad de anuncios"
|
||||
},
|
||||
"adblocker": {
|
||||
"description": "Bloquear todos los anuncios y el rastreo",
|
||||
"menu": {
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
},
|
||||
"update-available": {
|
||||
"buttons": {
|
||||
"disable": "Päivityksen pois päältä",
|
||||
"disable": "Poista päivitykset käytöstä",
|
||||
"download": "Lataa",
|
||||
"ok": "Selvä"
|
||||
},
|
||||
@ -215,8 +215,8 @@
|
||||
"name": "Mainos estäjä"
|
||||
},
|
||||
"album-actions": {
|
||||
"description": "Alapeukuta musiikki/videota, jotta voimme tarjota sinulle parhaimmat MIX:it",
|
||||
"name": "Albumin toiminnot"
|
||||
"description": "Lisää tykkäysnappulat, joilla voit lisätä tai poistaa tykkäyksiä kerralla kaikille soittolistan tai albumin kappaleille",
|
||||
"name": "Albumin Toiminnot"
|
||||
},
|
||||
"album-color-theme": {
|
||||
"description": "Käyttää dynaamista teemaa ja visuaalisia tehosteita albumin väripaletin perusteella",
|
||||
@ -228,7 +228,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Albumin värin teema"
|
||||
"name": "Albumin Värinen Teema"
|
||||
},
|
||||
"ambient-mode": {
|
||||
"description": "Antaa valaistustehosteen heittämällä videosta lempeitä värejä näytön taustalle",
|
||||
@ -236,11 +236,11 @@
|
||||
"blur-amount": {
|
||||
"label": "Sumennuksen voimakkuus",
|
||||
"submenu": {
|
||||
"pixels": "{{blurAmount}}pikseliä"
|
||||
"pixels": "{{blurAmount}} pikseliä"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"label": "Puskuroi",
|
||||
"label": "Puskurointi",
|
||||
"submenu": {
|
||||
"buffer": "{{buffer}}"
|
||||
}
|
||||
@ -254,7 +254,7 @@
|
||||
"quality": {
|
||||
"label": "Laatu",
|
||||
"submenu": {
|
||||
"pixels": "{{quality}}pikseliä"
|
||||
"pixels": "{{quality}} pikseliä"
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
@ -264,7 +264,10 @@
|
||||
}
|
||||
},
|
||||
"smoothness-transition": {
|
||||
"label": "sujuvuus siirtymässä"
|
||||
"label": "Siirtymän sujuvuus",
|
||||
"submenu": {
|
||||
"during": "Kesto {{interpolationTime}} s"
|
||||
}
|
||||
},
|
||||
"use-fullscreen": {
|
||||
"label": "Käytetään koko näytön tilaa"
|
||||
|
||||
@ -206,6 +206,9 @@
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"ad-speedup": {
|
||||
"description": "Pag mag-play ng ad, I-mute ang audio at i-set ang bilis ng playback ng 16x"
|
||||
},
|
||||
"adblocker": {
|
||||
"description": "I-block ang lahat ng ad at tracking"
|
||||
},
|
||||
@ -386,6 +389,17 @@
|
||||
"description": "Dina-download ang mga MP3 / source audio direkta mula sa interface",
|
||||
"menu": {
|
||||
"choose-download-folder": "Pumili ng download folder",
|
||||
"download-finish-settings": {
|
||||
"label": "Kung natapos ang download",
|
||||
"prompt": {
|
||||
"title": "I-configure kung kailan magda-download"
|
||||
},
|
||||
"submenu": {
|
||||
"enabled": "Napagana na",
|
||||
"percent": "Porsyento",
|
||||
"seconds": "Segundo"
|
||||
}
|
||||
},
|
||||
"download-playlist": "Dina-download ang playlist",
|
||||
"presets": "Mga preset",
|
||||
"skip-existing": "Laktawan ang mga kasalukuyang file"
|
||||
@ -427,6 +441,7 @@
|
||||
"connected-users": "Nakakonektang (mga) User",
|
||||
"disconnect": "Mag-diskonekta sa Music Together",
|
||||
"empty-user": "Walang naka-konektang user",
|
||||
"host": "Host ng Music Together",
|
||||
"join": "Sumali sa Music Together",
|
||||
"permission": {
|
||||
"all": "Payagan ang mga guest na kontrolin ang playlist at player",
|
||||
@ -465,13 +480,16 @@
|
||||
"notifications": {
|
||||
"description": "Magpakita ng notification kapag nagsimulang tumugtog ang kanta (magagamit ang mga interactive na notification sa Windows)",
|
||||
"menu": {
|
||||
"interactive": "Interactive na Notification",
|
||||
"interactive-settings": {
|
||||
"label": "Mga Interactive na Setting",
|
||||
"submenu": {
|
||||
"hide-button-text": "Itago ang button na texto",
|
||||
"refresh-on-play-pause": "I-refresh sa Pag-play/Pag-pause",
|
||||
"tray-controls": "Buksan/Isara sa pag-click sa tray"
|
||||
}
|
||||
},
|
||||
"priority": "Prioridad ng Notification",
|
||||
"unpause-notification": "Ipakita ang notification sa pag-unpause"
|
||||
}
|
||||
},
|
||||
@ -486,12 +504,13 @@
|
||||
}
|
||||
},
|
||||
"save-window-position": "I-save ang posisyon ng window",
|
||||
"save-window-size": "I-save ang laki ng windo",
|
||||
"save-window-size": "I-save ang laki ng window",
|
||||
"use-native-pip": "Gamitin ang browser native na PiP"
|
||||
}
|
||||
},
|
||||
"playback-speed": {
|
||||
"description": "Makinig na mabilisan, makinig na mabagalan! Nagdaragdag ito ng slider upang makontrol ang bilis ng kanta",
|
||||
"name": "Bilis ng Playback",
|
||||
"templates": {
|
||||
"button": "Bilis"
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@
|
||||
"label": "Définir un proxy",
|
||||
"prompt": {
|
||||
"label": "Entrez l'adresse proxy : (laissez vide pour désactiver)",
|
||||
"placeholder": "Exemple: SOCKS5://127.0.0.1:9999",
|
||||
"placeholder": "Exemple : SOCKS5://127.0.0.1:9999",
|
||||
"title": "Définir un proxy"
|
||||
}
|
||||
},
|
||||
@ -179,7 +179,7 @@
|
||||
"plugins": {
|
||||
"enabled": "Activé",
|
||||
"label": "Extensions",
|
||||
"new": "NOUVELLE"
|
||||
"new": "NOUVEAU"
|
||||
},
|
||||
"view": {
|
||||
"label": "Vue",
|
||||
@ -207,6 +207,10 @@
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"ad-speedup": {
|
||||
"description": "Si une publicité apparaît, le son est coupé et la vitesse de lecture est réglée sur 16x",
|
||||
"name": "Accélérer les publicités"
|
||||
},
|
||||
"adblocker": {
|
||||
"description": "Bloquer toutes les annonces et le suivi par défaut",
|
||||
"menu": {
|
||||
@ -410,6 +414,21 @@
|
||||
"description": "Télécharge les fichiers MP3/source audio directement depuis l'interface",
|
||||
"menu": {
|
||||
"choose-download-folder": "Choisissez le dossier de téléchargement",
|
||||
"download-finish-settings": {
|
||||
"label": "Télécharger une fois terminé",
|
||||
"prompt": {
|
||||
"last-percent": "Après x pour cent",
|
||||
"last-seconds": "Dernières x secondes",
|
||||
"title": "Configurer quand télécharger"
|
||||
},
|
||||
"submenu": {
|
||||
"advanced": "Avancé",
|
||||
"enabled": "Activé",
|
||||
"mode": "Mode de temps",
|
||||
"percent": "Pourcent",
|
||||
"seconds": "Secondes"
|
||||
}
|
||||
},
|
||||
"download-playlist": "Télécharger la liste de lecture",
|
||||
"presets": "Préconfigurations",
|
||||
"skip-existing": "Passer les fichiers existants"
|
||||
|
||||
@ -2,7 +2,13 @@
|
||||
"common": {
|
||||
"console": {
|
||||
"plugins": {
|
||||
"execute-failed": "נכשל ביצוע תוסף {{pluginName}}::{{contextName}}"
|
||||
"execute-failed": "נכשל ביצוע תוסף {{pluginName}}::{{contextName}}",
|
||||
"executed-at-ms": "התוסף {{pluginName}}:{{contextName}} בוצע ב {{ms}}ms",
|
||||
"initialize-failed": "טעינת התוסף \"{{pluginName}}\" נכשלה",
|
||||
"load-all": "טוען את כל התוספים",
|
||||
"load-failed": "טעינת התוסף \"{{pluginName}}\" נכשלה",
|
||||
"loaded": "התוסף \"{{pluginName}}\" נטען",
|
||||
"unload-failed": "הסרת התוסף \"{{pluginName}} נכשלה"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -10,5 +16,29 @@
|
||||
"code": "he",
|
||||
"local-name": "עברית",
|
||||
"name": "Hebrew"
|
||||
},
|
||||
"main": {
|
||||
"console": {
|
||||
"did-finish-load": {
|
||||
"dev-tools": "הטעינה הסתיימה. הכלים לפמתחים נפתחו"
|
||||
},
|
||||
"i18n": {
|
||||
"loaded": "i18n נטען"
|
||||
},
|
||||
"theme": {
|
||||
"css-file-not-found": "קובץ ה-CSS \"{{cssFile}}\" לא קיים. מדלג"
|
||||
},
|
||||
"when-ready": {
|
||||
"clearing-cache-after-20s": "מוחק קבצי מתמון"
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
"need-to-restart": {
|
||||
"buttons": {
|
||||
"later": "אחר כך",
|
||||
"restart-now": "מתחיל את התוכנה מחדש עכשיו"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -207,6 +207,10 @@
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"ad-speedup": {
|
||||
"description": "광고가 재생될 때, 오디오가 음소거되고 재생 속도가 16배로 설정됩니다",
|
||||
"name": "광고 배속"
|
||||
},
|
||||
"adblocker": {
|
||||
"description": "모든 광고와 트래커를 즉시 차단합니다",
|
||||
"menu": {
|
||||
@ -664,6 +668,23 @@
|
||||
"description": "인트로/아웃트로와 같은 음악이 아닌 부분이나, 노래가 재생되지 않는 뮤직 비디오의 일부를 자동으로 건너뜁니다",
|
||||
"name": "SponsorBlock"
|
||||
},
|
||||
"synced-lyrics": {
|
||||
"description": "LRClib등의 가사 제공자에서 싱크 가사를 불러옵니다.",
|
||||
"errors": {
|
||||
"fetch": "⚠️ - 가사를 불러오는 동안 오류가 발생했습니다. 나중에 다시 시도해 주세요.",
|
||||
"not-found": "⚠️ - 이 노래의 가사를 찾을 수 없습니다."
|
||||
},
|
||||
"name": "싱크 가사",
|
||||
"refetch-btn": {
|
||||
"fetching": "가져오는 중...",
|
||||
"normal": "가사 다시 가져오기"
|
||||
},
|
||||
"warnings": {
|
||||
"duration-mismatch": "⚠️ - 곡 길이 불일치로 인해 가사가 일치하지 않을 수 있습니다.",
|
||||
"inexact": "⚠️ - 이 노래의 가사는 정확하지 않을 수 있습니다",
|
||||
"instrumental": "⚠️ - 연주곡입니다"
|
||||
}
|
||||
},
|
||||
"taskbar-mediacontrol": {
|
||||
"description": "Windows 작업 표시줄에서 재생을 제어하세요",
|
||||
"name": "작업표시줄 미디어 컨트롤"
|
||||
|
||||
@ -207,6 +207,10 @@
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"ad-speedup": {
|
||||
"description": "Wycisza reklamę i przyśpiesza do 16x",
|
||||
"name": "Przyśpieszacz reklam"
|
||||
},
|
||||
"adblocker": {
|
||||
"description": "Blokuj wszystkie reklamy i śledzenie",
|
||||
"menu": {
|
||||
@ -410,6 +414,21 @@
|
||||
"description": "Pobiera MP3/ źródło audio bezpośrednio z interfejsu",
|
||||
"menu": {
|
||||
"choose-download-folder": "Wybierz folder pobierania",
|
||||
"download-finish-settings": {
|
||||
"label": "Pobierz po zakończeniu",
|
||||
"prompt": {
|
||||
"last-percent": "Po x procentach",
|
||||
"last-seconds": "Ostatnie x sekund",
|
||||
"title": "Konfiguruj, kiedy pobierać"
|
||||
},
|
||||
"submenu": {
|
||||
"advanced": "Zaawansowane",
|
||||
"enabled": "Włączone",
|
||||
"mode": "Tryb czasowy",
|
||||
"percent": "Procenty",
|
||||
"seconds": "Sekundy"
|
||||
}
|
||||
},
|
||||
"download-playlist": "Pobierz playlistę",
|
||||
"presets": "Predefiniowane ustawienia",
|
||||
"skip-existing": "Pomiń istniejące pliki"
|
||||
|
||||
@ -207,6 +207,10 @@
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"ad-speedup": {
|
||||
"description": "Se um anúncio for reproduzido, ele será silenciado o áudio e será definido a velocidade de reprodução para 16x",
|
||||
"name": "Acelerar os anúncios"
|
||||
},
|
||||
"adblocker": {
|
||||
"description": "Bloquear todos os anúncios e rastreamento automaticamente",
|
||||
"menu": {
|
||||
@ -222,9 +226,9 @@
|
||||
"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",
|
||||
"label": "Relação de mistura de cores",
|
||||
"submenu": {
|
||||
"percent": "Proporção"
|
||||
"percent": "{{ratio}}%"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -410,6 +414,21 @@
|
||||
"description": "Baixa MP3 / fonte de áudio diretamente da interface",
|
||||
"menu": {
|
||||
"choose-download-folder": "Escolha a pasta de download",
|
||||
"download-finish-settings": {
|
||||
"label": "Baixar ao terminar",
|
||||
"prompt": {
|
||||
"last-percent": "Depois de x por cento",
|
||||
"last-seconds": "Últimos x segundos",
|
||||
"title": "Configurar quando baixar"
|
||||
},
|
||||
"submenu": {
|
||||
"advanced": "Avançado",
|
||||
"enabled": "Ativado",
|
||||
"mode": "Modo de tempo",
|
||||
"percent": "Porcentagem",
|
||||
"seconds": "Segundos"
|
||||
}
|
||||
},
|
||||
"download-playlist": "Baixar lista de reprodução",
|
||||
"presets": "Predefinições",
|
||||
"skip-existing": "Ignorar arquivos existentes"
|
||||
@ -482,15 +501,15 @@
|
||||
"add-song-failed": "Falha ao adicionar canção",
|
||||
"closed": "Música Juntos encerrado",
|
||||
"disconnected": "Música Juntos foi desconectado",
|
||||
"host-failed": "Falha ao hospedar o Música Juntos",
|
||||
"host-failed": "Falha ao hospedar o Music Together",
|
||||
"id-copied": "ID de anfitrião copiado para a área de transferência",
|
||||
"id-copy-failed": "Falha ao copiar o ID de anfitrião para a área de transferência",
|
||||
"join-failed": "Falha ao entrar em Música Juntos",
|
||||
"joined": "Entrou em Música Juntos",
|
||||
"permission-changed": "A permissão do Música Juntos foi alterada para \"{{permission}}\"",
|
||||
"join-failed": "Falha ao entrar em Music Together",
|
||||
"joined": "Entrou em Music Together",
|
||||
"permission-changed": "A permissão do Music Together foi alterada para \"{{permission}}\"",
|
||||
"remove-song-failed": "Falha ao remover música",
|
||||
"user-connected": "{{name}} entrou em Música Juntos",
|
||||
"user-disconnected": "{{name}} saiu do Música Juntos"
|
||||
"user-connected": "{{name}} entrou em Music Together",
|
||||
"user-disconnected": "{{name}} saiu do Music Together"
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
|
||||
@ -7,11 +7,32 @@
|
||||
"initialize-failed": "\"{{pluginName}}\" ප්ලගිනය ආරම්භ කිරීමට අසමත් විය",
|
||||
"load-all": "සියලුම ප්ලගින පූරණය කරමින්",
|
||||
"load-failed": "\"{{pluginName}}\" ප්ලගිනය පූරණය කිරීමට අසමත් විය",
|
||||
"loaded": "ප්ලගිනය \"{{pluginName}}\" පූරණය කරන ලදී"
|
||||
"loaded": "ප්ලගිනය \"{{pluginName}}\" පූරණය කරන ලදී",
|
||||
"unload-failed": "ප්ලගින් \"{{pluginName}}\" ගලවන්න අසාර්ථක වුන",
|
||||
"unloaded": "ප්ලගින් \"{{pluginName}}\" ගැලෙව්වා"
|
||||
}
|
||||
}
|
||||
},
|
||||
"language": {
|
||||
"code": "si",
|
||||
"local-name": "සිංහල",
|
||||
"name": "Sinhala"
|
||||
},
|
||||
"main": {
|
||||
"console": {
|
||||
"did-finish-load": {
|
||||
"dev-tools": "පූරණය සම්පුර්නි. ඩෙව්ටූල්ස් ඇරිලා"
|
||||
},
|
||||
"i18n": {
|
||||
"loaded": "i18n පූරණය කර ඇත"
|
||||
},
|
||||
"second-instance": {
|
||||
"receive-command": "ප්රෝටෝකාල් හරහා විධානය ලැබුණි: \"{{command}}\""
|
||||
},
|
||||
"theme": {
|
||||
"css-file-not-found": "සීඑස්එස් ගොනුව \"{{cssFile}}\" නොපවතී, නොසලකා හැරීම"
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
"need-to-restart": {
|
||||
"title": "නැවත ආරම්භ කිරීම අවශ්යයි"
|
||||
|
||||
@ -410,6 +410,21 @@
|
||||
"description": "MP3 / kaynak sesini doğrudan arayüzden indir",
|
||||
"menu": {
|
||||
"choose-download-folder": "İndirme klasörünü seç",
|
||||
"download-finish-settings": {
|
||||
"label": "Bittiğinde indir",
|
||||
"prompt": {
|
||||
"last-percent": "Yüzde x'ten sonra",
|
||||
"last-seconds": "Son x saniyede",
|
||||
"title": "Ne zaman indirileceğini ayarla"
|
||||
},
|
||||
"submenu": {
|
||||
"advanced": "Gelişmiş",
|
||||
"enabled": "Etkin",
|
||||
"mode": "Zaman türü",
|
||||
"percent": "Yüzde",
|
||||
"seconds": "Saniye"
|
||||
}
|
||||
},
|
||||
"download-playlist": "Oynatma listesini indir",
|
||||
"presets": "Hazır Ayarlar",
|
||||
"skip-existing": "Mevcut dosyaları atla"
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"common": {
|
||||
"console": {
|
||||
"plugins": {
|
||||
"execute-failed": "Lỗi khi bắt đầu phần mở rộng {{pluginName}}::{{contextName}}",
|
||||
"execute-failed": "Lỗi thực thi plugin {{pluginName}}::{{contextName}}",
|
||||
"executed-at-ms": "Phần mở rộng {{pluginName}}::{{contextName}} đã bắt đầu trong {{ms}}ms",
|
||||
"initialize-failed": "Lỗi khi khởi động phần mở rộng \"{{pluginName}}\"",
|
||||
"load-all": "Đang tải tất cả phần mở rộng",
|
||||
@ -207,6 +207,10 @@
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"ad-speedup": {
|
||||
"description": "Nếu một quảng cáo được phát thì sẽ bị tắt tiếng và tăng tốc độ phát lên 16x",
|
||||
"name": "Tăng tốc quảng cáo"
|
||||
},
|
||||
"adblocker": {
|
||||
"description": "Chặn toàn bộ quảng cáo và trình theo dõi",
|
||||
"menu": {
|
||||
@ -410,6 +414,21 @@
|
||||
"description": "Tải xuống MP3 / âm thanh nguồn trực tiếp từ giao diện",
|
||||
"menu": {
|
||||
"choose-download-folder": "Chọn thư mục tải xuống",
|
||||
"download-finish-settings": {
|
||||
"label": "Tải xuống khi hoàn tất",
|
||||
"prompt": {
|
||||
"last-percent": "Sau x phần trăm",
|
||||
"last-seconds": "x giây cuối",
|
||||
"title": "Định cấu hình thời điểm tải xuống"
|
||||
},
|
||||
"submenu": {
|
||||
"advanced": "Nâng cao",
|
||||
"enabled": "Đã kích hoạt",
|
||||
"mode": "Chế độ thời gian",
|
||||
"percent": "Phần trăm",
|
||||
"seconds": "Giây"
|
||||
}
|
||||
},
|
||||
"download-playlist": "Tải danh sách phát",
|
||||
"presets": "Cài đặt sẵn",
|
||||
"skip-existing": "Bỏ qua các tập tin hiện có"
|
||||
|
||||
@ -410,6 +410,21 @@
|
||||
"description": "在界面内直接下载 MP3 / 源音频",
|
||||
"menu": {
|
||||
"choose-download-folder": "选择下载文件夹",
|
||||
"download-finish-settings": {
|
||||
"label": "边播边下",
|
||||
"prompt": {
|
||||
"last-percent": "播放超过指定百分比时开始下载",
|
||||
"last-seconds": "歌曲剩余指定秒数时开始下载",
|
||||
"title": "配置在何时开始下载"
|
||||
},
|
||||
"submenu": {
|
||||
"advanced": "高级",
|
||||
"enabled": "已启用",
|
||||
"mode": "激活时机",
|
||||
"percent": "按播放百分比",
|
||||
"seconds": "按播放秒数"
|
||||
}
|
||||
},
|
||||
"download-playlist": "下载播放列表",
|
||||
"presets": "预设",
|
||||
"skip-existing": "跳过已存在的文件"
|
||||
|
||||
@ -96,11 +96,11 @@
|
||||
"advanced-options": {
|
||||
"label": "進階選項",
|
||||
"submenu": {
|
||||
"auto-reset-app-cache": "當程式啟動時重設應用程式快取",
|
||||
"auto-reset-app-cache": "啟動時重設應用快取",
|
||||
"disable-hardware-acceleration": "關閉硬體加速",
|
||||
"edit-config-json": "編輯 config.json",
|
||||
"override-user-agent": "覆寫使用者代理",
|
||||
"restart-on-config-changes": "在設定檔更動時自動重啟應用程式",
|
||||
"restart-on-config-changes": "設定變更時自動重啟應用",
|
||||
"set-proxy": {
|
||||
"label": "設定代理伺服器",
|
||||
"prompt": {
|
||||
@ -123,7 +123,7 @@
|
||||
},
|
||||
"language": {
|
||||
"dialog": {
|
||||
"message": "語言會在下一次重啟應用程式時變更",
|
||||
"message": "語言會在重啟應用後變更",
|
||||
"title": "語言已變更"
|
||||
},
|
||||
"label": "語言",
|
||||
@ -131,7 +131,7 @@
|
||||
"to-help-translate": "想協助翻譯?按一下這裡"
|
||||
}
|
||||
},
|
||||
"resume-on-start": "應用啟動時繼續上次播放的歌曲",
|
||||
"resume-on-start": "應用開啟時繼續播放上次的歌曲",
|
||||
"single-instance-lock": "單實例模式",
|
||||
"start-at-login": "開機時啟動",
|
||||
"starting-page": {
|
||||
@ -142,8 +142,8 @@
|
||||
"label": "系統匣",
|
||||
"submenu": {
|
||||
"disabled": "已停用",
|
||||
"enabled-and-hide-app": "啟用並最小化應用程式",
|
||||
"enabled-and-show-app": "啟用但持續顯示應用程式",
|
||||
"enabled-and-hide-app": "啟用並最小化應用",
|
||||
"enabled-and-show-app": "啟用並顯示應用",
|
||||
"play-pause-on-click": "點擊時播放/暫停"
|
||||
}
|
||||
},
|
||||
@ -219,7 +219,7 @@
|
||||
"name": "進階專輯操作"
|
||||
},
|
||||
"album-color-theme": {
|
||||
"description": "依歌曲色調自動更改應用程式主題",
|
||||
"description": "根據專輯封面色調更改應用程式主題顏色",
|
||||
"menu": {
|
||||
"color-mix-ratio": {
|
||||
"label": "顏色混合程度",
|
||||
@ -407,9 +407,24 @@
|
||||
"writing-id3": "正在寫入 ID3 標籤…"
|
||||
}
|
||||
},
|
||||
"description": "在應用程式內下載 MP3/原始音樂檔",
|
||||
"description": "開啟應用程式內下載 MP3/原始音檔功能",
|
||||
"menu": {
|
||||
"choose-download-folder": "選擇下載位置",
|
||||
"download-finish-settings": {
|
||||
"label": "智慧下載",
|
||||
"prompt": {
|
||||
"last-percent": "歌曲剩餘多少 % 時下載",
|
||||
"last-seconds": "歌曲剩餘多少秒時下載",
|
||||
"title": "智慧下載進階設定"
|
||||
},
|
||||
"submenu": {
|
||||
"advanced": "進階",
|
||||
"enabled": "啟用",
|
||||
"mode": "判斷方式",
|
||||
"percent": "百分比",
|
||||
"seconds": "秒數"
|
||||
}
|
||||
},
|
||||
"download-playlist": "下載播放清單",
|
||||
"presets": "預設格式",
|
||||
"skip-existing": "跳過已存在的檔案"
|
||||
@ -494,7 +509,7 @@
|
||||
}
|
||||
},
|
||||
"navigation": {
|
||||
"description": "將上一頁/下一頁按鈕新增至應用程式上方, 就像你最熟悉的瀏覽器",
|
||||
"description": "允許應用程式上方顯示上一頁/下一頁按鈕",
|
||||
"name": "導覽列"
|
||||
},
|
||||
"no-google-login": {
|
||||
@ -650,7 +665,7 @@
|
||||
"name": "贊助阻擋"
|
||||
},
|
||||
"taskbar-mediacontrol": {
|
||||
"description": "透過工作列應用程式圖式控制媒體播放",
|
||||
"description": "允許工作列應用程式預覽介面顯示媒體控制相關按鈕",
|
||||
"name": "工作列媒體控制"
|
||||
},
|
||||
"touchbar": {
|
||||
|
||||
18
src/index.ts
18
src/index.ts
@ -100,9 +100,18 @@ if (config.get('options.disableHardwareAcceleration')) {
|
||||
app.disableHardwareAcceleration();
|
||||
}
|
||||
|
||||
if (is.linux() && config.plugins.isEnabled('shortcuts')) {
|
||||
if (is.linux()) {
|
||||
const disabledFeatures = [
|
||||
// Workaround for issue #2248
|
||||
'UseMultiPlaneFormatForSoftwareVideo',
|
||||
];
|
||||
|
||||
// Stops chromium from launching its own MPRIS service
|
||||
app.commandLine.appendSwitch('disable-features', 'MediaSessionService');
|
||||
if (config.plugins.isEnabled('shortcuts')) {
|
||||
disabledFeatures.push('MediaSessionService');
|
||||
}
|
||||
|
||||
app.commandLine.appendSwitch('disable-features', disabledFeatures.join());
|
||||
}
|
||||
|
||||
if (config.get('options.proxy')) {
|
||||
@ -312,10 +321,9 @@ async function createMainWindow() {
|
||||
const { x: windowX, y: windowY } = windowPosition;
|
||||
const winSize = win.getSize();
|
||||
const display = screen.getDisplayNearestPoint(windowPosition);
|
||||
const scaleFactor = is.windows() ? display.scaleFactor: 1;
|
||||
|
||||
const scaledWidth = Math.floor(windowSize.width / scaleFactor);
|
||||
const scaledHeight = Math.floor(windowSize.height / scaleFactor);
|
||||
const scaledWidth = windowSize.width;
|
||||
const scaledHeight = windowSize.height;
|
||||
|
||||
const scaledX = windowX;
|
||||
const scaledY = windowY;
|
||||
|
||||
56
src/plugins/adblocker/adSpeedup.ts
Normal file
56
src/plugins/adblocker/adSpeedup.ts
Normal file
@ -0,0 +1,56 @@
|
||||
function skipAd(target: Element) {
|
||||
const skipButton = target.querySelector<HTMLButtonElement>('button.ytp-ad-skip-button-modern');
|
||||
if (skipButton) {
|
||||
skipButton.click();
|
||||
}
|
||||
}
|
||||
|
||||
function speedUpAndMute(player: Element, isAdShowing: boolean) {
|
||||
const video = player.querySelector<HTMLVideoElement>('video');
|
||||
if (!video) return;
|
||||
if (isAdShowing) {
|
||||
video.playbackRate = 16;
|
||||
video.muted = true;
|
||||
} else if (!isAdShowing) {
|
||||
video.playbackRate = 1;
|
||||
video.muted = false;
|
||||
}
|
||||
}
|
||||
|
||||
export const loadAdSpeedup = async () => {
|
||||
const player = document.querySelector<HTMLVideoElement>('#movie_player');
|
||||
if (!player) return;
|
||||
|
||||
new MutationObserver((mutations) => {
|
||||
for (const mutation of mutations) {
|
||||
if (
|
||||
mutation.type === 'attributes' &&
|
||||
mutation.attributeName === 'class'
|
||||
) {
|
||||
const target = mutation.target as HTMLElement;
|
||||
|
||||
const isAdShowing =
|
||||
target.classList.contains('ad-showing') ||
|
||||
target.classList.contains('ad-interrupting');
|
||||
speedUpAndMute(target, isAdShowing);
|
||||
}
|
||||
if (
|
||||
mutation.type === 'childList' &&
|
||||
mutation.addedNodes.length &&
|
||||
mutation.target instanceof HTMLElement
|
||||
) {
|
||||
skipAd(mutation.target);
|
||||
}
|
||||
}
|
||||
}).observe(player, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
|
||||
const isAdShowing =
|
||||
player.classList.contains('ad-showing') ||
|
||||
player.classList.contains('ad-interrupting');
|
||||
speedUpAndMute(player, isAdShowing);
|
||||
skipAd(player);
|
||||
}
|
||||
@ -14,6 +14,7 @@ import { inject, isInjected } from './injectors/inject';
|
||||
import { t } from '@/i18n';
|
||||
|
||||
import type { BrowserWindow } from 'electron';
|
||||
import { loadAdSpeedup } from './adSpeedup';
|
||||
|
||||
interface AdblockerConfig {
|
||||
/**
|
||||
@ -72,6 +73,14 @@ export default createPlugin({
|
||||
},
|
||||
];
|
||||
},
|
||||
renderer: {
|
||||
async onPlayerApiReady(_, {getConfig}) {
|
||||
const config = await getConfig();
|
||||
if (config.blocker === blockers.AdSpeedup) {
|
||||
await loadAdSpeedup();
|
||||
}
|
||||
}
|
||||
},
|
||||
backend: {
|
||||
mainWindow: null as BrowserWindow | null,
|
||||
async start({ getConfig, window }) {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
export const blockers = {
|
||||
WithBlocklists: 'With blocklists',
|
||||
InPlayer: 'In player',
|
||||
AdSpeedup: 'Ad speedup',
|
||||
} as const;
|
||||
|
||||
@ -2,11 +2,12 @@ import { app, dialog, ipcMain } from 'electron';
|
||||
import { Client as DiscordClient } from '@xhayper/discord-rpc';
|
||||
import { dev } from 'electron-is';
|
||||
|
||||
import { ActivityType, GatewayActivityButton } from 'discord-api-types/v10';
|
||||
|
||||
import registerCallback, { type SongInfo } from '@/providers/song-info';
|
||||
import { createBackend, LoggerPrefix } from '@/utils';
|
||||
import { t } from '@/i18n';
|
||||
|
||||
import type { GatewayActivityButton } from 'discord-api-types/v10';
|
||||
import type { SetActivity } from '@xhayper/discord-rpc/dist/structures/ClientUser';
|
||||
import type { DiscordPluginConfig } from './index';
|
||||
|
||||
@ -180,6 +181,7 @@ export const backend = createBackend<
|
||||
}
|
||||
|
||||
const activityInfo: SetActivity = {
|
||||
type: ActivityType.Listening,
|
||||
details: songInfo.title,
|
||||
state: songInfo.artist,
|
||||
largeImageKey: songInfo.imageSrc ?? '',
|
||||
|
||||
@ -77,6 +77,7 @@ export const onRendererLoad = ({
|
||||
applyLyricsTabState();
|
||||
}
|
||||
};
|
||||
|
||||
const applyLyricsTabState = () => {
|
||||
if (lyrics) {
|
||||
tabs.lyrics.removeAttribute('disabled');
|
||||
@ -86,6 +87,7 @@ export const onRendererLoad = ({
|
||||
tabs.lyrics.setAttribute('aria-disabled', '');
|
||||
}
|
||||
};
|
||||
|
||||
const lyricsTabHandler = () => {
|
||||
const tabContainer = document.querySelector('ytmusic-tab-renderer');
|
||||
if (!tabContainer) return;
|
||||
|
||||
28
src/plugins/synced-lyrics/index.ts
Normal file
28
src/plugins/synced-lyrics/index.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import style from './style.css?inline';
|
||||
import { createPlugin } from '@/utils';
|
||||
|
||||
import { SyncedLyricsPluginConfig } from './types';
|
||||
|
||||
import { menu } from './menu';
|
||||
import { renderer } from './renderer';
|
||||
|
||||
import { t } from '@/i18n';
|
||||
|
||||
export default createPlugin({
|
||||
name: () => t('plugins.synced-lyrics.name'),
|
||||
description: () => t('plugins.synced-lyrics.description'),
|
||||
authors: ['Non0reo', 'ArjixWasTaken'],
|
||||
restartNeeded: true,
|
||||
addedVersion: '3.5.X',
|
||||
config: {
|
||||
preciseTiming: true,
|
||||
showLyricsEvenIfInexact: true,
|
||||
showTimeCodes: false,
|
||||
defaultTextString: '♪',
|
||||
lineEffect: 'scale',
|
||||
} as SyncedLyricsPluginConfig,
|
||||
|
||||
menu,
|
||||
renderer,
|
||||
stylesheets: [style],
|
||||
});
|
||||
138
src/plugins/synced-lyrics/menu.ts
Normal file
138
src/plugins/synced-lyrics/menu.ts
Normal file
@ -0,0 +1,138 @@
|
||||
import { MenuItemConstructorOptions } from 'electron';
|
||||
|
||||
import { MenuContext } from '@/types/contexts';
|
||||
import { SyncedLyricsPluginConfig } from './types';
|
||||
|
||||
export const menu = async ({
|
||||
getConfig,
|
||||
setConfig,
|
||||
}: MenuContext<SyncedLyricsPluginConfig>): Promise<
|
||||
MenuItemConstructorOptions[]
|
||||
> => {
|
||||
const config = await getConfig();
|
||||
|
||||
return [
|
||||
{
|
||||
label: 'Make the lyrics perfectly synced',
|
||||
toolTip:
|
||||
'Calculate to the milisecond the display of the next line (can have a small impact on performance)',
|
||||
type: 'checkbox',
|
||||
checked: config.preciseTiming,
|
||||
click(item) {
|
||||
setConfig({
|
||||
preciseTiming: item.checked,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Line effect',
|
||||
toolTip: 'Choose the effect to apply to the current line',
|
||||
type: 'submenu',
|
||||
submenu: [
|
||||
{
|
||||
label: 'Scale',
|
||||
toolTip: 'Scale the current line',
|
||||
type: 'radio',
|
||||
checked: config.lineEffect === 'scale',
|
||||
click() {
|
||||
setConfig({
|
||||
lineEffect: 'scale',
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Offset',
|
||||
toolTip: 'Offset on the right the current line',
|
||||
type: 'radio',
|
||||
checked: config.lineEffect === 'offset',
|
||||
click() {
|
||||
setConfig({
|
||||
lineEffect: 'offset',
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Focus',
|
||||
toolTip: 'Make only the current line white',
|
||||
type: 'radio',
|
||||
checked: config.lineEffect === 'focus',
|
||||
click() {
|
||||
setConfig({
|
||||
lineEffect: 'focus',
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Default character between lyrics',
|
||||
toolTip: 'Choose the default string to use for the gap between lyrics',
|
||||
type: 'submenu',
|
||||
submenu: [
|
||||
{
|
||||
label: '♪',
|
||||
type: 'radio',
|
||||
checked: config.defaultTextString === '♪',
|
||||
click() {
|
||||
setConfig({
|
||||
defaultTextString: '♪',
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '[SPACE]',
|
||||
type: 'radio',
|
||||
checked: config.defaultTextString === ' ',
|
||||
click() {
|
||||
setConfig({
|
||||
defaultTextString: ' ',
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '...',
|
||||
type: 'radio',
|
||||
checked: config.defaultTextString === '...',
|
||||
click() {
|
||||
setConfig({
|
||||
defaultTextString: '...',
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '———',
|
||||
type: 'radio',
|
||||
checked: config.defaultTextString === '———',
|
||||
click() {
|
||||
setConfig({
|
||||
defaultTextString: '———',
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Show time codes',
|
||||
toolTip: 'Show the time codes next to the lyrics',
|
||||
type: 'checkbox',
|
||||
checked: config.showTimeCodes,
|
||||
click(item) {
|
||||
setConfig({
|
||||
showTimeCodes: item.checked,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Show lyrics even if inexact',
|
||||
toolTip:
|
||||
'If the song is not found, the plugin tries again with a different search query.\nThe result from the second attempt may not be exact.',
|
||||
type: 'checkbox',
|
||||
checked: config.showLyricsEvenIfInexact,
|
||||
click(item) {
|
||||
setConfig({
|
||||
showLyricsEvenIfInexact: item.checked,
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
@ -0,0 +1,145 @@
|
||||
import { createSignal, For, Match, Show, Switch } from 'solid-js';
|
||||
|
||||
import { SyncedLine } from './SyncedLine';
|
||||
|
||||
import { t } from '@/i18n';
|
||||
import { getSongInfo } from '@/providers/song-info-front';
|
||||
|
||||
import { LineLyrics } from '../../types';
|
||||
import {
|
||||
differentDuration,
|
||||
hadSecondAttempt,
|
||||
isFetching,
|
||||
isInstrumental,
|
||||
makeLyricsRequest,
|
||||
} from '../lyrics/fetch';
|
||||
|
||||
export const [debugInfo, setDebugInfo] = createSignal<string>();
|
||||
export const [lineLyrics, setLineLyrics] = createSignal<LineLyrics[]>([]);
|
||||
export const [currentTime, setCurrentTime] = createSignal<number>(-1);
|
||||
|
||||
export const LyricsContainer = () => {
|
||||
const [error, setError] = createSignal('');
|
||||
|
||||
const onRefetch = async () => {
|
||||
if (isFetching()) return;
|
||||
setError('');
|
||||
|
||||
const info = getSongInfo();
|
||||
await makeLyricsRequest(info).catch((err) => {
|
||||
setError(`${err}`);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={'lyric-container'}>
|
||||
<Switch>
|
||||
<Match when={isFetching()}>
|
||||
<div style="margin-bottom: 8px;">
|
||||
<tp-yt-paper-spinner-lite
|
||||
active
|
||||
class="loading-indicator style-scope"
|
||||
/>
|
||||
</div>
|
||||
</Match>
|
||||
<Match when={error()}>
|
||||
<yt-formatted-string
|
||||
class="warning-lyrics description ytmusic-description-shelf-renderer"
|
||||
text={{
|
||||
runs: [
|
||||
{
|
||||
text: t('plugins.synced-lyrics.errors.fetch'),
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</Match>
|
||||
</Switch>
|
||||
|
||||
<Switch>
|
||||
<Match when={!lineLyrics().length}>
|
||||
<Show
|
||||
when={isInstrumental()}
|
||||
fallback={
|
||||
<>
|
||||
<yt-formatted-string
|
||||
class="warning-lyrics description ytmusic-description-shelf-renderer"
|
||||
text={{
|
||||
runs: [
|
||||
{
|
||||
text: t('plugins.synced-lyrics.errors.not-found'),
|
||||
},
|
||||
],
|
||||
}}
|
||||
style={'margin-bottom: 16px;'}
|
||||
/>
|
||||
<yt-button-renderer
|
||||
disabled={isFetching()}
|
||||
data={{
|
||||
icon: { iconType: 'REFRESH' },
|
||||
isDisabled: false,
|
||||
style: 'STYLE_DEFAULT',
|
||||
text: {
|
||||
simpleText: isFetching()
|
||||
? t('plugins.synced-lyrics.refetch-btn.fetching')
|
||||
: t('plugins.synced-lyrics.refetch-btn.normal'),
|
||||
},
|
||||
}}
|
||||
onClick={onRefetch}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<yt-formatted-string
|
||||
class="warning-lyrics description ytmusic-description-shelf-renderer"
|
||||
text={{
|
||||
runs: [
|
||||
{
|
||||
text: t('plugins.synced-lyrics.warnings.instrumental'),
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</Show>
|
||||
</Match>
|
||||
<Match when={lineLyrics().length && !hadSecondAttempt()}>
|
||||
<yt-formatted-string
|
||||
class="warning-lyrics description ytmusic-description-shelf-renderer"
|
||||
text={{
|
||||
runs: [
|
||||
{
|
||||
text: t('plugins.synced-lyrics.warnings.inexact'),
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</Match>
|
||||
<Match when={lineLyrics().length && !differentDuration()}>
|
||||
<yt-formatted-string
|
||||
class="warning-lyrics description ytmusic-description-shelf-renderer"
|
||||
text={{
|
||||
runs: [
|
||||
{
|
||||
text: t('plugins.synced-lyrics.warnings.duration-mismatch'),
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</Match>
|
||||
</Switch>
|
||||
|
||||
<For each={lineLyrics()}>{(item) => <SyncedLine line={item} />}</For>
|
||||
|
||||
<yt-formatted-string
|
||||
class="footer style-scope ytmusic-description-shelf-renderer"
|
||||
text={{
|
||||
runs: [
|
||||
{
|
||||
text: 'Source: LRCLIB',
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
53
src/plugins/synced-lyrics/renderer/components/SyncedLine.tsx
Normal file
53
src/plugins/synced-lyrics/renderer/components/SyncedLine.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import { createEffect, createMemo } from 'solid-js';
|
||||
|
||||
import { currentTime } from './LyricsContainer';
|
||||
|
||||
import { config } from '../renderer';
|
||||
import { _ytAPI } from '..';
|
||||
|
||||
import type { LineLyrics } from '../../types';
|
||||
|
||||
interface SyncedLineProps {
|
||||
line: LineLyrics;
|
||||
}
|
||||
|
||||
export const SyncedLine = ({ line }: SyncedLineProps) => {
|
||||
const status = createMemo(() => {
|
||||
const current = currentTime();
|
||||
|
||||
if (line.timeInMs >= current) return 'upcoming';
|
||||
if (current - line.timeInMs >= line.duration) return 'previous';
|
||||
return 'current';
|
||||
});
|
||||
|
||||
let ref: HTMLDivElement;
|
||||
createEffect(() => {
|
||||
if (status() === 'current') {
|
||||
ref.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref!}
|
||||
class={`synced-line ${status()}`}
|
||||
onClick={() => {
|
||||
_ytAPI?.seekTo(line.timeInMs / 1000);
|
||||
}}
|
||||
>
|
||||
<yt-formatted-string
|
||||
class="text-lyrics description ytmusic-description-shelf-renderer"
|
||||
text={{
|
||||
runs: [
|
||||
{
|
||||
text: '',
|
||||
},
|
||||
{
|
||||
text: `${config()?.showTimeCodes ? `[${line.time}] ` : ''}${line.text}`,
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
94
src/plugins/synced-lyrics/renderer/index.ts
Normal file
94
src/plugins/synced-lyrics/renderer/index.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { createRenderer } from '@/utils';
|
||||
|
||||
import { makeLyricsRequest } from './lyrics';
|
||||
import { selectors, tabStates } from './utils';
|
||||
import { setConfig } from './renderer';
|
||||
import { setCurrentTime } from './components/LyricsContainer';
|
||||
|
||||
import type { RendererContext } from '@/types/contexts';
|
||||
import type { YoutubePlayer } from '@/types/youtube-player';
|
||||
import type { SongInfo } from '@/providers/song-info';
|
||||
|
||||
import type { SyncedLyricsPluginConfig } from '../types';
|
||||
|
||||
export let _ytAPI: YoutubePlayer | null = null;
|
||||
|
||||
export const renderer = createRenderer<{
|
||||
observerCallback: MutationCallback;
|
||||
onPlayerApiReady: (api: YoutubePlayer) => void;
|
||||
hasAddedEvents: boolean;
|
||||
observer?: MutationObserver;
|
||||
videoDataChange: () => void;
|
||||
progressCallback: (evt: Event) => void;
|
||||
}, SyncedLyricsPluginConfig>({
|
||||
onConfigChange(newConfig) {
|
||||
setConfig(newConfig);
|
||||
},
|
||||
|
||||
observerCallback(mutations: MutationRecord[]) {
|
||||
for (const mutation of mutations) {
|
||||
const header = mutation.target as HTMLElement;
|
||||
|
||||
switch (mutation.attributeName) {
|
||||
case 'disabled':
|
||||
header.removeAttribute('disabled');
|
||||
break;
|
||||
case 'aria-selected':
|
||||
tabStates[header.ariaSelected as 'true' | 'false']?.(
|
||||
_ytAPI?.getVideoData(),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onPlayerApiReady(api: YoutubePlayer) {
|
||||
_ytAPI = api;
|
||||
|
||||
api.addEventListener('videodatachange', this.videoDataChange);
|
||||
|
||||
this.videoDataChange();
|
||||
},
|
||||
|
||||
hasAddedEvents: false,
|
||||
|
||||
videoDataChange() {
|
||||
if (!this.hasAddedEvents) {
|
||||
const video = document.querySelector('video');
|
||||
|
||||
video?.addEventListener('timeupdate', this.progressCallback);
|
||||
|
||||
if (video) this.hasAddedEvents = true;
|
||||
}
|
||||
|
||||
const header = document.querySelector<HTMLElement>(selectors.head);
|
||||
if (!header) return;
|
||||
|
||||
this.observer ??= new MutationObserver(
|
||||
this.observerCallback,
|
||||
);
|
||||
|
||||
// Force the lyrics tab to be enabled at all times.
|
||||
this.observer.disconnect();
|
||||
this.observer.observe(header, { attributes: true });
|
||||
header.removeAttribute('disabled');
|
||||
},
|
||||
|
||||
progressCallback(evt: Event) {
|
||||
switch (evt.type) {
|
||||
case 'timeupdate': {
|
||||
const video = evt.target as HTMLVideoElement;
|
||||
setCurrentTime(video.currentTime * 1000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async start(ctx: RendererContext<SyncedLyricsPluginConfig>) {
|
||||
setConfig(await ctx.getConfig());
|
||||
|
||||
ctx.ipc.on('ytmd:update-song-info', async (info: SongInfo) => {
|
||||
await makeLyricsRequest(info);
|
||||
});
|
||||
},
|
||||
});
|
||||
197
src/plugins/synced-lyrics/renderer/lyrics/fetch.ts
Normal file
197
src/plugins/synced-lyrics/renderer/lyrics/fetch.ts
Normal file
@ -0,0 +1,197 @@
|
||||
import { createSignal } from 'solid-js';
|
||||
import { jaroWinkler } from '@skyra/jaro-winkler';
|
||||
|
||||
import { SongInfo } from '@/providers/song-info';
|
||||
|
||||
import { LineLyrics, LRCLIBSearchResponse } from '../../types';
|
||||
import { config } from '../renderer';
|
||||
import { setDebugInfo, setLineLyrics } from '../components/LyricsContainer';
|
||||
|
||||
// prettier-ignore
|
||||
export const [isInstrumental, setIsInstrumental] = createSignal(false);
|
||||
// prettier-ignore
|
||||
export const [isFetching, setIsFetching] = createSignal(false);
|
||||
// prettier-ignore
|
||||
export const [hadSecondAttempt, setHadSecondAttempt] = createSignal(false);
|
||||
// prettier-ignore
|
||||
export const [differentDuration, setDifferentDuration] = createSignal(false);
|
||||
// eslint-disable-next-line prefer-const
|
||||
export let foundPlainTextLyrics = false;
|
||||
|
||||
export type SongData = {
|
||||
title: string;
|
||||
artist: string;
|
||||
album: string;
|
||||
songDuration: number;
|
||||
};
|
||||
|
||||
export const extractTimeAndText = (
|
||||
line: string,
|
||||
index: number,
|
||||
): LineLyrics | null => {
|
||||
const groups = /\[(\d+):(\d+)\.(\d+)\](.+)/.exec(line);
|
||||
if (!groups) return null;
|
||||
|
||||
const [_, rMinutes, rSeconds, rMillis, text] = groups;
|
||||
const [minutes, seconds, millis] = [
|
||||
parseInt(rMinutes),
|
||||
parseInt(rSeconds),
|
||||
parseInt(rMillis),
|
||||
];
|
||||
|
||||
// prettier-ignore
|
||||
const timeInMs = (minutes * 60 * 1000) + (seconds * 1000) + millis;
|
||||
|
||||
return {
|
||||
index,
|
||||
timeInMs,
|
||||
time: `${minutes}:${seconds}:${millis}`,
|
||||
text: text?.trim() ?? config()!.defaultTextString,
|
||||
status: 'upcoming',
|
||||
duration: 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const makeLyricsRequest = async (extractedSongInfo: SongInfo) => {
|
||||
setLineLyrics([]);
|
||||
const songData: SongData = {
|
||||
title: `${extractedSongInfo.title}`,
|
||||
artist: `${extractedSongInfo.artist}`,
|
||||
album: `${extractedSongInfo.album}`,
|
||||
songDuration: extractedSongInfo.songDuration,
|
||||
};
|
||||
|
||||
const lyrics = await getLyricsList(songData);
|
||||
setLineLyrics(lyrics ?? []);
|
||||
};
|
||||
|
||||
export const getLyricsList = async (
|
||||
songData: SongData,
|
||||
): Promise<LineLyrics[] | null> => {
|
||||
setIsFetching(true);
|
||||
setIsInstrumental(false);
|
||||
setHadSecondAttempt(false);
|
||||
setDifferentDuration(false);
|
||||
setDebugInfo('Searching for lyrics...');
|
||||
|
||||
let query = new URLSearchParams({
|
||||
artist_name: songData.artist,
|
||||
track_name: songData.title,
|
||||
});
|
||||
|
||||
if (songData.album) {
|
||||
query.set('album_name', songData.album);
|
||||
}
|
||||
|
||||
let url = `https://lrclib.net/api/search?${query.toString()}`;
|
||||
let response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
setIsFetching(false);
|
||||
setDebugInfo('Got non-OK response from server.');
|
||||
return null;
|
||||
}
|
||||
|
||||
let data = (await response.json().catch((e: Error) => {
|
||||
setDebugInfo(`Error: ${e.message}\n\n${e.stack}`);
|
||||
|
||||
return null;
|
||||
})) as LRCLIBSearchResponse | null;
|
||||
if (!data || !Array.isArray(data)) {
|
||||
setIsFetching(false);
|
||||
setDebugInfo('Unexpected server response.');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Note: If no lyrics are found, try again with a different search query
|
||||
if (data.length === 0) {
|
||||
if (!config()?.showLyricsEvenIfInexact) {
|
||||
return null;
|
||||
}
|
||||
|
||||
query = new URLSearchParams({ q: songData.title });
|
||||
url = `https://lrclib.net/api/search?${query.toString()}`;
|
||||
|
||||
response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
setIsFetching(false);
|
||||
setDebugInfo('Got non-OK response from server. (2)');
|
||||
return null;
|
||||
}
|
||||
|
||||
data = (await response.json()) as LRCLIBSearchResponse;
|
||||
if (!Array.isArray(data)) {
|
||||
setIsFetching(false);
|
||||
setDebugInfo('Unexpected server response. (2)');
|
||||
return null;
|
||||
}
|
||||
|
||||
setHadSecondAttempt(true);
|
||||
}
|
||||
|
||||
const filteredResults = [];
|
||||
for (const item of data) {
|
||||
if (!item.syncedLyrics) continue;
|
||||
|
||||
const { artist } = songData;
|
||||
const { artistName } = item;
|
||||
|
||||
const ratio = jaroWinkler(artist.toLowerCase(), artistName.toLowerCase());
|
||||
|
||||
if (ratio <= 0.9) continue;
|
||||
filteredResults.push(item);
|
||||
}
|
||||
|
||||
const duration = songData.songDuration;
|
||||
filteredResults.sort(({ duration: durationA }, { duration: durationB }) => {
|
||||
const left = Math.abs(durationA - duration);
|
||||
const right = Math.abs(durationB - duration);
|
||||
|
||||
return left - right;
|
||||
});
|
||||
|
||||
const closestResult = filteredResults[0];
|
||||
if (!closestResult) {
|
||||
setIsFetching(false);
|
||||
setDebugInfo('No search result matched the criteria.');
|
||||
return null;
|
||||
}
|
||||
|
||||
// setDebugInfo(JSON.stringify(closestResult, null, 4));
|
||||
|
||||
if (Math.abs(closestResult.duration - duration) > 15) return null;
|
||||
if (Math.abs(closestResult.duration - duration) > 5) {
|
||||
// show message that the timings may be wrong
|
||||
setDifferentDuration(true);
|
||||
}
|
||||
|
||||
setIsInstrumental(closestResult.instrumental);
|
||||
|
||||
// Separate the lyrics into lines
|
||||
const raw = closestResult.syncedLyrics.split('\n');
|
||||
|
||||
// Add a blank line at the beginning
|
||||
raw.unshift('[0:0.0] ');
|
||||
|
||||
const syncedLyricList = [];
|
||||
|
||||
for (let idx = 0; idx < raw.length; idx++) {
|
||||
const syncedLine = extractTimeAndText(raw[idx], idx);
|
||||
if (syncedLine) {
|
||||
syncedLyricList.push(syncedLine);
|
||||
}
|
||||
}
|
||||
|
||||
for (const line of syncedLyricList) {
|
||||
const next = syncedLyricList[line.index + 1];
|
||||
if (!next) {
|
||||
line.duration = Infinity;
|
||||
break;
|
||||
}
|
||||
|
||||
line.duration = next.timeInMs - line.timeInMs;
|
||||
}
|
||||
|
||||
setIsFetching(false);
|
||||
return syncedLyricList;
|
||||
};
|
||||
44
src/plugins/synced-lyrics/renderer/lyrics/index.ts
Normal file
44
src/plugins/synced-lyrics/renderer/lyrics/index.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { createEffect } from 'solid-js';
|
||||
|
||||
import { config } from '../renderer';
|
||||
|
||||
export { makeLyricsRequest } from './fetch';
|
||||
|
||||
createEffect(() => {
|
||||
if (!config()?.enabled) return;
|
||||
const root = document.documentElement;
|
||||
|
||||
// Set the line effect
|
||||
switch (config()?.lineEffect) {
|
||||
case 'scale':
|
||||
root.style.setProperty(
|
||||
'--previous-lyrics',
|
||||
'var(--ytmusic-text-primary)',
|
||||
);
|
||||
root.style.setProperty('--current-lyrics', 'var(--ytmusic-text-primary)');
|
||||
root.style.setProperty('--size-lyrics', '1.2');
|
||||
root.style.setProperty('--offset-lyrics', '0');
|
||||
root.style.setProperty('--lyric-width', '83%');
|
||||
break;
|
||||
case 'offset':
|
||||
root.style.setProperty(
|
||||
'--previous-lyrics',
|
||||
'var(--ytmusic-text-primary)',
|
||||
);
|
||||
root.style.setProperty('--current-lyrics', 'var(--ytmusic-text-primary)');
|
||||
root.style.setProperty('--size-lyrics', '1');
|
||||
root.style.setProperty('--offset-lyrics', '5%');
|
||||
root.style.setProperty('--lyric-width', '100%');
|
||||
break;
|
||||
case 'focus':
|
||||
root.style.setProperty(
|
||||
'--previous-lyrics',
|
||||
'var(--ytmusic-text-secondary)',
|
||||
);
|
||||
root.style.setProperty('--current-lyrics', 'var(--ytmusic-text-primary)');
|
||||
root.style.setProperty('--size-lyrics', '1');
|
||||
root.style.setProperty('--offset-lyrics', '0');
|
||||
root.style.setProperty('--lyric-width', '100%');
|
||||
break;
|
||||
}
|
||||
});
|
||||
20
src/plugins/synced-lyrics/renderer/renderer.tsx
Normal file
20
src/plugins/synced-lyrics/renderer/renderer.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { createSignal, Show } from 'solid-js';
|
||||
|
||||
import { VideoDetails } from '@/types/video-details';
|
||||
|
||||
import { LyricsContainer } from './components/LyricsContainer';
|
||||
|
||||
import { SyncedLyricsPluginConfig } from '../types';
|
||||
|
||||
export const [isVisible, setIsVisible] = createSignal<boolean>(false);
|
||||
|
||||
export const [config, setConfig] = createSignal<SyncedLyricsPluginConfig | null>(null);
|
||||
export const [playerState, setPlayerState] = createSignal<VideoDetails | null>(null);
|
||||
|
||||
export const LyricsRenderer = () => {
|
||||
return (
|
||||
<Show when={isVisible()}>
|
||||
<LyricsContainer />
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
37
src/plugins/synced-lyrics/renderer/utils.tsx
Normal file
37
src/plugins/synced-lyrics/renderer/utils.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { render } from 'solid-js/web';
|
||||
|
||||
import { LyricsRenderer, setIsVisible, setPlayerState } from './renderer';
|
||||
import { VideoDetails } from '@/types/video-details';
|
||||
|
||||
export const selectors = {
|
||||
head: '#tabsContent > .tab-header:nth-of-type(2)',
|
||||
body: {
|
||||
tabRenderer: '#tab-renderer[page-type="MUSIC_PAGE_TYPE_TRACK_LYRICS"]',
|
||||
root: 'ytmusic-description-shelf-renderer',
|
||||
},
|
||||
};
|
||||
|
||||
export const tabStates = {
|
||||
true: (data?: VideoDetails) => {
|
||||
setIsVisible(true);
|
||||
setPlayerState(data ?? null);
|
||||
|
||||
const tabRenderer = document.querySelector<HTMLElement>(
|
||||
selectors.body.tabRenderer,
|
||||
);
|
||||
if (!tabRenderer) return;
|
||||
|
||||
let container = document.querySelector('#synced-lyrics-container');
|
||||
if (container) return;
|
||||
|
||||
container = Object.assign(document.createElement('div'), {
|
||||
id: 'synced-lyrics-container',
|
||||
});
|
||||
|
||||
tabRenderer.appendChild(container);
|
||||
render(() => <LyricsRenderer />, container);
|
||||
},
|
||||
false: () => {
|
||||
setIsVisible(false);
|
||||
},
|
||||
};
|
||||
78
src/plugins/synced-lyrics/style.css
Normal file
78
src/plugins/synced-lyrics/style.css
Normal file
@ -0,0 +1,78 @@
|
||||
/* Hides the original lyrics, to only show our own. */
|
||||
#tab-renderer[page-type='MUSIC_PAGE_TYPE_TRACK_LYRICS'] > * {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#tab-renderer[page-type='MUSIC_PAGE_TYPE_TRACK_LYRICS'] > #synced-lyrics-container {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* :root {
|
||||
--ytmusic-text-primary: #fff;
|
||||
--ytmusic-text-secondary: #aaa;
|
||||
} */
|
||||
|
||||
:root {
|
||||
--global-margin: 0.7rem;
|
||||
--previous-lyrics: var(--ytmusic-text-primary);
|
||||
--current-lyrics: var(--ytmusic-text-primary);
|
||||
--upcoming-lyrics: var(--ytmusic-text-secondary);
|
||||
--size-lyrics: 1.2em;
|
||||
--offset-lyrics: 1em;
|
||||
}
|
||||
|
||||
.lyric-container {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: clamp(1.4rem, 1.1vmax, 3rem) !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.synced-line {
|
||||
width: var(--lyric-width, 100%);
|
||||
}
|
||||
|
||||
.synced-line > .text-lyrics {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.synced-lyrics {
|
||||
display: block;
|
||||
justify-content: left;
|
||||
text-align: left;
|
||||
margin: 0.5rem 0;
|
||||
margin-right: 20px;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.warning-lyrics {
|
||||
color: var(--ytmusic-text-secondary) !important;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.text-lyrics {
|
||||
display: block;
|
||||
text-align: left;
|
||||
margin: var(--global-margin) 0;
|
||||
transition: scale 0.3s ease-in-out, translate 0.3s ease-in-out, color 0.1s ease-in-out;
|
||||
transform-origin: 0 50%;
|
||||
}
|
||||
|
||||
.previous > .text-lyrics {
|
||||
color: var(--previous-lyrics);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.current > .text-lyrics {
|
||||
color: var(--current-lyrics);
|
||||
font-weight: bold;
|
||||
scale: var(--size-lyrics);
|
||||
translate: var(--offset-lyrics) 0;
|
||||
}
|
||||
|
||||
.upcoming > .text-lyrics {
|
||||
color: var(--upcoming-lyrics);
|
||||
font-weight: normal;
|
||||
}
|
||||
38
src/plugins/synced-lyrics/types.ts
Normal file
38
src/plugins/synced-lyrics/types.ts
Normal file
@ -0,0 +1,38 @@
|
||||
export type SyncedLyricsPluginConfig = {
|
||||
enabled: boolean;
|
||||
preciseTiming: boolean;
|
||||
showTimeCodes: boolean;
|
||||
defaultTextString: string;
|
||||
showLyricsEvenIfInexact: boolean;
|
||||
lineEffect: LineEffect;
|
||||
};
|
||||
|
||||
export type LineLyricsStatus = 'previous' | 'current' | 'upcoming';
|
||||
|
||||
export type LineLyrics = {
|
||||
index: number;
|
||||
time: string;
|
||||
timeInMs: number;
|
||||
text: string;
|
||||
duration: number;
|
||||
status: LineLyricsStatus;
|
||||
};
|
||||
|
||||
export type PlayPauseEvent = {
|
||||
isPaused: boolean;
|
||||
elapsedSeconds: number;
|
||||
};
|
||||
|
||||
export type LineEffect = 'scale' | 'offset' | 'focus';
|
||||
|
||||
export type LRCLIBSearchResponse = {
|
||||
id: number;
|
||||
name: string;
|
||||
trackName: string;
|
||||
artistName: string;
|
||||
albumName: string;
|
||||
duration: number;
|
||||
instrumental: boolean;
|
||||
plainLyrics: string;
|
||||
syncedLyrics: string;
|
||||
}[];
|
||||
@ -241,7 +241,7 @@ export interface FlagEndpoint {
|
||||
flagAction: string;
|
||||
}
|
||||
|
||||
export type VideoDataChangeValue = Record<string, unknown> & {
|
||||
export type VideoDataChangeValue = {
|
||||
videoId: string;
|
||||
title: string;
|
||||
author: string;
|
||||
|
||||
37
src/yt-web-components.d.ts
vendored
Normal file
37
src/yt-web-components.d.ts
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
import type { ComponentProps } from 'solid-js';
|
||||
|
||||
declare module 'solid-js' {
|
||||
namespace JSX {
|
||||
interface YtFormattedStringProps {
|
||||
text?: {
|
||||
runs: { text: string }[];
|
||||
};
|
||||
data?: object;
|
||||
disabled?: boolean;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
interface YtButtonRendererProps {
|
||||
data?: {
|
||||
icon?: {
|
||||
iconType: string;
|
||||
};
|
||||
isDisabled?: boolean;
|
||||
style?: string;
|
||||
text?: {
|
||||
simpleText: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface YpYtPaperSpinnerLiteProps {
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
interface IntrinsicElements {
|
||||
'yt-formatted-string': ComponentProps<'span'> & YtFormattedStringProps;
|
||||
'yt-button-renderer': ComponentProps<'button'> & YtButtonRendererProps;
|
||||
'tp-yt-paper-spinner-lite': ComponentProps<'div'> & YpYtPaperSpinnerLiteProps;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user