mirror of
https://github.com/jeffvli/feishin.git
synced 2026-07-23 10:56:28 +02:00
Compare commits
7 Commits
324936e0c8
...
e49e488b4c
| Author | SHA1 | Date | |
|---|---|---|---|
| e49e488b4c | |||
| 5d4547080d | |||
| cc8910cfd6 | |||
| 9fb241dca2 | |||
| e3a0879301 | |||
| 953494e9d0 | |||
| f190626c8c |
@@ -85,7 +85,7 @@
|
||||
"hotkey_zoomIn": "拡大",
|
||||
"scrobble_description": "再生した音楽をメディアサーバーから Scrobble します",
|
||||
"hotkey_browserForward": "ブラウザ 進む",
|
||||
"audioExclusiveMode_description": "排他出力モードを有効にします。このモードでは、システムの他の出力がロックされ、MPV のみがオーディオを出力できるようになります",
|
||||
"audioExclusiveMode_description": "排他出力モードを有効にします。このモードでは、システムの他の出力がロックされ、MPV のみがオーディオを出力できるようになります。このモードが有効になっている間は、ビジュアライザーやシステムオーディオのキャプチャ機能は動作しません",
|
||||
"discordUpdateInterval": "{{discord}} Rich Presence の更新間隔",
|
||||
"themeLight": "テーマ (ライト)",
|
||||
"fontType_optionBuiltIn": "組み込みフォント",
|
||||
@@ -425,7 +425,11 @@
|
||||
"sidePlayQueueLayout_optionHorizontal": "水平",
|
||||
"sidePlayQueueLayout_optionVertical": "垂直",
|
||||
"waveformLoadingDelay": "波形読み込みの遅延",
|
||||
"waveformLoadingDelay_description": "波形を読み込むまでの遅延時間(秒単位)を設定します。Web プレーヤー使用時にカクつきが発生する場合は、この値を増やしてください。"
|
||||
"waveformLoadingDelay_description": "波形を読み込むまでの遅延時間(秒単位)を設定します。Web プレーヤー使用時にカクつきが発生する場合は、この値を増やしてください。",
|
||||
"playerbarWaveformStretch": "波形伸縮",
|
||||
"playerbarWaveformStretch_description": "波形を伸縮させて、利用可能なスペースを埋めます",
|
||||
"preventSuspendOnPlayback_description": "音楽再生中にアプリケーションが停止しないようにします",
|
||||
"preventSuspendOnPlayback": "再生の中断を防止する"
|
||||
},
|
||||
"action": {
|
||||
"editPlaylist": "$t(entity.playlist, {\"count\": 1}) を編集",
|
||||
@@ -808,7 +812,7 @@
|
||||
"dynamicBackground": "ダイナミック背景",
|
||||
"synchronized": "同期",
|
||||
"followCurrentLyric": "歌詞を再生位置に追従",
|
||||
"opacity": "非透過率",
|
||||
"opacity": "不透明度",
|
||||
"lyricSize": "歌詞のサイズ",
|
||||
"showLyricProvider": "歌詞の提供元を表示",
|
||||
"unsynchronized": "非同期",
|
||||
@@ -817,7 +821,9 @@
|
||||
"lyricGap": "歌詞の間隔",
|
||||
"dynamicImageBlur": "画像のぼかしサイズ",
|
||||
"dynamicIsImage": "背景画像を有効にする",
|
||||
"lyricOffset": "歌詞のオフセット (ms)"
|
||||
"lyricOffset": "歌詞のオフセット (ms)",
|
||||
"lyricOpacityNonActive": "非アクティブな歌詞の不透明度",
|
||||
"lyricScaleNonActive": "非アクティブな歌詞のスケール"
|
||||
},
|
||||
"upNext": "次へ",
|
||||
"lyrics": "歌詞",
|
||||
@@ -1033,7 +1039,8 @@
|
||||
"input_skipDuplicates": "重複をスキップ",
|
||||
"input_playlists": "$t(entity.playlist, {\"count\": 2})",
|
||||
"create": "$t(entity.playlist, {\"count\": 1}) {{playlist}} を作成する",
|
||||
"searchOrCreate": "$t(entity.playlist, {\"count\": 2}) を検索するか、入力して新しいプレイリストを作成してください"
|
||||
"searchOrCreate": "$t(entity.playlist, {\"count\": 2}) を検索するか、入力して新しいプレイリストを作成してください",
|
||||
"noneAdded": "$t(entity.playlist, {\"count\": 1}) {{playlist}} にトラックが追加されませんでした"
|
||||
},
|
||||
"updateServer": {
|
||||
"title": "サーバーをアップデート",
|
||||
@@ -1341,6 +1348,7 @@
|
||||
"systemAudioConsentDecline": "拒否",
|
||||
"systemAudioConsentTitle": "システムオーディオへのアクセスを許可しますか?",
|
||||
"systemAudioCaptureFailed": "キャプチャを開始できませんでした: {{message}}",
|
||||
"systemAudioNoAudioTrack": "音声トラックが返されませんでした。プロンプトが表示されたら、音声キャプチャが有効になっていることを確認してください。"
|
||||
"systemAudioNoAudioTrack": "音声トラックが返されませんでした。プロンプトが表示されたら、音声キャプチャが有効になっていることを確認してください。",
|
||||
"systemAudioExclusiveModeNotSupported": "オーディオ排他モードが有効になっている間は、ビジュアライザーは利用できません。MPV の設定でオーディオ排他モードを無効にしてから、もう一度お試しください。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,11 +404,12 @@ export const createAuthHeader = (): string => {
|
||||
};
|
||||
|
||||
export const jfApiClient = (args: {
|
||||
forceRemoteUrl?: boolean;
|
||||
server: null | ServerListItemWithCredential;
|
||||
signal?: AbortSignal;
|
||||
url?: string;
|
||||
}) => {
|
||||
const { server, signal, url } = args;
|
||||
const { forceRemoteUrl, server, signal, url } = args;
|
||||
|
||||
return initClient(contract, {
|
||||
api: async ({ body, headers, method, path }) => {
|
||||
@@ -418,7 +419,7 @@ export const jfApiClient = (args: {
|
||||
const { params, path: api } = parsePath(path);
|
||||
|
||||
if (server) {
|
||||
const serverUrl = getServerUrl(server);
|
||||
const serverUrl = getServerUrl(server, forceRemoteUrl);
|
||||
baseUrl = serverUrl;
|
||||
token = server?.credential;
|
||||
} else {
|
||||
|
||||
@@ -395,10 +395,15 @@ export const JellyfinController: InternalControllerEndpoint = {
|
||||
userId: apiClientProps.server.userId,
|
||||
},
|
||||
query: {
|
||||
AlbumIds: query.id,
|
||||
EnableUserData: true,
|
||||
Fields: JF_FIELDS.SONG,
|
||||
IncludeItemTypes: 'Audio',
|
||||
ParentId: query.id,
|
||||
Recursive: true,
|
||||
SortBy: 'ParentIndexNumber,IndexNumber,SortName',
|
||||
SortOrder: JFSortOrder.ASC,
|
||||
StartIndex: 0,
|
||||
UserId: apiClientProps.server.userId,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -928,6 +933,7 @@ export const JellyfinController: InternalControllerEndpoint = {
|
||||
Fields: JF_FIELDS.PLAYLIST_LIST,
|
||||
IncludeItemTypes: 'Playlist',
|
||||
Limit: query.limit,
|
||||
MediaTypes: 'Audio',
|
||||
Recursive: true,
|
||||
SearchTerm: query.searchTerm,
|
||||
SortBy: playlistListSortMap.jellyfin[query.sortBy],
|
||||
|
||||
@@ -479,11 +479,12 @@ axiosClient.interceptors.response.use(
|
||||
);
|
||||
|
||||
export const ndApiClient = (args: {
|
||||
forceRemoteUrl?: boolean;
|
||||
server: null | ServerListItemWithCredential;
|
||||
signal?: AbortSignal;
|
||||
url?: string;
|
||||
}) => {
|
||||
const { server, signal, url } = args;
|
||||
const { forceRemoteUrl, server, signal, url } = args;
|
||||
|
||||
return initClient(contract, {
|
||||
api: async ({ body, headers, method, path }) => {
|
||||
@@ -493,7 +494,7 @@ export const ndApiClient = (args: {
|
||||
const { params, path: api } = parsePath(path);
|
||||
|
||||
if (server) {
|
||||
const serverUrl = getServerUrl(server);
|
||||
const serverUrl = getServerUrl(server, forceRemoteUrl);
|
||||
baseUrl = serverUrl ? `${serverUrl}/api` : undefined;
|
||||
token = server?.ndCredential;
|
||||
} else {
|
||||
|
||||
@@ -428,12 +428,13 @@ const silentlyTransformResponse = (data: any) => {
|
||||
};
|
||||
|
||||
export const ssApiClient = (args: {
|
||||
forceRemoteUrl?: boolean;
|
||||
server: null | ServerListItemWithCredential;
|
||||
signal?: AbortSignal;
|
||||
silent?: boolean;
|
||||
url?: string;
|
||||
}) => {
|
||||
const { server, signal, silent, url } = args;
|
||||
const { forceRemoteUrl, server, signal, silent, url } = args;
|
||||
|
||||
return initClient(contract, {
|
||||
api: async ({ body, headers, method, path, rawQuery }) => {
|
||||
@@ -443,7 +444,7 @@ export const ssApiClient = (args: {
|
||||
const { params, path: api } = parsePath(path);
|
||||
|
||||
if (server) {
|
||||
const serverUrl = getServerUrl(server);
|
||||
const serverUrl = getServerUrl(server, forceRemoteUrl);
|
||||
baseUrl = serverUrl ? `${serverUrl}/rest` : undefined;
|
||||
const token = server.credential;
|
||||
const params = token.split(/&?\w=/gm);
|
||||
|
||||
@@ -284,7 +284,10 @@ export const useDiscordRpc = () => {
|
||||
) {
|
||||
try {
|
||||
const info = await api.controller.getAlbumInfo({
|
||||
apiClientProps: { serverId: song._serverId },
|
||||
apiClientProps: {
|
||||
forceRemoteUrl: true,
|
||||
serverId: song._serverId,
|
||||
},
|
||||
query: { id: song.albumId },
|
||||
});
|
||||
|
||||
|
||||
@@ -6,12 +6,8 @@ import { Link, NavLink, useNavigate } from 'react-router';
|
||||
|
||||
import styles from './collapsed-sidebar.module.css';
|
||||
|
||||
import JellyfinLogo from '/@/renderer/features/servers/assets/jellyfin.png';
|
||||
import NavidromeLogo from '/@/renderer/features/servers/assets/navidrome.png';
|
||||
import OpenSubsonicLogo from '/@/renderer/features/servers/assets/opensubsonic.png';
|
||||
import { CollapsedSidebarButton } from '/@/renderer/features/sidebar/components/collapsed-sidebar-button';
|
||||
import { CollapsedSidebarItem } from '/@/renderer/features/sidebar/components/collapsed-sidebar-item';
|
||||
import { ServerSelectorItems } from '/@/renderer/features/sidebar/components/server-selector-items';
|
||||
import { getCollectionTo } from '/@/renderer/features/sidebar/components/sidebar-collection-list';
|
||||
import { SidebarIcon } from '/@/renderer/features/sidebar/components/sidebar-icon';
|
||||
import { AppMenu } from '/@/renderer/features/titlebar/components/app-menu';
|
||||
@@ -19,7 +15,6 @@ import { AppRoute } from '/@/renderer/router/routes';
|
||||
import {
|
||||
SidebarItemType,
|
||||
useCollections,
|
||||
useCurrentServer,
|
||||
useSidebarCollapsedNavigation,
|
||||
useSidebarItems,
|
||||
useWindowSettings,
|
||||
@@ -30,7 +25,7 @@ import { Group } from '/@/shared/components/group/group';
|
||||
import { Icon } from '/@/shared/components/icon/icon';
|
||||
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
|
||||
import { Stack } from '/@/shared/components/stack/stack';
|
||||
import { LibraryItem, ServerType } from '/@/shared/types/domain-types';
|
||||
import { LibraryItem } from '/@/shared/types/domain-types';
|
||||
import { Platform } from '/@/shared/types/types';
|
||||
|
||||
export const CollapsedSidebar = () => {
|
||||
@@ -40,8 +35,6 @@ export const CollapsedSidebar = () => {
|
||||
const { windowBarStyle } = useWindowSettings();
|
||||
const sidebarCollapsedNavigation = useSidebarCollapsedNavigation();
|
||||
const sidebarItems = useSidebarItems();
|
||||
const currentServer = useCurrentServer();
|
||||
|
||||
const translatedSidebarItemMap = useMemo(
|
||||
() => ({
|
||||
Albums: t('page.sidebar.albums', { postProcess: 'titleCase' }),
|
||||
@@ -174,38 +167,6 @@ export const CollapsedSidebar = () => {
|
||||
/>
|
||||
),
|
||||
)}
|
||||
{currentServer && (
|
||||
<DropdownMenu offset={0} position="right-end" width={240}>
|
||||
<DropdownMenu.Target>
|
||||
<CollapsedSidebarItem
|
||||
activeIcon={null}
|
||||
component={Flex}
|
||||
icon={
|
||||
<img
|
||||
className={styles.serverIcon}
|
||||
src={
|
||||
currentServer.type === ServerType.NAVIDROME
|
||||
? NavidromeLogo
|
||||
: currentServer.type === ServerType.JELLYFIN
|
||||
? JellyfinLogo
|
||||
: OpenSubsonicLogo
|
||||
}
|
||||
/>
|
||||
}
|
||||
label={''}
|
||||
py="md"
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
/>
|
||||
</DropdownMenu.Target>
|
||||
<DropdownMenu.Dropdown>
|
||||
<ScrollArea style={{ maxHeight: '95vh' }}>
|
||||
<ServerSelectorItems />
|
||||
</ScrollArea>
|
||||
</DropdownMenu.Dropdown>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</motion.div>
|
||||
);
|
||||
|
||||
@@ -87,6 +87,7 @@ export const NDSongQueryFields = [
|
||||
{ label: 'Artist Date Rated', type: 'date', value: 'artistdaterated' },
|
||||
{ label: 'Artist Is Favorite', type: 'boolean', value: 'artistloved' },
|
||||
{ label: 'Artist Play Count', type: 'number', value: 'artistplaycount' },
|
||||
{ label: 'Artist Rating', type: 'number', value: 'artistrating' },
|
||||
{ label: 'Artists', type: 'string', value: 'artists' },
|
||||
{ label: 'ASIN', type: 'string', value: 'asin' },
|
||||
{ label: 'Average Rating', type: 'number', value: 'averagerating' },
|
||||
@@ -158,7 +159,7 @@ export const NDSongQueryFields = [
|
||||
{ label: 'MusicBrainz Work Id', type: 'string', value: 'musicbrainz_workid' },
|
||||
{ label: 'Name', type: 'string', value: 'title' },
|
||||
{ label: 'Original Date', type: 'date', value: 'originaldate' },
|
||||
{ label: 'Original Year', type: 'date', value: 'originalyear' },
|
||||
{ label: 'Original Year', type: 'number', value: 'originalyear' },
|
||||
{ label: 'Performer', type: 'string', value: 'performer' },
|
||||
{ label: 'Play Count', type: 'number', value: 'playcount' },
|
||||
{ label: 'Playlist', type: 'playlist', value: 'id' },
|
||||
@@ -178,18 +179,18 @@ export const NDSongQueryFields = [
|
||||
{ label: 'ReplayGain Track Peak', type: 'number', value: 'replaygain_track_peak' },
|
||||
{ label: 'Remixer', type: 'string', value: 'remixer' },
|
||||
{ label: 'Script', type: 'string', value: 'script' },
|
||||
{ label: 'Sample Rate', type: 'number', value: 'sampleRate' },
|
||||
{ label: 'Sample Rate', type: 'number', value: 'samplerate' },
|
||||
{ label: 'Size', type: 'number', value: 'size' },
|
||||
{ label: 'Sort Album', type: 'string', value: 'albumsort' },
|
||||
{ label: 'Sort Album Artist', type: 'string', value: 'albumartistsort' },
|
||||
{ label: 'Sort Album', type: 'string', value: 'sortalbum' },
|
||||
{ label: 'Sort Album Artist', type: 'string', value: 'sortalbumartist' },
|
||||
{ label: 'Sort Album Artists', type: 'string', value: 'albumartistssort' },
|
||||
{ label: 'Sort Artist', type: 'string', value: 'artistsort' },
|
||||
{ label: 'Sort Artist', type: 'string', value: 'sortartist' },
|
||||
{ label: 'Sort Artists', type: 'string', value: 'artistssort' },
|
||||
{ label: 'Sort Composer', type: 'string', value: 'composersort' },
|
||||
{ label: 'Sort Lyricist', type: 'string', value: 'lyricistsort' },
|
||||
{ label: 'Sort Name', type: 'string', value: 'titlesort' },
|
||||
{ label: 'Sort Name', type: 'string', value: 'sorttitle' },
|
||||
{ label: 'Subtitle', type: 'string', value: 'subtitle' },
|
||||
{ label: 'Track Number', type: 'number', value: 'track' },
|
||||
{ label: 'Track Number', type: 'number', value: 'tracknumber' },
|
||||
{ label: 'Track Total', type: 'number', value: 'tracktotal' },
|
||||
{ label: 'Website', type: 'string', value: 'website' },
|
||||
{ label: 'Work', type: 'string', value: 'work' },
|
||||
|
||||
@@ -318,7 +318,7 @@ export const sortSongList = (songs: Song[], sortBy: SongListSort, sortOrder: Sor
|
||||
case SongListSort.YEAR:
|
||||
results = orderBy(
|
||||
results,
|
||||
['releaseYear', (v) => v.album?.toLowerCase(), 'discNumber', 'track'],
|
||||
['releaseYear', (v) => v.album?.toLowerCase(), 'discNumber', 'trackNumber'],
|
||||
[order, order, order, order],
|
||||
);
|
||||
break;
|
||||
|
||||
@@ -421,6 +421,7 @@ type ApiContext = {
|
||||
|
||||
type BaseEndpointArgs = {
|
||||
apiClientProps: {
|
||||
forceRemoteUrl?: boolean;
|
||||
server?: null | ServerListItemWithCredential;
|
||||
serverId: string;
|
||||
signal?: AbortSignal;
|
||||
|
||||
Reference in New Issue
Block a user