Merge branch 'master' into patch-2
This commit is contained in:
commit
e9c07f46e4
55 changed files with 931 additions and 1222 deletions
|
@ -63,6 +63,7 @@ module.exports = {
|
|||
'@typescript-eslint/no-shadow': ['error'],
|
||||
'no-throw-literal': ['error'],
|
||||
'no-trailing-spaces': ['error'],
|
||||
'no-undef-init': ['error'],
|
||||
'no-unneeded-ternary': ['error'],
|
||||
'no-unused-expressions': ['off'],
|
||||
'@typescript-eslint/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
|
||||
|
|
1
.github/workflows/publish.yml
vendored
1
.github/workflows/publish.yml
vendored
|
@ -33,6 +33,7 @@ jobs:
|
|||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
projectName: jellyfin-web
|
||||
branch: ${{ github.event.workflow_run.head_branch }}
|
||||
directory: dist
|
||||
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
- [sleepycatcoding](https://github.com/sleepycatcoding)
|
||||
- [TheMelmacian](https://github.com/TheMelmacian)
|
||||
- [tehciolo](https://github.com/tehciolo)
|
||||
- [scampower3](https://github.com/scampower3)
|
||||
|
||||
# Emby Contributors
|
||||
|
||||
|
|
|
@ -14,7 +14,9 @@ RUN yum update -y \
|
|||
&& yum install -y epel-release \
|
||||
&& yum install -y rpmdevtools git autoconf automake glibc-devel gcc-c++ make \
|
||||
&& yum install https://rpm.nodesource.com/pub_20.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm -y \
|
||||
&& yum install nodejs -y --setopt=nodesource-nodejs.module_hotfixes=1
|
||||
&& yum install nodejs -y --setopt=nodesource-nodejs.module_hotfixes=1 \
|
||||
&& yum clean all \
|
||||
&& rm -rf /var/cache/dnf
|
||||
|
||||
# Link to build script
|
||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.centos /build.sh
|
||||
|
|
|
@ -17,7 +17,8 @@ RUN apt-get update \
|
|||
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
|
||||
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y nodejs
|
||||
&& apt-get install -y nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
||||
|
||||
# Link to build script
|
||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian /build.sh
|
||||
|
|
|
@ -3,7 +3,7 @@ FROM node:20-alpine
|
|||
ARG SOURCE_DIR=/src
|
||||
ARG ARTIFACT_DIR=/jellyfin-web
|
||||
|
||||
RUN apk add autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python3
|
||||
RUN apk --no-cache add autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python3
|
||||
|
||||
WORKDIR ${SOURCE_DIR}
|
||||
COPY . .
|
||||
|
|
|
@ -11,8 +11,10 @@ ENV IS_DOCKER=YES
|
|||
|
||||
# Prepare Fedora environment
|
||||
RUN dnf update -y \
|
||||
&& yum install https://rpm.nodesource.com/pub_20.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm -y \
|
||||
&& dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core nodejs autoconf automake glibc-devel make --setopt=nodesource-nodejs.module_hotfixes=1
|
||||
&& dnf install https://rpm.nodesource.com/pub_20.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm -y \
|
||||
&& dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core nodejs autoconf automake glibc-devel make --setopt=nodesource-nodejs.module_hotfixes=1 \
|
||||
&& dnf clean all \
|
||||
&& rm -rf /var/cache/dnf
|
||||
|
||||
# Link to build script
|
||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora /build.sh
|
||||
|
|
|
@ -16,7 +16,8 @@ RUN apt-get update \
|
|||
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
|
||||
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y nodejs
|
||||
&& apt-get install -y nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*
|
||||
|
||||
# Link to build script
|
||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.portable /build.sh
|
||||
|
|
|
@ -5,7 +5,7 @@ import React from 'react';
|
|||
import globalize from 'scripts/globalize';
|
||||
|
||||
const LogLevelChip = ({ level }: { level: LogLevel }) => {
|
||||
let color: 'info' | 'warning' | 'error' | undefined = undefined;
|
||||
let color: 'info' | 'warning' | 'error' | undefined;
|
||||
switch (level) {
|
||||
case LogLevel.Information:
|
||||
color = 'info';
|
||||
|
|
|
@ -5,10 +5,11 @@ import globalize from 'scripts/globalize';
|
|||
import Loading from 'components/loading/LoadingComponent';
|
||||
import GenresSectionContainer from './GenresSectionContainer';
|
||||
import { CollectionType } from 'types/collectionType';
|
||||
import { ParentId } from 'types/library';
|
||||
|
||||
interface GenresItemsContainerProps {
|
||||
parentId?: string | null;
|
||||
collectionType?: CollectionType;
|
||||
parentId: ParentId;
|
||||
collectionType: CollectionType;
|
||||
itemType: BaseItemKind;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,10 +12,11 @@ import Loading from 'components/loading/LoadingComponent';
|
|||
import { appRouter } from 'components/router/appRouter';
|
||||
import SectionContainer from './SectionContainer';
|
||||
import { CollectionType } from 'types/collectionType';
|
||||
import { ParentId } from 'types/library';
|
||||
|
||||
interface GenresSectionContainerProps {
|
||||
parentId?: string | null;
|
||||
collectionType?: CollectionType;
|
||||
parentId: ParentId;
|
||||
collectionType: CollectionType;
|
||||
itemType: BaseItemKind;
|
||||
genre: BaseItemDto;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import React, { FC, useEffect, useRef } from 'react';
|
||||
|
||||
import ItemsContainerElement from '../../elements/ItemsContainerElement';
|
||||
import imageLoader from '../images/imageLoader';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import { ViewQuerySettings } from '../../types/interface';
|
||||
import ItemsContainerElement from 'elements/ItemsContainerElement';
|
||||
import imageLoader from 'components/images/imageLoader';
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import { LibraryViewSettings, ViewMode } from 'types/library';
|
||||
|
||||
interface ItemsContainerI {
|
||||
viewQuerySettings: ViewQuerySettings;
|
||||
libraryViewSettings: LibraryViewSettings;
|
||||
getItemsHtml: () => string
|
||||
}
|
||||
|
||||
const ItemsContainer: FC<ItemsContainerI> = ({ viewQuerySettings, getItemsHtml }) => {
|
||||
const ItemsContainer: FC<ItemsContainerI> = ({ libraryViewSettings, getItemsHtml }) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -19,7 +19,7 @@ const ItemsContainer: FC<ItemsContainerI> = ({ viewQuerySettings, getItemsHtml }
|
|||
imageLoader.lazyChildren(itemsContainer);
|
||||
}, [getItemsHtml]);
|
||||
|
||||
const cssClass = viewQuerySettings.imageType == 'list' ? 'vertical-list' : 'vertical-wrap';
|
||||
const cssClass = libraryViewSettings.ViewMode === ViewMode.ListView ? 'vertical-list' : 'vertical-wrap';
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
272
src/apps/experimental/components/library/ItemsView.tsx
Normal file
272
src/apps/experimental/components/library/ItemsView.tsx
Normal file
|
@ -0,0 +1,272 @@
|
|||
import type { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client';
|
||||
import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
|
||||
import React, { FC, useCallback } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import { useLocalStorage } from 'hooks/useLocalStorage';
|
||||
import { useGetItem, useGetItemsViewByType } from 'hooks/useFetchItems';
|
||||
import { getDefaultLibraryViewSettings, getSettingsKey } from 'utils/items';
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
import listview from 'components/listview/listview';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import globalize from 'scripts/globalize';
|
||||
import AlphabetPicker from './AlphabetPicker';
|
||||
import FilterButton from './filter/FilterButton';
|
||||
import ItemsContainer from './ItemsContainer';
|
||||
import NewCollectionButton from './NewCollectionButton';
|
||||
import Pagination from './Pagination';
|
||||
import PlayAllButton from './PlayAllButton';
|
||||
import QueueButton from './QueueButton';
|
||||
import ShuffleButton from './ShuffleButton';
|
||||
import SortButton from './SortButton';
|
||||
import GridListViewButton from './GridListViewButton';
|
||||
import { LibraryViewSettings, ParentId, ViewMode } from 'types/library';
|
||||
import { CollectionType } from 'types/collectionType';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
import { CardOptions } from 'types/cardOptions';
|
||||
|
||||
interface ItemsViewProps {
|
||||
viewType: LibraryTab;
|
||||
parentId: ParentId;
|
||||
itemType: BaseItemKind[];
|
||||
collectionType?: CollectionType;
|
||||
isBtnPlayAllEnabled?: boolean;
|
||||
isBtnQueueEnabled?: boolean;
|
||||
isBtnShuffleEnabled?: boolean;
|
||||
isBtnSortEnabled?: boolean;
|
||||
isBtnFilterEnabled?: boolean;
|
||||
isBtnNewCollectionEnabled?: boolean;
|
||||
isBtnGridListEnabled?: boolean;
|
||||
isAlphabetPickerEnabled?: boolean;
|
||||
noItemsMessage: string;
|
||||
}
|
||||
|
||||
const ItemsView: FC<ItemsViewProps> = ({
|
||||
viewType,
|
||||
parentId,
|
||||
collectionType,
|
||||
isBtnPlayAllEnabled = false,
|
||||
isBtnQueueEnabled = false,
|
||||
isBtnShuffleEnabled = false,
|
||||
isBtnSortEnabled = true,
|
||||
isBtnFilterEnabled = true,
|
||||
isBtnNewCollectionEnabled = false,
|
||||
isBtnGridListEnabled = true,
|
||||
isAlphabetPickerEnabled = true,
|
||||
itemType,
|
||||
noItemsMessage
|
||||
}) => {
|
||||
const [libraryViewSettings, setLibraryViewSettings] =
|
||||
useLocalStorage<LibraryViewSettings>(
|
||||
getSettingsKey(viewType, parentId),
|
||||
getDefaultLibraryViewSettings(viewType)
|
||||
);
|
||||
|
||||
const {
|
||||
isLoading,
|
||||
data: itemsResult,
|
||||
isPreviousData
|
||||
} = useGetItemsViewByType(
|
||||
viewType,
|
||||
parentId,
|
||||
itemType,
|
||||
libraryViewSettings
|
||||
);
|
||||
const { data: item } = useGetItem(parentId);
|
||||
|
||||
const getCardOptions = useCallback(() => {
|
||||
let shape;
|
||||
let preferThumb;
|
||||
let preferDisc;
|
||||
let preferLogo;
|
||||
let lines = libraryViewSettings.ShowTitle ? 2 : 0;
|
||||
|
||||
if (libraryViewSettings.ImageType === ImageType.Banner) {
|
||||
shape = 'banner';
|
||||
} else if (libraryViewSettings.ImageType === ImageType.Disc) {
|
||||
shape = 'square';
|
||||
preferDisc = true;
|
||||
} else if (libraryViewSettings.ImageType === ImageType.Logo) {
|
||||
shape = 'backdrop';
|
||||
preferLogo = true;
|
||||
} else if (libraryViewSettings.ImageType === ImageType.Thumb) {
|
||||
shape = 'backdrop';
|
||||
preferThumb = true;
|
||||
} else {
|
||||
shape = 'auto';
|
||||
}
|
||||
|
||||
const cardOptions: CardOptions = {
|
||||
shape: shape,
|
||||
showTitle: libraryViewSettings.ShowTitle,
|
||||
showYear: libraryViewSettings.ShowYear,
|
||||
cardLayout: libraryViewSettings.CardLayout,
|
||||
centerText: true,
|
||||
context: collectionType,
|
||||
coverImage: true,
|
||||
preferThumb: preferThumb,
|
||||
preferDisc: preferDisc,
|
||||
preferLogo: preferLogo,
|
||||
overlayPlayButton: false,
|
||||
overlayMoreButton: true,
|
||||
overlayText: !libraryViewSettings.ShowTitle
|
||||
};
|
||||
|
||||
if (
|
||||
viewType === LibraryTab.Songs
|
||||
|| viewType === LibraryTab.Albums
|
||||
|| viewType === LibraryTab.Episodes
|
||||
) {
|
||||
cardOptions.showParentTitle = libraryViewSettings.ShowTitle;
|
||||
} else if (viewType === LibraryTab.Artists) {
|
||||
cardOptions.showYear = false;
|
||||
lines = 1;
|
||||
}
|
||||
|
||||
cardOptions.lines = lines;
|
||||
|
||||
return cardOptions;
|
||||
}, [
|
||||
libraryViewSettings.ShowTitle,
|
||||
libraryViewSettings.ImageType,
|
||||
libraryViewSettings.ShowYear,
|
||||
libraryViewSettings.CardLayout,
|
||||
collectionType,
|
||||
viewType
|
||||
]);
|
||||
|
||||
const getItemsHtml = useCallback(() => {
|
||||
let html = '';
|
||||
|
||||
if (libraryViewSettings.ViewMode === ViewMode.ListView) {
|
||||
html = listview.getListViewHtml({
|
||||
items: itemsResult?.Items ?? [],
|
||||
context: collectionType
|
||||
});
|
||||
} else {
|
||||
html = cardBuilder.getCardsHtml(
|
||||
itemsResult?.Items ?? [],
|
||||
getCardOptions()
|
||||
);
|
||||
}
|
||||
|
||||
if (!itemsResult?.Items?.length) {
|
||||
html += '<div class="noItemsMessage centerMessage">';
|
||||
html += '<h1>' + globalize.translate('MessageNothingHere') + '</h1>';
|
||||
html += '<p>' + globalize.translate(noItemsMessage) + '</p>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
return html;
|
||||
}, [
|
||||
libraryViewSettings.ViewMode,
|
||||
itemsResult?.Items,
|
||||
collectionType,
|
||||
getCardOptions,
|
||||
noItemsMessage
|
||||
]);
|
||||
|
||||
const totalRecordCount = itemsResult?.TotalRecordCount ?? 0;
|
||||
const items = itemsResult?.Items ?? [];
|
||||
const hasFilters = Object.values(libraryViewSettings.Filters ?? {}).some(
|
||||
(filter) => !!filter
|
||||
);
|
||||
const hasSortName = libraryViewSettings.SortBy.includes(
|
||||
ItemSortBy.SortName
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box className='flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x'>
|
||||
<Pagination
|
||||
totalRecordCount={totalRecordCount}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
isPreviousData={isPreviousData}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
|
||||
{isBtnPlayAllEnabled && (
|
||||
<PlayAllButton
|
||||
item={item}
|
||||
items={items}
|
||||
viewType={viewType}
|
||||
hasFilters={hasFilters}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
{isBtnQueueEnabled
|
||||
&& item
|
||||
&& playbackManager.canQueue(item) && (
|
||||
<QueueButton
|
||||
item={item}
|
||||
items={items}
|
||||
hasFilters={hasFilters}
|
||||
/>
|
||||
)}
|
||||
{isBtnShuffleEnabled && totalRecordCount > 1 && (
|
||||
<ShuffleButton
|
||||
item={item}
|
||||
items={items}
|
||||
viewType={viewType}
|
||||
hasFilters={hasFilters}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
{isBtnSortEnabled && (
|
||||
<SortButton
|
||||
viewType={viewType}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
{isBtnFilterEnabled && (
|
||||
<FilterButton
|
||||
parentId={parentId}
|
||||
itemType={itemType}
|
||||
viewType={viewType}
|
||||
hasFilters={hasFilters}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
{isBtnNewCollectionEnabled && <NewCollectionButton />}
|
||||
{isBtnGridListEnabled && (
|
||||
<GridListViewButton
|
||||
viewType={viewType}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{isAlphabetPickerEnabled && hasSortName && (
|
||||
<AlphabetPicker
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<ItemsContainer
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
getItemsHtml={getItemsHtml}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Box className='flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x'>
|
||||
<Pagination
|
||||
totalRecordCount={totalRecordCount}
|
||||
libraryViewSettings={libraryViewSettings}
|
||||
isPreviousData={isPreviousData}
|
||||
setLibraryViewSettings={setLibraryViewSettings}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemsView;
|
|
@ -13,15 +13,17 @@ interface PaginationProps {
|
|||
libraryViewSettings: LibraryViewSettings;
|
||||
setLibraryViewSettings: React.Dispatch<React.SetStateAction<LibraryViewSettings>>;
|
||||
totalRecordCount: number;
|
||||
isPreviousData: boolean
|
||||
}
|
||||
|
||||
const Pagination: FC<PaginationProps> = ({
|
||||
libraryViewSettings,
|
||||
setLibraryViewSettings,
|
||||
totalRecordCount
|
||||
totalRecordCount,
|
||||
isPreviousData
|
||||
}) => {
|
||||
const limit = userSettings.libraryPageSize(undefined);
|
||||
const startIndex = libraryViewSettings.StartIndex || 0;
|
||||
const startIndex = libraryViewSettings.StartIndex ?? 0;
|
||||
const recordsStart = totalRecordCount ? startIndex + 1 : 0;
|
||||
const recordsEnd = limit ?
|
||||
Math.min(startIndex + limit, totalRecordCount) :
|
||||
|
@ -29,23 +31,19 @@ const Pagination: FC<PaginationProps> = ({
|
|||
const showControls = limit > 0 && limit < totalRecordCount;
|
||||
|
||||
const onNextPageClick = useCallback(() => {
|
||||
if (limit > 0) {
|
||||
const newIndex = startIndex + limit;
|
||||
setLibraryViewSettings((prevState) => ({
|
||||
...prevState,
|
||||
StartIndex: newIndex
|
||||
}));
|
||||
}
|
||||
const newIndex = startIndex + limit;
|
||||
setLibraryViewSettings((prevState) => ({
|
||||
...prevState,
|
||||
StartIndex: newIndex
|
||||
}));
|
||||
}, [limit, setLibraryViewSettings, startIndex]);
|
||||
|
||||
const onPreviousPageClick = useCallback(() => {
|
||||
if (limit > 0) {
|
||||
const newIndex = Math.max(0, startIndex - limit);
|
||||
setLibraryViewSettings((prevState) => ({
|
||||
...prevState,
|
||||
StartIndex: newIndex
|
||||
}));
|
||||
}
|
||||
const newIndex = Math.max(0, startIndex - limit);
|
||||
setLibraryViewSettings((prevState) => ({
|
||||
...prevState,
|
||||
StartIndex: newIndex
|
||||
}));
|
||||
}, [limit, setLibraryViewSettings, startIndex]);
|
||||
|
||||
return (
|
||||
|
@ -67,7 +65,7 @@ const Pagination: FC<PaginationProps> = ({
|
|||
<IconButton
|
||||
title={globalize.translate('Previous')}
|
||||
className='paper-icon-button-light btnPreviousPage autoSize'
|
||||
disabled={startIndex == 0}
|
||||
disabled={startIndex == 0 || isPreviousData}
|
||||
onClick={onPreviousPageClick}
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
|
@ -76,7 +74,7 @@ const Pagination: FC<PaginationProps> = ({
|
|||
<IconButton
|
||||
title={globalize.translate('Next')}
|
||||
className='paper-icon-button-light btnNextPage autoSize'
|
||||
disabled={startIndex + limit >= totalRecordCount }
|
||||
disabled={startIndex + limit >= totalRecordCount || isPreviousData }
|
||||
onClick={onNextPageClick}
|
||||
>
|
||||
<ArrowForwardIcon />
|
||||
|
|
|
@ -49,7 +49,7 @@ const RecommendationContainer: FC<RecommendationContainerProps> = ({
|
|||
return (
|
||||
<SectionContainer
|
||||
sectionTitle={escapeHTML(title)}
|
||||
items={recommendation.Items || []}
|
||||
items={recommendation.Items ?? []}
|
||||
cardOptions={{
|
||||
shape: 'overflowPortrait',
|
||||
showYear: true,
|
||||
|
|
|
@ -5,6 +5,7 @@ import React, { FC } from 'react';
|
|||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import SuggestionsSectionContainer from './SuggestionsSectionContainer';
|
||||
import { Sections, SectionsView, SectionsViewType } from 'types/suggestionsSections';
|
||||
import { ParentId } from 'types/library';
|
||||
|
||||
const getSuggestionsSections = (): Sections[] => {
|
||||
return [
|
||||
|
@ -178,7 +179,7 @@ const getSuggestionsSections = (): Sections[] => {
|
|||
};
|
||||
|
||||
interface SuggestionsItemsContainerProps {
|
||||
parentId?: string | null;
|
||||
parentId: ParentId;
|
||||
sectionsView: SectionsView[];
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,10 @@ import { appRouter } from 'components/router/appRouter';
|
|||
import SectionContainer from './SectionContainer';
|
||||
|
||||
import { Sections } from 'types/suggestionsSections';
|
||||
import { ParentId } from 'types/library';
|
||||
|
||||
interface SuggestionsSectionContainerProps {
|
||||
parentId?: string | null;
|
||||
parentId: ParentId;
|
||||
section: Sections;
|
||||
}
|
||||
|
||||
|
@ -37,7 +38,7 @@ const SuggestionsSectionContainer: FC<SuggestionsSectionContainerProps> = ({
|
|||
return (
|
||||
<SectionContainer
|
||||
sectionTitle={globalize.translate(section.name)}
|
||||
items={items || []}
|
||||
items={items ?? []}
|
||||
url={getRouteUrl()}
|
||||
cardOptions={{
|
||||
...section.cardOptions
|
||||
|
|
|
@ -28,7 +28,7 @@ import FiltersTags from './FiltersTags';
|
|||
import FiltersVideoTypes from './FiltersVideoTypes';
|
||||
import FiltersYears from './FiltersYears';
|
||||
|
||||
import { LibraryViewSettings } from 'types/library';
|
||||
import { LibraryViewSettings, ParentId } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
const Accordion = styled((props: AccordionProps) => (
|
||||
|
@ -73,9 +73,10 @@ const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
|
|||
}));
|
||||
|
||||
interface FilterButtonProps {
|
||||
parentId: string | null | undefined;
|
||||
itemType: BaseItemKind;
|
||||
parentId: ParentId;
|
||||
itemType: BaseItemKind[];
|
||||
viewType: LibraryTab;
|
||||
hasFilters: boolean;
|
||||
libraryViewSettings: LibraryViewSettings;
|
||||
setLibraryViewSettings: React.Dispatch<
|
||||
React.SetStateAction<LibraryViewSettings>
|
||||
|
@ -86,6 +87,7 @@ const FilterButton: FC<FilterButtonProps> = ({
|
|||
parentId,
|
||||
itemType,
|
||||
viewType,
|
||||
hasFilters,
|
||||
libraryViewSettings,
|
||||
setLibraryViewSettings
|
||||
}) => {
|
||||
|
@ -153,16 +155,13 @@ const FilterButton: FC<FilterButtonProps> = ({
|
|||
return viewType === LibraryTab.Episodes;
|
||||
};
|
||||
|
||||
const hasFilters =
|
||||
Object.values(libraryViewSettings.Filters || {}).some((filter) => !!filter);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<IconButton
|
||||
title={globalize.translate('Filter')}
|
||||
sx={{ ml: 2 }}
|
||||
aria-describedby={id}
|
||||
className='paper-icon-button-light btnShuffle autoSize'
|
||||
className='paper-icon-button-light btnFilter autoSize'
|
||||
onClick={handleClick}
|
||||
>
|
||||
<Badge color='info' variant='dot' invisible={!hasFilters}>
|
||||
|
|
|
@ -27,7 +27,7 @@ type ControllerProps = {
|
|||
|
||||
const Home: FunctionComponent = () => {
|
||||
const [ searchParams ] = useSearchParams();
|
||||
const initialTabIndex = parseInt(searchParams.get('tab') || '0', 10);
|
||||
const initialTabIndex = parseInt(searchParams.get('tab') ?? '0', 10);
|
||||
|
||||
const tabController = useRef<ControllerProps | null>();
|
||||
const tabControllers = useMemo<ControllerProps[]>(() => [], []);
|
||||
|
|
|
@ -1,30 +1,22 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import ViewItemsContainer from 'components/common/ViewItemsContainer';
|
||||
import ItemsView from '../../components/library/ItemsView';
|
||||
import { LibraryViewProps } from 'types/library';
|
||||
import { CollectionType } from 'types/collectionType';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
const CollectionsView: FC<LibraryViewProps> = ({ parentId }) => {
|
||||
const getBasekey = useCallback(() => {
|
||||
return 'collections';
|
||||
}, []);
|
||||
|
||||
const getItemTypes = useCallback(() => {
|
||||
return ['BoxSet'];
|
||||
}, []);
|
||||
|
||||
const getNoItemsMessage = useCallback(() => {
|
||||
return 'MessageNoCollectionsAvailable';
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ViewItemsContainer
|
||||
topParentId={parentId}
|
||||
<ItemsView
|
||||
viewType={LibraryTab.Collections}
|
||||
parentId={parentId}
|
||||
collectionType={CollectionType.Movies}
|
||||
isBtnFilterEnabled={false}
|
||||
isBtnNewCollectionEnabled={true}
|
||||
isAlphaPickerEnabled={false}
|
||||
getBasekey={getBasekey}
|
||||
getItemTypes={getItemTypes}
|
||||
getNoItemsMessage={getNoItemsMessage}
|
||||
isAlphabetPickerEnabled={false}
|
||||
itemType={[BaseItemKind.BoxSet]}
|
||||
noItemsMessage='MessageNoCollectionsAvailable'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,27 +1,17 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import ViewItemsContainer from 'components/common/ViewItemsContainer';
|
||||
import ItemsView from '../../components/library/ItemsView';
|
||||
import { LibraryViewProps } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
const FavoritesView: FC<LibraryViewProps> = ({ parentId }) => {
|
||||
const getBasekey = useCallback(() => {
|
||||
return 'favorites';
|
||||
}, []);
|
||||
|
||||
const getItemTypes = useCallback(() => {
|
||||
return ['Movie'];
|
||||
}, []);
|
||||
|
||||
const getNoItemsMessage = useCallback(() => {
|
||||
return 'MessageNoFavoritesAvailable';
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ViewItemsContainer
|
||||
topParentId={parentId}
|
||||
getBasekey={getBasekey}
|
||||
getItemTypes={getItemTypes}
|
||||
getNoItemsMessage={getNoItemsMessage}
|
||||
<ItemsView
|
||||
viewType={LibraryTab.Favorites}
|
||||
parentId={parentId}
|
||||
itemType={[BaseItemKind.Movie]}
|
||||
noItemsMessage='MessageNoFavoritesAvailable'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,28 +1,20 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import ViewItemsContainer from 'components/common/ViewItemsContainer';
|
||||
import ItemsView from '../../components/library/ItemsView';
|
||||
import { LibraryViewProps } from 'types/library';
|
||||
import { CollectionType } from 'types/collectionType';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
const MoviesView: FC<LibraryViewProps> = ({ parentId }) => {
|
||||
const getBasekey = useCallback(() => {
|
||||
return 'movies';
|
||||
}, []);
|
||||
|
||||
const getItemTypes = useCallback(() => {
|
||||
return ['Movie'];
|
||||
}, []);
|
||||
|
||||
const getNoItemsMessage = useCallback(() => {
|
||||
return 'MessageNoItemsAvailable';
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ViewItemsContainer
|
||||
topParentId={parentId}
|
||||
<ItemsView
|
||||
viewType={LibraryTab.Movies}
|
||||
parentId={parentId}
|
||||
collectionType={CollectionType.Movies}
|
||||
isBtnShuffleEnabled={true}
|
||||
getBasekey={getBasekey}
|
||||
getItemTypes={getItemTypes}
|
||||
getNoItemsMessage={getNoItemsMessage}
|
||||
itemType={[BaseItemKind.Movie]}
|
||||
noItemsMessage='MessageNoItemsAvailable'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,28 +1,17 @@
|
|||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import React, { FC, useCallback } from 'react';
|
||||
|
||||
import ViewItemsContainer from 'components/common/ViewItemsContainer';
|
||||
import ItemsView from '../../components/library/ItemsView';
|
||||
import { LibraryViewProps } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
const TrailersView: FC<LibraryViewProps> = ({ parentId }) => {
|
||||
const getBasekey = useCallback(() => {
|
||||
return 'trailers';
|
||||
}, []);
|
||||
|
||||
const getItemTypes = useCallback(() => {
|
||||
return ['Trailer'];
|
||||
}, []);
|
||||
|
||||
const getNoItemsMessage = useCallback(() => {
|
||||
return 'MessageNoTrailersFound';
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ViewItemsContainer
|
||||
topParentId={parentId}
|
||||
getBasekey={getBasekey}
|
||||
getItemTypes={getItemTypes}
|
||||
getNoItemsMessage={getNoItemsMessage}
|
||||
<ItemsView
|
||||
viewType={LibraryTab.Trailers}
|
||||
parentId={parentId}
|
||||
itemType={[BaseItemKind.Trailer]}
|
||||
noItemsMessage='MessageNoTrailersFound'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
import 'elements/emby-scroller/emby-scroller';
|
||||
import 'elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import 'elements/emby-tabs/emby-tabs';
|
||||
import 'elements/emby-button/emby-button';
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { useLocation, useSearchParams } from 'react-router-dom';
|
||||
import Page from 'components/Page';
|
||||
|
||||
import { getDefaultTabIndex } from '../../components/tabs/tabRoutes';
|
||||
import Page from 'components/Page';
|
||||
import CollectionsView from './CollectionsView';
|
||||
import FavoritesView from './FavoritesView';
|
||||
import GenresView from './GenresView';
|
||||
|
|
|
@ -669,7 +669,7 @@ function getCardFooterText(item, apiClient, options, footerClass, progressHtml,
|
|||
lines.push(globalize.translate('SeriesYearToPresent', productionYear || ''));
|
||||
} else if (item.EndDate && item.ProductionYear) {
|
||||
const endYear = datetime.toLocaleString(datetime.parseISO8601Date(item.EndDate).getFullYear(), { useGrouping: false });
|
||||
lines.push(productionYear + ((endYear === item.ProductionYear) ? '' : (' - ' + endYear)));
|
||||
lines.push(productionYear + ((endYear === productionYear) ? '' : (' - ' + endYear)));
|
||||
} else {
|
||||
lines.push(productionYear || '');
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import AlphaPicker from '../alphaPicker/alphaPicker';
|
||||
import { ViewQuerySettings } from '../../types/interface';
|
||||
|
||||
interface AlphaPickerContainerProps {
|
||||
viewQuerySettings: ViewQuerySettings;
|
||||
setViewQuerySettings: React.Dispatch<React.SetStateAction<ViewQuerySettings>>;
|
||||
}
|
||||
|
||||
const AlphaPickerContainer: FC<AlphaPickerContainerProps> = ({ viewQuerySettings, setViewQuerySettings }) => {
|
||||
const [ alphaPicker, setAlphaPicker ] = useState<AlphaPicker>();
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
alphaPicker?.updateControls(viewQuerySettings);
|
||||
|
||||
const onAlphaPickerChange = useCallback((e) => {
|
||||
const newValue = (e as CustomEvent).detail.value;
|
||||
let updatedValue: React.SetStateAction<ViewQuerySettings>;
|
||||
if (newValue === '#') {
|
||||
updatedValue = {
|
||||
NameLessThan: 'A',
|
||||
NameStartsWith: undefined
|
||||
};
|
||||
} else {
|
||||
updatedValue = {
|
||||
NameLessThan: undefined,
|
||||
NameStartsWith: newValue
|
||||
};
|
||||
}
|
||||
setViewQuerySettings((prevState) => ({
|
||||
...prevState,
|
||||
StartIndex: 0,
|
||||
...updatedValue
|
||||
}));
|
||||
}, [setViewQuerySettings]);
|
||||
|
||||
useEffect(() => {
|
||||
const alphaPickerElement = element.current;
|
||||
|
||||
setAlphaPicker(new AlphaPicker({
|
||||
element: alphaPickerElement,
|
||||
valueChangeEvent: 'click'
|
||||
}));
|
||||
|
||||
if (alphaPickerElement) {
|
||||
alphaPickerElement.addEventListener('alphavaluechanged', onAlphaPickerChange);
|
||||
}
|
||||
|
||||
return () => {
|
||||
alphaPickerElement?.removeEventListener('alphavaluechanged', onAlphaPickerChange);
|
||||
};
|
||||
}, [onAlphaPickerChange]);
|
||||
|
||||
return (
|
||||
<div ref={element} className='alphaPicker alphaPicker-fixed alphaPicker-fixed-right alphaPicker-vertical alphabetPicker-right' />
|
||||
);
|
||||
};
|
||||
|
||||
export default AlphaPickerContainer;
|
|
@ -1,65 +0,0 @@
|
|||
import React, { FC, useCallback, useEffect, useRef } from 'react';
|
||||
import IconButtonElement from '../../elements/IconButtonElement';
|
||||
import { ViewQuerySettings } from '../../types/interface';
|
||||
|
||||
interface FilterProps {
|
||||
topParentId?: string | null;
|
||||
getItemTypes: () => string[];
|
||||
getFilterMenuOptions: () => Record<string, never>;
|
||||
getVisibleFilters: () => string[];
|
||||
viewQuerySettings: ViewQuerySettings;
|
||||
setViewQuerySettings: React.Dispatch<React.SetStateAction<ViewQuerySettings>>;
|
||||
}
|
||||
|
||||
const Filter: FC<FilterProps> = ({
|
||||
topParentId,
|
||||
getItemTypes,
|
||||
getVisibleFilters,
|
||||
getFilterMenuOptions,
|
||||
viewQuerySettings,
|
||||
setViewQuerySettings
|
||||
}) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const showFilterMenu = useCallback(() => {
|
||||
import('../filtermenu/filtermenu').then(({ default: FilterMenu }) => {
|
||||
const filterMenu = new FilterMenu();
|
||||
filterMenu.show({
|
||||
settings: viewQuerySettings,
|
||||
visibleSettings: getVisibleFilters(),
|
||||
parentId: topParentId,
|
||||
itemTypes: getItemTypes(),
|
||||
serverId: window.ApiClient.serverId(),
|
||||
filterMenuOptions: getFilterMenuOptions(),
|
||||
setfilters: setViewQuerySettings
|
||||
}).catch(() => {
|
||||
// filter menu closed
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('[Filter] failed to load filter menu', err);
|
||||
});
|
||||
}, [viewQuerySettings, getVisibleFilters, topParentId, getItemTypes, getFilterMenuOptions, setViewQuerySettings]);
|
||||
|
||||
useEffect(() => {
|
||||
const btnFilter = element.current?.querySelector('.btnFilter');
|
||||
|
||||
btnFilter?.addEventListener('click', showFilterMenu);
|
||||
|
||||
return () => {
|
||||
btnFilter?.removeEventListener('click', showFilterMenu);
|
||||
};
|
||||
}, [showFilterMenu]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnFilter autoSize'
|
||||
title='Filter'
|
||||
icon='material-icons filter_list'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Filter;
|
|
@ -1,42 +0,0 @@
|
|||
import React, { FC, useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import IconButtonElement from '../../elements/IconButtonElement';
|
||||
|
||||
const NewCollection: FC = () => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const showCollectionEditor = useCallback(() => {
|
||||
import('../collectionEditor/collectionEditor').then(({ default: CollectionEditor }) => {
|
||||
const serverId = window.ApiClient.serverId();
|
||||
const collectionEditor = new CollectionEditor();
|
||||
collectionEditor.show({
|
||||
items: [],
|
||||
serverId: serverId
|
||||
}).catch(() => {
|
||||
// closed collection editor
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('[NewCollection] failed to load collection editor', err);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const btnNewCollection = element.current?.querySelector('.btnNewCollection');
|
||||
if (btnNewCollection) {
|
||||
btnNewCollection.addEventListener('click', showCollectionEditor);
|
||||
}
|
||||
}, [showCollectionEditor]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnNewCollection autoSize'
|
||||
title='Add'
|
||||
icon='material-icons add'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NewCollection;
|
|
@ -1,97 +0,0 @@
|
|||
import type { BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client';
|
||||
import React, { FC, useCallback, useEffect, useRef } from 'react';
|
||||
import IconButtonElement from '../../elements/IconButtonElement';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import { ViewQuerySettings } from '../../types/interface';
|
||||
|
||||
interface PaginationProps {
|
||||
viewQuerySettings: ViewQuerySettings;
|
||||
setViewQuerySettings: React.Dispatch<React.SetStateAction<ViewQuerySettings>>;
|
||||
itemsResult?: BaseItemDtoQueryResult;
|
||||
}
|
||||
|
||||
const Pagination: FC<PaginationProps> = ({ viewQuerySettings, setViewQuerySettings, itemsResult = {} }) => {
|
||||
const limit = userSettings.libraryPageSize(undefined);
|
||||
const totalRecordCount = itemsResult.TotalRecordCount || 0;
|
||||
const startIndex = viewQuerySettings.StartIndex || 0;
|
||||
const recordsStart = totalRecordCount ? startIndex + 1 : 0;
|
||||
const recordsEnd = limit ? Math.min(startIndex + limit, totalRecordCount) : totalRecordCount;
|
||||
const showControls = limit > 0 && limit < totalRecordCount;
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const onNextPageClick = useCallback(() => {
|
||||
if (limit > 0) {
|
||||
const newIndex = startIndex + limit;
|
||||
setViewQuerySettings((prevState) => ({
|
||||
...prevState,
|
||||
StartIndex: newIndex
|
||||
}));
|
||||
}
|
||||
}, [limit, setViewQuerySettings, startIndex]);
|
||||
|
||||
const onPreviousPageClick = useCallback(() => {
|
||||
if (limit > 0) {
|
||||
const newIndex = Math.max(0, startIndex - limit);
|
||||
setViewQuerySettings((prevState) => ({
|
||||
...prevState,
|
||||
StartIndex: newIndex
|
||||
}));
|
||||
}
|
||||
}, [limit, setViewQuerySettings, startIndex]);
|
||||
|
||||
useEffect(() => {
|
||||
const btnNextPage = element.current?.querySelector('.btnNextPage') as HTMLButtonElement;
|
||||
if (btnNextPage) {
|
||||
if (startIndex + limit >= totalRecordCount) {
|
||||
btnNextPage.disabled = true;
|
||||
} else {
|
||||
btnNextPage.disabled = false;
|
||||
}
|
||||
btnNextPage.addEventListener('click', onNextPageClick);
|
||||
}
|
||||
|
||||
const btnPreviousPage = element.current?.querySelector('.btnPreviousPage') as HTMLButtonElement;
|
||||
if (btnPreviousPage) {
|
||||
if (startIndex) {
|
||||
btnPreviousPage.disabled = false;
|
||||
} else {
|
||||
btnPreviousPage.disabled = true;
|
||||
}
|
||||
btnPreviousPage.addEventListener('click', onPreviousPageClick);
|
||||
}
|
||||
|
||||
return () => {
|
||||
btnNextPage?.removeEventListener('click', onNextPageClick);
|
||||
btnPreviousPage?.removeEventListener('click', onPreviousPageClick);
|
||||
};
|
||||
}, [totalRecordCount, onNextPageClick, onPreviousPageClick, limit, startIndex]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div className='paging'>
|
||||
<div className='listPaging' style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<span>
|
||||
{globalize.translate('ListPaging', recordsStart, recordsEnd, totalRecordCount)}
|
||||
</span>
|
||||
{showControls && (
|
||||
<>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnPreviousPage autoSize'
|
||||
icon='material-icons arrow_back'
|
||||
/>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnNextPage autoSize'
|
||||
icon='material-icons arrow_forward'
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Pagination;
|
|
@ -1,54 +0,0 @@
|
|||
import React, { FC, useCallback, useEffect, useRef } from 'react';
|
||||
import IconButtonElement from '../../elements/IconButtonElement';
|
||||
import { ViewQuerySettings } from '../../types/interface';
|
||||
|
||||
interface SelectViewProps {
|
||||
getVisibleViewSettings: () => string[];
|
||||
viewQuerySettings: ViewQuerySettings;
|
||||
setViewQuerySettings: React.Dispatch<React.SetStateAction<ViewQuerySettings>>;
|
||||
}
|
||||
|
||||
const SelectView: FC<SelectViewProps> = ({
|
||||
getVisibleViewSettings,
|
||||
viewQuerySettings,
|
||||
setViewQuerySettings
|
||||
}) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const showViewSettingsMenu = useCallback(() => {
|
||||
import('../viewSettings/viewSettings').then(({ default: ViewSettings }) => {
|
||||
const viewsettings = new ViewSettings();
|
||||
viewsettings.show({
|
||||
settings: viewQuerySettings,
|
||||
visibleSettings: getVisibleViewSettings(),
|
||||
setviewsettings: setViewQuerySettings
|
||||
}).catch(() => {
|
||||
// view settings closed
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('[SelectView] failed to load view settings', err);
|
||||
});
|
||||
}, [getVisibleViewSettings, viewQuerySettings, setViewQuerySettings]);
|
||||
|
||||
useEffect(() => {
|
||||
const btnSelectView = element.current?.querySelector('.btnSelectView') as HTMLButtonElement;
|
||||
btnSelectView?.addEventListener('click', showViewSettingsMenu);
|
||||
|
||||
return () => {
|
||||
btnSelectView?.removeEventListener('click', showViewSettingsMenu);
|
||||
};
|
||||
}, [showViewSettingsMenu]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnSelectView autoSize'
|
||||
title='ButtonSelectView'
|
||||
icon='material-icons view_comfy'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectView;
|
|
@ -1,45 +0,0 @@
|
|||
import type { BaseItemDtoQueryResult } from '@jellyfin/sdk/lib/generated-client';
|
||||
import React, { FC, useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
import { playbackManager } from '../playback/playbackmanager';
|
||||
import IconButtonElement from '../../elements/IconButtonElement';
|
||||
|
||||
interface ShuffleProps {
|
||||
itemsResult?: BaseItemDtoQueryResult;
|
||||
topParentId: string | null;
|
||||
}
|
||||
|
||||
const Shuffle: FC<ShuffleProps> = ({ itemsResult = {}, topParentId }) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const shuffle = useCallback(() => {
|
||||
window.ApiClient.getItem(
|
||||
window.ApiClient.getCurrentUserId(),
|
||||
topParentId as string
|
||||
).then((item) => {
|
||||
playbackManager.shuffle(item);
|
||||
}).catch(err => {
|
||||
console.error('[Shuffle] failed to fetch items', err);
|
||||
});
|
||||
}, [topParentId]);
|
||||
|
||||
useEffect(() => {
|
||||
const btnShuffle = element.current?.querySelector('.btnShuffle');
|
||||
if (btnShuffle) {
|
||||
btnShuffle.addEventListener('click', shuffle);
|
||||
}
|
||||
}, [itemsResult.TotalRecordCount, shuffle]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnShuffle autoSize'
|
||||
title='Shuffle'
|
||||
icon='material-icons shuffle'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Shuffle;
|
|
@ -1,58 +0,0 @@
|
|||
import React, { FC, useCallback, useEffect, useRef } from 'react';
|
||||
import IconButtonElement from '../../elements/IconButtonElement';
|
||||
import { ViewQuerySettings } from '../../types/interface';
|
||||
|
||||
interface SortProps {
|
||||
getSortMenuOptions: () => {
|
||||
name: string;
|
||||
value: string;
|
||||
}[];
|
||||
viewQuerySettings: ViewQuerySettings;
|
||||
setViewQuerySettings: React.Dispatch<React.SetStateAction<ViewQuerySettings>>;
|
||||
}
|
||||
|
||||
const Sort: FC<SortProps> = ({
|
||||
getSortMenuOptions,
|
||||
viewQuerySettings,
|
||||
setViewQuerySettings
|
||||
}) => {
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const showSortMenu = useCallback(() => {
|
||||
import('../sortmenu/sortmenu').then(({ default: SortMenu }) => {
|
||||
const sortMenu = new SortMenu();
|
||||
sortMenu.show({
|
||||
settings: viewQuerySettings,
|
||||
sortOptions: getSortMenuOptions(),
|
||||
setSortValues: setViewQuerySettings
|
||||
}).catch(() => {
|
||||
// sort menu closed
|
||||
});
|
||||
}).catch(err => {
|
||||
console.error('[Sort] failed to load sort menu', err);
|
||||
});
|
||||
}, [getSortMenuOptions, viewQuerySettings, setViewQuerySettings]);
|
||||
|
||||
useEffect(() => {
|
||||
const btnSort = element.current?.querySelector('.btnSort');
|
||||
|
||||
btnSort?.addEventListener('click', showSortMenu);
|
||||
|
||||
return () => {
|
||||
btnSort?.removeEventListener('click', showSortMenu);
|
||||
};
|
||||
}, [showSortMenu]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<IconButtonElement
|
||||
is='paper-icon-button-light'
|
||||
className='btnSort autoSize'
|
||||
title='Sort'
|
||||
icon='material-icons sort_by_alpha'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sort;
|
|
@ -1,411 +0,0 @@
|
|||
import { type BaseItemDtoQueryResult, ItemFields, ItemFilter } from '@jellyfin/sdk/lib/generated-client';
|
||||
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import loading from '../loading/loading';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import AlphaPickerContainer from './AlphaPickerContainer';
|
||||
import Filter from './Filter';
|
||||
import ItemsContainer from './ItemsContainer';
|
||||
import Pagination from './Pagination';
|
||||
import SelectView from './SelectView';
|
||||
import Shuffle from './Shuffle';
|
||||
import Sort from './Sort';
|
||||
import NewCollection from './NewCollection';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
||||
import listview from '../listview/listview';
|
||||
import cardBuilder from '../cardbuilder/cardBuilder';
|
||||
|
||||
import { ViewQuerySettings } from '../../types/interface';
|
||||
import { CardOptions } from '../../types/cardOptions';
|
||||
|
||||
interface ViewItemsContainerProps {
|
||||
topParentId: string | null;
|
||||
isBtnShuffleEnabled?: boolean;
|
||||
isBtnFilterEnabled?: boolean;
|
||||
isBtnNewCollectionEnabled?: boolean;
|
||||
isAlphaPickerEnabled?: boolean;
|
||||
getBasekey: () => string;
|
||||
getItemTypes: () => string[];
|
||||
getNoItemsMessage: () => string;
|
||||
}
|
||||
|
||||
const getDefaultSortBy = () => {
|
||||
return 'SortName';
|
||||
};
|
||||
|
||||
const getFields = (viewQuerySettings: ViewQuerySettings) => {
|
||||
const fields: ItemFields[] = [
|
||||
ItemFields.BasicSyncInfo,
|
||||
ItemFields.MediaSourceCount
|
||||
];
|
||||
|
||||
if (viewQuerySettings.imageType === 'primary') {
|
||||
fields.push(ItemFields.PrimaryImageAspectRatio);
|
||||
}
|
||||
|
||||
return fields.join(',');
|
||||
};
|
||||
|
||||
const getFilters = (viewQuerySettings: ViewQuerySettings) => {
|
||||
const filters: ItemFilter[] = [];
|
||||
|
||||
if (viewQuerySettings.IsPlayed) {
|
||||
filters.push(ItemFilter.IsPlayed);
|
||||
}
|
||||
|
||||
if (viewQuerySettings.IsUnplayed) {
|
||||
filters.push(ItemFilter.IsUnplayed);
|
||||
}
|
||||
|
||||
if (viewQuerySettings.IsFavorite) {
|
||||
filters.push(ItemFilter.IsFavorite);
|
||||
}
|
||||
|
||||
if (viewQuerySettings.IsResumable) {
|
||||
filters.push(ItemFilter.IsResumable);
|
||||
}
|
||||
|
||||
return filters;
|
||||
};
|
||||
|
||||
const getVisibleViewSettings = () => {
|
||||
return [
|
||||
'showTitle',
|
||||
'showYear',
|
||||
'imageType',
|
||||
'cardLayout'
|
||||
];
|
||||
};
|
||||
|
||||
const getFilterMenuOptions = () => {
|
||||
return {};
|
||||
};
|
||||
|
||||
const getVisibleFilters = () => {
|
||||
return [
|
||||
'IsUnplayed',
|
||||
'IsPlayed',
|
||||
'IsFavorite',
|
||||
'IsResumable',
|
||||
'VideoType',
|
||||
'HasSubtitles',
|
||||
'HasTrailer',
|
||||
'HasSpecialFeature',
|
||||
'HasThemeSong',
|
||||
'HasThemeVideo'
|
||||
];
|
||||
};
|
||||
|
||||
const getSortMenuOptions = () => {
|
||||
return [{
|
||||
name: globalize.translate('Name'),
|
||||
value: 'SortName,ProductionYear'
|
||||
}, {
|
||||
name: globalize.translate('OptionRandom'),
|
||||
value: 'Random'
|
||||
}, {
|
||||
name: globalize.translate('OptionImdbRating'),
|
||||
value: 'CommunityRating,SortName,ProductionYear'
|
||||
}, {
|
||||
name: globalize.translate('OptionCriticRating'),
|
||||
value: 'CriticRating,SortName,ProductionYear'
|
||||
}, {
|
||||
name: globalize.translate('OptionDateAdded'),
|
||||
value: 'DateCreated,SortName,ProductionYear'
|
||||
}, {
|
||||
name: globalize.translate('OptionDatePlayed'),
|
||||
value: 'DatePlayed,SortName,ProductionYear'
|
||||
}, {
|
||||
name: globalize.translate('OptionParentalRating'),
|
||||
value: 'OfficialRating,SortName,ProductionYear'
|
||||
}, {
|
||||
name: globalize.translate('OptionPlayCount'),
|
||||
value: 'PlayCount,SortName,ProductionYear'
|
||||
}, {
|
||||
name: globalize.translate('OptionReleaseDate'),
|
||||
value: 'PremiereDate,SortName,ProductionYear'
|
||||
}, {
|
||||
name: globalize.translate('Runtime'),
|
||||
value: 'Runtime,SortName,ProductionYear'
|
||||
}];
|
||||
};
|
||||
|
||||
const defaultViewQuerySettings: ViewQuerySettings = {
|
||||
showTitle: true,
|
||||
showYear: true,
|
||||
imageType: 'primary',
|
||||
viewType: '',
|
||||
cardLayout: false,
|
||||
SortBy: getDefaultSortBy(),
|
||||
SortOrder: 'Ascending',
|
||||
IsPlayed: false,
|
||||
IsUnplayed: false,
|
||||
IsFavorite: false,
|
||||
IsResumable: false,
|
||||
Is4K: null,
|
||||
IsHD: null,
|
||||
IsSD: null,
|
||||
Is3D: null,
|
||||
VideoTypes: '',
|
||||
SeriesStatus: '',
|
||||
HasSubtitles: null,
|
||||
HasTrailer: null,
|
||||
HasSpecialFeature: null,
|
||||
HasThemeSong: null,
|
||||
HasThemeVideo: null,
|
||||
GenreIds: '',
|
||||
StartIndex: 0
|
||||
};
|
||||
|
||||
const ViewItemsContainer: FC<ViewItemsContainerProps> = ({
|
||||
topParentId,
|
||||
isBtnShuffleEnabled = false,
|
||||
isBtnFilterEnabled = true,
|
||||
isBtnNewCollectionEnabled = false,
|
||||
isAlphaPickerEnabled = true,
|
||||
getBasekey,
|
||||
getItemTypes,
|
||||
getNoItemsMessage
|
||||
}) => {
|
||||
const getSettingsKey = useCallback(() => {
|
||||
return `${topParentId} - ${getBasekey()}`;
|
||||
}, [getBasekey, topParentId]);
|
||||
|
||||
const [isLoading, setisLoading] = useState(false);
|
||||
|
||||
const [viewQuerySettings, setViewQuerySettings] = useLocalStorage<ViewQuerySettings>(
|
||||
`viewQuerySettings - ${getSettingsKey()}`,
|
||||
defaultViewQuerySettings
|
||||
);
|
||||
|
||||
const [ itemsResult, setItemsResult ] = useState<BaseItemDtoQueryResult>({});
|
||||
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
const getContext = useCallback(() => {
|
||||
const itemType = getItemTypes().join(',');
|
||||
if (itemType === 'Movie' || itemType === 'BoxSet') {
|
||||
return 'movies';
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [getItemTypes]);
|
||||
|
||||
const getCardOptions = useCallback(() => {
|
||||
let shape;
|
||||
let preferThumb;
|
||||
let preferDisc;
|
||||
let preferLogo;
|
||||
|
||||
if (viewQuerySettings.imageType === 'banner') {
|
||||
shape = 'banner';
|
||||
} else if (viewQuerySettings.imageType === 'disc') {
|
||||
shape = 'square';
|
||||
preferDisc = true;
|
||||
} else if (viewQuerySettings.imageType === 'logo') {
|
||||
shape = 'backdrop';
|
||||
preferLogo = true;
|
||||
} else if (viewQuerySettings.imageType === 'thumb') {
|
||||
shape = 'backdrop';
|
||||
preferThumb = true;
|
||||
} else {
|
||||
shape = 'autoVertical';
|
||||
}
|
||||
|
||||
const cardOptions: CardOptions = {
|
||||
shape: shape,
|
||||
showTitle: viewQuerySettings.showTitle,
|
||||
showYear: viewQuerySettings.showYear,
|
||||
cardLayout: viewQuerySettings.cardLayout,
|
||||
centerText: true,
|
||||
context: getContext(),
|
||||
coverImage: true,
|
||||
preferThumb: preferThumb,
|
||||
preferDisc: preferDisc,
|
||||
preferLogo: preferLogo,
|
||||
overlayPlayButton: false,
|
||||
overlayMoreButton: true,
|
||||
overlayText: !viewQuerySettings.showTitle
|
||||
};
|
||||
|
||||
cardOptions.items = itemsResult.Items || [];
|
||||
|
||||
return cardOptions;
|
||||
}, [
|
||||
getContext,
|
||||
itemsResult.Items,
|
||||
viewQuerySettings.cardLayout,
|
||||
viewQuerySettings.imageType,
|
||||
viewQuerySettings.showTitle,
|
||||
viewQuerySettings.showYear
|
||||
]);
|
||||
|
||||
const getItemsHtml = useCallback(() => {
|
||||
let html = '';
|
||||
|
||||
if (viewQuerySettings.imageType === 'list') {
|
||||
html = listview.getListViewHtml({
|
||||
items: itemsResult.Items || [],
|
||||
context: getContext()
|
||||
});
|
||||
} else {
|
||||
html = cardBuilder.getCardsHtml(itemsResult.Items || [], getCardOptions());
|
||||
}
|
||||
|
||||
if (!itemsResult.Items?.length) {
|
||||
html += '<div class="noItemsMessage centerMessage">';
|
||||
html += '<h1>' + globalize.translate('MessageNothingHere') + '</h1>';
|
||||
html += '<p>' + globalize.translate(getNoItemsMessage()) + '</p>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
return html;
|
||||
}, [getCardOptions, getContext, itemsResult.Items, getNoItemsMessage, viewQuerySettings.imageType]);
|
||||
|
||||
const getQuery = useCallback(() => {
|
||||
const queryFilters = getFilters(viewQuerySettings);
|
||||
|
||||
let queryIsHD;
|
||||
|
||||
if (viewQuerySettings.IsHD) {
|
||||
queryIsHD = true;
|
||||
}
|
||||
|
||||
if (viewQuerySettings.IsSD) {
|
||||
queryIsHD = false;
|
||||
}
|
||||
|
||||
return {
|
||||
SortBy: viewQuerySettings.SortBy,
|
||||
SortOrder: viewQuerySettings.SortOrder,
|
||||
IncludeItemTypes: getItemTypes().join(','),
|
||||
Recursive: true,
|
||||
Fields: getFields(viewQuerySettings),
|
||||
ImageTypeLimit: 1,
|
||||
EnableImageTypes: 'Primary,Backdrop,Banner,Thumb,Disc,Logo',
|
||||
Limit: userSettings.libraryPageSize(undefined) || undefined,
|
||||
IsFavorite: getBasekey() === 'favorites' ? true : null,
|
||||
VideoTypes: viewQuerySettings.VideoTypes,
|
||||
GenreIds: viewQuerySettings.GenreIds,
|
||||
Is4K: viewQuerySettings.Is4K ? true : null,
|
||||
IsHD: queryIsHD,
|
||||
Is3D: viewQuerySettings.Is3D ? true : null,
|
||||
HasSubtitles: viewQuerySettings.HasSubtitles ? true : null,
|
||||
HasTrailer: viewQuerySettings.HasTrailer ? true : null,
|
||||
HasSpecialFeature: viewQuerySettings.HasSpecialFeature ? true : null,
|
||||
HasThemeSong: viewQuerySettings.HasThemeSong ? true : null,
|
||||
HasThemeVideo: viewQuerySettings.HasThemeVideo ? true : null,
|
||||
Filters: queryFilters.length ? queryFilters.join(',') : null,
|
||||
StartIndex: viewQuerySettings.StartIndex,
|
||||
NameLessThan: viewQuerySettings.NameLessThan,
|
||||
NameStartsWith: viewQuerySettings.NameStartsWith,
|
||||
ParentId: topParentId
|
||||
};
|
||||
}, [
|
||||
viewQuerySettings,
|
||||
getItemTypes,
|
||||
getBasekey,
|
||||
topParentId
|
||||
]);
|
||||
|
||||
const fetchData = useCallback(() => {
|
||||
loading.show();
|
||||
|
||||
const apiClient = ServerConnections.getApiClient(window.ApiClient.serverId());
|
||||
return apiClient.getItems(
|
||||
apiClient.getCurrentUserId(),
|
||||
{
|
||||
...getQuery()
|
||||
}
|
||||
);
|
||||
}, [getQuery]);
|
||||
|
||||
const reloadItems = useCallback(() => {
|
||||
const page = element.current;
|
||||
|
||||
if (!page) {
|
||||
console.error('Unexpected null reference');
|
||||
return;
|
||||
}
|
||||
setisLoading(false);
|
||||
fetchData().then((result) => {
|
||||
setItemsResult(result);
|
||||
|
||||
window.scrollTo(0, 0);
|
||||
|
||||
import('../../components/autoFocuser').then(({ default: autoFocuser }) => {
|
||||
autoFocuser.autoFocus(page);
|
||||
}).catch(err => {
|
||||
console.error('[ViewItemsContainer] failed to load autofocuser', err);
|
||||
});
|
||||
loading.hide();
|
||||
setisLoading(true);
|
||||
}).catch(err => {
|
||||
console.error('[ViewItemsContainer] failed to fetch data', err);
|
||||
});
|
||||
}, [fetchData]);
|
||||
|
||||
useEffect(() => {
|
||||
reloadItems();
|
||||
}, [reloadItems]);
|
||||
|
||||
return (
|
||||
<div ref={element}>
|
||||
<div className='flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x'>
|
||||
<Pagination
|
||||
itemsResult= {itemsResult}
|
||||
viewQuerySettings={viewQuerySettings}
|
||||
setViewQuerySettings={setViewQuerySettings}
|
||||
/>
|
||||
|
||||
{isBtnShuffleEnabled && <Shuffle itemsResult={itemsResult} topParentId={topParentId} />}
|
||||
|
||||
<SelectView
|
||||
getVisibleViewSettings={getVisibleViewSettings}
|
||||
viewQuerySettings={viewQuerySettings}
|
||||
setViewQuerySettings={setViewQuerySettings}
|
||||
/>
|
||||
|
||||
<Sort
|
||||
getSortMenuOptions={getSortMenuOptions}
|
||||
viewQuerySettings={viewQuerySettings}
|
||||
setViewQuerySettings={setViewQuerySettings}
|
||||
/>
|
||||
|
||||
{isBtnFilterEnabled && <Filter
|
||||
topParentId={topParentId}
|
||||
getItemTypes={getItemTypes}
|
||||
getVisibleFilters={getVisibleFilters}
|
||||
getFilterMenuOptions={getFilterMenuOptions}
|
||||
viewQuerySettings={viewQuerySettings}
|
||||
setViewQuerySettings={setViewQuerySettings}
|
||||
/>}
|
||||
|
||||
{isBtnNewCollectionEnabled && <NewCollection />}
|
||||
|
||||
</div>
|
||||
|
||||
{isAlphaPickerEnabled && <AlphaPickerContainer
|
||||
viewQuerySettings={viewQuerySettings}
|
||||
setViewQuerySettings={setViewQuerySettings}
|
||||
/>}
|
||||
|
||||
{isLoading && <ItemsContainer
|
||||
viewQuerySettings={viewQuerySettings}
|
||||
getItemsHtml={getItemsHtml}
|
||||
/>}
|
||||
|
||||
<div className='flex align-items-center justify-content-center flex-wrap-wrap padded-top padded-left padded-right padded-bottom focuscontainer-x'>
|
||||
<Pagination
|
||||
itemsResult= {itemsResult}
|
||||
viewQuerySettings={viewQuerySettings}
|
||||
setViewQuerySettings={setViewQuerySettings}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewItemsContainer;
|
|
@ -103,37 +103,29 @@ function onInputCommand(e) {
|
|||
}
|
||||
}
|
||||
function saveValues(context, settings, settingsKey, setfilters) {
|
||||
let elems = context.querySelectorAll('.simpleFilter');
|
||||
|
||||
// Video type
|
||||
const videoTypes = [];
|
||||
elems = context.querySelectorAll('.chkVideoTypeFilter');
|
||||
|
||||
for (let i = 0, length = elems.length; i < length; i++) {
|
||||
if (elems[i].checked) {
|
||||
videoTypes.push(elems[i].getAttribute('data-filter'));
|
||||
context.querySelectorAll('.chkVideoTypeFilter').forEach(elem => {
|
||||
if (elem.checked) {
|
||||
videoTypes.push(elem.getAttribute('data-filter'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Series status
|
||||
const seriesStatuses = [];
|
||||
elems = context.querySelectorAll('.chkSeriesStatus');
|
||||
|
||||
for (let i = 0, length = elems.length; i < length; i++) {
|
||||
if (elems[i].checked) {
|
||||
seriesStatuses.push(elems[i].getAttribute('data-filter'));
|
||||
context.querySelectorAll('.chkSeriesStatus').forEach(elem => {
|
||||
if (elem.checked) {
|
||||
seriesStatuses.push(elem.getAttribute('data-filter'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Genres
|
||||
const genres = [];
|
||||
elems = context.querySelectorAll('.chkGenreFilter');
|
||||
|
||||
for (let i = 0, length = elems.length; i < length; i++) {
|
||||
if (elems[i].checked) {
|
||||
genres.push(elems[i].getAttribute('data-filter'));
|
||||
context.querySelectorAll('.chkGenreFilter').forEach(elem => {
|
||||
if (elem.checked) {
|
||||
genres.push(elem.getAttribute('data-filter'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (setfilters) {
|
||||
setfilters((prevState) => ({
|
||||
|
@ -157,13 +149,13 @@ function saveValues(context, settings, settingsKey, setfilters) {
|
|||
GenreIds: genres.join(',')
|
||||
}));
|
||||
} else {
|
||||
for (let i = 0, length = elems.length; i < length; i++) {
|
||||
if (elems[i].tagName === 'INPUT') {
|
||||
setBasicFilter(context, settingsKey + '-filter-' + elems[i].getAttribute('data-settingname'), elems[i]);
|
||||
context.querySelectorAll('.simpleFilter').forEach(elem => {
|
||||
if (elem.tagName === 'INPUT') {
|
||||
setBasicFilter(context, settingsKey + '-filter-' + elem.getAttribute('data-settingname'), elem);
|
||||
} else {
|
||||
setBasicFilter(context, settingsKey + '-filter-' + elems[i].getAttribute('data-settingname'), elems[i].querySelector('input'));
|
||||
setBasicFilter(context, settingsKey + '-filter-' + elem.getAttribute('data-settingname'), elem.querySelector('input'));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
userSettings.setFilter(settingsKey + '-filter-GenreIds', genres.join(','));
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import layoutManager from 'components/layoutManager';
|
||||
import globalize from 'scripts/globalize';
|
||||
import { DEFAULT_SECTIONS, HomeSectionType } from 'types/homeSectionType';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
|
@ -33,6 +34,18 @@ function getAllSectionsToShow(userSettings, sectionCount) {
|
|||
sections.push(section);
|
||||
}
|
||||
|
||||
// Ensure libraries are visible in TV layout
|
||||
if (
|
||||
layoutManager.tv
|
||||
&& !sections.includes(HomeSectionType.SmallLibraryTiles)
|
||||
&& !sections.includes(HomeSectionType.LibraryButtons)
|
||||
) {
|
||||
return [
|
||||
HomeSectionType.SmallLibraryTiles,
|
||||
...sections
|
||||
];
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
|
@ -41,8 +54,10 @@ export function loadSections(elem, apiClient, user, userSettings) {
|
|||
let html = '';
|
||||
|
||||
if (userViews.length) {
|
||||
const sectionCount = 7;
|
||||
for (let i = 0; i < sectionCount; i++) {
|
||||
const userSectionCount = 7;
|
||||
// TV layout can have an extra section to ensure libraries are visible
|
||||
const totalSectionCount = layoutManager.tv ? userSectionCount + 1 : userSectionCount;
|
||||
for (let i = 0; i < totalSectionCount; i++) {
|
||||
html += '<div class="verticalSection section' + i + '"></div>';
|
||||
}
|
||||
|
||||
|
@ -50,7 +65,7 @@ export function loadSections(elem, apiClient, user, userSettings) {
|
|||
elem.classList.add('homeSectionsContainer');
|
||||
|
||||
const promises = [];
|
||||
const sections = getAllSectionsToShow(userSettings, sectionCount);
|
||||
const sections = getAllSectionsToShow(userSettings, userSectionCount);
|
||||
for (let i = 0; i < sections.length; i++) {
|
||||
promises.push(loadSection(elem, apiClient, user, userSettings, userViews, sections, i));
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ export function fillImage(entry) {
|
|||
throw new Error('entry cannot be null');
|
||||
}
|
||||
const target = entry.target;
|
||||
let source = undefined;
|
||||
let source;
|
||||
|
||||
if (target) {
|
||||
source = target.getAttribute('data-src');
|
||||
|
|
|
@ -1848,6 +1848,56 @@ class PlaybackManager {
|
|||
SortBy: options.shuffle ? 'Random' : 'SortName',
|
||||
MediaTypes: 'Audio'
|
||||
});
|
||||
} else if (firstItem.Type === 'Series' || firstItem.Type === 'Season') {
|
||||
const apiClient = ServerConnections.getApiClient(firstItem.ServerId);
|
||||
|
||||
promise = apiClient.getEpisodes(firstItem.SeriesId || firstItem.Id, {
|
||||
IsVirtualUnaired: false,
|
||||
IsMissing: false,
|
||||
UserId: apiClient.getCurrentUserId(),
|
||||
Fields: 'Chapters'
|
||||
}).then(function (episodesResult) {
|
||||
const originalResults = episodesResult.Items;
|
||||
const isSeries = firstItem.Type === 'Series';
|
||||
|
||||
let foundItem = false;
|
||||
|
||||
episodesResult.Items = episodesResult.Items.filter(function (e) {
|
||||
if (foundItem) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!e.UserData.Played && (isSeries || e.SeasonId === firstItem.Id)) {
|
||||
foundItem = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (episodesResult.Items.length === 0) {
|
||||
if (isSeries) {
|
||||
episodesResult.Items = originalResults;
|
||||
} else {
|
||||
episodesResult.Items = originalResults.filter(function (e) {
|
||||
if (foundItem) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.SeasonId === firstItem.Id) {
|
||||
foundItem = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
episodesResult.TotalRecordCount = episodesResult.Items.length;
|
||||
|
||||
return episodesResult;
|
||||
});
|
||||
} else if (firstItem.IsFolder && firstItem.CollectionType === 'homevideos') {
|
||||
promise = getItemsForPlayback(serverId, mergePlaybackQueries({
|
||||
ParentId: firstItem.Id,
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import type { ItemsApiGetItemsRequest } from '@jellyfin/sdk/lib/generated-client';
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
|
||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
import type { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
||||
import { ItemFields } from '@jellyfin/sdk/lib/generated-client/models/item-fields';
|
||||
import { ItemFilter } from '@jellyfin/sdk/lib/generated-client/models/item-filter';
|
||||
import { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order';
|
||||
import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
|
||||
import { getArtistsApi } from '@jellyfin/sdk/lib/utils/api/artists-api';
|
||||
import { getFilterApi } from '@jellyfin/sdk/lib/utils/api/filter-api';
|
||||
import { getGenresApi } from '@jellyfin/sdk/lib/utils/api/genres-api';
|
||||
import { getItemsApi } from '@jellyfin/sdk/lib/utils/api/items-api';
|
||||
|
@ -14,11 +13,14 @@ import { getMoviesApi } from '@jellyfin/sdk/lib/utils/api/movies-api';
|
|||
import { getStudiosApi } from '@jellyfin/sdk/lib/utils/api/studios-api';
|
||||
import { getTvShowsApi } from '@jellyfin/sdk/lib/utils/api/tv-shows-api';
|
||||
import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api/user-library-api';
|
||||
import { AxiosRequestConfig } from 'axios';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { JellyfinApiContext, useApi } from './useApi';
|
||||
import { getAlphaPickerQuery, getFieldsQuery, getFiltersQuery, getLimitQuery } from 'utils/items';
|
||||
import { Sections, SectionsViewType } from 'types/suggestionsSections';
|
||||
import { ParentId } from 'types/library';
|
||||
import { LibraryViewSettings, ParentId } from 'types/library';
|
||||
import { LibraryTab } from 'types/libraryTab';
|
||||
|
||||
const fetchGetItem = async (
|
||||
currentApi: JellyfinApiContext,
|
||||
|
@ -291,7 +293,7 @@ export const useGetGenres = (itemType: BaseItemKind, parentId: ParentId) => {
|
|||
const fetchGetStudios = async (
|
||||
currentApi: JellyfinApiContext,
|
||||
parentId: ParentId,
|
||||
itemType: BaseItemKind,
|
||||
itemType: BaseItemKind[],
|
||||
options?: AxiosRequestConfig
|
||||
) => {
|
||||
const { api, user } = currentApi;
|
||||
|
@ -299,7 +301,7 @@ const fetchGetStudios = async (
|
|||
const response = await getStudiosApi(api).getStudios(
|
||||
{
|
||||
userId: user.Id,
|
||||
includeItemTypes: [itemType],
|
||||
includeItemTypes: itemType,
|
||||
fields: [
|
||||
ItemFields.DateCreated,
|
||||
ItemFields.PrimaryImageAspectRatio
|
||||
|
@ -316,7 +318,7 @@ const fetchGetStudios = async (
|
|||
}
|
||||
};
|
||||
|
||||
export const useGetStudios = (parentId: ParentId, itemType: BaseItemKind) => {
|
||||
export const useGetStudios = (parentId: ParentId, itemType: BaseItemKind[]) => {
|
||||
const currentApi = useApi();
|
||||
return useQuery({
|
||||
queryKey: ['Studios', parentId, itemType],
|
||||
|
@ -329,7 +331,7 @@ export const useGetStudios = (parentId: ParentId, itemType: BaseItemKind) => {
|
|||
const fetchGetQueryFiltersLegacy = async (
|
||||
currentApi: JellyfinApiContext,
|
||||
parentId: ParentId,
|
||||
itemType: BaseItemKind,
|
||||
itemType: BaseItemKind[],
|
||||
options?: AxiosRequestConfig
|
||||
) => {
|
||||
const { api, user } = currentApi;
|
||||
|
@ -338,7 +340,7 @@ const fetchGetQueryFiltersLegacy = async (
|
|||
{
|
||||
userId: user.Id,
|
||||
parentId: parentId ?? undefined,
|
||||
includeItemTypes: [itemType]
|
||||
includeItemTypes: itemType
|
||||
},
|
||||
{
|
||||
signal: options?.signal
|
||||
|
@ -350,7 +352,7 @@ const fetchGetQueryFiltersLegacy = async (
|
|||
|
||||
export const useGetQueryFiltersLegacy = (
|
||||
parentId: ParentId,
|
||||
itemType: BaseItemKind
|
||||
itemType: BaseItemKind[]
|
||||
) => {
|
||||
const currentApi = useApi();
|
||||
return useQuery({
|
||||
|
@ -362,3 +364,148 @@ export const useGetQueryFiltersLegacy = (
|
|||
enabled: !!parentId
|
||||
});
|
||||
};
|
||||
|
||||
const fetchGetItemsViewByType = async (
|
||||
currentApi: JellyfinApiContext,
|
||||
viewType: LibraryTab,
|
||||
parentId: ParentId,
|
||||
itemType: BaseItemKind[],
|
||||
libraryViewSettings: LibraryViewSettings,
|
||||
options?: AxiosRequestConfig
|
||||
) => {
|
||||
const { api, user } = currentApi;
|
||||
if (api && user?.Id) {
|
||||
let response;
|
||||
switch (viewType) {
|
||||
case LibraryTab.AlbumArtists: {
|
||||
response = await getArtistsApi(api).getAlbumArtists(
|
||||
{
|
||||
userId: user.Id,
|
||||
parentId: parentId ?? undefined,
|
||||
enableImageTypes: [libraryViewSettings.ImageType, ImageType.Backdrop],
|
||||
...getFieldsQuery(viewType, libraryViewSettings),
|
||||
...getFiltersQuery(viewType, libraryViewSettings),
|
||||
...getLimitQuery(),
|
||||
...getAlphaPickerQuery(libraryViewSettings),
|
||||
sortBy: [libraryViewSettings.SortBy],
|
||||
sortOrder: [libraryViewSettings.SortOrder],
|
||||
includeItemTypes: itemType,
|
||||
startIndex: libraryViewSettings.StartIndex
|
||||
},
|
||||
{
|
||||
signal: options?.signal
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case LibraryTab.Artists: {
|
||||
response = await getArtistsApi(api).getArtists(
|
||||
{
|
||||
userId: user.Id,
|
||||
parentId: parentId ?? undefined,
|
||||
enableImageTypes: [libraryViewSettings.ImageType, ImageType.Backdrop],
|
||||
...getFieldsQuery(viewType, libraryViewSettings),
|
||||
...getFiltersQuery(viewType, libraryViewSettings),
|
||||
...getLimitQuery(),
|
||||
...getAlphaPickerQuery(libraryViewSettings),
|
||||
sortBy: [libraryViewSettings.SortBy],
|
||||
sortOrder: [libraryViewSettings.SortOrder],
|
||||
includeItemTypes: itemType,
|
||||
startIndex: libraryViewSettings.StartIndex
|
||||
},
|
||||
{
|
||||
signal: options?.signal
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case LibraryTab.Networks:
|
||||
response = await getStudiosApi(api).getStudios(
|
||||
{
|
||||
userId: user.Id,
|
||||
parentId: parentId ?? undefined,
|
||||
...getFieldsQuery(viewType, libraryViewSettings),
|
||||
includeItemTypes: itemType,
|
||||
enableImageTypes: [ImageType.Thumb],
|
||||
startIndex: libraryViewSettings.StartIndex
|
||||
},
|
||||
{
|
||||
signal: options?.signal
|
||||
}
|
||||
);
|
||||
break;
|
||||
default: {
|
||||
response = await getItemsApi(api).getItems(
|
||||
{
|
||||
userId: user.Id,
|
||||
recursive: true,
|
||||
imageTypeLimit: 1,
|
||||
parentId: parentId ?? undefined,
|
||||
enableImageTypes: [libraryViewSettings.ImageType, ImageType.Backdrop],
|
||||
...getFieldsQuery(viewType, libraryViewSettings),
|
||||
...getFiltersQuery(viewType, libraryViewSettings),
|
||||
...getLimitQuery(),
|
||||
...getAlphaPickerQuery(libraryViewSettings),
|
||||
isFavorite: viewType === LibraryTab.Favorites ? true : undefined,
|
||||
sortBy: [libraryViewSettings.SortBy],
|
||||
sortOrder: [libraryViewSettings.SortOrder],
|
||||
includeItemTypes: itemType,
|
||||
startIndex: libraryViewSettings.StartIndex
|
||||
},
|
||||
{
|
||||
signal: options?.signal
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
|
||||
export const useGetItemsViewByType = (
|
||||
viewType: LibraryTab,
|
||||
parentId: ParentId,
|
||||
itemType: BaseItemKind[],
|
||||
libraryViewSettings: LibraryViewSettings
|
||||
) => {
|
||||
const currentApi = useApi();
|
||||
return useQuery({
|
||||
queryKey: [
|
||||
'ItemsViewByType',
|
||||
viewType,
|
||||
parentId,
|
||||
itemType,
|
||||
libraryViewSettings
|
||||
],
|
||||
queryFn: ({ signal }) =>
|
||||
fetchGetItemsViewByType(
|
||||
currentApi,
|
||||
viewType,
|
||||
parentId,
|
||||
itemType,
|
||||
libraryViewSettings,
|
||||
{ signal }
|
||||
),
|
||||
refetchOnWindowFocus: false,
|
||||
keepPreviousData : true,
|
||||
enabled:
|
||||
[
|
||||
LibraryTab.Movies,
|
||||
LibraryTab.Favorites,
|
||||
LibraryTab.Collections,
|
||||
LibraryTab.Trailers,
|
||||
LibraryTab.Series,
|
||||
LibraryTab.Episodes,
|
||||
LibraryTab.Networks,
|
||||
LibraryTab.Albums,
|
||||
LibraryTab.AlbumArtists,
|
||||
LibraryTab.Artists,
|
||||
LibraryTab.Playlists,
|
||||
LibraryTab.Songs,
|
||||
LibraryTab.Books,
|
||||
LibraryTab.Photos,
|
||||
LibraryTab.Videos
|
||||
].includes(viewType) && !!parentId
|
||||
});
|
||||
};
|
||||
|
|
|
@ -38,7 +38,7 @@ const _GAMEPAD_LEFT_THUMBSTICK_UP_KEY = 'GamepadLeftThumbStickUp';
|
|||
const _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY = 'GamepadLeftThumbStickDown';
|
||||
const _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY = 'GamepadLeftThumbStickLeft';
|
||||
const _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY = 'GamepadLeftThumbStickRight';
|
||||
const _GAMEPAD_A_KEYCODE = 0;
|
||||
const _GAMEPAD_A_KEYCODE = 13;
|
||||
const _GAMEPAD_B_KEYCODE = 27;
|
||||
const _GAMEPAD_DPAD_UP_KEYCODE = 38;
|
||||
const _GAMEPAD_DPAD_DOWN_KEYCODE = 40;
|
||||
|
|
|
@ -1732,7 +1732,7 @@
|
|||
"EnableAudioNormalizationHelp": "Audio normalisation will add a constant gain to keep the average at a desired level (-18dB).",
|
||||
"EnableAudioNormalization": "Audio Normalisation",
|
||||
"LabelEnableLUFSScan": "Enable LUFS scan",
|
||||
"LabelEnableLUFSScanHelp": "Enable LUFS scan for music (This will take longer and more resources).",
|
||||
"LabelEnableLUFSScanHelp": "Clients can normalise audio playback to get equal loudness across tracks. This will make library scans longer and take more resources.",
|
||||
"AllowCollectionManagement": "Allow this user to manage collections",
|
||||
"GetThePlugin": "Get the Plugin",
|
||||
"Notifications": "Notifications",
|
||||
|
@ -1773,5 +1773,8 @@
|
|||
"MachineTranslated": "Machine Translated",
|
||||
"ForeignPartsOnly": "Forced/Foreign parts only",
|
||||
"HearingImpairedShort": "HI/SDH",
|
||||
"HeaderGuestCast": "Guest Stars"
|
||||
"HeaderGuestCast": "Guest Stars",
|
||||
"LabelIsHearingImpaired": "For hearing impaired (SDH)",
|
||||
"BackdropScreensaver": "Backdrop Screensaver",
|
||||
"LogoScreensaver": "Logo Screensaver"
|
||||
}
|
||||
|
|
|
@ -1602,5 +1602,8 @@
|
|||
"IgnoreDtsHelp": "غیر فعال کردن این گزینه ممکن است برخی اشکالات را رفع کند، مثل نبودن صدا بر روی کانال هایی که جریان صدا و تصویر جداگانه دارند.",
|
||||
"LabelDummyChapterDurationHelp": "وقفه استخراج تصاویر فصل به ثانیه.",
|
||||
"HeaderDummyChapter": "تصاویر فصل",
|
||||
"EnableAudioNormalization": "معمول سازی صوت"
|
||||
"EnableAudioNormalization": "معمول سازی صوت",
|
||||
"AllowCollectionManagement": "به این کاربر اجازه مدیریت مجموعه را بده",
|
||||
"AllowSegmentDeletion": "تکه ها را پاک کن",
|
||||
"AllowSegmentDeletionHelp": "پاک کردن تکه های قدیمی را بعد از فرستادن به کلاینت. این کار از ذخیره کل فایل transcode شده بر روی هارد جلوگیری می کند. این تنها زمانی کار می کند که throttling فعال باشد. درصورت مشکل در هنگام پخش این ویژگی را غیرفعال کنید."
|
||||
}
|
||||
|
|
|
@ -1437,7 +1437,7 @@
|
|||
"QuickConnectNotActive": "Pikayhdistys ei ole tällä palvelimella käytössä",
|
||||
"QuickConnectNotAvailable": "Pyydä palvelimen ylläpitoa ottamaan Pikayhdistys käyttöön",
|
||||
"QuickConnectInvalidCode": "Virheellinen Pikayhdistyskoodi",
|
||||
"QuickConnectDescription": "Kirjautuaksesi Pikayhdistyksellä, valitse 'Pikayhdistys'-painike laitteelta, josta yrität kirjautua ja syötä alla oleva koodi.",
|
||||
"QuickConnectDescription": "Kirjautuaksesi Pikayhdistyksellä, valitse 'Pikayhdistys' laitteelta, josta yrität kirjautua ja syötä näytettävä koodi alle.",
|
||||
"QuickConnectDeactivated": "Pikayhdistys katkaistiin ennen kirjautumispyynnön hyväksyntää",
|
||||
"QuickConnectAuthorizeSuccess": "Laitteesi on todennettu!",
|
||||
"QuickConnectAuthorizeFail": "Tuntematon Pikayhdistyskoodi",
|
||||
|
|
|
@ -215,5 +215,11 @@
|
|||
"ValueSongCount": "{0} sangir",
|
||||
"ButtonForgotPassword": "Gloymt loyniorð",
|
||||
"ButtonSignIn": "Innrita",
|
||||
"ButtonSignOut": "Útrita"
|
||||
"ButtonSignOut": "Útrita",
|
||||
"Channels": "Rásir",
|
||||
"Favorites": "Yndis",
|
||||
"Folders": "Mappur",
|
||||
"Collections": "Søvn",
|
||||
"Default": "Sjálvgildi",
|
||||
"Genres": "Greinar"
|
||||
}
|
||||
|
|
|
@ -1202,5 +1202,13 @@
|
|||
"LabelKodiMetadataEnableExtraThumbsHelp": "תמונות שהורדו יכולות להישמר לשדות extrafanart ו-extrathumbs בו זמנית לצורך התאמה מירבית לסקינים של קודי.",
|
||||
"LabelKodiMetadataEnableExtraThumbs": "העתק extrafanart לשדה extrathumbs",
|
||||
"LabelGroupMoviesIntoCollectionsHelp": "כל הסרטים באוסף יופיעו בתור פריט מקובץ אחד ברשימות סרטים.",
|
||||
"LabelHomeScreenSectionValue": "איזור {0} בעמוד הבית"
|
||||
"LabelHomeScreenSectionValue": "איזור {0} בעמוד הבית",
|
||||
"LabelExtractChaptersDuringLibraryScan": "חלץ את תמונות הפרק תוך כדי סריקת הספריה",
|
||||
"LabelBackdropScreensaverInterval": "interval שומר המסך של הרקע",
|
||||
"LabelEnableLUFSScan": "הפעלת סריקת LUFS",
|
||||
"LabelEnableAutomaticPortMapHelp": "בצע port forward בצורה אוטומטית מפורטים בראוטר אל פורטים לוקאליים על השרת באמצעות UPnP. ייתכן שזה לא יעבוד עם דגמי נתב מסוימים או עם קונפיגורציות רשת מסוימות. שינויים לא יחולו עד לאחר הפעלה מחדש של השרת.",
|
||||
"BackdropScreensaver": "שומר המסך של הרקע",
|
||||
"LabelExtractChaptersDuringLibraryScanHelp": "צור תמונות פרקים כאשר סרטונים מיובאים תוך כדי סריקת הספריה. אחרת, הם יחולצו תוך כדי המשימה המתוזמנת העוסקת בתמונות הפרק, מה שיאפשר לסריקת הספריה הרגילה להסתיים מהר יותר.",
|
||||
"LabelThrottleDelaySecondsHelp": "כמות הזמן בשניות שלאחריה המקודד יגיע ל-throttle. חייב להיות גדול מספיק כדי שהלקוח ישמור על buffer גדול מספיק. עובד רק אם throttling מופעל.",
|
||||
"HeaderGuestCast": "כוכבים אורחים"
|
||||
}
|
||||
|
|
|
@ -1627,7 +1627,7 @@
|
|||
"AllowEmbeddedSubtitlesAllowImageOption": "Kép engedélyezése",
|
||||
"AllowEmbeddedSubtitlesAllowNoneOption": "Összes tiltása",
|
||||
"AllowEmbeddedSubtitlesAllowAllOption": "Összes engedélyezése",
|
||||
"AllowEmbeddedSubtitlesHelp": "Tiltsa le a médiatárolókba csomagolt feliratokat. Teljes könyvtárfrissítést igényel.",
|
||||
"AllowEmbeddedSubtitlesHelp": "Letiltja a médiatárolókba csomagolt feliratokat. Teljes könyvtárfrissítést igényel.",
|
||||
"AllowEmbeddedSubtitles": "Különféle típusú beágyazott feliratok letiltása",
|
||||
"ShowParentImages": "Sorozatképek megjelenítése",
|
||||
"NextUpRewatching": "Újranézés",
|
||||
|
@ -1767,5 +1767,6 @@
|
|||
"AllowAv1Encoding": "AV1 kódolás engedélyezése",
|
||||
"GridView": "Rács Nézet",
|
||||
"ListView": "Lista Nézet",
|
||||
"MachineTranslated": "Gépi fordítás"
|
||||
"MachineTranslated": "Gépi fordítás",
|
||||
"AiTranslated": "Gépi fordítás"
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"TabNotifications": "Tilkynningar",
|
||||
"WelcomeToProject": "Velkomin/n í Jellyfin!",
|
||||
"Anytime": "Hvenær sem er",
|
||||
"Genres": "Tegundir",
|
||||
"Genres": "Stefnur",
|
||||
"ButtonAddImage": "Bæta við ljósmynd",
|
||||
"ButtonAddServer": "Bæta við þjón",
|
||||
"ButtonAddUser": "Bæta við notenda",
|
||||
|
@ -39,7 +39,7 @@
|
|||
"PasswordMatchError": "Lykilorð og ítrekun lykilorðs þarf að passa.",
|
||||
"PasswordResetConfirmation": "Ertu viss um að þú viljir endursetja lykilorðið þitt?",
|
||||
"PinCodeResetConfirmation": "Ertu viss um að þú viljir endursetja PIN kóðann þinn?",
|
||||
"HeaderAlbumArtists": "Höfundur plötu",
|
||||
"HeaderAlbumArtists": "Listamaður á umslagi",
|
||||
"HeaderContinueWatching": "Halda áfram að horfa",
|
||||
"Favorites": "Uppáhalds",
|
||||
"Play": "Spila",
|
||||
|
@ -70,7 +70,7 @@
|
|||
"AroundTime": "Um {0}",
|
||||
"Art": "List",
|
||||
"AllComplexFormats": "Öll flókin form (ASS, SSA, VobSub, PGS, SUB, IDX, …)",
|
||||
"Artists": "Listamaður",
|
||||
"Artists": "Listamenn",
|
||||
"AsManyAsPossible": "Eins margir og mögulegt er",
|
||||
"Ascending": "Í vaxandi röð",
|
||||
"AspectRatio": "Skjáhlutfall",
|
||||
|
@ -89,7 +89,7 @@
|
|||
"Yes": "Já",
|
||||
"People": "Fólk",
|
||||
"PerfectMatch": "Passar fullkomlega",
|
||||
"Channels": "Stöðvar",
|
||||
"Channels": "Rásir",
|
||||
"Collections": "Söfn",
|
||||
"OptionUnairedEpisode": "Ófrumsýndir þættir",
|
||||
"OptionWeekdays": "Vikudagar",
|
||||
|
@ -121,7 +121,7 @@
|
|||
"PlayNext": "Spila næsta",
|
||||
"PlayNextEpisodeAutomatically": "Spila næsta þátt sjálfkrafa",
|
||||
"Played": "Spilað",
|
||||
"Photos": "Myndir",
|
||||
"Photos": "Ljósmyndir",
|
||||
"Movies": "Kvikmyndir",
|
||||
"HeaderProfileInformation": "Upplýsingar um prófíl",
|
||||
"HeaderPassword": "Lykilorð",
|
||||
|
@ -205,11 +205,11 @@
|
|||
"AlwaysPlaySubtitlesHelp": "Allir textar sem samsvara við túngumáli valið verða alltaf hlaðnir óháð hljóðmáls túngumáli.",
|
||||
"AllowedRemoteAddressesHelp": "Kommu aðskilinn listi yfir ip tölur eða ip-númeramát fyrir net sem mega fjartengjas. Ef þetta er autt eru allar fjartengingar leyfðar.",
|
||||
"AllowHWTranscodingHelp": "Leyfa viðtæki að umbreyta straumi í rauntíma.Þetta getur minnkað álag á þjón.",
|
||||
"ValueSpecialEpisodeName": "Sérstakt - {0}",
|
||||
"Shows": "Sýningar",
|
||||
"Playlists": "Spilunarlisti",
|
||||
"ValueSpecialEpisodeName": "Sérstaktur - {0}",
|
||||
"Shows": "Þættir",
|
||||
"Playlists": "Efnisskrár",
|
||||
"ButtonScanAllLibraries": "Skanna Öll Gagnasöfn",
|
||||
"AllLibraries": "Öll gagnasöfn",
|
||||
"AllLibraries": "Öll efnissöfn",
|
||||
"RefreshMetadata": "Endurhlaða lýsigögn",
|
||||
"Refresh": "Endurhlaða",
|
||||
"ReleaseDate": "Útgáfudagur",
|
||||
|
@ -472,11 +472,12 @@
|
|||
"EnablePlugin": "Virkja",
|
||||
"Bwdif": "BWDIF",
|
||||
"DisablePlugin": "Slökkva",
|
||||
"EnableAutoCast": "Setja sem Sjálfgefið",
|
||||
"EnableAutoCast": "Setja sem sjálfgefið",
|
||||
"EnableDetailsBanner": "Upplýsingar borði",
|
||||
"DeleteAll": "Eyða Öllu",
|
||||
"ButtonBackspace": "Backspace",
|
||||
"ButtonUseQuickConnect": "Nota hraðtengingu",
|
||||
"Digital": "Stafrænt",
|
||||
"DownloadAll": "Sækja allt"
|
||||
"DownloadAll": "Sækja allt",
|
||||
"AllowCollectionManagement": "Leyfa þessum notanda að sýsla með söfn"
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@
|
|||
"LabelFailed": "Neizdevās",
|
||||
"LabelEveryXMinutes": "Katru",
|
||||
"LabelEvent": "Notikums",
|
||||
"LabelEpisodeNumber": "Epizodes numurs",
|
||||
"LabelEpisodeNumber": "Sērijas numurs",
|
||||
"LabelEndDate": "Beigu datums",
|
||||
"LabelEnableHardwareDecodingFor": "Iespējot aparatūras atkodēšanu priekš",
|
||||
"LabelEnableDlnaServer": "Iespējot DLNA serveri",
|
||||
|
@ -224,12 +224,12 @@
|
|||
"Home": "Mājas",
|
||||
"HideWatchedContentFromLatestMedia": "Paslēpt skatītos vienumus no 'Jaunākās Multivides'",
|
||||
"Hide": "Paslēpt",
|
||||
"HeaderStopRecording": "Apturēt Ierakstu",
|
||||
"HeaderStatus": "Status",
|
||||
"HeaderStartNow": "Sākt Tagad",
|
||||
"HeaderSpecialEpisodeInfo": "Speciālās Epizodes Info",
|
||||
"HeaderSortOrder": "Kārtošanas Secība",
|
||||
"HeaderSortBy": "Kārtot Pēc",
|
||||
"HeaderStopRecording": "Apturēt ierakstu",
|
||||
"HeaderStatus": "Statuss",
|
||||
"HeaderStartNow": "Sākt tagad",
|
||||
"HeaderSpecialEpisodeInfo": "Speciālās sērijas info",
|
||||
"HeaderSortOrder": "Kārtošanas secība",
|
||||
"HeaderSortBy": "Kārtot pēc",
|
||||
"HeaderSetupLibrary": "Uzstādīt multvides bibliotēkas",
|
||||
"HeaderServerSettings": "Servera Iestatījumi",
|
||||
"HeaderSeriesStatus": "Sēriju Status",
|
||||
|
@ -245,16 +245,16 @@
|
|||
"HeaderRecentlyPlayed": "Nesen Atskaņots",
|
||||
"HeaderProfileInformation": "Profila Informācija",
|
||||
"HeaderPleaseSignIn": "Lūdzu ieej",
|
||||
"HeaderPlaybackError": "Atskaņošanas Kļūda",
|
||||
"HeaderPlayback": "Multvides Atskaņošanas",
|
||||
"HeaderPlayAll": "Atskaņot Visu",
|
||||
"HeaderPlaybackError": "Atskaņošanas kļūda",
|
||||
"HeaderPlayback": "Multvides atskaņošana",
|
||||
"HeaderPlayAll": "Atskaņot visu",
|
||||
"HeaderPinCodeReset": "Atiestatīt vieglo PIN Kodu",
|
||||
"HeaderPhotoAlbums": "Foto Albūmi",
|
||||
"HeaderPhotoAlbums": "Fotoalbumi",
|
||||
"HeaderPaths": "Ceļi",
|
||||
"HeaderPasswordReset": "Paroles Nomaiņa",
|
||||
"HeaderPasswordReset": "Paroles atiestatīšana",
|
||||
"HeaderPassword": "Parole",
|
||||
"HeaderNextVideoPlayingInValue": "Nākamais Video tiks atskaņots pēc {0}",
|
||||
"HeaderNextEpisodePlayingInValue": "Nākamā Epizode tiks atskaņota pēc: {0}",
|
||||
"HeaderNextVideoPlayingInValue": "Nākošais video sāksies pēc {0}",
|
||||
"HeaderNextEpisodePlayingInValue": "Nākošā sērija sāksies pēc: {0}",
|
||||
"HeaderNewDevices": "Jaunas Ierīces",
|
||||
"HeaderNewApiKey": "Jauna API Atslēga",
|
||||
"HeaderNavigation": "Navigācija",
|
||||
|
@ -262,7 +262,7 @@
|
|||
"HeaderMyMedia": "Mana Multvide",
|
||||
"HeaderMyDevice": "Mana Ierīce",
|
||||
"HeaderMusicQuality": "Audio Kvalitāte",
|
||||
"HeaderMoreLikeThis": "Vairāk Kā Šis",
|
||||
"HeaderMoreLikeThis": "Līdzīgs saturs",
|
||||
"HeaderMetadataSettings": "Metadatu Iestatījumi",
|
||||
"HeaderMediaFolders": "Multvides Mapes",
|
||||
"HeaderMedia": "Multvide",
|
||||
|
@ -276,7 +276,7 @@
|
|||
"HeaderLatestMusic": "Jaunākā Mūzika",
|
||||
"HeaderLatestMovies": "Jaunākās Filmas",
|
||||
"HeaderLatestMedia": "Jaunākā Multvide",
|
||||
"HeaderLatestEpisodes": "Jaunākās Epizodes",
|
||||
"HeaderLatestEpisodes": "Jaunākās sērijas",
|
||||
"HeaderKeepRecording": "Turpināt Ierakstu",
|
||||
"HeaderInstall": "Uzstādīt",
|
||||
"HeaderImageSettings": "Attēlu Iestatījumi",
|
||||
|
@ -323,33 +323,33 @@
|
|||
"HeaderDeveloperInfo": "Izstrādātāju Info",
|
||||
"HeaderDeleteItems": "Noņemt Vienumus",
|
||||
"HeaderDeleteItem": "Noņemt Vienumu",
|
||||
"HeaderDeleteDevice": "Noņemt Ierīci",
|
||||
"HeaderContinueWatching": "Turpināt Skatīšanos",
|
||||
"HeaderContinueListening": "Turpināt Klausīšanos",
|
||||
"HeaderContainerProfile": "Konteinera Profils",
|
||||
"HeaderConnectionFailure": "Savienojuma Kļūda",
|
||||
"HeaderConnectToServer": "Pievienoties pie Servera",
|
||||
"HeaderDeleteDevice": "Noņemt ierīci",
|
||||
"HeaderContinueWatching": "Turpināt skatīšanos",
|
||||
"HeaderContinueListening": "Turpināt klausīšanos",
|
||||
"HeaderContainerProfile": "Konteinera profils",
|
||||
"HeaderConnectionFailure": "Savienojuma kļūda",
|
||||
"HeaderConnectToServer": "Pievienoties pie servera",
|
||||
"HeaderConfirmRevokeApiKey": "Atsaukt API Atslēgu",
|
||||
"HeaderConfirmPluginInstallation": "Apstiprināt Palašinājumu Uzstādījumu",
|
||||
"HeaderConfigureRemoteAccess": "Uzstādīt Attālināto Piekļuvi",
|
||||
"HeaderCodecProfile": "Kodeksu Profils",
|
||||
"HeaderChannelAccess": "Kanālu Piekļuve",
|
||||
"HeaderCancelSeries": "Atcelt Sēriju",
|
||||
"HeaderCancelRecording": "Atcelt Ierakstus",
|
||||
"HeaderAudioSettings": "Audio Iestatījumi",
|
||||
"HeaderAudioBooks": "Audio Grāmatas",
|
||||
"HeaderConfirmPluginInstallation": "Apstiprināt palašinājuma uzstādīšanu",
|
||||
"HeaderConfigureRemoteAccess": "Uzstādīt attālināto piekļuvi",
|
||||
"HeaderCodecProfile": "Kodeku profils",
|
||||
"HeaderChannelAccess": "Piekļuve kanāliem",
|
||||
"HeaderCancelSeries": "Atcelt sēriju",
|
||||
"HeaderCancelRecording": "Atcelt ierakstus",
|
||||
"HeaderAudioSettings": "Audio iestatījumi",
|
||||
"HeaderAudioBooks": "Audio grāmatas",
|
||||
"HeaderApp": "Lietotne",
|
||||
"HeaderApiKeys": "API Atslēgas",
|
||||
"HeaderApiKey": "API Atslēga",
|
||||
"HeaderAllowMediaDeletionFrom": "Atļaut Multvides Dzēšanu no",
|
||||
"HeaderApiKeys": "API atslēgas",
|
||||
"HeaderApiKey": "API atslēga",
|
||||
"HeaderAllowMediaDeletionFrom": "Atļaut multvides dzēšanu no",
|
||||
"HeaderAlert": "Paziņojums",
|
||||
"HeaderAlbumArtists": "Albumu Izpildītāji",
|
||||
"HeaderAlbumArtists": "Albumu izpildītāji",
|
||||
"HeaderAdmin": "Administrācija",
|
||||
"HeaderAddToPlaylist": "Pievienot Atskaņošanas Sarakstam",
|
||||
"HeaderAddToPlaylist": "Pievienot atskaņošanas sarakstam",
|
||||
"HeaderAddToCollection": "Pievienot kolekcijai",
|
||||
"HeaderActivity": "Aktivitāte",
|
||||
"HeaderActiveRecordings": "Aktīvie Ieraksti",
|
||||
"HeaderActiveDevices": "Aktīvās Ierīces",
|
||||
"HeaderActiveRecordings": "Aktīvie ieraksti",
|
||||
"HeaderActiveDevices": "Aktīvās ierīces",
|
||||
"HDPrograms": "HD programmas",
|
||||
"Guide": "Gids",
|
||||
"GuestStar": "Vieszvaigzne",
|
||||
|
@ -367,11 +367,11 @@
|
|||
"FileReadCancelled": "Datnes lasīšana tika atcelta.",
|
||||
"FileNotFound": "Datne nav atrasta.",
|
||||
"File": "Datne",
|
||||
"Favorites": "Favorīti",
|
||||
"Favorites": "Izlase",
|
||||
"Favorite": "Favorīts",
|
||||
"ExitFullscreen": "Iziet no pilnekrāna",
|
||||
"EveryNDays": "Ik pa {0} dienām",
|
||||
"Episodes": "Epizodes",
|
||||
"Episodes": "Sērijas",
|
||||
"EndsAtValue": "Beigsies {0}",
|
||||
"EnableThemeVideosHelp": "Atskaņot tēmas video fonā bibliotēkas pārlūkošanas laikā.",
|
||||
"EnableThemeSongsHelp": "Atskaņot tēmas mūziku fonā bibliotēkas pārlūkošanas laikā.",
|
||||
|
@ -396,20 +396,20 @@
|
|||
"DisplayInMyMedia": "Rādīt mājas ekrānā",
|
||||
"Display": "Displejs",
|
||||
"Disc": "Disks",
|
||||
"Directors": "Direktori",
|
||||
"Director": "Direktors",
|
||||
"Directors": "Režisori",
|
||||
"Director": "Režisors",
|
||||
"DirectStreaming": "Tiešā straumēšana",
|
||||
"DirectPlaying": "Tiešā Atskaņošana",
|
||||
"DetectingDevices": "Meklē Ierīces",
|
||||
"DirectPlaying": "Tiešā atskaņošana",
|
||||
"DetectingDevices": "Meklē ierīces",
|
||||
"Desktop": "Darbvirsma",
|
||||
"DeleteImage": "Dzēst Attēlu",
|
||||
"DeleteImage": "Dzēst attēlu",
|
||||
"Delete": "Izdzēst",
|
||||
"Default": "Noklusējuma",
|
||||
"DatePlayed": "Atskaņošanas datums",
|
||||
"DateAdded": "Pievienošanas datums",
|
||||
"CriticRating": "Kritiķu reitings",
|
||||
"CopyStreamURLSuccess": "URL veiksmīgi nokopēts.",
|
||||
"CopyStreamURL": "Kopēt Straumes URL",
|
||||
"CopyStreamURL": "Kopēt straumes URL",
|
||||
"Continuing": "Turpina",
|
||||
"ContinueWatching": "Turpināt skatīties",
|
||||
"Connect": "Savienot",
|
||||
|
@ -423,7 +423,7 @@
|
|||
"Categories": "Kategorijas",
|
||||
"CancelSeries": "Atcelt sēriju",
|
||||
"CancelRecording": "Atcelt ierakstu",
|
||||
"ButtonWebsite": "tīmekļa vietne",
|
||||
"ButtonWebsite": "Tīmekļa vietne",
|
||||
"ButtonUninstall": "Atinstalēt",
|
||||
"ButtonTrailer": "Treileri",
|
||||
"ButtonSplit": "Sadalīt",
|
||||
|
@ -433,15 +433,15 @@
|
|||
"ButtonSignIn": "Ieiet",
|
||||
"ButtonShutdown": "Izslēgt",
|
||||
"ButtonSend": "Nosūtīt",
|
||||
"ButtonSelectView": "Izvēlies Skatu",
|
||||
"ButtonSelectDirectory": "Izvēlies Mapi",
|
||||
"ButtonScanAllLibraries": "Skenēt Visas Bibliotēkas",
|
||||
"ButtonSelectView": "Izvēlēties skatu",
|
||||
"ButtonSelectDirectory": "Izvēlēties mapi",
|
||||
"ButtonScanAllLibraries": "Skenēt visas bibliotēkas",
|
||||
"ButtonRevoke": "Atsaukt",
|
||||
"ButtonResume": "Turpināt",
|
||||
"ButtonResetEasyPassword": "Nomainīt vieglo PIN kodu",
|
||||
"ButtonRename": "Pārsaukt",
|
||||
"ButtonRemove": "Noņemt",
|
||||
"ButtonRefreshGuideData": "Atjaunot Ceļveža Datus",
|
||||
"ButtonRefreshGuideData": "Atjaunot ceļveža datus",
|
||||
"ButtonPreviousTrack": "Iepriekšējais celiņš",
|
||||
"ButtonPause": "Pauzēt",
|
||||
"ButtonParentalControl": "Vecāku pārvaldība",
|
||||
|
@ -454,14 +454,14 @@
|
|||
"ButtonInfo": "Informācija",
|
||||
"ButtonGotIt": "Sapratu",
|
||||
"ButtonFullscreen": "Pilnekrāns",
|
||||
"ButtonForgotPassword": "Aizmirsu Paroli",
|
||||
"ButtonChangeServer": "Nomainīt Serveri",
|
||||
"ButtonForgotPassword": "Aizmirsu paroli",
|
||||
"ButtonChangeServer": "Nomainīt serveri",
|
||||
"ButtonCancel": "Atcelt",
|
||||
"ButtonBack": "Atpakaļ",
|
||||
"ButtonAudioTracks": "Audio Celiņi",
|
||||
"ButtonAddUser": "Pievienot Lietotāju",
|
||||
"ButtonAddServer": "Pievienot Serveri",
|
||||
"ButtonAddMediaLibrary": "Pievienot Multimēdiju Bibliotēku",
|
||||
"ButtonAudioTracks": "Audio celiņi",
|
||||
"ButtonAddUser": "Pievienot lietotāju",
|
||||
"ButtonAddServer": "Pievienot serveri",
|
||||
"ButtonAddMediaLibrary": "Pievienot multimēdiju bibliotēku",
|
||||
"ButtonAddImage": "Pievienot attēlu",
|
||||
"MessageBrowsePluginCatalog": "Pārlūko mūsu paplašinājumu katalogu, lai redzētu pieejamos paplašinājumus.",
|
||||
"Browse": "Pārlūkot",
|
||||
|
@ -470,17 +470,17 @@
|
|||
"Books": "Grāmatas",
|
||||
"Help": "Palīdzība",
|
||||
"HeaderYears": "Gadi",
|
||||
"HeaderXmlSettings": "XML Iestatījumi",
|
||||
"HeaderXmlDocumentAttribute": "XML Dokumenta Atribūts",
|
||||
"HeaderXmlDocumentAttributes": "XML Dokumenta Atribūti",
|
||||
"HeaderXmlSettings": "XML iestatījumi",
|
||||
"HeaderXmlDocumentAttribute": "XML dokumenta atribūts",
|
||||
"HeaderXmlDocumentAttributes": "XML dokumenta atribūti",
|
||||
"HeaderVideos": "Video",
|
||||
"HeaderVideoTypes": "Video Veidi",
|
||||
"HeaderVideoType": "Video Veids",
|
||||
"HeaderVideoQuality": "Video Kvalitāte",
|
||||
"HeaderVideoTypes": "Video veidi",
|
||||
"HeaderVideoType": "Video veids",
|
||||
"HeaderVideoQuality": "Video kvalitāte",
|
||||
"HeaderUsers": "Lietotāji",
|
||||
"HeaderUser": "Lietotājs",
|
||||
"HeaderUploadImage": "Augšupielādēt Attēlu",
|
||||
"HeaderUpcomingOnTV": "Nākamais Televīzijā",
|
||||
"HeaderUploadImage": "Augšupielādēt attēlu",
|
||||
"HeaderUpcomingOnTV": "Nākamais televīzijā",
|
||||
"HeaderTypeText": "Ievadīt Tekstu",
|
||||
"HeaderTypeImageFetchers": "Attēlu Sagādnieki ({0})",
|
||||
"HeaderTuners": "Tūneri",
|
||||
|
@ -489,10 +489,10 @@
|
|||
"HeaderTracks": "Celiņi",
|
||||
"HeaderThisUserIsCurrentlyDisabled": "Šis lietotājs pašlaik ir atspējots",
|
||||
"HeaderSystemDlnaProfiles": "Sistēmas Profili",
|
||||
"HeaderSubtitleProfiles": "Subtitru Profili",
|
||||
"HeaderSubtitleProfile": "Subtitru Profils",
|
||||
"HeaderSubtitleDownloads": "Subtitru Lejupielāde",
|
||||
"HeaderSubtitleAppearance": "Subtitru Izskats",
|
||||
"HeaderSubtitleProfiles": "Subtitru profili",
|
||||
"HeaderSubtitleProfile": "Subtitru profils",
|
||||
"HeaderSubtitleDownloads": "Subtitru lejupielāde",
|
||||
"HeaderSubtitleAppearance": "Subtitru izskats",
|
||||
"BirthPlaceValue": "Dzimšanas vieta: {0}",
|
||||
"BirthLocation": "Dzimšanas vieta",
|
||||
"BirthDateValue": "Dzimis: {0}",
|
||||
|
@ -506,8 +506,8 @@
|
|||
"Art": "Māksla",
|
||||
"AroundTime": "Aptuveni {0}",
|
||||
"Anytime": "Jebkad",
|
||||
"AnyLanguage": "Jebkura Valoda",
|
||||
"AlwaysPlaySubtitles": "Vienmēr Atskaņot",
|
||||
"AnyLanguage": "Jebkura valoda",
|
||||
"AlwaysPlaySubtitles": "Vienmēr atskaņot",
|
||||
"AllowedRemoteAddressesHelp": "Ar komatiem atdalīts IP adrešu vai IP/tīkla masku saraksts, kas norāda uz tīkliem, kas var pieslēgties attālināti. Ja atstāts tukšs, visas attālinātās adreses tiks atļautas.",
|
||||
"AllowRemoteAccessHelp": "Ja nav atzīmēts, visi attālinātie savienojumi tiks bloķēti.",
|
||||
"AllowRemoteAccess": "Atļaut attālinātus savienojumus ar šo serveri",
|
||||
|
@ -515,8 +515,8 @@
|
|||
"AllowMediaConversion": "Atļaut multivides pārveidošanu",
|
||||
"AllLibraries": "Visas bibliotēkas",
|
||||
"AllLanguages": "Visas valodas",
|
||||
"AllEpisodes": "Visas epizodes",
|
||||
"AllComplexFormats": "Visi Sarežģītie Formāti (ASS, SSA, VobSub, PGS, SUB, IDX, …)",
|
||||
"AllEpisodes": "Visas sērijas",
|
||||
"AllComplexFormats": "Visi sarežģītie formāti (ASS, SSA, VobSub, PGS, SUB, IDX, …)",
|
||||
"AllChannels": "Visi kanāli",
|
||||
"All": "Viss/i",
|
||||
"Alerts": "Paziņojumi",
|
||||
|
@ -546,8 +546,8 @@
|
|||
"No": "Nr",
|
||||
"Next": "Nākamais",
|
||||
"News": "Ziņas",
|
||||
"NewEpisodesOnly": "Tikai jaunas epizodes",
|
||||
"NewEpisodes": "Jaunas epizodes",
|
||||
"NewEpisodesOnly": "Tikai jaunas sērijas",
|
||||
"NewEpisodes": "Jaunas sērijas",
|
||||
"NewCollectionNameExample": "Piemēram: Zvaigžņu Karu Kolekcija",
|
||||
"AllowMediaConversionHelp": "Atļauj vai aizliedz piekļuvi pie multivides pārveidošanas funkcijas.",
|
||||
"Aired": "Raidīts",
|
||||
|
@ -558,7 +558,7 @@
|
|||
"XmlTvNewsCategoriesHelp": "Raidījumi ar šīm kategorijām tiks rādīti kā ziņu raidījumi. Atdali vairākus ar '|'.",
|
||||
"XmlTvMovieCategoriesHelp": "Raidījumi ar šīm kategorijām tiks rādīti kā filmas. Atdali vairākus ar '|'.",
|
||||
"XmlTvKidsCategoriesHelp": "Raidījumi ar šīm kategorijām tiks rādīti kā raidījumi, kas paredzētas bērniem. Atdali vairākus ar '|'.",
|
||||
"Writer": "Rakstnieks",
|
||||
"Writer": "Scenārists",
|
||||
"WelcomeToProject": "Esiet sveicināts Jellyfin!",
|
||||
"Wednesday": "Trešdiena",
|
||||
"Watched": "Skatīts",
|
||||
|
@ -575,16 +575,16 @@
|
|||
"ValueOneSeries": "1 sērija",
|
||||
"ValueOneMusicVideo": "1 mūzikas video",
|
||||
"ValueOneMovie": "1 filma",
|
||||
"ValueOneEpisode": "1 epizode",
|
||||
"ValueOneEpisode": "1 sērija",
|
||||
"ValueOneAlbum": "1 albums",
|
||||
"ValueMusicVideoCount": "{0} mūzikas video",
|
||||
"ValueMovieCount": "{0} filmas",
|
||||
"ValueMinutes": "{0} min",
|
||||
"ValueEpisodeCount": "{0} epizodes",
|
||||
"ValueEpisodeCount": "{0} sērijas",
|
||||
"ValueDiscNumber": "Disks {0}",
|
||||
"ValueContainer": "Konteiners: {0}",
|
||||
"ValueCodec": "Kodeks: {0}",
|
||||
"ValueAudioCodec": "Audio Kodeks: {0}",
|
||||
"ValueAudioCodec": "Audio kodeks: {0}",
|
||||
"ValueAlbumCount": "{0} albumi",
|
||||
"UserProfilesIntro": "Jellyfin satur atbalstu lietotāju profiliem ar granulāru kontroli pār displeja iestatījumiem, atskaņošanas statusu un vecāku pārvaldi.",
|
||||
"Upload": "Augšupielādēt",
|
||||
|
@ -605,35 +605,35 @@
|
|||
"ThemeSongs": "Tēmas mūzika",
|
||||
"TellUsAboutYourself": "Pastāsti mums par sevi",
|
||||
"TagsValue": "Tagi: {0}",
|
||||
"Tags": "Tagi",
|
||||
"Tags": "Birkas",
|
||||
"TabStreaming": "Straumēšana",
|
||||
"TabServer": "Serveris",
|
||||
"TabResponses": "Atbildes",
|
||||
"TabProfiles": "Profili",
|
||||
"TabPlugins": "Paplašinājumi",
|
||||
"TabParentalControl": "Vecāku Pārvaldība",
|
||||
"TabParentalControl": "Vecāku pārvaldība",
|
||||
"TabOther": "Cits",
|
||||
"TabNotifications": "Paziņojumi",
|
||||
"TabNfoSettings": "NFO Iestatījumi",
|
||||
"TabNfoSettings": "NFO iestatījumi",
|
||||
"TabNetworking": "Tīklošana",
|
||||
"TabNetworks": "TV Tīkli",
|
||||
"TabMyPlugins": "Mani Paplašinājumi",
|
||||
"TabNetworks": "TV tīkli",
|
||||
"TabMyPlugins": "Mani paplašinājumi",
|
||||
"TabMusic": "Mūzika",
|
||||
"TabLogs": "Žurnāli",
|
||||
"TabLatest": "Jaunākais",
|
||||
"TabDirectPlay": "Tiešā Atskaņošana",
|
||||
"TabDirectPlay": "Tiešā atskaņošana",
|
||||
"TabContainers": "Konteineri",
|
||||
"TabCodecs": "Kodeksi",
|
||||
"TabCodecs": "Kodeki",
|
||||
"TabCatalog": "Katalogs",
|
||||
"ValueSpecialEpisodeName": "Speciālais - {0}",
|
||||
"Sync": "Sinhronizācija",
|
||||
"Songs": "Dziesmas",
|
||||
"Shows": "Raidījumi",
|
||||
"Playlists": "Atskaņošanas Saraksti",
|
||||
"Playlists": "Atskaņošanas saraksti",
|
||||
"Photos": "Attēli",
|
||||
"RepeatOne": "Atkārtot vienu",
|
||||
"RepeatMode": "Atkārtošanas Režīms",
|
||||
"RepeatEpisodes": "Atkārtot epizodes",
|
||||
"RepeatMode": "Atkārtošanas režīms",
|
||||
"RepeatEpisodes": "Atkārtot sērijas",
|
||||
"RepeatAll": "Atkārtot visu",
|
||||
"Repeat": "Atkārtot",
|
||||
"RemoveFromPlaylist": "Noņemt no atskaņošanas sarakasta",
|
||||
|
@ -760,10 +760,10 @@
|
|||
"HeaderRecordingPostProcessing": "Ierakstu Pēcapstrāde",
|
||||
"HeaderProfileServerSettingsHelp": "Šīs vērtības kontrolē, kā serveris sevi rādīs klientiem.",
|
||||
"HeaderPreferredMetadataLanguage": "Ieteicamā Metadatu Valoda",
|
||||
"HeaderPluginInstallation": "Paplašinājuma Instalācija",
|
||||
"HeaderPlayOn": "Atskaņot Uz",
|
||||
"HeaderParentalRatings": "Vecāku Vērtējumi",
|
||||
"HeaderOtherItems": "Citi Vienumi",
|
||||
"HeaderPluginInstallation": "Paplašinājuma instalācija",
|
||||
"HeaderPlayOn": "Atskaņot uz",
|
||||
"HeaderParentalRatings": "Vecāku vērtējumi",
|
||||
"HeaderOtherItems": "Citi vienumi",
|
||||
"HeaderOnNow": "Tagad",
|
||||
"HeaderLoginFailure": "Ieiešanas Kļūda",
|
||||
"HeaderKodiMetadataHelp": "Lai iespējotu vai atspējotu NFO metadatus, rediģē bibliotēku un atrodi sadaļu 'Metadatu glabātājs'.",
|
||||
|
@ -778,16 +778,16 @@
|
|||
"HeaderDetectMyDevices": "Noteikt Manas Ierīces",
|
||||
"HeaderDeleteTaskTrigger": "Dzēst Uzdevuma Trigeri",
|
||||
"HeaderDeleteProvider": "Dzēst Sniedzēju",
|
||||
"HeaderDefaultRecordingSettings": "Noklusējuma Ierakstīšanas Iestatījumi",
|
||||
"HeaderDateIssued": "Izdošanas Datums",
|
||||
"HeaderCustomDlnaProfiles": "Pielāgoti Profili",
|
||||
"HeaderConfirmProfileDeletion": "Apstiprināt Profila Dzēšanu",
|
||||
"HeaderChapterImages": "Nodaļu Attēli",
|
||||
"HeaderCastAndCrew": "Lomas un Apkalpe",
|
||||
"HeaderDefaultRecordingSettings": "Noklusējuma ieraksta iestatījumi",
|
||||
"HeaderDateIssued": "Izdošanas datums",
|
||||
"HeaderCustomDlnaProfiles": "Pielāgoti profili",
|
||||
"HeaderConfirmProfileDeletion": "Apstiprināt profila dzēšanu",
|
||||
"HeaderChapterImages": "Nodaļu attēli",
|
||||
"HeaderCastAndCrew": "Aktieri un komanda",
|
||||
"HeaderAppearsOn": "Redzams",
|
||||
"FFmpegSavePathNotFound": "Mēs nespējām atrast FFmpeg norādītajā ceļā. FFprobe arī ir vajadzīgs, un tam ir jāatrodas tajā pašā mapē. Šīs komponentes parasti tiek apvienotas vienā un tajā pašā lejupielādē. Lūdzu pārbaudiet ceļu un mēģiniet vēlreiz.",
|
||||
"HeaderAdditionalParts": "Papildus Ceļi",
|
||||
"HeaderAddUpdateImage": "Pievienot/Atjaunot Attēlu",
|
||||
"HeaderAdditionalParts": "Papildus daļas",
|
||||
"HeaderAddUpdateImage": "Pievienot/atjaunot attēlu",
|
||||
"GuideProviderLogin": "Ieiet",
|
||||
"Ended": "Beidzies",
|
||||
"EnableStreamLoopingHelp": "Iespējo šo, ja tiešsaistes straume satur tikai pāris sekunžu datus, un ir nepārtraukti jāatjauno. Iespējojot bez vajadzības var radīties problēmas.",
|
||||
|
@ -796,15 +796,15 @@
|
|||
"EnableExternalVideoPlayersHelp": "Ārēja atskaņotāja izvēlne tiks parādīta, kad tiks sākta video atskaņošana.",
|
||||
"EnableColorCodedBackgrounds": "Krāsu kodēti foni",
|
||||
"EnableBackdropsHelp": "Attēlot fona attēlus dažu lapu fonā bibliotēkas pārlūkošanas laikā.",
|
||||
"DropShadow": "Fona Ēnojums",
|
||||
"DisplayMissingEpisodesWithinSeasons": "Rādīt trūkstošās epizodes sezonās",
|
||||
"DropShadow": "Fona ēnojums",
|
||||
"DisplayMissingEpisodesWithinSeasons": "Rādīt trūkstošās sērijas sezonās",
|
||||
"Disconnect": "Atvienot",
|
||||
"DirectStreamHelp2": "Tiešās straumēšanas patērētā jauda ir pārsvarā atkarīga no audio profila. Tikai video straume ir bezzuduma.",
|
||||
"DirectStreamHelp1": "Video avots ir saderīgs ar šo ierīci, bet tam ir nesaderīgs audio formāts (DTS, Dolby TrueHD utt.) vai audio kanālu skaits. Video tiks pārpakots bez zudumiem pirms tas tiks pārsūtīts uz ierīci. Tikai audio tiks pārkodēts.",
|
||||
"Descending": "Dilstošs",
|
||||
"Depressed": "Atspiests",
|
||||
"DeleteUserConfirmation": "Vai tu tiešām vēlies izdzēst šo lietotāju?",
|
||||
"DeleteUser": "Dzēst Lietotāju",
|
||||
"DeleteUser": "Dzēst lietotāju",
|
||||
"DeleteMedia": "Dzēst mediju",
|
||||
"DeleteImageConfirmation": "Vai tu tiešām vēlies izdzēst šo attēlu?",
|
||||
"DeleteDeviceConfirmation": "Vai tu tiešām vēlies noņemt šo ierīci? Tā parādīsies atkārtoti nākamo reizi, kad lietotājs ieiet ar to.",
|
||||
|
@ -821,20 +821,20 @@
|
|||
"ChannelAccessHelp": "Izvēlies kanālus, ko koplietot ar šo lietotāju. Administratori spēs rediģēt visus kanālus izmantojot metadatu pārvaldnieku.",
|
||||
"ChangingMetadataImageSettingsNewContent": "Izmaiņas metadatu vai mākslas lejupielādes iestatījumos tiks izmantotas tikai jauniem bibliotēkas vienumiem. Lai pielietotu šīs izmaiņas jau esošiem vienumiem, tev vajadzēs atjaunot šo vienumu metadatus manuāli.",
|
||||
"ButtonSubmit": "Iesniegt",
|
||||
"ButtonQuickStartGuide": "Ātrā Lietošanas Instrukcija",
|
||||
"ButtonManualLogin": "Manuālā Ieiešana",
|
||||
"ButtonQuickStartGuide": "Ātrā lietošanas instrukcija",
|
||||
"ButtonManualLogin": "Manuālā pieteikšanās",
|
||||
"ButtonEditOtherUserPreferences": "Rediģē šī lietotāja profilu, attēlu un personas iestatījumus.",
|
||||
"ButtonArrowRight": "Pa labi",
|
||||
"ButtonArrowLeft": "Pa kreisi",
|
||||
"ButtonAddScheduledTaskTrigger": "Pievienot Palaidēju",
|
||||
"ButtonAddScheduledTaskTrigger": "Pievienot palaidēju",
|
||||
"BookLibraryHelp": "Audio un teksta grāmatas tiek atbalstītas. Pārskati {0} grāmatu nosaukšanas instrukciju {1}.",
|
||||
"Blacklist": "Melnais saraksts",
|
||||
"AuthProviderHelp": "Izvēlies autentifikācijas nodrošinājumu, kas tiks izmantots lai autentificētu šī lietotāja paroli.",
|
||||
"AspectRatio": "Attēla Proporcijas",
|
||||
"AspectRatio": "Attēla proporcijas",
|
||||
"AskAdminToCreateLibrary": "Vaicājiet administratoram, lai izveidotu bibliotēku.",
|
||||
"Ascending": "Augoši",
|
||||
"AlwaysPlaySubtitlesHelp": "Valodas priekšrokai atbilstošie subtitri tiks ielādēti neatkarīgi no audio valodas.",
|
||||
"AllowFfmpegThrottling": "Ierobežot Transkodējumus",
|
||||
"AllowFfmpegThrottling": "Ierobežot transkodējumus",
|
||||
"AllowHWTranscodingHelp": "Atļaut uztvērējam transkodēt straumes tiešsaistē. Tas var atvieglot transkodēšanu, kas jāveic serverim.",
|
||||
"AirDate": "Tiešraides datums",
|
||||
"LabelHardwareAccelerationTypeHelp": "Aparatūras paātrināšanai ir vajadzīga papildus konfigurācija.",
|
||||
|
@ -846,8 +846,8 @@
|
|||
"HeaderBlockItemsWithNoRating": "Bloķēt vienumus, kam nav vai nav atpazīta reitinga informācija",
|
||||
"HeaderApiKeysHelp": "Ārējām lietotnēm ir vajadzīgas API atslēgas, lai sazinātos ar serveri. Atslēgas tiek izdotas, ieejot lietotāja kontā vai manuāli ģenerējot lietotnei atslēgu.",
|
||||
"HeaderAccessScheduleHelp": "Izveido grafiku, lai ierobežotu piekļuvi noteiktās stundās.",
|
||||
"HeaderAccessSchedule": "Piekļuves Grafiks",
|
||||
"ExtraLarge": "Ļoti Liels",
|
||||
"HeaderAccessSchedule": "Piekļuves grafiks",
|
||||
"ExtraLarge": "Ļoti liels",
|
||||
"ErrorPleaseSelectLineup": "Lūdzu izvēlies sarakstu un mēģini vēlreiz. Ja nav pieejams neviens saraksts, pārliecinies ka tavs lietotājvārds, parole un pasta kods ir pareizi.",
|
||||
"ErrorGettingTvLineups": "Notika kļūda lejupielādējot TV sarakstus. Lūdzu pārliecinies, ka tava informācija ir pareiza un mēģini vēlreiz.",
|
||||
"DisplayMissingEpisodesWithinSeasonsHelp": "Tam arī jābūt iespējotam priekš TV bibliotēkām servera konfigurācijā.",
|
||||
|
@ -929,7 +929,7 @@
|
|||
"LabelAllowedRemoteAddresses": "Attālās IP adreses filtrs",
|
||||
"LabelAlbumArtPN": "Albumu vāku PN",
|
||||
"LabelAirsBeforeSeason": "Tiešraidē pirms sezonas",
|
||||
"LabelAirsBeforeEpisode": "Tiešraidē pirms epizodes",
|
||||
"LabelAirsBeforeEpisode": "Tiešraidē pirms sērijas",
|
||||
"LabelAirsAfterSeason": "Tiešraidē pēc sezonas",
|
||||
"HeaderSubtitleProfilesHelp": "Subtitru profili apraksta ierīces atbalstītos subtitru formātus.",
|
||||
"HeaderKeepSeries": "Paturēt Sēriju",
|
||||
|
@ -939,23 +939,23 @@
|
|||
"ErrorDeletingItem": "Notika kļūda dzēšot vienumu no servera. Lūdzu pārliecinies vai Jellyfin ir rakstoša piekļuve pie satura mapes un mēģini vēlreiz.",
|
||||
"ErrorAddingTunerDevice": "Kļūda pievienojot tūnera ierīci. Lūdzu pārliecinies ka tā ir pieejama un mēģini vēlreiz.",
|
||||
"ErrorAddingMediaPathToVirtualFolder": "Notika kļūda pievienojot satura ceļu. Lūdzu pārliecinies ka ceļš ir derīgs un ka Jellyfin Servera procesam ir piekļuve tai vietai.",
|
||||
"Episode": "Epizode",
|
||||
"Episode": "Sērija",
|
||||
"DeviceAccessHelp": "Tas attiecas tikai uz ierīcēm, kas var tikt unikāli identificētas un neaizliegs piekļuvi no pārlūka. Filtrējot lietotāju ierīču piekļuvi neatļaus tiem izmantot jaunas ierīces, līdz tās nav tikušas šeit atļautas.",
|
||||
"DeinterlaceMethodHelp": "Izvēlies rindpārlēces sakļaušanas (deinterlacing) metodi, kad tiek trans-kodēts rindpārlēces izvērsts (interlaced) saturs. Ja ir ieslēgta aparatūras paātrinājuma atbalstoša aparatūras rindpārlēces sakļaušana, tad šī iestatījuma vietā tiks lietots aparatūras rindpārlēces sakļāvējs.",
|
||||
"CustomDlnaProfilesHelp": "Izveido pielāgotu profilu priekš jaunas ierīces, vai pārraksti sistēmas profilu.",
|
||||
"ColorTransfer": "Krāsu pārsūtīšana",
|
||||
"ClientSettings": "Klientu Iestatījumi",
|
||||
"ButtonTogglePlaylist": "Atskaņošanas Saraksts",
|
||||
"ButtonTogglePlaylist": "Atskaņošanas saraksts",
|
||||
"BurnSubtitlesHelp": "Nosaka, vai serverim ir jāiededzina subtitri video trans-kodēšanas laikā. To nedarot tiks stipri palielināta veiktspēja. Izvēlies Auto lai iededzinātu uz attēliem bāzētus formātus (VOBSUB, PGS, SUB, IDX, …) un noteiktus ASS vai SSA subtitrus.",
|
||||
"Artist": "Izpildītājs",
|
||||
"AllowOnTheFlySubtitleExtractionHelp": "Iegultie subtitri var tikt izvilkti no video un nogādāti klientiem kā parasts teksts, lai nevajadzētu veikt lieku video transkodēšanu. Uz dažām sistēmām tas var aizņemt ilgu laiku un likt video atskaņošanai uzkārties izvilkšanas procesa laikā. Atspējo šo, lai iegultos subtitrus iededzinātu video transkodēšanas veidā, kad tos noklusēti neatbalsta klienta ierīce.",
|
||||
"AlbumArtist": "Albuma Izpildītājs",
|
||||
"AlbumArtist": "Albuma izpildītājs",
|
||||
"Album": "Albums",
|
||||
"PleaseRestartServerName": "Lūdzu, atsāknējiet Jellyfin serverī {0}.",
|
||||
"PlayNextEpisodeAutomatically": "Atskaņot nākamo epizodi automātiski",
|
||||
"PlayFromBeginning": "Atskaņot no sākuma",
|
||||
"PlayCount": "Atskaņošanas reizes",
|
||||
"PlaybackData": "Atskaņošanas Dati",
|
||||
"PlaybackData": "Atskaņošanas dati",
|
||||
"Person": "Persona",
|
||||
"PerfectMatch": "Ideāla saderība",
|
||||
"PasswordResetConfirmation": "Vai tu tiešām gribi atiestatīt paroli?",
|
||||
|
@ -978,7 +978,7 @@
|
|||
"OptionProtocolHls": "HTTP Tiešraides Straumes (HLS)",
|
||||
"OptionPremiereDate": "Pirmizrādes Datums",
|
||||
"OptionPlayCount": "Atskaņošanas Skaits",
|
||||
"OptionMissingEpisode": "Trūkstošās Epizodes",
|
||||
"OptionMissingEpisode": "Trūkstošās sērijas",
|
||||
"OptionMax": "Maksimums",
|
||||
"OptionLoginAttemptsBeforeLockoutHelp": "Vērtība nulle nozīmē noklusējuma trīs mēģinājumu priekš lietotājiem un piecu priekš administratoriem izmantošanu. Uzstādot uz -1 atspējos funkciju.",
|
||||
"OptionLoginAttemptsBeforeLockout": "Nosaka, cik daudz nepareizi piekļuves mēģinājumi var notikt pirms notiek bloķēšana.",
|
||||
|
@ -1029,7 +1029,7 @@
|
|||
"LabelEmbedAlbumArtDidl": "Ievietot albumu vākus iekš Didl",
|
||||
"LabelSelectFolderGroups": "Automātiski grupēt saturu no sekojošām datnēm skatos kā Filmas, Mūzika un TV",
|
||||
"AllowFfmpegThrottlingHelp": "Kad transkodējums vai remux tiek pietiekami tālu priekšā pašreizējai atskaņošanas vietai, process tiks pauzēts, lai patērētu mazāk resursu. Tas ir visnoderīgāks, kad skatās bez biežas pārlēkšanas. Atspējo šo, ja saskaries ar atskaņošanas problēmām.",
|
||||
"ButtonSyncPlay": "Sinhronizēta Atskaņošana",
|
||||
"ButtonSyncPlay": "Sinhronizēta atskaņošana",
|
||||
"LabelCustomRating": "Pielāgotais vērtējums",
|
||||
"LabelCurrentStatus": "Pašreizējais status",
|
||||
"LabelAudioBitDepth": "Audio bitu dziļums",
|
||||
|
@ -1061,7 +1061,7 @@
|
|||
"EnableBlurHashHelp": "Attēli, kuri joprojām tiek ielādēti, tiks parādīti ar unikālu vietturi.",
|
||||
"EnableBlurHash": "Iespējot aizmiglotus vietturus attēliem",
|
||||
"DeleteDevicesConfirmation": "Vai tiešām vēlies noņemt visas ierīces? Visas pārējās sesijas tiks atvienotas. Ierīces parādīsies atkārtoti nākamajā reizē, kad lietotājs pievienosies.",
|
||||
"DeleteAll": "Dzēst Visu",
|
||||
"DeleteAll": "Dzēst visu",
|
||||
"DailyAt": "Katru dienu {0}",
|
||||
"ClearQueue": "Notīrīt rindu",
|
||||
"Bwdif": "BWDIF",
|
||||
|
@ -1069,10 +1069,10 @@
|
|||
"EnableDetailsBannerHelp": "Rādīt reklāmkaroga attēlu vienuma informācijas lapas augšdaļā.",
|
||||
"EnableDetailsBanner": "Informācijas reklāmkarogs",
|
||||
"ButtonPlayer": "Atskaņotājs",
|
||||
"ButtonCast": "Raidīt uz Ierīci",
|
||||
"ButtonCast": "Raidīt uz ierīci",
|
||||
"AllowTonemappingHelp": "Toņu kartēšana var pārveidot video dinamisko diapazonu no HDR uz SDR, saglabājot attēla detaļas un krāsas, kas ir ļoti svarīga informācija, lai attēlotu sākotnējo ainu. Pašlaik strādā tikai, kad tiek pārkodēti video ar iegultiem 10-bitu HDR10, HLG vai DoVi metadatiem. Ja atskaņošana nav vienmērīga vai tā neizdodas, lūdzu, apsveriet atbilstošā aparatūras dekodētāja izslēgšanu.",
|
||||
"LabelChromecastVersion": "Chromecast Versija",
|
||||
"HeaderUploadSubtitle": "Augšupielādēt Subtitrus",
|
||||
"HeaderUploadSubtitle": "Augšupielādēt subtitrus",
|
||||
"HeaderRemoteAccessSettings": "Attālinātas Pieejas Iestatījumi",
|
||||
"HeaderPortRanges": "Ugunsmūra un Starpniekservera Iestatījumi",
|
||||
"HeaderNewRepository": "Jauna Krātuve",
|
||||
|
@ -1081,8 +1081,8 @@
|
|||
"HeaderDeleteDevices": "Izdzēst Visas Ierīces",
|
||||
"HeaderDebugging": "Atkļūdošana un problēmu izsekošana",
|
||||
"HeaderContinueReading": "Turpināt lasīt",
|
||||
"HeaderAddUser": "Pievienot Lietotāju",
|
||||
"HeaderAddUpdateSubtitle": "Pievienot/Atjaunot Subtitrus",
|
||||
"HeaderAddUser": "Pievienot lietotāju",
|
||||
"HeaderAddUpdateSubtitle": "Pievienot/atjaunot subtitrus",
|
||||
"Framerate": "Kadri sekundē",
|
||||
"FastForward": "Ātri uz priekšu",
|
||||
"Extras": "Ekstras",
|
||||
|
@ -1222,9 +1222,9 @@
|
|||
"HeaderDVR": "DVR",
|
||||
"HeaderContainerProfileHelp": "Konteineru profili norāda uz ierīces ierobežojumiem, atskaņojot noteiktus formātus. Ja tiek piemērots ierobežojums, multivide tiks pārkodēta, pat ja formāts ir konfigurēts tiešai atskaņošanai.",
|
||||
"HeaderCodecProfileHelp": "Kodeku profili norāda uz ierīces ierobežojumiem, atskaņojot noteiktus kodekus. Ja tiek piemērots ierobežojums, fails tiks pārkodēts pat tad, ja kodeks ir konfigurēts tiešai atskaņošanai.",
|
||||
"HeaderAutoDiscovery": "Tīkla Atklāšana",
|
||||
"HeaderAutoDiscovery": "Tīkla atklāšana",
|
||||
"H264CrfHelp": "CRF is noklusējuma kvalitātes iestatījums priekš x264 un x265 kodētājiem. Pieļaujamās vērtības 0-51, kur zemāka vērtības atbilst labākai kvalitātei (jo mazāks skaitlis jo lielāki faili). Parasti izvēlas 18-28. Noklusējums x264 kodētājam ir 23, x265 ir 28. Noklusējuma vērtības var izmantot kā sākuma punktu.",
|
||||
"GuideProviderSelectListings": "Izvēlēties Sarakstus",
|
||||
"GuideProviderSelectListings": "Izvēlēties sarakstus",
|
||||
"ErrorPlayerNotFound": "Atskaņotājs pieprasītajam mēdijam nav atrasts.",
|
||||
"ErrorAddingListingsToSchedulesDirect": "Pievienojot sarakstu jūsu Schedules Direct kontam. Schedules Direct atļauj vienā kontā tikai ierobežotu grupu skaitu. Pirms turpināt, jums būs jāpiesakās Schedules Direct vietnē un jānoņem citi ieraksti no sava konta.",
|
||||
"Engineer": "Skaņas inženieris",
|
||||
|
@ -1316,7 +1316,7 @@
|
|||
"LabelChapterImageResolution": "Izšķirtspēja",
|
||||
"OptionAllowRemoteControlOthers": "Atļaut citu lietotāju attālo pārvaldību",
|
||||
"OptionReleaseDate": "Izlaiduma gads",
|
||||
"OptionUnairedEpisode": "Neizlaistās epizodes",
|
||||
"OptionUnairedEpisode": "Neizlaistās sērijas",
|
||||
"DownloadAll": "Lejuplādēt visu",
|
||||
"AllowEmbeddedSubtitlesAllowAllOption": "Atļaut visu",
|
||||
"LabelStereoDownmixAlgorithm": "Stereo Lejupmiksēšanas Algoritms",
|
||||
|
@ -1332,7 +1332,7 @@
|
|||
"MessageForgotPasswordFileCreated": "Sekojošais fails ir ticis izveidots uz tava servera un satur instrukcijas tālākai rīcībai",
|
||||
"LabelMaxVideoResolution": "Maksimālā Atļautā Video Transkodēšanas Izšķirtspēja",
|
||||
"MessageAddRepository": "Ja tu vēlies pievienot repozitoriju, nospied pogu, kas atrodas blakus galvenei un aizpildi prasīto informāciju.",
|
||||
"HomeVideosPhotos": "Mājas Video un Fotogrāfijas",
|
||||
"HomeVideosPhotos": "Mājas video un fotogrāfijas",
|
||||
"MediaInfoBitrate": "Datu pārraides ātrums",
|
||||
"LabelEnableAudioVbr": "Iespējot VBR skaņas kodēšanu",
|
||||
"LabelEnableAudioVbrHelp": "Mainīgs bitreits sneidz labāku kvalitāti lai izlīdzinātu bitreita attiecību, bet dažos retos gadījumos var izraisīt ilgus bufera laikus un savietojamības problēmas.",
|
||||
|
@ -1361,7 +1361,7 @@
|
|||
"MessageNoTrailersFound": "Instalējiet treileru kanālu, lai uzlabotu savu filmu pieredzi, pievienojot interneta treileru bibliotēku.",
|
||||
"EnableRewatchingNextUp": "Atkārtotas skatīšanas iespējošana sadaļā Nākamais",
|
||||
"EnableAudioNormalizationHelp": "Audio normalizācija pievienos konstantu skaļuma pastiprinājumu, lai saglabātu vidējo skaļumu vēlamajā līmenī (-18dB).",
|
||||
"EnableAudioNormalization": "Audio Normalizācija",
|
||||
"EnableAudioNormalization": "Audio normalizācija",
|
||||
"LabelEnableLUFSScan": "Iespējot LUFS skenēšanu",
|
||||
"LabelEnableLUFSScanHelp": "Klienti var normalizēt audio atskaņošanu, lai iegūtu vienādu skaļumu visos ierakstos. (Tas pataisīs bibliotēkas skenēšanu lēnāku un patērēs vairāk resursus).",
|
||||
"MessageNoItemsAvailable": "Pašlaik nav pieejams neviens vienums.",
|
||||
|
@ -1394,7 +1394,7 @@
|
|||
"SettingsSaved": "Iestatījumi ir saglabāti.",
|
||||
"MessageSyncPlayNoGroupsAvailable": "Nav pieejama neviena grupa. Vispirms sāciet kaut ko skatīties.",
|
||||
"MessageUnsetContentHelp": "Saturs tiks parādīts kā vienkāršas mapes. Lai iegūtu labākos rezultātus, izmantojiet metadatu pārvaldnieku, lai iestatītu apakšmapju satura veidus.",
|
||||
"GetThePlugin": "Iegūt Paplašinājumu",
|
||||
"GetThePlugin": "Iegūt paplašinājumu",
|
||||
"MessageSyncPlayErrorNoActivePlayer": "Nav atrasts aktīvs atskaņotājs. SyncPlay tika izslēgts.",
|
||||
"MessageSyncPlayPlaybackPermissionRequired": "Nepieciešama atskaņošanas atļauja.",
|
||||
"MessageSyncPlayUserLeft": "{0} pameta grupu.",
|
||||
|
@ -1412,8 +1412,8 @@
|
|||
"AllowEmbeddedSubtitlesHelp": "Atslēgt subtitrus, kas ir iekļauti multivides konteineros. Nepieciešama pilnīga bibliotēkas atjaunošana.",
|
||||
"PreviousTrack": "Pāriet uz iepriekšējo",
|
||||
"MoveLeft": "Pārvietoties pa kreisi",
|
||||
"PreferEmbeddedEpisodeInfosOverFileNames": "Priekšroku dot iegultai epizodes informācijai, nevis failu nosaukumiem",
|
||||
"PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Izmantojiet informāciju par epizodi no iegultajiem metadatiem, ja tā ir pieejama.",
|
||||
"PreferEmbeddedEpisodeInfosOverFileNames": "Priekšroku dot iegultai sērijas informācijai, nevis failu nosaukumiem",
|
||||
"PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Izmantojiet informāciju par sēriju no iegultajiem metadatiem, ja tā ir pieejama.",
|
||||
"Production": "Produkcija",
|
||||
"PluginFromRepo": "{0} no repozitorija {1}",
|
||||
"Premieres": "Pirmizrādes",
|
||||
|
@ -1517,5 +1517,27 @@
|
|||
"LogLevel.None": "Nekas",
|
||||
"RefreshQueued": "Atsvaidzināšana ieplānota.",
|
||||
"LabelSystem": "Sistēma",
|
||||
"LogLevel.Critical": "Kritisks"
|
||||
"LogLevel.Critical": "Kritisks",
|
||||
"Trailer": "Treileris",
|
||||
"BehindTheScenes": "Aiz kadra",
|
||||
"ContainerBitrateExceedsLimit": "Video bitu pārraides ātrums pārsniedz ierobežojumu",
|
||||
"TabAccess": "Piekļuve",
|
||||
"ContainerNotSupported": "Konteiners netiek atbalstīts",
|
||||
"TabDashboard": "Infopanelis",
|
||||
"Controls": "Vadība",
|
||||
"TV": "TV",
|
||||
"AllowHevcEncoding": "Atļaut kodēšanu HEVC formātā",
|
||||
"TabRepositories": "Krātuves",
|
||||
"DirectPlayError": "Sākot tiešo atskaņošanu, radās kļūda",
|
||||
"DeletedScene": "Izdzēsta aina",
|
||||
"AnamorphicVideoNotSupported": "Anamorfiskais video netiek atbalstīts",
|
||||
"TabScheduledTasks": "Plānotie uzdevumi",
|
||||
"AllowAv1Encoding": "Atļaut kodēšanu AV1 formātā",
|
||||
"Clip": "Klips",
|
||||
"AiTranslated": "Tulkots ar MI",
|
||||
"ForeignPartsOnly": "Piespiedu/Tikai cittautu daļas",
|
||||
"TextSent": "Teksts nosūtīts.",
|
||||
"Track": "Celiņš",
|
||||
"TabUpcoming": "Nākošais",
|
||||
"Writers": "Scenāristi"
|
||||
}
|
||||
|
|
|
@ -1698,5 +1698,36 @@
|
|||
"SubtitleGreen": "Verde",
|
||||
"SubtitleWhite": "Alb",
|
||||
"SubtitleYellow": "Galben",
|
||||
"VideoRangeTypeNotSupported": "Tipul intervalului video nu este suportat"
|
||||
"VideoRangeTypeNotSupported": "Tipul intervalului video nu este suportat",
|
||||
"Unreleased": "Indisponibil momentan",
|
||||
"LabelIsHearingImpaired": "Pentru deficiente de auz (SDH)",
|
||||
"PasswordRequiredForAdmin": "O parola este necesara pentru conturile de administrator.",
|
||||
"Studio": "Studio",
|
||||
"LabelSyncPlayNoGroups": "Niciun grup disponibil",
|
||||
"Notifications": "Notificari",
|
||||
"LabelDate": "Data",
|
||||
"LabelLevel": "Nivel",
|
||||
"LabelSystem": "Sistem",
|
||||
"LabelMediaDetails": "Detalii media",
|
||||
"LogLevel.Information": "Informatie",
|
||||
"Unknown": "Necunoscut",
|
||||
"GoHome": "Catre Ecranul Principal",
|
||||
"UnknownError": "A aparut o eroare neprevazuta.",
|
||||
"LogLevel.Warning": "Avertisment",
|
||||
"LogLevel.Error": "Eroare",
|
||||
"LogLevel.Critical": "Critic",
|
||||
"LabelDeveloper": "Dezvoltator",
|
||||
"AllowCollectionManagement": "Permite acestui utilizator sa administreze colectiile",
|
||||
"ListView": "Vizualizare ca lista",
|
||||
"MenuOpen": "Deschideti Meniul",
|
||||
"MenuClose": "Inchidere Meniu",
|
||||
"UserMenu": "Meniu Utilizator",
|
||||
"Select": "Selecteaza",
|
||||
"EnableAudioNormalizationHelp": "Normalizarea audio va adauga o crestere constanta pentru a mentine media la un nivel dorit (-18 decibeli).",
|
||||
"AiTranslated": "Tradus de catre IA",
|
||||
"AllowSegmentDeletion": "Sterge segmentele",
|
||||
"AllowSegmentDeletionHelp": "Sterge segmentele vechi dupa ce acestea au fost trimise catre client. Previne necesitatea de a stoca intregul fisier transcodificat pe disc. Va functiona doar cu throttling activat. Opriti aceasta optiune daca intampinati probleme de redare.",
|
||||
"LabelThrottleDelaySeconds": "Limitare dupa",
|
||||
"LabelSegmentKeepSeconds": "Timpul pentru a pastra segmentele",
|
||||
"EnableAudioNormalization": "Normalizare audio"
|
||||
}
|
||||
|
|
|
@ -1401,7 +1401,7 @@
|
|||
"QuickConnectDescription": "Чтобы войти в систему с помощью Быстрого подключения, нажмите кнопку «Быстрое подключение» на устройстве, с которого вы выполняете вход, и введите указанный ниже код.",
|
||||
"QuickConnectDeactivated": "Быстрое подключение было деактивировано до утверждения запроса на вход",
|
||||
"QuickConnectAuthorizeFail": "Неопознанный код Быстрого подключения",
|
||||
"QuickConnectAuthorizeSuccess": "Запрос авторизован",
|
||||
"QuickConnectAuthorizeSuccess": "Вы успешно авторизовали ваше устройство!",
|
||||
"QuickConnectAuthorizeCode": "Введите код {0} для входа",
|
||||
"QuickConnectActivationSuccessful": "Активировано успешно",
|
||||
"QuickConnect": "Быстрое подключение",
|
||||
|
@ -1424,7 +1424,7 @@
|
|||
"LabelTonemappingPeakHelp": "Этим значением перекрывается сигнальный/номинальный/эталонный пик. Полезно, когда встроенная информация о пиках в метаданных дисплея ненадёжна или при тонмаппинге из узкого диапазона в более широкий. Значения рекомендуемое и по умолчанию - 100 и 0.",
|
||||
"LabelTonemappingThresholdHelp": "Параметры алгоритма тонмаппинга подстраиваются для каждой сцены. А порог используется, чтобы определить, изменилась ли сцена или нет. Если дистанция между средней яркостью текущего кадра и текущим скользящим средним превышает пороговое значение, мы пересчитаем среднюю и пиковую яркость сцены. Значения рекомендуемое и по умолчанию - 0.8 и 0.2.",
|
||||
"TonemappingAlgorithmHelp": "Тонмаппинг можно подстроить. Если вы не уверены с этими параметрами, оставьте значения по умолчанию. Рекомендуемое значение - \"BT.2390\".",
|
||||
"AllowTonemappingHelp": "Тонмаппинг может преобразовать динамический диапазон видео из HDR в SDR, сохраняя детали изображения и цвета, которые являются очень важной информацией для представления исходной сцены. В настоящее время работает только с 10-бит HDR10, HLGи DoVi видео. Для этого требуется соответствующая среда выполнения OpenCL или CUDA.",
|
||||
"AllowTonemappingHelp": "Тонмаппинг может преобразовать динамический диапазон видео из HDR в SDR, сохраняя детали изображения и цвета, которые являются очень важной информацией для представления исходного видео. В настоящее время работает только с 10-бит HDR10, HLG и DoVi. Для этого требуется соответствующая среда выполнения OpenCL или CUDA.",
|
||||
"LabelOpenclDeviceHelp": "Это устройство OpenCL, которое используется для тонмаппинга. Слева от точки - номер платформы, а справа - это номер устройства на платформе. Значение по умолчанию - 0.0. Требуется файл приложения FFmpeg, содержащий метод аппаратного ускорения OpenCL.",
|
||||
"OptionAllowContentDownloadHelp": "Пользователи могут загружать медиафайлы и хранить их на своих устройствах. Это не то же самое, как функция синхронизации. Для правильной работы книжных медиатек это необходимо.",
|
||||
"HeaderDeleteDevices": "Удалить все устройства",
|
||||
|
@ -1742,7 +1742,7 @@
|
|||
"LogLevel.Critical": "Критично",
|
||||
"LogLevel.None": "Ничего",
|
||||
"HeaderEpisodesStatus": "Статус эпизодов",
|
||||
"LabelEnableLUFSScanHelp": "Включите сканирование LUFS для музыки (это займет больше времени и ресурсов).",
|
||||
"LabelEnableLUFSScanHelp": "Клиенты могут нормализировать воспроизведения звука, чтобы обеспечить одинаковую громкость в разных треках. Сканированния библеотек займет больше времени и ресурсов.",
|
||||
"LabelLevel": "Уровень",
|
||||
"LogLevel.Debug": "Отладка",
|
||||
"MessageRepositoryInstallDisclaimer": "ПРЕДУПРЕЖДЕНИЕ: Установка стороннего репозитория плагинов несет определенные риски. Он может содержать нестабильный или вредоносный код и может изменяться в любое время. Устанавливайте репозитории только от авторов, которым вы доверяете.",
|
||||
|
@ -1764,7 +1764,17 @@
|
|||
"LogLevel.Error": "Ошибка",
|
||||
"LabelBackdropScreensaverInterval": "Интервал между фонами у заставки",
|
||||
"LabelBackdropScreensaverIntervalHelp": "Время в секундах между разными фонами, когда используется заставка.",
|
||||
"BackdropScreensaver": "Заставка",
|
||||
"BackdropScreensaver": "Фоновая заставка",
|
||||
"GoHome": "Домой",
|
||||
"GridView": "Сеткой"
|
||||
"GridView": "Отображение сеткой",
|
||||
"LabelIsHearingImpaired": "Для людей со слабым слухом (SDH)",
|
||||
"AllowAv1Encoding": "Разрешить кодирование AV1 формата",
|
||||
"LogoScreensaver": "Логотип заставки",
|
||||
"HeaderGuestCast": "Приглашенные звезды",
|
||||
"UnknownError": "Возникла не известная ошибка.",
|
||||
"ListView": "Отображение списком",
|
||||
"AiTranslated": "Переведено при помощи ИИ",
|
||||
"MachineTranslated": "Машинный перевод",
|
||||
"HearingImpairedShort": "HI/SDH",
|
||||
"ForeignPartsOnly": "Только для принудительных и иностранных частей"
|
||||
}
|
||||
|
|
|
@ -662,7 +662,7 @@
|
|||
"TabLatest": "Najnovšie pridané",
|
||||
"TabMusic": "Hudba",
|
||||
"TabMyPlugins": "Moje zásuvné moduly",
|
||||
"TabNetworks": "TV Siete",
|
||||
"TabNetworks": "Stanice",
|
||||
"TabNfoSettings": "NFO nastavenia",
|
||||
"TabNotifications": "Upozornenia",
|
||||
"TabOther": "Ostatné",
|
||||
|
@ -953,7 +953,7 @@
|
|||
"GuideProviderSelectListings": "Výber zobrazenia",
|
||||
"GroupVersions": "Skupinové verzie",
|
||||
"FetchingData": "Načítavanie dodatočných dát",
|
||||
"Extras": "Extras",
|
||||
"Extras": "Bonusové materiály",
|
||||
"FastForward": "Rýchlo dopredu",
|
||||
"FFmpegSavePathNotFound": "Nepodarilo sa nám nájsť FFmpeg pomocou vami zadanej cesty. FFprobe je taktiež potrebná a musí existovať v rovnakom priečinku. Tieto komponenty sú za normálnych okolností zabalené spolu do toho istého priečinku. Prosím, overte zadanú cestu a skúste to znova.",
|
||||
"ErrorSavingTvProvider": "Nastala chyba pri ukladaní sprostredkovateľa TV vysielania. Prosím, uistite sa, že je prístupný a skúste to znova.",
|
||||
|
@ -1081,11 +1081,11 @@
|
|||
"MessagePluginInstallDisclaimer": "UPOZORNENIE: Inštalácia zásuvného modulu tretej strany má určité riziká. Modul môže obsahovať nestabilný alebo škodlivý kód a môže sa kedykoľvek zmeniť. Inštalujte zásuvné moduly len od autorov, ktorým dôverujete a majte na vedomí ich potenciálne následky, vrátane kontaktovania externých služieb, dlhšieho prehľadávanie knižníc alebo dodatočných procesov na pozadí.",
|
||||
"MessagePluginConfigurationRequiresLocalAccess": "Pre konfiguráciu tohoto zásuvného modulu sa prihláste priamo na lokálny server.",
|
||||
"MessagePlayAccessRestricted": "Prehrávanie tohoto obsahu je aktuálne obmedzené. Prosím, kontaktujte svojho administrátora servera pre viac informácií.",
|
||||
"MessagePasswordResetForUsers": "Nasledujúci používatelia si nechali obnoviť heslo. Teraz sa môžu prihlásiť s PIN kódmi, ktoré použili k pri obnove hesla.",
|
||||
"MessagePasswordResetForUsers": "Nasledujúcim používateľom boli resetované heslá. Teraz sa môžu prihlásiť pomocou kódov PIN, ktoré boli použité na resetovanie.",
|
||||
"MessageNoServersAvailable": "Žiadne servery neboli nájdené pomocou automatického objavovania serverov.",
|
||||
"MessageNoMovieSuggestionsAvailable": "V súčasnosti nie sú k dispozícií žiadne filmové návrhy. Začnite pozerať a hodnotiť vaše filmy, potom sa sem vráťte pre vaše odporúčania.",
|
||||
"MessageNoCollectionsAvailable": "Kolekcie vám umožnia užiť si vlastné zoskupenia filmov, seriálov a albumov. Kliknite na tlačítko '+' pre začatie vytvárania kolekcie.",
|
||||
"MessageImageTypeNotSelected": "Prosím, vyberte typ obrázku z rozbaľovacieho menu.",
|
||||
"MessageImageTypeNotSelected": "Z rozbaľovacej ponuky vyberte typ obrázka.",
|
||||
"MessageForgotPasswordInNetworkRequired": "Prosím, skúste to znova vo vašej domácej sieti pre zahájenie procesu obnovy hesla.",
|
||||
"MessageForgotPasswordFileCreated": "Nasledujúci súbor bol vytvorený na vašom serveri a obsahuje inštrukcie, ako postupovať",
|
||||
"MessageDownloadQueued": "Sťahovanie zaradené do fronty.",
|
||||
|
@ -1095,7 +1095,7 @@
|
|||
"MessageCreateAccountAt": "Vytvoriť účet v {0}",
|
||||
"MessageContactAdminToResetPassword": "Prosím, kontaktujte vášho systémového administrátora k obnoveniu hesla.",
|
||||
"MessageConfirmRevokeApiKey": "Ste si istý, že chcete odňať tento API kľúč? Aplikácie pripojené k tomuto serveru budú rázne ukončené.",
|
||||
"Menu": "Menu",
|
||||
"Menu": "Ponuka",
|
||||
"MediaIsBeingConverted": "Médium sa konvertuje do formátu, ktorý je kompatibilný so zariadením, kde sa médium prehráva.",
|
||||
"MediaInfoSampleRate": "Vzorkovacia frekvencia",
|
||||
"MediaInfoRefFrames": "Ref snímky",
|
||||
|
@ -1397,7 +1397,7 @@
|
|||
"SelectServer": "Vybrať server",
|
||||
"Restart": "Reštartovať",
|
||||
"ResetPassword": "Obnoviť heslo",
|
||||
"QuickConnectAuthorizeSuccess": "Požiadavka autorizovaná",
|
||||
"QuickConnectAuthorizeSuccess": "Úspešne ste overili vaše zariadenie!",
|
||||
"QuickConnectAuthorizeFail": "Neznámy kód pre Rýchle pripojenie",
|
||||
"QuickConnectAuthorizeCode": "Zadajte kód {0} pre prihlásenie",
|
||||
"QuickConnectActivationSuccessful": "Úspešne aktivované",
|
||||
|
@ -1447,7 +1447,7 @@
|
|||
"AspectRatioCover": "Obal",
|
||||
"VideoAudio": "Video Zvuk",
|
||||
"Video": "Video",
|
||||
"AllowTonemappingHelp": "Tone-mapping can transform the dynamic range of a video from HDR to SDR while maintaining image details and colors, which are very important information for representing the original scene. Currently works only with 10bit HDR10, HLG and DoVi videos. This requires the corresponding OpenCL or CUDA runtime.",
|
||||
"AllowTonemappingHelp": "Mapovanie tónov umožňuje zmeniť dynamický rozsah videa z HDR na SDR bez straty dôležitých informácií pôvodného obrazu, ako sú detaily a farby. Táto funkcia v súčasnosti funguje len pre videá, ktoré obsahujú 10-bitové HDR10, HLG alebo Dolby Vision. Funkcia vyžaduje OpenCL alebo CUDA.",
|
||||
"LabelTonemappingThresholdHelp": "Parametre algoritmu mapovania tónov sú prispôsobené jednotlivým scénam. A tento prah sa používa na zistenie, či sa scéna zmenila alebo nie. Pokiaľ rozdiel medzi súčasnou priemernou svetlosťou snímku a priebežným priemerom tento prah prekročí, bude priemerná a vrchná svetlosť scény prepočítaná. Doporučené a predvolené hodnoty sú 0.8 a 0.2.",
|
||||
"LabelUDPPortRangeHelp": "Obmedzí UDP pripojenie Jellyfinu na tento rozsah. (Predvolená hodnota je 1024 - 65535).<br/> Poznámka: Niektoré funkcie vyžadujú určité porty, ktoré sa môžu nachádzať mimo tohto rozsahu.",
|
||||
"Remuxing": "Remuxovanie",
|
||||
|
@ -1508,7 +1508,7 @@
|
|||
"DisablePlugin": "Zakázať",
|
||||
"EnablePlugin": "Povoliť",
|
||||
"Framerate": "Snímková frekvencia",
|
||||
"DirectPlayHelp": "Zdrojový súbor je s klientom plne kompatibilný a relácia ho preto prijíma bez dodatočných modifikácií.",
|
||||
"DirectPlayHelp": "Zdrojový súbor je úplne kompatibilný s týmto klientom a relácia prijíma súbor bez úprav.",
|
||||
"HeaderContinueReading": "Pokračovať v čítaní",
|
||||
"LabelSyncPlaySettingsDescription": "Zmeniť nastavenia SyncPlay",
|
||||
"LabelSlowResponseTime": "Čas v milisekundách, ktorý je považovaný za pomalú odozvu",
|
||||
|
@ -1679,7 +1679,7 @@
|
|||
"OptionDateShowAdded": "Dátum pridania seriálu",
|
||||
"OptionDateEpisodeAdded": "Dátum pridania epizódy",
|
||||
"IgnoreDtsHelp": "Vypnutím sa môžu vyriešiť niektoré problémy, napr. chýbajúci zvuk pri kanáloch so samostatnými zvukovými a video streamami.",
|
||||
"IgnoreDts": "Ignorovať DTS (dekódovacia časová pečiatka)",
|
||||
"IgnoreDts": "Ignorovať DTS (dekódovacia časová značka)",
|
||||
"Unreleased": "Zatiaľ nevydané",
|
||||
"EnableCardLayout": "Zobraziť vizuálny CardBox",
|
||||
"MessageNoItemsAvailable": "Momentálne nie sú k dispozícii žiadne položky.",
|
||||
|
@ -1694,11 +1694,11 @@
|
|||
"LabelDummyChapterCount": "Limit",
|
||||
"LabelDummyChapterCountHelp": "Maximálny počet obrázkov kapitol, ktoré budú extrahované pre každý mediálny súbor.",
|
||||
"LabelChapterImageResolution": "Rozlíšenie",
|
||||
"LabelChapterImageResolutionHelp": "Rozlíšenie extrahovaných obrázkov kapitol. Zmena tohto nastavenia nemá žiaden vplyv na existujúce kapitoly.",
|
||||
"LabelChapterImageResolutionHelp": "Rozlíšenie extrahovaných obrázkov kapitol. Zmena tohto nastavenia nebude mať žiadny vplyv na existujúce fiktívne kapitoly.",
|
||||
"ResolutionMatchSource": "Rovnaké ako zdroj",
|
||||
"SaveRecordingNFOHelp": "Uloží metadáta z EPG položiek sprievodcu spolu s médiami.",
|
||||
"SaveRecordingImages": "Uložiť obrázky EPG nahrávky",
|
||||
"LabelDummyChapterDurationHelp": "Interval medzi kapitolami. Vytváranie kapitol je možné vypnúť nastavením na 0. Zmena tohto nastavenia nemá žiaden vplyv na existujúce kapitoly.",
|
||||
"LabelDummyChapterDurationHelp": "Interval medzi fiktívnymi kapitolami. Nastavením na 0 vypnete generovanie fiktívnych kapitol. Zmena tejto hodnoty nebude mať žiadny vplyv na existujúce fiktívne kapitoly.",
|
||||
"SaveRecordingNFO": "Uložiť metadáta nahrávky zo sprievodcu EPG do NFO",
|
||||
"SaveRecordingImagesHelp": "Uloží obrázky z EPG položiek sprievodcu spolu s médiami.",
|
||||
"HeaderRecordingMetadataSaving": "Metadáta nahrávok",
|
||||
|
@ -1706,36 +1706,75 @@
|
|||
"LabelEnableAudioVbr": "Povoliť kódovanie zvuku VBR",
|
||||
"HeaderPerformance": "Výkon",
|
||||
"AllowCollectionManagement": "Povoliť tomuto používateľovi spravovať kolekcie",
|
||||
"LabelParallelImageEncodingLimitHelp": "Maximálny počet kódovania obrázkov, ktoré môžu naraz bežať. Nastavením na 0 bude limit nastavení podľa parametrov systému.",
|
||||
"TonemappingModeHelp": "Vyberte režim mapovania tónu. Ak narazíte na preexponované svetlé miesta, skúste prepnúť na režim RGB.",
|
||||
"LabelParallelImageEncodingLimitHelp": "Maximálny počet kódovaní obrazu, ktoré môžu bežať paralelne. Nastavením tejto hodnoty na 0 sa zvolí limit na základe špecifikácie vášho systému.",
|
||||
"TonemappingModeHelp": "Vyberte režim mapovania tónov. Ak sa vyskytnú preexponované svetlé miesta, skúste prepnúť na režim RGB.",
|
||||
"Featurette": "Stredne dlhý film",
|
||||
"Short": "Krátky film",
|
||||
"PasswordRequiredForAdmin": "Administrátorské úcty musia mať nastavené heslo.",
|
||||
"LabelTonemappingMode": "Režim mapovania tónu",
|
||||
"SubtitleCyan": "Tyrkysová",
|
||||
"SubtitleGray": "Sivá",
|
||||
"PasswordRequiredForAdmin": "Pre administrátorské účty sa vyžaduje heslo.",
|
||||
"LabelTonemappingMode": "Režim mapovania tónov",
|
||||
"SubtitleCyan": "Azúrová",
|
||||
"SubtitleGray": "Šedá",
|
||||
"Studio": "Štúdio",
|
||||
"SubtitleBlue": "Modrá",
|
||||
"SubtitleBlack": "Čierna",
|
||||
"SubtitleGreen": "Zelená",
|
||||
"SubtitleLightGray": "Svetlo sivá",
|
||||
"SubtitleLightGray": "Svetlo šedá",
|
||||
"SubtitleRed": "Červená",
|
||||
"SubtitleYellow": "Žltá",
|
||||
"SubtitleWhite": "Biela",
|
||||
"Select": "Vybrať",
|
||||
"EnableAudioNormalization": "Normalizácia hlasitosti",
|
||||
"EnableAudioNormalization": "Normalizácia zvuku",
|
||||
"GetThePlugin": "Získať zásuvný modul",
|
||||
"LabelParallelImageEncodingLimit": "Limit paralelného kódovania obrázkov",
|
||||
"Notifications": "Upozornenia",
|
||||
"NotificationsMovedMessage": "Funkcia upozornení bola presunutá do zásuvného modulu Webhook.",
|
||||
"PreferEmbeddedExtrasTitlesOverFileNames": "Preferovať vložené názvy pred názvami súborov pre doplnky",
|
||||
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Doplnky väčšinou majú totožní vložení názov ako nadriadená položka. Zaškrtnutím ich môžete napriek uprednostniť.",
|
||||
"SubtitleMagenta": "Fialová",
|
||||
"LabelEnableAudioVbrHelp": "Premenlivý bitový tok ponúka lepší pomer medzi kvalitou a priemerným bitovým tokom, ale niekdy môže spôsobiť dodatočné načítavanie alebo problémy s kompatibilitou.",
|
||||
"PreferEmbeddedExtrasTitlesOverFileNames": "Preferovať vložené názvy pred názvami súborov pre bonusové materiály",
|
||||
"PreferEmbeddedExtrasTitlesOverFileNamesHelp": "Bonusové materiály majú často rovnaký vložený názov ako nadradená položka, zaškrtnite túto možnosť, aby ste aj tak použili vložené názvy.",
|
||||
"SubtitleMagenta": "Magenta",
|
||||
"LabelEnableAudioVbrHelp": "Variabilný dátový tok ponúka lepší pomer kvality a priemerného dátového toku, ale v niektorých zriedkavých prípadoch môže spôsobiť problémy s vyrovnávacou pamäťou a kompatibilitou.",
|
||||
"MenuClose": "Zatvoriť ponuku",
|
||||
"UserMenu": "Užívateľská ponuka",
|
||||
"UserMenu": "Používateľská ponuka",
|
||||
"LabelEnableLUFSScan": "Povoliť skenovanie LUFS",
|
||||
"LabelEnableLUFSScanHelp": "Povoliť skenovanie LUFS pre hudbu (Predlžuje skenovanie a je náročnejšie na výkon).",
|
||||
"LabelEnableLUFSScanHelp": "Klienti môžu normalizovať prehrávanie zvuku, aby sa dosiahla rovnaká hlasitosť všetkých stôp. (Predlžuje prehľadávanie knižnice a je náročnejšie na výkon).",
|
||||
"MenuOpen": "Otvoriť ponuku",
|
||||
"AllowSegmentDeletion": "Zmazať oddiel"
|
||||
"AllowSegmentDeletion": "Zmazať segmenty",
|
||||
"LogoScreensaver": "Šetrič obrazovky s logom",
|
||||
"AllowAv1Encoding": "Povoliť kódovanie do formátu AV1",
|
||||
"BackdropScreensaver": "Šetrič obrazovky s pozadím",
|
||||
"PleaseConfirmRepositoryInstallation": "Kliknutím na tlačidlo OK potvrďte, že ste si prečítali vyššie uvedené informácie a chcete pokračovať v inštalácii repozitára zásuvných modulov.",
|
||||
"UnknownError": "Došlo k neznámej chybe.",
|
||||
"LabelIsHearingImpaired": "Titulky pre nepočujúcich",
|
||||
"LabelSyncPlayNoGroups": "Nie sú k dispozícii žiadne skupiny",
|
||||
"HeaderGuestCast": "Hosťujúce hviezdy",
|
||||
"LabelDate": "Dátum",
|
||||
"LabelLevel": "Úroveň",
|
||||
"MessageRepositoryInstallDisclaimer": "UPOZORNENIE: Inštalácia repozitárov zásuvných modulov tretích strán so sebou prináša určité riziká. Môžu obsahovať nestabilný alebo škodlivý kód a môžu sa kedykoľvek zmeniť. Inštalujte len repozitáre od autorov, ktorým dôverujete.",
|
||||
"HeaderEpisodesStatus": "Stav epizód",
|
||||
"LabelSystem": "Systém",
|
||||
"LogLevel.Trace": "Stopa",
|
||||
"LogLevel.Debug": "Debug",
|
||||
"GoHome": "Prejsť na domovskú obrazovku",
|
||||
"LabelBackdropScreensaverInterval": "Interval šetriča obrazovky s pozadím",
|
||||
"LabelBackdropScreensaverIntervalHelp": "Čas v sekundách medzi rôznymi pozadiami pri použití šetriča obrazovky s pozadím.",
|
||||
"LogLevel.Information": "Informácia",
|
||||
"LabelDeveloper": "Vývojár",
|
||||
"LabelMediaDetails": "Podrobnosti o médiách",
|
||||
"GridView": "Zobrazenie v mriežke",
|
||||
"ListView": "Zobrazenie v zozname",
|
||||
"LogLevel.Warning": "Upozornenie",
|
||||
"LogLevel.Error": "Chyba",
|
||||
"LogLevel.Critical": "Kritická",
|
||||
"LogLevel.None": "Žiadny",
|
||||
"EnableAudioNormalizationHelp": "Normalizácia zvuku pridá konštantné zosilnenie, aby sa priemer hlasitosti udržal na požadovanej úrovni (-18 dB).",
|
||||
"HeaderConfirmRepositoryInstallation": "Potvrdiť inštaláciu repozitára zásuvných modulov",
|
||||
"Unknown": "Neznámy",
|
||||
"AiTranslated": "Preložené pomocou AI",
|
||||
"MachineTranslated": "Strojovo preložené",
|
||||
"ForeignPartsOnly": "Iba vynútené",
|
||||
"HearingImpairedShort": "Titulky pre nepočujúcich",
|
||||
"LabelThrottleDelaySeconds": "Obmedziť po",
|
||||
"LabelSegmentKeepSeconds": "Doba ponechania segmentov",
|
||||
"LabelThrottleDelaySecondsHelp": "Čas v sekundách, po ktorom bude prekódovanie obmedzené. Musí byť dostatočne veľký, aby mal klient v rezerve dostatočné množstvo prehrávaného súboru. Funguje len vtedy, ak je povolená funkcia Obmedziť prekódovanie.",
|
||||
"AllowSegmentDeletionHelp": "Odstránenie starých segmentov po ich odoslaní klientovi. Tým sa zabráni tomu, aby sa celý prekódovaný súbor musel ukladať na disk. Funguje len so zapnutou funkciou Obmedziť prekódovanie. Ak sa vyskytnú problémy s prehrávaním, vypnite túto funkciu.",
|
||||
"LabelSegmentKeepSecondsHelp": "Čas v sekundách, počas ktorého budú segmenty uložené. Musí byť dlhší ako je čas určený v \"Obmedziť po\". Funguje len vtedy, ak je povolená funkcia Zmazania segmentov."
|
||||
}
|
||||
|
|
|
@ -1353,7 +1353,7 @@
|
|||
"LabelOpenclDevice": "OpenCL 设备",
|
||||
"LabelOpenclDeviceHelp": "这是用于色调映射的 OpenCL 设备。 点左边是平台号,右边是平台上的设备号。 默认值为 0.0。 需要支持OpenCL 硬件加速的 FFmpeg 应用程序。",
|
||||
"EnableTonemapping": "启用色调映射",
|
||||
"AllowTonemappingHelp": "色调映射可以将视频的动态范围从HDR转换为SDR,同时保持图像细节和颜色,这些都是表示原始场景的非常重要的信息。目前只适用于10bit HDR10、HLG和DoVi视频。这需要相应的OpenCL或CUDA运行库。",
|
||||
"AllowTonemappingHelp": "色调映射可以将视频的动态范围从 HDR 变换成 SDR,同时保持图像细节与颜色等对于表现原始场景非常重要的信息。目前仅对 10bit HDR10,HLG 和 DoVi 视频生效。此项需要对应的 OpenCL 或 CUDA 运行库。",
|
||||
"LabelTonemappingAlgorithm": "选择要使用的色调映射算法",
|
||||
"TonemappingAlgorithmHelp": "色调映射可以微调。如果你不是很熟悉这些选项,保持默认即可。建议值为 'BT.2390'。",
|
||||
"LabelTonemappingRange": "色调映射范围",
|
||||
|
@ -1764,7 +1764,7 @@
|
|||
"HeaderEpisodesStatus": "剧集状态",
|
||||
"LabelBackdropScreensaverInterval": "屏幕保护程序间隔",
|
||||
"LabelBackdropScreensaverIntervalHelp": "不同屏幕保护切换的时间间隔秒数。",
|
||||
"AllowAv1Encoding": "允许以AV1格式进行编码",
|
||||
"AllowAv1Encoding": "允许以 AV1 格式进行编码",
|
||||
"HeaderGuestCast": "特邀嘉宾",
|
||||
"GridView": "网格视图",
|
||||
"ListView": "列表视图",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
|
||||
import { CollectionType } from './collectionType';
|
||||
|
||||
export interface CardOptions {
|
||||
itemsContainer?: HTMLElement | null;
|
||||
|
@ -32,7 +33,7 @@ export interface CardOptions {
|
|||
showUnplayedIndicator?: boolean;
|
||||
showChildCountIndicator?: boolean;
|
||||
lines?: number;
|
||||
context?: string | null;
|
||||
context?: CollectionType;
|
||||
action?: string | null;
|
||||
defaultShape?: string;
|
||||
indexBy?: string;
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
export interface ViewQuerySettings {
|
||||
showTitle?: boolean;
|
||||
showYear?: boolean;
|
||||
imageType?: string;
|
||||
viewType?: string;
|
||||
cardLayout?: boolean;
|
||||
SortBy?: string | null;
|
||||
SortOrder?: string | null;
|
||||
IsPlayed?: boolean | null;
|
||||
IsUnplayed?: boolean | null;
|
||||
IsFavorite?: boolean | null;
|
||||
IsResumable?: boolean | null;
|
||||
Is4K?: boolean | null;
|
||||
IsHD?: boolean | null;
|
||||
IsSD?: boolean | null;
|
||||
Is3D?: boolean | null;
|
||||
VideoTypes?: string | null;
|
||||
SeriesStatus?: string | null;
|
||||
HasSubtitles?: boolean | null;
|
||||
HasTrailer?: boolean | null;
|
||||
HasSpecialFeature?: boolean | null;
|
||||
ParentIndexNumber?: boolean | null;
|
||||
HasThemeSong?: boolean | null;
|
||||
HasThemeVideo?: boolean | null;
|
||||
GenreIds?: string | null;
|
||||
NameLessThan?: string | null;
|
||||
NameStartsWith?: string | null;
|
||||
StartIndex?: number;
|
||||
}
|
|
@ -8,7 +8,7 @@ import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
|
|||
export type ParentId = string | null | undefined;
|
||||
|
||||
export interface LibraryViewProps {
|
||||
parentId: string | null;
|
||||
parentId: ParentId;
|
||||
}
|
||||
|
||||
export enum FeatureFilters {
|
||||
|
|
|
@ -144,12 +144,12 @@ export const getSettingsKey = (viewType: LibraryTab, parentId: ParentId) => {
|
|||
return `${viewType} - ${parentId}`;
|
||||
};
|
||||
|
||||
export const getDefaultLibraryViewSettings = (): LibraryViewSettings => {
|
||||
export const getDefaultLibraryViewSettings = (viewType: LibraryTab): LibraryViewSettings => {
|
||||
return {
|
||||
ShowTitle: true,
|
||||
ShowYear: false,
|
||||
ViewMode: ViewMode.GridView,
|
||||
ImageType: ImageType.Primary,
|
||||
ViewMode: viewType === LibraryTab.Songs ? ViewMode.ListView : ViewMode.GridView,
|
||||
ImageType: viewType === LibraryTab.Networks ? ImageType.Thumb : ImageType.Primary,
|
||||
CardLayout: false,
|
||||
SortBy: ItemSortBy.SortName,
|
||||
SortOrder: SortOrder.Ascending,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue