Compare commits

..

5 Commits

Author SHA1 Message Date
jeffvli c03f611547 convert remaining locales to sentence case 2026-05-11 19:26:15 -07:00
jeffvli bfbba60873 refactor some strings to sentence case 2026-05-11 19:25:51 -07:00
jeffvli 808b9f2e9d remove sentence case override configuration 2026-05-11 19:25:10 -07:00
jeffvli 36d9f513ac convert locales to new casing 2026-05-11 19:25:10 -07:00
jeffvli 737f1afd76 convert EN localization to use proper casing, remove postprocessing from renderer 2026-05-11 19:19:01 -07:00
194 changed files with 4396 additions and 12481 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ body:
- type: checkboxes
id: check-duplicate
attributes:
label: I have already checked through the existing (both open AND closed) bug reports and found no duplicates
label: I have already checked through the existing bug reports and found no duplicates
options:
- label: 'Yes'
required: true
+4
View File
@@ -44,6 +44,8 @@ jobs:
- name: Install Node and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
@@ -127,6 +129,8 @@ jobs:
- name: Install Node and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
+4
View File
@@ -19,6 +19,8 @@ jobs:
- name: Install Node and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
@@ -121,6 +123,8 @@ jobs:
- name: Install Node and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
+5 -9
View File
@@ -4,11 +4,6 @@ permissions: write-all
on:
workflow_dispatch:
inputs:
tag:
description: 'Docker image tag (e.g. 1.12.0 or latest)'
required: true
type: string
push:
tags:
- 'v*.*.*'
@@ -38,10 +33,11 @@ jobs:
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=${{ inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }}
type=semver,pattern={{version}},enable=${{ github.event_name == 'push' }}
type=semver,pattern={{major}}.{{minor}},enable=${{ github.event_name == 'push' }}
type=semver,pattern={{major}},enable=${{ github.event_name == 'push' }}
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Setup Docker buildx
+2
View File
@@ -16,6 +16,8 @@ jobs:
- name: Install Node and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
+2
View File
@@ -16,6 +16,8 @@ jobs:
- name: Install Node and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
+2
View File
@@ -38,6 +38,8 @@ jobs:
- name: Install Node and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
+2
View File
@@ -16,6 +16,8 @@ jobs:
- name: Install Node and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
+2
View File
@@ -16,6 +16,8 @@ jobs:
- name: Install Node and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
+5 -6
View File
@@ -12,8 +12,7 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v6
- uses: dessant/lock-threads@v5
with:
process-only: 'issues, prs'
issue-inactive-days: 120
@@ -30,19 +29,19 @@ jobs:
days-before-pr-close: 30
stale-issue-message: >
This issue has been automatically marked as stale because it has not had recent activity. The resources of the Feishin team are limited, and so we are asking for your help.
If this is a **bug** and you can still reproduce this error on the <code>development</code> branch, please reply with all of the information you have about it in order to keep the issue open.
This issue will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.
stale-pr-message: >
This PR has been automatically marked as stale because it has not had recent activity. The resources of the Feishin team are limited, and so we are asking for your help.
This PR will automatically be closed in the near future if no further activity occurs. Thank you for all your contributions.
stale-issue-label: 'stale'
exempt-issue-labels: 'keep,security,enhancement'
exempt-issue-labels: 'keep,security'
stale-pr-label: 'stale'
exempt-pr-labels: 'keep,security'
+2
View File
@@ -12,6 +12,8 @@ jobs:
- name: Install Node.js and PNPM
uses: pnpm/action-setup@v4
with:
version: 10
- name: Install dependencies
run: pnpm install
+3 -1
View File
@@ -3,7 +3,9 @@ FROM node:23-alpine AS builder
WORKDIR /app
# Copy package.json first to cache node_modules
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .
COPY package.json pnpm-lock.yaml .
RUN npm install -g pnpm
RUN pnpm install
-12
View File
@@ -43,13 +43,7 @@ These variables override app settings **on first run** when no persisted setting
| `general.showVisualizerInSidebar` | `true` | `FS_GENERAL_SHOW_VISUALIZER_IN_SIDEBAR` | `true` / `false` — Show visualizer in sidebar. |
| `general.sidebarCollapsedNavigation` | `true` | `FS_GENERAL_SIDEBAR_COLLAPSED_NAVIGATION` | `true` / `false` — Start with collapsed sidebar nav. |
| `general.sidebarCollapseShared` | `false` | `FS_GENERAL_SIDEBAR_COLLAPSE_SHARED` | `true` / `false` — Share sidebar collapse state. |
| `general.sidebarPlaylistFolders` | `true` | `FS_GENERAL_SIDEBAR_PLAYLIST_FOLDERS` | `true` / `false` — Group playlists into folders by name separator. |
| `general.sidebarPlaylistFolderSeparator` | `/` | `FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_SEPARATOR` | Character or string that separates folder levels in a playlist name. Empty = use default. |
| `general.sidebarPlaylistFolderTreeIndent` | `16` | `FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_TREE_INDENT` | Pixels each tree level is indented (064). |
| `general.sidebarPlaylistFolderTreeLineColor` | *(empty)* | `FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_TREE_LINE_COLOR` | CSS color for tree connecting lines. Empty = theme default. |
| `general.sidebarPlaylistFolderView` | `tree` | `FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_VIEW` | `single` / `tree` / `navigation` — How folders are displayed in the sidebar. |
| `general.sidebarPlaylistList` | `true` | `FS_GENERAL_SIDEBAR_PLAYLIST_LIST` | `true` / `false` — Show playlist list in sidebar. |
| `general.sidebarPlaylistMode` | `expanded` | `FS_GENERAL_SIDEBAR_PLAYLIST_MODE` | `compact` / `expanded` — Sidebar playlist row layout. |
| `general.sidebarPlaylistSorting` | `false` | `FS_GENERAL_SIDEBAR_PLAYLIST_SORTING` | `true` / `false` — Enable playlist sorting in sidebar. |
| `general.sideQueueType` | `sideQueue` | `FS_GENERAL_SIDE_QUEUE_TYPE` | `sideDrawerQueue` / `sideQueue` — Side play queue style. |
| `general.sideQueueLayout` | `horizontal` | `FS_GENERAL_SIDE_QUEUE_LAYOUT` | `horizontal` / `vertical` — Attached side queue layout orientation. |
@@ -72,9 +66,6 @@ These variables override app settings **on first run** when no persisted setting
| `playback.scrobble.scrobbleAtDuration` | `240` | `FS_PLAYBACK_SCROBBLE_AT_DURATION` | Seconds of playback before scrobble. |
| `playback.scrobble.scrobbleAtPercentage` | `75` | `FS_PLAYBACK_SCROBBLE_AT_PERCENTAGE` | Percentage of track before scrobble. |
| `playback.transcode.enabled` | `false` | `FS_PLAYBACK_TRANSCODE_ENABLED` | `true` / `false` — Enable transcoding. |
| `playback.transcode.format` | *(unset)* | `FS_PLAYBACK_TRANSCODE_FORMAT` | Transcode format string (codec/container), e.g. server-specific value. Empty = use default. |
| `playback.transcode.bitrate` | *(unset)* | `FS_PLAYBACK_TRANSCODE_BITRATE` | Transcode bitrate (number, kbps or as defined by server). |
| `playback.filters` | `[]` | `FS_PLAYBACK_FILTERS` | JSON array of player filters: each object needs `id`, `field`, `operator`, `value`; optional `isEnabled`. Invalid JSON or shape is ignored. |
---
@@ -114,11 +105,8 @@ These variables override app settings **on first run** when no persisted setting
| Setting path | Default | Env variable | Available values / Description |
|-------------|---------|--------------|--------------------------------|
| `autoDJ.albumStrategy` | `similar` | `FS_AUTO_DJ_ALBUM_STRATEGY` | `similar` / `library_random`. |
| `autoDJ.enabled` | `false` | `FS_AUTO_DJ_ENABLED` | `true` / `false`. |
| `autoDJ.itemCount` | `5` | `FS_AUTO_DJ_ITEM_COUNT` | Number of items to add. |
| `autoDJ.mode` | `songs` | `FS_AUTO_DJ_MODE` | `songs` / `albums`. |
| `autoDJ.songStrategy` | `similar` | `FS_AUTO_DJ_SONG_STRATEGY` | `similar` / `library_random`. |
| `autoDJ.timing` | `1` | `FS_AUTO_DJ_TIMING` | Timing value (number). |
---
+3 -3
View File
@@ -1,7 +1,7 @@
appId: org.jeffvli.feishin
productName: Feishin
artifactName: ${productName}-${version}-${os}-${arch}.${ext}
electronVersion: 41.7.0
electronVersion: 39.4.0
directories:
buildResources: assets
files:
@@ -47,7 +47,7 @@ mac:
gatekeeperAssess: false
notarize: false
extendInfo:
NSAudioCaptureUsageDescription: 'System audio access is required for mpv visualizer capture in Feishin'
NSAudioCaptureUsageDescription: "System audio access is required for mpv visualizer capture in Feishin"
NSLocalNetworkUsageDescription: 'Local network is necessary for accessing servers hosted on the same system as Feishin'
dmg:
@@ -63,7 +63,7 @@ linux:
artifactName: ${productName}-${os}-${arch}.${ext}
toolsets:
appimage: '1.0.3'
appimage: '1.0.2'
npmRebuild: false
+3 -3
View File
@@ -1,7 +1,7 @@
appId: org.jeffvli.feishin
productName: Feishin
artifactName: ${productName}-${version}-${os}-${arch}.${ext}
electronVersion: 41.7.0
electronVersion: 39.4.0
directories:
buildResources: assets
files:
@@ -47,7 +47,7 @@ mac:
gatekeeperAssess: false
notarize: false
extendInfo:
NSAudioCaptureUsageDescription: 'System audio access is required for mpv visualizer capture in Feishin'
NSAudioCaptureUsageDescription: "System audio access is required for mpv visualizer capture in Feishin"
NSLocalNetworkUsageDescription: 'Local network is necessary for accessing servers hosted on the same system as Feishin'
dmg:
@@ -63,7 +63,7 @@ linux:
artifactName: ${productName}-${os}-${arch}.${ext}
toolsets:
appimage: '1.0.3'
appimage: '1.0.2'
npmRebuild: false
publish:
+2 -2
View File
@@ -1,7 +1,7 @@
appId: org.jeffvli.feishin
productName: Feishin
artifactName: ${productName}-${version}-${os}-${arch}.${ext}
electronVersion: 41.7.0
electronVersion: 39.4.0
directories:
buildResources: assets
files:
@@ -63,7 +63,7 @@ linux:
artifactName: ${productName}-${os}-${arch}.${ext}
toolsets:
appimage: '1.0.3'
appimage: '1.0.2'
npmRebuild: false
afterAllArtifactBuild: scripts/after-all-artifact-build.mjs
+49 -43
View File
@@ -1,6 +1,6 @@
{
"name": "feishin",
"version": "1.13.0",
"version": "1.11.0",
"description": "A modern self-hosted music player.",
"keywords": [
"subsonic",
@@ -69,78 +69,79 @@
"postversion": "node ./scripts/update-app-stream.mjs"
},
"resolutions": {
"react-router": "7.14.0",
"xml2js": "0.5.0"
},
"dependencies": {
"@atlaskit/pragmatic-drag-and-drop": "1.7.7",
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^2.1.5",
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.2.0",
"@atlaskit/pragmatic-drag-and-drop-hitbox": "^1.1.0",
"@electron-toolkit/preload": "^3.0.2",
"@electron-toolkit/utils": "^4.0.0",
"@mantine/colors-generator": "^9.3.0",
"@mantine/core": "^9.3.0",
"@mantine/dates": "^9.3.0",
"@mantine/form": "^9.3.0",
"@mantine/hooks": "^9.3.0",
"@mantine/modals": "^9.3.0",
"@mantine/notifications": "^9.3.0",
"@radix-ui/react-context-menu": "^2.3.0",
"@tanstack/react-query": "5.96.2",
"@tanstack/react-query-devtools": "5.96.2",
"@tanstack/react-query-persist-client": "5.96.2",
"@mantine/colors-generator": "^9.1.1",
"@mantine/core": "^9.1.1",
"@mantine/dates": "^9.1.1",
"@mantine/form": "^9.1.1",
"@mantine/hooks": "^9.1.1",
"@mantine/modals": "^9.1.1",
"@mantine/notifications": "^9.1.1",
"@radix-ui/react-context-menu": "^2.2.16",
"@tanstack/react-query": "^5.96.2",
"@tanstack/react-query-devtools": "^5.96.2",
"@tanstack/react-query-persist-client": "^5.96.2",
"@ts-rest/core": "^3.52.1",
"@wavesurfer/react": "^1.0.12",
"@xhayper/discord-rpc": "^1.3.4",
"@xhayper/discord-rpc": "^1.3.3",
"audiomotion-analyzer": "^4.5.4",
"axios": "^1.17.0",
"axios": "^1.14.0",
"butterchurn": "3.0.0-beta.5",
"butterchurn-presets": "3.0.0-beta.4",
"cheerio": "^1.2.0",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"dayjs": "^1.11.21",
"dompurify": "^3.4.8",
"dayjs": "^1.11.20",
"dompurify": "^3.3.3",
"electron-debug": "^3.2.0",
"electron-localshortcut": "^3.2.1",
"electron-log": "^5.4.4",
"electron-log": "^5.4.3",
"electron-store": "^8.2.0",
"electron-updater": "^6.8.9",
"electron-updater": "^6.8.3",
"fast-average-color": "9.5.0",
"fast-xml-parser": "^5.8.0",
"fast-xml-parser": "^5.5.10",
"format-duration": "^3.0.2",
"fuse.js": "^7.4.2",
"fuse.js": "^7.2.0",
"i18next": "^25.10.10",
"icecast-metadata-stats": "^0.1.12",
"idb-keyval": "^6.2.5",
"idb-keyval": "^6.2.2",
"immer": "^10.2.0",
"is-electron": "^2.2.2",
"lodash": "^4.18.1",
"md5": "^2.3.0",
"motion": "^12.40.0",
"motion": "^12.38.0",
"mpris-service": "^2.1.2",
"nanoid": "^3.3.12",
"nanoid": "^3.3.11",
"node-mpv": "github:jeffvli/Node-MPV#32b4d64395289ad710c41d481d2707a7acfc228f",
"overlayscrollbars": "^2.16.0",
"overlayscrollbars": "^2.14.0",
"overlayscrollbars-react": "^0.5.6",
"qs": "^6.15.2",
"react": "^19.2.7",
"qs": "^6.15.0",
"react": "^19.2.4",
"react-call": "^1.8.2",
"react-dom": "^19.2.7",
"react-dom": "^19.2.4",
"react-error-boundary": "^5.0.0",
"react-i18next": "^16.6.6",
"react-icons": "^5.6.0",
"react-player": "^2.16.1",
"react-router": "^7.17.0",
"react-router": "^7.14.0",
"react-split-pane": "^3.2.0",
"react-virtualized-auto-sizer": "^1.0.26",
"react-window": "1.8.11",
"react-window-v2": "npm:react-window@^2.2.7",
"semver": "^7.8.2",
"semver": "^7.7.4",
"string-to-color": "^2.2.2",
"wavesurfer.js": "^7.12.7",
"ws": "^8.21.0",
"wavesurfer.js": "^7.12.5",
"ws": "^8.20.0",
"zod": "^3.25.76",
"zustand": "^5.0.14"
"zustand": "^5.0.12"
},
"devDependencies": {
"@electron-toolkit/eslint-config-prettier": "^3.0.0",
@@ -149,8 +150,8 @@
"@types/electron-localshortcut": "^3.1.3",
"@types/lodash": "^4.17.24",
"@types/md5": "^2.3.6",
"@types/node": "^24.13.1",
"@types/react": "^19.2.17",
"@types/node": "^24.12.2",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@types/react-window": "^1.8.8",
"@types/source-map-support": "^0.5.10",
@@ -159,32 +160,37 @@
"babel-plugin-react-compiler": "^1.0.0",
"concurrently": "^9.2.1",
"cross-env": "^10.1.0",
"electron": "^41.7.1",
"electron-builder": "^26.15.0",
"electron": "^39.8.6",
"electron-builder": "^26.8.2",
"electron-devtools-installer": "^4.0.0",
"electron-vite": "^4.0.1",
"eslint": "^9.39.4",
"eslint-plugin-perfectionist": "^4.15.1",
"eslint-plugin-prettier": "^5.5.6",
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.26",
"i18next-parser": "^9.4.0",
"postcss-preset-mantine": "^1.18.0",
"postcss-simple-vars": "^7.0.1",
"prettier": "^3.8.3",
"prettier": "^3.8.1",
"prettier-plugin-packagejson": "^2.5.22",
"stylelint": "^16.26.1",
"stylelint-config-css-modules": "^4.6.0",
"stylelint-config-recess-order": "^7.7.0",
"stylelint-config-standard": "^39.0.1",
"typescript": "^5.9.3",
"vite": "^7.3.5",
"vite": "^7.3.1",
"vite-plugin-conditional-import": "^0.1.7",
"vite-plugin-dynamic-import": "^1.6.0",
"vite-plugin-ejs": "^1.7.0",
"vite-plugin-pwa": "^1.3.0"
"vite-plugin-pwa": "^1.2.0"
},
"pnpm": {
"onlyBuiltDependencies": [
"electron",
"esbuild"
]
},
"packageManager": "pnpm@11.5.2",
"productName": "feishin"
}
+2399 -2185
View File
File diff suppressed because it is too large Load Diff
-9
View File
@@ -1,9 +0,0 @@
allowBuilds:
abstract-socket: true
electron: true
electron-winstaller: true
esbuild: true
minimumReleaseAge: 1440
overrides:
'xml2js': '0.5.0'
'react-router': '7.14.0'
-12
View File
@@ -38,13 +38,7 @@ window.FS_GENERAL_SHOW_RATINGS = "${FS_GENERAL_SHOW_RATINGS}";
window.FS_GENERAL_SHOW_VISUALIZER_IN_SIDEBAR = "${FS_GENERAL_SHOW_VISUALIZER_IN_SIDEBAR}";
window.FS_GENERAL_SIDEBAR_COLLAPSED_NAVIGATION = "${FS_GENERAL_SIDEBAR_COLLAPSED_NAVIGATION}";
window.FS_GENERAL_SIDEBAR_COLLAPSE_SHARED = "${FS_GENERAL_SIDEBAR_COLLAPSE_SHARED}";
window.FS_GENERAL_SIDEBAR_PLAYLIST_FOLDERS = "${FS_GENERAL_SIDEBAR_PLAYLIST_FOLDERS}";
window.FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_SEPARATOR = "${FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_SEPARATOR}";
window.FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_TREE_INDENT = "${FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_TREE_INDENT}";
window.FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_TREE_LINE_COLOR = "${FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_TREE_LINE_COLOR}";
window.FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_VIEW = "${FS_GENERAL_SIDEBAR_PLAYLIST_FOLDER_VIEW}";
window.FS_GENERAL_SIDEBAR_PLAYLIST_LIST = "${FS_GENERAL_SIDEBAR_PLAYLIST_LIST}";
window.FS_GENERAL_SIDEBAR_PLAYLIST_MODE = "${FS_GENERAL_SIDEBAR_PLAYLIST_MODE}";
window.FS_GENERAL_SIDEBAR_PLAYLIST_SORTING = "${FS_GENERAL_SIDEBAR_PLAYLIST_SORTING}";
window.FS_GENERAL_SIDE_QUEUE_TYPE = "${FS_GENERAL_SIDE_QUEUE_TYPE}";
window.FS_GENERAL_SIDE_QUEUE_LAYOUT = "${FS_GENERAL_SIDE_QUEUE_LAYOUT}";
@@ -64,9 +58,6 @@ window.FS_PLAYBACK_SCROBBLE_NOTIFY = "${FS_PLAYBACK_SCROBBLE_NOTIFY}";
window.FS_PLAYBACK_SCROBBLE_AT_DURATION = "${FS_PLAYBACK_SCROBBLE_AT_DURATION}";
window.FS_PLAYBACK_SCROBBLE_AT_PERCENTAGE = "${FS_PLAYBACK_SCROBBLE_AT_PERCENTAGE}";
window.FS_PLAYBACK_TRANSCODE_ENABLED = "${FS_PLAYBACK_TRANSCODE_ENABLED}";
window.FS_PLAYBACK_TRANSCODE_FORMAT = "${FS_PLAYBACK_TRANSCODE_FORMAT}";
window.FS_PLAYBACK_TRANSCODE_BITRATE = "${FS_PLAYBACK_TRANSCODE_BITRATE}";
window.FS_PLAYBACK_FILTERS = "${FS_PLAYBACK_FILTERS}";
window.FS_DISCORD_ENABLED = "${FS_DISCORD_ENABLED}";
window.FS_DISCORD_CLIENT_ID = "${FS_DISCORD_CLIENT_ID}";
@@ -88,11 +79,8 @@ window.FS_LYRICS_TRANSLATION_API_KEY = "${FS_LYRICS_TRANSLATION_API_KEY}";
window.FS_LYRICS_TRANSLATION_TARGET_LANGUAGE = "${FS_LYRICS_TRANSLATION_TARGET_LANGUAGE}";
window.FS_LYRICS_ALIGNMENT = "${FS_LYRICS_ALIGNMENT}";
window.FS_AUTO_DJ_ALBUM_STRATEGY = "${FS_AUTO_DJ_ALBUM_STRATEGY}";
window.FS_AUTO_DJ_ENABLED = "${FS_AUTO_DJ_ENABLED}";
window.FS_AUTO_DJ_ITEM_COUNT = "${FS_AUTO_DJ_ITEM_COUNT}";
window.FS_AUTO_DJ_MODE = "${FS_AUTO_DJ_MODE}";
window.FS_AUTO_DJ_SONG_STRATEGY = "${FS_AUTO_DJ_SONG_STRATEGY}";
window.FS_AUTO_DJ_TIMING = "${FS_AUTO_DJ_TIMING}";
window.FS_CSS_CONTENT = "${FS_CSS_CONTENT}";
-18
View File
@@ -8,7 +8,6 @@ import cs from './locales/cs.json';
import de from './locales/de.json';
import en from './locales/en.json';
import es from './locales/es.json';
import et from './locales/et.json';
import eu from './locales/eu.json';
import fa from './locales/fa.json';
import fi from './locales/fi.json';
@@ -28,8 +27,6 @@ import sl from './locales/sl.json';
import sr from './locales/sr.json';
import sv from './locales/sv.json';
import ta from './locales/ta.json';
import th from './locales/th.json';
import tl from './locales/tl.json';
import tr from './locales/tr.json';
import zhHans from './locales/zh-Hans.json';
import zhHant from './locales/zh-Hant.json';
@@ -41,7 +38,6 @@ const resources = {
de: { translation: de },
en: { translation: en },
es: { translation: es },
et: { translation: et },
eu: { translation: eu },
fa: { translation: fa },
fi: { translation: fi },
@@ -61,8 +57,6 @@ const resources = {
sr: { translation: sr },
sv: { translation: sv },
ta: { translation: ta },
th: { translation: th },
tl: { translation: tl },
tr: { translation: tr },
'zh-Hans': { translation: zhHans },
'zh-Hant': { translation: zhHant },
@@ -93,10 +87,6 @@ export const languages = [
label: 'Español',
value: 'es',
},
{
label: 'Eesti',
value: 'et',
},
{
label: 'Basque',
value: 'eu',
@@ -173,14 +163,6 @@ export const languages = [
label: 'Tamil',
value: 'ta',
},
{
label: 'Thai',
value: 'th',
},
{
label: 'Tagalog',
value: 'tl',
},
{
label: 'Türkçe',
value: 'tr',
+15 -349
View File
@@ -2,48 +2,32 @@
"action": {
"addToFavorites": "إضافة الى $t(entity.favorite, {\"count\": 2})",
"addToPlaylist": "إضافة الى $t(entity.playlist, {\"count\": 1})",
"clearQueue": "مسح قائمة التشغيل",
"clearQueue": "مسح قائمة الإنتظار",
"createPlaylist": "إنشاء $t(entity.playlist, {\"count\": 1})",
"deletePlaylist": "حذف $t(entity.playlist, {\"count\": 1})",
"deselectAll": "إلغاء تحديد الكل",
"editPlaylist": "تعديل $t(entity.playlist, {\"count\": 1})",
"goToPage": "اذهب الى الصفحة",
"moveToNext": "نقل إلى التالي",
"moveToBottom": "نقل إلى الأسفل",
"moveToTop": "نقل إلى الأعلى",
"goToPage": "اذهب الى صفحة",
"moveToNext": "الذهاب الى التالي",
"moveToBottom": "الذهاب الى الأسفل",
"moveToTop": "الذهاب الى الأعلى",
"refresh": "$t(common.refresh)",
"removeFromFavorites": "حذف من $t(entity.favorite, {\"count\": 2})",
"removeFromPlaylist": "حذف من $t(entity.playlist, {\"count\": 1})",
"removeFromQueue": "حذف من قائمة التشغيل",
"removeFromQueue": "حذف من قائمة الإنتظار",
"setRating": "تحديد التقييم",
"toggleSmartPlaylistEditor": "إظهار / إخفاء وضع التعديل لـ $t(entity.smartPlaylist)",
"viewPlaylists": "عرض $t(entity.playlist, {\"count\": 2})",
"toggleSmartPlaylistEditor": "تشغيل / إطفاء وضع التعديل لـ $t(entity.smartPlaylist)",
"viewPlaylists": "إظهار $t(entity.playlist, {\"count\": 2})",
"openIn": {
"lastfm": "فتح في Last.fm",
"musicbrainz": "فتح في MusicBrainz",
"listenbrainz": "فتح في ListenBrainz",
"qobuz": "فتح في Qobuz",
"spotify": "فتح في Spotify"
"musicbrainz": "فتح في MusicBrainz"
},
"addOrRemoveFromSelection": "إضافة أو إزالة من الإختيارات",
"selectRangeOfItems": "اختر مجموعة من العناصر",
"goToCurrent": "الانتقال إلى العنصر الحالي",
"createRadioStation": "إنشاء $t(entity.radioStation, {\"count\": 1})",
"createRadioStation": "يخلق $t(entity.radioStation, {\"count\": 1})",
"deleteRadioStation": "يمسح $t(entity.radioStation, {\"count\": 1})",
"selectAll": "تحديد الكل",
"shuffle": "لخبط",
"shuffleAll": "لخبط الكل",
"shuffleSelected": "لخبط المحدد",
"collapseAllFolders": "اطو جميع المجلدات",
"expandAllFolders": "بسط الملفات",
"downloadStarted": "بدأ تحميل {{count}} عنصر",
"moveUp": "نقل إلى فوق",
"moveDown": "نقل إلى تحت",
"holdToMoveToTop": "اضغط مطولاً للنقل إلى الأعلى",
"holdToMoveToBottom": "اضغط مطولاً للنقل إلى الأسفل",
"moveItems": "نقل العناصر",
"viewMore": "عرض المزيد",
"openApplicationDirectory": "فتح مجلد التطبيق"
"selectAll": "تحديد الكل"
},
"common": {
"action_zero": "عملية",
@@ -55,13 +39,13 @@
"add": "إضافة",
"additionalParticipants": "مشاركين إضافيين",
"newVersion": "تم تثبيت تحديث جديد {{version}}",
"viewReleaseNotes": "عرض ملاحظات الإصدار",
"viewReleaseNotes": "عرض معلومات الإصدار",
"albumGain": "مستوى صوت الألبوم",
"albumPeak": "اعلى مستوى للألبوم",
"areYouSure": "هل أنت متأكد؟",
"ascending": "تصاعدي",
"backward": "خلف",
"biography": "السيرة",
"biography": "سيرة",
"bitDepth": "عمق البت",
"bitrate": "معدل البت (البت ريت)",
"bpm": "نبضة في الدقيقة",
@@ -157,35 +141,7 @@
"unknown": "غير معروف",
"version": "الإصدار",
"year": "السنة",
"yes": "نعم",
"explicitStatus": "حالة المحتوى الصريح",
"countSelected": "{{count}} عنصر محدد",
"back": "للخلف",
"doNotShowAgain": "لا تظهر هذا مجدداً",
"view": "عرض",
"example": "مثال",
"externalLinks": "روابط الخارجية",
"openFolder": "فتح المجلد",
"faster": "أسرع",
"filter_single": "فردي",
"filter_multiple": "متعدد",
"grouping": "مجموعات",
"mood": "مزاج",
"numberOfResults": "{{numberOfResults}} نتيجة",
"noFilters": "لا توجد فلاتر معينة",
"private": "خاص",
"public": "عام",
"retry": "إعادة المحاولة",
"recordLabel": "شركة التسجيل",
"releaseType": "نوع الإصدار",
"rename": "إعادة تسمية",
"slower": "أبطأ",
"sort": "فرز",
"explicit": "صريح",
"clean": "نظيف",
"gridRows": "صفوف الشبكة",
"tableColumns": "أعمدة الجدول",
"newVersionAvailable": "هناك نسخة جديدة متاحة"
"yes": "نعم"
},
"entity": {
"album_zero": "الالبوم",
@@ -199,296 +155,6 @@
"albumArtist_two": "فنان الالبومين",
"albumArtist_few": "فنان الالبومات",
"albumArtist_many": "فنان الالبومات",
"albumArtist_other": "فنان الالبومات",
"albumArtistCount_zero": "{{count}} فنان الالبوم",
"albumArtistCount_one": "{{count}} فنان الالبوم",
"albumArtistCount_two": "{{count}} فنان الالبومين",
"albumArtistCount_few": "{{count}} فنان الالبومات",
"albumArtistCount_many": "{{count}} فنان الالبومات",
"albumArtistCount_other": "{{count}} فنان الالبومات",
"albumWithCount_zero": "{{count}} البوم",
"albumWithCount_one": "{{count}} البوم",
"albumWithCount_two": "{{count}} البومين",
"albumWithCount_few": "{{count}} البومات",
"albumWithCount_many": "{{count}} البومات",
"albumWithCount_other": "{{count}} البومات",
"radioStation_zero": "محطة راديو",
"radioStation_one": "محطة راديو",
"radioStation_two": "محطتان راديو",
"radioStation_few": "محطات راديو",
"radioStation_many": "محطات راديو",
"radioStation_other": "محطات راديو",
"radioStationWithCount_zero": "{{count}} محطة راديو",
"radioStationWithCount_one": "{{count}} محطة راديو",
"radioStationWithCount_two": "{{count}} محطتان راديو",
"radioStationWithCount_few": "{{count}} محطات راديو",
"radioStationWithCount_many": "{{count}} محطات راديو",
"radioStationWithCount_other": "{{count}} محطات راديو",
"artist_zero": "فنان",
"artist_one": "فنان",
"artist_two": "فنانان",
"artist_few": "فنانين",
"artist_many": "فنانين",
"artist_other": "فنانين",
"artistWithCount_zero": "{{count}} فنان",
"artistWithCount_one": "{{count}} فنان",
"artistWithCount_two": "{{count}} فنانان",
"artistWithCount_few": "{{count}} فنانين",
"artistWithCount_many": "{{count}} فنانين",
"artistWithCount_other": "{{count}} فنانين",
"favorite_zero": "مفضلة",
"favorite_one": "مفضلة",
"favorite_two": "مفضلتان",
"favorite_few": "مفضلات",
"favorite_many": "مفضلات",
"favorite_other": "مفضلات",
"folder_zero": "مجلد",
"folder_one": "مجلد",
"folder_two": "مجلدان",
"folder_few": "مجلدات",
"folder_many": "مجلدات",
"folder_other": "مجلدات",
"folderWithCount_zero": "{{count}} مجلد",
"folderWithCount_one": "{{count}} مجلد",
"folderWithCount_two": "{{count}} مجلدان",
"folderWithCount_few": "{{count}} مجلدات",
"folderWithCount_many": "{{count}} مجلدات",
"folderWithCount_other": "{{count}} مجلدات",
"genre_zero": "نوع",
"genre_one": "نوع",
"genre_two": "نوعان",
"genre_few": "أنواع",
"genre_many": "أنواع",
"genre_other": "أنواع",
"genreWithCount_zero": "{{count}} نوع",
"genreWithCount_one": "{{count}} نوع",
"genreWithCount_two": "{{count}} نوعان",
"genreWithCount_few": "{{count}} أنواع",
"genreWithCount_many": "{{count}} أنواع",
"genreWithCount_other": "{{count}} أنواع",
"playlist_zero": "قائمة تشغيل",
"playlist_one": "قائمة تشغيل",
"playlist_two": "قائمتان تشغيل",
"playlist_few": "قوائم تشغيل",
"playlist_many": "قوائم تشغيل",
"playlist_other": "قوائم تشغيل",
"play_zero": "{{count}} قائمة تشغيل",
"play_one": "{{count}} قائمة تشغيل",
"play_two": "{{count}} قائمتان تشغيل",
"play_few": "{{count}} قوائم تشغيل",
"play_many": "{{count}} قوائم تشغيل",
"play_other": "{{count}} قوائم تشغيل",
"playlistWithCount_zero": "{{count}} قائمة تشغيل",
"playlistWithCount_one": "{{count}} قائمة تشغيل",
"playlistWithCount_two": "{{count}} قائمتان تشغيل",
"playlistWithCount_few": "{{count}} قوائم تشغيل",
"playlistWithCount_many": "{{count}} قوائم تشغيل",
"playlistWithCount_other": "{{count}} قوائم تشغيل",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) قائمة تشغيل ذكية",
"track_zero": "مقطع",
"track_one": "مقطع",
"track_two": "مقطعان",
"track_few": "مقاطع",
"track_many": "مقاطع",
"track_other": "مقاطع",
"song_zero": "أغنية",
"song_one": "أغنية",
"song_two": "أغنيتان",
"song_few": "أغاني",
"song_many": "أغاني",
"song_other": "أغاني",
"trackWithCount_zero": "{{count}} مقطع",
"trackWithCount_one": "{{count}} مقطع",
"trackWithCount_two": "{{count}} مقطعان",
"trackWithCount_few": "{{count}} مقاطع",
"trackWithCount_many": "{{count}} مقاطع",
"trackWithCount_other": "{{count}} مقاطع"
},
"error": {
"apiRouteError": "تعذّر توجيه الطلب",
"audioDeviceFetchError": "حصل خطأ أثناء محاولة الحصول على أجهزة الصوت",
"authenticationFailed": "فشلت المصادقة",
"badAlbum": "أنت ترى هذة الصفحة لأن هذه الأغنية ليست جزءاً من ألبوم. على الأرجح تظهر لك هذه المشكلة إذا كان لديك أغنية في المستوى الأعلى من مجلد الموسيقى. يقوم Jellyfin بتجميع الأغاني فقط إذا كانت داخل مجلد",
"credentialsRequired": "يتطلب بيانات اعتماد",
"genericError": "حدث خطأ",
"loginRateError": "تجاوزت الحد لمحاولات الدخول. حاول مجدداً بعد بضع ثوان",
"mpvRequired": "يتطلب MPV",
"multipleServerSaveQueueError": "قائمة التشغيل تحتوي على أغنية أو أكثر من خادم مختلف. هذا غير مدعوم",
"networkError": "حصل خطأ في الشبكة",
"noNetwork": "الخادم غير متوفر",
"noNetworkDescription": "تعذر الإتصال بالخادم",
"notificationDenied": "تم رفض أذن الإشعارات. هذا الإعداد لن يكون له أي تأثير",
"openError": "تعذر فتح الملف",
"playbackError": "حدث خطأ أثناء محاولة تشغيل الوسائط",
"playbackPausedDueToError": "تم ايقاف التشغيل بسبب خطأ",
"remoteDisableError": "حدث خطأ أثناء محاولة $t(common.disable) الخادم البعيد",
"remoteEnableError": "حدث خطأ أثناء محاولة $t(common.enable) الخادم البعيد",
"remotePortError": "حدث خطأ أثناء محاولة تعيين الخادم البعيد",
"remotePortWarning": "أعد تشغيل الخادم لتطبيق المنفذ الجديد",
"saveQueueFailed": "فشل حفظ قائمة التشغيل",
"serverLockSingleServer": "فقط خادم واحد متاح إذا الخادم مقفل",
"serverNotSelectedError": "لم يتم اختيار أي خادم",
"serverRequired": "يتطلب خادم",
"sessionExpiredError": "انتهت صلاحية جلستك",
"systemFontError": "حدث خطأ أثناء محاولة الحصول على خطوط النظام",
"settingsSyncError": "تم اكتشاف تعارضات بين إعدادات العارض والعملية الرئيسية. أعد تشغيل التطبيق لتطبيق التغييرات",
"invalidJson": "JSON غير صالح",
"invalidServer": "خادم غير صالح",
"localFontAccessDenied": "تم رفض الوصول إلى الخطوط المحلية"
},
"filter": {
"album": "$t(entity.album, {\"count\": 1})",
"albumArtist": "$t(entity.albumArtist, {\"count\": 1})",
"matchAnd": "و",
"matchOr": "أو",
"biography": "السيرة",
"bitrate": "معدل البت (البت ريت)",
"bpm": "نبضة في الدقيقة",
"comment": "تعليق",
"communityRating": "تقييم المجتمع",
"criticRating": "تقييم الناقد",
"dateAdded": "تاريخ الإضافة",
"disc": "قرص",
"duration": "المدة",
"favorited": "مفضل",
"fromYear": "من سنة",
"id": "معرف",
"isFavorited": "مفضل",
"isPublic": "عام",
"isRated": "مقيم",
"isRecentlyPlayed": "تم التشغيل حديثاً",
"lastPlayed": "أخر تشغيل",
"mostPlayed": "أكثر تشغيل",
"name": "الأسم",
"note": "الملاحظة",
"path": "المسار",
"playCount": "عدد التشغيلات",
"random": "عشوائي",
"rating": "التقييم",
"recentlyAdded": "مضاف حديثاً",
"recentlyPlayed": "تم التشغيل حديثاً",
"recentlyUpdated": "محدث حديثاً",
"releaseDate": "تاريخ الإصدار",
"releaseYear": "سنة الإصدار",
"search": "بحث",
"songCount": "عدد الأغاني",
"sortName": "أسم الفرز",
"title": "العنوان",
"toYear": "إلى سنة",
"trackNumber": "مقطع",
"isCompilation": "تجميعة"
},
"datetime": {
"minuteShort": "د",
"secondShort": "ث",
"hourShort": "س",
"dayShort": "ي"
},
"filterOperator": {
"after": "بعد",
"afterDate": "بعد (تاريخ)",
"before": "قبل",
"beforeDate": "قبل (تاريخ)",
"contains": "يحتوي على",
"endsWith": "ينتهي بـ",
"inPlaylist": "في",
"inTheLast": "في أخِر",
"inTheRange": "في مدى",
"inTheRangeDate": "في مدى (تاريخ)",
"is": "في",
"isNot": "ليس في",
"isGreaterThan": "أكبر من",
"isLessThan": "أقل من",
"matchesRegex": "يطابق التعبير النمطي",
"notContains": "لا يحتوي على",
"notInPlaylist": "ليس في",
"notInTheLast": "ليس في أخِر",
"startsWith": "يبدأ بـ"
},
"form": {
"addServer": {
"error_savePassword": "حدث خطأ أثناء محاولة حفظ كلمة السر",
"input_legacyAuthentication": "تفعيل المصادقة القديمة",
"input_name": "أسم الخادم",
"input_password": "كلمة السر",
"input_preferRemoteUrl": "تفضيل رابط عام",
"input_remoteUrl": "رابط عام",
"input_savePassword": "حفظ كلمة السر",
"input_url": "الرابط",
"input_username": "أسم المستخدم",
"success": "تمت إضافة الخادم بنجاح",
"title": "إضافة خادم",
"input_preferInstantMix": "تفضيل الميكس الفوري",
"input_preferInstantMixDescription": "استخدم الميكس الفوري فقط للحصول على أغاني مشابهة. مفيد إذا كان لديك إضافات تعدّل هذا السلوك",
"input_remoteUrlPlaceholder": "اختياري: عنوان URL عام للميزات الخارجية"
},
"largeFetchConfirmation": {
"title": "أضف العناصر إلى قائمة التشغيل",
"description": "سيقوم هذا الإجراء بإضافة جميع العناصر في العرض المفلتر الحالي"
},
"addToPlaylist": {
"input_skipDuplicates": "تخطي العناصر المكررة",
"title": "أضف إلى $t(entity.playlist, {\"count\": 1})",
"create": "إنشاء $t(entity.playlist, {\"count\": 1}) {{playlist}}"
},
"createPlaylist": {
"input_public": "عام"
},
"createRadioStation": {
"input_homepageUrl": "رابط الرئيسية",
"input_name": "الأسم",
"input_streamUrl": "رابط البث",
"success": "تم إنشاء محطة راديو جديدة بنجاح",
"title": "إنشاء محطة راديو"
},
"editRadioStation": {
"success": "تم تحديث محطة الراديو بنجاح"
},
"deletePlaylist": {
"input_confirm": "أكتب أسم $t(entity.playlist, {\"count\": 1}) للتأكيد",
"success": "تم حذف $t(entity.playlist, {\"count\": 1}) بنجاح",
"title": "حذف $t(entity.playlist, {\"count\": 1})"
},
"editPlaylist": {
"success": "تم تحديث $t(entity.playlist, {\"count\": 1}) بنجاح",
"title": "تعديل $t(entity.playlist, {\"count\": 1})",
"publicJellyfinNote": "لسبب ما، لا يكشف Jellyfin عما إذا كانت قائمة التشغيل عامة أم لا. إذا كنت ترغب في إبقائها عامة، يرجى التأكد من تحديد الخيار التالي"
},
"lyricsExport": {
"export": "تصدير الكلمات",
"input_synced": "تصدير الكلمات المتزامنة"
},
"lyricSearch": {
"title": "البحث بالكلمات"
},
"queryEditor": {
"input_optionMatchAll": "تطابق الجميع",
"input_optionMatchAny": "تطابق أي",
"title": "محرر الاستعلامات",
"addRuleGroup": "إضافة مجموعة قواعد",
"removeRuleGroup": "إزالة مجموعة قواعد",
"resetToDefault": "استعادة الإعدادات الافتراضية"
},
"shareItem": {
"allowDownloading": "السماح بالتحميل",
"description": "الوصف"
},
"shuffleAll": {
"title": "تشغيل عشوائي",
"input_kind_albums": "ألبومات",
"input_kind_songs": "أغاني",
"input_kind": "إختيارات عشوائية",
"input_minYear": "من سنة",
"input_maxYear": "إلى سنة"
},
"updateServer": {
"success": "تم تحديث الخادم بنجاح",
"title": "تحديث الخادم"
}
},
"page": {
"albumArtistDetail": {
"favoriteSongs": "الأغاني المفضلة"
}
"albumArtist_other": "فنان الالبومات"
}
}
+37 -74
View File
@@ -1,7 +1,7 @@
{
"page": {
"sidebar": {
"myLibrary": "La meva mediateca",
"myLibrary": "La meva llibreria",
"albumArtists": "$t(entity.albumArtist, {\"count\": 2})",
"albums": "$t(entity.album, {\"count\": 2})",
"artists": "$t(entity.artist, {\"count\": 2})",
@@ -340,36 +340,34 @@
"filter_multiple": "Multi",
"rename": "Reanomena",
"newVersionAvailable": "Hi ha una nova versió disponible",
"numberOfResults": "{{numberOfResults}} resultats",
"back": "Enrere",
"openFolder": "Obre la carpeta"
"numberOfResults": "{{numberOfResults}} resultats"
},
"entity": {
"album_one": "Àlbum",
"album_many": "Àlbums",
"album_many": "àlbums",
"album_other": "Àlbums",
"albumWithCount_one": "{{count}} àlbum",
"albumWithCount_many": "{{count}} àlbums",
"albumWithCount_other": "{{count}} àlbums",
"albumArtist_one": "Artista de l'àlbum",
"albumArtist_many": "Artistes de l'àlbum",
"albumArtist_many": "artistes de l'àlbum",
"albumArtist_other": "Artistes de l'àlbum",
"albumArtistCount_one": "{{count}} artista de l'àlbum",
"albumArtistCount_many": "{{count}} artistes de l'àlbum",
"albumArtistCount_other": "{{count}} artistes de l'àlbum",
"artist_one": "Artista",
"artist_many": "Artistes",
"artist_many": "artistes",
"artist_other": "Artistes",
"artistWithCount_one": "{{count}} artista",
"artistWithCount_many": "{{count}} artistes",
"artistWithCount_other": "{{count}} artistes",
"playlist_one": "Llista de reproducció",
"playlist_many": "Llistes de reproducció",
"playlist_many": "llistes de reproducció",
"playlist_other": "Llistes de reproducció",
"playlistWithCount_one": "{{count}} llista de reproducció",
"playlistWithCount_many": "{{count}} llistes de reproducció",
"playlistWithCount_other": "{{count}} llistes de reproducció",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) Intel·ligent",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) intel·ligent",
"play_one": "{{count}} reproducció",
"play_many": "{{count}} reproduccions",
"play_other": "{{count}} reproduccions",
@@ -380,25 +378,25 @@
"genreWithCount_many": "{{count}} gèneres",
"genreWithCount_other": "{{count}} gèneres",
"track_one": "Pista",
"track_many": "Pistes",
"track_many": "pistes",
"track_other": "Pistes",
"trackWithCount_one": "{{count}} pista",
"trackWithCount_many": "{{count}} pistes",
"trackWithCount_other": "{{count}} pistes",
"folder_one": "Carpeta",
"folder_many": "Carpetes",
"folder_many": "carpetes",
"folder_other": "Carpetes",
"genre_one": "Gènere",
"genre_many": "Gèneres",
"genre_many": "gèneres",
"genre_other": "Gèneres",
"song_one": "Cançó",
"song_many": "Cançons",
"song_many": "cançons",
"song_other": "Cançons",
"favorite_one": "Preferit",
"favorite_many": "Preferits",
"favorite_many": "preferits",
"favorite_other": "Preferits",
"radioStation_one": "Emissora de ràdio",
"radioStation_many": "Emissores de ràdio",
"radioStation_many": "emissores de ràdio",
"radioStation_other": "Emissores de ràdio",
"radioStationWithCount_one": "{{count}} emissora de ràdio",
"radioStationWithCount_many": "{{count}} emissores de ràdio",
@@ -463,7 +461,7 @@
"expireInvalid": "La data d'expiració ha de ser al futur",
"createFailed": "No s'ha pogut crear el recurs compartit (està habilitat, l'ús compartit?)",
"copyToClipboard": "Copiar al porta-retalls: Ctrl+C, enter",
"successMustClick": "Compartició creada correctament. Feu clic aquí per obrir-la"
"successMustClick": "Compartició creada correctament. Feu clic aquí per obrir-la."
},
"updateServer": {
"success": "S'ha actualitzat el servidor amb èxit",
@@ -496,12 +494,7 @@
"input_played": "Reprodueix el filtre",
"input_played_optionAll": "Totes les pistes",
"input_played_optionUnplayed": "Només les pistes sense reproduir",
"input_played_optionPlayed": "Només les pistes reproduïdes",
"input_kind_albums": "Àlbums",
"input_kind_songs": "Cançons",
"input_kind": "Seleccions a l'atzar",
"input_limit_albums": "Quants àlbums?",
"input_limit_songs": "Quantes cançons?"
"input_played_optionPlayed": "Només les pistes reproduïdes"
},
"createRadioStation": {
"success": "Emissora de ràdio creada amb èxit",
@@ -564,9 +557,7 @@
"selectRangeOfItems": "Selecciona un interval d'elements",
"selectAll": "Selecciona-ho tot",
"openApplicationDirectory": "Obre el directori de l'aplicació",
"goToCurrent": "Anar a l'element actual",
"collapseAllFolders": "Replega totes les carpetes",
"expandAllFolders": "Expandeix totes les carpetes"
"goToCurrent": "Anar a l'element actual"
},
"setting": {
"language_description": "Estableix l'idioma de l'aplicació ($t(common.restartRequired))",
@@ -632,7 +623,7 @@
"customCssEnable_description": "Permet escriure CSS personalitzat",
"customCssNotice": "Atenció: tot i que hi ha un filtre (no es permet ni URL() ni content:), l'ús de CSS personalitzat pot presentar riscs si canvieu la interfície",
"customCss": "Css personalitzat",
"customCss_description": "Contingut del CSS personalitzat. Nota: la propietat \"content\" i els urls remots no es permeten. A sota hi teniu una previsualització. Els camps addicionals que no establiu hi apareixen a causa de la sanitització. Escriptori: Feishin llegeix i escriu custom.css al directori de configuració de l'aplicació i el recarrega quan el fitxer canvia",
"customCss_description": "Contingut del CSS personalitzat. Nota: la propietat \"content\" i els urls remots no es permeten. A sota hi teniu una previsualització. Els camps addicionals que no establiu hi apareixin pel filtre",
"customFontPath": "Ruta de font personalitzada",
"customFontPath_description": "Estableix la ruta a una font personalitzada per utilitzar-la a l'aplicació",
"discordApplicationId": "ID d'aplicació de {{discord}}",
@@ -813,7 +804,7 @@
"releaseChannel": "Canal de versions",
"releaseChannel_description": "Trieu entre versions estables i beta o alfa (diàries) per les actualitzacions automàtiques",
"mediaSession": "Activa media session",
"mediaSession_description": "Activa la integració amb Media Session per mostrar els controls multimèdia i les metadades a l'indicador de volum del sistema i la pantalla de bloqueig. Requereix el Reproductor Web d'Àudio.",
"mediaSession_description": "Activa la integració amb Media Session per mostrar els controls multimèdia i les metadades a l'indicador de volum del sistema i la pantalla de bloqueig",
"crossfadeStyle": "Estil de fosa encadenada",
"discordRichPresence": "Estat d'activitat de {{discord}}",
"enableAutoTranslation_description": "Activa la traducció automàtica en carregar la lletra",
@@ -833,8 +824,9 @@
"notify_description": "Mostra notificacions quan la cançó actual canviï",
"transcode": "Activa la transcodificació",
"autoDJ": "DJ automàtic",
"autoDJ_description": "Afegeix cançons similars a la cua automàticament",
"autoDJ_itemCount": "Número d'elements",
"autoDJ_itemCount_description": "El nombre d'elements que s'intenten afegir a la cua",
"autoDJ_itemCount_description": "El nombre d'elements que s'intenten afegir a la cua quan el DJ automàtic està activat",
"autoDJ_timing": "Temps",
"autoDJ_timing_description": "El nombre de cançons que han de quedar a la cua per activar el DJ automàtic",
"analyticsDisable": "Desactiva les analítiques basades en l'ús",
@@ -929,8 +921,8 @@
"primaryShade_description": "Substitueix el to primari (09) utilitzat per a botons, enllaços i altres elements de color primari",
"playerItemConfiguration_description": "Configurar quins elements es mostren i en quin ordre al reproductor de pantalla completa",
"playerItemConfiguration": "Configuració d'elements del jugador",
"listenbrainz_description": "Mostra enllaços a ListenBrainz a les pàgines d'artista/àlbum",
"listenbrainz": "Mostra enllaços a ListenBrainz",
"listenbrainz_description": "mostra enllaços a ListenBrainz a les pàgines d'artista/àlbum",
"listenbrainz": "mostra enllaços a ListenBrainz",
"qobuz_description": "Mostra enllaços a Qobuz a les pàgines d'artista/àlbum",
"qobuz": "Mostra enllaços a Qobuz",
"spotify_description": "Mostra enllaços a Spotify a les pàgines d'artista/àlbum",
@@ -938,52 +930,25 @@
"nativeSpotify_description": "Obre amb Spotify en lloc del vostre navegador",
"nativeSpotify": "Fes servir Spotify",
"playerbarWaveformStretch": "Extensió de la forma d'ona",
"playerbarWaveformStretch_description": "Estén la forma d'ona per omplir l'espai disponible",
"playerbarWaveformStretch_description": "estén la forma d'ona per omplir l'espai disponible",
"sidePlayQueueLayout": "Disposició de la cua de reproducció lateral",
"sidePlayQueueLayout_description": "Estableix la disposició de la cua de reproducció lateral adjunta",
"sidePlayQueueLayout_optionHorizontal": "Horitzontal",
"sidePlayQueueLayout_optionVertical": "Vertical",
"sidePlayQueueLayout_description": "estableix la disposició de la cua de reproducció lateral adjunta",
"sidePlayQueueLayout_optionHorizontal": "horitzontal",
"sidePlayQueueLayout_optionVertical": "vertical",
"waveformLoadingDelay": "Demora de càrrega de la forma d'ona",
"waveformLoadingDelay_description": "Demora en segons abans de carregar la forma d'ona. Incrementeu aquest valor si patiu interrupcions en fer servir el reproductor web.",
"preventSuspendOnPlayback_description": "Evita que l'aplicació quedi suspesa mentre es reprodueix música",
"preventSuspendOnPlayback": "Evita la suspensió durant la reproducció",
"hotkey_listShowPlayingSong": "Mostra la cançó en reproducció a la llista",
"sidebarPlaylistFolders_description": "Create una vista de carpeta per lliste de reproducció que inclogui el separador configurat al nom",
"sidebarPlaylistFolders": "Habilita les carpetes",
"sidebarPlaylistFolderSeparator_description": "Caràcter (o cadena) que separa els nivells de carpetes al nom d'una llista de reproducció",
"sidebarPlaylistFolderSeparator": "Separador de carpetes",
"sidebarPlaylistFolderView_description": "Com es mostren les carpetes a la barra lateral",
"sidebarPlaylistFolderView": "Vista de carpeta",
"sidebarPlaylistFolderView_optionSingle": "Carpeta única",
"sidebarPlaylistFolderView_optionTree": "Vista d'arbre",
"sidebarPlaylistFolderView_optionNavigation": "Vista de navegació",
"sidebarPlaylistFolderTreeIndent_description": "Píxels de sagnat per cada nivell de l'arbre",
"sidebarPlaylistFolderTreeIndent": "Sagnat de l'arbre",
"sidebarPlaylistFolderTreeLineColor_description": "Color de les línies connectores de l'arbre (deixeu-ho en blanc per fer servir el valor predeterminat)",
"sidebarPlaylistFolderTreeLineColor": "Color de la línia de l'arbre",
"sidebarPlaylistMode_description": "Com es mostra cada llista de reproducció a la llista de la barra lateral",
"sidebarPlaylistMode": "Mode de llista de reproducció a la barra lateral",
"sidebarPlaylistMode_optionCompact": "Compacte",
"sidebarPlaylistMode_optionExpanded": "Expandit",
"autoDJ_mode": "Mode",
"autoDJ_mode_albums": "Àlbums",
"autoDJ_mode_description": "Trieu si voleu afegir cançons o àlbums sencers a la cua",
"autoDJ_mode_songs": "Cançons",
"autoDJ_enabled": "Activa el DJ automàtic",
"autoDJ_albumStrategy": "Mode de selecció d'àlbum",
"autoDJ_songStrategy": "Mode de selecció de cançó",
"autoDJ_strategy_option_library_random": "A l'atzar",
"autoDJ_strategy_option_similar": "Similar"
"preventSuspendOnPlayback": "Evita la suspensió durant la reproducció"
},
"table": {
"column": {
"albumCount": "Àlbums",
"artist": "Artista",
"channels": "Canals",
"codec": "dec",
"genre": "Gènere",
"size": "Mida",
"songCount": "Pistes",
"albumCount": "$t(entity.album, {\"count\": 2})",
"artist": "$t(entity.artist, {\"count\": 1})",
"channels": "$t(common.channel, {\"count\": 2})",
"codec": "$t(common.codec)",
"genre": "$t(entity.genre, {\"count\": 1})",
"size": "$t(common.size)",
"songCount": "$t(entity.track, {\"count\": 2})",
"releaseYear": "Any",
"playCount": "Reproduccions",
"releaseDate": "Data de llançament",
@@ -1001,8 +966,8 @@
"path": "Ruta",
"rating": "Qualificació",
"title": "Títol",
"bitDepth": "Profunditat de bits",
"sampleRate": "Freqüència de mostreig",
"bitDepth": "$t(common.bitDepth)",
"sampleRate": "$t(common.sampleRate)",
"owner": "Propietari"
},
"config": {
@@ -1181,9 +1146,7 @@
"sleepTimer_timeRemaining": "Queden {{time}}",
"sleepTimer_setCustom": "Configura el temporitzador",
"sleepTimer_cancel": "Cancel·la el temporitzador",
"albumRadio": "Ràdio d'àlbums",
"scrobbleForceSubmit": "Força l'scrobble",
"sleepTimer_endOfAlbum": "Final de l'àlbum actual"
"albumRadio": "Ràdio d'àlbums"
},
"error": {
"credentialsRequired": "Credencials requerides",
+45 -82
View File
@@ -48,9 +48,7 @@
"sleepTimer_timeRemaining": "Zbývá {{time}}",
"sleepTimer_setCustom": "Nastavit časovač",
"sleepTimer_cancel": "Zrušit časovač",
"albumRadio": "Rádio alba",
"scrobbleForceSubmit": "Vynutit scrobble",
"sleepTimer_endOfAlbum": "Konec aktuálního alba"
"albumRadio": "Rádio alba"
},
"setting": {
"crossfadeStyle_description": "Vyberte způsob prolnutí u přehrávače zvuku",
@@ -235,7 +233,7 @@
"customCssEnable": "Povolit vlastní CSS",
"customCssEnable_description": "Umožnit psaní vlastního CSS",
"customCssNotice": "Varování: i když provádíme určitou sanitizaci (zakázáním URL() a content:), může používání CSS stále představovat riziko změnami rozhraní",
"customCss_description": "Vlastní CSS obsah. Upozornění: vlastnosti content a vzdálené URL jsou zakázané. Níže je zobrazen náhled vašeho obsahu. Další pole, která jste nenastavili, jsou přítomna z důvodu sanitizace. Počítačový Feishin čte a zapisuje soubor custom.css do konfiguračního adresáře aplikace a znovu jej načte po jeho změně",
"customCss_description": "Vlastní CSS obsah. Upozornění: vlastnosti content a vzdálené URL jsou zakázané. Níže je zobrazen náhled vašeho obsahu. Další pole, která jste nenastavili, jsou přítomna z důvodu sanitizace",
"customCss": "Vlastní css",
"webAudio": "Použít webový zvuk",
"webAudio_description": "Použít webový zvuk. Tím povolíte pokročilé funkce jako ReplayGain. Zakažte, pokud se objeví problémy",
@@ -248,7 +246,7 @@
"albumBackground_description": "Přidá obrázek alba na pozadí pro stránky alba obsahující obrázky alba",
"albumBackgroundBlur": "Velikost rozostření obrázku alba na pozadí",
"albumBackgroundBlur_description": "Upraví množství rozostření použité na obrázek alba na pozadí",
"playerbarOpenDrawer": "Lišta přehrávače jako přepínač celé obrazovky",
"playerbarOpenDrawer": "Lišta přehrávače jako přEPínač celé obrazovky",
"playerbarOpenDrawer_description": "Umožňuje kliknutí na lištu přehrávače pro otevření celoobrazovkového přehrávače",
"artistConfiguration": "Nastavení stránky umělce alba",
"artistConfiguration_description": "Nastavit, které položky na stránce umělce alba budou zobrazeny a v jakém pořadí",
@@ -297,7 +295,7 @@
"releaseChannel": "Kanál vydání",
"releaseChannel_description": "Vyberte si mezi stabilními, beta nebo alpha (nočními) vydáními pro automatické aktualizace",
"mediaSession": "Povolit relaci médií",
"mediaSession_description": "Povolí integraci do služby Media Session, což zobrazí ovládání a metadata médií v překrytí systémové hlasitosti a na zamykací obrazovce. Vyžaduje webový přehrávač zvuku.",
"mediaSession_description": "Povolí integraci do služby Media Session, což zobrazí ovládání a metadata médií v překrytí systémové hlasitosti a na zamykací obrazovce",
"exportImportSettings_control_description": "Exportovat a importovat nastavení pomocí souboru JSON",
"exportImportSettings_control_exportText": "Exportovat nastavení",
"exportImportSettings_control_importText": "Importovat nastavení",
@@ -345,8 +343,9 @@
"playerFilters_description": "Vynechat skladby z přidání do fronty na základě následujících kritérií",
"playerbarSlider_description": "Vlnová křivka není doporučena, pokud se nacházíte na pomalém nebo měřeném internetovém připojení",
"autoDJ": "Automatický DJ",
"autoDJ_description": "Automaticky přidávat podobné skladby do fronty",
"autoDJ_itemCount": "Počet položek",
"autoDJ_itemCount_description": "Počet položek, které se pokusíme přidat do fronty",
"autoDJ_itemCount_description": "Počet položek, které se pokusíme přidat do fronty po povolení automatického DJ",
"autoDJ_timing": "Časování",
"autoDJ_timing_description": "Počet skladeb zbývajících ve frontě před spuštěním automatického DJ",
"logLevel": "Úroveň protokolu",
@@ -430,34 +429,7 @@
"playerbarWaveformStretch": "Natáhnutí vlnové křivky",
"playerbarWaveformStretch_description": "Natáhně vlnovou křivku tak, aby vyplnila dostupný prostor",
"preventSuspendOnPlayback_description": "Zabránit aplikaci v uspání během přehrávání hudby",
"preventSuspendOnPlayback": "Zabránit uspání při přehrávání",
"hotkey_listShowPlayingSong": "Zobrazit přehrávanou skladbu v seznamu",
"sidebarPlaylistFolders_description": "Vytvořit zobrazení složky pro seznamy skadeb, které zahrnují nastavený oddělovač v názvu",
"sidebarPlaylistFolders": "Povolit složky",
"sidebarPlaylistFolderSeparator_description": "Znak (nebo řetězec) oddělující úrovně složek v názvu seznamu skladeb",
"sidebarPlaylistFolderSeparator": "Oddělovač složek",
"sidebarPlaylistFolderView_description": "Jak jsou složky zobrazeny v postranní liště",
"sidebarPlaylistFolderView": "Zobrazení složky",
"sidebarPlaylistFolderView_optionSingle": "Jedna složka",
"sidebarPlaylistFolderView_optionTree": "Stromové zobrazení",
"sidebarPlaylistFolderView_optionNavigation": "Zobrazení navigace",
"sidebarPlaylistFolderTreeIndent_description": "Počet pixelů, o který je odsazená každá větev",
"sidebarPlaylistFolderTreeIndent": "Odsazení větví",
"sidebarPlaylistFolderTreeLineColor_description": "Barva spojujících linek větví (ponechte prázdné pro výchozí)",
"sidebarPlaylistFolderTreeLineColor": "Barva linky stromu",
"sidebarPlaylistMode_description": "Jak je každý seznam skladeb zobrazen v seznamu v postranní liště",
"sidebarPlaylistMode": "Režim seznamů skladeb v postranní liště",
"sidebarPlaylistMode_optionCompact": "Kompaktní",
"sidebarPlaylistMode_optionExpanded": "Rozšířený",
"autoDJ_mode": "Režim",
"autoDJ_mode_albums": "Alba",
"autoDJ_mode_description": "Vyberte, zda do fronty přidávat skladby nebo celá alba",
"autoDJ_mode_songs": "Skladby",
"autoDJ_enabled": "Povolit automatického DJ",
"autoDJ_albumStrategy": "Režim výběru alb",
"autoDJ_songStrategy": "Režim výběru skladeb",
"autoDJ_strategy_option_library_random": "Náhodně",
"autoDJ_strategy_option_similar": "Podobné"
"preventSuspendOnPlayback": "Zabránit uspání při přehrávání"
},
"action": {
"editPlaylist": "Upravit $t(entity.playlist, {\"count\": 1})",
@@ -501,9 +473,7 @@
"addOrRemoveFromSelection": "Přidat nebo odebrat z výběru",
"selectRangeOfItems": "Vyberte rozsah položek",
"selectAll": "Vybrat vše",
"goToCurrent": "Přejít na aktuální položku",
"collapseAllFolders": "Sbalit všechny složky",
"expandAllFolders": "Rozbalit všechny složky"
"goToCurrent": "Přejít na aktuální položku"
},
"common": {
"backward": "Zpátky",
@@ -549,19 +519,19 @@
"cancel": "Zrušit",
"forceRestartRequired": "Restartujte pro použití změn… zavřete oznámení pro restartování",
"setting_one": "Nastavení",
"setting_few": "Nastavení",
"setting_few": "nastavení",
"setting_other": "Nastavení",
"version": "Verze",
"title": "Název",
"filter_one": "Filtr",
"filter_few": "Filtry",
"filter_few": "filtry",
"filter_other": "Filtrů",
"filters": "Filtry",
"create": "Vytvořit",
"bitrate": "Datový tok",
"saveAndReplace": "Uložit a nahradit",
"action_one": "Akce",
"action_few": "Akce",
"action_few": "akce",
"action_other": "Akcí",
"playerMustBePaused": "Přehrávač musí být pozastaven",
"confirm": "Potvrdit",
@@ -570,7 +540,7 @@
"comingSoon": "Již brzy…",
"reset": "Resetovat",
"channel_one": "Kanál",
"channel_few": "Kanály",
"channel_few": "kanály",
"channel_other": "Kanálů",
"disable": "Vypnout",
"sortOrder": "Pořadí",
@@ -632,9 +602,7 @@
"rename": "Přejmenovat",
"newVersionAvailable": "Je dostupná nová verze",
"numberOfResults": "{{numberOfResults}} výsledků",
"grouping": "Seskupování",
"back": "Zpět",
"openFolder": "Otevřít složku"
"grouping": "Seskupování"
},
"table": {
"config": {
@@ -721,7 +689,7 @@
"rating": "Hodnocení",
"favorite": "Oblíbené",
"playCount": "Přehrání",
"albumCount": "Alba",
"albumCount": "$t(entity.album, {\"count\": 2})",
"releaseYear": "Rok",
"lastPlayed": "Naposledy přehráno",
"biography": "Biografie",
@@ -729,20 +697,20 @@
"bitrate": "Datový tok",
"title": "Název",
"bpm": "BPM",
"dateAdded": "Datum přidání",
"artist": "Umělec",
"songCount": "Skladby",
"dateAdded": "Datum přIDání",
"artist": "$t(entity.artist, {\"count\": 1})",
"songCount": "$t(entity.track, {\"count\": 2})",
"trackNumber": "Skladba",
"genre": "Žánr",
"genre": "$t(entity.genre, {\"count\": 1})",
"albumArtist": "Umělec alba",
"path": "Cesta",
"discNumber": "Disk",
"channels": "Kanály",
"size": "Velikost",
"codec": "Kodek",
"channels": "$t(common.channel, {\"count\": 2})",
"size": "$t(common.size)",
"codec": "$t(common.codec)",
"owner": "Majitel",
"bitDepth": "Bitová hloubka",
"sampleRate": "Vzorkovací frekvence"
"bitDepth": "$t(common.bitDepth)",
"sampleRate": "$t(common.sampleRate)"
}
},
"error": {
@@ -794,10 +762,10 @@
"search": "Hledat",
"bitrate": "Datový tok",
"genre": "$t(entity.genre, {\"count\": 1})",
"recentlyAdded": "Nedávno přidáno",
"recentlyAdded": "Nedávno přIDáno",
"note": "Poznámka",
"name": "Název",
"dateAdded": "Datum přidání",
"dateAdded": "Datum přIDání",
"releaseDate": "Datum vydání",
"albumCount": "Počet $t(entity.album, {\"count\": 2})",
"communityRating": "Komunitní hodnocení",
@@ -919,7 +887,7 @@
},
"home": {
"mostPlayed": "Nejpřehrávanější",
"newlyAdded": "Nově přidáno",
"newlyAdded": "Nově přIDáno",
"title": "$t(common.home)",
"explore": "Procházet z vaší knihovny",
"recentlyPlayed": "Nedávno přehráno",
@@ -1062,7 +1030,7 @@
"input_password": "Heslo",
"input_legacyAuthentication": "Zapnout zastaralé ověřování",
"input_name": "Název serveru",
"success": "Server úspěšně přidán",
"success": "Server úspěšně přIDán",
"input_savePassword": "Uložit heslo",
"ignoreSsl": "Ignorovat ssl $t(common.restartRequired)",
"ignoreCors": "Ignorovat cors $t(common.restartRequired)",
@@ -1074,7 +1042,7 @@
"input_remoteUrlPlaceholder": "Volitelné: veřejná adresa url pro externí funkce"
},
"addToPlaylist": {
"success": "$t(entity.trackWithCount, {\"count\": {{message}} }) přidáno do $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })",
"success": "PřIDáno $t(entity.trackWithCount, {\"count\": {{message}} }) do $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })",
"title": "Přidat do $t(entity.playlist, {\"count\": 1})",
"input_skipDuplicates": "Přeskočit duplicity",
"input_playlists": "$t(entity.playlist, {\"count\": 2})",
@@ -1133,12 +1101,7 @@
"input_played": "Přehrát filtr",
"input_played_optionAll": "Všechny skladby",
"input_played_optionUnplayed": "Pouze nepřehrané skladby",
"input_played_optionPlayed": "Pouze přehrané skladby",
"input_kind_albums": "Alba",
"input_kind_songs": "Skladby",
"input_kind": "Náhodný výběr",
"input_limit_albums": "Kolik alb?",
"input_limit_songs": "Kolik skladeb?"
"input_played_optionPlayed": "Pouze přehrané skladby"
},
"saveQueue": {
"success": "Fronta přehrávání uložena na server"
@@ -1161,25 +1124,25 @@
},
"entity": {
"genre_one": "Žánr",
"genre_few": "Žánry",
"genre_few": "žánry",
"genre_other": "Žánry",
"playlistWithCount_one": "{{count}} playlist",
"playlistWithCount_few": "{{count}} playlisty",
"playlistWithCount_other": "{{count}} playlistů",
"playlist_one": "Playlist",
"playlist_few": "Playlisty",
"playlist_few": "playlisty",
"playlist_other": "Playlisty",
"artist_one": "Umělec",
"artist_few": "Umělci",
"artist_few": "umělci",
"artist_other": "Umělci",
"folderWithCount_one": "{{count}} složka",
"folderWithCount_few": "{{count}} složky",
"folderWithCount_other": "{{count}} složek",
"albumArtist_one": "Umělec alba",
"albumArtist_few": "Umělci alb",
"albumArtist_few": "umělci alb",
"albumArtist_other": "Umělci alb",
"track_one": "Skladba",
"track_few": "Skladby",
"track_few": "skladby",
"track_other": "Skladby",
"albumArtistCount_one": "{{count}} umělec alba",
"albumArtistCount_few": "{{count}} umělci alba",
@@ -1188,17 +1151,17 @@
"albumWithCount_few": "{{count}} alba",
"albumWithCount_other": "{{count}} alb",
"favorite_one": "Oblíbený",
"favorite_few": "Oblíbené",
"favorite_few": "oblíbené",
"favorite_other": "Oblíbené",
"artistWithCount_one": "{{count}} umělec",
"artistWithCount_few": "{{count}} umělci",
"artistWithCount_other": "{{count}} umělců",
"folder_one": "Složka",
"folder_few": "Složky",
"folder_few": "složky",
"folder_other": "Složky",
"smartPlaylist": "Chytrý $t(entity.playlist, {\"count\": 1})",
"album_one": "Album",
"album_few": "Alba",
"album_few": "alba",
"album_other": "Alba",
"genreWithCount_one": "{{count}} žánr",
"genreWithCount_few": "{{count}} žánry",
@@ -1209,11 +1172,11 @@
"play_one": "{{count}} přehrání",
"play_few": "{{count}} přehrání",
"play_other": "{{count}} přehrání",
"song_one": "Skladba",
"song_few": "Skladby",
"song_other": "Skladby",
"song_one": "Píseň",
"song_few": "písničky",
"song_other": "Písní",
"radioStation_one": "Stanice rádia",
"radioStation_few": "Stanice rádia",
"radioStation_few": "stanice rádia",
"radioStation_other": "Stanice rádia",
"radioStationWithCount_one": "{{count}} stanice rádia",
"radioStationWithCount_few": "{{count}} stanice rádia",
@@ -1273,10 +1236,10 @@
"startsWith": "Začíná na"
},
"datetime": {
"minuteShort": "min.",
"secondShort": "s",
"hourShort": "h.",
"dayShort": "d."
"minuteShort": "Min.",
"secondShort": "S",
"hourShort": "H.",
"dayShort": "D."
},
"visualizer": {
"visualizerType": "Typ vizualizéru",
+1
View File
@@ -695,6 +695,7 @@
},
"setting": {
"autoDJ": "Auto-DJ",
"autoDJ_description": "Tilføj automatisk lignende sange til køen",
"autoDJ_itemCount": "Antal elementer",
"autoDJ_itemCount_description": "Antallet af elementer der forsøges tilføjet til køen, når auto-DJ er aktiveret",
"autoDJ_timing": "Tidspunkt",
+51 -62
View File
@@ -13,8 +13,8 @@
"removeFromPlaylist": "Aus $t(entity.playlist, {\"count\": 1}) entfernen",
"viewPlaylists": "$t(entity.playlist, {\"count\": 2}) anzeigen",
"refresh": "$t(common.refresh)",
"removeFromQueue": "Aus Wiedergabeliste entfernen",
"setRating": "Bewertung setzen",
"removeFromQueue": "Aus wiedergabeliste entfernen",
"setRating": "Bewerten",
"toggleSmartPlaylistEditor": "Editor für $t(entity.smartPlaylist) ein-/ausblenden",
"removeFromFavorites": "Aus $t(entity.favorite, {\"count\": 2}) entfernen",
"openIn": {
@@ -41,14 +41,12 @@
"selectRangeOfItems": "Wählen sie eine reihe von elementen",
"holdToMoveToTop": "Halten um nach oben zu bewegen",
"holdToMoveToBottom": "Halten um nach unten zu bewegen",
"goToCurrent": "Zu aktuellem Eintrag wechseln",
"collapseAllFolders": "Alle Ordner einklappen",
"expandAllFolders": "Alle Ordner ausklappen"
"goToCurrent": "Zu aktuellem eintrag wechseln"
},
"common": {
"backward": "Zurück",
"increase": "Erhöhen",
"rating": "Bewertung",
"rating": "Wertung",
"bpm": "Bpm",
"refresh": "Aktualisieren",
"unknown": "Unbekannt",
@@ -167,10 +165,9 @@
"rename": "Umbenennen",
"filter_single": "Einzeln",
"filter_multiple": "Mehrfach",
"retry": "Erneut versuchen",
"retry": "Wiederholen",
"newVersionAvailable": "Eine neue version ist verfügbar",
"numberOfResults": "{{numberOfResults}} ergebnisse",
"openFolder": "Verzeichnis öffnen"
"numberOfResults": "{{numberOfResults}} ergebnisse"
},
"error": {
"remotePortWarning": "Starten Sie den Server neu, um den neuen Port anzuwenden",
@@ -180,7 +177,7 @@
"remotePortError": "Beim Versuch, den Remote-Server-Port festzulegen, ist ein Fehler aufgetreten",
"serverRequired": "Server benötigt",
"authenticationFailed": "Authentifizierung fehlgeschlagen",
"apiRouteError": "Anfrage kann nicht weitergeleitet werden",
"apiRouteError": "Anforderung kann nicht weitergeleitet werden",
"genericError": "Ein Fehler ist aufgetreten",
"credentialsRequired": "Anmeldeinformationen erforderlich",
"sessionExpiredError": "Deine Sitzung ist abgelaufen",
@@ -192,13 +189,13 @@
"audioDeviceFetchError": "Beim Versuch, Audiogeräte abzurufen, ist ein Fehler aufgetreten",
"invalidServer": "Ungültiger Server",
"loginRateError": "Zu viele Anmeldeversuche, bitte versuche es in einigen Sekunden erneut",
"badAlbum": "Sie sehen diese Seite, weil dieses Lied nicht Teil eines Albums ist. Dieses Problem tritt meist auf, wenn sich ein Lied im Überordner befindet. Jellyfin gruppiert Tracks nur, wenn diese sich innerhalb eines Verzeichnisses befinden",
"badAlbum": "Sie sehen diese Seite, weil dieses Lied nicht Teil eines Albums ist. Dieses Problem tritt meist auf, wenn sich ein Lied im Überordner befindet. Jellyfin gruppiert Tracks nur, wenn diese sich innerhalb eines Ordners befinden",
"networkError": "Ein Netzwerkfehler ist aufgetreten",
"openError": "Datei kann nicht geöffnet werden",
"badValue": "Ungültige option \"{{value}}\". Dieser Wert existiert nicht mehr",
"notificationDenied": "Berechtigungen über Benachrichtigungen wurden verweigert. Diese Einstellung hat keinen Effekt",
"saveQueueFailed": "Wiedergabeliste konnte nicht gespeichert werden",
"multipleServerSaveQueueError": "Die Wiedergabeliste enthält einen oder mehrere Titel, die nicht vom aktuellen Server stammen. Dies wird nicht unterstützt",
"multipleServerSaveQueueError": "Die Wiedergabeliste enthält einen oder mehrere Titel, die nicht vom aktuellen Server stammen. dies wird nicht unterstützt",
"noNetwork": "Server nicht verfügbar",
"noNetworkDescription": "Verbindung zum Server konnte nicht hergestellt werden",
"invalidJson": "JSON ungültig",
@@ -221,7 +218,7 @@
"recentlyAdded": "Kürzlich hinzugefügt",
"note": "Hinweis",
"name": "Name",
"dateAdded": "Hinzugefügt am",
"dateAdded": "Datum hinzugefügt",
"releaseDate": "Veröffentlichungsdatum",
"albumCount": "$t(entity.album, {\"count\": 2}) anzahl",
"communityRating": "Community-wertung",
@@ -251,8 +248,7 @@
"artist": "$t(entity.artist, {\"count\": 1})",
"explicitStatus": "$t(common.explicitStatus)",
"matchAnd": "Und",
"matchOr": "Oder",
"sortName": "Sortierungsname"
"matchOr": "Oder"
},
"form": {
"deletePlaylist": {
@@ -310,7 +306,7 @@
"editPlaylist": {
"title": "Bearbeite $t(entity.playlist, {\"count\": 1})",
"success": "$t(entity.playlist, {\"count\": 1}) erfolgreich aktualisiert",
"publicJellyfinNote": "Jellyfin legt aus irgendwelchen Gründen nicht offen, ob eine Wiedergabeliste öffentlich ist oder nicht. Wenn du möchtest, dass sie öffentlich bleibt, wähle bitte diese Option aus"
"publicJellyfinNote": "Jellyfin legt aus irgendwelchen Gründen nicht offen ob eine Wiedergabeliste öffentlich ist oder nicht. Wenn du möchtest, dass sie öffentlich bleibt, wähle bitte diese Option aus"
},
"lyricSearch": {
"title": "Songtext suche",
@@ -328,12 +324,12 @@
"successMustClick": "Freigabe erfolgreich erstellt. Hier klicken um diese zu öffnen"
},
"privateMode": {
"enabled": "Privater Modus aktiviert, Wiedergabe-Status wird externen Quellen nicht preisgegeben",
"disabled": "Privater Modus deaktiviert, Wiedergabe-Status wird externen Quellen preisgegeben",
"title": "Privater Modus"
"enabled": "Privatmodus aktiviert, Wiedergabe-Status wird externen Quellen nicht preisgegeben",
"disabled": "Privatmodus deaktiviert, Wiedergabe-Status wird externen Quellen preisgegeben",
"title": "Privatmodus"
},
"largeFetchConfirmation": {
"title": "Elemente der Wiedergabeliste hinzufügen",
"title": "Elemente der wiedergabeliste hinzufügen",
"description": "Diese Aktion fügt alle Elemente in der aktuell gefilterten Ansicht hinzu"
},
"shuffleAll": {
@@ -348,7 +344,7 @@
"input_played": "Wiedergabefilter"
},
"saveQueue": {
"success": "Wiedergabeliste auf Server gespeichert"
"success": "Wiedergabeliste auf server gespeichert"
},
"createRadioStation": {
"success": "Radiosender erfolgreich erstellt",
@@ -359,7 +355,7 @@
},
"lyricsExport": {
"input_offset": "$t(setting.lyricOffset)",
"export": "Liedtext exportieren",
"export": "Songtexte exportieren",
"input_synced": "Synchronisierte songtexte exportieren"
},
"editRadioStation": {
@@ -369,14 +365,14 @@
"entity": {
"genre_one": "Genre",
"genre_other": "Genres",
"playlistWithCount_one": "{{count}} Wiedergabeliste",
"playlistWithCount_other": "{{count}} Wiedergabelisten",
"playlistWithCount_one": "{{count}} wiedergabeliste",
"playlistWithCount_other": "{{count}} wiedergabelisten",
"playlist_one": "Wiedergabeliste",
"playlist_other": "Wiedergabelisten",
"artist_one": "Interpret",
"artist_other": "Interpreten",
"folderWithCount_one": "{{count}} Verzeichnis",
"folderWithCount_other": "{{count}} Verzeichnisse",
"folderWithCount_one": "{{count}} verzeichnis",
"folderWithCount_other": "{{count}} verzeichnisse",
"albumArtist_one": "Albuminterpret",
"albumArtist_other": "Albuminterpreten",
"track_one": "Track",
@@ -543,19 +539,19 @@
"selectServer": "Server auswählen",
"version": "Version {{version}}",
"manageServers": "Server verwalten",
"expandSidebar": "Seitenleiste ausklappen",
"expandSidebar": "Seitenleiste erweitern",
"collapseSidebar": "Seitenleiste einklappen",
"openBrowserDevtools": "Browser-entwicklungswerkzeuge öffnen",
"goBack": "Gehe zurück",
"goForward": "Gehe vorwärts",
"settings": "$t(common.setting, {\"count\": 2})",
"quit": "$t(common.quit)",
"privateModeOff": "Privaten Modus deaktivieren",
"privateModeOn": "Privaten Modus aktivieren",
"privateModeOff": "Privatmodus deaktivieren",
"privateModeOn": "Privatmodus aktivieren",
"commandPalette": "Kommandopalette öffnen",
"selectMusicFolder": "Musikverzeichnis wählen",
"noMusicFolder": "Kein Musikverzeichnis gewählt",
"multipleMusicFolders": "{{count}} Musikverzeichnis ausgewählt"
"selectMusicFolder": "Musikordner wählen",
"noMusicFolder": "Kein musikordner gewählt",
"multipleMusicFolders": "{{count}} musikordner ausgewählt"
},
"home": {
"mostPlayed": "Meistgespielt",
@@ -682,9 +678,9 @@
"topSongs": "Toplieder",
"relatedArtists": "Ähnliche $t(entity.artist, {\"count\": 2})",
"groupingTypeAll": "Alle veröffentlichungsformate",
"groupingTypePrimary": "Primäre Veröffentlichungsformate",
"favoriteSongs": "Lieblingslieder",
"favoriteSongsFrom": "Liebslingslieder von {{title}}",
"groupingTypePrimary": "Primäre veröffentlichungsformate",
"favoriteSongs": "Lieblingssongs",
"favoriteSongsFrom": "Liebslingssongs von {{title}}",
"topSongsCommunity": "Community",
"topSongsPersonal": "Persönlich"
},
@@ -715,7 +711,7 @@
},
"windowBar": {
"paused": "(Pausiert) ",
"privateMode": "(Privater Modus)"
"privateMode": "(Privater modus)"
},
"collections": {
"saveAsCollection": "Als sammlung speichern",
@@ -762,8 +758,8 @@
"addLastShuffled": "Als Letztes (zufällige Wiedergabe)",
"addNextShuffled": "Als Nächstes (zufällige Wiedergabe)",
"holdToShuffle": "Halten für zufallswiedergabe",
"restoreQueueFromServer": "Wiedergabeliste von Server wiederherstellen",
"saveQueueToServer": "Wiedergabeliste auf Server speichern",
"restoreQueueFromServer": "Wiedergabeliste von server wiederherstellen",
"saveQueueToServer": "Wiedergabeliste auf server speichern",
"lyrics": "Songtexte",
"artistRadio": "Künstler radio",
"sleepTimer_endOfSong": "Ende des aktuellen liedes",
@@ -901,13 +897,13 @@
"sidebarPlaylistSorting": "Wiedergabelisten-sortierung in der seitenleiste",
"minimizeToTray": "Zur taskleiste minimieren",
"skipPlaylistPage": "Wiedergabeliste-seite überspringen",
"themeDark": "Design (dunkel)",
"themeDark": "Erscheinungsbild (dunkel)",
"sidebarCollapsedNavigation": "Navigation in der seitenleiste (komprimiert)",
"gaplessAudio_optionWeak": "Schwach (empfohlen)",
"minimumScrobbleSeconds": "Minimum scrobble-dauer (sekunden)",
"hotkey_playbackStop": "Stoppen",
"savePlayQueue_description": "Speichert die Wiedergabeliste beim Schließen der Anwendung, und stellt diese wieder her, wenn die Anwendung geöffnet wird",
"useSystemTheme": "Nach Erscheinungsbild des Systems richten",
"useSystemTheme": "Nach erscheinungsbild des systems richten",
"enableRemote_description": "Aktiviert den Server für die Fernsteuerung, damit andere Geräte die Anwendung steuern können",
"fontType_optionSystem": "System schriftart",
"discordUpdateInterval": "{{discord}} rich presence aktualisierungsintervall",
@@ -923,7 +919,7 @@
"fontType": "Schriftartenquelle",
"followLyric": "Aktuellen songtext synchronisieren",
"font_description": "Wähle die Schriftart für die Anwendung",
"themeLight": "Design (hell)",
"themeLight": "Erscheinungsbild (hell)",
"sidePlayQueueStyle_optionDetached": "Lösgelöst",
"windowBarStyle_description": "Legt das Erscheinungsbild des Fensterrahmens fest",
"hotkey_toggleCurrentSongFavorite": "$t(common.currentSong) zu favoriten hinzufügen",
@@ -951,7 +947,7 @@
"albumBackgroundBlur_description": "Passt die Stärke der Unschärfe an, welche auf das Hintergrundbild des Albums angewandt wird",
"clearCacheSuccess": "Cache erfolgreich geleert",
"contextMenu": "Kontextmenü-einstellungen (rechtsklick)",
"customCssEnable_description": "Erlaubt das Hinzufügen von benutzerdefiniertem CSS",
"customCssEnable_description": "Erlaubt das hinzufügen von benutzerdefiniertem CSS",
"artistBackground": "Künstler hintergrundbild",
"artistBackground_description": "Fügt ein Hintergrundbild für die Künstlerseite hinzu",
"artistConfiguration": "Künstler albumseite konfiguration",
@@ -979,10 +975,11 @@
"logLevel_optionError": "Fehler",
"logLevel_optionInfo": "Info",
"logLevel_optionWarn": "Warnung",
"autoDJ_description": "Füge automatisch ähnliche Lieder der Wiedergabeliste hinzu",
"autoDJ": "Auto DJ",
"autoDJ_itemCount": "Anzahl",
"autoDJ_itemCount_description": "Die Anzahl der Lieder, die zur Wiedergabeliste hinzugefügt werden soll",
"autoDJ_timing_description": "Die Anzahl der Lieder, die sich noch in der Wiedergabeliste befinden, bevor Auto-DJ ausgelöst wird",
"autoDJ_itemCount_description": "Die anzahl der lieder, die bei aktiviertem auto DJ zur wiedergabeliste hinzugefügt werden sollen",
"autoDJ_timing_description": "Die anzahl der lieder, die sich noch in der wiedergabeliste befinden, bevor auto DJ ausgelöst wird",
"autoDJ_timing": "Timing",
"discordDisplayType": "{{discord}} presence darstellungsart",
"discordLinkType_mbz_lastfm": "{{musicbrainz}} mit {{lastfm}} als ersatz",
@@ -1023,7 +1020,7 @@
"artistConfiguration_description": "Legt fest, welche Elemente auf der Albumkünstlerseite angezeigt werden und in welcher Reihenfolge",
"contextMenu_description": "Legt die Einträge fest, die im Rechtsklick-Menü angezeigt werden sollen. Abgewählte Einträge werden ausgeblendet",
"crossfadeStyle": "Art der überblende",
"customCss_description": "Benutzerdefinierter CSS-Inhalt. Hinweis: Content und Remote URLs sind nicht zulässige Eigenschaften. Eine Vorschau deines Inhalts wird unten angezeigt. Aufgrund von Bereinigung werden womöglich zusätzliche, nicht von dir definierte Felder angezeigt. Desktop: Feishin liest und schreibt in eine custom.css Datei im App-Konfigurationsverzeichnis, und lädt diese neu, wenn sich die Datei ändert.",
"customCss_description": "Benutzerdefinierter CSS-inhalt. Hinweis: inhalte und remote-urls sind nicht zulässige eigenschaften. Unten siehst du eine vorschau deines inhalts. Aufgrund von bereinigung werden womöglich zusätzliche, nicht von dir definierte felder angezeigt",
"customCssNotice": "Warnung: obwohl eine gewisse bereinigung erfolgt (nicht zulässig sind z. B. \"URL()\" und \"content:\"), kann ein benutzerdefiniertes CSS risiken mit sich bringen, da die benutzeroberfläche dadurch verändert wird",
"releaseChannel_optionBeta": "Beta",
"releaseChannel_optionLatest": "Stabil",
@@ -1065,7 +1062,7 @@
"automaticUpdates": "Automatische updates",
"automaticUpdates_description": "Updates automatisch suchen und installieren",
"releaseChannel_optionAlpha": "Alpha (nightly)",
"useThemeAccentColor": "Standard Akzentfarbe übernehmen",
"useThemeAccentColor": "Akzentfarbe des themas nutzen",
"analyticsEnable_description": "Anonymisierte Nutzungsdaten werden an den Entwickler gesendet, um die Anwendung zu verbessern",
"artistReleaseTypeConfiguration_description": "Konfigurieren, welche Release-Typen und in welcher Reihenfolge diese auf der Album-Künstlerseite angezeigt werden",
"homeConfiguration_description": "Konfigurieren, welche Elemente und in welcher Reihenfolge diese auf der Startseite angezeigt werden",
@@ -1112,14 +1109,14 @@
"queryBuilder": "Abfrage-editor",
"queryBuilderCustomFields_inputLabel": "Label",
"queryBuilderCustomFields_description": "Füge benutzerdefinierte Felder für den Abfrage-Editor hinzu",
"autosave": "Automatisch aktuelle Wiedergabeliste speichern",
"autosave_description": "Aktiviere die automatische Speicherung der aktuellen Wiedergabe auf dem Server. Diese Funktion ist nur bei Navidrome/Subsonic Servern verfügbar und es darf sich nicht um eine gemischte Wiedergabeliste handeln.",
"autosaveCount": "Häufigkeit der automatischen Speicherung bei Wiedergabelisten",
"autosave": "Automatisch aktuelle wiedergabeliste speichern",
"autosave_description": "Aktiviere die automatische speicherung der aktuellen wiedergabe auf dem server. Diese funktion ist nur bei Navidrome/Subsonic servern verfügbar und es darf sich nicht um eine gemischte wiedergabeliste handeln.",
"autosaveCount": "Häufigkeit der automatischen speicherung bei wiedergabelisten",
"autosaveCount_description": "Wieviele Lieder gespielt werden, bevor die Wiedergabeliste gespeichert wird. 1 (Minimum) bedeutet die Speicherung nach jedem gespielten Lied",
"useThemeAccentColor_description": "Verwendet die primäre Farbe des gewählten Designs",
"useThemePrimaryShade": "Standard Farbton übernehmen",
"useThemePrimaryShade_description": "Verwendet den primären Farbton des ausgewählten Designs für die Primärfarbvarianten",
"primaryShade": "Primärer Farbton",
"useThemeAccentColor_description": "Verwendet die Primärfarbe des gewählten Themas anstatt einer ausgewählten Akzentfarbe",
"useThemePrimaryShade": "Primärschatten des themas nutzen",
"useThemePrimaryShade_description": "Verwendet den Primärschatten des ausgewählten Themas als primäre Farbvarianten",
"primaryShade": "Primärschatten",
"listenbrainz": "ListenBrainz Links anzeigen",
"listenbrainz_description": "Zeige Links zu ListenBrainz auf den Interpreten/Alben Seiten",
"mpvExtraParameters": "Zusätzliche mpv parameter",
@@ -1134,15 +1131,7 @@
"nativeSpotify_description": "In der Spotify app statt im browser öffnen",
"imageResolution_optionFullScreenPlayer": "Wiedergabe im vollbildmodus",
"sidePlayQueueLayout_optionHorizontal": "Horizontal",
"sidePlayQueueLayout_optionVertical": "Vertikal",
"sidebarPlaylistFolders": "Verzeichnisse aktivieren",
"sidebarPlaylistFolderSeparator": "Verzeichnistrennzeichen",
"sidebarPlaylistFolderView_description": "Wie Verzeichnisse in der Seitenleiste angezeigt werden",
"sidebarPlaylistFolderView": "Verzeichnisansicht",
"sidebarPlaylistFolderView_optionSingle": "Einzelne Ordner",
"sidebarPlaylistFolderView_optionTree": "Baumstruktur",
"sidebarPlaylistFolderView_optionNavigation": "Navigationsansicht",
"sidebarPlaylistFolderSeparator_description": "Zeichen (oder Zeichenfolge), das die Verzeichnisebenen im Wiedergabelistentitel trennt"
"sidePlayQueueLayout_optionVertical": "Vertikal"
},
"dragDropZone": {
"error_oneFileOnly": "Bitte wähle nur 1 Datei",
+9 -46
View File
@@ -6,8 +6,6 @@
"selectRangeOfItems": "Select a range of items",
"clearQueue": "Clear queue",
"goToCurrent": "Go to current item",
"collapseAllFolders": "Collapse all folders",
"expandAllFolders": "Expand all folders",
"createPlaylist": "Create $t(entity.playlist, {\"count\": 1})",
"createRadioStation": "Create $t(entity.radioStation, {\"count\": 1})",
"deletePlaylist": "Delete $t(entity.playlist, {\"count\": 1})",
@@ -58,7 +56,6 @@
"albumPeak": "Album peak",
"areYouSure": "Are you sure?",
"ascending": "Ascending",
"back": "Back",
"backward": "Backward",
"biography": "Biography",
"bitDepth": "Bit depth",
@@ -92,7 +89,6 @@
"expand": "Expand",
"example": "Example",
"externalLinks": "External links",
"openFolder": "Open folder",
"faster": "Faster",
"favorite": "Favorite",
"filter_one": "Filter",
@@ -175,14 +171,14 @@
"entity": {
"album_one": "Album",
"album_other": "Albums",
"albumArtist_one": "Album Artist",
"albumArtist_other": "Album Artists",
"albumArtist_one": "Album artist",
"albumArtist_other": "Album artists",
"albumArtistCount_one": "{{count}} album artist",
"albumArtistCount_other": "{{count}} album artists",
"albumWithCount_one": "{{count}} album",
"albumWithCount_other": "{{count}} albums",
"radioStation_one": "Radio Station",
"radioStation_other": "Radio Stations",
"radioStation_one": "Radio station",
"radioStation_other": "Radio stations",
"radioStationWithCount_one": "{{count}} radio station",
"radioStationWithCount_other": "{{count}} radio stations",
"artist_one": "Artist",
@@ -416,11 +412,6 @@
},
"shuffleAll": {
"title": "Play random",
"input_kind_albums": "Albums",
"input_kind_songs": "Songs",
"input_kind": "Random picks",
"input_limit_albums": "How many albums?",
"input_limit_songs": "How many songs?",
"input_genre": "$t(entity.genre, {\"count\": 1})",
"input_limit": "How many songs?",
"input_minYear": "From year",
@@ -699,15 +690,13 @@
"viewQueue": "View queue",
"sleepTimer": "Sleep timer",
"sleepTimer_endOfSong": "End of current song",
"sleepTimer_endOfAlbum": "End of current album",
"sleepTimer_minutes": "{{count}} min",
"sleepTimer_hours": "{{count}} hr",
"sleepTimer_custom": "Custom",
"sleepTimer_off": "Off",
"sleepTimer_timeRemaining": "{{time}} remaining",
"sleepTimer_setCustom": "Set timer",
"sleepTimer_cancel": "Cancel timer",
"scrobbleForceSubmit": "Force scrobble"
"sleepTimer_cancel": "Cancel timer"
},
"queryBuilder": {
"standardTags": "Standard tags",
@@ -738,19 +727,11 @@
},
"setting": {
"autoDJ": "Auto DJ",
"autoDJ_description": "Automatically add similar songs to the queue",
"autoDJ_itemCount": "Item count",
"autoDJ_itemCount_description": "The number of items attempted to be added to the queue",
"autoDJ_itemCount_description": "The number of items attempted to be added to the queue when auto DJ is enabled",
"autoDJ_timing": "Timing",
"autoDJ_timing_description": "The number of songs remaining in the queue before auto DJ is triggered",
"autoDJ_mode": "Mode",
"autoDJ_mode_albums": "Albums",
"autoDJ_mode_description": "Choose to add either songs or entire albums to the queue",
"autoDJ_mode_songs": "Songs",
"autoDJ_enabled": "Enable Auto DJ",
"autoDJ_albumStrategy": "Album selection mode",
"autoDJ_songStrategy": "Song selection mode",
"autoDJ_strategy_option_library_random": "Random",
"autoDJ_strategy_option_similar": "Similar",
"autosave": "Automatically save play queue",
"autosave_description": "Enable automatically saving the play queue to your server. This is only possible when using Navidrome/Subsonic, and you cannot have a mixed play queue.",
"autosaveCount": "Automatic play queue save frequency",
@@ -800,7 +781,7 @@
"crossfadeDuration": "Crossfade duration",
"crossfadeStyle": "Crossfade style",
"crossfadeStyle_description": "Select the crossfade style to use for the audio player",
"customCss_description": "Custom CSS content. Note: content and remote urls are disallowed properties. A preview of your content is shown below. Additional fields you didn't set are present due to sanitization. Desktop: feishin reads and writes custom.css in the app config directory and reloads it when the file changes",
"customCss_description": "Custom CSS content. Note: content and remote urls are disallowed properties. A preview of your content is shown below. Additional fields you didn't set are present due to sanitization",
"customCss": "Custom CSS",
"customCssEnable_description": "Allow for writing custom CSS",
"customCssEnable": "Enable custom CSS",
@@ -892,7 +873,6 @@
"hotkey_listPlayLast": "List play last",
"hotkey_listPlayNext": "List play next",
"hotkey_listPlayNow": "List play now",
"hotkey_listShowPlayingSong": "Show playing song in list",
"hotkey_navigateHome": "Navigate to home",
"hotkey_playbackNext": "Next track",
"hotkey_playbackPause": "Pause",
@@ -1063,25 +1043,8 @@
"sidebarConfiguration": "Sidebar configuration",
"playerItemConfiguration_description": "Configure what items are shown, and in what order, on the fullscreen player",
"playerItemConfiguration": "Player item configuration",
"sidebarPlaylistFolders_description": "Create a folder view for playlists that include the configured separator in the name",
"sidebarPlaylistFolders": "Enable folders",
"sidebarPlaylistFolderSeparator_description": "Character (or string) that separates folder levels in a playlist name",
"sidebarPlaylistFolderSeparator": "Folder separator",
"sidebarPlaylistFolderView_description": "How folders are displayed in the sidebar",
"sidebarPlaylistFolderView": "Folder view",
"sidebarPlaylistFolderView_optionSingle": "Single folder",
"sidebarPlaylistFolderView_optionTree": "Tree view",
"sidebarPlaylistFolderView_optionNavigation": "Navigation view",
"sidebarPlaylistFolderTreeIndent_description": "Pixels each tree level is indented",
"sidebarPlaylistFolderTreeIndent": "Tree indent",
"sidebarPlaylistFolderTreeLineColor_description": "Color of the connecting tree lines (leave empty for theme default)",
"sidebarPlaylistFolderTreeLineColor": "Tree line color",
"sidebarPlaylistList_description": "Show or hide the playlist list in the sidebar",
"sidebarPlaylistList": "Sidebar playlist list",
"sidebarPlaylistMode_description": "How each playlist is displayed in the sidebar list",
"sidebarPlaylistMode": "Sidebar playlist mode",
"sidebarPlaylistMode_optionCompact": "Compact",
"sidebarPlaylistMode_optionExpanded": "Expanded",
"sidebarPlaylistSorting_description": "Allows manual playlist sorting in the sidebar using drag and drop instead of the default server order",
"sidebarPlaylistSorting": "Sidebar playlist sorting",
"sidebarPlaylistListFilterRegex_description": "Hide playlists in the sidebar that match this regular expression",
@@ -1094,7 +1057,7 @@
"sidePlayQueueLayout_description": "Sets the layout of the attached side play queue",
"sidePlayQueueLayout_optionHorizontal": "Horizontal",
"sidePlayQueueLayout_optionVertical": "Vertical",
"mediaSession_description": "Enables media session integration, displaying media controls and metadata in the system volume overlay and lock screen. Requires the Web Audio Player.",
"mediaSession_description": "Enables media session integration, displaying media controls and metadata in the system volume overlay and lock screen",
"mediaSession": "Enable media session",
"sidePlayQueueStyle": "Side play queue style",
"skipDuration_description": "Sets the duration to skip when using the skip buttons on the player bar",
+46 -83
View File
@@ -48,9 +48,7 @@
"sleepTimer_off": "Apagado",
"sleepTimer_endOfSong": "Fin de la canción actual",
"sleepTimer": "Temporizador de apagado",
"albumRadio": "Radio del álbum",
"scrobbleForceSubmit": "Forzar scrobble",
"sleepTimer_endOfAlbum": "Fin del álbum actual"
"albumRadio": "Radio del álbum"
},
"setting": {
"crossfadeStyle_description": "Selecciona el estilo de crossfade a usar por el reproductor de audio",
@@ -236,7 +234,7 @@
"customCssEnable_description": "Permite escribir CSS personalizado",
"customCss": "CSS personalizado",
"customCssNotice": "Aviso: mientras hay alguna sanitización (rechazar URL() y content:), usar CSS personalizado puede aún entrañar riesgos cambiando la interfaz",
"customCss_description": "Content CSS personalizado. Nota: content y remote urls son propiedades rechazadas. Una vista previa de tu content se muestra debajo. Las entradas adicionales que no estableciste están presentes debido a la sanitización. Escritorio: Feishin lee y escribe custom.css en el directorio de configuración de la aplicación y lo recarga cuando cambia el archivo",
"customCss_description": "Content CSS personalizado. Nota: content y urls remotas son propiedades rechazadas. Una vista previa de tu content se muestra debajo. Las entradas adicionales que no estableciste están presentes debido a la sanitización",
"webAudio": "Usar audio web",
"webAudio_description": "Utilizar audio web. Esto habilita funciones avanzadas como ReplayGain. Desactiva esta opción si tienes problemas",
"transcode_description": "Permite la transcodificación a distintos formatos",
@@ -265,7 +263,7 @@
"lastfmApiKey": "Clave API para {{lastfm}}",
"discordServeImage": "Servir imágenes de {{discord}} desde el servidor",
"discordServeImage_description": "Comparte el arte de la portada para el estado de actividad de {{discord}} desde el propio servidor, solo disponible para Jellyfin y Navidrome. {{discord}} usa un bot para obtener las imágenes, por lo que tu servidor debe ser alcanzable desde el internet público",
"lastfm": "Mostrar enlaces de Last.fm",
"lastfm": "Mostrar enlaces de last.fm",
"lastfm_description": "Muestra enlaces a Last.fm en las páginas de artistas/álbumes",
"musicbrainz": "Mostrar enlaces de MusicBrainz",
"musicbrainz_description": "Muestra enlaces a MusicBrainz en las páginas de artistas/álbumes, donde exista MusicBrainz ID",
@@ -297,7 +295,7 @@
"releaseChannel_description": "Elige entre lanzamientos estables, beta, o alpha (nightly) para las actualizaciones automáticas",
"artistBackground_description": "Añade una imagen de fondo para las páginas de artistas que contienen el arte de los artistas",
"mediaSession": "Activar sesión de medios",
"mediaSession_description": "Activa la integración de la sesión de medios, mostrando los controles de medios y los metadatos en la superposición del volumen del sistema y en la pantalla de bloqueo. Requiere el Reproductor Web de Audio.",
"mediaSession_description": "Activa la integración de la sesión de medios, mostrando los controles de medios y los metadatos en la superposición del volumen del sistema y en la pantalla de bloqueo",
"exportImportSettings_control_description": "Exporta e importa la configuración a través de JSON",
"exportImportSettings_control_exportText": "Exportar configuración",
"exportImportSettings_control_importText": "Importar configuración",
@@ -344,9 +342,10 @@
"playerFilters": "Filtrar las canciones de la cola",
"playerFilters_description": "Omite la adición de canciones a la cola basado en los siguientes criterios",
"playerbarSlider_description": "La forma de onda no es recomendable en una conexión a Internet lenta o medida",
"autoDJ": "DJ Automático",
"autoDJ": "DJ automático",
"autoDJ_description": "Añade canciones similares a las de la cola automáticamente",
"autoDJ_itemCount": "Recuento de elementos",
"autoDJ_itemCount_description": "El número de elementos que se ha intentado añadir a la cola",
"autoDJ_itemCount_description": "El número de elementos que se ha intentado añadir a la cola cuando DJ automático está activado",
"autoDJ_timing_description": "El número de canciones restantes en la cola antes de que DJ automático se dispare",
"autoDJ_timing": "Tiempo",
"logLevel": "Nivel de registro",
@@ -430,34 +429,7 @@
"playerbarWaveformStretch": "Estiramiento de la forma de onda",
"playerbarWaveformStretch_description": "Estira la forma de onda para rellenar el espacio disponible",
"preventSuspendOnPlayback": "Evitar la suspensión durante la reproducción",
"preventSuspendOnPlayback_description": "Evita que la aplicación se suspenda mientras se reproduce música",
"hotkey_listShowPlayingSong": "Mostrar canción en reproducción en la lista",
"sidebarPlaylistFolders": "Habilitar carpetas",
"sidebarPlaylistFolders_description": "Crea una vista de carpeta para las listas de reproducción que incluye el separador configurado en el nombre",
"sidebarPlaylistFolderSeparator_description": "Carácter (o cadena) que separa los niveles de la carpeta en un nombre de lista de reproducción",
"sidebarPlaylistFolderSeparator": "Separador de carpeta",
"sidebarPlaylistFolderView": "Vista de carpeta",
"sidebarPlaylistFolderView_description": "Cómo se muestran las carpetas en la barra lateral",
"sidebarPlaylistFolderView_optionSingle": "Carpeta simple",
"sidebarPlaylistFolderView_optionTree": "Vista en árbol",
"sidebarPlaylistFolderView_optionNavigation": "Vista de navegación",
"sidebarPlaylistFolderTreeIndent": "Sangría del árbol",
"sidebarPlaylistFolderTreeLineColor": "Color de línea del árbol",
"sidebarPlaylistFolderTreeLineColor_description": "Color de las líneas interconectadas del árbol (dejar en blanco para usar el tema predeterminado)",
"sidebarPlaylistMode": "Modo de lista de reproducción de la barra lateral",
"sidebarPlaylistMode_optionCompact": "Compacto",
"sidebarPlaylistMode_optionExpanded": "Expandido",
"sidebarPlaylistMode_description": "Cómo se muestra cada lista de reproducción en la lista de la barra lateral",
"sidebarPlaylistFolderTreeIndent_description": "Píxeles que está sangrado cada nivel del árbol",
"autoDJ_mode": "Modo",
"autoDJ_mode_albums": "Álbumes",
"autoDJ_mode_songs": "Canciones",
"autoDJ_enabled": "Activar DJ automático",
"autoDJ_albumStrategy": "Modo de selección de álbum",
"autoDJ_songStrategy": "Modo de selección de canción",
"autoDJ_strategy_option_library_random": "Aleatorio",
"autoDJ_strategy_option_similar": "Similar",
"autoDJ_mode_description": "Elegir para añadir canciones o álbumes enteros a la cola"
"preventSuspendOnPlayback_description": "Evita que la aplicación se suspenda mientras se reproduce música"
},
"action": {
"editPlaylist": "Editar $t(entity.playlist, {\"count\": 1})",
@@ -501,9 +473,7 @@
"addOrRemoveFromSelection": "Añadir o quitar de la selección",
"selectRangeOfItems": "Seleccionar un intervalo de elementos",
"selectAll": "Seleccionar todo",
"goToCurrent": "Ir al elemento actual",
"collapseAllFolders": "Contraer todas las carpetas",
"expandAllFolders": "Expandir todas las carpetas"
"goToCurrent": "Ir al elemento actual"
},
"common": {
"backward": "Hacia atrás",
@@ -563,7 +533,7 @@
"comingSoon": "Próximamente…",
"reset": "Restablecer",
"disable": "Desactivar",
"sortOrder": "Orden",
"sortOrder": "Ordenar",
"none": "Ninguno",
"menu": "Menú",
"restartRequired": "Reinicio requerido",
@@ -632,9 +602,7 @@
"rename": "Renombrar",
"newVersionAvailable": "Una nueva versión está disponible",
"numberOfResults": "{{numberOfResults}} resultados",
"grouping": "Agrupar",
"back": "Atrás",
"openFolder": "Abrir carpeta"
"grouping": "Agrupar"
},
"error": {
"remotePortWarning": "Reiniciar el servidor para aplicar el nuevo puerto",
@@ -732,7 +700,7 @@
"artists": "$t(entity.artist, {\"count\": 2})",
"albumArtists": "$t(entity.albumArtist, {\"count\": 2})",
"shared": "Compartido $t(entity.playlist, {\"count\": 2})",
"myLibrary": "Mi Biblioteca",
"myLibrary": "Mi biblioteca",
"favorites": "$t(entity.favorite, {\"count\": 2})",
"radio": "$t(entity.radioStation, {\"count\": 2})",
"collections": "Colecciones"
@@ -793,7 +761,7 @@
"genres": "$t(entity.genre, {\"count\": 2})"
},
"fullscreenPlayer": {
"upNext": "A continuación",
"upNext": "Siguiente",
"config": {
"dynamicBackground": "Fondo dinámico",
"synchronized": "Sincronizado",
@@ -832,7 +800,7 @@
"updates": "Actualización",
"cache": "Caché",
"application": "Aplicación",
"queryBuilder": "Generador de Consultas",
"queryBuilder": "Generador de consultas",
"theme": "Tema",
"controls": "Controles",
"remote": "Remoto",
@@ -843,9 +811,9 @@
"transcoding": "Transcodificación",
"discord": "Discord",
"sidebar": "Barra lateral",
"playerFilters": "Filtros del Reproductor",
"playerFilters": "Filtros del reproductor",
"logger": "Registrador",
"lyricsDisplay": "Mostrar Letras"
"lyricsDisplay": "Mostrar letras"
},
"albumArtistList": {
"title": "$t(entity.albumArtist, {\"count\": 2})"
@@ -958,10 +926,10 @@
"ignoreSsl": "Ignorar SSL ($t(common.restartRequired))",
"ignoreCors": "Ignorar CORS ($t(common.restartRequired))",
"error_savePassword": "Un error ocurrió cuando se intentó guardar la contraseña",
"input_preferInstantMix": "Preferir Mix Instantáneo",
"input_preferInstantMix": "Preferir mix instantáneo",
"input_preferInstantMixDescription": "Usa solo el mix instantáneo para obtener canciones similares. Útil si tienes complementos que modifican este comportamiento",
"input_remoteUrl": "URL Pública",
"input_preferRemoteUrl": "Preferir URL Pública",
"input_remoteUrl": "URL pública",
"input_preferRemoteUrl": "Preferir URL pública",
"input_remoteUrlPlaceholder": "Opcional: URL pública para características externas"
},
"addToPlaylist": {
@@ -1024,12 +992,7 @@
"input_played": "Reproducir filtro",
"input_played_optionAll": "Todas las pistas",
"input_played_optionUnplayed": "Solo las pistas sin reproducir",
"input_played_optionPlayed": "Solo las pistas reproducidas",
"input_kind_albums": "Álbumes",
"input_kind_songs": "Canciones",
"input_limit_albums": "¿Cuántos álbumes?",
"input_limit_songs": "¿Cuántas canciones?",
"input_kind": "Selecciones aleatorias"
"input_played_optionPlayed": "Solo las pistas reproducidas"
},
"saveQueue": {
"success": "Cola de reproducción guardada en el servidor"
@@ -1057,7 +1020,7 @@
"album": "Álbum",
"favorite": "Favorito",
"playCount": "Reproducciones",
"albumCount": "Álbumes",
"albumCount": "$t(entity.album, {\"count\": 2})",
"releaseYear": "Año",
"lastPlayed": "Última reproducción",
"biography": "Biografía",
@@ -1066,19 +1029,19 @@
"title": "Título",
"bpm": "BPM",
"dateAdded": "Fecha de adición",
"artist": "Artista",
"songCount": "Pistas",
"artist": "$t(entity.artist, {\"count\": 1})",
"songCount": "$t(entity.track, {\"count\": 2})",
"trackNumber": "Pista",
"genre": "Género",
"genre": "$t(entity.genre, {\"count\": 1})",
"albumArtist": "Artista del álbum",
"path": "Ruta",
"discNumber": "Disco",
"channels": "Canales",
"size": "Tamaño",
"codec": "decs",
"channels": "$t(common.channel, {\"count\": 2})",
"size": "$t(common.size)",
"codec": "$t(common.codec)",
"owner": "Propietario",
"bitDepth": "Profundidad de Bit",
"sampleRate": "Frecuencia de Muestreo"
"bitDepth": "$t(common.bitDepth)",
"sampleRate": "$t(common.sampleRate)"
},
"config": {
"label": {
@@ -1117,7 +1080,7 @@
"sampleRate": "$t(common.sampleRate)",
"titleArtist": "$t(common.title) (artista)",
"composer": "Compositor",
"albumGroup": "Grupo del Álbum"
"albumGroup": "Grupo del álbum"
},
"general": {
"gap": "$t(common.gap)",
@@ -1160,27 +1123,27 @@
}
},
"entity": {
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) Inteligente",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) inteligente",
"genre_one": "Género",
"genre_many": "Géneros",
"genre_many": "géneros",
"genre_other": "Géneros",
"playlistWithCount_one": "{{count}} lista de reproducción",
"playlistWithCount_many": "{{count}} listas de reproducción",
"playlistWithCount_other": "{{count}} listas de reproducción",
"playlist_one": "Lista de reproducción",
"playlist_many": "Listas de reproducción",
"playlist_many": "listas de reproducción",
"playlist_other": "Listas de reproducción",
"artist_one": "Artista",
"artist_many": "Artistas",
"artist_many": "artistas",
"artist_other": "Artistas",
"folderWithCount_one": "{{count}} carpeta",
"folderWithCount_many": "{{count}} carpetas",
"folderWithCount_other": "{{count}} carpetas",
"albumArtist_one": "Artista del álbum",
"albumArtist_many": "Artistas del álbum",
"albumArtist_many": "artistas del álbum",
"albumArtist_other": "Artistas del álbum",
"track_one": "Pista",
"track_many": "Pistas",
"track_many": "pistas",
"track_other": "Pistas",
"albumArtistCount_one": "{{count}} artista del álbum",
"albumArtistCount_many": "{{count}} artistas del álbum",
@@ -1189,16 +1152,16 @@
"albumWithCount_many": "{{count}} álbumes",
"albumWithCount_other": "{{count}} álbumes",
"favorite_one": "Favorito",
"favorite_many": "Favoritos",
"favorite_many": "favoritos",
"favorite_other": "Favoritos",
"artistWithCount_one": "{{count}} artista",
"artistWithCount_many": "{{count}} artistas",
"artistWithCount_other": "{{count}} artistas",
"folder_one": "Carpeta",
"folder_many": "Carpetas",
"folder_many": "carpetas",
"folder_other": "Carpetas",
"album_one": "Álbum",
"album_many": "Álbumes",
"album_many": "álbumes",
"album_other": "Álbumes",
"genreWithCount_one": "{{count}} género",
"genreWithCount_many": "{{count}} géneros",
@@ -1210,11 +1173,11 @@
"play_many": "{{count}} reproducciones",
"play_other": "{{count}} reproducciones",
"song_one": "Canción",
"song_many": "Canciones",
"song_many": "canciones",
"song_other": "Canciones",
"radioStation_one": "Estación de Radio",
"radioStation_many": "Estaciones de Radio",
"radioStation_other": "Estaciones de Radio",
"radioStation_one": "Estación de radio",
"radioStation_many": "Estaciones de radio",
"radioStation_other": "Estaciones de radio",
"radioStationWithCount_one": "{{count}} estación de radio",
"radioStationWithCount_many": "{{count}} estaciones de radio",
"radioStationWithCount_other": "{{count}} estaciones de radio"
@@ -1234,16 +1197,16 @@
},
"secondary": {
"audiobook": "Audiolibro",
"audioDrama": "Audio Drama",
"audioDrama": "Audio drama",
"compilation": "Compilación",
"djMix": "Mezcla del DJ",
"fieldRecording": "Grabación de Campo",
"fieldRecording": "Grabación de campo",
"interview": "Entrevista",
"live": "En vivo",
"mixtape": "Recopilatorio",
"remix": "Remix",
"soundtrack": "Banda sonora",
"spokenWord": "Palabra Hablada",
"spokenWord": "Palabra hablada",
"demo": "Maqueta"
}
},
File diff suppressed because it is too large Load Diff
+1
View File
@@ -658,6 +658,7 @@
"transcodeFormat": "Transkodetzeko formatua",
"queryBuilderCustomFields_inputLabel": "Etiketa",
"autoDJ": "DJ automatikoa",
"autoDJ_description": "Automatikoki gehitu antzeko abestiak ilaran",
"autoDJ_itemCount_description": "DJ automatikoa gaituta dagoenean ilaran gehitzen saiatu diren elementuen kopurua",
"autoDJ_timing_description": "DJ automatikoa aktibatu aurretik ilaran geratzen diren abestien kopurua",
"analyticsDisable": "Erabileran oinarritutako analisiei uko egin",
+1
View File
@@ -630,6 +630,7 @@
"releaseChannel_description": "Valitse vakaiden ja beetaversioiden välillä automaattisille päivityksille",
"discordDisplayType_artistname": "Artistin nimi / artistien nimet",
"autoDJ": "Auto DJ",
"autoDJ_description": "Lisää automaattisesti samanlaisia kappaleita jonoon",
"autoDJ_itemCount": "Kohteiden määrä",
"autoDJ_itemCount_description": "Jonoon lisättäväksi yritettyjen kohteiden määrä, kun auto DJ on käytössä",
"autoDJ_timing": "Ajastus"
+22 -59
View File
@@ -48,9 +48,7 @@
"sleepTimer_timeRemaining": "{{time}} restante(s)",
"sleepTimer_setCustom": "Définir le minuteur",
"sleepTimer_cancel": "Annuler le minuteur",
"albumRadio": "Radio d'album",
"scrobbleForceSubmit": "Forcer le scrobble",
"sleepTimer_endOfAlbum": "Fin de l'album actuel"
"albumRadio": "Radio d'album"
},
"action": {
"editPlaylist": "Éditer $t(entity.playlist, {\"count\": 1})",
@@ -94,9 +92,7 @@
"selectRangeOfItems": "Sélectionner une plage d'entrées",
"selectAll": "Tout sélectionner",
"openApplicationDirectory": "Ouvrir le répertoire de l'application",
"goToCurrent": "Aller à la piste en cours",
"collapseAllFolders": "Réduire tous les dossiers",
"expandAllFolders": "Développer tous les dossiers"
"goToCurrent": "Aller à la piste en cours"
},
"common": {
"backward": "En arrière",
@@ -226,9 +222,7 @@
"filter_multiple": "Multiple",
"rename": "Renommer",
"newVersionAvailable": "Une nouvelle version est disponible",
"numberOfResults": "{{numberOfResults}} résultats",
"back": "Retour",
"openFolder": "Ouvrir le dossier"
"numberOfResults": "{{numberOfResults}} résultats"
},
"error": {
"remotePortWarning": "Redémarrer le serveur pour appliquer le nouveau port",
@@ -705,7 +699,7 @@
"transcodeFormat_description": "Sélectionne le format du transcodage. laisser vide pour laisser le serveur décider",
"volumeWidth": "Largeur de la barre de volume",
"volumeWidth_description": "La largeur de la barre de volume",
"customCssEnable": "Activer le CSS personnalisé",
"customCssEnable": "Active le CSS personnalisé",
"customCssEnable_description": "Permet l'écriture de CSS personnalisé",
"customCssNotice": "Attention : bien qu'il y ait un certain assainissement (blocage de URL() et de content :), l'utilisation de CSS personnalisé peut toujours présenter des risques en modifiant l'interface",
"customCss": "Css personnalisé",
@@ -730,7 +724,7 @@
"translationTargetLanguage": "Langue cible de traduction",
"trayEnabled": "Afficher la barre d’état système",
"translationApiProvider_description": "Fournisseur d'API pour la traduction",
"customCss_description": "Contenu CSS personnalisé. Remarque : les propriétés 'content' et les URL distantes ne sont pas autorisées. Un aperçu de votre contenu est affiché ci-dessous. Des champs supplémentaires que vous n'avez pas définis sont présents en raison d'assainissement. Application de Bureau uniquement: feishin lit et écrit le fichier custom.css dans le répertoire de configuration de l'application et le recharge lorsque celui-ci est modifié",
"customCss_description": "Contenu CSS personnalisé. Remarque : les propriétés 'content' et les URL distantes ne sont pas autorisées. Un aperçu de votre contenu est affiché ci-dessous. Des champs supplémentaires que vous n'avez pas définis sont présents en raison d'assainissement",
"translationApiKey": "Clé API de traduction",
"translationTargetLanguage_description": "Langue cible pour la traduction",
"trayEnabled_description": "Afficher/masquer licône/le menu dans la barre d’état système. si désactivé, désactive également la réduction/fermeture vers la barre d’état système",
@@ -814,8 +808,9 @@
"queryBuilderCustomFields": "Champs personnalisé",
"queryBuilderCustomFields_description": "Ajouter des champs personnalisés à utiliser dans les constructeurs de requêtes",
"autoDJ": "DJ auto",
"autoDJ_description": "Ajouter automatiquement des titres similaire à la file d'attente",
"autoDJ_itemCount": "Nombre d'entrée",
"autoDJ_itemCount_description": "Le nombre d'entrées tentées d'être ajoutées à la file d'attente",
"autoDJ_itemCount_description": "Le nombre d'entrées tentées d'être ajoutées à la file d'attente lorsque le DJ auto est activé",
"autoDJ_timing": "Timing",
"autoDJ_timing_description": "Le nombre de titres restant dans la file d'attente avant le déclenchement du DJ auto",
"followCurrentSong_description": "Défiler automatiquement la file d'attente jusqu'au titre en cours",
@@ -900,33 +895,7 @@
"sidePlayQueueLayout_optionHorizontal": "Horizontal",
"sidePlayQueueLayout_optionVertical": "Vertical",
"waveformLoadingDelay": "Délai de chargement de la forme d'onde",
"waveformLoadingDelay_description": "Délai en secondes avant le chargement de l'onde. augmentez cette valeur si vous rencontrez des saccades lors de l'utilisation du lecteur web.",
"sidebarPlaylistMode_optionExpanded": "Étendu",
"hotkey_listShowPlayingSong": "Affiche le titre en cours dans la liste",
"preventSuspendOnPlayback_description": "Empêcher la suspension de l'application lors de la lecture de musique",
"preventSuspendOnPlayback": "Empêcher la suspension lors de la lecture",
"sidebarPlaylistFolders_description": "Crée une vue de dossier pour les listes de lecture dont le nom inclut le séparateur configuré",
"sidebarPlaylistFolders": "Activer les dossiers",
"sidebarPlaylistFolderSeparator_description": "Caractère (ou chaîne de caractères) servant à séparer les niveaux de dossiers dans le nom d'une liste de lecture",
"sidebarPlaylistFolderSeparator": "Séparateur de dossiers",
"sidebarPlaylistFolderView_description": "Comment les dossiers sont affichés dans la barre latérale",
"sidebarPlaylistFolderView": "Affichage des dossiers",
"sidebarPlaylistFolderView_optionSingle": "Dossier unique",
"sidebarPlaylistFolderView_optionTree": "Vue arborescente",
"sidebarPlaylistFolderView_optionNavigation": "Vue de navigation",
"sidebarPlaylistFolderTreeIndent": "Indentation de l'arbre",
"sidebarPlaylistMode_description": "Comment chaque liste de lecture est affichée dans la barre latérale",
"sidebarPlaylistMode": "Mode de liste de lecture de la barre latérale",
"sidebarPlaylistMode_optionCompact": "Compacte",
"autoDJ_mode": "Mode",
"autoDJ_mode_albums": "Albums",
"autoDJ_mode_description": "Choisissez d'ajouter des titres ou des albums entiers à la file d'attente",
"autoDJ_mode_songs": "Titres",
"autoDJ_enabled": "Activer le DJ auto",
"autoDJ_albumStrategy": "Mode de sélection d'album",
"autoDJ_songStrategy": "Mode de sélection de titre",
"autoDJ_strategy_option_library_random": "Aléatoire",
"autoDJ_strategy_option_similar": "Similaire"
"waveformLoadingDelay_description": "Délai en secondes avant le chargement de l'onde. augmentez cette valeur si vous rencontrez des saccades lors de l'utilisation du lecteur web."
},
"form": {
"deletePlaylist": {
@@ -953,13 +922,12 @@
"input_remoteUrlPlaceholder": "Optionnel : URL publique pour les fonctionnalités externes"
},
"addToPlaylist": {
"success": "$t(entity.trackWithCount, {\"count\": {{message}} }) ajouté à $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })",
"success": "$t(entity.trackWithCount, {\"count\" : {{message}} }) ajouté à $t(entity.playlistWithCount, {\"count\" : {{numOfPlaylists}} })",
"title": "Ajouter à $t(entity.playlist, {\"count\": 1})",
"input_skipDuplicates": "Sauter les doublons",
"input_playlists": "$t(entity.playlist, {\"count\": 2})",
"create": "Créer $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "Rechercher $t(entity.playlist, {\"count\": 2}) ou tapez pour en créer une nouvelle",
"noneAdded": "Aucune piste n'a été ajoutée à $t(entity.playlist, {\"count\": 1}) {{playlist}}"
"searchOrCreate": "Rechercher $t(entity.playlist, {\"count\": 2}) ou tapez pour en créer une nouvelle"
},
"createPlaylist": {
"title": "Créer une $t(entity.playlist, {\"count\": 1})",
@@ -1020,12 +988,7 @@
"input_played": "Filtre de lecture",
"input_played_optionAll": "Toutes les pistes",
"input_played_optionUnplayed": "Seulement les pistes non jouées",
"input_played_optionPlayed": "Seulement les pistes jouées",
"input_kind_songs": "Titres",
"input_kind_albums": "Albums",
"input_kind": "Sélections aléatoires",
"input_limit_albums": "Combien d'albums?",
"input_limit_songs": "Combien de titres?"
"input_played_optionPlayed": "Seulement les pistes jouées"
},
"createRadioStation": {
"success": "Station radio créée avec succès",
@@ -1048,25 +1011,25 @@
},
"entity": {
"genre_one": "Genre",
"genre_many": "Genres",
"genre_many": "genres",
"genre_other": "Genres",
"playlistWithCount_one": "{{count}} liste de lecture",
"playlistWithCount_many": "{{count}} listes de lecture",
"playlistWithCount_other": "{{count}} listes de lecture",
"playlist_one": "Liste de lecture",
"playlist_many": "Listes de lecture",
"playlist_many": "listes de lecture",
"playlist_other": "Listes de lecture",
"artist_one": "Artiste",
"artist_many": "Artistes",
"artist_many": "artistes",
"artist_other": "Artistes",
"folderWithCount_one": "{{count}} dossier",
"folderWithCount_many": "{{count}} dossiers",
"folderWithCount_other": "{{count}} dossiers",
"albumArtist_one": "Artiste d'album",
"albumArtist_many": "Artistes d'albums",
"albumArtist_many": "artistes d'albums",
"albumArtist_other": "Artistes d'albums",
"track_one": "Piste",
"track_many": "Pistes",
"track_many": "pistes",
"track_other": "Pistes",
"albumArtistCount_one": "{{count}} artiste de l'album",
"albumArtistCount_many": "{{count}} artistes d'albums",
@@ -1075,17 +1038,17 @@
"albumWithCount_many": "{{count}} albums",
"albumWithCount_other": "{{count}} albums",
"favorite_one": "Favori",
"favorite_many": "Favoris",
"favorite_many": "favoris",
"favorite_other": "Favoris",
"artistWithCount_one": "{{count}} artiste",
"artistWithCount_many": "{{count}} artistes",
"artistWithCount_other": "{{count}} artistes",
"folder_one": "Dossier",
"folder_many": "Dossiers",
"folder_many": "dossiers",
"folder_other": "Dossiers",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) Intelligente",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) intelligente",
"album_one": "Album",
"album_many": "Albums",
"album_many": "albums",
"album_other": "Albums",
"genreWithCount_one": "{{count}} genre",
"genreWithCount_many": "{{count}} genres",
@@ -1097,10 +1060,10 @@
"play_many": "{{count}} écoutes",
"play_other": "{{count}} écoutes",
"song_one": "Titre",
"song_many": "Titres",
"song_many": "titres",
"song_other": "Titres",
"radioStation_one": "Station radio",
"radioStation_many": "Stations radio",
"radioStation_many": "stations radio",
"radioStation_other": "Stations radio",
"radioStationWithCount_one": "{{count}} station radio",
"radioStationWithCount_many": "{{count}} stations radio",
+3 -2
View File
@@ -192,8 +192,8 @@
"play_other": "{{count}} lejátszások",
"trackWithCount_one": "{{count}} sáv",
"trackWithCount_other": "{{count}} sávok",
"radioStation_one": "Rádióállomás",
"radioStation_other": "Rádióállomások",
"radioStation_one": "Rádió állomás",
"radioStation_other": "Rádió állomások",
"radioStationWithCount_one": "{{count}} rádióállomás",
"radioStationWithCount_other": "{{count}} rádióállomások"
},
@@ -917,6 +917,7 @@
"queryBuilderCustomFields_description": "Egyéni mezők hozzáadása a lekérdezés-építőhöz",
"autoDJ": "Auto DJ",
"autoDJ_timing": "Időzítés",
"autoDJ_description": "Hasonló dalokat automatikusan hozzáad a műsorlistához",
"autoDJ_itemCount": "Elem szám",
"autoDJ_itemCount_description": "Az auto DJ engedélyezésekor a műsorsorba felvenni kívánt elemek száma",
"autoDJ_timing_description": "Az auto DJ elindulása előtt a műsorlistában maradt dalok száma",
+66 -129
View File
@@ -16,10 +16,7 @@
"viewPlaylists": "Lihat $t(entity.playlist, {\"count\": 2})",
"openIn": {
"lastfm": "Buka di Last.fm",
"musicbrainz": "Buka di MusicBrainz",
"listenbrainz": "Buka di ListenBrainz",
"qobuz": "Buka di Qobuz",
"spotify": "Buka di Spotify"
"musicbrainz": "Buka di MusicBrainz"
},
"addToFavorites": "Tambahkan ke $t(entity.favorite, {\"count\": 2})",
"clearQueue": "Kosongkan antrian",
@@ -41,14 +38,12 @@
"shuffleSelected": "Acak yang dipilih",
"viewMore": "Lihat lebih banyak",
"openApplicationDirectory": "Buka direktori aplikasi",
"goToCurrent": "Pergi ke item saat ini",
"collapseAllFolders": "Ciutkan semua folder",
"expandAllFolders": "Bentangkan semua folder"
"goToCurrent": "Pergi ke item saat ini"
},
"common": {
"clear": "Bersihkan",
"action_other": "Aksi",
"codec": "Kodek",
"codec": "Koded",
"channel_other": "Saluran",
"duration": "Durasi",
"create": "Buat",
@@ -101,7 +96,7 @@
"random": "Acak",
"rating": "Penilaian",
"refresh": "Segarkan",
"reload": "Muat ulang",
"reload": "Muat Ulang",
"reset": "Reset",
"resetToDefault": "Reset ke default",
"restartRequired": "Restart diperlukan",
@@ -116,7 +111,7 @@
"sortOrder": "Urutkan",
"title": "Judul",
"trackNumber": "Pista",
"trackGain": "Gain trek",
"trackGain": "Gain pista",
"trackPeak": "Puncak lagu",
"unknown": "Tidak dikenal",
"version": "Versi",
@@ -161,15 +156,11 @@
"clean": "Bersih",
"gridRows": "Baris kisi",
"tableColumns": "Kolom tabel",
"itemsMore": "{{count}} lagi",
"back": "Kembali",
"grouping": "Pengelompokan",
"numberOfResults": "{{numberOfResults}} hasil",
"newVersionAvailable": "Versi baru tersedia"
"itemsMore": "{{count}} lagi"
},
"entity": {
"album_other": "Album",
"albumArtist_other": "Artis Album",
"albumArtist_other": "Artis album",
"albumArtistCount_other": "{{count}} artis album",
"albumWithCount_other": "{{count}} album",
"artist_other": "Artis",
@@ -179,21 +170,21 @@
"folderWithCount_other": "{{count}} folder",
"genre_other": "Genre",
"genreWithCount_other": "{{count}} genre",
"playlist_other": "Daftar Putar",
"playlist_other": "Daftar putar",
"play_other": "Putar {{count}}",
"playlistWithCount_other": "{{count}} daftar putar",
"smartPlaylist": "Cerdas $t(entity.playlist, {\"count\": 1})",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) pintar",
"track_other": "Pista",
"song_other": "Lagu",
"trackWithCount_other": "{{count}} pista",
"radioStation_other": "Stasiun Radio",
"radioStation_other": "Stasiun radio",
"radioStationWithCount_other": "{{count}} stasiun radio"
},
"error": {
"apiRouteError": "Tidak dapat mengarahkan permintaan",
"audioDeviceFetchError": "Terjadi kesalahan saat mencoba mengambil perangkat audio",
"authenticationFailed": "Autentikasi gagal",
"badAlbum": "Anda melihat halaman ini karena lagu ini bukan bagian dari album. Masalah ini kemungkinan besar muncul jika Anda memiliki lagu di tingkat teratas folder musik Anda. Jellyfin hanya mengelompokkan trek jika berada di dalam folder",
"badAlbum": "Anda melihat halaman ini karena lagu ini tidak termasuk dalam album. Masalah ini bisa terjadi jika Anda memiliki lagu di tingkat atas folder musik Anda. Jellyfin hanya mengelompokkan lagu jika mereka berada di dalam folder",
"credentialsRequired": "Kredensial diperlukan",
"endpointNotImplementedError": "Endpoint {{endpoint}} tidak diimplementasikan untuk {{serverType}}",
"genericError": "Terjadi kesalahan",
@@ -220,8 +211,7 @@
"saveQueueFailed": "Gagal menyimpan antrean",
"settingsSyncError": "Ditemukan ketidaksesuaian antara pengaturan di perender dan proses utama. mulai ulang aplikasi untuk menerapkan perubahan",
"invalidJson": "JSON tidak valid",
"serverLockSingleServer": "Hanya satu server yang diizinkan ketika server dikunci",
"playbackPausedDueToError": "Pemutaran dijeda karena terjadi kesalahan"
"serverLockSingleServer": "Hanya satu server yang diizinkan ketika server dikunci"
},
"filter": {
"album": "$t(entity.album, {\"count\": 1})",
@@ -296,8 +286,7 @@
"success": "Ditambahkan $t(entity.trackWithCount, {\"count\": {{message}} }) ke $t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })",
"title": "Tambahkan ke $t(entity.playlist, {\"count\": 1})",
"create": "Buat $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "Cari $t(entity.playlist, {\"count\": 2}) atau ketik untuk membuat yang baru",
"noneAdded": "Tidak ada trek yang ditambahkan ke $t(entity.playlist, {\"count\": 1}) '{{playlist}}'"
"searchOrCreate": "Cari $t(entity.playlist, {\"count\": 2}) atau ketik untuk membuat yang baru"
},
"createPlaylist": {
"input_description": "$t(common.description)",
@@ -332,12 +321,12 @@
"clearFilters": "Hapus filter"
},
"shareItem": {
"allowDownloading": "Izinkan pengunduhan",
"allowDownloading": "Izinkan unduhan",
"description": "Deskripsi",
"setExpiration": "Atur masa berlaku",
"success": "Tautan bagikan disalin ke papan klip (atau klik di sini untuk membukanya)",
"expireInvalid": "Masa berlaku harus di waktu mendatang",
"createFailed": "Gagal membuat bagikanan (apakah fitur berbagi diaktifkan?)",
"success": "Tautan berbagi berhasil disalin ke papan klip (atau klik di sini untuk membuka)",
"expireInvalid": "Masa berlaku harus di masa depan",
"createFailed": "Tidak dapat membuat sumber daya berbagi (Apakah berbagi diaktifkan?)",
"copyToClipboard": "Salin ke clipboard: Ctrl+C, enter",
"successMustClick": "Berbagi berhasil dibuat. klik di sini untuk membuka"
},
@@ -379,22 +368,19 @@
"enabled": "Mode pribadi diaktifkan, status pemutaran kini disembunyikan dari integrasi eksternal",
"disabled": "Mode pribadi dinonaktifkan, status pemutaran kini terlihat oleh integrasi eksternal yang diaktifkan",
"title": "Mode pribadi"
},
"editRadioStation": {
"success": "Stasiun radio berhasil diperbarui"
}
},
"page": {
"albumArtistDetail": {
"about": "Tentang {{artist}}",
"recentReleases": "Rilisan terbaru",
"recentReleases": "Rilis terbaru",
"viewDiscography": "Lihat diskografi",
"relatedArtists": "$t(entity.artist, {\"count\": 2}) terkait",
"topSongs": "Lagu teratas",
"topSongsFrom": "Lagu teratas dari {{title}}",
"relatedArtists": "$t(entity.artist, {\"count\": 2}) serupa",
"topSongs": "Lagu terbaik",
"topSongsFrom": "Lagu terbaik dari {{title}}",
"viewAll": "Lihat semua",
"viewAllTracks": "Lihat semua $t(entity.track, {\"count\": 2})",
"appearsOn": "Muncul di",
"appearsOn": "Tampil di",
"groupingTypeAll": "Semua jenis rilis",
"groupingTypePrimary": "Jenis rilis utama",
"favoriteSongs": "Lagu favorit",
@@ -461,7 +447,7 @@
"setRating": "$t(action.setRating)",
"playShuffled": "$t(player.shuffle)",
"shareItem": "Bagikan item",
"showDetails": "Dapatkan info",
"showDetails": "Lihat detail",
"moveToTop": "$t(action.moveToTop)",
"play": "$t(player.play)",
"moveItems": "$t(action.moveItems)",
@@ -484,9 +470,7 @@
"unsynchronized": "Tidak sinkronisasi",
"useImageAspectRatio": "Gunakan rasio aspek gambar",
"lyricOffset": "Offset lirik (ms)",
"lyricGap": "Jarak lirik",
"lyricOpacityNonActive": "Opasitas lirik nonaktif",
"lyricScaleNonActive": "Skala lirik nonaktif"
"lyricGap": "Jarak lirik"
},
"lyrics": "Lirik",
"related": "Terkait",
@@ -519,7 +503,7 @@
"itemDetail": {
"copyPath": "Salin jalur ke papan klip",
"copiedPath": "Jalur berhasil disalin",
"openFile": "Tampilkan trek di pengelola file"
"openFile": "Tampilkan lagu di pengelola file"
},
"playlist": {
"reorder": "Pengurutan ulang hanya diaktifkan saat mengurutkan berdasarkan ID"
@@ -647,20 +631,19 @@
"sleepTimer_off": "Mati",
"sleepTimer_timeRemaining": "{{time}} tersisa",
"sleepTimer_setCustom": "Atur pengatur waktu",
"sleepTimer_cancel": "Batalkan pengatur waktu",
"scrobbleForceSubmit": "Paksa scrobble"
"sleepTimer_cancel": "Batalkan pengatur waktu"
},
"setting": {
"accentColor": "Warna sorotan",
"accentColor_description": "Menetapkan warna sorotan aplikasi",
"albumBackground": "Gambar latar belakang album",
"albumBackground_description": "Menambahkan gambar latar belakang untuk halaman album yang berisi sampul album",
"albumBackgroundBlur": "Ukuran keburaman gambar latar belakang album",
"albumBackgroundBlur_description": "Menyesuaikan tingkat keburaman yang diterapkan pada gambar latar belakang album",
"albumBackground_description": "Tambahkan gambar latar belakang ke halaman album yang berisi sampul album",
"albumBackgroundBlur": "Ukuran blur gambar latar belakang album",
"albumBackgroundBlur_description": "Atur tingkat blur gambar latar belakang album",
"applicationHotkeys": "Tombol pintasan aplikasi",
"applicationHotkeys_description": "Menetapkan tombol pintasan aplikasi. centang untuk menjadikannya tombol pintasan global (desktop saja)",
"artistConfiguration": "Konfigurasi halaman artis album",
"artistConfiguration_description": "Atur item apa saja yang ditampilkan, dan dalam urutan apa, pada halaman artis album",
"artistConfiguration": "Pengaturan halaman artis album",
"artistConfiguration_description": "Atur elemen apa yang ditampilkan dan urutannya di halaman artis album",
"audioDevice": "Perangkat audio",
"audioDevice_description": "Pilih perangkat audio yang digunakan untuk pemutaran",
"audioExclusiveMode": "Mode audio eksklusif",
@@ -674,12 +657,12 @@
"windowBarStyle_description": "Pilih gaya bilah jendela",
"zoom": "Persentase zoom",
"zoom_description": "Tentukan persentase zoom aplikasi",
"clearCache_description": "'Pembersihan keras' Feishin. Selain membersihkan cache Feishin, cache browser juga dikosongkan (gambar tersimpan dan aset lainnya). Kredensial server dan pengaturan tetap dipertahankan",
"clearCache_description": "'Pembersihan keras' Feishin. Untuk membersihkan cache Feishin, kosongkan cache browser (gambar yang disimpan dan elemen lainnya). Kredensial dan pengaturan server tetap terjaga",
"clearQueryCache": "Bersihkan cache Feishin",
"clearQueryCache_description": "'Pembersihan lunak' Feishin. Ini akan menyegarkan daftar putar, metadata trek, dan mengatur ulang lirik yang disimpan. Pengaturan, kredensial server, dan gambar yang dicache tetap dipertahankan",
"clearCacheSuccess": "Cache berhasil dikosongkan",
"contextMenu": "Konfigurasi menu konteks (klik kanan)",
"contextMenu_description": "Memungkinkan Anda menyembunyikan item yang ditampilkan di menu saat Anda mengeklik kanan suatu item. Item yang tidak dicentang akan disembunyikan",
"clearQueryCache_description": "'Pembersihan lunak' Feishin. Ini akan menyegarkan daftar putar, metadata lagu, dan mengatur ulang lirik yang disimpan. Pengaturan, kredensial server, dan gambar cache tetap terjaga",
"clearCacheSuccess": "Cache berhasil dibersihkan",
"contextMenu": "Pengaturan menu konteks (klik kanan)",
"contextMenu_description": "Memungkinkan Anda menyembunyikan elemen yang ditampilkan dalam menu saat Anda klik kanan pada elemen. Elemen yang tidak dipilih akan disembunyikan",
"crossfadeDuration": "Durasi crossfade",
"crossfadeDuration_description": "Atur durasi efek crossfade",
"crossfadeStyle_description": "Pilih gaya crossfade yang digunakan oleh pemutar audio",
@@ -694,7 +677,7 @@
"discordApplicationId_description": "ID aplikasi untuk rich presence {{discord}} (default: {{defaultId}})",
"discordIdleStatus": "Tampilkan status tidak aktif dalam status aktivitas",
"discordIdleStatus_description": "Ketika diaktifkan, memperbarui status saat pemutar tidak aktif",
"discordListening": "Tampilkan status sebagai sedang mendengarkan",
"discordListening": "Tampilkan status sebagai mendengarkan",
"discordListening_description": "Tampilkan status sebagai mendengarkan alih-alih bermain",
"discordRichPresence_description": "Aktifkan status pemutaran di status aktivitas {{discord}}. Gambar tombol adalah: {{icon}}, {{playing}}, dan {{paused}}",
"discordUpdateInterval": "Interval pembaruan status aktivitas {{discord}}",
@@ -702,7 +685,7 @@
"enableRemote": "Aktifkan kontrol jarak jauh server",
"enableRemote_description": "Aktifkan kontrol jarak jauh server untuk memungkinkan perangkat lain mengontrol aplikasi",
"externalLinks": "Tampilkan tautan eksternal",
"externalLinks_description": "Mengaktifkan penampilan tautan eksternal (Last.fm, MusicBrainz) pada halaman artis/album",
"externalLinks_description": "Izinkan untuk menampilkan tautan eksternal (Last.fm, MusicBrainz) di halaman artis/album",
"exitToTray": "Keluar ke baki",
"exitToTray_description": "Keluar dari aplikasi ke baki sistem",
"followLyric": "Ikuti lirik saat ini",
@@ -719,14 +702,14 @@
"gaplessAudio_optionWeak": "Lemah (disarankan)",
"globalMediaHotkeys": "Tombol pintasan media global",
"globalMediaHotkeys_description": "Aktifkan atau nonaktifkan penggunaan tombol pintasan sistem media untuk mengontrol pemutaran",
"homeConfiguration": "Konfigurasi halaman beranda",
"homeConfiguration_description": "Atur item apa saja yang ditampilkan, dan dalam urutan apa, pada halaman beranda",
"homeFeature": "Karusel unggulan beranda",
"homeFeature_description": "Mengatur apakah karusel unggulan besar ditampilkan di halaman beranda",
"homeConfiguration": "Pengaturan halaman beranda",
"homeConfiguration_description": "Mengatur elemen mana yang ditampilkan dan urutannya di halaman beranda",
"homeFeature": "Karusel fitur beranda",
"homeFeature_description": "Mengontrol apakah karusel besar fitur ditampilkan di halaman beranda",
"hotkey_browserBack": "Mundur",
"hotkey_browserForward": "Maju",
"hotkey_favoriteCurrentSong": "Favoritkan $t(common.currentSong)",
"hotkey_favoritePreviousSong": "Favoritkan $t(common.previousSong)",
"hotkey_favoriteCurrentSong": "$t(common.currentSong) favorit",
"hotkey_favoritePreviousSong": "$t(common.previousSong) favorit",
"hotkey_globalSearch": "Pencarian global",
"hotkey_localSearch": "Pencarian di halaman",
"hotkey_playbackNext": "Lagu berikutnya",
@@ -735,7 +718,7 @@
"hotkey_playbackPlayPause": "Putar / jeda",
"hotkey_playbackPrevious": "Lagu sebelumnya",
"hotkey_playbackStop": "Berhenti",
"hotkey_rate0": "Hapus penilaian",
"hotkey_rate0": "Bersihkan penilaian",
"hotkey_rate1": "Beri penilaian 1 bintang",
"hotkey_rate2": "Beri penilaian 2 bintang",
"hotkey_rate3": "Beri penilaian 3 bintang",
@@ -749,15 +732,15 @@
"hotkey_toggleQueue": "Ubah antrean",
"hotkey_toggleRepeat": "Toggle ulangi",
"hotkey_toggleShuffle": "Toggle acak",
"hotkey_unfavoriteCurrentSong": "Batalkan favorit $t(common.currentSong)",
"hotkey_unfavoritePreviousSong": "Batalkan favorit $t(common.previousSong)",
"hotkey_unfavoriteCurrentSong": "$t(common.currentSong) tidak favorit",
"hotkey_unfavoritePreviousSong": "$t(common.previousSong) tidak favorit",
"hotkey_volumeDown": "Turunkan volume",
"hotkey_volumeMute": "Senyapkan volume",
"hotkey_volumeUp": "Naikkan volume",
"hotkey_zoomIn": "Perbesar",
"hotkey_zoomOut": "Perkecil",
"imageAspectRatio": "Gunakan rasio aspek asli sampul",
"imageAspectRatio_description": "Jika diaktifkan, sampul akan ditampilkan menggunakan rasio aspek aslinya. Untuk sampul yang tidak 1:1, ruang yang tersisa akan kosong",
"imageAspectRatio": "Gunakan rasio aspek sampul asli",
"imageAspectRatio_description": "Jika diaktifkan, sampul akan ditampilkan dengan rasio aspek aslinya. Untuk seni yang tidak 1:1, ruang yang tersisa akan kosong",
"language_description": "Menetapkan bahasa untuk aplikasi ($t(common.restartRequired))",
"lastfmApiKey": "Kunci API untuk {{lastfm}}",
"lastfmApiKey_description": "Kunci API untuk {{lastfm}}. Diperlukan untuk sampul",
@@ -786,8 +769,8 @@
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"playButtonBehavior_optionPlayShuffled": "$t(player.shuffle)",
"playerbarOpenDrawer": "Tombol alih layar penuh bilah pemutar",
"playerbarOpenDrawer_description": "Memungkinkan bilah pemutar diklik untuk membuka pemutar layar penuh",
"playerbarOpenDrawer": "Buka pemutar ke layar penuh",
"playerbarOpenDrawer_description": "Izinkan mengklik bilah pemutar untuk membuka pemutar di layar penuh",
"remotePassword": "Kata sandi kontrol jarak jauh server",
"remotePassword_description": "Tentukan kata sandi untuk kontrol jarak jauh server. Kredensial ini dikirimkan dengan tidak aman secara default, jadi Anda harus menggunakan kata sandi unik untuk menghindari masalah",
"remotePort": "Port kontrol jarak jauh server",
@@ -846,7 +829,7 @@
"translationApiKey_description": "Kunci API untuk terjemahan (hanya endpoint layanan global)",
"translationTargetLanguage": "Bahasa tujuan penerjemahan",
"translationTargetLanguage_description": "Bahasa tujuan untuk penerjemahan",
"trayEnabled": "Tampilkan baki",
"trayEnabled": "Tampilkan di area pemberitahuan",
"trayEnabled_description": "Tampilkan/sembunyikan ikon/menu di area pemberitahuan. Jika dinonaktifkan, juga menonaktifkan meminimalkan/keluar ke baki",
"useSystemTheme": "Gunakan tema sistem",
"useSystemTheme_description": "Ikuti preferensi terang atau gelap yang ditetapkan oleh sistem",
@@ -855,13 +838,14 @@
"volumeWidth": "Lebar penggeser volume",
"volumeWidth_description": "Lebar penggeser volume",
"webAudio": "Gunakan audio web",
"clearCache": "Kosongkan cache browser",
"clearCache": "Bersihkan cache browser",
"disableLibraryUpdateOnStartup": "Nonaktifkan pemeriksaan versi baru saat startup",
"mpvExecutablePath": "Jalur executable mpv",
"playButtonBehavior_optionPlay": "$t(player.play)",
"sampleRate": "Rasio sampel",
"savePlayQueue": "Simpan antrean pemutaran",
"autoDJ": "DJ Otomatis",
"autoDJ": "DJ otomatis",
"autoDJ_description": "Tambahkan lagu serupa secara otomatis ke antrean",
"autoDJ_itemCount": "Jumlah item",
"autoDJ_itemCount_description": "Jumlah item yang dicoba ditambahkan ke antrean saat DJ otomatis diaktifkan",
"autoDJ_timing": "Waktu",
@@ -1006,76 +990,36 @@
"playerItemConfiguration": "Konfigurasi item pemutar",
"sidebarPlaylistListFilterRegex_description": "Sembunyikan playlist di bilah sisi yang cocok dengan ekspresi reguler ini",
"sidebarPlaylistListFilterRegex_placeholder": "Mis. ^daily mix.*",
"sidebarPlaylistListFilterRegex": "Regex filter playlist",
"autosave": "Simpan antrean putar secara otomatis",
"autosave_description": "Aktifkan penyimpanan otomatis antrean putar ke server Anda. Ini hanya dimungkinkan saat menggunakan Navidrome/Subsonic, dan Anda tidak dapat memiliki antrean putar campuran.",
"autosaveCount": "Frekuensi penyimpanan otomatis antrean putar",
"autosaveCount_description": "Berapa banyak perubahan trek sebelum antrean disimpan. 1 (minimum) berarti setiap pergantian lagu",
"hotkey_listShowPlayingSong": "Tampilkan lagu yang sedang diputar dalam daftar",
"listenbrainz_description": "Tampilkan tautan ke ListenBrainz pada halaman artis/album",
"listenbrainz": "Tampilkan tautan ListenBrainz",
"qobuz_description": "Tampilkan tautan ke Qobuz pada halaman artis/album",
"qobuz": "Tampilkan tautan Qobuz",
"spotify_description": "Tampilkan tautan ke Spotify pada halaman artis/album",
"spotify": "Tampilkan tautan Spotify",
"nativeSpotify_description": "Buka di aplikasi Spotify alih-alih di browser Anda",
"nativeSpotify": "Gunakan aplikasi Spotify",
"playerbarWaveformStretch": "Peregangan bentuk gelombang",
"playerbarWaveformStretch_description": "Meregangkan bentuk gelombang untuk memenuhi ruang yang tersedia",
"preventSuspendOnPlayback_description": "Cegah aplikasi ditangguhkan saat musik diputar",
"preventSuspendOnPlayback": "Cegah penangguhan saat pemutaran",
"sidebarPlaylistFolders_description": "Buat tampilan folder untuk daftar putar yang menyertakan pemisah yang dikonfigurasi dalam namanya",
"sidebarPlaylistFolders": "Aktifkan folder",
"sidebarPlaylistFolderSeparator_description": "Karakter (atau string) yang memisahkan tingkat folder dalam nama daftar putar",
"sidebarPlaylistFolderSeparator": "Pemisah folder",
"sidebarPlaylistFolderView_description": "Cara folder ditampilkan di bilah sisi",
"sidebarPlaylistFolderView": "Tampilan folder",
"sidebarPlaylistFolderView_optionSingle": "Folder tunggal",
"sidebarPlaylistFolderView_optionTree": "Tampilan pohon",
"sidebarPlaylistFolderView_optionNavigation": "Tampilan navigasi",
"sidebarPlaylistFolderTreeIndent_description": "Jumlah piksel indentasi tiap tingkat pohon",
"sidebarPlaylistFolderTreeIndent": "Indentasi pohon",
"sidebarPlaylistFolderTreeLineColor_description": "Warna garis penghubung pohon (biarkan kosong untuk default tema)",
"sidebarPlaylistFolderTreeLineColor": "Warna garis pohon",
"sidebarPlaylistMode_description": "Cara setiap daftar putar ditampilkan dalam daftar bilah sisi",
"sidebarPlaylistMode": "Mode daftar putar bilah sisi",
"sidebarPlaylistMode_optionCompact": "Ringkas",
"sidebarPlaylistMode_optionExpanded": "Diperluas",
"sidePlayQueueLayout": "Tata letak antrean putar samping",
"sidePlayQueueLayout_description": "Mengatur tata letak antrean putar samping yang terlampir",
"sidePlayQueueLayout_optionHorizontal": "Horizontal",
"sidePlayQueueLayout_optionVertical": "Vertikal",
"waveformLoadingDelay": "Penundaan pemuatan bentuk gelombang",
"waveformLoadingDelay_description": "Penundaan dalam detik sebelum memuat bentuk gelombang. Tingkatkan nilai ini jika Anda mengalami tersendat saat menggunakan pemutar web."
"sidebarPlaylistListFilterRegex": "Regex filter playlist"
},
"table": {
"column": {
"album": "Album",
"albumArtist": "Artis album",
"albumCount": "Album",
"artist": "Artis",
"albumCount": "$t(entity.album, {\"count\": 2})",
"artist": "$t(entity.artist, {\"count\": 1})",
"biography": "Biografi",
"bitrate": "Bitrate",
"bpm": "Lpm",
"channels": "Saluran",
"codec": "Kodek",
"channels": "$t(common.channel, {\"count\": 2})",
"codec": "$t(common.codec)",
"comment": "Komentar",
"dateAdded": "Tanggal ditambahkan",
"discNumber": "Nomor disk",
"favorite": "Favorit",
"genre": "Genre",
"genre": "$t(entity.genre, {\"count\": 1})",
"lastPlayed": "Terakhir diputar",
"path": "Jalur",
"playCount": "Putaran",
"rating": "Penilaian",
"releaseDate": "Tanggal rilis",
"releaseYear": "Tahun",
"size": "Ukuran",
"songCount": "Trek",
"size": "$t(common.size)",
"songCount": "$t(entity.track, {\"count\": 2})",
"title": "Judul",
"trackNumber": "Pista",
"bitDepth": "Kedalaman Bit",
"sampleRate": "Laju Sampel",
"bitDepth": "$t(common.bitDepth)",
"sampleRate": "$t(common.sampleRate)",
"owner": "Pemilik"
},
"config": {
@@ -1363,13 +1307,6 @@
"d": "D",
"z": "Z"
}
},
"systemAudioConsentAllow": "Izinkan",
"systemAudioConsentBody": "Visualizer memerlukan akses ke audio sistem agar dapat berfungsi",
"systemAudioConsentDecline": "Tolak",
"systemAudioConsentTitle": "Izinkan akses ke audio sistem?",
"systemAudioCaptureFailed": "Tidak dapat memulai pengambilan: {{message}}",
"systemAudioNoAudioTrack": "Tidak ada trek audio yang dikembalikan. Pastikan pengambilan audio diaktifkan saat diminta.",
"systemAudioExclusiveModeNotSupported": "Visualizer tidak tersedia saat mode audio eksklusif diaktifkan. Nonaktifkan Mode Audio Eksklusif di pengaturan MPV lalu coba lagi."
}
}
}
+14 -13
View File
@@ -438,6 +438,7 @@
"discordLinkType_none": "$t(common.none)",
"discordLinkType_mbz_lastfm": "{{musicbrainz}} con {{lastfm}} fallback",
"autoDJ": "Auto DJ",
"autoDJ_description": "Aggiungi automaticamente canzoni simili alla coda",
"autoDJ_itemCount": "Conteggio elementi",
"analyticsDisable_description": "Alcuni dati anonimi sull'utilizzo vengono inviati allo sviluppatore per migliorare l'applicazione",
"artistBackground": "Immagine dello sfondo dell'artista",
@@ -929,25 +930,25 @@
},
"entity": {
"genre_one": "Genere",
"genre_many": "Generi",
"genre_many": "generi",
"genre_other": "Generi",
"playlistWithCount_one": "{{count}} playlist",
"playlistWithCount_many": "{{count}} playlist",
"playlistWithCount_other": "{{count}} playlist",
"playlist_one": "Playlist",
"playlist_many": "Playlist",
"playlist_many": "playlist",
"playlist_other": "Playlist",
"artist_one": "Artista",
"artist_many": "Artisti",
"artist_many": "artisti",
"artist_other": "Artisti",
"folderWithCount_one": "{{count}} cartella",
"folderWithCount_many": "{{count}} cartelle",
"folderWithCount_other": "{{count}} cartelle",
"albumArtist_one": "Artista Album",
"albumArtist_many": "Artisti Album",
"albumArtist_other": "Artisti Album",
"albumArtist_one": "Artista album",
"albumArtist_many": "artisti album",
"albumArtist_other": "Artisti album",
"track_one": "Traccia",
"track_many": "Tracce",
"track_many": "tracce",
"track_other": "Tracce",
"albumArtistCount_one": "{{count}} artista album",
"albumArtistCount_many": "{{count}} artisti album",
@@ -956,17 +957,17 @@
"albumWithCount_many": "{{count}} album",
"albumWithCount_other": "{{count}} album",
"favorite_one": "Preferito",
"favorite_many": "Preferiti",
"favorite_many": "preferiti",
"favorite_other": "Preferiti",
"artistWithCount_one": "{{count}} artista",
"artistWithCount_many": "{{count}} artisti",
"artistWithCount_other": "{{count}} artisti",
"folder_one": "Cartella",
"folder_many": "Cartelle",
"folder_many": "cartelle",
"folder_other": "Cartelle",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) Smart",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) smart",
"album_one": "Album",
"album_many": "Album",
"album_many": "album",
"album_other": "Album",
"genreWithCount_one": "{{count}} genere",
"genreWithCount_many": "{{count}} generi",
@@ -978,10 +979,10 @@
"play_many": "{{count}} riproduzioni",
"play_other": "{{count}} riproduzioni",
"song_one": "Traccia",
"song_many": "Tracce",
"song_many": "tracce",
"song_other": "Tracce",
"radioStation_one": "Stazione radio",
"radioStation_many": "Stazioni radio",
"radioStation_many": "stazioni radio",
"radioStation_other": "Stazioni radio",
"radioStationWithCount_one": "{{count}} stazione radio",
"radioStationWithCount_many": "{{count}} stazioni radio",
+35 -55
View File
@@ -48,9 +48,7 @@
"holdToShuffle": "長押しでシャッフル",
"albumRadio": "アルバム・ラジオ",
"artistRadio": "アーティストラジオ",
"trackRadio": "ラジオを追跡する",
"scrobbleForceSubmit": "強制 Scrobble",
"sleepTimer_endOfAlbum": "現在のアルバムの終了"
"trackRadio": "ラジオを追跡する"
},
"setting": {
"crossfadeStyle_description": "オーディオプレーヤーが使用するクロスフェードのスタイルを選択します",
@@ -316,6 +314,7 @@
"exportImportSettings_importSuccess": "設定が正常にインポートされました!",
"exportImportSettings_importModalTitle": "Feishin 設定をインポート",
"exportImportSettings_importBtn": "設定をインポート",
"autoDJ_description": "類似の曲を自動でキューに追加します",
"autoDJ": "自動 DJ",
"autoDJ_itemCount_description": "自動 DJ が有効なときにキューに追加しようとした曲数",
"autoDJ_itemCount": "曲数",
@@ -430,22 +429,12 @@
"playerbarWaveformStretch": "波形伸縮",
"playerbarWaveformStretch_description": "波形を伸縮させて、利用可能なスペースを埋めます",
"preventSuspendOnPlayback_description": "音楽再生中にアプリケーションが停止しないようにします",
"preventSuspendOnPlayback": "再生の中断を防止する",
"hotkey_listShowPlayingSong": "再生中の曲をリストに表示",
"autoDJ_mode": "モード",
"autoDJ_mode_albums": "アルバム",
"autoDJ_mode_description": "キューに曲を追加するか、アルバム全体を追加するかを選択してください。",
"autoDJ_mode_songs": "曲",
"autoDJ_enabled": "Auto DJを有効にする",
"autoDJ_albumStrategy": "アルバム選択モード",
"autoDJ_songStrategy": "選曲モード",
"autoDJ_strategy_option_library_random": "ランダム",
"autoDJ_strategy_option_similar": "類似"
"preventSuspendOnPlayback": "再生の中断を防止する"
},
"action": {
"editPlaylist": "$t(entity.playlist, {\"count\": 1}) を編集",
"goToPage": "ページへ移動",
"moveToTop": "一番上へ移動",
"moveToTop": "先頭に移動",
"clearQueue": "キューをクリア",
"addToFavorites": "$t(entity.favorite, {\"count\": 2}) に追加",
"addToPlaylist": "$t(entity.playlist, {\"count\": 1}) に追加",
@@ -456,9 +445,9 @@
"deletePlaylist": "$t(entity.playlist, {\"count\": 1}) を削除",
"removeFromQueue": "キューから削除",
"deselectAll": "すべて選択解除",
"moveToBottom": "一番下へ移動",
"setRating": "評価を設定",
"toggleSmartPlaylistEditor": "$t(entity.smartPlaylist) エディターを切り替え",
"moveToBottom": "末尾に移動",
"setRating": "評価を設定する",
"toggleSmartPlaylistEditor": "$t(entity.smartPlaylist) エディタ切り替え",
"removeFromFavorites": "$t(entity.favorite, {\"count\": 2}) から削除",
"openIn": {
"lastfm": "Last.fm で開く",
@@ -467,9 +456,9 @@
"listenbrainz": "ListenBrainz で開く",
"qobuz": "Qobuz で開く"
},
"moveToNext": "次へ進む",
"moveToNext": "次",
"downloadStarted": "{{count}} 曲のダウンロードを開始しました",
"moveItems": "項目を移動",
"moveItems": "を移動",
"shuffle": "シャッフル",
"shuffleAll": "すべてシャッフル",
"shuffleSelected": "選択した曲をシャッフル",
@@ -481,28 +470,26 @@
"moveDown": "下に移動",
"holdToMoveToTop": "押し続けると一番上に移動します",
"holdToMoveToBottom": "押し続けると一番下に移動します",
"openApplicationDirectory": "アプリディレクトリを開く",
"openApplicationDirectory": "アプリケーションディレクトリを開く",
"selectRangeOfItems": "項目の範囲を選択",
"addOrRemoveFromSelection": "選択に追加または選択から除外",
"goToCurrent": "現在の項目へ移動",
"collapseAllFolders": "すべてのフォルダーを折りたたむ",
"expandAllFolders": "すべてのフォルダーを展開する"
"addOrRemoveFromSelection": "選択に追加または削除",
"goToCurrent": "現在の項目へ移動"
},
"common": {
"backward": "逆行",
"backward": "戻る",
"increase": "増加",
"rating": "評価",
"bpm": "BPM",
"refresh": "再読み込み",
"unknown": "不明",
"areYouSure": "実行してもよろしいですか",
"areYouSure": "実行しすか?",
"edit": "編集",
"favorite": "お気に入り",
"left": "左側",
"save": "保存",
"right": "右側",
"currentSong": "現在の $t(entity.track, {\"count\": 1})",
"collapse": "折りたた",
"collapse": "折りたた",
"trackNumber": "トラック",
"descending": "降順",
"add": "追加",
@@ -544,7 +531,7 @@
"confirm": "確認",
"resetToDefault": "デフォルトにリセット",
"home": "ホーム",
"comingSoon": "近日公開…",
"comingSoon": "近日利用可能になる予定です…",
"reset": "リセット",
"channel_other": "チャンネル",
"disable": "無効",
@@ -553,7 +540,7 @@
"menu": "メニュー",
"restartRequired": "再起動が必要です",
"previousSong": "前の $t(entity.track, {\"count\": 1})",
"noResultsFromQuery": "クエリに一致する結果がありません",
"noResultsFromQuery": "条件にマッチするものがありません",
"quit": "終了",
"expand": "展開",
"search": "検索",
@@ -563,11 +550,11 @@
"random": "ランダム",
"size": "サイズ",
"biography": "バイオグラフィー",
"note": "注記",
"note": "ノート",
"explicitStatus": "明示的なステータス",
"additionalParticipants": "追加参加者",
"newVersion": "新しいバージョン ({{version}}) がインストールされました",
"viewReleaseNotes": "リリースノートを表示",
"viewReleaseNotes": "リリースノートを表示する",
"bitDepth": "ビット深度",
"close": "閉じる",
"codec": "コーデック",
@@ -575,7 +562,7 @@
"sampleRate": "サンプルレート",
"preview": "プレビュー",
"private": "プライベート",
"public": "公開",
"public": "パブリック",
"share": "共有",
"tags": "タグ",
"trackGain": "トラックゲイン",
@@ -607,9 +594,7 @@
"rename": "名前を変更",
"newVersionAvailable": "新しいバージョンが利用可能です",
"numberOfResults": "{{numberOfResults}} 件の結果",
"grouping": "グループ化",
"back": "戻る",
"openFolder": "フォルダーを開く"
"grouping": "グループ化"
},
"table": {
"config": {
@@ -658,7 +643,7 @@
"titleCombined": "$t(common.title) (結合)",
"dateAdded": "追加日",
"size": "$t(common.size)",
"bpm": "$t(common.bpm)",
"bpm": "$t(common.BPM)",
"lastPlayed": "最後に再生",
"trackNumber": "トラック番号",
"rowIndex": "行インデックス",
@@ -696,7 +681,7 @@
"rating": "評価",
"favorite": "お気に入り",
"playCount": "再生回数",
"albumCount": "アルバム",
"albumCount": "$t(entity.album, {\"count\": 2})",
"releaseYear": "年",
"lastPlayed": "最後に再生",
"biography": "バイオグラフィー",
@@ -705,23 +690,23 @@
"title": "タイトル",
"bpm": "BPM",
"dateAdded": "追加日",
"artist": "アーティスト",
"songCount": "トラック",
"artist": "$t(entity.artist, {\"count\": 1})",
"songCount": "$t(entity.track, {\"count\": 2})",
"trackNumber": "トラック",
"genre": "ジャンル",
"genre": "$t(entity.genre, {\"count\": 1})",
"albumArtist": "アルバムアーティスト",
"path": "パス",
"discNumber": "ディスク",
"channels": "チャンネル",
"size": "サイズ",
"codec": "コーデック",
"bitDepth": "ビット深度",
"sampleRate": "サンプリング周波数",
"channels": "$t(common.channel, {\"count\": 2})",
"size": "$t(common.size)",
"codec": "$t(common.codec)",
"bitDepth": "$t(common.bitDepth)",
"sampleRate": "$t(common.sampleRate)",
"owner": "所有者"
}
},
"error": {
"remotePortWarning": "新しいポート設定を反映させるには、サーバーを再起動してください",
"remotePortWarning": "新たなポート設定を適用するためサーバーを再起動してください",
"systemFontError": "システムフォントを取得する際にエラーが発生しました",
"playbackError": "メディアの再生開始時にエラーが発生しました",
"remotePortError": "リモートサーバーのポート設定時にエラーが発生しました",
@@ -736,7 +721,7 @@
"serverNotSelectedError": "サーバーが選択されていません",
"remoteDisableError": "リモートサーバーを$t(common.disable)にする際にエラーが発生しました",
"mpvRequired": "MPV が必要です",
"audioDeviceFetchError": "オーディオデバイス取得しようとした際にエラーが発生しました",
"audioDeviceFetchError": "オーディオデバイス取得にエラーが発生しました",
"invalidServer": "無効なサーバー",
"loginRateError": "ログイン試行回数が多すぎます。数秒後に再試行してください",
"endpointNotImplementedError": "{{serverType}} にはエンドポイント {{endpoint}} が実装されていません",
@@ -744,7 +729,7 @@
"networkError": "ネットワークエラーが発生しました",
"notificationDenied": "通知の許可が拒否されました。この設定は効果がありません",
"openError": "ファイルを開けませんでした",
"badValue": "無効なオプション「{{value}}」です。この値は存在しません",
"badValue": "無効なオプション「{{value}}」。この値は存在しません",
"multipleServerSaveQueueError": "再生キューに現在のサーバーに存在しない曲が 1 曲以上あります。これはサポートされていません",
"noNetwork": "サーバーが利用できません",
"noNetworkDescription": "このサーバーに接続できませんでした",
@@ -1120,12 +1105,7 @@
"input_played_optionAll": "すべてのトラック",
"input_played_optionUnplayed": "未再生のトラックのみ",
"input_played_optionPlayed": "再生されたトラックのみ",
"input_played": "再生フィルター",
"input_kind_albums": "アルバム",
"input_kind_songs": "曲",
"input_kind": "ランダムピック",
"input_limit_albums": "アルバムは何枚ですか?",
"input_limit_songs": "何曲ですか?"
"input_played": "再生フィルター"
},
"saveQueue": {
"success": "プレイキューをサーバーに保存しました"
+24 -244
View File
@@ -17,10 +17,7 @@
"removeFromPlaylist": "$t(entity.playlist, {\"count\": 1})에서 제거",
"openIn": {
"musicbrainz": "MusicBrainz에서 보기",
"lastfm": "Last.fm에서 보기",
"listenbrainz": "ListenBrainz에서 열기",
"qobuz": "Qobuz에서 열기",
"spotify": "Spotify에서 열기"
"lastfm": "Last.fm에서 보기"
},
"viewPlaylists": "$t(entity.playlist, {\"count\": 2}) 보기",
"setRating": "평점 지정",
@@ -40,10 +37,7 @@
"shuffleAll": "모두 섞기",
"shuffleSelected": "선택항목 섞기",
"viewMore": "더 보기",
"openApplicationDirectory": "앱 디렉토리 열기",
"goToCurrent": "현재 항목으로 이동",
"collapseAllFolders": "모든 폴더 접기",
"expandAllFolders": "모든 폴더 확장"
"openApplicationDirectory": "앱 디렉토리 열기"
},
"common": {
"translation": "번역",
@@ -155,18 +149,7 @@
"sort": "정렬",
"gridRows": "행 그리드",
"tableColumns": "테이블 열",
"itemsMore": "{{count}}개 더",
"back": "뒤로",
"example": "예",
"openFolder": "폴더 열기",
"filter_single": "미혼",
"filter_multiple": "다중",
"grouping": "그룹화",
"mood": "기분",
"numberOfResults": "결과 {{numberOfResults}}개",
"retry": "다시 해 보다",
"rename": "이름 변경",
"newVersionAvailable": "새로운 버전이 나왔습니다"
"itemsMore": "{{count}}개 더"
},
"entity": {
"albumWithCount_other": "{{count}} 앨범",
@@ -214,15 +197,7 @@
"localFontAccessDenied": "로컬 글꼴에 접근 거부되었습니다",
"apiRouteError": "요청 보내기 실패",
"badValue": "옵션이 없습니다 {{value}}. 이 값은 더이상 존재하지 않습니다",
"notificationDenied": "알림에 대한 권한이 거부되었습니다. 이 설정은 변경되지 않습니다",
"invalidJson": "유효하지 않은 JSON",
"multipleServerSaveQueueError": "재생 대기열에 현재 서버에 속하지 않은 곡이 하나 이상 포함되어 있습니다. 이는 지원되지 않습니다",
"noNetwork": "서버를 이용할 수 없음",
"noNetworkDescription": "이 서버에 연결할 수 없습니다",
"playbackPausedDueToError": "오류로 인해 재생이 일시 중지되었습니다",
"saveQueueFailed": "큐 저장 실패",
"serverLockSingleServer": "서버가 잠겨 있을 때는 서버를 하나만 허용합니다",
"settingsSyncError": "렌더러와 메인 프로세스의 설정 간에 불일치가 발견되었습니다. 변경 사항을 적용하려면 애플리케이션을 다시 시작하십시오"
"notificationDenied": "알림에 대한 권한이 거부되었습니다. 이 설정은 변경되지 않습니다"
},
"filter": {
"title": "곡명",
@@ -247,7 +222,7 @@
"disc": "디스크",
"bitrate": "비트 전송률",
"biography": "바이오그래피",
"channels": "$t(common.channel, {\"count\": 2})",
"channels": "$t(common.channel_other)",
"duration": "길이",
"bpm": "BPM",
"albumCount": "$t(entity.album, {\"count\": 2}) 앨범수",
@@ -267,10 +242,7 @@
"songCount": "곡 갯수",
"toYear": "년도까지",
"trackNumber": "트랙",
"explicitStatus": "$t(common.explicitStatus)",
"matchAnd": "그리고",
"matchOr": "또는",
"sortName": "이름 정렬"
"explicitStatus": "$t(common.explicitStatus)"
},
"form": {
"addServer": {
@@ -286,10 +258,7 @@
"input_legacyAuthentication": "레거시 인증 사용",
"input_username": "유저 이름",
"input_preferInstantMix": "즉석 믹스 선호",
"input_preferInstantMixDescription": "비슷한 곳을 찾기 위해 즉석 믹스를 사용합니다. 이 명령을 수정하기 위한 플러그인을 설치한 경우 유용합니다",
"input_preferRemoteUrl": "공개 URL 선호",
"input_remoteUrl": "공개 URL",
"input_remoteUrlPlaceholder": "선택 사항: 외부 기능을 위한 공개 URL"
"input_preferInstantMixDescription": "비슷한 곳을 찾기 위해 즉석 믹스를 사용합니다. 이 명령을 수정하기 위한 플러그인을 설치한 경우 유용합니다"
},
"addToPlaylist": {
"input_skipDuplicates": "중복 건너뛰기",
@@ -297,8 +266,7 @@
"input_playlists": "$t(entity.playlist, {\"count\": 2})",
"success": "$t(entity.playlistWithCount, {\"count\": {{numOfPlaylists}} })에 $t(entity.trackWithCount, {\"count\": {{message}} })가 추가되었습니다",
"create": "$t(entity.playlist, {\"count\": 1}) {{playlist}} 생성",
"searchOrCreate": "$t(entity.playlist, {\"count\": 2}) 검색 또는 입력하여 새로 만들기",
"noneAdded": "$t(entity.playlist, {\"count\": 1}) '{{playlist}}'에 트랙이 추가되지 않았습니다"
"searchOrCreate": "$t(entity.playlist, {\"count\": 2}) 검색 또는 입력하여 새로 만들기"
},
"lyricSearch": {
"title": "가사 검색",
@@ -308,11 +276,7 @@
"queryEditor": {
"input_optionMatchAll": "모두 일치",
"input_optionMatchAny": "무엇이든 일치",
"title": "쿼리 편집기",
"addRuleGroup": "규칙 그룹 추가",
"removeRuleGroup": "규칙 그룹 제거",
"resetToDefault": "기본값으로 초기화",
"clearFilters": "필터 초기화"
"title": "쿼리 편집기"
},
"editPlaylist": {
"title": "$t(entity.playlist, {\"count\": 1}) 편집",
@@ -325,9 +289,7 @@
"success": "클립보드에 공유 링크를 복사했습니다 (또는 열어보려면 클릭하세요)",
"expireInvalid": "만료 날짜는 미래 날짜여야만 합니다",
"createFailed": "공유 링크를 생성하는데 실패하였습니다 (혹시 공유하기 설정되어 있나요?)",
"setExpiration": "만료 기간 설정하기",
"copyToClipboard": "클립보드로 복사: Ctrl+C, Enter",
"successMustClick": "공유가 성공적으로 생성되었습니다. 여기를 클릭하여 여세요"
"setExpiration": "만료 기간 설정하기"
},
"updateServer": {
"title": "서버 업데이트",
@@ -350,44 +312,6 @@
"enabled": "프라이빗 모드가 활성화되었습니다. 재생상태가 외부 서비스에 지금부터 노출되지 않습니다",
"disabled": "프라이빗 모드가 비활성화되었습니다. 재생상태가 외부서비스에서 지금부터 표시됩니다",
"title": "프라이빗 모드"
},
"largeFetchConfirmation": {
"title": "대기열에 항목을 추가하세요",
"description": "이 작업은 현재 필터링된 보기의 모든 항목을 추가합니다"
},
"createRadioStation": {
"success": "라디오 방송국이 성공적으로 생성되었습니다",
"title": "라디오 방송국 만들기",
"input_homepageUrl": "홈페이지 URL",
"input_name": "명의",
"input_streamUrl": "스트림 URL"
},
"editRadioStation": {
"success": "라디오 방송국이 성공적으로 업데이트되었습니다"
},
"lyricsExport": {
"export": "가사 내보내기",
"input_synced": "동기화된 가사 내보내기",
"input_offset": "$t(setting.lyricOffset)"
},
"saveQueue": {
"success": "재생 대기열을 서버에 저장했습니다"
},
"shuffleAll": {
"title": "무작위 재생",
"input_kind_albums": "앨범",
"input_kind_songs": "노래들",
"input_kind": "무작위 선택",
"input_limit_albums": "앨범이 몇 장인가요?",
"input_limit_songs": "몇 곡인가요?",
"input_genre": "$t(entity.genre, {\"count\": 1})",
"input_limit": "몇 곡인가요?",
"input_minYear": "연도부터",
"input_maxYear": "연도까지",
"input_played": "재생 필터",
"input_played_optionAll": "모든 트랙",
"input_played_optionUnplayed": "재생하지 않은 트랙만",
"input_played_optionPlayed": "재생된 트랙만"
}
},
"page": {
@@ -401,13 +325,7 @@
"collapseSidebar": "사이드바 줄이기",
"expandSidebar": "사이드바 확장",
"privateModeOff": "프라이빗 모드 끄기",
"privateModeOn": "프라이빗 모드 켜기",
"commandPalette": "명령 팔레트 열기",
"quit": "$t(common.quit)",
"selectMusicFolder": "음악 폴더 선택",
"noMusicFolder": "음악 폴더가 선택되지 않았습니다",
"multipleMusicFolders": "{{count}}개의 음악 폴더가 선택되었습니다",
"settings": "$t(common.setting, {\"count\": 2})"
"privateModeOn": "프라이빗 모드 켜기"
},
"manageServers": {
"title": "서버 설정하기",
@@ -432,9 +350,7 @@
"lyricGap": "가사 간격",
"lyricSize": "가사 크기",
"showLyricMatch": "가사 일치 표시",
"showLyricProvider": "가사 제공자 표시",
"lyricOpacityNonActive": "비활성 가사 불투명도",
"lyricScaleNonActive": "비활성 서정적 척도"
"showLyricProvider": "가사 제공자 표시"
},
"lyrics": "가사",
"related": "관련",
@@ -448,27 +364,7 @@
"shareItem": "공유",
"goToAlbum": "$t(entity.album, {\"count\": 1})으로 이동",
"goToAlbumArtist": "$t(entity.albumArtist, {\"count\": 1})으로 이동",
"showDetails": "추가정보",
"addFavorite": "$t(action.addToFavorites)",
"addLast": "$t(player.addLast)",
"addNext": "$t(player.addNext)",
"addToFavorites": "$t(action.addToFavorites)",
"addToPlaylist": "$t(action.addToPlaylist)",
"createPlaylist": "$t(action.createPlaylist)",
"deletePlaylist": "$t(action.deletePlaylist)",
"deselectAll": "$t(action.deselectAll)",
"moveItems": "$t(action.moveItems)",
"moveToNext": "$t(action.moveToNext)",
"moveToBottom": "$t(action.moveToBottom)",
"moveToTop": "$t(action.moveToTop)",
"play": "$t(player.play)",
"playSimilarSongs": "$t(player.playSimilarSongs)",
"removeFromFavorites": "$t(action.removeFromFavorites)",
"removeFromPlaylist": "$t(action.removeFromPlaylist)",
"removeFromQueue": "$t(action.removeFromQueue)",
"setRating": "$t(action.setRating)",
"playShuffled": "$t(player.shuffle)",
"goTo": "이동"
"showDetails": "추가정보"
},
"albumArtistDetail": {
"about": "{{artist}}에 대해",
@@ -479,13 +375,7 @@
"topSongs": "최고의 곡들",
"topSongsFrom": "{{title}}이 포함된 최고의 곡들",
"viewAll": "전부 보이기",
"viewAllTracks": "$t(entity.track, {\"count\": 2}) 전부 보이기",
"favoriteSongs": "좋아하는 노래들",
"groupingTypeAll": "모든 릴리스 유형",
"groupingTypePrimary": "주요 릴리스 유형",
"topSongsCommunity": "공동체",
"topSongsPersonal": "개인의",
"favoriteSongsFrom": "{{title}}에서 가장 좋아하는 곡들"
"viewAllTracks": "$t(entity.track, {\"count\": 2}) 전부 보이기"
},
"albumArtistList": {
"title": "$t(entity.albumArtist, {\"count\": 2})"
@@ -496,14 +386,11 @@
"released": "발매"
},
"albumList": {
"artistAlbums": "{{artist}}의 앨범",
"genreAlbums": "\"{{genre}}\" $t(entity.album, {\"count\": 2})",
"title": "$t(entity.album, {\"count\": 2})"
"artistAlbums": "{{artist}}의 앨범"
},
"genreList": {
"showAlbums": "$t(entity.genre, {\"count\": 1}) $t(entity.album, {\"count\": 2}) 표시",
"showTracks": "$t(entity.genre, {\"count\": 1}) $t(entity.track, {\"count\": 2}) 표시",
"title": "$t(entity.genre, {\"count\": 2})"
"showTracks": "$t(entity.genre, {\"count\": 1}) $t(entity.track, {\"count\": 2}) 표시"
},
"globalSearch": {
"commands": {
@@ -518,9 +405,7 @@
"mostPlayed": "자주 플레이된 곡",
"newlyAdded": "최근에 추가된 곡",
"recentlyPlayed": "최근에 플레이된 곡",
"recentlyReleased": "최근에 발매된 곡",
"genres": "$t(entity.genre, {\"count\": 2})",
"title": "$t(common.home)"
"recentlyReleased": "최근에 발매된 곡"
},
"itemDetail": {
"copyPath": "클립보드에 경로를 복사",
@@ -535,71 +420,15 @@
"generalTab": "일반",
"hotkeysTab": "단축키",
"playbackTab": "재생",
"windowTab": "윈도우",
"analytics": "해석학",
"updates": "업데이트",
"cache": "은닉처",
"application": "애플리케이션",
"queryBuilder": "쿼리 빌더",
"theme": "테마",
"controls": "통제 수단",
"sidebar": "사이드바",
"exportImport": "가져오기/내보내기",
"audio": "오디오",
"lyrics": "가사",
"lyricsDisplay": "가사 표시",
"transcoding": "트랜스코딩",
"discord": "Discord",
"logger": "로거",
"playerFilters": "선수 필터"
"windowTab": "윈도우"
},
"sidebar": {
"myLibrary": "내 라이브러리",
"nowPlaying": "재생중",
"shared": "공유 $t(entity.playlist, {\"count\": 2})",
"albumArtists": "$t(entity.albumArtist, {\"count\": 2})",
"albums": "$t(entity.album, {\"count\": 2})",
"collections": "컬렉션",
"artists": "$t(entity.artist, {\"count\": 2})",
"favorites": "$t(entity.favorite, {\"count\": 2})",
"folders": "$t(entity.folder, {\"count\": 2})",
"genres": "$t(entity.genre, {\"count\": 2})",
"home": "$t(common.home)",
"radio": "$t(entity.radioStation, {\"count\": 2})",
"playlists": "$t(entity.playlist, {\"count\": 2})",
"search": "$t(common.search)",
"settings": "$t(common.setting, {\"count\": 2})",
"tracks": "$t(entity.track, {\"count\": 2})"
"shared": "공유 $t(entity.playlist, {\"count\": 2})"
},
"trackList": {
"artistTracks": "{{artist}}의 음악",
"genreTracks": "\"{{genre}}\" $t(entity.track, {\"count\": 2})",
"title": "$t(entity.track, {\"count\": 2})"
},
"radioList": {
"title": "라디오 방송국"
},
"releasenotes": {
"commitsSinceStable": "{{stable}} 이후 커밋",
"noNewCommits": "이 범위에 새로운 커밋이 없습니다",
"noStableReleaseToCompare": "비교할 수 있는 안정화 릴리스가 없습니다"
},
"favorites": {
"title": "$t(entity.favorite, {\"count\": 2})"
},
"windowBar": {
"paused": "(일시 정지됨) ",
"privateMode": "(비공개 모드)"
},
"folderList": {
"title": "$t(entity.folder, {\"count\": 2})"
},
"playlistList": {
"title": "$t(entity.playlist, {\"count\": 2})"
},
"collections": {
"overrideExisting": "기존 항목 덮어쓰기",
"saveAsCollection": "컬렉션으로 저장"
"artistTracks": "{{artist}}의 음악"
}
},
"table": {
@@ -644,25 +473,7 @@
"toggleFullscreenPlayer": "전체화면으로 전환",
"unfavorite": "즐겨찾기 취소",
"pause": "멈춤",
"viewQueue": "대기열 보기",
"addLastShuffled": "마지막 (섞인)",
"addNextShuffled": "다음 (무작위)",
"albumRadio": "앨범 라디오",
"artistRadio": "아티스트 라디오",
"holdToShuffle": "길게 눌러 섞기",
"lyrics": "가사",
"restoreQueueFromServer": "서버에서 큐 복원",
"saveQueueToServer": "대기열을 서버에 저장",
"trackRadio": "라디오 추적",
"sleepTimer": "취침 타이머",
"sleepTimer_endOfSong": "현재 곡 종료",
"sleepTimer_endOfAlbum": "현재 앨범의 끝",
"sleepTimer_minutes": "{{count}}분",
"sleepTimer_hours": "{{count}}시간",
"sleepTimer_off": "끄다",
"sleepTimer_timeRemaining": "{{time}} 남음",
"sleepTimer_setCustom": "타이머 설정",
"sleepTimer_cancel": "타이머 취소"
"viewQueue": "대기열 보기"
},
"setting": {
"accentColor_description": "앱의 강조색상 설정",
@@ -671,7 +482,7 @@
"albumBackground": "앨범 배경이미지",
"albumBackgroundBlur_description": "앨범 배경이미지의 흐려짐 정도 조정",
"albumBackgroundBlur": "앨범배경이미지 흐려짐 크기",
"applicationHotkeys_description": "애플리케이션 단축키 설정합니다. 체크박스를 전환하여 전역 단축키 설정하세요(데스크톱 전용)",
"applicationHotkeys_description": "앱의 단축키 설정. 앱 전체에 적용되는 단축키 설정하기 위해서는 체크박스에 체크하세요(PC만 가능)",
"applicationHotkeys": "앱 단축키",
"artistBackground": "아티스트 배경이미지",
"artistBackground_description": "아티스트 페이지에 아티스트가 포함된 배경이미지를 추가",
@@ -681,7 +492,7 @@
"artistConfiguration_description": "앨범아티스트 페이지에 표시할 정보 및 순서 설정",
"audioDevice_description": "음악재생에 사용할 장치 선택(웹플레이어만 가능)",
"audioDevice": "오디오 장치",
"audioExclusiveMode_description": "독점 출력 모드를 활성화합니다. 이 모드에서는 일반적으로 시스템의 오디오 출력이 차단되며, 오직 mpv만이 오디오를 출력할 수 있습니다. 이 모드가 활성화된 동안에는 비주얼라이저의 시스템 오디오 캡처 기능이 작동하지 않습니다",
"audioExclusiveMode_description": "단독재생모드 켜기. 이 모드에서는 일반적으로 시스템의 재생장치가 고정되며 MPV로만 오디오가 재생됩니다",
"audioExclusiveMode": "오디오 단독재생모드",
"audioPlayer_description": "재생을 위한 오디오 플레이어 선택",
"audioPlayer": "오디오 플레이어",
@@ -694,8 +505,7 @@
"broadcast": "방송",
"ep": "ep앨범",
"other": "기타",
"single": "싱글",
"album": "$t(entity.album, {\"count\": 1})"
"single": "싱글"
},
"secondary": {
"audiobook": "오디오북",
@@ -711,35 +521,5 @@
"soundtrack": "사운드트랙",
"spokenWord": "보컬사운드"
}
},
"datetime": {
"minuteShort": "분",
"secondShort": "초",
"hourShort": "시간",
"dayShort": "일"
},
"filterOperator": {
"after": "~ 뒤에 있나요",
"afterDate": "(날짜) 이후입니까",
"before": "~보다 앞서 있다",
"beforeDate": "(날짜) 이전인가요",
"contains": "포함",
"endsWith": "~로 끝남",
"inPlaylist": "~ 안에 있다",
"inTheLast": "마지막에 있습니다",
"inTheRange": "범위 내에 있습니다",
"inTheRangeDate": "범위 내에 있음 (날짜)",
"is": "~이다",
"isNot": "~이 아닙니까",
"isGreaterThan": "~보다 크다",
"isLessThan": "~보다 작다",
"matchesRegex": "정규식과 일치",
"notContains": "함유하지 않음",
"notInPlaylist": "~ 안에 있지 않다",
"notInTheLast": "마지막에 있지 않다",
"startsWith": "~로 시작함"
},
"queryBuilder": {
"customTags": "사용자 정의 태그"
}
}
+4 -25
View File
@@ -3,9 +3,7 @@
"openIn": {
"lastfm": "Åpne i Last.fm",
"musicbrainz": "Åpne i MusicBrainz",
"spotify": "Åpne i Spotify",
"listenbrainz": "Åpne i ListenBrainz",
"qobuz": "Åpne i Qobuz"
"spotify": "Åpne i Spotify"
},
"moveToBottom": "Flytt til bunnen",
"deletePlaylist": "Slett $t(entity.playlist, {\"count\": 1})",
@@ -40,10 +38,7 @@
"shuffleAll": "Tilfelding avspilling av alt",
"shuffleSelected": "Tilfelding avspilling av utvalgte",
"viewMore": "Se mer",
"openApplicationDirectory": "Åpne applikasjonskatalogen",
"goToCurrent": "Gå til gjeldende element",
"collapseAllFolders": "Skjul alle mapper",
"expandAllFolders": "Utvid alle mapper"
"openApplicationDirectory": "Åpne applikasjonskatalogen"
},
"common": {
"bpm": "Bpm",
@@ -166,11 +161,7 @@
"tableColumns": "Tabellkolonner",
"itemsMore": "{{count}} fler",
"explicitStatus": "Grovhetsstatus",
"newVersionAvailable": "En ny version er tilgjengelig",
"back": "Tilbake",
"openFolder": "Åpne mappe",
"grouping": "Grupper",
"numberOfResults": "{{numberOfResults}} resultater"
"newVersionAvailable": "En ny version er tilgjengelig"
},
"entity": {
"smartPlaylist": "Smart $t(entity.playlist, {\"count\": 1})",
@@ -676,19 +667,7 @@
"b": "B",
"c": "C",
"d": "D",
"z": "Z",
"none": "Ingen"
},
"frequencyScale": {
"linear": "Lineær skala",
"log": "Logaritmisk skala"
},
"channelLayout": {
"single": "Enkel"
},
"gradient": {
"rainbow": "Regnbue",
"prism": "Prisme"
"z": "Z"
}
}
}
+1
View File
@@ -653,6 +653,7 @@
"globalMediaHotkeys_description": "Het gebruik van systeem mediahotkeys voor controle van afspelen aan-/uitzetten",
"globalMediaHotkeys": "Globale mediasneltoetsen",
"autoDJ": "Auto-DJ",
"autoDJ_description": "Soortgelijke nummers automatisch aan wachtrij toevoegen",
"autoDJ_itemCount": "Aantal items",
"autoDJ_itemCount_description": "Het aantal items dat aan de wachtrij wordt geprobeerd toe te voegen als auto-DJ is ingeschakeld",
"autoDJ_timing": "Timing",
+52 -89
View File
@@ -1,7 +1,7 @@
{
"action": {
"editPlaylist": "Edytuj $t(entity.playlist, {\"count\": 1})",
"goToPage": "Idź do strony",
"goToPage": "IDź do strony",
"clearQueue": "Wyczyść kolejkę",
"addToFavorites": "Dodaj do $t(entity.favorite, {\"count\": 2})",
"removeFromPlaylist": "Usuń z $t(entity.playlist, {\"count\": 1})",
@@ -41,9 +41,7 @@
"selectRangeOfItems": "Wybierz zakres elementów",
"selectAll": "Wybierz wszystkie",
"openApplicationDirectory": "Otwórz katalog aplikacji",
"goToCurrent": "Przejdź do aktualnego elementu",
"collapseAllFolders": "Zwiń wszystkie foldery",
"expandAllFolders": "Rozwiń wszystkie foldery"
"goToCurrent": "Przejdź do aktualnego elementu"
},
"common": {
"increase": "Zwiększ",
@@ -80,20 +78,20 @@
"cancel": "Anuluj",
"forceRestartRequired": "Zrestartuj aby zastosować zmiany... Zamknij powiadomienie aby zrestartować",
"setting_one": "Ustawienie",
"setting_few": "Ustawienia",
"setting_many": "Ustawień",
"setting_few": "ustawienia",
"setting_many": "ustawień",
"version": "Wersja",
"title": "Tytuł",
"filter_one": "Filtr",
"filter_few": "Filtry",
"filter_many": "Filtrów",
"filter_few": "filtry",
"filter_many": "filtrów",
"filters": "Filtry",
"create": "Stwórz",
"bitrate": "Bitrate",
"saveAndReplace": "Zapisz i zamień",
"action_one": "Akcja",
"action_few": "Akcje",
"action_many": "Akcji",
"action_few": "akcje",
"action_many": "akcji",
"playerMustBePaused": "Odtwarzacz musi być zapauzowany",
"confirm": "Potwierdź",
"resetToDefault": "Przywróć do domyślnych",
@@ -101,8 +99,8 @@
"comingSoon": "Już wkrótce…",
"reset": "Zresetuj",
"channel_one": "Kanał",
"channel_few": "Kanałów",
"channel_many": "Kanałów",
"channel_few": "kanałów",
"channel_many": "kanałów",
"disable": "Wyłącz",
"sortOrder": "Kolejność",
"none": "Żaden",
@@ -172,32 +170,30 @@
"rename": "Zmień nazwę",
"newVersionAvailable": "Nowa wersja jest dostępna",
"numberOfResults": "{{numberOfResults}} wyników",
"grouping": "Grupowanie",
"back": "Wstecz",
"openFolder": "Otwórz folder"
"grouping": "Grupowanie"
},
"entity": {
"genre_one": "Gatunek",
"genre_few": "Gatunki",
"genre_many": "Gatunków",
"genre_few": "gatunki",
"genre_many": "gatunków",
"playlistWithCount_one": "{{count}} playlista",
"playlistWithCount_few": "{{count}} playlisty",
"playlistWithCount_many": "{{count}} playlist",
"playlist_one": "Playlista",
"playlist_few": "Playlisty",
"playlist_many": "Playlist",
"playlist_few": "playlisty",
"playlist_many": "playlist",
"artist_one": "Wykonawca",
"artist_few": "Wykonawców",
"artist_many": "Wykonawców",
"artist_few": "wykonawcy",
"artist_many": "wykonawców",
"folderWithCount_one": "{{count}} katalog",
"folderWithCount_few": "{{count}} katalogi",
"folderWithCount_many": "{{count}} katalogów",
"albumArtist_one": "Wykonawca albumu",
"albumArtist_few": "Wykonawców albumów",
"albumArtist_many": "Wykonawców albumów",
"albumArtist_few": "wykonawcy albumu",
"albumArtist_many": "wykonawcy albumów",
"track_one": "Utwór",
"track_few": "Utwory",
"track_many": "Utworów",
"track_few": "utwory",
"track_many": "utworów",
"albumArtistCount_one": "{{count}} wykonawca albumu",
"albumArtistCount_few": "{{count}} wykonawców albumu",
"albumArtistCount_many": "{{count}} wykonawców albumu",
@@ -205,18 +201,18 @@
"albumWithCount_few": "{{count}} albumy",
"albumWithCount_many": "{{count}} albumów",
"favorite_one": "Ulubiony",
"favorite_few": "Ulubione",
"favorite_many": "Ulubionych",
"favorite_few": "ulubione",
"favorite_many": "ulubionych",
"artistWithCount_one": "{{count}} wykonawca",
"artistWithCount_few": "{{count}} wykonawców",
"artistWithCount_many": "{{count}} wykonawców",
"folder_one": "Katalog",
"folder_few": "Katalogi",
"folder_many": "Katalogów",
"folder_few": "katalogi",
"folder_many": "katalogów",
"smartPlaylist": "Inteligentna $t(entity.playlist, {\"count\": 1})",
"album_one": "Album",
"album_few": "Albumy",
"album_many": "Albumów",
"album_few": "albumy",
"album_many": "albumów",
"genreWithCount_one": "{{count}} gatunek",
"genreWithCount_few": "{{count}} gatunki",
"genreWithCount_many": "{{count}} gatunków",
@@ -227,11 +223,11 @@
"play_few": "{{count}} odtworzenia",
"play_many": "{{count}} odtworzeń",
"song_one": "Piosenka",
"song_few": "Piosenki",
"song_many": "­Piosenek",
"song_few": "piosenki",
"song_many": "­piosenek",
"radioStation_one": "Stacja radiowa",
"radioStation_few": "Stacje radiowe",
"radioStation_many": "Stacji radiowych",
"radioStation_few": "stacje radiowe",
"radioStation_many": "stacji radiowych",
"radioStationWithCount_one": "{{count}} stacja radiowa",
"radioStationWithCount_few": "{{count}} stacje radiowych",
"radioStationWithCount_many": "{{count}} stacji radiowych"
@@ -410,12 +406,7 @@
"input_played": "Filtr odtwarzania",
"input_played_optionAll": "Wszystkie utwory",
"input_played_optionUnplayed": "Tylko nieodtworzone utwory",
"input_played_optionPlayed": "Tylko odtworzone utwory",
"input_kind_albums": "Albumy",
"input_kind_songs": "Piosenki",
"input_kind": "Losowy wybór",
"input_limit_albums": "Ile albumów?",
"input_limit_songs": "Ile piosenek?"
"input_played_optionPlayed": "Tylko odtworzone utwory"
},
"saveQueue": {
"success": "Zapisano kolejkę odtwarzania na serwerze"
@@ -699,9 +690,7 @@
"sleepTimer_timeRemaining": "Pozostało {{time}}",
"sleepTimer_setCustom": "Ustaw wyłącznik",
"sleepTimer_cancel": "Anuluj wyłączanie",
"albumRadio": "Radio albumu",
"scrobbleForceSubmit": "Wymuś scrobble",
"sleepTimer_endOfAlbum": "Koniec aktualnego albumu"
"albumRadio": "Radio albumu"
},
"setting": {
"crossfadeStyle_description": "Wybierz styl przenikania, który ma być używany do odtwarzania dźwięku",
@@ -890,7 +879,7 @@
"customCssEnable": "Włącz niestandardowy CSS",
"customCssEnable_description": "Pozwalaj na pisanie niestandardowego CSS",
"customCssNotice": "Ostrzeżenie: chociaż istnieje pewne filtrowanie (uniemożliwia używanie URL() i content:), używanie niestandardowego CSS-a może stwarzać ryzyko przez zmiany w interfejsie",
"customCss_description": "Zawartość niestandardowego CSS. Uwaga: content i zdalne URL są niedozwolonymi właściwościami. Podgląd twojej zawartości jest pokazany poniżej. Dodatkowe pola których nie ustawiłeś są obecne z powodu sanityzacji. Aplikacja komputerowa: feishin odczytuje i zapisuje custom.css w katalogu ustawień aplikacji i przeładowuje go gdy plik się zmieni",
"customCss_description": "Zawartość niestandardowego CSS. Uwaga: content i zdalne URL są niedozwolonymi właściwościami. Podgląd twojej zawartości jest pokazana poniżej. Dodatkowe pola których nie ustawiłeś, są obecne z powodu sanityzacji",
"customCss": "Niestandardowy css",
"trayEnabled_description": "Pokaż/ukryj ikonę/menu w zasobniku. jeżeli wyłączone, wyłącza też minimalizowanie.wyjście do zasobnika",
"webAudio_description": "Używaj web audio. Włącza to zaawansowane funkcje takie jak ReplayGain. Wyłącz jeżeli nie działa poprawnie",
@@ -898,7 +887,7 @@
"playButtonBehavior_optionPlayShuffled": "$t(player.shuffle)",
"playerbarOpenDrawer_description": "Pozwala przełączyć na odtwarzacz pełnoekranowy po kliknięciu paska odtwarzania",
"playerbarOpenDrawer": "Przełącznik pełnego ekranu na pasku odtwarzania",
"imageAspectRatio": "Używaj natywnych proporcji okładki",
"imageAspectRatio": "Używaj natywnych proporcji OKładki",
"volumeWidth": "Szerokość paska głośności",
"discordListening": "Pokazuj status jako słucha",
"imageAspectRatio_description": "Jeżeli włączone, okładka będzie pokazywana z użyciem jej natywnych proporcji. dla okładek które nie mają proporcji 1:1, pozostałe miejsce będzie puste",
@@ -923,7 +912,7 @@
"musicbrainz_description": "Pokazuj linki do MusicBrainz na stronach wykonawców/albumów, gdzie istnieje MusicBrainz ID",
"discordPausedStatus": "Pokaż status podczas pauzy",
"discordServeImage": "Wysyłaj obrazy dla {{discord}} z serwera",
"discordServeImage_description": "Pokazuj okładki w statusie {{discord}} prosto z serwera, dostępne tylko dla Jellyfin i Navidrome. {{discord}} używa bota do pobierania obrazów, więc twój serwer musi być dostępny publicznie w internecie",
"discordServeImage_description": "Pokazuj OKładki w statusie {{discord}} prosto z serwera, dostępne tylko dla Jellyfin i Navidrome. {{discord}} używa bota do pobierania obrazów, więc twój serwer musi być dostępny publicznie w internecie",
"analyticsDisable": "Zrezygnuj z analityki bazowanej na użytkowaniu",
"analyticsDisable_description": "Zanonymizowane dane użytkowania są wysyłane do dewelopera w celu poprawienia aplikacji",
"artistBackground": "Obraz tła wykonawcy",
@@ -980,7 +969,7 @@
"preservePitch": "Utrzymuj ton",
"preventSleepOnPlayback_description": "Powstrzymuje ekran przed uśpieniem, gdy muzyka jest odtwarzana",
"preventSleepOnPlayback": "Powstrzymuj uśpienie podczas odtwarzania",
"mediaSession_description": "Włącza integrację z Media Session, wyświetlając sterowanie mediami i metadane w systemowym oknie zmiany głośności i na ekranie blokad. Wymaga odtwarzacza web audio.",
"mediaSession_description": "Włącza integrację z Media Session, wyświetlając sterowanie mediami i metadane w systemowym oknie zmiany głośności i na ekranie blokady",
"mediaSession": "Włącz media session",
"transcode": "Włącz transkodowanie",
"queryBuilder": "Kreator zaptań",
@@ -989,15 +978,16 @@
"queryBuilderCustomFields": "Niestandardowe pola",
"queryBuilderCustomFields_description": "Dodaj niestandardowe pola do użycia w kreatorach zapytań",
"followCurrentSong_description": "Automatycznie przewija kolejkę odtwarzania do aktualnie odtwarzanej piosenki",
"followCurrentSong": ledź aktualną piosenkę",
"followCurrentSong": LEDź aktualną piosenkę",
"playerFilters": "Filtruj piosenki z kolejki",
"playerFilters_description": "Nie dodawaj piosenek do kolejki na podstawie poniższych kryteriów",
"playerbarSlider_description": "Krzywe nie są zalecane w przypadku wolnego lub ograniczonego połączenia internetowego",
"audioFadeOnStatusChange": "Przenikanie dźwięku przy zmianie statusu",
"audioFadeOnStatusChange_description": "Umożliwia zanikanie lub pojawianie się dźwięku gdy zmieni się status play/pauza",
"autoDJ": "Automatyczny DJ",
"autoDJ_description": "Automatycznie dodawaj podobne piosenki do kolejki",
"autoDJ_itemCount": "Liczba elementów",
"autoDJ_itemCount_description": "Liczba elementów, które będzie próbować dodać do kolejki",
"autoDJ_itemCount_description": "Liczba elementów, które będzie próbować dodać do kolejki kiedy automatyczny DJ jest włączony",
"autoDJ_timing": "Czas dodawania",
"autoDJ_timing_description": "Ilość piosenek pozostałych w kolejce przed tym gdy zostanie włączony automatyczny DJ",
"logLevel": "Poziom logów",
@@ -1014,7 +1004,7 @@
"imageResolution_description": "Rozdzielczość dla obrazów używanych w programie. użycie wartości 0 ustawi rozdzielczość na natywną",
"imageResolution_optionTable": "Tabela",
"imageResolution_optionItemCard": "Karta elementu",
"imageResolution_optionSidebar": "Pasek boczny",
"imageResolution_optionSidebar": "­pasek boczny",
"imageResolution_optionHeader": "Nagłówek",
"imageResolution_optionFullScreenPlayer": "Odtwarzacz pełnoekranowy",
"combinedLyricsAndVisualizer_description": "Połącz tekst i wizualizacje w tym samym panelu",
@@ -1081,34 +1071,7 @@
"playerbarWaveformStretch": "Rozciąganie przebiegu",
"playerbarWaveformStretch_description": "Rozciąga przebieg aby wypełnić dostępną przestrzeń",
"preventSuspendOnPlayback_description": "Powstrzymuj wstrzymanie podczas odtwarzania muzyki",
"preventSuspendOnPlayback": "Powstrzymuje wstrzymanie przy odtwarzaniu",
"hotkey_listShowPlayingSong": "Pokaż odtwarzaną piosenkę w liście",
"sidebarPlaylistFolders_description": "Utwórz widok folderu dla playlist który zawiera skonfigurowany separator w nazwie",
"sidebarPlaylistFolders": "Włącz foldery",
"sidebarPlaylistFolderSeparator_description": "Znak (lub ciąg) który oddziela poziomy folderów w nazwie playlisty",
"sidebarPlaylistFolderSeparator": "Separator folderów",
"sidebarPlaylistFolderView_description": "Jak foldery są wyświetlane w pasku bocznym",
"sidebarPlaylistFolderView": "Widok folderów",
"sidebarPlaylistFolderView_optionSingle": "Pojedynczy folder",
"sidebarPlaylistFolderView_optionTree": "Widok drzewa",
"sidebarPlaylistFolderView_optionNavigation": "Widok nawigacji",
"sidebarPlaylistFolderTreeIndent_description": "Piksele na każdym poziomie drzewa mają wcięcie",
"sidebarPlaylistFolderTreeIndent": "Wcięcie drzewa",
"sidebarPlaylistFolderTreeLineColor_description": "Kolor linii łączących drzewo (pozostaw pusty dla domyślnego dla motywu)",
"sidebarPlaylistFolderTreeLineColor": "Kolor linii drzewa",
"sidebarPlaylistMode_description": "Jak każda z playlist jest wyświetlana w liście w pasku bocznym",
"sidebarPlaylistMode": "Tryb playlist bocznego paska",
"sidebarPlaylistMode_optionCompact": "Kompaktowy",
"sidebarPlaylistMode_optionExpanded": "Rozszerzony",
"autoDJ_mode": "Tryb",
"autoDJ_mode_albums": "Albumy",
"autoDJ_mode_description": "Wybierz dodawanie piosenek lub całych albumów do kolejki",
"autoDJ_mode_songs": "Piosenki",
"autoDJ_enabled": "Włącz Auto DJ",
"autoDJ_albumStrategy": "Tryb wyboru albumów",
"autoDJ_songStrategy": "Tryb wyboru piosenek",
"autoDJ_strategy_option_library_random": "Losowo",
"autoDJ_strategy_option_similar": "Podobne"
"preventSuspendOnPlayback": "Powstrzymuje wstrzymanie przy odtwarzaniu"
},
"table": {
"config": {
@@ -1126,7 +1089,7 @@
"size": "$t(common.size)",
"itemSize": "Rozmiar elementu (px)",
"itemGap": "Odstęp między elementami (px)",
"followCurrentSong": ledź aktualną piosenkę",
"followCurrentSong": LEDź aktualną piosenkę",
"advancedSettings": "Zaawansowane ustawienia",
"autosize": "Rozmiar automatyczny",
"moveUp": "Przesuń w górę",
@@ -1195,7 +1158,7 @@
"rating": "Ocena",
"favorite": "Ulubione",
"playCount": "Odtwarzane",
"albumCount": "Albumy",
"albumCount": "$t(entity.album, {\"count\": 2})",
"releaseYear": "Rok",
"lastPlayed": "Ostatnio odtwarzane",
"biography": "Biografia",
@@ -1204,19 +1167,19 @@
"title": "Tytuł",
"bpm": "BPM",
"dateAdded": "Data dodania",
"artist": "Wykonawca",
"songCount": "Utwory",
"artist": "$t(entity.artist, {\"count\": 1})",
"songCount": "$t(entity.track, {\"count\": 2})",
"trackNumber": "Utwór",
"genre": "Gatunek",
"genre": "$t(entity.genre, {\"count\": 1})",
"albumArtist": "Wykonawca albumu",
"path": "Ścieżka",
"discNumber": "Płyta",
"channels": "Kanały",
"size": "Rozmiar",
"codec": "Kodek",
"channels": "$t(common.channel, {\"count\": 2})",
"size": "$t(common.size)",
"codec": "$t(common.codec)",
"owner": "Właściciel",
"bitDepth": "Głębia bitowa",
"sampleRate": "Częstotliwość próbkowania"
"bitDepth": "$t(common.bitDepth)",
"sampleRate": "$t(common.sampleRate)"
}
},
"queryBuilder": {
+9 -9
View File
@@ -764,31 +764,31 @@
},
"entity": {
"albumArtist_one": "Artista do álbum",
"albumArtist_many": "Artistas do álbum",
"albumArtist_many": "artistas do álbum",
"albumArtist_other": "Artistas do álbum",
"albumArtistCount_one": "{{count}} artista do álbum",
"albumArtistCount_many": "{{count}} artistas do álbum",
"albumArtistCount_other": "{{count}} artistas do álbum",
"album_one": "Álbum",
"album_many": "Álbuns",
"album_many": "álbuns",
"album_other": "Álbuns",
"artist_one": "Artista",
"artist_many": "Artistas",
"artist_many": "artistas",
"artist_other": "Artistas",
"albumWithCount_one": "{{count}} álbum",
"albumWithCount_many": "{{count}} álbuns",
"albumWithCount_other": "{{count}} álbuns",
"favorite_one": "Favorito",
"favorite_many": "Favoritos",
"favorite_many": "favoritos",
"favorite_other": "Favoritos",
"artistWithCount_one": "{{count}} artista",
"artistWithCount_many": "{{count}} artistas",
"artistWithCount_other": "{{count}} artistas",
"folder_one": "Pasta",
"folder_many": "Pastas",
"folder_many": "pastas",
"folder_other": "Pastas",
"genre_one": "Gênero",
"genre_many": "Gêneros",
"genre_many": "gêneros",
"genre_other": "Gêneros",
"playlistWithCount_one": "{{count}} playlist",
"playlistWithCount_many": "{{count}} playlists",
@@ -806,11 +806,11 @@
"trackWithCount_many": "{{count}} faixas",
"trackWithCount_other": "{{count}} faixas",
"track_one": "Faixa",
"track_many": "Faixas",
"track_many": "faixas",
"track_other": "Faixas",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) Inteligente",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) inteligente",
"song_one": "Música",
"song_many": "Músicas",
"song_many": "músicas",
"song_other": "Músicas",
"play_one": "{{count}} reprodução",
"play_many": "{{count}} reproduções",
+27 -358
View File
@@ -20,25 +20,8 @@
"viewPlaylists": "Ver $t(entity.playlist, {\"count\": 2})",
"openIn": {
"lastfm": "Abrir em Last.fm",
"musicbrainz": "Abrir em MusicBrainz",
"listenbrainz": "Abrir no ListenBrainz",
"qobuz": "Abrir no Qobuz",
"spotify": "Abrir no Spotify"
},
"addOrRemoveFromSelection": "Adicionar ou remover da seleção",
"goToCurrent": "Ir para o elemento atual",
"collapseAllFolders": "Colapsar todas as pastas",
"expandAllFolders": "Expandir todas as pastas",
"createRadioStation": "Criar $t(entity.radioStation, {\"count\": 1})",
"deleteRadioStation": "Apagar $t(entity.radioStation, {\"count\": 1})",
"selectAll": "Selecionar tudo",
"moveUp": "Mover para cima",
"moveDown": "Mover para baixo",
"holdToMoveToTop": "Segure para ir ao topo",
"holdToMoveToBottom": "Mover para ir ao fundo",
"moveItems": "Mover elementos",
"viewMore": "Ver mais",
"openApplicationDirectory": "Abrir a pasta da aplicação"
"musicbrainz": "Abrir em MusicBrainz"
}
},
"common": {
"action_one": "Ação",
@@ -139,28 +122,14 @@
"unknown": "Desconhecido",
"version": "Versão",
"year": "Ano",
"yes": "Sim",
"countSelected": "{{count}} selecionado",
"bitDepth": "Profundidade de bits",
"example": "Exemplo",
"externalLinks": "Ligações externas",
"mood": "Humor",
"private": "Privado",
"public": "Público",
"retry": "Tentar novamente",
"rename": "Renomear",
"sampleRate": "Taxa de amostragem",
"sort": "Ordenar",
"clean": "Limpar",
"itemsMore": "{{count}} mais",
"newVersionAvailable": "Uma nova versão está disponível"
"yes": "Sim"
},
"entity": {
"album_one": "Álbum",
"album_many": "Álbuns",
"album_many": "álbuns",
"album_other": "Álbuns",
"albumArtist_one": "Artista do álbum",
"albumArtist_many": "Artistas do álbum",
"albumArtist_many": "artistas do álbum",
"albumArtist_other": "Artistas do álbum",
"albumArtistCount_one": "{{count}} artista do álbum",
"albumArtistCount_many": "{{count}} artistas do álbum",
@@ -169,28 +138,28 @@
"albumWithCount_many": "{{count}} álbuns",
"albumWithCount_other": "{{count}} álbuns",
"artist_one": "Artista",
"artist_many": "Artistas",
"artist_many": "artistas",
"artist_other": "Artistas",
"artistWithCount_one": "{{count}} artista",
"artistWithCount_many": "{{count}} artistas",
"artistWithCount_other": "{{count}} artistas",
"favorite_one": "Favorito",
"favorite_many": "Favoritos",
"favorite_many": "favoritos",
"favorite_other": "Favoritos",
"folder_one": "Pasta",
"folder_many": "Pastas",
"folder_many": "pastas",
"folder_other": "Pastas",
"folderWithCount_one": "{{count}} pasta",
"folderWithCount_many": "{{count}} pastas",
"folderWithCount_other": "{{count}} pastas",
"genre_one": "Gênero",
"genre_many": "Gêneros",
"genre_many": "gêneros",
"genre_other": "Gêneros",
"genreWithCount_one": "{{count}} gênero",
"genreWithCount_many": "{{count}} gêneros",
"genreWithCount_other": "{{count}} gêneros",
"playlist_one": "Playlist",
"playlist_many": "Playlists",
"playlist_many": "playlists",
"playlist_other": "Playlists",
"play_one": "{{count}} reprodução",
"play_many": "{{count}} reproduções",
@@ -198,12 +167,12 @@
"playlistWithCount_one": "{{count}} playlist",
"playlistWithCount_many": "{{count}} playlists",
"playlistWithCount_other": "{{count}} playlists",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) Inteligente",
"smartPlaylist": "$t(entity.playlist, {\"count\": 1}) inteligente",
"track_one": "Faixa",
"track_many": "Faixas",
"track_many": "faixas",
"track_other": "Faixas",
"song_one": "Música",
"song_many": "Músicas",
"song_many": "músicas",
"song_other": "Músicas",
"trackWithCount_one": "{{count}} faixa",
"trackWithCount_many": "{{count}} faixas",
@@ -232,9 +201,7 @@
"serverNotSelectedError": "Nenhum servidor selecionado",
"serverRequired": "Servidor necessário",
"sessionExpiredError": "A sua sessão expirou",
"systemFontError": "Ocorreu um erro ao tentar obter fontes do sistema",
"invalidJson": "JSON inválido",
"noNetwork": "Servidor não disponível"
"systemFontError": "Ocorreu um erro ao tentar obter fontes do sistema"
},
"filter": {
"album": "$t(entity.album, {\"count\": 1})",
@@ -278,10 +245,7 @@
"songCount": "Contador de músicas",
"title": "Titulo",
"toYear": "Até o ano",
"trackNumber": "Faixa",
"matchAnd": "E",
"matchOr": "Ou",
"sortName": "Ordenar por nome"
"trackNumber": "Faixa"
},
"form": {
"addServer": {
@@ -295,8 +259,7 @@
"input_url": "Url",
"input_username": "Nome de utilizador",
"success": "Servidor adicionado com sucesso",
"title": "Adicionar servidor",
"input_remoteUrl": "URL público"
"title": "Adicionar servidor"
},
"addToPlaylist": {
"input_playlists": "$t(entity.playlist, {\"count\": 2})",
@@ -329,9 +292,7 @@
},
"queryEditor": {
"input_optionMatchAll": "Corresponder todos",
"input_optionMatchAny": "Corresponder qualquer um",
"resetToDefault": "Restaurar à predefinição",
"clearFilters": "Limpar filtros"
"input_optionMatchAny": "Corresponder qualquer um"
},
"shareItem": {
"allowDownloading": "Permitir descargas",
@@ -344,21 +305,6 @@
"updateServer": {
"success": "Servidor atualizado com sucesso",
"title": "Atualizar servidor"
},
"createRadioStation": {
"title": "Criar estação de rádio",
"input_name": "Nome"
},
"lyricsExport": {
"input_synced": "Exportar letras sincronizadas"
},
"shuffleAll": {
"title": "Tocar aleatório",
"input_minYear": "A partir do ano",
"input_maxYear": "Até o ano"
},
"privateMode": {
"title": "Modo Privado"
}
},
"page": {
@@ -371,9 +317,7 @@
"topSongs": "Músicas mais tocadas",
"topSongsFrom": "Músicas mais tocadas de {{title}}",
"viewAll": "Ver tudo",
"viewAllTracks": "Ver todas as $t(entity.track, {\"count\": 2})",
"topSongsCommunity": "Comunidade",
"topSongsPersonal": "Pessoal"
"viewAllTracks": "Ver todas as $t(entity.track, {\"count\": 2})"
},
"albumArtistList": {
"title": "$t(entity.albumArtist, {\"count\": 2})"
@@ -430,8 +374,7 @@
"setRating": "$t(action.setRating)",
"playShuffled": "$t(player.shuffle)",
"shareItem": "Partilhar elemento",
"showDetails": "Obter informações",
"goTo": "Ir para"
"showDetails": "Obter informações"
},
"fullscreenPlayer": {
"config": {
@@ -474,8 +417,7 @@
"mostPlayed": "Mais tocado",
"newlyAdded": "Lançamentos recém-adicionados",
"recentlyPlayed": "Tocado recentemente",
"title": "$t(common.home)",
"genres": "$t(entity.genre, {\"count\": 2})"
"title": "$t(common.home)"
},
"itemDetail": {
"copyPath": "Copiar caminho para a área de transferência",
@@ -493,18 +435,7 @@
"generalTab": "Geral",
"hotkeysTab": "Teclas de atalho",
"playbackTab": "Reprodução",
"windowTab": "Janela",
"application": "Aplicação",
"queryBuilder": "Construtor de Consultas",
"theme": "Tema",
"controls": "Controles",
"sidebar": "Barra lateral",
"remote": "Remoto",
"exportImport": "Importar/exportar",
"audio": "Áudio",
"lyrics": "Letras",
"transcoding": "Transcodificar",
"discord": "Discord"
"windowTab": "Janela"
},
"sidebar": {
"albumArtists": "$t(entity.albumArtist, {\"count\": 2})",
@@ -519,19 +450,12 @@
"search": "$t(common.search)",
"settings": "$t(common.setting, {\"count\": 2})",
"shared": "$t(entity.playlist, {\"count\": 2}) partilhada",
"tracks": "$t(entity.track, {\"count\": 2})",
"collections": "Coleções"
"tracks": "$t(entity.track, {\"count\": 2})"
},
"trackList": {
"artistTracks": "Faixas de {{artist}}",
"genreTracks": "\"{{genre}}\" $t(entity.track, {\"count\": 2})",
"title": "$t(entity.track, {\"count\": 2})"
},
"radioList": {
"title": "Estações de rádio"
},
"folderList": {
"title": "$t(entity.folder, {\"count\": 2})"
}
},
"player": {
@@ -565,11 +489,7 @@
"toggleFullscreenPlayer": "Alternar player de ecrã cheio",
"unfavorite": "Remover favorito",
"pause": "Pausar",
"viewQueue": "Ver fila",
"lyrics": "Letra",
"sleepTimer_minutes": "{{count}} min",
"sleepTimer_hours": "{{count}} hr",
"sleepTimer_off": "Desligado"
"viewQueue": "Ver fila"
},
"setting": {
"accentColor": "Cor de realce",
@@ -608,269 +528,18 @@
"discordApplicationId": "{{discord}} ID da aplicação",
"discordIdleStatus_description": "Quando ativado, atualiza o estado enquanto o player está ocioso",
"discordUpdateInterval_description": "O tempo em segundos entre cada atualização (mínimo 15 segundos)",
"playButtonBehavior_description": "Define o comportamento padrão do botão play ao adicionar músicas à fila",
"autoDJ_itemCount": "Número de elementos",
"autoDJ_timing": "Tempo",
"customCss_description": "Conteúdo CSS personalizado. Observação: conteúdo e urls remotas são propriedades não permitidas. Uma pré-visualização do seu conteúdo é exibida abaixo. Campos adicionais que não definiu estão presentes devido à sanitização",
"automaticUpdates": "Atualizações automáticas",
"releaseChannel_optionBeta": "Beta",
"releaseChannel_optionLatest": "Mais recente",
"discordApplicationId_description": "O ID da aplicação para o rich presence do {{discord}} (defaults: {{defaultId}})",
"discordDisplayType_artistname": "Nome(s) do(s) artista(s)",
"discordDisplayType": "Tipo de exibição da presença do {{discord}}",
"discordIdleStatus": "Mostrar estado ocioso do rich presence",
"discordLinkType_mbz_lastfm": "{{musicbrainz}} com alternativa para {{lastfm}}",
"discordLinkType_none": "$t(common.none)",
"discordLinkType": "Ligações de presença do {{discord}}",
"discordServeImage_description": "Partilhar a capa para o rich presence do {{discord}} a partir do próprio servidor, disponível apenas para Jellyfin e Navidrome. O {{discord}} usa um bot para buscar imagens, portanto o seu servidor deve estar acessível pela internet pública",
"discordUpdateInterval": "Intervalo de atualização do rich presence do {{discord}}",
"exportImportSettings_control_importText": "Importar configurações",
"exportImportSettings_control_title": "Importar / exportar configurações",
"exportImportSettings_importBtn": "Importar configurações",
"exportImportSettings_importModalTitle": "Importar configurações do Feishin",
"externalLinks": "Mostrar ligações externas",
"font": "Fonte",
"fontType_optionBuiltIn": "Fonte embutida",
"fontType_optionCustom": "Fonte personalizada",
"fontType_optionSystem": "Fonte do sistema",
"fontType": "Tipo da fonte",
"homeFeatureStyle_optionMultiple": "Múltiplos",
"hotkey_globalSearch": "Pequisa global",
"hotkey_playbackPause": "Pausar",
"hotkey_playbackPlay": "Tocar",
"hotkey_playbackPlayPause": "Play / pausar",
"hotkey_playbackStop": "Parar",
"hotkey_volumeMute": "Volume mudo",
"hotkey_volumeUp": "Aumentar volume",
"hotkey_zoomIn": "Aproximar",
"hotkey_zoomOut": "Afastar",
"imageAspectRatio_description": "Se ativado, a capa será exibida usando a sua proporção nativa. Para capas que não forem 1:1, o espaço restante ficará vazio",
"imageAspectRatio": "Usar proporção nativa da capa",
"language_description": "Define o idioma da aplicação ($t(common.restartRequired))",
"lastfm_description": "Exibir ligações para o Last.fm nas páginas de artista/álbum",
"lastfm": "Mostrar ligações do Last.fm",
"lastfmApiKey_description": "A chave de API para {{lastfm}}. Necessária para capas de álbuns",
"lastfmApiKey": "{{lastfm}} chave API",
"lyricFetch_description": "Buscar letras em várias fontes da internet",
"lyricFetch": "Buscar letras na internet",
"lyricFetchProvider": "Provedores para buscar letras",
"lyricOffset_description": "Compensar a letra pelo valor especificado em milissegundos",
"lyricOffset": "Compensação da letra (ms)",
"minimizeToTray_description": "Minimizar a aplicação para a bandeja do sistema",
"minimizeToTray": "Minimizar para a bandeja",
"minimumScrobblePercentage_description": "O percentual mínimo da música que deve ser reproduzido antes de ser scrobblada",
"minimumScrobblePercentage": "Duração mínima para scrobble (percentual)",
"minimumScrobbleSeconds_description": "A duração mínima em segundos da música que deve ser reproduzida antes de ser scrobblada",
"minimumScrobbleSeconds": "Scrobble mínimo (segundos)",
"mpvExecutablePath": "Caminho do executável do mpv",
"mpvExtraParameters_help": "Um por linha",
"musicbrainz_description": "Exibir ligações para o MusicBrainz nas páginas de artista/álbum, quando o ID do MusicBrainz existir",
"musicbrainz": "Mostrar ligações do MusicBrainz",
"neteaseTranslation_description": "Quando ativado, busca e exibe letras traduzidas do NetEase, se disponíveis",
"neteaseTranslation": "Ativar traduções do NetEase",
"passwordStore_description": "Qual armazenamento de palavras-passe/segredos usar. Altere isto se tem problemas para armazenar palavras-passe",
"playbackStyle_description": "Selecione o estilo de reprodução a ser usado pelo reprodutor de áudio",
"playbackStyle_optionCrossFade": "Transição suave",
"playbackStyle_optionNormal": "Normal",
"playbackStyle": "Estilo de reprodução",
"playButtonBehavior_optionAddLast": "$t(player.addLast)",
"playButtonBehavior_optionAddNext": "$t(player.addNext)",
"playButtonBehavior_optionPlay": "$t(player.play)",
"playButtonBehavior_optionPlayShuffled": "$t(player.shuffle)",
"playButtonBehavior": "Comportamento do botão de reprodução",
"imageResolution_optionSidebar": "Barra lateral",
"imageResolution_optionHeader": "Cabeçalho",
"imageResolution_optionFullScreenPlayer": "Reprodutor de ecrã cheio",
"playerbarOpenDrawer_description": "Permite clicar na barra do reprodutor para abrir o reprodutor em ecrã cheio",
"playerbarOpenDrawer": "Alternar ecrã cheio na barra do reprodutor",
"playerbarSliderType_optionWaveform": "Forma de onda",
"playerbarWaveformAlign_optionTop": "Topo",
"playerbarWaveformAlign_optionCenter": "Centro",
"playerbarWaveformAlign_optionBottom": "Fundo",
"showRatings_description": "Exibir ou ocultar as avaliações por estrelas",
"showRatings": "Exibir avaliações por estrelas",
"remotePassword_description": "Define a palavra-passe do servidor de controlo remoto. Estas credenciais, por padrão, são transferidas de forma insegura — use uma palavra-passe única da qualnão dependa",
"remotePort": "Porta do servidor de controlo remoto",
"replayGainClipping_description": "Evitar clipping causado pelo {{ReplayGain}} reduzindo automaticamente o ganho",
"replayGainClipping": "Clipping do {{ReplayGain}}",
"replayGainFallback": "Fallback do {{ReplayGain}}",
"replayGainMode_optionAlbum": "$t(entity.album, {\"count\": 1})",
"replayGainMode_optionNone": "$t(common.none)",
"replayGainMode_optionTrack": "$t(entity.track, {\"count\": 1})",
"replayGainMode": "Modo {{ReplayGain}}",
"replayGainPreamp": "Pré-amplificador {{ReplayGain}} (db)",
"sampleRate": "Taxa de amostragem",
"sidebarPlaylistFolderView_optionNavigation": "Vista de navegação",
"sidebarPlaylistMode_optionCompact": "Compacto",
"sidebarPlaylistMode_optionExpanded": "Expandido",
"sidePlayQueueStyle_optionAttached": "Anexado",
"sidePlayQueueLayout_optionHorizontal": "Horizontal",
"sidePlayQueueLayout_optionVertical": "Vertical",
"startMinimized": "Abrir minimizado",
"theme": "Tema",
"themeDark": "Tema (escuro)",
"transcode": "Ativar transcodificação",
"translationTargetLanguage": "Idioma de destino para tradução",
"useSystemTheme": "Usar tema do sistema",
"queryBuilderCustomFields_inputLabel": "Etiqueta"
"playButtonBehavior_description": "Define o comportamento padrão do botão play ao adicionar músicas à fila"
},
"table": {
"column": {
"discNumber": "Disco",
"size": "Tamanho",
"title": "Titulo",
"album": "Álbum",
"albumArtist": "Artista do álbum",
"albumCount": "Álbuns",
"artist": "Artista",
"biography": "Bibliografia",
"bitDepth": "Profundidade de Bits",
"bitrate": "Bitrate",
"bpm": "Bpm",
"channels": "Canais",
"codec": "Codec",
"comment": "Comentário",
"dateAdded": "Data Adicionada",
"favorite": "Favorito",
"genre": "Gênero",
"lastPlayed": "Última tocada",
"path": "Caminho",
"playCount": "Reproduções",
"rating": "Avaliação",
"releaseDate": "Data de Lançamento",
"releaseYear": "Ano",
"sampleRate": "Taxa de amostragem",
"trackNumber": "Faixa",
"owner": "Dono"
"size": "$t(common.size)",
"title": "Titulo"
},
"config": {
"label": {
"discNumber": "Numero do disco",
"titleCombined": "$t(common.title) (combinado)",
"album": "$t(entity.album, {\"count\": 1})",
"albumCount": "$t(entity.album, {\"count\": 2})",
"albumArtist": "$t(entity.albumArtist, {\"count\": 1})",
"artist": "$t(entity.artist, {\"count\": 1})",
"biography": "$t(common.biography)",
"bitrate": "$t(common.bitrate)",
"bpm": "$t(common.bpm)",
"codec": "$t(common.codec)",
"composer": "Compositor",
"duration": "$t(common.duration)",
"favorite": "$t(common.favorite)",
"genre": "$t(entity.genre, {\"count\": 1})",
"image": "Imagem",
"lastPlayed": "Última reprodução",
"note": "$t(common.note)",
"owner": "$t(common.owner)",
"path": "$t(common.path)",
"playCount": "Contador de Reprodução",
"rating": "$t(common.rating)",
"releaseDate": "Data de Lançamento",
"size": "$t(common.size)",
"songCount": "$t(entity.track, {\"count\": 2})",
"title": "$t(common.title)",
"year": "$t(common.year)"
},
"general": {
"advancedSettings": "Configurações avançadas",
"moveUp": "Mover para cima",
"moveDown": "Mover para baixo",
"alignLeft": "Alinhar à esquerda",
"alignCenter": "Alinhar ao centro",
"alignRight": "Alinhar à direita",
"displayType": "Tipo do ecrã",
"gap": "$t(common.gap)",
"size": "$t(common.size)",
"size_default": "Predefinição",
"size_compact": "Compacto",
"size_large": "Grande",
"tableColumns": "Colunas das tabelas",
"pagination": "Paginação",
"pagination_itemsPerPage": "Elementos por página",
"pagination_infinite": "Infinito",
"pagination_paginate": "Paginado",
"showHeader": "Exibir cabeçalho"
},
"view": {
"grid": "Grade"
}
}
},
"filterOperator": {
"contains": "Contém",
"endsWith": "Termina com",
"is": "É",
"isNot": "Não é",
"isGreaterThan": "É maior que",
"isLessThan": "É menor que",
"notContains": "Não contém",
"startsWith": "Começa com"
},
"releaseType": {
"primary": {
"broadcast": "Broadcast",
"ep": "EP",
"other": "Outros",
"single": "Simples"
},
"secondary": {
"compilation": "Compilação",
"djMix": "Mixagem de DJ",
"demo": "Demo",
"interview": "Entrevista",
"live": "Ao Vivo"
}
},
"visualizer": {
"systemAudioConsentAllow": "Permitir",
"systemAudioConsentDecline": "Recusar",
"ignoredPresets": "Predefinições Ignoradas",
"selectedPresets": "84",
"presets": "Predefinições",
"applyPreset": "Aplicar Predefinição",
"saveAsPreset": "Gravar como Predefinição",
"updatePreset": "Atualizar Predefinição",
"copyConfiguration": "Copiar Configuração",
"pasteConfiguration": "Colar Configuração",
"presetNamePlaceholder": "Digite o nome da predefinição",
"general": "Geral",
"mode": "Modo",
"mode1To8": "Modo 1 - 8",
"mode10": "Modo 10",
"lineWidth": "Largura da Linha",
"opacity": "Opacidade",
"vertical": "Vertical",
"horizontal": "Horizontal",
"level": "Nível",
"remove": "Remover",
"custom": "Personalizado",
"builtIn": "Embutido",
"colors": "Cores",
"gradient": "Gradiente",
"smoothing": "Suavizamento",
"sensitivity": "Sensibilidade",
"gravity": "Gravidade",
"radial": "Radial",
"radius": "Raio",
"options": {
"colorMode": {
"gradient": "Gradiente"
},
"gradient": {
"classic": "Clássico",
"rainbow": "Arco-íris"
},
"frequencyScale": {
"none": "Nenhum"
},
"weightingFilter": {
"none": "Nenhum",
"a": "A",
"b": "B",
"c": "C",
"d": "D",
"z": "Z"
"titleCombined": "$t(common.title) (combinado)"
}
}
}
+14 -183
View File
@@ -41,9 +41,7 @@
"viewMore": "Посмотреть больше",
"openApplicationDirectory": "Открыть папку приложения",
"selectRangeOfItems": "Выбрать диапазон элементов",
"goToCurrent": "Перейти к текущему элементу",
"collapseAllFolders": "Свернуть все папки",
"expandAllFolders": "Развернуть все папки"
"goToCurrent": "Перейти к текущему элементу"
},
"common": {
"backward": "Назад",
@@ -173,9 +171,7 @@
"externalLinks": "Внешние ссылки",
"explicitStatus": "Признак нецензурного контента",
"newVersionAvailable": "Доступна новая версия",
"numberOfResults": "{{numberOfResults}} результатов",
"back": "Назад",
"openFolder": "Открыть папку"
"numberOfResults": "{{numberOfResults}} результатов"
},
"entity": {
"album_one": "Альбом",
@@ -241,10 +237,7 @@
"table": {
"config": {
"view": {
"table": "Таблица",
"detail": "Детали",
"grid": "Сетка",
"list": "Список"
"table": "Таблица"
},
"general": {
"displayType": "Тип отображения",
@@ -254,29 +247,7 @@
"followCurrentSong": "Следовать за исполняемым треком",
"size": "$t(common.size)",
"itemSize": "Размер элементов (px)",
"itemGap": "Отступ между элементами (px)",
"advancedSettings": "Расширенные настройки",
"autosize": "Автоматический выбор размера",
"moveUp": "Переместить выше",
"moveDown": "Переместить ниже",
"pinToLeft": "Закрепить слева",
"pinToRight": "Закрепить права",
"alignLeft": "Выровнять по левой стороне",
"alignCenter": "Выровнять по центру",
"alignRight": "Выровнять по правой стороне",
"itemsPerRow": "Элементов в строке",
"size_default": "По-умолчанию",
"size_compact": "Компактный",
"size_large": "Большой",
"pagination": "Пагинация",
"pagination_itemsPerPage": "Элементов на странице",
"pagination_infinite": "Бесконечно",
"pagination_paginate": "Разбитый по страницам",
"alternateRowColors": "Переменный цвет строк",
"horizontalBorders": "Границы строки",
"rowHoverHighlight": "Подсветка строки при наведении",
"showHeader": "Показать заголовок",
"verticalBorders": "Границы колонки"
"itemGap": "Отступ между элементами (px)"
},
"label": {
"releaseDate": "Дата выхода",
@@ -302,10 +273,7 @@
"favorite": "$t(common.favorite)",
"year": "$t(common.year)",
"codec": "$t(common.codec)",
"titleArtist": "$t(common.title) (артист)",
"albumGroup": "Группа альбома",
"composer": "Композитор",
"image": "Изображение"
"titleArtist": "$t(common.title) (артист)"
}
},
"column": {
@@ -328,14 +296,7 @@
"comment": "Комментарий",
"bitrate": "Битрейт",
"channels": "$t(common.channel_other)",
"bpm": "BPM",
"albumCount": "Альбомы",
"artist": "Исполнители",
"bitDepth": "Битовая глубина",
"genre": "Жанр",
"sampleRate": "Частота дискретизации",
"songCount": "Треки",
"owner": "Правообладатель"
"bpm": "BPM"
}
},
"error": {
@@ -469,9 +430,7 @@
"sleepTimer_timeRemaining": "{{time}} осталось",
"sleepTimer_setCustom": "Установить таймер",
"sleepTimer_custom": "Пользовательский",
"sleepTimer_cancel": "Отменить таймер",
"scrobbleForceSubmit": "Принудительная скробблинг",
"sleepTimer_endOfAlbum": "Конец этого альбома"
"sleepTimer_cancel": "Отменить таймер"
},
"page": {
"sidebar": {
@@ -532,8 +491,7 @@
"selectMusicFolder": "Выбрать папку с музыкой",
"noMusicFolder": "Папка с музыкой не выбрана",
"multipleMusicFolders": "{{count}} выбрано музыкальных папок",
"commandPalette": "открыть командную строку",
"settings": "$t(common.setting, {\"count\": 2})"
"commandPalette": "открыть командную строку"
},
"manageServers": {
"title": "Сервера",
@@ -611,8 +569,7 @@
},
"genreList": {
"showAlbums": "показать $t(entity.genre, {\"count\": 1}) $t(entity.album, {\"count\": 2})",
"showTracks": "показать $t(entity.genre, {\"count\": 1}) $t(entity.track, {\"count\": 2})",
"title": "$t(entity.genre, {\"count\": 2})"
"showTracks": "показать $t(entity.genre, {\"count\": 1}) $t(entity.track, {\"count\": 2})"
},
"trackList": {
"artistTracks": "Треки {{artist}}",
@@ -724,8 +681,7 @@
"input_skipDuplicates": "не добавлять дубликаты",
"create": "создать $t(entity.playlist, {\"count\": 1}) {{playlist}}",
"searchOrCreate": "для создания нового списка выполните поиск по $t(entity.playlist, {\"count\": 2}) или введите соответствующий текст",
"input_playlists": "$t(entity.playlist, {\"count\": 2})",
"noneAdded": "Ни один трек не добавлен в $t(entity.playlist, {\"count\": 1}) '{{playlist}}'"
"input_playlists": "$t(entity.playlist, {\"count\": 2})"
},
"updateServer": {
"title": "Обновление сервера",
@@ -793,12 +749,7 @@
"input_played_optionAll": "Все треки",
"input_played_optionUnplayed": "Только не игранные треки",
"input_played_optionPlayed": "Только воспроизведённые треки",
"input_genre": "$t(entity.genre, {\"count\": 1})",
"input_kind_albums": "Альбомы",
"input_kind_songs": "Песни",
"input_kind": "Случайный выбор",
"input_limit_albums": "Сколько альбомов?",
"input_limit_songs": "Сколько песен?"
"input_genre": "$t(entity.genre, {\"count\": 1})"
},
"editRadioStation": {
"success": "Радиостанция успешно обновлена"
@@ -999,6 +950,7 @@
"artistBackground_description": "Добавляет фоновое изображение для страниц исполнителя, содержащих обложку исполнителя",
"artistBackgroundBlur": "Процент размытия обложки исполнителя",
"artistBackgroundBlur_description": "Регулирует процент размытия к заднему фону исполнителя",
"autoDJ_description": "Автоматически добавлять похожие песни в очередь воспроизведения",
"autoDJ_itemCount": "Количество элементов",
"autoDJ_itemCount_description": "Количество элементов, которые пытаются добавить в очередь при включенной функции автоматического диджеинга",
"autoDJ_timing": "Расчетное время",
@@ -1129,78 +1081,7 @@
"audioFadeOnStatusChange": "плавное изменение звука",
"audioFadeOnStatusChange_description": "включает эффекты затухания и появления звука при изменении статуса (пауза/проигрывание)",
"preventSleepOnPlayback_description": "запрещает спящий режим экрана, пока играет музыка",
"preventSleepOnPlayback": "не переходить в спящий режим",
"autoDJ_mode": "Режим",
"autoDJ_mode_albums": "Альбомы",
"autoDJ_mode_description": "Добавь песни или целые альбомы в очередь",
"autoDJ_mode_songs": "Песни",
"autoDJ_enabled": "Включить Auto DJ",
"autoDJ_albumStrategy": "Режим выбора альбома",
"autoDJ_songStrategy": "Режим выбора песни",
"autoDJ_strategy_option_library_random": "Случайно",
"autoDJ_strategy_option_similar": "Похожие",
"hotkey_listShowPlayingSong": "Показать текущую песню в списке",
"listenbrainz_description": "Показать ссылки на ListenBrains на страницах исполнителя/альбома",
"listenbrainz": "Показать ссылки на ListenBrainz",
"qobuz_description": "Показать ссылки на Qobuz на страницах исполнителя/альбома",
"qobuz": "Показать ссылки на Qobuz",
"spotify_description": "Показать ссылки на Spotify на странице исполнителя/альбома",
"spotify": "Показать ссылки на Spotify",
"nativeSpotify_description": "Открывать в приложении Spotify вместо браузера",
"nativeSpotify": "Использовать приложение Spotify",
"imageResolution_optionTable": "Таблица",
"preventSuspendOnPlayback_description": "Не приостанавливать приложение во время проигрывания музыки",
"preventSuspendOnPlayback": "Не приостанавливать во время проигрывания",
"playerItemConfiguration_description": "Настроить какие элементы и в каком порядке видны в полноэкранном плеере",
"playerItemConfiguration": "Настройка плеера",
"sidebarPlaylistFolders": "Включить папки",
"sidebarPlaylistFolderSeparator_description": "Символ (или строка), который разделяет уровни папок в названии плейлиста",
"sidebarPlaylistFolderSeparator": "Разделитель папок",
"sidebarPlaylistFolderView_description": "Как отображать папки в боковой панели",
"sidebarPlaylistFolderView": "Вид папок",
"sidebarPlaylistFolderView_optionSingle": "Единстванная папка",
"sidebarPlaylistFolderView_optionTree": "Вид дерева",
"sidebarPlaylistFolderView_optionNavigation": "Вид навигации",
"sidebarPlaylistFolderTreeIndent_description": "Отступ в пикселях на каждом уровне дерева",
"sidebarPlaylistFolderTreeIndent": "Отступ в дереве",
"sidebarPlaylistFolderTreeLineColor_description": "Цвет линий соединения в дереве (оставь пустым, чтобы использовать настройки темы)",
"sidebarPlaylistFolderTreeLineColor": "Цвет линии в дереве",
"sidebarPlaylistMode_description": "Как отображать каждый плейлист в списке в боковой панели",
"sidebarPlaylistMode": "Режим плейлиста в боковой панели",
"sidebarPlaylistMode_optionCompact": "Компактный",
"sidebarPlaylistMode_optionExpanded": "Просторный",
"sidebarPlaylistSorting_description": "Разрешить ручную сортировку плейлистов в боковой панели с помощью перетаскивания вместо сортировки со стороны сервера",
"sidebarPlaylistSorting": "Сортировка плейлистов в боковой панели",
"sidebarPlaylistListFilterRegex_description": "Скрывать плейлисты в боковой панели, которые соответствуют этому регулярному выражению",
"sidebarPlaylistListFilterRegex_placeholder": "Например ^daily mix.*",
"sidebarPlaylistListFilterRegex": "Регулярное выражение для фильтрации плейлистов",
"sidePlayQueueLayout": "Макет очереди проигрывания сбоку",
"sidePlayQueueLayout_description": "Задает макет прикрепленной очереди проигрывания сбоку",
"sidePlayQueueLayout_optionHorizontal": "Горизонтальный",
"sidePlayQueueLayout_optionVertical": "Вертикальный",
"mediaSession_description": "Включает интеграцию сессии медиа, отображая элементы управления и метаданные медиа в системном оверлее управления громкостью и на экране блокировки. Требуется Web Audio Player.",
"mediaSession": "Включить сессию медиа",
"skipPlaylistPage_description": "Когда переходишь в плейлист, откроется страница со списком песен плейлиста, вместо страницы по-умолчанию",
"transcode": "Включить транскодирование",
"transcodeFormat_description": "Выбирает форматы для транскодирования. Оставь пустым, чтобы решение принимал сервер",
"translationApiKey_description": "Ключ API для перевода (только эндпойнт глобального сервиса)",
"translationApiKey": "Ключ API перевода",
"translationApiProvider_description": "Поставщик API для перевода",
"translationApiProvider": "Поставщик API перевода",
"translationTargetLanguage_description": "На какой язык выполнять перевод",
"translationTargetLanguage": "На какой язык переводить",
"trayEnabled_description": "Показать/скрыть иконку/меню в трее. Если скрыто, то также отключается сворачивать в трей/свернуть в трей при выходе",
"trayEnabled": "Показать в трее",
"queryBuilder": "Создатель очереди",
"queryBuilderCustomFields_inputLabel": "Метка",
"queryBuilderCustomFields_inputTag": "Тег",
"queryBuilderCustomFields": "Пользовательские поля",
"queryBuilderCustomFields_description": "Добавь пользовательские поля для использования создателями очереди",
"hotkey_listNavigateToPage": "Перейти к странице элемента",
"hotkey_listPlayDefault": "Воспроизвести список",
"hotkey_listPlayLast": "Воспроизвести последний в списке",
"hotkey_listPlayNext": "Воспроизвести следующий в списке",
"sidebarPlaylistFolders_description": "Создать вид папки для плейлистов, которые включают настраиваемый разделитель в имени"
"preventSleepOnPlayback": "не переходить в спящий режим"
},
"releaseType": {
"secondary": {
@@ -1275,56 +1156,6 @@
"presetName": "Название пресета",
"presetNamePlaceholder": "Введите название пресета",
"general": "Главная",
"lineWidth": "Ширина линии",
"systemAudioConsentAllow": "Разрешить",
"systemAudioConsentBody": "Для работы визуализатора требуется доступ к аудио в системе",
"systemAudioConsentDecline": "Запретить",
"systemAudioConsentTitle": "Разрешить доступ к аудио в системе?",
"systemAudioCaptureFailed": "Не удается начать захват: {{message}}",
"visualizerType": "Тип визуализатора",
"cyclePresets": "Переключаться между наборами настроек",
"cycleTime": "Время между переключениями (в секундах)",
"includeAllPresets": "Включить все наборы настроек",
"ignoredPresets": "Игнорируемые наборы настроек",
"selectedPresets": "Выбранные наборы настроек",
"randomizeNextPreset": "Выбирать следующий набор настроек случайным образом",
"blendTime": "Время смешивания",
"mode": "Режим",
"mode1To8": "Режимы 1-8",
"mode10": "Режим 10",
"maxFPS": "Максимум кадров в секунду",
"opacity": "Прозрачность",
"customGradients": "Пользовательские градиенты",
"addCustomGradient": "Добавить пользовательский градиент",
"gradientName": "Название градиента",
"gradientNamePlaceholder": "Название градиента",
"vertical": "Вертикальный",
"horizontal": "Горизонтальный",
"addColor": "Добавить цвет",
"position": "Расположение",
"level": "Уровень",
"remove": "Удалить",
"pasteGradient": "Вставить градиент",
"pasteGradientPlaceholder": "Вставить JSON с градиентом сюда...",
"custom": "Пользовательский",
"builtIn": "Встроенный",
"colors": "Цвета",
"colorMode": "Цветовой режим",
"gradient": "Градиент",
"gradientLeft": "Градиент слева",
"gradientRight": "Градиент справа",
"smoothing": "Сглаживание",
"minimumFrequency": "Минимальная частота",
"maximumFrequency": "Максимальная частота",
"sensitivity": "Чуствительность",
"minimumDecibels": "Минимум децибел",
"maximumDecibels": "Максимум децибел",
"linearAmplitude": "Линейная амплитуда",
"showPeaks": "Показывать пики"
},
"dragDropZone": {
"error_oneFileOnly": "Выбери только 1 файл",
"error_readingFile": "Проблема при чтении файла: {{errorMessage}}",
"mainText": "Перемести файл сюда"
"lineWidth": "Ширина линии"
}
}
+8 -8
View File
@@ -580,25 +580,25 @@
},
"entity": {
"genre_one": "Žanr",
"genre_few": "Žanrova",
"genre_few": "žanrova",
"genre_other": "Žanrova",
"playlistWithCount_one": "{{count}} plejlista",
"playlistWithCount_few": "{{count}} plejlista",
"playlistWithCount_other": "{{count}} plejlista",
"playlist_one": "Plejlista",
"playlist_few": "Plejlista",
"playlist_few": "plejlista",
"playlist_other": "Plejlista",
"artist_one": "Umetnik",
"artist_few": "Umetnika",
"artist_few": "umetnika",
"artist_other": "Umetnika",
"folderWithCount_one": "{{count}} folder",
"folderWithCount_few": "{{count}} foldera",
"folderWithCount_other": "{{count}} foldera",
"albumArtist_one": "Album umetnika",
"albumArtist_few": "Albuma umetnika",
"albumArtist_few": "albuma umetnika",
"albumArtist_other": "Albuma umetnika",
"track_one": "Pesma",
"track_few": "Pesama",
"track_few": "pesama",
"track_other": "Pesama",
"albumArtistCount_one": "{{count}} album umetnika",
"albumArtistCount_few": "{{count}} albuma umetnika",
@@ -607,17 +607,17 @@
"albumWithCount_few": "{{count}} albuma",
"albumWithCount_other": "{{count}} albuma",
"favorite_one": "Favorit",
"favorite_few": "Favorita",
"favorite_few": "favorita",
"favorite_other": "Favorita",
"artistWithCount_one": "{{count}} umetnik",
"artistWithCount_few": "{{count}} umetnika",
"artistWithCount_other": "{{count}} umetnika",
"folder_one": "Folder",
"folder_few": "Foldera",
"folder_few": "foldera",
"folder_other": "Foldera",
"smartPlaylist": "Pametna $t(entity.playlist, {\"count\": 1})",
"album_one": "Album",
"album_few": "Albumi",
"album_few": "albumi",
"album_other": "Albuma",
"genreWithCount_one": "{{count}} žanr",
"genreWithCount_few": "{{count}} žanrova",
+1
View File
@@ -881,6 +881,7 @@
"preservePitch": "சுருதியைப் பாதுகாக்கவும்",
"preservePitch_description": "பின்னணி வேகத்தை மாற்றும்போது சுருதியைப் பாதுகாக்கிறது",
"autoDJ": "ஆட்டோ டி.சே",
"autoDJ_description": "தானாக வரிசையில் ஒத்த பாடல்களைச் சேர்க்கவும்",
"autoDJ_itemCount": "பொருள் எண்ணிக்கை",
"autoDJ_itemCount_description": "ஆட்டோ DJ இயக்கப்பட்டிருக்கும் போது, வரிசையில் சேர்க்க முயற்சிக்கும் உருப்படிகளின் எண்ணிக்கை",
"autoDJ_timing": "நேரவிவரம்",
-14
View File
@@ -1,14 +0,0 @@
{
"action": {
"addToFavorites": "Idagdag sa $t(entity.favorite, {\"count\": 2})",
"addToPlaylist": "Idagdag sa $t(entity.playlist, {\"count\": 1})",
"addOrRemoveFromSelection": "Idagdag o alisin sa pinili",
"collapseAllFolders": "Isara lahat ng mga folder",
"expandAllFolders": "Buksan lahat ng mga folder",
"createPlaylist": "Gumawa $t(entity.playlist, {\"count\": 1})",
"createRadioStation": "Gumawa $t(entity.radioStation, {\"count\": 1})",
"selectAll": "Piliin lahat",
"deselectAll": "Huwag piliin lahat",
"downloadStarted": "Nagsimulang mag-dowload ng {{count}} (mga) aytem"
}
}
+1 -2
View File
@@ -169,8 +169,7 @@
"tableColumns": "Стовпці таблиці",
"itemsMore": "{{count}} більше",
"numberOfResults": "{{numberOfResults}} результатів",
"newVersionAvailable": "Доступна нова версія",
"back": "Повернутися"
"newVersionAvailable": "Доступна нова версія"
},
"entity": {
"album_one": "Альбом",
+1
View File
@@ -508,6 +508,7 @@
"combinedLyricsAndVisualizer_description": "将歌词和可视化界面合并到同一面板中",
"queryBuilderCustomFields_description": "在查询构建器添加自定义字段",
"combinedLyricsAndVisualizer": "在播放器侧边栏合并歌词和可视化界面",
"autoDJ_description": "自动添加相似歌曲到队列中",
"notify_description": "歌曲变更时显示通知",
"mpvExtraParameters_description": "向MPV传递额外参数",
"audioFadeOnStatusChange": "音频改变时淡入淡出",
+135 -172
View File
@@ -5,22 +5,22 @@
"bitrate": "位元率",
"bpm": "BPM",
"clear": "清空",
"collapse": "疊",
"collapse": "疊",
"comingSoon": "即將推出…",
"confirm": "確認",
"decrease": "降低",
"delete": "刪除",
"descending": "降冪",
"description": "描述",
"forceRestartRequired": "重啟以套用變更… 關閉通知後即可重啟",
"forceRestartRequired": "重新啟動應用程式以使更改生效…關閉通知後即可重啟",
"menu": "選單",
"action_other": "操作",
"add": "新增",
"areYouSure": "你確定嗎?",
"ascending": "升冪",
"disable": "用",
"disable": "用",
"disc": "光碟",
"dismiss": "不理會",
"dismiss": "不再顯示",
"duration": "時長",
"edit": "編輯",
"enable": "啟用",
@@ -31,7 +31,7 @@
"forward": "前進",
"gap": "空隙",
"home": "首頁",
"increase": "提升",
"increase": "增高",
"left": "左",
"limit": "限制",
"manage": "管理",
@@ -40,14 +40,14 @@
"owner": "所有者",
"path": "路徑",
"playerMustBePaused": "播放器必須先暫停",
"previousSong": "上一首 $t(entity.track, {\"count\": 1})",
"previousSong": "上一首$t(entity.track, {\"count\": 1})",
"quit": "退出",
"random": "隨機",
"rating": "評分",
"refresh": "重新整理",
"reset": "重置",
"resetToDefault": "重置為預設",
"restartRequired": "需要重新啟動",
"resetToDefault": "恢復為預設",
"restartRequired": "需要重新啟動應用程式",
"right": "右",
"save": "儲存",
"saveAndReplace": "儲存並取代",
@@ -55,7 +55,7 @@
"search": "搜尋",
"sortOrder": "順序",
"title": "標題",
"trackNumber": "曲目",
"trackNumber": "音軌編號",
"unknown": "未知",
"size": "大小",
"version": "版本",
@@ -64,24 +64,24 @@
"cancel": "取消",
"center": "中央",
"channel_other": "聲道",
"configure": "配置",
"configure": "設定",
"create": "建立",
"currentSong": "當前 $t(entity.track, {\"count\": 1})",
"currentSong": "目前$t(entity.track, {\"count\": 1})",
"minimize": "最小化",
"modified": "已修改",
"name": "名稱",
"no": "否",
"none": "無",
"noResultsFromQuery": "查詢回傳了無結果",
"noResultsFromQuery": "查詢到匹配結果",
"note": "注釋",
"additionalParticipants": "額外參與者",
"newVersion": "新版本 ({{version}}) 已被安裝",
"newVersion": "已安裝新版本 ({{version}})",
"viewReleaseNotes": "查看發行註記",
"albumGain": "專輯增益",
"albumPeak": "專輯峰值",
"bitDepth": "位元深度",
"close": "關閉",
"codec": "編解碼器",
"codec": "編",
"mbid": "MusicBrainz ID",
"preview": "預覽",
"reload": "重新載入",
@@ -90,7 +90,7 @@
"share": "分享",
"tags": "標籤",
"trackGain": "曲目增益",
"trackPeak": "曲峰值",
"trackPeak": "曲峰值",
"translation": "翻譯",
"doNotShowAgain": "不再顯示",
"externalLinks": "外部連結",
@@ -103,11 +103,11 @@
"sort": "排序",
"tableColumns": "表格欄位",
"clean": "清除",
"explicitStatus": "露骨狀態",
"explicit": "露骨",
"gridRows": "網格",
"noFilters": "未配置篩選器",
"countSelected": "{{count}} 個已選取",
"explicitStatus": "Explicit狀態",
"explicit": "Explicit",
"gridRows": "網格",
"noFilters": "未設定任何過濾器",
"countSelected": "{{count}}個已選取",
"retry": "重試",
"example": "範例",
"mood": "情緒",
@@ -116,45 +116,43 @@
"itemsMore": "{{count}} 更多",
"filter_single": "單選",
"filter_multiple": "複選",
"newVersionAvailable": "有新版本可用",
"newVersionAvailable": "有新版本可供使用",
"numberOfResults": "{{numberOfResults}} 項結果",
"grouping": "分組",
"back": "返回",
"openFolder": "開啟資料夾"
"grouping": "分組"
},
"error": {
"endpointNotImplementedError": "{{serverType}} 尚未實端點 {{endpoint}}",
"apiRouteError": "無法路由請求",
"audioDeviceFetchError": "嘗試取得音訊裝置時發生了錯誤",
"endpointNotImplementedError": "{{serverType}} 尚未實端點 {{endpoint}}",
"apiRouteError": "請求失敗:無法路由",
"audioDeviceFetchError": "無法取得音訊設備",
"authenticationFailed": "驗證失敗",
"credentialsRequired": "需要憑證",
"genericError": "發生了錯誤",
"invalidServer": "無效的伺服器",
"localFontAccessDenied": "存取本地字型被拒絕",
"localFontAccessDenied": "無法取得本地字體",
"loginRateError": "登入請求嘗試次數過多,請稍後再試",
"remoteDisableError": "嘗試 $t(common.disable) 遠端伺服器時發生了錯誤",
"remoteEnableError": "嘗試 $t(common.enable) 遠端伺服器時發生了錯誤",
"remotePortError": "嘗試設定遠端伺服器連接埠時發生錯誤",
"remotePortWarning": "重啟伺服器以套用新連接埠",
"remoteDisableError": "$t(common.disable)遠端伺服器時出現錯誤",
"remoteEnableError": "$t(common.enable)遠端伺服器時出現錯誤",
"remotePortError": "設定遠端伺服器連接埠時發生錯誤",
"remotePortWarning": "重啟伺服器使新連接埠生效",
"serverRequired": "需要伺服器",
"sessionExpiredError": "您的工作階段已過期",
"systemFontError": "嘗試取得系統字型時發生了錯誤",
"sessionExpiredError": "工作階段已過期",
"systemFontError": "嘗試取得系統字體時出現錯誤",
"serverNotSelectedError": "未選擇伺服器",
"mpvRequired": "需要 MPV",
"playbackError": "嘗試播放媒體時發生了錯誤",
"badAlbum": "您看到此頁面是因為這首歌不是專輯的一部分。如果您的音樂資料夾頂層有一首歌,則很可能會看到此問題。 Jellyfin 僅將資料夾中的曲目分組",
"playbackError": "無法播放媒體",
"badAlbum": "您看到此頁面是因為這首歌不是專輯的一部分。如果您的音樂資料夾頂層有一首歌,則很可能會看到此問題。 Jellyfin 僅將資料夾中的曲目分組",
"badValue": "無效選項「{{value}}」。該值不再存在",
"networkError": "發生網路錯誤",
"notificationDenied": "通知權限被拒絕。此設定無影響",
"networkError": "發生網路錯誤",
"notificationDenied": "通知權限被拒絕。此設定無",
"openError": "無法開啟檔案",
"multipleServerSaveQueueError": "播放佇列中包含了並非來自前伺服器的歌曲此操作不受支援",
"multipleServerSaveQueueError": "播放佇列中包含不是來自前伺服器的歌曲此操作不受支援",
"saveQueueFailed": "儲存播放佇列失敗",
"settingsSyncError": "偵測到渲染器與主程式之間的設定不一致請重新啟動應用程式以套用變更",
"noNetwork": "伺服器不可用",
"noNetworkDescription": "無法連線至此伺服器",
"settingsSyncError": "偵測到渲染器與主程式之間的設定不一致請重新啟動應用程式以套用變更",
"noNetwork": "伺服器無法連線",
"noNetworkDescription": "無法連接到此伺服器",
"invalidJson": "無效的 JSON",
"serverLockSingleServer": "當伺服器鎖定時只允許一個伺服器",
"playbackPausedDueToError": "播放因錯誤而暫停"
"playbackPausedDueToError": "發生錯誤,已停止播放"
},
"page": {
"contextMenu": {
@@ -199,12 +197,12 @@
"title": "$t(common.home)",
"mostPlayed": "最多播放",
"newlyAdded": "最近新增的發行",
"recentlyReleased": "最近發",
"recentlyReleased": "最近發",
"genres": "$t(entity.genre, {\"count\": 2})"
},
"appMenu": {
"openBrowserDevtools": "開啟瀏覽器開發者工具",
"collapseSidebar": "疊側邊欄",
"collapseSidebar": "疊側邊欄",
"expandSidebar": "展開側邊欄",
"goBack": "返回",
"goForward": "前進",
@@ -223,7 +221,7 @@
"fullscreenPlayer": {
"config": {
"showLyricProvider": "顯示歌詞提供者",
"useImageAspectRatio": "使用圖片長寬比",
"useImageAspectRatio": "使用圖片縱橫比",
"dynamicBackground": "動態背景",
"followCurrentLyric": "跟隨目前歌詞",
"lyricAlignment": "歌詞對齊",
@@ -269,7 +267,7 @@
"transcoding": "轉碼",
"discord": "Discord",
"queryBuilder": "查詢建構器",
"playerFilters": "播放篩選器",
"playerFilters": "播放過濾器",
"logger": "日誌記錄器",
"lyricsDisplay": "歌詞顯示"
},
@@ -324,8 +322,8 @@
"topSongsFrom": "{{title}} 的熱門歌曲",
"viewAll": "檢視所有",
"viewAllTracks": "檢視所有$t(entity.track, {\"count\": 2})",
"groupingTypeAll": "所有發類型",
"groupingTypePrimary": "主要發類型",
"groupingTypeAll": "所有發類型",
"groupingTypePrimary": "主要發類型",
"favoriteSongs": "最愛歌曲",
"favoriteSongsFrom": "{{title}} 的最愛歌曲",
"topSongsCommunity": "社群",
@@ -381,7 +379,7 @@
"playbackSpeed": "播放速度",
"playRandom": "隨機播放",
"previous": "上一首",
"queue_clear": "清空佇列",
"queue_clear": "清空播放佇列",
"queue_remove": "移除所選",
"repeat": "循環",
"repeat_all": "全部循環",
@@ -419,9 +417,7 @@
"sleepTimer_timeRemaining": "剩餘 {{time}}",
"sleepTimer_setCustom": "設定定時器",
"sleepTimer_cancel": "取消定時器",
"albumRadio": "專輯電台",
"scrobbleForceSubmit": "強制紀錄",
"sleepTimer_endOfAlbum": "專輯播完時"
"albumRadio": "專輯電台"
},
"setting": {
"audioPlayer_description": "選擇用於播放的音訊播放器",
@@ -444,9 +440,9 @@
"crossfadeDuration": "淡入淡出持續時間",
"crossfadeDuration_description": "設定淡入淡出持續時間",
"crossfadeStyle_description": "選擇用於音訊播放器的淡入淡出風格",
"customFontPath": "自訂字型路徑",
"customFontPath_description": "設定應用程式使用的自訂字型路徑",
"disableLibraryUpdateOnStartup": "用啟動時檢查新版本",
"customFontPath": "自定字體路徑",
"customFontPath_description": "設定應用程式使用的自定字體路徑",
"disableLibraryUpdateOnStartup": "用啟動時檢查新版本",
"discordApplicationId": "{{discord}} 應用程式 ID",
"discordApplicationId_description": "{{discord}} Rich Presence 應用程式 ID(預設為 {{defaultId}}",
"discordIdleStatus": "顯示 Rich Presence 閒置狀態",
@@ -458,12 +454,12 @@
"enableRemote_description": "啟用遠端控制伺服器,以允許其他設備控制此應用程式",
"exitToTray": "關閉時到將視窗最小化",
"followLyric": "跟隨目前歌詞",
"font_description": "設定應用程式使用的字",
"fontType": "字類型",
"fontType_description": "內建字可以選擇 Feishin 提供的字之一。系統字允許您選擇作業系統提供的任何字。自選項允許您使用自己的字",
"fontType_optionBuiltIn": "內建字",
"fontType_optionCustom": "自訂字型",
"fontType_optionSystem": "系統字",
"font_description": "設定應用程式使用的字",
"fontType": "字類型",
"fontType_description": "內建字可以選擇 Feishin 提供的字之一。系統字允許您選擇作業系統提供的任何字。自選項允許您使用自己的字",
"fontType_optionBuiltIn": "內建字",
"fontType_optionCustom": "自定字體",
"fontType_optionSystem": "系統字",
"gaplessAudio": "無間隔音訊",
"gaplessAudio_description": "調整 MPV 無間隔音訊設定",
"gaplessAudio_optionWeak": "弱(建議)",
@@ -503,7 +499,7 @@
"lyricFetchProvider_description": "選擇歌詞來源",
"minimizeToTray": "最小化到系統匣",
"minimizeToTray_description": "將應用程式最小化到系統匣",
"minimumScrobbleSeconds": "最小紀錄時(秒)",
"minimumScrobbleSeconds": "最小紀錄時(秒)",
"minimumScrobbleSeconds_description": "歌曲被記錄為已播放(Scrobble)所需的最小播放時間",
"mpvExecutablePath": "MPV 執行檔路徑",
"playbackStyle_optionCrossFade": "淡入淡出",
@@ -532,8 +528,8 @@
"showSkipButton": "顯示跳過按鈕",
"showSkipButton_description": "在播放條上顯示/隱藏跳過按鈕",
"sidebarPlaylistList": "側邊欄播放清單列表",
"sidebarCollapsedNavigation": "側邊欄(已疊)導航",
"sidebarCollapsedNavigation_description": "在疊的側邊欄中顯示或隱藏導航",
"sidebarCollapsedNavigation": "側邊欄(已疊)導航",
"sidebarCollapsedNavigation_description": "在疊的側邊欄中顯示或隱藏導航",
"sidebarConfiguration": "側邊欄設定",
"sidebarConfiguration_description": "選擇側邊欄包含的項目與順序",
"sidebarPlaylistList_description": "顯示或隱藏側邊欄歌單清單",
@@ -561,8 +557,8 @@
"playbackStyle": "播放風格",
"exitToTray_description": "退出應用程式時最小化到系統匣而非關閉",
"followLyric_description": "滾動歌詞到目前播放位置",
"font": "字",
"globalMediaHotkeys_description": "啟用或用系統媒體快捷鍵以控制播放",
"font": "字",
"globalMediaHotkeys_description": "啟用或用系統媒體快捷鍵以控制播放",
"hotkey_browserBack": "瀏覽器返回",
"hotkey_favoriteCurrentSong": "收藏 $t(common.currentSong)",
"hotkey_playbackStop": "停止",
@@ -573,7 +569,7 @@
"remotePassword": "遠端控制伺服器密碼",
"remotePassword_description": "設定遠端控制伺服器的密碼。這些憑證預設以不安全的方式傳輸,因此您應該使用一個您不在意的唯一密碼",
"remotePort_description": "設定遠端控制伺服器的連接埠",
"remoteUsername_description": "設定遠端控制伺服器的使用者名稱。如果使用者名稱和密碼都為空,則身分驗證將被用",
"remoteUsername_description": "設定遠端控制伺服器的使用者名稱。如果使用者名稱和密碼都為空,則身分驗證將被用",
"replayGainClipping_description": "自動降低增益以防止{{ReplayGain}}造成削波",
"showSkipButtons": "顯示跳過按鈕",
"themeDark_description": "應用程式將使用深色主題",
@@ -596,7 +592,7 @@
"customCssEnable_description": "允許撰寫自訂CSS",
"customCssNotice": "警告:即使已限制某些用法(不允許 URL() 和 content:),但使用自訂 CSS 仍然會透過更改介面帶來風險",
"customCss": "自訂CSS",
"customCss_description": "自訂 CSS 內容。注意:內容和遠端 URL 是不允許使用的屬性。您的內容預覽如下所示。由於需要進行清理,因此存在一些您未設定的其他欄位。桌面端:feishin在應用程式配置目錄中讀取和寫入custom.css,並在檔案更改時重新載入",
"customCss_description": "自訂 CSS 內容。注意:內容和遠端 URL 是不允許使用的屬性。您的內容預覽如下所示。由於需要進行清理,因此存在一些您未設定的其他欄位",
"discordPausedStatus": "暫停時顯示 Rich Presence",
"discordPausedStatus_description": "啟用後,播放器暫停時將顯示狀態",
"discordListening": "將狀態設為\"正在聽\"",
@@ -619,7 +615,7 @@
"lastfmApiKey_description": "{{lastfm}}的API金鑰。用於封面照",
"mpvExtraParameters_help": "一行一個",
"musicbrainz": "顯示 MusicBrainz 連結",
"musicbrainz_description": "在擁有 MusicBrainz ID 的藝人/專輯頁面上顯示 MusicBrainz 的連結",
"musicbrainz_description": "在存在 MusicBrainz ID 的藝人/專輯頁面上顯示 MusicBrainz 的連結",
"neteaseTranslation": "啟用網易翻譯",
"neteaseTranslation_description": "啟用後,將從網易取得並顯示翻譯的歌詞(如果有)",
"passwordStore": "密碼/secret儲存",
@@ -630,7 +626,7 @@
"startMinimized": "啟動時最小化",
"startMinimized_description": "在系統匣中啟動應用程式",
"transcode_description": "啟用轉碼到不同格式",
"transcodeBitrate": "轉碼的位元率",
"transcodeBitrate": "轉碼的位元率",
"transcodeBitrate_description": "選擇要轉碼的位元率。 0 表示讓伺服器選擇",
"transcodeFormat": "轉碼的格式",
"transcodeFormat_description": "選擇要轉碼的格式。留空來讓伺服器決定",
@@ -667,7 +663,7 @@
"preventSleepOnPlayback": "防止播放時進入睡眠狀態",
"preventSleepOnPlayback_description": "在音樂播放時防止螢幕進入睡眠狀態",
"mediaSession": "啟用 Media Session",
"mediaSession_description": "啟用 Media Session 整合功能,系統音量疊加層和鎖定畫面顯示媒體控制項與中繼資料。此功能需要使用網頁播放器。",
"mediaSession_description": "啟用 Media Session 整合功能,系統音量 Overlay 和鎖定畫面顯示媒體資料與控制面板",
"releaseChannel": "發佈通道",
"analyticsDisable": "選擇退出使用情況分析",
"analyticsDisable_description": "經過匿名處理的使用情況資料將傳送給開發者,以協助改進應用程式",
@@ -712,15 +708,16 @@
"queryBuilderCustomFields_description": "在查詢建構器中新增自訂欄位",
"followCurrentSong_description": "自動將播放佇列捲動至當前播放的歌曲",
"followCurrentSong": "跟隨當前歌曲",
"playerbarSlider_description": "不建議在速度緩慢或計費的網路下使用波形",
"playerFilters": "從佇列中篩選歌曲",
"playerbarSlider_description": "不建議在網路速度緩慢或計費的網路下使用波形",
"playerFilters": "從佇列中過濾歌曲",
"playerFilters_description": "根據以下條件,排除要新增至佇列中的歌曲",
"autoDJ": "Auto DJ",
"autoDJ_itemCount": "項目數量",
"autoDJ_itemCount_description": "嘗試加入佇列的項目數量",
"autoDJ_description": "自動將相似的歌曲加入到播放佇列",
"autoDJ_itemCount": "歌曲數量",
"autoDJ_itemCount_description": "在啟用Auto DJ時嘗試加入佇列的歌曲數量",
"autoDJ_timing_description": "佇列中剩餘多少歌曲時啟動 Auto DJ",
"autoDJ_timing": "觸發時機",
"logLevel": "Log等級",
"logLevel": "log等級",
"logLevel_description": "設定要顯示的最低日誌等級。Debug 會顯示所有日誌,Error 僅會顯示錯誤訊息",
"logLevel_optionDebug": "Debug",
"logLevel_optionError": "Error",
@@ -728,7 +725,7 @@
"logLevel_optionWarn": "Warn",
"useThemeAccentColor": "使用主題強調色",
"useThemeAccentColor_description": "使用所選主題中定義的主要顏色,而非自訂的強調色",
"artistRadioCount_description": "設定為藝人電台與曲目電台擷取的歌曲數量",
"artistRadioCount_description": "設定為藝人電台與曲目電台擷取的歌曲數量",
"imageResolution": "圖片解析度",
"imageResolution_description": "應用程式中所使用圖片的解析度。設定為 0 時,將使用圖片的原始解析度",
"imageResolution_optionTable": "表格",
@@ -763,11 +760,11 @@
"enableGridMultiSelect_description": "啟用時,允許在網格檢視中選擇多項。停用時,單擊網格項目圖片將導航到項目頁面",
"sidebarPlaylistSorting_description": "允許在側邊欄中使用拖放手動對播放清單進行排序,而不是預設的伺服器排序",
"sidebarPlaylistListFilterRegex_description": "在側邊欄中隱藏與此正規表達式匹配的播放清單",
"sidebarPlaylistListFilterRegex_placeholder": "範例: ^daily mix.*",
"sidebarPlaylistListFilterRegex": "播放清單篩選器正規表達式",
"sidebarPlaylistListFilterRegex_placeholder": "範例: ^Daily Mix.*",
"sidebarPlaylistListFilterRegex": "播放清單過濾器正規表達式",
"blurExplicitImages": "模糊露骨圖片",
"blurExplicitImages_description": "標記為露骨的專輯和歌曲封面將被模糊",
"releaseChannel_optionAlpha": "Alpha (每日建構版)",
"releaseChannel_optionAlpha": "alpha (每日建構版)",
"analyticsEnable": "傳送基於使用情況的分析報告",
"analyticsEnable_description": "匿名化的使用情況資料會傳送給開發者,以協助改進應用程式",
"automaticUpdates": "自動更新",
@@ -786,49 +783,22 @@
"autosaveCount_description": "在儲存佇列之前,有多少曲目更改。1(最小)表示每次歌曲更改",
"spotify_description": "在藝人與專輯頁面顯示 Spotify 的連結",
"spotify": "顯示 Spotify 的連結",
"nativeSpotify_description": "在 Spotify 應用程式而非瀏覽器中開啟",
"nativeSpotify_description": "在 Spotify 應用程式中開啟,而非瀏覽器中開啟",
"nativeSpotify": "使用 Spotify 應用程式",
"sidePlayQueueLayout": "側邊播放佇列佈局",
"sidePlayQueueLayout_description": "設定吸附側邊播放佇列的佈局",
"sidePlayQueueLayout_optionHorizontal": "水平",
"sidePlayQueueLayout_optionVertical": "垂直",
"listenbrainz_description": "在藝/專輯頁面上顯示 ListenBrainz 的連結",
"listenbrainz_description": "在藝術家/專輯頁面上顯示 ListenBrainz 的連結",
"listenbrainz": "顯示 ListenBrainz 連結",
"qobuz_description": "在藝/專輯頁面上顯示 Qobuz 的連結",
"qobuz_description": "在藝術家/專輯頁面上顯示 Qobuz 的連結",
"qobuz": "顯示 Qobuz 連結",
"waveformLoadingDelay": "波形載入延遲",
"waveformLoadingDelay_description": "載入波形前的延遲(以秒為單位)。如果您在使用網頁播放器時遇到卡頓,請增加此值。",
"playerbarWaveformStretch": "波形拉伸",
"playerbarWaveformStretch_description": "拉伸波形來填補可用空間",
"preventSuspendOnPlayback_description": "音樂播放時防止應用程式進入休眠",
"preventSuspendOnPlayback": "在播放時防止應用程式暫停",
"hotkey_listShowPlayingSong": "在清單中顯示正在播放的歌曲",
"sidebarPlaylistFolders_description": "為名稱中包含配置分隔符的播放清單建立資料夾檢視",
"sidebarPlaylistFolders": "啟用資料夾",
"sidebarPlaylistFolderSeparator_description": "在播放清單名稱中分隔資料夾級別的字元(或字串)",
"sidebarPlaylistFolderSeparator": "資料夾分隔符",
"sidebarPlaylistFolderView_description": "資料夾在側邊欄中顯示的方式",
"sidebarPlaylistFolderView": "資料夾檢視",
"sidebarPlaylistFolderView_optionSingle": "單一資料夾",
"sidebarPlaylistFolderView_optionTree": "樹狀顯示",
"sidebarPlaylistFolderView_optionNavigation": "導覽視圖",
"sidebarPlaylistFolderTreeIndent_description": "每個樹狀層級縮排的像素",
"sidebarPlaylistFolderTreeIndent": "樹狀縮排",
"sidebarPlaylistFolderTreeLineColor_description": "樹狀連接線的顏色(留空則使用佈景主題預設值)",
"sidebarPlaylistFolderTreeLineColor": "樹狀線條顏色",
"sidebarPlaylistMode_description": "各播放清單在側邊欄列表中的顯示方式",
"sidebarPlaylistMode": "側邊欄播放清單模式",
"sidebarPlaylistMode_optionCompact": "緊湊",
"sidebarPlaylistMode_optionExpanded": "展開",
"autoDJ_mode": "模式",
"autoDJ_mode_albums": "專輯",
"autoDJ_mode_description": "選擇將歌曲或整張專輯加入佇列",
"autoDJ_mode_songs": "歌曲",
"autoDJ_enabled": "啟用Auto DJ",
"autoDJ_albumStrategy": "專輯選擇模式",
"autoDJ_songStrategy": "歌曲選擇模式",
"autoDJ_strategy_option_library_random": "隨機",
"autoDJ_strategy_option_similar": "相似"
"preventSuspendOnPlayback": "在播放時防止應用程式暫停"
},
"table": {
"config": {
@@ -869,7 +839,7 @@
"album": "$t(entity.album, {\"count\": 1})",
"albumArtist": "$t(entity.albumArtist, {\"count\": 1})",
"artist": "$t(entity.artist, {\"count\": 1})",
"bpm": "$t(common.bpm)",
"bpm": "$t(common.BPM)",
"biography": "$t(common.biography)",
"bitrate": "$t(common.bitrate)",
"channels": "$t(common.channel, {\"count\": 2})",
@@ -888,7 +858,7 @@
"size": "$t(common.size)",
"title": "$t(common.title)",
"titleCombined": "$t(common.title)(合併)",
"trackNumber": "曲目",
"trackNumber": "曲目編號",
"year": "$t(common.year)",
"rating": "$t(common.rating)",
"codec": "$t(common.codec)",
@@ -912,11 +882,11 @@
"column": {
"album": "專輯",
"albumArtist": "專輯藝人",
"albumCount": "專輯",
"artist": "藝人",
"albumCount": "$t(entity.album, {\"count\": 2})",
"artist": "$t(entity.artist, {\"count\": 1})",
"biography": "簡介",
"bitrate": "位元率",
"channels": "聲道",
"channels": "$t(common.channel, {\"count\": 2})",
"comment": "評論",
"dateAdded": "新增日期",
"discNumber": "光碟",
@@ -924,43 +894,43 @@
"lastPlayed": "最後播放",
"path": "路徑",
"playCount": "播放次數",
"rating": "評",
"releaseDate": "發日期",
"rating": "評",
"releaseDate": "發日期",
"releaseYear": "年份",
"genre": "曲風",
"genre": "$t(entity.genre, {\"count\": 1})",
"bpm": "BPM",
"songCount": "曲目",
"songCount": "$t(entity.track, {\"count\": 2})",
"title": "標題",
"trackNumber": "曲目",
"size": "大小",
"codec": "編解碼器",
"trackNumber": "曲目編號",
"size": "$t(common.size)",
"codec": "$t(common.codec)",
"owner": "擁有者",
"bitDepth": "位元深度",
"sampleRate": "取樣率"
"bitDepth": "$t(common.bitDepth)",
"sampleRate": "$t(common.sampleRate)"
}
},
"action": {
"addToFavorites": "新增$t(entity.favorite, {\"count\": 2})",
"clearQueue": "清空佇列",
"createPlaylist": "建立 $t(entity.playlist, {\"count\": 1})",
"deletePlaylist": "刪除 $t(entity.playlist, {\"count\": 1})",
"addToPlaylist": "新增$t(entity.playlist, {\"count\": 1})",
"addToFavorites": "新增$t(entity.favorite, {\"count\": 2})",
"clearQueue": "清空播放佇列",
"createPlaylist": "建立$t(entity.playlist, {\"count\": 1})",
"deletePlaylist": "刪除$t(entity.playlist, {\"count\": 1})",
"addToPlaylist": "新增$t(entity.playlist, {\"count\": 1})",
"deselectAll": "取消全選",
"editPlaylist": "編輯 $t(entity.playlist, {\"count\": 1})",
"goToPage": "前往頁面",
"moveToBottom": "移至底部",
"moveToTop": "移至頂部",
"refresh": "$t(common.refresh)",
"removeFromFavorites": "從 $t(entity.favorite, {\"count\": 2}) 移除",
"removeFromPlaylist": "從 $t(entity.playlist, {\"count\": 1}) 移除",
"removeFromQueue": "從佇列中移除",
"removeFromFavorites": "從$t(entity.favorite, {\"count\": 2})移除",
"removeFromPlaylist": "從$t(entity.playlist, {\"count\": 1})移除",
"removeFromQueue": "從播放佇列中移除",
"setRating": "評分",
"toggleSmartPlaylistEditor": "切換 $t(entity.smartPlaylist) 編輯器",
"viewPlaylists": "查看 $t(entity.playlist, {\"count\": 2})",
"toggleSmartPlaylistEditor": "切換$t(entity.smartPlaylist)編輯器",
"viewPlaylists": "查看$t(entity.playlist, {\"count\": 2})",
"moveToNext": "移至下一項",
"openIn": {
"lastfm": "在 Last.fm開啟",
"musicbrainz": "在 MusicBrainz 開啟",
"lastfm": "在Last.fm開啟",
"musicbrainz": "在MusicBrainz開啟",
"spotify": "在 Spotify 中開啟",
"listenbrainz": "在 ListenBrainz 中開啟",
"qobuz": "在 Qobuz 中開啟"
@@ -981,9 +951,7 @@
"addOrRemoveFromSelection": "新增或移除選取項目",
"selectAll": "全選",
"selectRangeOfItems": "批量選取",
"goToCurrent": "前往當前項目",
"expandAllFolders": "展開所有資料夾",
"collapseAllFolders": "摺疊所有資料夾"
"goToCurrent": "前往當前項目"
},
"entity": {
"album_other": "專輯",
@@ -998,39 +966,39 @@
"genreWithCount_other": "{{count}} 種曲風",
"playlist_other": "播放清單",
"playlistWithCount_other": "{{count}} 個播放清單",
"smartPlaylist": "智慧 $t(entity.playlist, {\"count\": 1})",
"smartPlaylist": "智慧$t(entity.playlist, {\"count\": 1})",
"track_other": "曲目",
"trackWithCount_other": "{{count}} 曲目",
"trackWithCount_other": "{{count}} 曲目",
"albumWithCount_other": "{{count}} 張專輯",
"play_other": "{{count}} 次播放",
"play_other": "{{count}}次播放",
"song_other": "歌曲",
"radioStation_other": "電台",
"radioStationWithCount_other": "{{count}} 個電台"
},
"filter": {
"albumCount": "$t(entity.album, {\"count\": 2}) 數",
"albumCount": "$t(entity.album, {\"count\": 2})數",
"artist": "$t(entity.artist, {\"count\": 1})",
"biography": "簡介",
"biography": "個人簡介",
"bitrate": "位元率",
"bpm": "BPM",
"channels": "$t(common.channel, {\"count\": 2})",
"comment": "評論",
"communityRating": "社群評分",
"criticRating": "評論家評分",
"dateAdded": "新增日期",
"dateAdded": "新增日期",
"disc": "光碟",
"duration": "時長",
"id": "ID",
"fromYear": "從年份",
"genre": "$t(entity.genre, {\"count\": 1})",
"isCompilation": "是否為合輯",
"isFavorited": "是否為收藏",
"isPublic": "是否為公開",
"isRated": "是否已評分",
"isCompilation": "為合輯",
"isFavorited": "收藏",
"isPublic": "公開",
"isRated": "已評分",
"name": "名稱",
"note": "注釋",
"isRecentlyPlayed": "是否最近播放過",
"lastPlayed": "上次播放",
"isRecentlyPlayed": "最近播放過",
"lastPlayed": "上次播放",
"mostPlayed": "播放最多",
"owner": "$t(common.owner)",
"path": "路徑",
@@ -1039,16 +1007,16 @@
"rating": "評分",
"recentlyPlayed": "最近播放",
"recentlyUpdated": "最近更新",
"releaseDate": "發日期",
"releaseDate": "發日期",
"songCount": "曲目數",
"album": "$t(entity.album, {\"count\": 1})",
"albumArtist": "$t(entity.albumArtist, {\"count\": 1})",
"favorited": "已收藏",
"recentlyAdded": "最近新增",
"releaseYear": "發年份",
"releaseYear": "發年份",
"search": "搜尋",
"title": "標題",
"toYear": "年份",
"toYear": "年份",
"trackNumber": "曲目",
"explicitStatus": "$t(common.explicitStatus)",
"sortName": "排序名稱",
@@ -1066,8 +1034,8 @@
"success": "伺服器新增成功",
"title": "新增伺服器",
"error_savePassword": "儲存密碼時出現錯誤",
"ignoreCors": "忽略 CORS $t(common.restartRequired)",
"ignoreSsl": "忽略 SSL $t(common.restartRequired)",
"ignoreCors": "忽略 cors $t(common.restartRequired)",
"ignoreSsl": "忽略 ssl $t(common.restartRequired)",
"input_preferInstantMix": "偏好即時混音",
"input_preferInstantMixDescription": "僅使用即時混音功能來取得相似歌曲。若您擁有能修改此行為的外掛,此功能將相當實用",
"input_preferRemoteUrl": "優先使用公開網址",
@@ -1102,7 +1070,7 @@
"title": "查詢編輯器",
"addRuleGroup": "新增規則群組",
"removeRuleGroup": "移除規則群組",
"resetToDefault": "重置為預設",
"resetToDefault": "恢復為預設",
"clearFilters": "清除篩選"
},
"updateServer": {
@@ -1130,8 +1098,8 @@
"successMustClick": "分享建立成功,點擊此處開啟"
},
"privateMode": {
"enabled": "已啟用私人模式,播放狀態現在將對外部整合功能隱藏",
"disabled": "已停用私人模式,啟用的外部整合功能現在可查看播放狀態",
"enabled": "已啟用私人模式,播放狀態將對外部整合隱藏",
"disabled": "已停用私人模式,播放狀態現對已啟用的外部整合可見",
"title": "私人模式"
},
"largeFetchConfirmation": {
@@ -1143,16 +1111,11 @@
"input_genre": "$t(entity.genre, {\"count\": 1})",
"input_limit": "多少曲目?",
"input_minYear": "起始年份",
"input_maxYear": "年份",
"input_played": "播放篩選器",
"input_maxYear": "結束年份",
"input_played": "播放過濾器",
"input_played_optionAll": "所有曲目",
"input_played_optionUnplayed": "僅未播放的曲目",
"input_played_optionPlayed": "僅播放過的曲目",
"input_kind_albums": "專輯",
"input_kind_songs": "歌曲",
"input_kind": "隨機選取",
"input_limit_albums": "專輯數量?",
"input_limit_songs": "歌曲數量?"
"input_played_optionPlayed": "僅播放過的曲目"
},
"createRadioStation": {
"success": "電台建立成功",
@@ -1170,7 +1133,7 @@
"input_offset": "$t(setting.lyricOffset)"
},
"editRadioStation": {
"success": "電更新成功"
"success": "電更新成功"
}
},
"releaseType": {
@@ -1189,7 +1152,7 @@
"fieldRecording": "現場錄音",
"demo": "Demo",
"interview": "訪談",
"live": "現場演出",
"live": "Live",
"mixtape": "混音帶",
"remix": "Remix",
"soundtrack": "原聲帶",
@@ -1381,7 +1344,7 @@
}
},
"systemAudioCaptureFailed": "無法開始擷取:{{message}}",
"systemAudioNoAudioTrack": "沒有回傳任何曲目。確保在提示時啟用音訊擷取。",
"systemAudioNoAudioTrack": "沒有回傳任何音軌。確保在提示時啟用音訊擷取。",
"systemAudioConsentAllow": "允許",
"systemAudioConsentBody": "此視覺化器需要存取系統音訊才能運作",
"systemAudioConsentDecline": "拒絕",
-15
View File
@@ -1,15 +0,0 @@
export const disableAutoUpdates = () => {
return process.env['DISABLE_AUTO_UPDATES'];
};
export const isMacOS = () => {
return process.platform === 'darwin';
};
export const isWindows = () => {
return process.platform === 'win32';
};
export const isLinux = () => {
return process.platform === 'linux';
};
-1
View File
@@ -4,4 +4,3 @@ import './player';
import './remote';
import './settings';
import './discord-rpc';
import './visualizer';
+1 -1
View File
@@ -72,7 +72,7 @@ export const orderSearchResults = (args: {
searchResults = Array.from(combinedResults.values());
} else {
searchResults = fuse.search({
searchResults = fuse.search<InternetProviderLyricSearchResponse>({
...(params.artist && { artist: params.artist }),
...(params.name && { name: params.name }),
});
+2 -9
View File
@@ -7,10 +7,9 @@ import { pid } from 'node:process';
import process from 'process';
import { getMainWindow, sendToastToRenderer } from '../../../index';
import { createLog } from '../../../utils';
import { createLog, isMacOS, isWindows } from '../../../utils';
import { store } from '../settings';
import { isMacOS, isWindows } from '/@/main/env';
import { PlayerData } from '/@/shared/types/domain-types';
declare module 'node-mpv';
@@ -120,14 +119,8 @@ const createMpv = async (data: {
}): Promise<MpvAPI> => {
const { binaryPath, extraParameters, properties } = data;
const resolvedBinaryPath = await resolveMpvBinaryPath(binaryPath);
const normalizedExtraParameters = (extraParameters ?? [])
.map((param) => param.trim())
.filter((param) => param.length > 0);
const params = uniq([
...DEFAULT_MPV_PARAMETERS(normalizedExtraParameters),
...normalizedExtraParameters,
]);
const params = uniq([...DEFAULT_MPV_PARAMETERS(extraParameters), ...(extraParameters || [])]);
const mpv = new MpvAPI(
{
+1 -1
View File
@@ -1,8 +1,8 @@
import { BrowserWindow, globalShortcut, systemPreferences } from 'electron';
import { isLinux, isMacOS } from '../../../utils';
import { store } from '../settings';
import { isLinux, isMacOS } from '/@/main/env';
import { PlayerType } from '/@/shared/types/types';
export const enableMediaKeys = (window: BrowserWindow | null) => {
+1 -1
View File
@@ -9,8 +9,8 @@ import { deflate, gzip } from 'zlib';
import manifest from './manifest.json';
import { isLinux } from '/@/main/env';
import { getMainWindow } from '/@/main/index';
import { isLinux } from '/@/main/utils';
import { QueueSong } from '/@/shared/types/domain-types';
import { ClientEvent, ServerEvent } from '/@/shared/types/remote-types';
import { PlayerRepeat, PlayerStatus, SongState } from '/@/shared/types/types';
+1 -120
View File
@@ -1,18 +1,7 @@
import type { TitleTheme } from '/@/shared/types/types';
import type { FSWatcher } from 'fs';
import {
app,
BrowserWindow,
dialog,
ipcMain,
nativeTheme,
OpenDialogOptions,
safeStorage,
shell,
} from 'electron';
import { app, dialog, ipcMain, nativeTheme, OpenDialogOptions, safeStorage } from 'electron';
import Store from 'electron-store';
import { promises as fs, watch as fsWatch } from 'fs';
import path from 'path';
const getFrame = () => {
@@ -37,67 +26,6 @@ const storePath = isDevelopment
? path.normalize(`${defaultUserDataPath}-dev`)
: path.normalize(defaultUserDataPath);
const CUSTOM_CSS_FILENAME = 'custom.css';
const customCssPath = path.join(storePath, CUSTOM_CSS_FILENAME);
let customCssWatcher: FSWatcher | null = null;
let customCssDebounce: NodeJS.Timeout | null = null;
const readCustomCss = async (): Promise<{ content: string; exists: boolean }> => {
try {
const content = await fs.readFile(customCssPath, 'utf8');
return { content, exists: true };
} catch (error) {
const fsError = error as NodeJS.ErrnoException;
if (fsError.code === 'ENOENT') {
return { content: '', exists: false };
}
console.error('Failed to read custom css file', error);
return { content: '', exists: false };
}
};
const notifyCustomCssUpdate = async () => {
const { content, exists } = await readCustomCss();
BrowserWindow.getAllWindows().forEach((window) => {
window.webContents.send('custom-css-updated', {
content,
exists,
path: customCssPath,
});
});
};
const scheduleCustomCssUpdate = () => {
if (customCssDebounce) {
clearTimeout(customCssDebounce);
}
customCssDebounce = setTimeout(() => {
notifyCustomCssUpdate().catch((error) => {
console.error('Failed to broadcast custom css update', error);
});
}, 100);
};
const startCustomCssWatcher = async () => {
if (customCssWatcher) return;
try {
await fs.mkdir(storePath, { recursive: true });
customCssWatcher = fsWatch(storePath, (eventType, filename) => {
if (!filename) return;
if (filename.toString() !== CUSTOM_CSS_FILENAME) return;
if (eventType === 'change' || eventType === 'rename') {
scheduleCustomCssUpdate();
}
});
} catch (error) {
console.error('Failed to watch custom css file', error);
}
};
export const store = new Store<any>({
beforeEachMigration: (_store, context) => {
console.log(`settings migrate from ${context.fromVersion}${context.toVersion}`);
@@ -141,14 +69,6 @@ ipcMain.on('settings-set', (__event, data: { property: string; value: any }) =>
}
});
ipcMain.handle('settings-set-sync', (__event, data: { property: string; value: any }) => {
if (data.value === null) {
store.delete(data.property);
} else {
store.set(data.property, data.value);
}
});
ipcMain.handle('password-get', (_event, server: string): null | string => {
if (safeStorage.isEncryptionAvailable()) {
const servers = store.get('server') as Record<string, string> | undefined;
@@ -200,42 +120,3 @@ ipcMain.handle('open-file-selector', async (_event, options: OpenDialogOptions)
return result.filePaths[0] || null;
});
ipcMain.handle('custom-css-get', async () => {
const { content, exists } = await readCustomCss();
return {
content,
exists,
path: customCssPath,
};
});
ipcMain.handle('custom-css-save', async (_event, data: { content: string }) => {
const content = typeof data?.content === 'string' ? data.content : '';
await fs.mkdir(storePath, { recursive: true });
await fs.writeFile(customCssPath, content, 'utf8');
await notifyCustomCssUpdate();
return true;
});
ipcMain.handle('custom-css-open-folder', async () => {
await fs.mkdir(storePath, { recursive: true });
await shell.openPath(storePath);
return true;
});
app.whenReady()
.then(() => startCustomCssWatcher())
.catch((error) => console.error('Failed to start custom css watcher', error));
app.on('before-quit', () => {
if (customCssWatcher) {
customCssWatcher.close();
customCssWatcher = null;
}
if (customCssDebounce) {
clearTimeout(customCssDebounce);
customCssDebounce = null;
}
});
@@ -1,30 +0,0 @@
import { ipcMain } from 'electron';
import { getMpvInstance } from '../player';
import { store } from '../settings';
import { PlayerType } from '/@/shared/types/types';
let isLocalVisualizerSurfaceVisible = false;
export const setLocalVisualizerSurfaceVisible = (visible: boolean) => {
isLocalVisualizerSurfaceVisible = visible;
};
export const canHandleVisualizerDisplayMedia = (): boolean => {
const playbackType = store.get('playbackType', PlayerType.WEB) as PlayerType;
if (playbackType !== PlayerType.LOCAL) {
return false;
}
if (!isLocalVisualizerSurfaceVisible) {
return false;
}
return Boolean(getMpvInstance()?.isRunning());
};
ipcMain.on('visualizer-set-local-surface-visible', (_event, visible: boolean) => {
setLocalVisualizerSurfaceVisible(Boolean(visible));
});
+25 -108
View File
@@ -16,7 +16,6 @@ import {
protocol,
Rectangle,
screen,
session,
shell,
Tray,
} from 'electron';
@@ -31,12 +30,18 @@ import packageJson from '../../package.json';
import { disableMediaKeys, enableMediaKeys } from './features/core/player/media-keys';
import { shutdownServer } from './features/core/remote';
import { store } from './features/core/settings';
import { canHandleVisualizerDisplayMedia } from './features/core/visualizer';
import MenuBuilder, { MenuPlaybackState } from './menu';
import {
autoUpdaterLogInterface,
createLog,
disableAutoUpdates,
hotkeyToElectronAccelerator,
isLinux,
isMacOS,
isWindows,
} from './utils';
import './features';
import { autoUpdaterLogInterface, createLog, hotkeyToElectronAccelerator } from './utils';
import { disableAutoUpdates, isLinux, isMacOS, isWindows } from '/@/main/env';
import { PlayerRepeat, PlayerStatus, PlayerType, TitleTheme } from '/@/shared/types/types';
const ALPHA_UPDATER_CONFIG: {
@@ -252,9 +257,7 @@ function createAlphaUpdaterInstance(): AppImageUpdater | MacUpdater | NsisUpdate
return new NsisUpdater(ALPHA_UPDATER_CONFIG);
}
protocol.registerSchemesAsPrivileged([
{ privileges: { bypassCSP: true, corsEnabled: true }, scheme: 'feishin' },
]);
protocol.registerSchemesAsPrivileged([{ privileges: { bypassCSP: true }, scheme: 'feishin' }]);
process.on('uncaughtException', (error: any) => {
console.error('Error in main process', error);
@@ -282,16 +285,6 @@ let currentRepeatMode: PlayerRepeat = PlayerRepeat.NONE;
let currentSidebarCollapsed = false;
let currentShuffleEnabled = false;
let playbackMenuAccelerators: MenuPlaybackState['accelerators'] = {};
let inputFocused = false;
ipcMain.on('input-focus-state', (_event, focused: boolean) => {
const next = !!focused;
if (inputFocused === next) return;
inputFocused = next;
if (isMacOS()) {
rebuildMainMenu();
}
});
if (process.env.NODE_ENV === 'production') {
import('source-map-support').then((sourceMapSupport) => {
@@ -337,7 +330,7 @@ if (isDevelopment) {
}
const RESOURCES_PATH = app.isPackaged
? path.join(path.dirname(app.getAppPath()), 'assets')
? path.join(process.resourcesPath, 'assets')
: path.join(__dirname, '../../assets');
const getAssetPath = (...paths: string[]): string => {
@@ -352,7 +345,7 @@ const rebuildMainMenu = () => {
if (!menuBuilder || !mainWindow) return;
menuBuilder.buildMenu({
accelerators: inputFocused ? {} : playbackMenuAccelerators,
accelerators: playbackMenuAccelerators,
playbackStatus: currentPlaybackStatus,
privateMode: currentPrivateMode,
repeatMode: currentRepeatMode,
@@ -483,15 +476,6 @@ const createTray = () => {
tray.setContextMenu(contextMenu);
};
const validateUrl = (url: string): boolean => {
// Minor security, really. Enforce only loading websites (http/https). file://
// URLs and the like should've already been blocked, but this is another check.
// Note that arbitrary web URLs are still allowed under this scheme, although
// that should really only be hit by Subsonic share url (or if artist homepage
// is allowed for ND extensions)
return url.startsWith('http://') || url.startsWith('https://');
};
async function createWindow(first = true): Promise<void> {
if (isDevelopment) {
await installExtensions().catch(console.log);
@@ -531,9 +515,9 @@ async function createWindow(first = true): Promise<void> {
backgroundThrottling: false,
contextIsolation: true,
devTools: true,
nodeIntegration: false,
nodeIntegration: true,
preload: join(__dirname, '../preload/index.js'),
sandbox: true,
sandbox: false,
webSecurity: !store.get('ignore_cors'),
},
width: 1440,
@@ -745,18 +729,11 @@ async function createWindow(first = true): Promise<void> {
// Open URLs in the user's browser
mainWindow.webContents.setWindowOpenHandler((edata) => {
if (validateUrl(edata.url)) {
shell.openExternal(edata.url);
}
shell.openExternal(edata.url);
return { action: 'deny' };
});
mainWindow.webContents.session.setDisplayMediaRequestHandler((_request, callback) => {
if (!canHandleVisualizerDisplayMedia()) {
callback({});
return;
}
if (!isMacOS()) {
callback({ audio: 'loopback' });
return;
@@ -787,9 +764,7 @@ async function createWindow(first = true): Promise<void> {
nativeTheme.themeSource = theme || 'dark';
mainWindow.webContents.setWindowOpenHandler((details) => {
if (validateUrl(details.url)) {
shell.openExternal(details.url);
}
shell.openExternal(details.url);
return { action: 'deny' };
});
@@ -991,33 +966,14 @@ app.on('window-all-closed', () => {
}
});
const FONT_HEADERS = new Set([
const FONT_HEADERS = [
'font/collection',
'font/otf',
'font/sfnt',
'font/ttf',
'font/woff',
'font/woff2',
]);
const bytesToInt = (array: Uint8Array, length: number): number => {
let value = 0;
for (let i = 0; i < length; i++) {
value = (value << 8) + array[i];
}
return value;
};
const FONT_FOUR_BYTE_MAGIC_NUMBERS = new Set([
0x4f54544f, // font/otf
0x774f4632, // font/woff2
0x774f4646, // font/woff
]);
const FONT_FIVE_BYTE_MAGIC_NUMBERS = new Set([
0x0001000000, // ttf, collection, sfnt
]);
];
const singleInstance = isDevelopment ? true : app.requestSingleInstanceLock();
@@ -1038,9 +994,12 @@ if (!singleInstance) {
app.whenReady()
.then(() => {
protocol.handle('feishin', async () => {
const filePath = store.get('local_font_path');
if (typeof filePath !== 'string') {
protocol.handle('feishin', async (request) => {
const filePath = `file:${request.url.slice('feishin:'.length)}`;
const response = await net.fetch(filePath);
const contentType = response.headers.get('content-type');
if (!contentType || !FONT_HEADERS.includes(contentType)) {
getMainWindow()?.webContents.send('custom-font-error', filePath);
return new Response(null, {
@@ -1049,49 +1008,7 @@ if (!singleInstance) {
});
}
const response = await net.fetch('file:' + filePath);
const contentType = response.headers.get('content-type');
// On Linux, the mime type is included in the response header
// In this case, we can forward the response with no further processing
if (contentType && FONT_HEADERS.has(contentType)) {
return response;
}
// Otherwise, let's check the magic number to see if
// the file is a font type. This is either four or five bytes
const payload = await response.arrayBuffer();
const magicNumber = new Uint8Array(payload.slice(0, 5));
const fiveHex = bytesToInt(magicNumber, 5);
const fourHex = bytesToInt(magicNumber, 4);
if (
FONT_FIVE_BYTE_MAGIC_NUMBERS.has(fiveHex) ||
FONT_FOUR_BYTE_MAGIC_NUMBERS.has(fourHex)
) {
// We have to create a new response with the payload, since it has been read now
return new Response(payload, {
headers: response.headers,
});
}
getMainWindow()?.webContents.send('custom-font-error', filePath);
return new Response(null, {
status: 403,
statusText: 'Forbidden',
});
});
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [
"script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline' https://umami.jeffvli.org; style-src 'self' 'unsafe-inline'; media-src 'self' http: https: data: blob:; img-src 'self' http: https: data: blob:; connect-src 'self' http: https: ws: wss:; default-src 'self';",
],
},
});
return response;
});
createWindow();
+16
View File
@@ -18,6 +18,22 @@ if (process.env.NODE_ENV === 'development') {
};
}
export const disableAutoUpdates = () => {
return process.env['DISABLE_AUTO_UPDATES'];
};
export const isMacOS = () => {
return process.platform === 'darwin';
};
export const isWindows = () => {
return process.platform === 'win32';
};
export const isLinux = () => {
return process.platform === 'linux';
};
export const hotkeyToElectronAccelerator = (hotkey: string) => {
let accelerator = hotkey;
+3
View File
@@ -1,8 +1,11 @@
import { ElectronAPI } from '@electron-toolkit/preload';
import { PreloadApi } from './index';
declare global {
interface Window {
api: PreloadApi;
electron: ElectronAPI;
LEGACY_AUTHENTICATION?: boolean;
queryLocalFonts?: () => Promise<Font[]>;
REMOTE_URL?: string;
+5 -4
View File
@@ -1,4 +1,5 @@
import { contextBridge, webUtils } from 'electron';
import { electronAPI } from '@electron-toolkit/preload';
import { contextBridge } from 'electron';
import { autodiscover } from './autodiscover';
import { browser } from './browser';
@@ -10,14 +11,12 @@ import { mpris } from './mpris';
import { mpvPlayer, mpvPlayerListener } from './mpv-player';
import { remote } from './remote';
import { utils } from './utils';
import { visualizer } from './visualizer';
// Custom APIs for renderer
const api = {
autodiscover,
browser,
discordRpc,
getPathForFile: webUtils.getPathForFile,
ipc,
localSettings,
lyrics,
@@ -26,7 +25,6 @@ const api = {
mpvPlayerListener,
remote,
utils,
visualizer,
};
export type PreloadApi = typeof api;
@@ -36,11 +34,14 @@ export type PreloadApi = typeof api;
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI);
contextBridge.exposeInMainWorld('api', api);
} catch (error) {
console.error(error);
}
} else {
// @ts-ignore (define in dts)
window.electron = electronAPI;
// @ts-ignore (define in dts)
window.api = api;
}
+10
View File
@@ -8,11 +8,21 @@ const send = (channel: string, ...args: any[]) => {
ipcRenderer.send(channel, ...args);
};
const invoke = (channel: string, ...args: any[]) => {
return ipcRenderer.invoke(channel, ...args);
};
const on = (channel: string, listener: (event: any, ...args: any[]) => void) => {
ipcRenderer.on(channel, listener);
};
const removeListener = (channel: string, listener: (event: any, ...args: any[]) => void) => {
ipcRenderer.removeListener(channel, listener);
};
export const ipc = {
invoke,
on,
removeAllListeners,
removeListener,
send,
+3 -11
View File
@@ -1,4 +1,4 @@
import { ipcRenderer, OpenDialogOptions, webFrame } from 'electron';
import { ipcRenderer, IpcRendererEvent, OpenDialogOptions, webFrame } from 'electron';
import { TitleTheme } from '/@/shared/types/types';
@@ -9,13 +9,6 @@ const set = (
ipcRenderer.send('settings-set', { property, value });
};
const setSync = async (
property: string,
value: boolean | null | Record<string, unknown> | string | string[],
) => {
return ipcRenderer.invoke('settings-set-sync', { property, value });
};
const get = async (property: string) => {
return ipcRenderer.invoke('settings-get', { property });
};
@@ -48,8 +41,8 @@ const setZoomFactor = (zoomFactor: number) => {
webFrame.setZoomFactor(zoomFactor / 100);
};
const fontError = (cb: (file: string) => void) => {
ipcRenderer.on('custom-font-error', (_, file) => cb(file));
const fontError = (cb: (event: IpcRendererEvent, file: string) => void) => {
ipcRenderer.on('custom-font-error', cb);
};
const themeSet = (theme: TitleTheme): void => {
@@ -106,7 +99,6 @@ export const localSettings = {
passwordSet,
restart,
set,
setSync,
setZoomFactor,
themeSet,
};
+15 -11
View File
@@ -1,4 +1,4 @@
import { ipcRenderer } from 'electron';
import { ipcRenderer, IpcRendererEvent } from 'electron';
import { QueueSong } from '/@/shared/types/domain-types';
import { PlayerRepeat, PlayerStatus } from '/@/shared/types/types';
@@ -31,24 +31,28 @@ const updateSong = (song: QueueSong | undefined, imageUrl?: null | string) => {
ipcRenderer.send('update-song', song, imageUrl);
};
const requestSeek = (cb: (data: { offset: number }) => void) => {
ipcRenderer.on('request-seek', (_, data) => cb(data));
const requestSeek = (cb: (event: IpcRendererEvent, data: { offset: number }) => void) => {
ipcRenderer.on('request-seek', cb);
};
const requestPosition = (cb: (data: { position: number }) => void) => {
ipcRenderer.on('request-position', (_, data) => cb(data));
const requestPosition = (cb: (event: IpcRendererEvent, data: { position: number }) => void) => {
ipcRenderer.on('request-position', cb);
};
const requestToggleRepeat = (cb: (data: { repeat: PlayerRepeat }) => void) => {
ipcRenderer.on('mpris-request-toggle-repeat', (_, data) => cb(data));
const requestToggleRepeat = (
cb: (event: IpcRendererEvent, data: { repeat: PlayerRepeat }) => void,
) => {
ipcRenderer.on('mpris-request-toggle-repeat', cb);
};
const requestToggleShuffle = (cb: (data: { shuffle: boolean }) => void) => {
ipcRenderer.on('mpris-request-toggle-shuffle', (_, data) => cb(data));
const requestToggleShuffle = (
cb: (event: IpcRendererEvent, data: { shuffle: boolean }) => void,
) => {
ipcRenderer.on('mpris-request-toggle-shuffle', cb);
};
const requestVolume = (cb: (data: { volume: number }) => void) => {
ipcRenderer.on('request-volume', (_, data) => cb(data));
const requestVolume = (cb: (event: IpcRendererEvent, data: { volume: number }) => void) => {
ipcRenderer.on('request-volume', cb);
};
export const mpris = {
+37 -37
View File
@@ -1,4 +1,4 @@
import { ipcRenderer } from 'electron';
import { ipcRenderer, IpcRendererEvent } from 'electron';
import { PlayerData } from '/@/shared/types/domain-types';
@@ -102,76 +102,76 @@ const getAudioDevices = async () => {
return ipcRenderer.invoke('player-get-audio-devices');
};
const rendererAutoNext = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-auto-next', (_, data) => cb(data));
const rendererAutoNext = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-auto-next', cb);
};
const rendererCurrentTime = (cb: (data: number) => void) => {
ipcRenderer.on('renderer-player-current-time', (_, data) => cb(data));
const rendererCurrentTime = (cb: (event: IpcRendererEvent, data: number) => void) => {
ipcRenderer.on('renderer-player-current-time', cb);
};
const rendererNext = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-next', (_, data) => cb(data));
const rendererNext = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-next', cb);
};
const rendererPause = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-pause', (_, data) => cb(data));
const rendererPause = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-pause', cb);
};
const rendererPlay = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-play', (_, data) => cb(data));
const rendererPlay = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-play', cb);
};
const rendererPlayPause = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-play-pause', (_, data) => cb(data));
const rendererPlayPause = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-play-pause', cb);
};
const rendererPrevious = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-previous', (_, data) => cb(data));
const rendererPrevious = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-previous', cb);
};
const rendererStop = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-stop', (_, data) => cb(data));
const rendererStop = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-stop', cb);
};
const rendererSkipForward = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-skip-forward', (_, data) => cb(data));
const rendererSkipForward = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-skip-forward', cb);
};
const rendererSkipBackward = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-skip-backward', (_, data) => cb(data));
const rendererSkipBackward = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-skip-backward', cb);
};
const rendererVolumeUp = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-up', (_, data) => cb(data));
const rendererVolumeUp = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-up', cb);
};
const rendererVolumeDown = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-down', (_, data) => cb(data));
const rendererVolumeDown = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-down', cb);
};
const rendererVolumeMute = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-mute', (_, data) => cb(data));
const rendererVolumeMute = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-mute', cb);
};
const rendererToggleRepeat = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-toggle-repeat', (_, data) => cb(data));
const rendererToggleRepeat = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-toggle-repeat', cb);
};
const rendererToggleShuffle = (cb: (data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-toggle-shuffle', (_, data) => cb(data));
const rendererToggleShuffle = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-toggle-shuffle', cb);
};
const rendererQuit = (cb: () => void) => {
ipcRenderer.on('renderer-player-quit', () => cb());
const rendererQuit = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-player-quit', cb);
};
const rendererError = (cb: (data: string) => void) => {
ipcRenderer.on('renderer-player-error', (_, data) => cb(data));
const rendererError = (cb: (event: IpcRendererEvent, data: string) => void) => {
ipcRenderer.on('renderer-player-error', cb);
};
const rendererPlayerFallback = (cb: (data: boolean) => void) => {
ipcRenderer.on('renderer-player-fallback', (_, data) => cb(data));
const rendererPlayerFallback = (cb: (event: IpcRendererEvent, data: boolean) => void) => {
ipcRenderer.on('renderer-player-fallback', cb);
};
export const mpvPlayer = {
+16 -11
View File
@@ -1,28 +1,33 @@
import { ipcRenderer } from 'electron';
import { ipcRenderer, IpcRendererEvent } from 'electron';
import { QueueSong } from '/@/shared/types/domain-types';
import { PlayerStatus } from '/@/shared/types/types';
const requestFavorite = (
cb: (data: { favorite: boolean; id: string; serverId: string }) => void,
cb: (
event: IpcRendererEvent,
data: { favorite: boolean; id: string; serverId: string },
) => void,
) => {
ipcRenderer.on('request-favorite', (_, data) => cb(data));
ipcRenderer.on('request-favorite', cb);
};
const requestPosition = (cb: (data: { position: number }) => void) => {
ipcRenderer.on('request-position', (_, data) => cb(data));
const requestPosition = (cb: (event: IpcRendererEvent, data: { position: number }) => void) => {
ipcRenderer.on('request-position', cb);
};
const requestRating = (cb: (data: { id: string; rating: number; serverId: string }) => void) => {
ipcRenderer.on('request-rating', (_, data) => cb(data));
const requestRating = (
cb: (event: IpcRendererEvent, data: { id: string; rating: number; serverId: string }) => void,
) => {
ipcRenderer.on('request-rating', cb);
};
const requestSeek = (cb: (data: { offset: number }) => void) => {
ipcRenderer.on('request-seek', (_, data) => cb(data));
const requestSeek = (cb: (event: IpcRendererEvent, data: { offset: number }) => void) => {
ipcRenderer.on('request-seek', cb);
};
const requestVolume = (cb: (data: { volume: number }) => void) => {
ipcRenderer.on('request-volume', (_, data) => cb(data));
const requestVolume = (cb: (event: IpcRendererEvent, data: { volume: number }) => void) => {
ipcRenderer.on('request-volume', cb);
};
const setRemoteEnabled = (enabled: boolean): Promise<null | string> => {
+33 -72
View File
@@ -1,6 +1,6 @@
import { ipcRenderer, webFrame } from 'electron';
import { ipcRenderer, IpcRendererEvent, webFrame } from 'electron';
import { disableAutoUpdates, isLinux, isMacOS, isWindows } from '../main/env';
import { disableAutoUpdates, isLinux, isMacOS, isWindows } from '../main/utils';
const openItem = async (path: string) => {
return ipcRenderer.invoke('open-item', path);
@@ -10,44 +10,29 @@ const openApplicationDirectory = async () => {
return ipcRenderer.invoke('open-application-directory');
};
const getCustomCss = async (): Promise<
| undefined
| {
content: string;
exists: boolean;
path?: string;
}
> => {
return ipcRenderer.invoke('custom-css-get');
};
const saveCustomCss = async (content: string) => {
return ipcRenderer.invoke('custom-css-save', { content });
};
const openCustomCssFolder = async () => {
return ipcRenderer.invoke('custom-css-open-folder');
};
const customCssUpdatedListener = (
cb: (data: { content?: string; exists?: boolean; path?: string }) => void,
) => {
const listener = (_event: unknown, data: { content?: string; exists?: boolean }) => cb(data);
ipcRenderer.on('custom-css-updated', listener);
return () => {
ipcRenderer.removeListener('custom-css-updated', listener);
};
};
const playerErrorListener = (cb: (data: { code: number }) => void) => {
ipcRenderer.on('player-error-listener', (_, data) => cb(data));
const playerErrorListener = (cb: (event: IpcRendererEvent, data: { code: number }) => void) => {
ipcRenderer.on('player-error-listener', cb);
};
const mainMessageListener = (
cb: (data: { message: string; type: 'error' | 'info' | 'success' | 'warning' }) => void,
cb: (
event: IpcRendererEvent,
data: { message: string; type: 'error' | 'info' | 'success' | 'warning' },
) => void,
) => {
ipcRenderer.on('toast-from-main', (_, data) => cb(data));
ipcRenderer.on('toast-from-main', cb);
};
const logger = (
cb: (
event: IpcRendererEvent,
data: {
message: string;
type: 'debug' | 'error' | 'info' | 'verbose' | 'warning';
},
) => void,
) => {
ipcRenderer.send('logger', cb);
};
const download = (url: string) => {
@@ -58,14 +43,6 @@ const checkForUpdates = (): Promise<{ updateAvailable: boolean; version?: string
return ipcRenderer.invoke('app-check-for-updates');
};
const startPowerSaveBlocker = (full: boolean) => {
return ipcRenderer.invoke('power-save-blocker-start', { full });
};
const stopPowerSaveBlocker = () => {
return ipcRenderer.invoke('power-save-blocker-stop');
};
const forceGarbageCollection = (): boolean => {
try {
if (typeof global.gc === 'function') {
@@ -84,51 +61,41 @@ const forceGarbageCollection = (): boolean => {
}
};
const setInputFocused = (focused: boolean) => {
ipcRenderer.send('input-focus-state', focused);
const rendererOpenSettings = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-open-settings', cb);
};
const rendererOpenSettings = (cb: () => void) => {
ipcRenderer.on('renderer-open-settings', () => cb());
const rendererOpenCommandPalette = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-open-command-palette', cb);
};
const rendererOpenCommandPalette = (cb: () => void) => {
ipcRenderer.on('renderer-open-command-palette', () => cb());
const rendererOpenManageServers = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-open-manage-servers', cb);
};
const rendererOpenManageServers = (cb: () => void) => {
ipcRenderer.on('renderer-open-manage-servers', () => cb());
};
const rendererTogglePrivateMode = (cb: () => void) => {
const rendererTogglePrivateMode = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-toggle-private-mode', cb);
};
const rendererToggleSidebar = (cb: () => void) => {
ipcRenderer.on('renderer-toggle-sidebar', () => cb());
const rendererToggleSidebar = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-toggle-sidebar', cb);
};
const rendererOpenReleaseNotes = (cb: () => void) => {
ipcRenderer.on('renderer-open-release-notes', () => cb());
};
const rendererUpdateAvailable = (cb: (version: string) => void) => {
ipcRenderer.on('update-available', (_, version) => cb(version));
const rendererOpenReleaseNotes = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-open-release-notes', cb);
};
export const utils = {
checkForUpdates,
customCssUpdatedListener,
disableAutoUpdates,
download,
forceGarbageCollection,
getCustomCss,
isLinux,
isMacOS,
isWindows,
logger,
mainMessageListener,
openApplicationDirectory,
openCustomCssFolder,
openItem,
playerErrorListener,
rendererOpenCommandPalette,
@@ -137,12 +104,6 @@ export const utils = {
rendererOpenSettings,
rendererTogglePrivateMode,
rendererToggleSidebar,
rendererUpdateAvailable,
saveCustomCss,
separator: isWindows() ? '\\' : '/',
setInputFocused,
startPowerSaveBlocker,
stopPowerSaveBlocker,
};
export type Utils = typeof utils;
-11
View File
@@ -1,11 +0,0 @@
import { ipcRenderer } from 'electron';
const setLocalSurfaceVisible = (visible: boolean) => {
ipcRenderer.send('visualizer-set-local-surface-visible', visible);
};
export const visualizer = {
setLocalSurfaceVisible,
};
export type VisualizerApi = typeof visualizer;
-36
View File
@@ -54,15 +54,6 @@ export const contract = c.router({
400: jfType._response.error,
},
},
deleteArtistImage: {
body: null,
method: 'DELETE',
path: 'Items/:id/Images/Primary',
responses: {
204: jfType._response.deleteArtistImage,
400: jfType._response.error,
},
},
deletePlaylist: {
body: null,
method: 'DELETE',
@@ -72,15 +63,6 @@ export const contract = c.router({
400: jfType._response.error,
},
},
deletePlaylistImage: {
body: null,
method: 'DELETE',
path: 'Items/:id/Images/Primary',
responses: {
204: jfType._response.deletePlaylistImage,
400: jfType._response.error,
},
},
getAlbumArtistDetail: {
method: 'GET',
path: 'users/:userId/items/:id',
@@ -374,24 +356,6 @@ export const contract = c.router({
400: jfType._response.error,
},
},
uploadArtistImage: {
body: z.string(),
method: 'POST',
path: 'Items/:id/Images/Primary',
responses: {
204: jfType._response.uploadArtistImage,
400: jfType._response.error,
},
},
uploadPlaylistImage: {
body: z.string(),
method: 'POST',
path: 'Items/:id/Images/Primary',
responses: {
204: jfType._response.uploadPlaylistImage,
400: jfType._response.error,
},
},
});
const axiosClient = axios.create({});
@@ -1,4 +1,3 @@
import axios from 'axios';
import { set } from 'idb-keyval';
import chunk from 'lodash/chunk';
import filter from 'lodash/filter';
@@ -14,10 +13,6 @@ import { getFeatures, hasFeature, sortSongList, VersionInfo } from '/@/shared/ap
import {
albumArtistListSortMap,
albumListSortMap,
DeleteArtistImageArgs,
DeleteArtistImageResponse,
DeletePlaylistImageArgs,
DeletePlaylistImageResponse,
Folder,
genreListSortMap,
ImageArgs,
@@ -34,10 +29,6 @@ import {
SortOrder,
sortOrderMap,
Tag,
UploadArtistImageArgs,
UploadArtistImageResponse,
UploadPlaylistImageArgs,
UploadPlaylistImageResponse,
} from '/@/shared/types/domain-types';
import { ServerFeature } from '/@/shared/types/features-types';
@@ -72,94 +63,6 @@ const formatCommaDelimitedString = (value: string[]) => {
return value.join(',');
};
const getImageContentType = (bytes: Uint8Array): string => {
if (bytes[0] === 0x89 && bytes[1] === 0x50) {
return 'image/png';
}
if (bytes[0] === 0xff && bytes[1] === 0xd8) {
return 'image/jpeg';
}
if (bytes[0] === 0x47 && bytes[1] === 0x49) {
return 'image/gif';
}
if (bytes[0] === 0x52 && bytes[1] === 0x49) {
return 'image/webp';
}
return 'image/jpeg';
};
const uint8ArrayToBase64 = (bytes: Uint8Array): string => {
let binary = '';
const chunkSize = 0x8000;
for (let i = 0; i < bytes.length; i += chunkSize) {
const chunk = bytes.subarray(i, i + chunkSize);
binary += String.fromCharCode(...chunk);
}
return btoa(binary);
};
type JellyfinApiClientProps = DeletePlaylistImageArgs['apiClientProps'];
const deleteItemPrimaryImage = async (
apiClientProps: JellyfinApiClientProps,
id: string,
errorMessage: string,
): Promise<boolean> => {
const res = await jfApiClient({
...apiClientProps,
server: apiClientProps.server ?? null,
}).deleteArtistImage({
params: {
id,
},
});
if (res.status !== 204) {
throw new Error(errorMessage);
}
return true;
};
const uploadItemPrimaryImage = async (
apiClientProps: JellyfinApiClientProps,
id: string,
image: Uint8Array,
errorMessage: string,
): Promise<boolean> => {
const server = apiClientProps.server;
const serverUrl = getServerUrl(server);
if (!serverUrl) {
throw new Error('Server is required');
}
const contentType = getImageContentType(image);
const base64 = uint8ArrayToBase64(image);
const authHeader = createAuthHeader();
const authorization = server?.credential
? authHeader.concat(`, Token="${server.credential}"`)
: authHeader;
const res = await axios.post(`${serverUrl}/Items/${id}/Images/Primary`, base64, {
headers: {
Authorization: authorization,
'Content-Type': contentType,
},
signal: apiClientProps.signal,
});
if (res.status !== 204) {
throw new Error(errorMessage);
}
return true;
};
// Limit the query to 50 at a time to be *extremely* conservative on the
// length of the full URL, since the ids are part of the query string and
// not the POST body
@@ -177,14 +80,7 @@ const VERSION_INFO: VersionInfo = [
[ServerFeature.PUBLIC_PLAYLIST]: [1],
},
],
[
'10.0.0',
{
[ServerFeature.ARTIST_IMAGE_UPLOAD]: [1],
[ServerFeature.PLAYLIST_IMAGE_UPLOAD]: [1],
[ServerFeature.TAGS]: [1],
},
],
['10.0.0', { [ServerFeature.TAGS]: [1] }],
];
const JF_FIELDS = {
@@ -335,11 +231,6 @@ export const JellyfinController: InternalControllerEndpoint = {
id: res.body.Id,
};
},
deleteArtistImage: async (args: DeleteArtistImageArgs): Promise<DeleteArtistImageResponse> => {
const { apiClientProps, query } = args;
return deleteItemPrimaryImage(apiClientProps, query.id, 'Failed to delete artist image');
},
deleteFavorite: async (args) => {
const { apiClientProps, query } = args;
@@ -390,13 +281,6 @@ export const JellyfinController: InternalControllerEndpoint = {
return null;
},
deletePlaylistImage: async (
args: DeletePlaylistImageArgs,
): Promise<DeletePlaylistImageResponse> => {
const { apiClientProps, query } = args;
return deleteItemPrimaryImage(apiClientProps, query.id, 'Failed to delete playlist image');
},
getAlbumArtistDetail: async (args) => {
const { apiClientProps, query } = args;
@@ -527,12 +411,8 @@ export const JellyfinController: InternalControllerEndpoint = {
throw new Error('Failed to get album detail');
}
// Workaround for Jellyfin bug that returns items that share the same album name
const albumIdSet = new Set([query.id]);
const songs = songsRes.body.Items.filter((item) => albumIdSet.has(item.AlbumId!));
return jfNormalize.album(
{ ...res.body, Songs: songs },
{ ...res.body, Songs: songsRes.body.Items },
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
@@ -1182,9 +1062,7 @@ export const JellyfinController: InternalControllerEndpoint = {
throw new Error('Failed to get server info');
}
const defaultFeatures = {
[ServerFeature.REPORT_PLAYBACK]: [1],
};
const defaultFeatures = {};
const features = {
...defaultFeatures,
@@ -1793,17 +1671,6 @@ export const JellyfinController: InternalControllerEndpoint = {
return null;
}
if (query.event === 'stop') {
jfApiClient(apiClientProps).scrobbleStopped({
body: {
ItemId: query.id,
PositionTicks: position,
},
});
return null;
}
jfApiClient(apiClientProps).scrobbleProgress({
body: {
ItemId: query.id,
@@ -1974,28 +1841,6 @@ export const JellyfinController: InternalControllerEndpoint = {
return null;
},
uploadArtistImage: async (args: UploadArtistImageArgs): Promise<UploadArtistImageResponse> => {
const { apiClientProps, body, query } = args;
return uploadItemPrimaryImage(
apiClientProps,
query.id,
body.image,
'Failed to upload artist image',
);
},
uploadPlaylistImage: async (
args: UploadPlaylistImageArgs,
): Promise<UploadPlaylistImageResponse> => {
const { apiClientProps, body, query } = args;
return uploadItemPrimaryImage(
apiClientProps,
query.id,
body.image,
'Failed to upload playlist image',
);
},
};
function getLibraryId(musicFolderId?: string | string[]) {
@@ -100,6 +100,9 @@ const EXCLUDED_SONG_TAGS = new Set<string>(['disctotal', 'tracktotal']);
const numericSortCollator = new Intl.Collator(undefined, { numeric: true });
const collator = new Intl.Collator();
// Tags that use IDs as values as opposed to the tag value
const ID_TAGS = new Set<string>(['albumversion', 'mood']);
const excludeMissing = (server?: null | ServerListItemWithCredential) => {
if (!server) {
return undefined;
@@ -962,13 +965,13 @@ export const NavidromeController: InternalControllerEndpoint = {
if (!EXCLUDED_TAGS.has(tag.tagName)) {
if (tagsToValues.has(tag.tagName)) {
tagsToValues.get(tag.tagName)!.push({
id: tag.id,
id: ID_TAGS.has(tag.tagName) ? tag.id : tag.tagValue,
name: tag.tagValue,
});
} else {
tagsToValues.set(tag.tagName, [
{
id: tag.id,
id: ID_TAGS.has(tag.tagName) ? tag.id : tag.tagValue,
name: tag.tagValue,
},
]);
@@ -289,14 +289,6 @@ export const contract = c.router({
200: ssType._response.removeFavorite,
},
},
reportPlayback: {
method: 'GET',
path: 'reportPlayback.view',
query: ssType._parameters.reportPlayback,
responses: {
200: ssType._response.reportPlayback,
},
},
savePlayQueue: {
method: 'GET',
path: 'savePlayQueue.view',
@@ -366,10 +366,7 @@ export const SubsonicController: InternalControllerEndpoint = {
query.type === LibraryItem.ALBUM_ARTIST || query.type === LibraryItem.ARTIST
? query.id
: undefined,
id:
query.type === LibraryItem.SONG || query.type === LibraryItem.PLAYLIST_SONG
? query.id
: undefined,
id: query.type === LibraryItem.SONG ? query.id : undefined,
},
});
@@ -422,10 +419,7 @@ export const SubsonicController: InternalControllerEndpoint = {
query.type === LibraryItem.ALBUM_ARTIST || query.type === LibraryItem.ARTIST
? query.id
: undefined,
id:
query.type === LibraryItem.SONG || query.type === LibraryItem.PLAYLIST_SONG
? query.id
: undefined,
id: query.type === LibraryItem.SONG ? query.id : undefined,
},
});
@@ -1466,10 +1460,6 @@ export const SubsonicController: InternalControllerEndpoint = {
features.serverPlayQueue = [1];
}
if (subsonicFeatures[SubsonicExtensions.PLAYBACK_REPORT]) {
features.reportPlayback = [1];
}
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion };
},
getSimilarSongs: async (args) => {
@@ -2308,72 +2298,6 @@ export const SubsonicController: InternalControllerEndpoint = {
scrobble: async (args) => {
const { apiClientProps, query } = args;
if (hasFeature(apiClientProps.server, ServerFeature.REPORT_PLAYBACK)) {
if (query.submission || query.event === 'start') {
const res = await ssApiClient(apiClientProps).scrobble({
query: {
id: query.id,
submission: query.submission,
},
});
if (res.status !== 200) {
throw new Error('Failed to scrobble');
}
if (query.submission) {
return null;
}
}
const defaultParams = {
ignoreScrobble: true,
mediaId: query.id,
mediaType: query.mediaType,
playbackRate: query.playbackRate,
positionMs: query.position ?? 0,
};
const reportPlayback = (state: 'paused' | 'playing' | 'starting' | 'stopped') => {
return ssApiClient(apiClientProps).reportPlayback({
query: {
...defaultParams,
state,
},
});
};
const promises: Promise<any>[] = [];
switch (query.event) {
case 'pause':
promises.push(reportPlayback('paused'));
break;
case 'start':
promises.push(reportPlayback('starting'));
promises.push(reportPlayback('playing'));
break;
case 'stop':
promises.push(reportPlayback('stopped'));
break;
case 'unpause':
promises.push(reportPlayback('playing'));
break;
default:
break;
}
for (const promise of promises) {
const res = await promise;
if (res.status !== 200) {
throw new Error('Failed to report playback');
}
}
return null;
}
const res = await ssApiClient(apiClientProps).scrobble({
query: {
id: query.id,
+1 -113
View File
@@ -15,12 +15,7 @@ import { useCheckForUpdates } from '/@/renderer/hooks/use-check-for-updates';
import { useNativeMenuSync } from '/@/renderer/hooks/use-native-menu-sync';
import { useSyncSettingsToMain } from '/@/renderer/hooks/use-sync-settings-to-main';
import { AppRouter } from '/@/renderer/router/app-router';
import {
useCssSettings,
useHotkeySettings,
useLanguage,
useSettingsStoreActions,
} from '/@/renderer/store';
import { useCssSettings, useHotkeySettings, useLanguage } from '/@/renderer/store';
import { useAppTheme } from '/@/renderer/themes/use-app-theme';
import { sanitizeCss } from '/@/renderer/utils/sanitize';
import { WebAudio } from '/@/shared/types/types';
@@ -36,7 +31,6 @@ const UpdateAvailableDialog = lazy(() =>
);
const ipc = isElectron() ? window.api.ipc : null;
const utils = isElectron() ? window.api.utils : null;
export const App = () => {
return <ThemedApp />;
@@ -95,12 +89,10 @@ const AppEffects = () => (
<>
<SyncSettingsEffect />
<UpdateCheckEffect />
<CustomCssFileEffect />
<CssSettingsEffect />
<GlobalShortcutsEffect />
<LanguageEffect />
<NativeMenuSyncEffect />
<InputFocusEffect />
</>
);
@@ -149,71 +141,6 @@ const CssSettingsEffect = () => {
return null;
};
const CustomCssFileEffect = () => {
const { setSettings } = useSettingsStoreActions();
const { content } = useCssSettings();
const latestContentRef = useRef(content);
useEffect(() => {
latestContentRef.current = content;
}, [content]);
useEffect(() => {
if (!isElectron() || !utils) return;
let disposed = false;
const applyContent = (rawContent: string | undefined) => {
const sanitized = sanitizeCss(`<style>${rawContent ?? ''}`);
if (sanitized !== latestContentRef.current) {
setSettings({
css: {
content: sanitized,
},
});
}
};
const loadCustomCss = async () => {
try {
const result = await utils.getCustomCss();
if (disposed || !result) return;
if (!result.exists && latestContentRef.current) {
await utils.saveCustomCss(latestContentRef.current);
return;
}
applyContent(result.content);
} catch (error) {
console.error('Failed to load custom css', error);
}
};
const handleCustomCssUpdated = (data: { content?: string; exists?: boolean }) => {
if (disposed) return;
if (data?.exists === false) {
applyContent('');
return;
}
applyContent(data?.content);
};
const removeCustomCssUpdatedListener =
utils.customCssUpdatedListener(handleCustomCssUpdated);
loadCustomCss();
return () => {
disposed = true;
removeCustomCssUpdatedListener();
};
}, [setSettings]);
return null;
};
const GlobalShortcutsEffect = () => {
const { bindings } = useHotkeySettings();
@@ -243,42 +170,3 @@ const NativeMenuSyncEffect = () => {
return null;
};
const InputFocusEffect = () => {
useEffect(() => {
if (!isElectron()) return;
const handleFocusIn = (e: FocusEvent) => {
const target = e.target as Element | null;
if (
target instanceof HTMLInputElement ||
target instanceof HTMLTextAreaElement ||
(target instanceof HTMLElement && target.isContentEditable)
) {
window.api?.utils?.setInputFocused?.(true);
}
};
const handleFocusOut = (e: FocusEvent) => {
const related = e.relatedTarget as Element | null;
if (
related instanceof HTMLInputElement ||
related instanceof HTMLTextAreaElement ||
(related instanceof HTMLElement && related.isContentEditable)
) {
return;
}
window.api?.utils?.setInputFocused?.(false);
};
document.addEventListener('focusin', handleFocusIn);
document.addEventListener('focusout', handleFocusOut);
return () => {
document.removeEventListener('focusin', handleFocusIn);
document.removeEventListener('focusout', handleFocusOut);
};
}, []);
return null;
};
@@ -6,7 +6,7 @@ import styles from './drag-preview.module.css';
import { useItemImageUrl } from '/@/renderer/components/item-image/item-image';
import { Icon } from '/@/shared/components/icon/icon';
import { LibraryItem } from '/@/shared/types/domain-types';
import { DragData, DragTarget } from '/@/shared/types/drag-and-drop';
import { DragData } from '/@/shared/types/drag-and-drop';
interface DragPreviewProps {
data: DragData;
@@ -29,8 +29,7 @@ export const DragPreview = memo(({ data }: DragPreviewProps) => {
const { t } = useTranslation();
const itemCount = items.length;
const firstItem = items[0];
const folderName = data.type === DragTarget.SIDEBAR_PLAYLIST_FOLDER ? data.id[0] : undefined;
const itemName = folderName || (firstItem ? getItemName(firstItem) : 'Item');
const itemName = firstItem ? getItemName(firstItem) : 'Item';
const itemImage = useItemImageUrl({
id: (firstItem as { imageId: string })?.imageId,
@@ -51,9 +50,6 @@ export const DragPreview = memo(({ data }: DragPreviewProps) => {
</div>
) : (
<div className={styles['icon-container']}>
{data.type === DragTarget.SIDEBAR_PLAYLIST_FOLDER && (
<Icon icon="folder" size="xl" />
)}
{data.itemType === LibraryItem.ALBUM && <Icon icon="album" size="xl" />}
{data.itemType === LibraryItem.SONG && (
<Icon icon="itemSong" size="xl" />
@@ -1,26 +0,0 @@
import { ItemTableListColumnConfig } from '/@/renderer/components/item-list/types';
import { TableColumn } from '/@/shared/types/types';
const ROW_PLAY_CONTROL_COLUMN_IDS = [TableColumn.TRACK_NUMBER, TableColumn.ROW_INDEX] as const;
type RowPlayControlColumnId = (typeof ROW_PLAY_CONTROL_COLUMN_IDS)[number];
const isRowPlayControlColumnId = (columnId: TableColumn): columnId is RowPlayControlColumnId =>
ROW_PLAY_CONTROL_COLUMN_IDS.includes(columnId as RowPlayControlColumnId);
export const getRowPlayControlColumnId = (
columns: Array<Pick<ItemTableListColumnConfig, 'id'>>,
): null | TableColumn => {
for (const column of columns) {
if (isRowPlayControlColumnId(column.id)) {
return column.id;
}
}
return null;
};
export const isRowPlayControlColumn = (
columnId: TableColumn,
columns: Array<Pick<ItemTableListColumnConfig, 'id'>>,
): boolean => getRowPlayControlColumnId(columns) === columnId;
@@ -13,7 +13,7 @@ import { useListContext } from '/@/renderer/context/list-context';
import { eventEmitter } from '/@/renderer/events/event-emitter';
import { UserFavoriteEventPayload, UserRatingEventPayload } from '/@/renderer/events/events';
import { getListRefreshMutationKey } from '/@/renderer/features/shared/components/list-refresh-button';
import { LibraryItem, SortKeyRandom } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain-types';
export const getListQueryKeyName = (itemType: LibraryItem): string => {
switch (itemType) {
@@ -108,19 +108,8 @@ export const useItemListInfiniteLoader = ({
[serverId, itemType, query],
);
const isRandomSort = query?.sortBy === SortKeyRandom;
const fetchPage = useCallback(
async (pageNumber: number) => {
if (isRandomSort) {
const existingData =
queryClient.getQueryData<InfiniteLoaderCacheData>(dataQueryKey);
if (existingData?.pagesLoaded?.[pageNumber]) {
lastFetchedPageRef.current = Math.max(lastFetchedPageRef.current, pageNumber);
return;
}
}
const startIndex = pageNumber * itemsPerPage;
const queryParams = {
limit: itemsPerPage,
@@ -129,7 +118,6 @@ export const useItemListInfiniteLoader = ({
};
const result = await queryClient.fetchQuery({
gcTime: isRandomSort ? 1000 * 60 * 10 : 1000 * 15,
queryFn: async ({ signal }) => {
const result = await listQueryFn({
apiClientProps: { serverId, signal },
@@ -139,7 +127,6 @@ export const useItemListInfiniteLoader = ({
return result;
},
queryKey: queryKeys[getListQueryKeyName(itemType)].list(serverId, queryParams),
staleTime: isRandomSort ? 1000 * 60 * 10 : 1000 * 15,
});
// Update the query data with the fetched page
@@ -167,32 +154,13 @@ export const useItemListInfiniteLoader = ({
// Track the last fetched page
lastFetchedPageRef.current = Math.max(lastFetchedPageRef.current, pageNumber);
},
[
itemsPerPage,
query,
queryClient,
serverId,
dataQueryKey,
listQueryFn,
itemType,
isRandomSort,
],
[itemsPerPage, query, queryClient, serverId, dataQueryKey, listQueryFn, itemType],
);
// Reset the loaded pages and refetch current page when the query changes
useEffect(() => {
const currentDataQueryKey = JSON.stringify(dataQueryKey);
if (isRandomSort) {
const existingData = queryClient.getQueryData<InfiniteLoaderCacheData | undefined>(
dataQueryKey,
);
if (existingData?.dataMap && existingData.dataMap.size > 0) {
previousDataQueryKeyRef.current = currentDataQueryKey;
return;
}
}
if (previousDataQueryKeyRef.current === currentDataQueryKey || isRefetchingRef.current) {
return;
}
@@ -12,7 +12,7 @@ import { useListContext } from '/@/renderer/context/list-context';
import { eventEmitter } from '/@/renderer/events/event-emitter';
import { UserFavoriteEventPayload, UserRatingEventPayload } from '/@/renderer/events/events';
import { getListRefreshMutationKey } from '/@/renderer/features/shared/components/list-refresh-button';
import { LibraryItem, SortKeyRandom } from '/@/shared/types/domain-types';
import { LibraryItem } from '/@/shared/types/domain-types';
const getQueryKeyName = (itemType: LibraryItem): string => {
switch (itemType) {
@@ -76,8 +76,6 @@ export const useItemListPaginatedLoader = ({
const fetchRange = getFetchRange(currentPage, itemsPerPage);
const startIndex = fetchRange.startIndex;
const isRandomSort = query?.sortBy === SortKeyRandom;
const queryParams = useMemo(
() => ({
limit: itemsPerPage,
@@ -88,7 +86,7 @@ export const useItemListPaginatedLoader = ({
);
const { data } = useQuery({
gcTime: isRandomSort ? 1000 * 60 * 10 : 1000 * 15,
gcTime: 1000 * 15,
placeholderData: { items: getInitialData(itemsPerPage) },
queryFn: async ({ signal }) => {
const result = await listQueryFn({
@@ -99,7 +97,7 @@ export const useItemListPaginatedLoader = ({
return result;
},
queryKey: queryKeys[getQueryKeyName(itemType)].list(serverId, queryParams),
staleTime: isRandomSort ? 1000 * 60 * 10 : 1000 * 15,
staleTime: 1000 * 15,
});
const refreshMutation = useMutation({
@@ -1,74 +0,0 @@
import { ItemListStateActions } from '/@/renderer/components/item-list/helpers/item-list-state';
import { Album, AlbumArtist, Artist, LibraryItem, Song } from '/@/shared/types/domain-types';
import { Play } from '/@/shared/types/types';
type PlayableArtistItem = AlbumArtist | Artist;
interface PlayerQueueByDataActions {
addToQueueByData: (data: Song[], type: Play, playSongId?: string) => void;
}
interface PlayerQueueByFetchActions {
addToQueueByFetch: (
serverId: string,
ids: string[],
itemType: LibraryItem,
playType: Play,
) => void;
}
export const playSongFromItemListControl = ({
index,
internalState,
item,
meta,
player,
}: {
index?: number;
internalState?: ItemListStateActions;
item: Song;
meta?: Record<string, unknown>;
player: PlayerQueueByDataActions;
}) => {
const playType = (meta?.playType as Play) || Play.NOW;
const singleSongOnly = meta?.singleSongOnly === true;
if (singleSongOnly) {
player.addToQueueByData([item], playType, item.id);
return;
}
const items = internalState?.getData() as Song[];
if (index !== undefined && items) {
player.addToQueueByData(items, playType, item.id);
}
};
export const playAlbumFromItemListControl = ({
album,
meta,
player,
}: {
album: Album;
meta?: Record<string, unknown>;
player: PlayerQueueByFetchActions;
}) => {
const playType = (meta?.playType as Play) || Play.NOW;
player.addToQueueByFetch(album._serverId, [album.id], LibraryItem.ALBUM, playType);
};
export const playArtistFromItemListControl = ({
artist,
itemType,
meta,
player,
}: {
artist: PlayableArtistItem;
itemType: LibraryItem.ALBUM_ARTIST | LibraryItem.ARTIST;
meta?: Record<string, unknown>;
player: PlayerQueueByFetchActions;
}) => {
const playType = (meta?.playType as Play) || Play.NOW;
player.addToQueueByFetch(artist._serverId, [artist.id], itemType, playType);
};
@@ -16,13 +16,11 @@ export const useListHotkeys = ({
focused,
internalState,
itemType,
onShowPlayingSong,
}: {
controls: ItemControls;
focused: boolean;
internalState: ItemListStateActions;
itemType: LibraryItem;
onShowPlayingSong?: () => void;
}) => {
const { bindings } = useHotkeySettings();
const playButtonBehavior = usePlayButtonBehavior();
@@ -121,11 +119,5 @@ export const useListHotkeys = ({
}
},
],
[
bindings.listShowPlayingSong.hotkey,
() => {
onShowPlayingSong?.();
},
],
]);
};
@@ -27,7 +27,6 @@ import { PlayCountColumn } from './play-count-column';
import { RatingColumn } from './rating-column';
import { ReleaseDateColumn } from './release-date-column';
import { RowIndexColumn } from './row-index-column';
import { ItemDetailRowPlayControlCell } from './row-play-control-cell';
import { SampleRateColumn } from './sample-rate-column';
import { SizeColumn } from './size-column';
import { TitleArtistColumn } from './title-artist-column';
@@ -112,7 +111,6 @@ export {
GenreBadgeColumn,
GenreColumn,
ImageColumn,
ItemDetailRowPlayControlCell,
LastPlayedColumn,
PathColumn,
PlayCountColumn,
@@ -1,40 +1,23 @@
import styles from './row-index-column.module.css';
import { ItemDetailRowPlayControlCell } from './row-play-control-cell';
import { ItemDetailListCellProps } from './types';
import { useDetailRowPlayControl } from './use-detail-row-play-control';
import { isRowPlayControlColumn } from '/@/renderer/components/item-list/helpers/get-row-play-control-column';
import { useIsCurrentSong } from '/@/renderer/features/player/hooks/use-is-current-song';
import { usePlayerStatus } from '/@/renderer/store';
import { Icon } from '/@/shared/components/icon/icon';
import { TableColumn } from '/@/shared/types/types';
import { PlayerStatus } from '/@/shared/types/types';
const DefaultRowIndexColumn = ({ rowIndex }: ItemDetailListCellProps) => {
return <>{String((rowIndex ?? 0) + 1)}</>;
};
export const RowIndexColumn = ({ rowIndex, song }: ItemDetailListCellProps) => {
const status = usePlayerStatus();
const { isActive } = useIsCurrentSong(song);
const isPlaying = isActive && status === PlayerStatus.PLAYING;
const PlayableRowIndexColumn = (props: ItemDetailListCellProps) => {
const { handlePlay, isActive, isPlaying, showPlayControls } = useDetailRowPlayControl(props);
const indexContent = isActive ? (
<div className={styles.iconWrapper}>
<Icon fill="primary" icon={isPlaying ? 'mediaPlay' : 'mediaPause'} />
</div>
) : (
String((props.rowIndex ?? 0) + 1)
);
return (
<ItemDetailRowPlayControlCell
indexContent={indexContent}
onPlay={handlePlay}
showPlayControls={showPlayControls}
/>
);
};
export const RowIndexColumn = (props: ItemDetailListCellProps) => {
if (!props.columns || !isRowPlayControlColumn(TableColumn.ROW_INDEX, props.columns)) {
return <DefaultRowIndexColumn {...props} />;
if (isActive) {
return (
<div className={styles.iconWrapper}>
<Icon fill="primary" icon={isPlaying ? 'mediaPlay' : 'mediaPause'} />
</div>
);
}
return <PlayableRowIndexColumn {...props} />;
return <>{String((rowIndex ?? 0) + 1)}</>;
};
@@ -1,30 +0,0 @@
.cell-wrapper {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
min-height: 100%;
}
.play-target {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
.index-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
.icon-wrapper {
display: flex;
align-items: center;
justify-content: center;
}
@@ -1,39 +0,0 @@
import { ReactNode } from 'react';
import styles from './row-play-control-cell.module.css';
import { ItemRowPlayControls } from '/@/renderer/features/shared/components/item-row-play-controls';
import { HoverCard } from '/@/shared/components/hover-card/hover-card';
import { Play } from '/@/shared/types/types';
export const ItemDetailRowPlayControlCell = ({
indexContent,
onPlay,
showPlayControls,
}: {
indexContent: ReactNode;
onPlay: (playType: Play) => void;
showPlayControls: boolean;
}) => {
if (!showPlayControls) {
return <>{indexContent}</>;
}
return (
<div className={styles.cellWrapper}>
<HoverCard
offset={{ crossAxis: 32, mainAxis: 16 }}
openDelay={300}
position="top"
withArrow
>
<HoverCard.Target>
<div className={styles.playTarget}>{indexContent}</div>
</HoverCard.Target>
<HoverCard.Dropdown onClick={(e) => e.stopPropagation()}>
<ItemRowPlayControls onPlay={onPlay} />
</HoverCard.Dropdown>
</HoverCard>
</div>
);
};
@@ -1,46 +1,7 @@
import { ItemDetailRowPlayControlCell } from './row-play-control-cell';
import styles from './row-play-control-cell.module.css';
import { ItemDetailListCellProps } from './types';
import { useDetailRowPlayControl } from './use-detail-row-play-control';
import { isRowPlayControlColumn } from '/@/renderer/components/item-list/helpers/get-row-play-control-column';
import { Icon } from '/@/shared/components/icon/icon';
import { TableColumn } from '/@/shared/types/types';
const formatTrackNumber = (song: ItemDetailListCellProps['song']) => {
export const TrackNumberColumn = ({ song }: ItemDetailListCellProps) => {
const disc = song.discNumber ?? 1;
const track = song.trackNumber.toString().padStart(2, '0');
return `${disc}-${track}`;
};
const DefaultTrackNumberColumn = ({ song }: ItemDetailListCellProps) => {
return <>{formatTrackNumber(song)}</>;
};
const PlayableTrackNumberColumn = (props: ItemDetailListCellProps) => {
const { handlePlay, isActive, isPlaying, showPlayControls } = useDetailRowPlayControl(props);
const indexContent = isActive ? (
<div className={styles.iconWrapper}>
<Icon fill="primary" icon={isPlaying ? 'mediaPlay' : 'mediaPause'} />
</div>
) : (
formatTrackNumber(props.song)
);
return (
<ItemDetailRowPlayControlCell
indexContent={indexContent}
onPlay={handlePlay}
showPlayControls={showPlayControls}
/>
);
};
export const TrackNumberColumn = (props: ItemDetailListCellProps) => {
if (!props.columns || !isRowPlayControlColumn(TableColumn.TRACK_NUMBER, props.columns)) {
return <DefaultTrackNumberColumn {...props} />;
}
return <PlayableTrackNumberColumn {...props} />;
};
@@ -1,9 +1,8 @@
import { ItemListStateActions } from '/@/renderer/components/item-list/helpers/item-list-state';
import { ItemControls, ItemTableListColumnConfig } from '/@/renderer/components/item-list/types';
import { ItemControls } from '/@/renderer/components/item-list/types';
import { Song } from '/@/shared/types/domain-types';
export interface ItemDetailListCellProps {
columns?: ItemTableListColumnConfig[];
controls?: ItemControls;
internalState?: ItemListStateActions;
isMutatingFavorite?: boolean;
@@ -1,47 +0,0 @@
import { useCallback } from 'react';
import { ItemDetailListCellProps } from './types';
import { playSongFromItemListControl } from '/@/renderer/components/item-list/helpers/play-row-from-list';
import { usePlayer } from '/@/renderer/features/player/context/player-context';
import { useIsCurrentSong } from '/@/renderer/features/player/hooks/use-is-current-song';
import { usePlayerStatus } from '/@/renderer/store';
import { Song } from '/@/shared/types/domain-types';
import { Play, PlayerStatus } from '/@/shared/types/types';
export const useDetailRowPlayControl = ({
internalState,
rowIndex = 0,
song,
}: Pick<ItemDetailListCellProps, 'internalState' | 'rowIndex' | 'song'>) => {
const status = usePlayerStatus();
const player = usePlayer();
const { isActive } = useIsCurrentSong(song);
const isPlaying = isActive && status === PlayerStatus.PLAYING;
const showPlayControls = !!song?.id;
const handlePlay = useCallback(
(playType: Play) => {
if (!song) {
return;
}
playSongFromItemListControl({
index: rowIndex,
internalState,
item: song as Song,
meta: { playType, singleSongOnly: true },
player,
});
},
[internalState, player, rowIndex, song],
);
return {
handlePlay,
isActive,
isPlaying,
showPlayControls,
};
};
@@ -447,14 +447,6 @@
padding-left: 0;
}
.row .track-cell-play-control {
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: visible;
}
.track-row-dragging {
opacity: 0.5;
}
@@ -33,7 +33,6 @@ import styles from './item-detail-list.module.css';
import { ItemCardControls } from '/@/renderer/components/item-card/item-card-controls';
import { ItemImage } from '/@/renderer/components/item-image/item-image';
import { getDraggedItems } from '/@/renderer/components/item-list/helpers/get-dragged-items';
import { isRowPlayControlColumn } from '/@/renderer/components/item-list/helpers/get-row-play-control-column';
import { useDefaultItemListControls } from '/@/renderer/components/item-list/helpers/item-list-controls';
import {
ItemListStateActions,
@@ -366,7 +365,6 @@ const TrackRow = memo(
const isTitleColumn = col.id === TableColumn.TITLE;
const isImageColumn = col.id === TableColumn.IMAGE;
const isIconActionColumn = isNoHorizontalPaddingColumn(col.id);
const isPlayControlColumn = isRowPlayControlColumn(col.id, columns);
const showHoverContent = shouldShowHoverOnlyColumnContent(
col.id,
isRowHovered,
@@ -376,7 +374,6 @@ const TrackRow = memo(
const content = isSongsLoading ? null : showHoverContent ? (
<CellComponent
columnId={col.id}
columns={columns}
controls={controls}
internalState={internalState}
isMutatingFavorite={isMutatingFavorite}
@@ -396,7 +393,6 @@ const TrackRow = memo(
[styles.trackCellImage]: isImageColumn,
[styles.trackCellMuted]: !isTitleColumn,
[styles.trackCellNoHPadding]: isIconActionColumn,
[styles.trackCellPlayControl]: isPlayControlColumn,
[styles.trackCellVerticalBorderVisible]:
enableVerticalBorders && !isLastColumn,
[styles.trackCellWithVerticalBorder]: !isLastColumn,
@@ -1,45 +1,3 @@
.full-size-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
overflow: visible;
-webkit-line-clamp: unset;
line-clamp: unset;
}
.expansion-cell {
overflow: visible;
}
.expansion-inner {
position: relative;
width: 100%;
height: 100%;
overflow: visible;
}
.play-target {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
.expand {
position: absolute;
top: 50%;
left: 50%;
z-index: 4;
transform: translate(-50%, -50%);
}
.index-content {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
@@ -2,38 +2,30 @@ import clsx from 'clsx';
import styles from './row-index-column.module.css';
import { isRowPlayControlColumn } from '/@/renderer/components/item-list/helpers/get-row-play-control-column';
import { RowPlayControlCell } from '/@/renderer/components/item-list/item-table-list/columns/row-play-control-cell';
import { useRowPlayControl } from '/@/renderer/components/item-list/item-table-list/columns/use-row-play-control';
import {
ItemTableListInnerColumn,
TableColumnContainer,
TableColumnTextContainer,
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
import { useIsActiveRow } from '/@/renderer/components/item-list/item-table-list/item-table-list-context';
import { ItemListItem } from '/@/renderer/components/item-list/types';
import { usePlayerStatus } from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Flex } from '/@/shared/components/flex/flex';
import { Icon } from '/@/shared/components/icon/icon';
import { Text } from '/@/shared/components/text/text';
import { LibraryItem } from '/@/shared/types/domain-types';
import { TableColumn } from '/@/shared/types/types';
import { LibraryItem, QueueSong } from '/@/shared/types/domain-types';
import { PlayerStatus } from '/@/shared/types/types';
const RowIndexColumnBase = (props: ItemTableListInnerColumn) => {
const { itemType } = props;
if (!isRowPlayControlColumn(TableColumn.ROW_INDEX, props.columns)) {
return <DefaultRowIndexColumn {...props} />;
}
switch (itemType) {
case LibraryItem.ALBUM:
case LibraryItem.ALBUM_ARTIST:
case LibraryItem.ARTIST:
case LibraryItem.FOLDER:
case LibraryItem.PLAYLIST_SONG:
case LibraryItem.QUEUE_SONG:
case LibraryItem.SONG:
return <PlayableRowIndexColumn {...props} />;
return <QueueSongRowIndexColumn {...props} />;
default:
return <DefaultRowIndexColumn {...props} />;
}
@@ -96,8 +88,12 @@ const DefaultRowIndexColumn = (props: ItemTableListInnerColumn) => {
return <TableColumnTextContainer {...props}>{adjustedRowIndex}</TableColumnTextContainer>;
};
const PlayableRowIndexColumn = (props: ItemTableListInnerColumn) => {
const { handlePlay, isActive, isPlaying, showPlayControls } = useRowPlayControl(props);
const QueueSongRowIndexColumn = (props: ItemTableListInnerColumn) => {
const status = usePlayerStatus();
const song = (props.getRowItem?.(props.rowIndex) ?? props.data[props.rowIndex]) as QueueSong;
const isActive = useIsActiveRow(song?.id, song?._uniqueId);
const isActiveAndPlaying = isActive && status === PlayerStatus.PLAYING;
let adjustedRowIndex =
props.getAdjustedRowIndex?.(props.rowIndex) ??
@@ -108,20 +104,38 @@ const PlayableRowIndexColumn = (props: ItemTableListInnerColumn) => {
adjustedRowIndex = props.startRowIndex + adjustedRowIndex;
}
const indexContent = isActive ? (
<Flex className={styles.indexContent}>
<Icon fill="primary" icon={isPlaying ? 'mediaPlay' : 'mediaPause'} />
</Flex>
) : (
adjustedRowIndex
);
return (
<RowPlayControlCell
<InnerQueueSongRowIndexColumn
{...props}
indexContent={indexContent}
onPlay={handlePlay}
showPlayControls={showPlayControls}
adjustedRowIndex={adjustedRowIndex}
isActive={isActive}
isPlaying={isActiveAndPlaying}
/>
);
};
const InnerQueueSongRowIndexColumn = (
props: ItemTableListInnerColumn & {
adjustedRowIndex: number;
isActive: boolean;
isPlaying: boolean;
},
) => {
return (
<TableColumnTextContainer {...props}>
{props.isActive ? (
props.isPlaying ? (
<Flex>
<Icon fill="primary" icon="mediaPlay" />
</Flex>
) : (
<Flex>
<Icon fill="primary" icon="mediaPause" />
</Flex>
)
) : (
props.adjustedRowIndex
)}
</TableColumnTextContainer>
);
};
@@ -1,125 +0,0 @@
import clsx from 'clsx';
import { ReactNode, useCallback } from 'react';
import styles from './row-index-column.module.css';
import {
ItemTableListInnerColumn,
TableColumnContainer,
TableColumnTextContainer,
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
import { ItemListItem } from '/@/renderer/components/item-list/types';
import { ItemRowPlayControls } from '/@/renderer/features/shared/components/item-row-play-controls';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Flex } from '/@/shared/components/flex/flex';
import { HoverCard } from '/@/shared/components/hover-card/hover-card';
import { Text } from '/@/shared/components/text/text';
import { Play } from '/@/shared/types/types';
export const RowPlayControlCell = (
props: ItemTableListInnerColumn & {
indexContent: ReactNode;
onPlay: (playType: Play) => void;
showPlayControls: boolean;
},
) => {
const {
controls,
data,
enableExpansion,
getRowItem,
indexContent,
internalState,
itemType,
onPlay,
rowIndex,
showPlayControls,
} = props;
const handleExpand = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
const item = (getRowItem?.(rowIndex) ?? data[rowIndex]) as ItemListItem;
const rowId = internalState.extractRowId(item);
const index = rowId ? internalState.findItemIndex(rowId) : -1;
controls.onExpand?.({
event: e,
index,
internalState,
item,
itemType,
});
},
[controls, data, getRowItem, internalState, itemType, rowIndex],
);
const getIndexDisplay = (useMutedText: boolean) => {
const hideOnHoverClass = enableExpansion ? 'hide-on-hover' : undefined;
if (typeof indexContent === 'number') {
return useMutedText ? (
<Text className={hideOnHoverClass} isMuted isNoSelect>
{indexContent}
</Text>
) : (
indexContent
);
}
return <span className={hideOnHoverClass}>{indexContent}</span>;
};
const expansionTarget = (
<div className={styles.playTarget}>
{getIndexDisplay(true)}
<ActionIcon
className={clsx(styles.expand, 'hover-only')}
icon="arrowDownS"
iconProps={{ color: 'muted', size: 'md' }}
onClick={handleExpand}
size="xs"
variant="subtle"
/>
</div>
);
if (enableExpansion) {
return (
<TableColumnContainer {...props} className={styles.expansionCell}>
<div className={styles.expansionInner}>
{showPlayControls ? (
<HoverCard openDelay={300} position="top" withArrow withinPortal={false}>
<HoverCard.Target>{expansionTarget}</HoverCard.Target>
<HoverCard.Dropdown onClick={(e) => e.stopPropagation()}>
<ItemRowPlayControls onPlay={onPlay} />
</HoverCard.Dropdown>
</HoverCard>
) : (
expansionTarget
)}
</div>
</TableColumnContainer>
);
}
if (!showPlayControls) {
return (
<TableColumnTextContainer {...props}>{getIndexDisplay(false)}</TableColumnTextContainer>
);
}
return (
<TableColumnTextContainer {...props} className={styles.fullSizeContent}>
<HoverCard openDelay={300} position="top" withArrow withinPortal={false}>
<HoverCard.Target>
<Flex className={styles.indexContent} justify="center" w="100%">
{getIndexDisplay(false)}
</Flex>
</HoverCard.Target>
<HoverCard.Dropdown onClick={(e) => e.stopPropagation()}>
<ItemRowPlayControls onPlay={onPlay} />
</HoverCard.Dropdown>
</HoverCard>
</TableColumnTextContainer>
);
};
@@ -58,8 +58,6 @@ a.title {
color: var(--theme-colors-foreground-muted);
white-space: nowrap;
user-select: none;
--text-text-wrap: nowrap;
}
.folder-icon {
@@ -1,51 +0,0 @@
import styles from './row-index-column.module.css';
import { isRowPlayControlColumn } from '/@/renderer/components/item-list/helpers/get-row-play-control-column';
import { NumericColumn } from '/@/renderer/components/item-list/item-table-list/columns/numeric-column';
import { RowPlayControlCell } from '/@/renderer/components/item-list/item-table-list/columns/row-play-control-cell';
import {
supportsTrackNumberRowPlayControls,
useRowPlayControl,
} from '/@/renderer/components/item-list/item-table-list/columns/use-row-play-control';
import { ItemTableListInnerColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
import { Flex } from '/@/shared/components/flex/flex';
import { Icon } from '/@/shared/components/icon/icon';
import { TableColumn } from '/@/shared/types/types';
export const TrackNumberColumn = (props: ItemTableListInnerColumn) => {
if (
isRowPlayControlColumn(TableColumn.TRACK_NUMBER, props.columns) &&
supportsTrackNumberRowPlayControls(props.itemType)
) {
return <PlayableTrackNumberColumn {...props} />;
}
return <NumericColumn {...props} />;
};
const PlayableTrackNumberColumn = (props: ItemTableListInnerColumn) => {
const { handlePlay, isActive, isPlaying, showPlayControls } = useRowPlayControl(props);
const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as unknown[])[props.rowIndex];
const trackNumber = (rowItem as undefined | { trackNumber?: number })?.trackNumber;
if (typeof trackNumber !== 'number') {
return <NumericColumn {...props} />;
}
const indexContent = isActive ? (
<Flex className={styles.indexContent}>
<Icon fill="primary" icon={isPlaying ? 'mediaPlay' : 'mediaPause'} />
</Flex>
) : (
trackNumber
);
return (
<RowPlayControlCell
{...props}
indexContent={indexContent}
onPlay={handlePlay}
showPlayControls={showPlayControls}
/>
);
};
@@ -1,140 +0,0 @@
import { useCallback } from 'react';
import {
playAlbumFromItemListControl,
playArtistFromItemListControl,
playSongFromItemListControl,
} from '/@/renderer/components/item-list/helpers/play-row-from-list';
import { ItemTableListInnerColumn } from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
import { useIsActiveRow } from '/@/renderer/components/item-list/item-table-list/item-table-list-context';
import { usePlayer } from '/@/renderer/features/player/context/player-context';
import { usePlayerSong, usePlayerStatus } from '/@/renderer/store';
import {
Album,
AlbumArtist,
Artist,
LibraryItem,
QueueSong,
Song,
} from '/@/shared/types/domain-types';
import { Play, PlayerStatus } from '/@/shared/types/types';
export const supportsRowPlayControls = (itemType: LibraryItem) =>
itemType === LibraryItem.ALBUM ||
itemType === LibraryItem.ALBUM_ARTIST ||
itemType === LibraryItem.ARTIST ||
itemType === LibraryItem.PLAYLIST_SONG ||
itemType === LibraryItem.SONG;
export const supportsTrackNumberRowPlayControls = (itemType: LibraryItem) =>
itemType === LibraryItem.PLAYLIST_SONG ||
itemType === LibraryItem.QUEUE_SONG ||
itemType === LibraryItem.SONG;
export const hasPlayableRowItem = (
itemType: LibraryItem,
items: { album: Album; artist: AlbumArtist | Artist; song: QueueSong },
) => {
switch (itemType) {
case LibraryItem.ALBUM:
return !!items.album?.id;
case LibraryItem.ALBUM_ARTIST:
case LibraryItem.ARTIST:
return !!items.artist?.id;
default:
return !!items.song;
}
};
export const useRowPlayControl = (props: ItemTableListInnerColumn) => {
const status = usePlayerStatus();
const currentSong = usePlayerSong();
const player = usePlayer();
const rowItem = props.getRowItem?.(props.rowIndex) ?? props.data[props.rowIndex];
const song = rowItem as QueueSong;
const album = rowItem as Album;
const artist = rowItem as AlbumArtist | Artist;
const isActiveFromRow = useIsActiveRow(song?.id, song?._uniqueId);
const isActive = (() => {
switch (props.itemType) {
case LibraryItem.ALBUM:
return !!album?.id && currentSong?.albumId === album.id;
case LibraryItem.ALBUM_ARTIST:
return (
!!artist?.id &&
!!currentSong?.albumArtists?.some(
(relatedArtist) => relatedArtist.id === artist.id,
)
);
case LibraryItem.ARTIST:
return (
!!artist?.id &&
!!currentSong?.artists?.some((relatedArtist) => relatedArtist.id === artist.id)
);
default:
return isActiveFromRow;
}
})();
const isPlaying = isActive && status === PlayerStatus.PLAYING;
const showPlayControls =
supportsRowPlayControls(props.itemType) &&
hasPlayableRowItem(props.itemType, { album, artist, song });
const handlePlay = useCallback(
(playType: Play) => {
if (props.itemType === LibraryItem.ALBUM) {
if (!album?.id) {
return;
}
playAlbumFromItemListControl({
album,
meta: { playType },
player,
});
return;
}
if (
props.itemType === LibraryItem.ALBUM_ARTIST ||
props.itemType === LibraryItem.ARTIST
) {
if (!artist?.id) {
return;
}
playArtistFromItemListControl({
artist,
itemType: props.itemType,
meta: { playType },
player,
});
return;
}
if (!song) {
return;
}
playSongFromItemListControl({
item: song as Song,
meta: { playType, singleSongOnly: true },
player,
});
},
[album, artist, player, props.itemType, song],
);
return {
album,
artist,
handlePlay,
isActive,
isPlaying,
showPlayControls,
song,
};
};
@@ -54,7 +54,6 @@ import { TextColumn } from '/@/renderer/components/item-list/item-table-list/col
import { TitleArtistColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-artist-column';
import { TitleColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-column';
import { TitleCombinedColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-combined-column';
import { TrackNumberColumn } from '/@/renderer/components/item-list/item-table-list/columns/track-number-column';
import { YearColumn } from '/@/renderer/components/item-list/item-table-list/columns/year-column';
import { useItemDragDropState } from '/@/renderer/components/item-list/item-table-list/hooks/use-item-drag-drop-state';
import { TableItemProps } from '/@/renderer/components/item-list/item-table-list/item-table-list';
@@ -240,6 +239,7 @@ const ItemTableListColumnBase = (props: ItemTableListColumn) => {
case TableColumn.CHANNELS:
case TableColumn.DISC_NUMBER:
case TableColumn.SAMPLE_RATE:
case TableColumn.TRACK_NUMBER:
return <NumericColumn {...props} {...dragProps} controls={controls} type={type} />;
case TableColumn.COMPOSER:
@@ -302,11 +302,6 @@ const ItemTableListColumnBase = (props: ItemTableListColumn) => {
/>
);
case TableColumn.TRACK_NUMBER:
return (
<TrackNumberColumn {...props} {...dragProps} controls={controls} type={type} />
);
case TableColumn.USER_FAVORITE:
return <FavoriteColumn {...props} {...dragProps} controls={controls} type={type} />;
@@ -66,7 +66,6 @@ import {
ItemTableListColumnConfig,
} from '/@/renderer/components/item-list/types';
import { PlayerContext, usePlayer } from '/@/renderer/features/player/context/player-context';
import { usePlayerStore } from '/@/renderer/store';
import { animationProps } from '/@/shared/components/animations/animation-props';
import { useFocusWithin } from '/@/shared/hooks/use-focus-within';
import { useMergedRef } from '/@/shared/hooks/use-merged-ref';
@@ -1597,22 +1596,11 @@ const BaseItemTableList = ({
],
);
const onShowPlayingSong = useCallback(() => {
const targetId = usePlayerStore.getState().getCurrentSong()?.id;
if (!targetId) return;
const index =
getItemIndex?.(targetId) ??
data.findIndex((item) => (item as null | { id?: string })?.id === targetId);
if (index === undefined || index < 0) return;
handleRef.current?.scrollToIndex(index, { align: 'center', behavior: 'auto' });
}, [data, getItemIndex]);
useListHotkeys({
controls,
focused,
internalState,
itemType,
onShowPlayingSong,
});
const tableConfigValue = useMemo<ItemTableListConfig>(
@@ -12,7 +12,6 @@ import styles from './album-detail-content.module.css';
import { useGridCarouselContainerQuery } from '/@/renderer/components/grid-carousel/grid-carousel-v2';
import { useItemListStateSubscription } from '/@/renderer/components/item-list/helpers/item-list-state';
import { playSongFromItemListControl } from '/@/renderer/components/item-list/helpers/play-row-from-list';
import { useItemListColumnReorder } from '/@/renderer/components/item-list/helpers/use-item-list-column-reorder';
import { useItemListColumnResize } from '/@/renderer/components/item-list/helpers/use-item-list-column-resize';
import { SONG_TABLE_COLUMNS } from '/@/renderer/components/item-list/item-table-list/default-columns';
@@ -61,7 +60,7 @@ import {
SongListSort,
SortOrder,
} from '/@/shared/types/domain-types';
import { ItemListKey, ListDisplayType } from '/@/shared/types/types';
import { ItemListKey, ListDisplayType, Play } from '/@/shared/types/types';
const MetadataPillGroup = ({
items,
@@ -831,13 +830,13 @@ const AlbumDetailSongsTable = ({ songs }: AlbumDetailSongsTableProps) => {
return;
}
playSongFromItemListControl({
index,
internalState,
item: item as Song,
meta,
player,
});
const playType = (meta?.playType as Play) || Play.NOW;
const items = internalState?.getData() as Song[];
if (index !== undefined) {
player.addToQueueByData(items, playType, item.id);
}
},
};
}, [player]);
@@ -14,7 +14,6 @@ import styles from './album-artist-detail-content.module.css';
import { queryKeys } from '/@/renderer/api/query-keys';
import { DataRow, MemoizedItemCard } from '/@/renderer/components/item-card/item-card';
import { useDefaultItemListControls } from '/@/renderer/components/item-list/helpers/item-list-controls';
import { playSongFromItemListControl } from '/@/renderer/components/item-list/helpers/play-row-from-list';
import { useGridRows } from '/@/renderer/components/item-list/helpers/use-grid-rows';
import { useItemListColumnReorder } from '/@/renderer/components/item-list/helpers/use-item-list-column-reorder';
import { useItemListColumnResize } from '/@/renderer/components/item-list/helpers/use-item-list-column-resize';
@@ -366,13 +365,12 @@ const AlbumArtistMetadataTopSongsContent = ({
return;
}
playSongFromItemListControl({
index,
internalState,
item: item as Song,
meta,
player,
});
const playType = (meta?.playType as Play) || Play.NOW;
const items = internalState?.getData() as Song[];
if (index !== undefined) {
player.addToQueueByData(items, playType, item.id);
}
},
};
}, [player]);
@@ -659,13 +657,12 @@ const AlbumArtistMetadataFavoriteSongs = ({
return;
}
playSongFromItemListControl({
index,
internalState,
item: item as Song,
meta,
player,
});
const playType = (meta?.playType as Play) || Play.NOW;
const items = internalState?.getData() as Song[];
if (index !== undefined) {
player.addToQueueByData(items, playType, item.id);
}
},
};
}, [player]);
@@ -2,7 +2,6 @@ import { useSuspenseQueries } from '@tanstack/react-query';
import { useMemo } from 'react';
import { useParams } from 'react-router';
import { playSongFromItemListControl } from '/@/renderer/components/item-list/helpers/play-row-from-list';
import { useItemListColumnReorder } from '/@/renderer/components/item-list/helpers/use-item-list-column-reorder';
import { useItemListColumnResize } from '/@/renderer/components/item-list/helpers/use-item-list-column-resize';
import { ItemTableList } from '/@/renderer/components/item-list/item-table-list/item-table-list';
@@ -25,7 +24,7 @@ import { useCurrentServer } from '/@/renderer/store/auth.store';
import { useSettingsStore } from '/@/renderer/store/settings.store';
import { sortSongList } from '/@/shared/api/utils';
import { LibraryItem, Song } from '/@/shared/types/domain-types';
import { ItemListKey } from '/@/shared/types/types';
import { ItemListKey, Play } from '/@/shared/types/types';
const AlbumArtistDetailFavoriteSongsListRoute = () => {
const { albumArtistId, artistId } = useParams() as {
@@ -97,13 +96,12 @@ const AlbumArtistDetailFavoriteSongsListRoute = () => {
return;
}
playSongFromItemListControl({
index,
internalState,
item: item as Song,
meta,
player,
});
const playType = (meta?.playType as Play) || Play.NOW;
const items = internalState?.getData() as Song[];
if (index !== undefined) {
player.addToQueueByData(items, playType, item.id);
}
},
};
}, [player]);

Some files were not shown because too many files have changed in this diff Show More