mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-15 18:08:17 -07:00
Merge pull request #5255 from thornbill/remove-dlna-pages
Remove DLNA configuration pages pages
This commit is contained in:
commit
a3552b2d07
@ -1,26 +1,15 @@
|
||||
import { Devices, Analytics, Input, ExpandLess, ExpandMore } from '@mui/icons-material';
|
||||
import Collapse from '@mui/material/Collapse';
|
||||
import { Devices, Analytics, Input } from '@mui/icons-material';
|
||||
import List from '@mui/material/List';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import ListSubheader from '@mui/material/ListSubheader';
|
||||
import React from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import ListItemLink from 'components/ListItemLink';
|
||||
import globalize from 'scripts/globalize';
|
||||
|
||||
const DLNA_PATHS = [
|
||||
'/dashboard/dlna',
|
||||
'/dashboard/dlna/profiles'
|
||||
];
|
||||
|
||||
const DevicesDrawerSection = () => {
|
||||
const location = useLocation();
|
||||
|
||||
const isDlnaSectionOpen = DLNA_PATHS.includes(location.pathname);
|
||||
|
||||
return (
|
||||
<List
|
||||
aria-labelledby='devices-subheader'
|
||||
@ -47,24 +36,13 @@ const DevicesDrawerSection = () => {
|
||||
</ListItemLink>
|
||||
</ListItem>
|
||||
<ListItem disablePadding>
|
||||
<ListItemLink to='/dashboard/dlna' selected={false}>
|
||||
<ListItemLink to='/dashboard/dlna'>
|
||||
<ListItemIcon>
|
||||
<Input />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={'DLNA'} />
|
||||
{isDlnaSectionOpen ? <ExpandLess /> : <ExpandMore />}
|
||||
</ListItemLink>
|
||||
</ListItem>
|
||||
<Collapse in={isDlnaSectionOpen} timeout='auto' unmountOnExit>
|
||||
<List component='div' disablePadding>
|
||||
<ListItemLink to='/dashboard/dlna' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('Settings')} />
|
||||
</ListItemLink>
|
||||
<ListItemLink to='/dashboard/dlna/profiles' sx={{ pl: 4 }}>
|
||||
<ListItemText inset primary={globalize.translate('TabProfiles')} />
|
||||
</ListItemLink>
|
||||
</List>
|
||||
</Collapse>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
@ -2,6 +2,7 @@ import { AsyncRouteType, type AsyncRoute } from 'components/router/AsyncRoute';
|
||||
|
||||
export const ASYNC_ADMIN_ROUTES: AsyncRoute[] = [
|
||||
{ path: 'activity', type: AsyncRouteType.Dashboard },
|
||||
{ path: 'dlna', type: AsyncRouteType.Dashboard },
|
||||
{ path: 'notifications', type: AsyncRouteType.Dashboard },
|
||||
{ path: 'users', type: AsyncRouteType.Dashboard },
|
||||
{ path: 'users/access', type: AsyncRouteType.Dashboard },
|
||||
|
@ -31,24 +31,6 @@ export const LEGACY_ADMIN_ROUTES: LegacyRoute[] = [
|
||||
controller: 'dashboard/devices/device',
|
||||
view: 'dashboard/devices/device.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dlna/profiles/edit',
|
||||
pageProps: {
|
||||
controller: 'dashboard/dlna/profile',
|
||||
view: 'dashboard/dlna/profile.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dlna/profiles',
|
||||
pageProps: {
|
||||
controller: 'dashboard/dlna/profiles',
|
||||
view: 'dashboard/dlna/profiles.html'
|
||||
}
|
||||
}, {
|
||||
path: 'dlna',
|
||||
pageProps: {
|
||||
controller: 'dashboard/dlna/settings',
|
||||
view: 'dashboard/dlna/settings.html'
|
||||
}
|
||||
}, {
|
||||
path: 'plugins/add',
|
||||
pageProps: {
|
||||
|
@ -8,8 +8,8 @@ export const REDIRECTS: Redirect[] = [
|
||||
{ from: 'dashboardgeneral.html', to: '/dashboard/settings' },
|
||||
{ from: 'device.html', to: '/dashboard/devices/edit' },
|
||||
{ from: 'devices.html', to: '/dashboard/devices' },
|
||||
{ from: 'dlnaprofile.html', to: '/dashboard/dlna/profiles/edit' },
|
||||
{ from: 'dlnaprofiles.html', to: '/dashboard/dlna/profiles' },
|
||||
{ from: 'dlnaprofile.html', to: '/dashboard/dlna' },
|
||||
{ from: 'dlnaprofiles.html', to: '/dashboard/dlna' },
|
||||
{ from: 'dlnasettings.html', to: '/dashboard/dlna' },
|
||||
{ from: 'edititemmetadata.html', to: '/metadata' },
|
||||
{ from: 'encodingsettings.html', to: '/dashboard/playback/transcoding' },
|
||||
|
33
src/apps/dashboard/routes/dlna.tsx
Normal file
33
src/apps/dashboard/routes/dlna.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import Alert from '@mui/material/Alert/Alert';
|
||||
import Box from '@mui/material/Box/Box';
|
||||
import Button from '@mui/material/Button/Button';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import Page from 'components/Page';
|
||||
import globalize from 'scripts/globalize';
|
||||
|
||||
const DlnaPage = () => (
|
||||
<Page
|
||||
id='dlnaSettingsPage'
|
||||
title='DLNA'
|
||||
className='mainAnimatedPage type-interior'
|
||||
>
|
||||
<div className='content-primary'>
|
||||
<h2>DLNA</h2>
|
||||
<Alert severity='info'>
|
||||
<Box sx={{ marginBottom: 2 }}>
|
||||
{globalize.translate('DlnaMovedMessage')}
|
||||
</Box>
|
||||
<Button
|
||||
component={Link}
|
||||
to='/dashboard/plugins/add?name=DLNA&guid=33eba9cd7da14720967fdd7dae7b74a1'
|
||||
>
|
||||
{globalize.translate('GetThePlugin')}
|
||||
</Button>
|
||||
</Alert>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
|
||||
export default DlnaPage;
|
@ -1,23 +1,13 @@
|
||||
import Alert from '@mui/material/Alert/Alert';
|
||||
import Box from '@mui/material/Box/Box';
|
||||
import Button from '@mui/material/Button/Button';
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import Page from 'components/Page';
|
||||
import globalize from 'scripts/globalize';
|
||||
|
||||
const PluginLink = () => (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `<a
|
||||
is='emby-linkbutton'
|
||||
class='button-link'
|
||||
href='#/dashboard/plugins/add?name=Webhook&guid=71552a5a5c5c4350a2aeebe451a30173'
|
||||
>
|
||||
${globalize.translate('GetThePlugin')}
|
||||
</a>`
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const Notifications = () => (
|
||||
const NotificationsPage = () => (
|
||||
<Page
|
||||
id='notificationSettingPage'
|
||||
title={globalize.translate('Notifications')}
|
||||
@ -25,12 +15,20 @@ const Notifications = () => (
|
||||
>
|
||||
<div className='content-primary'>
|
||||
<h2>{globalize.translate('Notifications')}</h2>
|
||||
<p>
|
||||
{globalize.translate('NotificationsMovedMessage')}
|
||||
</p>
|
||||
<PluginLink />
|
||||
|
||||
<Alert severity='info'>
|
||||
<Box sx={{ marginBottom: 2 }}>
|
||||
{globalize.translate('NotificationsMovedMessage')}
|
||||
</Box>
|
||||
<Button
|
||||
component={Link}
|
||||
to='/dashboard/plugins/add?name=Webhook&guid=71552a5a5c5c4350a2aeebe451a30173'
|
||||
>
|
||||
{globalize.translate('GetThePlugin')}
|
||||
</Button>
|
||||
</Alert>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
|
||||
export default Notifications;
|
||||
export default NotificationsPage;
|
||||
|
@ -1,584 +0,0 @@
|
||||
<div id="dlnaProfilePage" data-role="page" class="page type-interior dlnaPage withTabs">
|
||||
<div data-role="content">
|
||||
<div class="content-primary">
|
||||
<form class="dlnaProfileForm" style="max-width: 650px;">
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${HeaderProfileInformation}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div data-role="controlgroup" data-type="horizontal" data-mini="true">
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioInfo" data-value="tabInfo">${ButtonInfo}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioDirectPlay" data-value="tabDirectPlayProfiles">${TabDirectPlay}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioTranscoding" data-value="tabTranscodingProfiles">${Transcoding}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioContainers" data-value="tabContainerProfiles">${TabContainers}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioCodecs" data-value="tabCodecProfiles">${TabCodecs}</a>
|
||||
<a href="#" is="emby-linkbutton" data-role="button" class="radioTabButton" id="radioMediaProfiles" data-value="tabMediaProfiles">${TabResponses}</a>
|
||||
</div>
|
||||
<br />
|
||||
<div class="tabContent tabInfo">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtName" required="required" label="${LabelName}" />
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectUser" label="${LabelUserLibrary}"></select>
|
||||
<div class="fieldDescription">${LabelUserLibraryHelp}</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="checkboxListLabel">${LabelSupportedMediaTypes}</h3>
|
||||
<div class="checkboxList paperList checkboxList-paperList">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkAudio" data-value="Audio" class="chkMediaType" />
|
||||
<span>${Audio}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkPhoto" data-value="Photo" class="chkMediaType" />
|
||||
<span>${Photo}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkVideo" data-value="Video" class="chkMediaType" />
|
||||
<span>${Video}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtMaxAllowedBitrate" pattern="[0-9]*" min="1" label="${LabelMaxStreamingBitrate}" />
|
||||
<div class="fieldDescription">${LabelMaxStreamingBitrateHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtMusicStreamingTranscodingBitrate" pattern="[0-9]*" min="1" label="${LabelMusicStreamingTranscodingBitrate}" />
|
||||
<div class="fieldDescription">${LabelMusicStreamingTranscodingBitrateHelp}</div>
|
||||
</div>
|
||||
<div style="display:none;">
|
||||
<label for="chkIgnoreTranscodeByteRangeRequests">${OptionIgnoreTranscodeByteRangeRequests}</label>
|
||||
<input type="checkbox" id="chkIgnoreTranscodeByteRangeRequests" data-mini="true" />
|
||||
<div class="fieldDescription">${OptionIgnoreTranscodeByteRangeRequestsHelp}</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${HeaderIdentification}">
|
||||
<div class="collapseContent">
|
||||
<h3>${HeaderIdentificationCriteriaHelp}</h3>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdFriendlyName" label="${LabelFriendlyName}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdManufacturer" label="${LabelManufacturer}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdManufacturerUrl" label="${LabelManufacturerUrl}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdModelName" label="${LabelModelName}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdModelNumber" label="${LabelModelNumber}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdModelDesription" label="${LabelModelDescription}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdModelUrl" label="${LabelModelUrl}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdSerialNumber" label="${LabelSerialNumber}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdDeviceDescription" label="${LabelDeviceDescription}" />
|
||||
<div class="fieldDescription">${LabelIdentificationFieldHelp}</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2 style="vertical-align:middle;display:inline-block;">${HeaderHttpHeaders}</h2>
|
||||
<button is="emby-button" type="button" class="fab btnAddIdentificationHttpHeader submit sectionTitleButton" style="margin-left:1em;" title="${Add}">
|
||||
<span class="material-icons add" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="httpHeaderIdentificationList"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${Display}">
|
||||
<div class="collapseContent">
|
||||
<br />
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkRequiresPlainFolders" />
|
||||
<span>${OptionPlainStorageFolders}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionPlainStorageFoldersHelp}</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkRequiresPlainVideoItems" />
|
||||
<span>${OptionPlainVideoItems}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionPlainVideoItemsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${HeaderImageSettings}">
|
||||
<div class="collapseContent">
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableAlbumArtInDidl" data-mini="true" />
|
||||
<span>${LabelEmbedAlbumArtDidl}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEmbedAlbumArtDidlHelp}</div>
|
||||
</div>
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableSingleImageLimit" data-mini="true" />
|
||||
<span>${LabelEnableSingleImageInDidlLimit}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableSingleImageInDidlLimitHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input type="text" is="emby-input" id="txtAlbumArtPn" label="${LabelAlbumArtPN}" />
|
||||
<div class="fieldDescription">${LabelAlbumArtHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input type="number" is="emby-input" id="txtAlbumArtMaxWidth" pattern="[0-9]*" min="1" label="${LabelAlbumArtMaxWidth}" />
|
||||
<div class="fieldDescription">${LabelAlbumArtMaxResHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input type="number" is="emby-input" id="txtAlbumArtMaxHeight" pattern="[0-9]*" min="1" label="${LabelAlbumArtMaxHeight}" />
|
||||
<div class="fieldDescription">${LabelAlbumArtMaxResHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input type="number" is="emby-input" id="txtIconMaxWidth" pattern="[0-9]*" min="1" label="${LabelIconMaxWidth}" />
|
||||
<div class="fieldDescription">${LabelIconMaxResHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input type="number" is="emby-input" id="txtIconMaxHeight" pattern="[0-9]*" min="1" label="${LabelIconMaxHeight}" />
|
||||
<div class="fieldDescription">${LabelIconMaxResHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${HeaderServerSettings}">
|
||||
<div class="collapseContent">
|
||||
<p>${HeaderProfileServerSettingsHelp}</p>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoFriendlyName" label="${LabelFriendlyName}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoManufacturer" label="${LabelManufacturer}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoManufacturerUrl" label="${LabelManufacturerUrl}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoModelName" label="${LabelModelName}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoModelNumber" label="${LabelModelNumber}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoModelDesription" label="${LabelModelDescription}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoModelUrl" label="${LabelModelUrl}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtInfoSerialNumber" label="${LabelSerialNumber}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtProtocolInfo" label="${LabelProtocolInfo}" />
|
||||
<div class="fieldDescription">${LabelProtocolInfoHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtXDlnaCap" label="${LabelXDlnaCap}" />
|
||||
<div class="fieldDescription">${LabelXDlnaCapHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtXDlnaDoc" label="${LabelXDlnaDoc}" />
|
||||
<div class="fieldDescription">${LabelXDlnaDocHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtSonyAggregationFlags" label="${LabelSonyAggregationFlags}" />
|
||||
<div class="fieldDescription">${LabelSonyAggregationFlagsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${HeaderSubtitleProfiles}">
|
||||
<div class="collapseContent">
|
||||
<p>${HeaderSubtitleProfilesHelp}</p>
|
||||
<button is="emby-button" type="button" class="raised submit block btnAddSubtitleProfile">
|
||||
<span>${Add}</span>
|
||||
</button>
|
||||
<div class="subtitleProfileList"></div>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<div is="emby-collapse" title="${HeaderXmlSettings}">
|
||||
<div class="collapseContent">
|
||||
<div>
|
||||
<h2 style="vertical-align:middle;display:inline-block;">${HeaderXmlDocumentAttributes}</h2>
|
||||
<button is="emby-button" type="button" class="fab btnAddXmlDocumentAttribute submit sectionTitleButton" style="margin-left:1em;" title="${Add}">
|
||||
<span class="material-icons add" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="xmlDocumentAttributeList"></div>
|
||||
<div class="fieldDescription">${XmlDocumentAttributeListHelp}</div>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabContent tabDirectPlayProfiles">
|
||||
<p>${HeaderDirectPlayProfileHelp}</p>
|
||||
<button is="emby-button" class="raised submit block btnAddDirectPlayProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
|
||||
<br />
|
||||
<div class="directPlayProfiles"></div>
|
||||
</div>
|
||||
<div class="tabContent tabTranscodingProfiles">
|
||||
<p>${HeaderTranscodingProfileHelp}</p>
|
||||
<button is="emby-button" class="raised submit block btnAddTranscodingProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
|
||||
<br />
|
||||
<div class="transcodingProfiles"></div>
|
||||
</div>
|
||||
<div class="tabContent tabContainerProfiles">
|
||||
<p>${HeaderContainerProfileHelp}</p>
|
||||
<button is="emby-button" class="raised submit block btnAddContainerProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
|
||||
<br />
|
||||
<div class="containerProfiles"></div>
|
||||
</div>
|
||||
<div class="tabContent tabCodecProfiles">
|
||||
<p>${HeaderCodecProfileHelp}</p>
|
||||
<button is="emby-button" class="raised submit block btnAddCodecProfile" type="button" data-icon="plus">${New}</button>
|
||||
<br />
|
||||
<div class="codecProfiles"></div>
|
||||
</div>
|
||||
<div class="tabContent tabMediaProfiles">
|
||||
<p>${HeaderResponseProfileHelp}</p>
|
||||
<button is="emby-button" class="raised submit block btnAddResponseProfile" type="button" data-mini="true" data-icon="plus">${New}</button>
|
||||
<br />
|
||||
<div class="mediaProfiles"></div>
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="button-cancel raised block" onclick="Dashboard.navigate('dashboard/dlna/profiles');">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div data-role="popup" id="popupEditDirectPlayProfile" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="editDirectPlayProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderDirectPlayProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div class="selectContainer">
|
||||
<select id="selectDirectPlayProfileType" name="selectDirectPlayProfileType" is="emby-select" label="${LabelType}">
|
||||
<option value="Audio">${Audio}</option>
|
||||
<option value="Photo">${Photo}</option>
|
||||
<option value="Video">${Video}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtDirectPlayContainer" label="${LabelProfileContainer}" />
|
||||
<div class="fieldDescription">${LabelProfileContainersHelp}</div>
|
||||
</div>
|
||||
<div id="fldDirectPlayVideoCodec" style="margin: 1em 0;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtDirectPlayVideoCodec" label="${LabelProfileVideoCodecs}" />
|
||||
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldDirectPlayAudioCodec" style="margin: 1em 0 2em;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtDirectPlayAudioCodec" label="${LabelProfileAudioCodecs}" />
|
||||
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="transcodingProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="transcodingProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderTranscodingProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div data-role="controlgroup" data-type="horizontal" data-mini="true">
|
||||
<input type="radio" name="radioTranscodingTab" class="radioTabButton" id="radioTranscodingBasics" value="tabTranscodingBasics">
|
||||
<label for="radioTranscodingBasics">${ButtonInfo}</label>
|
||||
<input type="radio" name="radioTranscodingTab" class="radioTabButton" id="radioTranscodingAdvanced" value="tabTranscodingAdvanced">
|
||||
<label for="radioTranscodingAdvanced">${TabAdvanced}</label>
|
||||
</div>
|
||||
<br />
|
||||
<div class="tabContent tabTranscodingBasics" style="display: none;">
|
||||
<div class="selectContainer">
|
||||
<select id="selectTranscodingProfileType" name="selectTranscodingProfileType" is="emby-select" label="${LabelType}">
|
||||
<option value="Audio">${Audio}</option>
|
||||
<option value="Photo">${Photo}</option>
|
||||
<option value="Video">${Video}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="fldTranscodingProtocol" style="margin: 1em 0;">
|
||||
<div class="selectContainer">
|
||||
<select id="selectTranscodingProtocol" name="selectTranscodingProtocol" is="emby-select" label="${LabelProtocol}">
|
||||
<option value="Http">${OptionProtocolHttp}</option>
|
||||
<option value="Hls">${OptionProtocolHls}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtTranscodingContainer" label="${LabelProfileContainer}"; required="required" />
|
||||
</div>
|
||||
<div id="fldTranscodingVideoCodec" style="margin: 1em 0;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtTranscodingVideoCodec" label="${LabelVideoCodec}" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldTranscodingAudioCodec" style="margin: 1em 0;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtTranscodingAudioCodec" label="${LabelAudioCodec}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabContent tabTranscodingAdvanced" style="display: none;">
|
||||
<div id="fldEnableMpegtsM2TsMode" style="margin: 1em 0;">
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEnableMpegtsM2TsMode" />
|
||||
<span>${OptionEnableM2tsMode}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionEnableM2tsModeHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldEstimateContentLength" style="margin: 1em 0;">
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkEstimateContentLength" />
|
||||
<span>${OptionEstimateContentLength}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldReportByteRangeRequests" style="margin: 1em 0;">
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input is="emby-checkbox" type="checkbox" id="chkReportByteRangeRequests" />
|
||||
<span>${OptionReportByteRangeSeekingWhenTranscoding}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${OptionReportByteRangeSeekingWhenTranscodingHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="containerProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="containerProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderContainerProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<p>${HeaderContainerProfileHelp}</p>
|
||||
<div class="tabContent tabContainerBasics">
|
||||
<div class="selectContainer">
|
||||
<select id="selectContainerProfileType" name="selectContainerProfileType" is="emby-select" label="${LabelType}">
|
||||
<option value="Audio">${Audio}</option>
|
||||
<option value="Photo">${Photo}</option>
|
||||
<option value="Video">${Video}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtContainerProfileContainer" label="${LabelProfileContainer}" />
|
||||
<div class="fieldDescription">${LabelProfileContainersHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabContent tabContainerConditions" style="display: none;"></div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="codecProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="codecProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderCodecProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<p>${HeaderCodecProfileHelp}</p>
|
||||
<div class="selectContainer">
|
||||
<select id="selectCodecProfileType" name="selectCodecProfileType" is="emby-select" label="${LabelType}">
|
||||
<option value="Video">${Video}</option>
|
||||
<option value="VideoAudio">${VideoAudio}</option>
|
||||
<option value="Audio">${Audio}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtCodecProfileCodec" label="${LabelProfileCodecs}" />
|
||||
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="responseProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="editResponseProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderResponseProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div class="selectContainer">
|
||||
<select id="selectResponseProfileType" name="selectResponseProfileType" is="emby-select" label="${LabelType}">
|
||||
<option value="Audio">${Audio}</option>
|
||||
<option value="Photo">${Photo}</option>
|
||||
<option value="Video">${Video}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtResponseProfileContainer" label="${LabelProfileContainer}" />
|
||||
<div class="fieldDescription">${LabelProfileContainersHelp}</div>
|
||||
</div>
|
||||
<div id="fldResponseProfileVideoCodec" style="margin: 1em 0;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtResponseProfileVideoCodec" label="${LabelProfileVideoCodecs}" />
|
||||
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldResponseProfileAudioCodec" style="margin: 1em 0 2em;">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtResponseProfileAudioCodec" label="${LabelProfileAudioCodecs}" />
|
||||
<div class="fieldDescription">${LabelProfileCodecsHelp}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="identificationHeaderPopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="identificationHeaderForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderIdentificationHeader}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdentificationHeaderName" label="${LabelName}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtIdentificationHeaderValue" label="${LabelValue}" />
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select id="selectMatchType" name="selectMatchType" is="emby-select" label="${LabelMatchType}">
|
||||
<option value="Equals">${OptionEquals}</option>
|
||||
<option value="Regex">${OptionRegex}</option>
|
||||
<option value="Substring">${OptionSubstring}</option>
|
||||
</select>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="xmlAttributePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="xmlAttributeForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderXmlDocumentAttribute}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtXmlAttributeName" label="${LabelName}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtXmlAttributeValue" label="${LabelValue}" />
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div data-role="popup" id="subtitleProfilePopup" class="dialog dialog-fixedSize dialog-medium hide" style="position: fixed; top: 10%;">
|
||||
<form class="subtitleProfileForm" style="padding:1em;">
|
||||
<div class="ui-bar-a">
|
||||
<h3 class="sectionTitle">${HeaderSubtitleProfile}</h3>
|
||||
</div>
|
||||
<div data-role="content">
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="text" id="txtSubtitleProfileFormat" label="${LabelFormat}" />
|
||||
<div class="fieldDescription">${LabelSubtitleFormatHelp}</div>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select id="selectSubtitleProfileMethod" name="selectSubtitleProfileMethod" is="emby-select" label="${LabelMethod}">
|
||||
<option value="Embed">${OptionEmbedSubtitles}</option>
|
||||
<option value="External">${OptionExternallyDownloaded}</option>
|
||||
<option value="Hls">${OptionHlsSegmentedSubtitles}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select id="selectSubtitleProfileDidlMode" name="selectSubtitleProfileDidlMode" is="emby-select" label="${LabelDidlMode}">
|
||||
<option value="">${OptionResElement}</option>
|
||||
<option value="CaptionInfoEx">${OptionCaptionInfoExSamsung}</option>
|
||||
</select>
|
||||
</div>
|
||||
<p>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block" data-icon="check" data-mini="true">
|
||||
<span>${ButtonOk}</span>
|
||||
</button>
|
||||
<button is="emby-button" type="button" class="raised button-cancel block" data-icon="delete" onclick="$(this).parents('.dialog').addClass('hide');" data-mini="true">
|
||||
<span>${ButtonCancel}</span>
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
@ -1,830 +0,0 @@
|
||||
import escapeHtml from 'escape-html';
|
||||
import 'jquery';
|
||||
import loading from '../../../components/loading/loading';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import '../../../elements/emby-select/emby-select';
|
||||
import '../../../elements/emby-button/emby-button';
|
||||
import '../../../elements/emby-input/emby-input';
|
||||
import '../../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../../components/listview/listview.scss';
|
||||
import Dashboard from '../../../utils/dashboard';
|
||||
import toast from '../../../components/toast/toast';
|
||||
import { getParameterByName } from '../../../utils/url.ts';
|
||||
|
||||
function loadProfile(page) {
|
||||
loading.show();
|
||||
const promise1 = getProfile();
|
||||
const promise2 = ApiClient.getUsers();
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
currentProfile = responses[0];
|
||||
renderProfile(page, currentProfile, responses[1]);
|
||||
loading.hide();
|
||||
});
|
||||
}
|
||||
|
||||
function getProfile() {
|
||||
const id = getParameterByName('id');
|
||||
const url = id ? 'Dlna/Profiles/' + id : 'Dlna/Profiles/Default';
|
||||
return ApiClient.getJSON(ApiClient.getUrl(url));
|
||||
}
|
||||
|
||||
function renderProfile(page, profile, users) {
|
||||
$('#txtName', page).val(profile.Name);
|
||||
$('.chkMediaType', page).each(function () {
|
||||
this.checked = (profile.SupportedMediaTypes || '').split(',').indexOf(this.getAttribute('data-value')) != -1;
|
||||
});
|
||||
$('#chkEnableAlbumArtInDidl', page).prop('checked', profile.EnableAlbumArtInDidl);
|
||||
$('#chkEnableSingleImageLimit', page).prop('checked', profile.EnableSingleAlbumArtLimit);
|
||||
renderXmlDocumentAttributes(page, profile.XmlRootAttributes || []);
|
||||
const idInfo = profile.Identification || {};
|
||||
renderIdentificationHeaders(page, idInfo.Headers || []);
|
||||
renderSubtitleProfiles(page, profile.SubtitleProfiles || []);
|
||||
$('#txtInfoFriendlyName', page).val(profile.FriendlyName || '');
|
||||
$('#txtInfoModelName', page).val(profile.ModelName || '');
|
||||
$('#txtInfoModelNumber', page).val(profile.ModelNumber || '');
|
||||
$('#txtInfoModelDescription', page).val(profile.ModelDescription || '');
|
||||
$('#txtInfoModelUrl', page).val(profile.ModelUrl || '');
|
||||
$('#txtInfoManufacturer', page).val(profile.Manufacturer || '');
|
||||
$('#txtInfoManufacturerUrl', page).val(profile.ManufacturerUrl || '');
|
||||
$('#txtInfoSerialNumber', page).val(profile.SerialNumber || '');
|
||||
$('#txtIdFriendlyName', page).val(idInfo.FriendlyName || '');
|
||||
$('#txtIdModelName', page).val(idInfo.ModelName || '');
|
||||
$('#txtIdModelNumber', page).val(idInfo.ModelNumber || '');
|
||||
$('#txtIdModelDescription', page).val(idInfo.ModelDescription || '');
|
||||
$('#txtIdModelUrl', page).val(idInfo.ModelUrl || '');
|
||||
$('#txtIdManufacturer', page).val(idInfo.Manufacturer || '');
|
||||
$('#txtIdManufacturerUrl', page).val(idInfo.ManufacturerUrl || '');
|
||||
$('#txtIdSerialNumber', page).val(idInfo.SerialNumber || '');
|
||||
$('#txtIdDeviceDescription', page).val(idInfo.DeviceDescription || '');
|
||||
$('#txtAlbumArtPn', page).val(profile.AlbumArtPn || '');
|
||||
$('#txtAlbumArtMaxWidth', page).val(profile.MaxAlbumArtWidth || '');
|
||||
$('#txtAlbumArtMaxHeight', page).val(profile.MaxAlbumArtHeight || '');
|
||||
$('#txtIconMaxWidth', page).val(profile.MaxIconWidth || '');
|
||||
$('#txtIconMaxHeight', page).val(profile.MaxIconHeight || '');
|
||||
$('#chkIgnoreTranscodeByteRangeRequests', page).prop('checked', profile.IgnoreTranscodeByteRangeRequests);
|
||||
$('#txtMaxAllowedBitrate', page).val(profile.MaxStreamingBitrate || '');
|
||||
$('#txtMusicStreamingTranscodingBitrate', page).val(profile.MusicStreamingTranscodingBitrate || '');
|
||||
$('#chkRequiresPlainFolders', page).prop('checked', profile.RequiresPlainFolders);
|
||||
$('#chkRequiresPlainVideoItems', page).prop('checked', profile.RequiresPlainVideoItems);
|
||||
$('#txtProtocolInfo', page).val(profile.ProtocolInfo || '');
|
||||
$('#txtXDlnaCap', page).val(profile.XDlnaCap || '');
|
||||
$('#txtXDlnaDoc', page).val(profile.XDlnaDoc || '');
|
||||
$('#txtSonyAggregationFlags', page).val(profile.SonyAggregationFlags || '');
|
||||
profile.DirectPlayProfiles = profile.DirectPlayProfiles || [];
|
||||
profile.TranscodingProfiles = profile.TranscodingProfiles || [];
|
||||
profile.ContainerProfiles = profile.ContainerProfiles || [];
|
||||
profile.CodecProfiles = profile.CodecProfiles || [];
|
||||
profile.ResponseProfiles = profile.ResponseProfiles || [];
|
||||
const usersHtml = '<option></option>' + users.map(function (u) {
|
||||
return '<option value="' + u.Id + '">' + escapeHtml(u.Name) + '</option>';
|
||||
}).join('');
|
||||
$('#selectUser', page).html(usersHtml).val(profile.UserId || '');
|
||||
renderSubProfiles(page, profile);
|
||||
}
|
||||
|
||||
function renderIdentificationHeaders(page, headers) {
|
||||
let index = 0;
|
||||
const html = '<div class="paperList">' + headers.map(function (h) {
|
||||
let li = '<div class="listItem">';
|
||||
li += '<span class="material-icons listItemIcon info" aria-hidden="true"></span>';
|
||||
li += '<div class="listItemBody">';
|
||||
li += '<h3 class="listItemBodyText">' + escapeHtml(h.Name + ': ' + (h.Value || '')) + '</h3>';
|
||||
li += '<div class="listItemBodyText secondary">' + escapeHtml(h.Match || '') + '</div>';
|
||||
li += '</div>';
|
||||
li += '<button type="button" is="paper-icon-button-light" class="btnDeleteIdentificationHeader listItemButton" data-index="' + index + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
li += '</div>';
|
||||
index++;
|
||||
return li;
|
||||
}).join('') + '</div>';
|
||||
const elem = $('.httpHeaderIdentificationList', page).html(html).trigger('create');
|
||||
$('.btnDeleteIdentificationHeader', elem).on('click', function () {
|
||||
const itemIndex = parseInt(this.getAttribute('data-index'), 10);
|
||||
currentProfile.Identification.Headers.splice(itemIndex, 1);
|
||||
renderIdentificationHeaders(page, currentProfile.Identification.Headers);
|
||||
});
|
||||
}
|
||||
|
||||
function openPopup(elem) {
|
||||
elem.classList.remove('hide');
|
||||
}
|
||||
|
||||
function closePopup(elem) {
|
||||
elem.classList.add('hide');
|
||||
}
|
||||
|
||||
function editIdentificationHeader(page, header) {
|
||||
isSubProfileNew = header == null;
|
||||
header = header || {};
|
||||
currentSubProfile = header;
|
||||
const popup = $('#identificationHeaderPopup', page);
|
||||
$('#txtIdentificationHeaderName', popup).val(header.Name || '');
|
||||
$('#txtIdentificationHeaderValue', popup).val(header.Value || '');
|
||||
$('#selectMatchType', popup).val(header.Match || 'Equals');
|
||||
openPopup(popup[0]);
|
||||
}
|
||||
|
||||
function saveIdentificationHeader(page) {
|
||||
currentSubProfile.Name = $('#txtIdentificationHeaderName', page).val();
|
||||
currentSubProfile.Value = $('#txtIdentificationHeaderValue', page).val();
|
||||
currentSubProfile.Match = $('#selectMatchType', page).val();
|
||||
|
||||
if (isSubProfileNew) {
|
||||
currentProfile.Identification = currentProfile.Identification || {};
|
||||
currentProfile.Identification.Headers = currentProfile.Identification.Headers || [];
|
||||
currentProfile.Identification.Headers.push(currentSubProfile);
|
||||
}
|
||||
|
||||
renderIdentificationHeaders(page, currentProfile.Identification.Headers);
|
||||
currentSubProfile = null;
|
||||
closePopup($('#identificationHeaderPopup', page)[0]);
|
||||
}
|
||||
|
||||
function renderXmlDocumentAttributes(page, attribute) {
|
||||
const html = '<div class="paperList">' + attribute.map(function (h) {
|
||||
let li = '<div class="listItem">';
|
||||
li += '<span class="material-icons listItemIcon info" aria-hidden="true"></span>';
|
||||
li += '<div class="listItemBody">';
|
||||
li += '<h3 class="listItemBodyText">' + escapeHtml(h.Name + ' = ' + (h.Value || '')) + '</h3>';
|
||||
li += '</div>';
|
||||
li += '<button type="button" is="paper-icon-button-light" class="btnDeleteXmlAttribute listItemButton" data-index="0"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
li += '</div>';
|
||||
return li;
|
||||
}).join('') + '</div>';
|
||||
const elem = $('.xmlDocumentAttributeList', page).html(html).trigger('create');
|
||||
$('.btnDeleteXmlAttribute', elem).on('click', function () {
|
||||
const itemIndex = parseInt(this.getAttribute('data-index'), 10);
|
||||
currentProfile.XmlRootAttributes.splice(itemIndex, 1);
|
||||
renderXmlDocumentAttributes(page, currentProfile.XmlRootAttributes);
|
||||
});
|
||||
}
|
||||
|
||||
function editXmlDocumentAttribute(page, attribute) {
|
||||
isSubProfileNew = attribute == null;
|
||||
attribute = attribute || {};
|
||||
currentSubProfile = attribute;
|
||||
const popup = $('#xmlAttributePopup', page);
|
||||
$('#txtXmlAttributeName', popup).val(attribute.Name || '');
|
||||
$('#txtXmlAttributeValue', popup).val(attribute.Value || '');
|
||||
openPopup(popup[0]);
|
||||
}
|
||||
|
||||
function saveXmlDocumentAttribute(page) {
|
||||
currentSubProfile.Name = $('#txtXmlAttributeName', page).val();
|
||||
currentSubProfile.Value = $('#txtXmlAttributeValue', page).val();
|
||||
|
||||
if (isSubProfileNew) {
|
||||
currentProfile.XmlRootAttributes.push(currentSubProfile);
|
||||
}
|
||||
|
||||
renderXmlDocumentAttributes(page, currentProfile.XmlRootAttributes);
|
||||
currentSubProfile = null;
|
||||
closePopup($('#xmlAttributePopup', page)[0]);
|
||||
}
|
||||
|
||||
function renderSubtitleProfiles(page, profiles) {
|
||||
let index = 0;
|
||||
const html = '<div class="paperList">' + profiles.map(function (h) {
|
||||
let li = '<div class="listItem lnkEditSubProfile" data-index="' + index + '">';
|
||||
li += '<span class="material-icons listItemIcon info" aria-hidden="true"></span>';
|
||||
li += '<div class="listItemBody">';
|
||||
li += '<h3 class="listItemBodyText">' + escapeHtml(h.Format || '') + '</h3>';
|
||||
li += '</div>';
|
||||
li += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-index="' + index + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
li += '</div>';
|
||||
index++;
|
||||
return li;
|
||||
}).join('') + '</div>';
|
||||
const elem = $('.subtitleProfileList', page).html(html).trigger('create');
|
||||
$('.btnDeleteProfile', elem).on('click', function () {
|
||||
const itemIndex = parseInt(this.getAttribute('data-index'), 10);
|
||||
currentProfile.SubtitleProfiles.splice(itemIndex, 1);
|
||||
renderSubtitleProfiles(page, currentProfile.SubtitleProfiles);
|
||||
});
|
||||
$('.lnkEditSubProfile', elem).on('click', function () {
|
||||
const itemIndex = parseInt(this.getAttribute('data-index'), 10);
|
||||
editSubtitleProfile(page, currentProfile.SubtitleProfiles[itemIndex]);
|
||||
});
|
||||
}
|
||||
|
||||
function editSubtitleProfile(page, profile) {
|
||||
isSubProfileNew = profile == null;
|
||||
profile = profile || {};
|
||||
currentSubProfile = profile;
|
||||
const popup = $('#subtitleProfilePopup', page);
|
||||
$('#txtSubtitleProfileFormat', popup).val(profile.Format || '');
|
||||
$('#selectSubtitleProfileMethod', popup).val(profile.Method || '');
|
||||
$('#selectSubtitleProfileDidlMode', popup).val(profile.DidlMode || '');
|
||||
openPopup(popup[0]);
|
||||
}
|
||||
|
||||
function saveSubtitleProfile(page) {
|
||||
currentSubProfile.Format = $('#txtSubtitleProfileFormat', page).val();
|
||||
currentSubProfile.Method = $('#selectSubtitleProfileMethod', page).val();
|
||||
currentSubProfile.DidlMode = $('#selectSubtitleProfileDidlMode', page).val();
|
||||
|
||||
if (isSubProfileNew) {
|
||||
currentProfile.SubtitleProfiles.push(currentSubProfile);
|
||||
}
|
||||
|
||||
renderSubtitleProfiles(page, currentProfile.SubtitleProfiles);
|
||||
currentSubProfile = null;
|
||||
closePopup($('#subtitleProfilePopup', page)[0]);
|
||||
}
|
||||
|
||||
function renderSubProfiles(page, profile) {
|
||||
renderDirectPlayProfiles(page, profile.DirectPlayProfiles);
|
||||
renderTranscodingProfiles(page, profile.TranscodingProfiles);
|
||||
renderContainerProfiles(page, profile.ContainerProfiles);
|
||||
renderCodecProfiles(page, profile.CodecProfiles);
|
||||
renderResponseProfiles(page, profile.ResponseProfiles);
|
||||
}
|
||||
|
||||
function saveDirectPlayProfile(page) {
|
||||
currentSubProfile.Type = $('#selectDirectPlayProfileType', page).val();
|
||||
currentSubProfile.Container = $('#txtDirectPlayContainer', page).val();
|
||||
currentSubProfile.AudioCodec = $('#txtDirectPlayAudioCodec', page).val();
|
||||
currentSubProfile.VideoCodec = $('#txtDirectPlayVideoCodec', page).val();
|
||||
|
||||
if (isSubProfileNew) {
|
||||
currentProfile.DirectPlayProfiles.push(currentSubProfile);
|
||||
}
|
||||
|
||||
renderSubProfiles(page, currentProfile);
|
||||
currentSubProfile = null;
|
||||
closePopup($('#popupEditDirectPlayProfile', page)[0]);
|
||||
}
|
||||
|
||||
function renderDirectPlayProfiles(page, profiles) {
|
||||
let html = '';
|
||||
html += '<ul data-role="listview" data-inset="true" data-split-icon="delete">';
|
||||
let currentType;
|
||||
|
||||
for (const [index, profile] of profiles.entries()) {
|
||||
if (profile.Type !== currentType) {
|
||||
html += '<li data-role="list-divider">' + profile.Type + '</li>';
|
||||
currentType = profile.Type;
|
||||
}
|
||||
|
||||
html += '<div>';
|
||||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + index + '">';
|
||||
html += '<p>' + globalize.translate('ValueContainer', profile.Container || allText) + '</p>';
|
||||
|
||||
if (profile.Type == 'Video') {
|
||||
html += '<p>' + globalize.translate('ValueVideoCodec', profile.VideoCodec || allText) + '</p>';
|
||||
html += '<p>' + globalize.translate('ValueAudioCodec', profile.AudioCodec || allText) + '</p>';
|
||||
} else if (profile.Type == 'Audio') {
|
||||
html += '<p>' + globalize.translate('ValueCodec', profile.AudioCodec || allText) + '</p>';
|
||||
}
|
||||
|
||||
html += '</a>';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-profileindex="' + index + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</ul>';
|
||||
const elem = $('.directPlayProfiles', page).html(html).trigger('create');
|
||||
$('.btnDeleteProfile', elem).on('click', function () {
|
||||
const index = this.getAttribute('data-profileindex');
|
||||
deleteDirectPlayProfile(page, index);
|
||||
});
|
||||
$('.lnkEditSubProfile', elem).on('click', function () {
|
||||
const index = parseInt(this.getAttribute('data-profileindex'), 10);
|
||||
editDirectPlayProfile(page, currentProfile.DirectPlayProfiles[index]);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteDirectPlayProfile(page, index) {
|
||||
currentProfile.DirectPlayProfiles.splice(index, 1);
|
||||
renderDirectPlayProfiles(page, currentProfile.DirectPlayProfiles);
|
||||
}
|
||||
|
||||
function editDirectPlayProfile(page, directPlayProfile) {
|
||||
isSubProfileNew = directPlayProfile == null;
|
||||
directPlayProfile = directPlayProfile || {};
|
||||
currentSubProfile = directPlayProfile;
|
||||
const popup = $('#popupEditDirectPlayProfile', page);
|
||||
$('#selectDirectPlayProfileType', popup).val(directPlayProfile.Type || 'Video').trigger('change');
|
||||
$('#txtDirectPlayContainer', popup).val(directPlayProfile.Container || '');
|
||||
$('#txtDirectPlayAudioCodec', popup).val(directPlayProfile.AudioCodec || '');
|
||||
$('#txtDirectPlayVideoCodec', popup).val(directPlayProfile.VideoCodec || '');
|
||||
openPopup(popup[0]);
|
||||
}
|
||||
|
||||
function renderTranscodingProfiles(page, profiles) {
|
||||
let html = '';
|
||||
html += '<ul data-role="listview" data-inset="true" data-split-icon="delete">';
|
||||
let currentType;
|
||||
|
||||
for (let i = 0, length = profiles.length; i < length; i++) {
|
||||
const profile = profiles[i];
|
||||
|
||||
if (profile.Type !== currentType) {
|
||||
html += '<li data-role="list-divider">' + profile.Type + '</li>';
|
||||
currentType = profile.Type;
|
||||
}
|
||||
|
||||
html += '<div>';
|
||||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
||||
html += '<p>Protocol: ' + (profile.Protocol || 'Http') + '</p>';
|
||||
html += '<p>' + globalize.translate('ValueContainer', profile.Container || allText) + '</p>';
|
||||
|
||||
if (profile.Type == 'Video') {
|
||||
html += '<p>' + globalize.translate('ValueVideoCodec', profile.VideoCodec || allText) + '</p>';
|
||||
html += '<p>' + globalize.translate('ValueAudioCodec', profile.AudioCodec || allText) + '</p>';
|
||||
} else if (profile.Type == 'Audio') {
|
||||
html += '<p>' + globalize.translate('ValueCodec', profile.AudioCodec || allText) + '</p>';
|
||||
}
|
||||
|
||||
html += '</a>';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-profileindex="' + i + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</ul>';
|
||||
const elem = $('.transcodingProfiles', page).html(html).trigger('create');
|
||||
$('.btnDeleteProfile', elem).on('click', function () {
|
||||
const index = this.getAttribute('data-profileindex');
|
||||
deleteTranscodingProfile(page, index);
|
||||
});
|
||||
$('.lnkEditSubProfile', elem).on('click', function () {
|
||||
const index = parseInt(this.getAttribute('data-profileindex'), 10);
|
||||
editTranscodingProfile(page, currentProfile.TranscodingProfiles[index]);
|
||||
});
|
||||
}
|
||||
|
||||
function editTranscodingProfile(page, transcodingProfile) {
|
||||
isSubProfileNew = transcodingProfile == null;
|
||||
transcodingProfile = transcodingProfile || {};
|
||||
currentSubProfile = transcodingProfile;
|
||||
const popup = $('#transcodingProfilePopup', page);
|
||||
$('#selectTranscodingProfileType', popup).val(transcodingProfile.Type || 'Video').trigger('change');
|
||||
$('#txtTranscodingContainer', popup).val(transcodingProfile.Container || '');
|
||||
$('#txtTranscodingAudioCodec', popup).val(transcodingProfile.AudioCodec || '');
|
||||
$('#txtTranscodingVideoCodec', popup).val(transcodingProfile.VideoCodec || '');
|
||||
$('#selectTranscodingProtocol', popup).val(transcodingProfile.Protocol || 'Http');
|
||||
$('#chkEnableMpegtsM2TsMode', popup).prop('checked', transcodingProfile.EnableMpegtsM2TsMode || false);
|
||||
$('#chkEstimateContentLength', popup).prop('checked', transcodingProfile.EstimateContentLength || false);
|
||||
$('#chkReportByteRangeRequests', popup).prop('checked', transcodingProfile.TranscodeSeekInfo == 'Bytes');
|
||||
$('.radioTabButton:first', popup).trigger('click');
|
||||
openPopup(popup[0]);
|
||||
}
|
||||
|
||||
function deleteTranscodingProfile(page, index) {
|
||||
currentProfile.TranscodingProfiles.splice(index, 1);
|
||||
renderTranscodingProfiles(page, currentProfile.TranscodingProfiles);
|
||||
}
|
||||
|
||||
function saveTranscodingProfile(page) {
|
||||
currentSubProfile.Type = $('#selectTranscodingProfileType', page).val();
|
||||
currentSubProfile.Container = $('#txtTranscodingContainer', page).val();
|
||||
currentSubProfile.AudioCodec = $('#txtTranscodingAudioCodec', page).val();
|
||||
currentSubProfile.VideoCodec = $('#txtTranscodingVideoCodec', page).val();
|
||||
currentSubProfile.Protocol = $('#selectTranscodingProtocol', page).val();
|
||||
currentSubProfile.Context = 'Streaming';
|
||||
currentSubProfile.EnableMpegtsM2TsMode = $('#chkEnableMpegtsM2TsMode', page).is(':checked');
|
||||
currentSubProfile.EstimateContentLength = $('#chkEstimateContentLength', page).is(':checked');
|
||||
currentSubProfile.TranscodeSeekInfo = $('#chkReportByteRangeRequests', page).is(':checked') ? 'Bytes' : 'Auto';
|
||||
|
||||
if (isSubProfileNew) {
|
||||
currentProfile.TranscodingProfiles.push(currentSubProfile);
|
||||
}
|
||||
|
||||
renderSubProfiles(page, currentProfile);
|
||||
currentSubProfile = null;
|
||||
closePopup($('#transcodingProfilePopup', page)[0]);
|
||||
}
|
||||
|
||||
function renderContainerProfiles(page, profiles) {
|
||||
let html = '';
|
||||
html += '<ul data-role="listview" data-inset="true" data-split-icon="delete">';
|
||||
let currentType;
|
||||
|
||||
for (let i = 0, length = profiles.length; i < length; i++) {
|
||||
const profile = profiles[i];
|
||||
|
||||
if (profile.Type !== currentType) {
|
||||
html += '<li data-role="list-divider">' + profile.Type + '</li>';
|
||||
currentType = profile.Type;
|
||||
}
|
||||
|
||||
html += '<div>';
|
||||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
||||
html += '<p>' + globalize.translate('ValueContainer', profile.Container || allText) + '</p>';
|
||||
|
||||
if (profile.Conditions?.length) {
|
||||
html += '<p>';
|
||||
html += globalize.translate('ValueConditions', profile.Conditions.map(function (c) {
|
||||
return c.Property;
|
||||
}).join(', '));
|
||||
html += '</p>';
|
||||
}
|
||||
|
||||
html += '</a>';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-profileindex="' + i + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</ul>';
|
||||
const elem = $('.containerProfiles', page).html(html).trigger('create');
|
||||
$('.btnDeleteProfile', elem).on('click', function () {
|
||||
const index = this.getAttribute('data-profileindex');
|
||||
deleteContainerProfile(page, index);
|
||||
});
|
||||
$('.lnkEditSubProfile', elem).on('click', function () {
|
||||
const index = parseInt(this.getAttribute('data-profileindex'), 10);
|
||||
editContainerProfile(page, currentProfile.ContainerProfiles[index]);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteContainerProfile(page, index) {
|
||||
currentProfile.ContainerProfiles.splice(index, 1);
|
||||
renderContainerProfiles(page, currentProfile.ContainerProfiles);
|
||||
}
|
||||
|
||||
function editContainerProfile(page, containerProfile) {
|
||||
isSubProfileNew = containerProfile == null;
|
||||
containerProfile = containerProfile || {};
|
||||
currentSubProfile = containerProfile;
|
||||
const popup = $('#containerProfilePopup', page);
|
||||
$('#selectContainerProfileType', popup).val(containerProfile.Type || 'Video').trigger('change');
|
||||
$('#txtContainerProfileContainer', popup).val(containerProfile.Container || '');
|
||||
$('.radioTabButton:first', popup).trigger('click');
|
||||
openPopup(popup[0]);
|
||||
}
|
||||
|
||||
function saveContainerProfile(page) {
|
||||
currentSubProfile.Type = $('#selectContainerProfileType', page).val();
|
||||
currentSubProfile.Container = $('#txtContainerProfileContainer', page).val();
|
||||
|
||||
if (isSubProfileNew) {
|
||||
currentProfile.ContainerProfiles.push(currentSubProfile);
|
||||
}
|
||||
|
||||
renderSubProfiles(page, currentProfile);
|
||||
currentSubProfile = null;
|
||||
closePopup($('#containerProfilePopup', page)[0]);
|
||||
}
|
||||
|
||||
function renderCodecProfiles(page, profiles) {
|
||||
let html = '';
|
||||
html += '<ul data-role="listview" data-inset="true" data-split-icon="delete">';
|
||||
let currentType;
|
||||
|
||||
for (let i = 0, length = profiles.length; i < length; i++) {
|
||||
const profile = profiles[i];
|
||||
const type = profile.Type.replace('VideoAudio', 'Video Audio');
|
||||
|
||||
if (type !== currentType) {
|
||||
html += '<li data-role="list-divider">' + type + '</li>';
|
||||
currentType = type;
|
||||
}
|
||||
|
||||
html += '<div>';
|
||||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
||||
html += '<p>' + globalize.translate('ValueCodec', profile.Codec || allText) + '</p>';
|
||||
|
||||
if (profile.Conditions?.length) {
|
||||
html += '<p>';
|
||||
html += globalize.translate('ValueConditions', profile.Conditions.map(function (c) {
|
||||
return c.Property;
|
||||
}).join(', '));
|
||||
html += '</p>';
|
||||
}
|
||||
|
||||
html += '</a>';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-profileindex="' + i + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</ul>';
|
||||
const elem = $('.codecProfiles', page).html(html).trigger('create');
|
||||
$('.btnDeleteProfile', elem).on('click', function () {
|
||||
const index = this.getAttribute('data-profileindex');
|
||||
deleteCodecProfile(page, index);
|
||||
});
|
||||
$('.lnkEditSubProfile', elem).on('click', function () {
|
||||
const index = parseInt(this.getAttribute('data-profileindex'), 10);
|
||||
editCodecProfile(page, currentProfile.CodecProfiles[index]);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteCodecProfile(page, index) {
|
||||
currentProfile.CodecProfiles.splice(index, 1);
|
||||
renderCodecProfiles(page, currentProfile.CodecProfiles);
|
||||
}
|
||||
|
||||
function editCodecProfile(page, codecProfile) {
|
||||
isSubProfileNew = codecProfile == null;
|
||||
codecProfile = codecProfile || {};
|
||||
currentSubProfile = codecProfile;
|
||||
const popup = $('#codecProfilePopup', page);
|
||||
$('#selectCodecProfileType', popup).val(codecProfile.Type || 'Video').trigger('change');
|
||||
$('#txtCodecProfileCodec', popup).val(codecProfile.Codec || '');
|
||||
$('.radioTabButton:first', popup).trigger('click');
|
||||
openPopup(popup[0]);
|
||||
}
|
||||
|
||||
function saveCodecProfile(page) {
|
||||
currentSubProfile.Type = $('#selectCodecProfileType', page).val();
|
||||
currentSubProfile.Codec = $('#txtCodecProfileCodec', page).val();
|
||||
|
||||
if (isSubProfileNew) {
|
||||
currentProfile.CodecProfiles.push(currentSubProfile);
|
||||
}
|
||||
|
||||
renderSubProfiles(page, currentProfile);
|
||||
currentSubProfile = null;
|
||||
closePopup($('#codecProfilePopup', page)[0]);
|
||||
}
|
||||
|
||||
function renderResponseProfiles(page, profiles) {
|
||||
let html = '';
|
||||
html += '<ul data-role="listview" data-inset="true" data-split-icon="delete">';
|
||||
let currentType;
|
||||
|
||||
for (let i = 0, length = profiles.length; i < length; i++) {
|
||||
const profile = profiles[i];
|
||||
|
||||
if (profile.Type !== currentType) {
|
||||
html += '<li data-role="list-divider">' + profile.Type + '</li>';
|
||||
currentType = profile.Type;
|
||||
}
|
||||
|
||||
html += '<div>';
|
||||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
||||
html += '<p>' + globalize.translate('ValueContainer', profile.Container || allText) + '</p>';
|
||||
|
||||
if (profile.Type == 'Video') {
|
||||
html += '<p>' + globalize.translate('ValueVideoCodec', profile.VideoCodec || allText) + '</p>';
|
||||
html += '<p>' + globalize.translate('ValueAudioCodec', profile.AudioCodec || allText) + '</p>';
|
||||
} else if (profile.Type == 'Audio') {
|
||||
html += '<p>' + globalize.translate('ValueCodec', profile.AudioCodec || allText) + '</p>';
|
||||
}
|
||||
|
||||
if (profile.Conditions?.length) {
|
||||
html += '<p>';
|
||||
html += globalize.translate('ValueConditions', profile.Conditions.map(function (c) {
|
||||
return c.Property;
|
||||
}).join(', '));
|
||||
html += '</p>';
|
||||
}
|
||||
|
||||
html += '</a>';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile listItemButton" data-profileindex="' + i + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</ul>';
|
||||
const elem = $('.mediaProfiles', page).html(html).trigger('create');
|
||||
$('.btnDeleteProfile', elem).on('click', function () {
|
||||
const index = this.getAttribute('data-profileindex');
|
||||
deleteResponseProfile(page, index);
|
||||
});
|
||||
$('.lnkEditSubProfile', elem).on('click', function () {
|
||||
const index = parseInt(this.getAttribute('data-profileindex'), 10);
|
||||
editResponseProfile(page, currentProfile.ResponseProfiles[index]);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteResponseProfile(page, index) {
|
||||
currentProfile.ResponseProfiles.splice(index, 1);
|
||||
renderResponseProfiles(page, currentProfile.ResponseProfiles);
|
||||
}
|
||||
|
||||
function editResponseProfile(page, responseProfile) {
|
||||
isSubProfileNew = responseProfile == null;
|
||||
responseProfile = responseProfile || {};
|
||||
currentSubProfile = responseProfile;
|
||||
const popup = $('#responseProfilePopup', page);
|
||||
$('#selectResponseProfileType', popup).val(responseProfile.Type || 'Video').trigger('change');
|
||||
$('#txtResponseProfileContainer', popup).val(responseProfile.Container || '');
|
||||
$('#txtResponseProfileAudioCodec', popup).val(responseProfile.AudioCodec || '');
|
||||
$('#txtResponseProfileVideoCodec', popup).val(responseProfile.VideoCodec || '');
|
||||
$('.radioTabButton:first', popup).trigger('click');
|
||||
openPopup(popup[0]);
|
||||
}
|
||||
|
||||
function saveResponseProfile(page) {
|
||||
currentSubProfile.Type = $('#selectResponseProfileType', page).val();
|
||||
currentSubProfile.Container = $('#txtResponseProfileContainer', page).val();
|
||||
currentSubProfile.AudioCodec = $('#txtResponseProfileAudioCodec', page).val();
|
||||
currentSubProfile.VideoCodec = $('#txtResponseProfileVideoCodec', page).val();
|
||||
|
||||
if (isSubProfileNew) {
|
||||
currentProfile.ResponseProfiles.push(currentSubProfile);
|
||||
}
|
||||
|
||||
renderSubProfiles(page, currentProfile);
|
||||
currentSubProfile = null;
|
||||
closePopup($('#responseProfilePopup', page)[0]);
|
||||
}
|
||||
|
||||
function saveProfile(page, profile) {
|
||||
updateProfile(page, profile);
|
||||
const id = getParameterByName('id');
|
||||
|
||||
if (id) {
|
||||
ApiClient.ajax({
|
||||
type: 'POST',
|
||||
url: ApiClient.getUrl('Dlna/Profiles/' + id),
|
||||
data: JSON.stringify(profile),
|
||||
contentType: 'application/json'
|
||||
}).then(function () {
|
||||
toast(globalize.translate('SettingsSaved'));
|
||||
}, Dashboard.processErrorResponse);
|
||||
} else {
|
||||
ApiClient.ajax({
|
||||
type: 'POST',
|
||||
url: ApiClient.getUrl('Dlna/Profiles'),
|
||||
data: JSON.stringify(profile),
|
||||
contentType: 'application/json'
|
||||
}).then(function () {
|
||||
Dashboard.navigate('dashboard/dlna/profiles');
|
||||
}, Dashboard.processErrorResponse);
|
||||
}
|
||||
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
function updateProfile(page, profile) {
|
||||
profile.Name = $('#txtName', page).val();
|
||||
profile.EnableAlbumArtInDidl = $('#chkEnableAlbumArtInDidl', page).is(':checked');
|
||||
profile.EnableSingleAlbumArtLimit = $('#chkEnableSingleImageLimit', page).is(':checked');
|
||||
profile.SupportedMediaTypes = $('.chkMediaType:checked', page).get().map(function (c) {
|
||||
return c.getAttribute('data-value');
|
||||
}).join(',');
|
||||
profile.Identification = profile.Identification || {};
|
||||
profile.FriendlyName = $('#txtInfoFriendlyName', page).val();
|
||||
profile.ModelName = $('#txtInfoModelName', page).val();
|
||||
profile.ModelNumber = $('#txtInfoModelNumber', page).val();
|
||||
profile.ModelDescription = $('#txtInfoModelDescription', page).val();
|
||||
profile.ModelUrl = $('#txtInfoModelUrl', page).val();
|
||||
profile.Manufacturer = $('#txtInfoManufacturer', page).val();
|
||||
profile.ManufacturerUrl = $('#txtInfoManufacturerUrl', page).val();
|
||||
profile.SerialNumber = $('#txtInfoSerialNumber', page).val();
|
||||
profile.Identification.FriendlyName = $('#txtIdFriendlyName', page).val();
|
||||
profile.Identification.ModelName = $('#txtIdModelName', page).val();
|
||||
profile.Identification.ModelNumber = $('#txtIdModelNumber', page).val();
|
||||
profile.Identification.ModelDescription = $('#txtIdModelDescription', page).val();
|
||||
profile.Identification.ModelUrl = $('#txtIdModelUrl', page).val();
|
||||
profile.Identification.Manufacturer = $('#txtIdManufacturer', page).val();
|
||||
profile.Identification.ManufacturerUrl = $('#txtIdManufacturerUrl', page).val();
|
||||
profile.Identification.SerialNumber = $('#txtIdSerialNumber', page).val();
|
||||
profile.Identification.DeviceDescription = $('#txtIdDeviceDescription', page).val();
|
||||
profile.AlbumArtPn = $('#txtAlbumArtPn', page).val();
|
||||
profile.MaxAlbumArtWidth = $('#txtAlbumArtMaxWidth', page).val();
|
||||
profile.MaxAlbumArtHeight = $('#txtAlbumArtMaxHeight', page).val();
|
||||
profile.MaxIconWidth = $('#txtIconMaxWidth', page).val();
|
||||
profile.MaxIconHeight = $('#txtIconMaxHeight', page).val();
|
||||
profile.RequiresPlainFolders = $('#chkRequiresPlainFolders', page).is(':checked');
|
||||
profile.RequiresPlainVideoItems = $('#chkRequiresPlainVideoItems', page).is(':checked');
|
||||
profile.IgnoreTranscodeByteRangeRequests = $('#chkIgnoreTranscodeByteRangeRequests', page).is(':checked');
|
||||
profile.MaxStreamingBitrate = $('#txtMaxAllowedBitrate', page).val();
|
||||
profile.MusicStreamingTranscodingBitrate = $('#txtMusicStreamingTranscodingBitrate', page).val();
|
||||
profile.ProtocolInfo = $('#txtProtocolInfo', page).val();
|
||||
profile.XDlnaCap = $('#txtXDlnaCap', page).val();
|
||||
profile.XDlnaDoc = $('#txtXDlnaDoc', page).val();
|
||||
profile.SonyAggregationFlags = $('#txtSonyAggregationFlags', page).val();
|
||||
profile.UserId = $('#selectUser', page).val();
|
||||
}
|
||||
|
||||
let currentProfile;
|
||||
let currentSubProfile;
|
||||
let isSubProfileNew;
|
||||
const allText = globalize.translate('All');
|
||||
|
||||
$(document).on('pageinit', '#dlnaProfilePage', function () {
|
||||
const page = this;
|
||||
$('.radioTabButton', page).on('click', function () {
|
||||
$(this).siblings().removeClass('ui-btn-active');
|
||||
$(this).addClass('ui-btn-active');
|
||||
const value = this.tagName == 'A' ? this.getAttribute('data-value') : this.value;
|
||||
const elem = $('.' + value, page);
|
||||
elem.siblings('.tabContent').hide();
|
||||
elem.show();
|
||||
});
|
||||
$('#selectDirectPlayProfileType', page).on('change', function () {
|
||||
if (this.value == 'Video') {
|
||||
$('#fldDirectPlayVideoCodec', page).show();
|
||||
} else {
|
||||
$('#fldDirectPlayVideoCodec', page).hide();
|
||||
}
|
||||
|
||||
if (this.value == 'Photo') {
|
||||
$('#fldDirectPlayAudioCodec', page).hide();
|
||||
} else {
|
||||
$('#fldDirectPlayAudioCodec', page).show();
|
||||
}
|
||||
});
|
||||
$('#selectTranscodingProfileType', page).on('change', function () {
|
||||
if (this.value == 'Video') {
|
||||
$('#fldTranscodingVideoCodec', page).show();
|
||||
$('#fldTranscodingProtocol', page).show();
|
||||
$('#fldEnableMpegtsM2TsMode', page).show();
|
||||
} else {
|
||||
$('#fldTranscodingVideoCodec', page).hide();
|
||||
$('#fldTranscodingProtocol', page).hide();
|
||||
$('#fldEnableMpegtsM2TsMode', page).hide();
|
||||
}
|
||||
|
||||
if (this.value == 'Photo') {
|
||||
$('#fldTranscodingAudioCodec', page).hide();
|
||||
$('#fldEstimateContentLength', page).hide();
|
||||
$('#fldReportByteRangeRequests', page).hide();
|
||||
} else {
|
||||
$('#fldTranscodingAudioCodec', page).show();
|
||||
$('#fldEstimateContentLength', page).show();
|
||||
$('#fldReportByteRangeRequests', page).show();
|
||||
}
|
||||
});
|
||||
$('#selectResponseProfileType', page).on('change', function () {
|
||||
if (this.value == 'Video') {
|
||||
$('#fldResponseProfileVideoCodec', page).show();
|
||||
} else {
|
||||
$('#fldResponseProfileVideoCodec', page).hide();
|
||||
}
|
||||
|
||||
if (this.value == 'Photo') {
|
||||
$('#fldResponseProfileAudioCodec', page).hide();
|
||||
} else {
|
||||
$('#fldResponseProfileAudioCodec', page).show();
|
||||
}
|
||||
});
|
||||
$('.btnAddDirectPlayProfile', page).on('click', function () {
|
||||
editDirectPlayProfile(page);
|
||||
});
|
||||
$('.btnAddTranscodingProfile', page).on('click', function () {
|
||||
editTranscodingProfile(page);
|
||||
});
|
||||
$('.btnAddContainerProfile', page).on('click', function () {
|
||||
editContainerProfile(page);
|
||||
});
|
||||
$('.btnAddCodecProfile', page).on('click', function () {
|
||||
editCodecProfile(page);
|
||||
});
|
||||
$('.btnAddResponseProfile', page).on('click', function () {
|
||||
editResponseProfile(page);
|
||||
});
|
||||
$('.btnAddIdentificationHttpHeader', page).on('click', function () {
|
||||
editIdentificationHeader(page);
|
||||
});
|
||||
$('.btnAddXmlDocumentAttribute', page).on('click', function () {
|
||||
editXmlDocumentAttribute(page);
|
||||
});
|
||||
$('.btnAddSubtitleProfile', page).on('click', function () {
|
||||
editSubtitleProfile(page);
|
||||
});
|
||||
$('.dlnaProfileForm').off('submit', DlnaProfilePage.onSubmit).on('submit', DlnaProfilePage.onSubmit);
|
||||
$('.editDirectPlayProfileForm').off('submit', DlnaProfilePage.onDirectPlayFormSubmit).on('submit', DlnaProfilePage.onDirectPlayFormSubmit);
|
||||
$('.transcodingProfileForm').off('submit', DlnaProfilePage.onTranscodingProfileFormSubmit).on('submit', DlnaProfilePage.onTranscodingProfileFormSubmit);
|
||||
$('.containerProfileForm').off('submit', DlnaProfilePage.onContainerProfileFormSubmit).on('submit', DlnaProfilePage.onContainerProfileFormSubmit);
|
||||
$('.codecProfileForm').off('submit', DlnaProfilePage.onCodecProfileFormSubmit).on('submit', DlnaProfilePage.onCodecProfileFormSubmit);
|
||||
$('.editResponseProfileForm').off('submit', DlnaProfilePage.onResponseProfileFormSubmit).on('submit', DlnaProfilePage.onResponseProfileFormSubmit);
|
||||
$('.identificationHeaderForm').off('submit', DlnaProfilePage.onIdentificationHeaderFormSubmit).on('submit', DlnaProfilePage.onIdentificationHeaderFormSubmit);
|
||||
$('.xmlAttributeForm').off('submit', DlnaProfilePage.onXmlAttributeFormSubmit).on('submit', DlnaProfilePage.onXmlAttributeFormSubmit);
|
||||
$('.subtitleProfileForm').off('submit', DlnaProfilePage.onSubtitleProfileFormSubmit).on('submit', DlnaProfilePage.onSubtitleProfileFormSubmit);
|
||||
}).on('pageshow', '#dlnaProfilePage', function () {
|
||||
const page = this;
|
||||
$('#radioInfo', page).trigger('click');
|
||||
loadProfile(page);
|
||||
});
|
||||
window.DlnaProfilePage = {
|
||||
onSubmit: function () {
|
||||
loading.show();
|
||||
saveProfile($(this).parents('.page'), currentProfile);
|
||||
return false;
|
||||
},
|
||||
onDirectPlayFormSubmit: function () {
|
||||
saveDirectPlayProfile($(this).parents('.page'));
|
||||
return false;
|
||||
},
|
||||
onTranscodingProfileFormSubmit: function () {
|
||||
saveTranscodingProfile($(this).parents('.page'));
|
||||
return false;
|
||||
},
|
||||
onContainerProfileFormSubmit: function () {
|
||||
saveContainerProfile($(this).parents('.page'));
|
||||
return false;
|
||||
},
|
||||
onCodecProfileFormSubmit: function () {
|
||||
saveCodecProfile($(this).parents('.page'));
|
||||
return false;
|
||||
},
|
||||
onResponseProfileFormSubmit: function () {
|
||||
saveResponseProfile($(this).parents('.page'));
|
||||
return false;
|
||||
},
|
||||
onIdentificationHeaderFormSubmit: function () {
|
||||
saveIdentificationHeader($(this).parents('.page'));
|
||||
return false;
|
||||
},
|
||||
onXmlAttributeFormSubmit: function () {
|
||||
saveXmlDocumentAttribute($(this).parents('.page'));
|
||||
return false;
|
||||
},
|
||||
onSubtitleProfileFormSubmit: function () {
|
||||
saveSubtitleProfile($(this).parents('.page'));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
@ -1,32 +0,0 @@
|
||||
<div id="dlnaProfilesPage" data-role="page" class="page type-interior dlnaPage withTabs">
|
||||
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
|
||||
<div class="readOnlyContent">
|
||||
|
||||
<div class="verticalSection verticalSection-extrabottompadding">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${HeaderCustomDlnaProfiles}</h2>
|
||||
<a is="emby-linkbutton" href="#/dashboard/dlna/profiles/edit" class="fab submit" style="margin:0 0 0 1em">
|
||||
<span class="material-icons add" aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p>${CustomDlnaProfilesHelp}</p>
|
||||
<div class="customProfiles"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${HeaderSystemDlnaProfiles}</h2>
|
||||
</div>
|
||||
|
||||
<p>${SystemDlnaProfilesHelp}</p>
|
||||
<div class="systemProfiles"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,93 +0,0 @@
|
||||
import escapeHtml from 'escape-html';
|
||||
import 'jquery';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import loading from '../../../components/loading/loading';
|
||||
import libraryMenu from '../../../scripts/libraryMenu';
|
||||
import '../../../components/listview/listview.scss';
|
||||
import '../../../elements/emby-button/emby-button';
|
||||
import confirm from '../../../components/confirm/confirm';
|
||||
|
||||
function loadProfiles(page) {
|
||||
loading.show();
|
||||
ApiClient.getJSON(ApiClient.getUrl('Dlna/ProfileInfos')).then(function (result) {
|
||||
renderUserProfiles(page, result);
|
||||
renderSystemProfiles(page, result);
|
||||
loading.hide();
|
||||
});
|
||||
}
|
||||
|
||||
function renderUserProfiles(page, profiles) {
|
||||
renderProfiles(page, page.querySelector('.customProfiles'), profiles.filter(function (p) {
|
||||
return p.Type == 'User';
|
||||
}));
|
||||
}
|
||||
|
||||
function renderSystemProfiles(page, profiles) {
|
||||
renderProfiles(page, page.querySelector('.systemProfiles'), profiles.filter(function (p) {
|
||||
return p.Type == 'System';
|
||||
}));
|
||||
}
|
||||
|
||||
function renderProfiles(page, element, profiles) {
|
||||
let html = '';
|
||||
|
||||
if (profiles.length) {
|
||||
html += '<div class="paperList">';
|
||||
}
|
||||
|
||||
for (let i = 0, length = profiles.length; i < length; i++) {
|
||||
const profile = profiles[i];
|
||||
html += '<div class="listItem listItem-border">';
|
||||
html += '<span class="listItemIcon material-icons live_tv" aria-hidden="true"></span>';
|
||||
html += '<div class="listItemBody two-line">';
|
||||
html += "<a is='emby-linkbutton' style='padding:0;margin:0;' data-ripple='false' class='clearLink' href='#/dashboard/dlna/profiles/edit?id=" + profile.Id + "'>";
|
||||
html += '<div>' + escapeHtml(profile.Name) + '</div>';
|
||||
html += '</a>';
|
||||
html += '</div>';
|
||||
|
||||
if (profile.Type == 'User') {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnDeleteProfile" data-profileid="' + profile.Id + '" title="' + globalize.translate('Delete') + '"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
if (profiles.length) {
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
element.innerHTML = html;
|
||||
$('.btnDeleteProfile', element).on('click', function () {
|
||||
const id = this.getAttribute('data-profileid');
|
||||
deleteProfile(page, id);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteProfile(page, id) {
|
||||
confirm(globalize.translate('MessageConfirmProfileDeletion'), globalize.translate('HeaderConfirmProfileDeletion')).then(function () {
|
||||
loading.show();
|
||||
ApiClient.ajax({
|
||||
type: 'DELETE',
|
||||
url: ApiClient.getUrl('Dlna/Profiles/' + id)
|
||||
}).then(function () {
|
||||
loading.hide();
|
||||
loadProfiles(page);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/dashboard/dlna',
|
||||
name: globalize.translate('Settings')
|
||||
}, {
|
||||
href: '#/dashboard/dlna/profiles',
|
||||
name: globalize.translate('TabProfiles')
|
||||
}];
|
||||
}
|
||||
|
||||
$(document).on('pageshow', '#dlnaProfilesPage', function () {
|
||||
libraryMenu.setTabs('dlna', 1, getTabs);
|
||||
loadProfiles(this);
|
||||
});
|
||||
|
@ -1,69 +0,0 @@
|
||||
<div id="dlnaSettingsPage" data-role="page" class="page type-interior withTabs">
|
||||
|
||||
<div>
|
||||
<div class="content-primary">
|
||||
|
||||
<form class="dlnaSettingsForm">
|
||||
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h2 class="sectionTitle">${Settings}</h2>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://jellyfin.org/docs/general/networking/dlna">${Help}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnablePlayTo" />
|
||||
<span>${LabelEnableDlnaPlayTo}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableDlnaPlayToHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableDlnaDebugLogging" />
|
||||
<span>${LabelEnableDlnaDebugLogging}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableDlnaDebugLoggingHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtClientDiscoveryInterval" min="1" max="300" label="${LabelEnableDlnaClientDiscoveryInterval}" />
|
||||
<div class="fieldDescription">${LabelEnableDlnaClientDiscoveryIntervalHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkEnableServer" />
|
||||
<span>${LabelEnableDlnaServer}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableDlnaServerHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkBlastAliveMessages" />
|
||||
<span>${LabelEnableBlastAliveMessages}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${LabelEnableBlastAliveMessagesHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtBlastInterval" min="1" max="3600" label="${LabelBlastMessageInterval}" />
|
||||
<div class="fieldDescription">${LabelBlastMessageIntervalHelp}</div>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectUser" data-mini="true" label="${LabelDefaultUser}"></select>
|
||||
<div class="fieldDescription">${LabelDefaultUserHelp}</div>
|
||||
</div>
|
||||
<div>
|
||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||
<span>${Save}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,60 +0,0 @@
|
||||
import escapeHtml from 'escape-html';
|
||||
import 'jquery';
|
||||
import loading from '../../../components/loading/loading';
|
||||
import libraryMenu from '../../../scripts/libraryMenu';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import Dashboard from '../../../utils/dashboard';
|
||||
|
||||
function loadPage(page, config, users) {
|
||||
page.querySelector('#chkEnablePlayTo').checked = config.EnablePlayTo;
|
||||
page.querySelector('#chkEnableDlnaDebugLogging').checked = config.EnableDebugLog;
|
||||
$('#txtClientDiscoveryInterval', page).val(config.ClientDiscoveryIntervalSeconds);
|
||||
$('#chkEnableServer', page).prop('checked', config.EnableServer);
|
||||
$('#chkBlastAliveMessages', page).prop('checked', config.BlastAliveMessages);
|
||||
$('#txtBlastInterval', page).val(config.BlastAliveMessageIntervalSeconds);
|
||||
const usersHtml = users.map(function (u) {
|
||||
return '<option value="' + u.Id + '">' + escapeHtml(u.Name) + '</option>';
|
||||
}).join('');
|
||||
$('#selectUser', page).html(usersHtml).val(config.DefaultUserId || '');
|
||||
loading.hide();
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
loading.show();
|
||||
const form = this;
|
||||
ApiClient.getNamedConfiguration('dlna').then(function (config) {
|
||||
config.EnablePlayTo = form.querySelector('#chkEnablePlayTo').checked;
|
||||
config.EnableDebugLog = form.querySelector('#chkEnableDlnaDebugLogging').checked;
|
||||
config.ClientDiscoveryIntervalSeconds = $('#txtClientDiscoveryInterval', form).val();
|
||||
config.EnableServer = $('#chkEnableServer', form).is(':checked');
|
||||
config.BlastAliveMessages = $('#chkBlastAliveMessages', form).is(':checked');
|
||||
config.BlastAliveMessageIntervalSeconds = $('#txtBlastInterval', form).val();
|
||||
config.DefaultUserId = $('#selectUser', form).val();
|
||||
ApiClient.updateNamedConfiguration('dlna', config).then(Dashboard.processServerConfigurationUpdateResult);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
function getTabs() {
|
||||
return [{
|
||||
href: '#/dashboard/dlna',
|
||||
name: globalize.translate('Settings')
|
||||
}, {
|
||||
href: '#/dashboard/dlna/profiles',
|
||||
name: globalize.translate('TabProfiles')
|
||||
}];
|
||||
}
|
||||
|
||||
$(document).on('pageinit', '#dlnaSettingsPage', function () {
|
||||
$('.dlnaSettingsForm').off('submit', onSubmit).on('submit', onSubmit);
|
||||
}).on('pageshow', '#dlnaSettingsPage', function () {
|
||||
libraryMenu.setTabs('dlna', 0, getTabs);
|
||||
loading.show();
|
||||
const page = this;
|
||||
const promise1 = ApiClient.getNamedConfiguration('dlna');
|
||||
const promise2 = ApiClient.getUsers();
|
||||
Promise.all([promise1, promise2]).then(function (responses) {
|
||||
loadPage(page, responses[0], responses[1]);
|
||||
});
|
||||
});
|
||||
|
@ -165,7 +165,6 @@
|
||||
"CopyStreamURLSuccess": "URL copied successfully.",
|
||||
"CriticRating": "Critics rating",
|
||||
"Cursive": "Cursive",
|
||||
"CustomDlnaProfilesHelp": "Create a custom profile to target a new device or override a system profile.",
|
||||
"DailyAt": "Daily at {0}",
|
||||
"Data": "Data",
|
||||
"DateAdded": "Date added",
|
||||
@ -208,6 +207,7 @@
|
||||
"DisplayMissingEpisodesWithinSeasons": "Display missing episodes within seasons",
|
||||
"DisplayMissingEpisodesWithinSeasonsHelp": "This must also be enabled for TV libraries in the server configuration.",
|
||||
"DisplayModeHelp": "Select the layout style you want for the interface.",
|
||||
"DlnaMovedMessage": "The DLNA functionality has moved to a plugin.",
|
||||
"DoNotRecord": "Do not record",
|
||||
"Down": "Down",
|
||||
"Download": "Download",
|
||||
@ -338,21 +338,15 @@
|
||||
"HeaderCastAndCrew": "Cast & Crew",
|
||||
"HeaderChannelAccess": "Channel Access",
|
||||
"HeaderChapterImages": "Chapter Images",
|
||||
"HeaderCodecProfile": "Codec Profile",
|
||||
"HeaderCodecProfileHelp": "Codec profiles indicate the limitations of a device when playing specific codecs. If a limitation applies then the media will be transcoded, even if the codec is configured for direct playback.",
|
||||
"HeaderConfigureRemoteAccess": "Set up Remote Access",
|
||||
"HeaderConfirmPluginInstallation": "Confirm Plugin Installation",
|
||||
"HeaderConfirmRepositoryInstallation": "Confirm Plugin Repository Installation",
|
||||
"HeaderConfirmProfileDeletion": "Confirm Profile Deletion",
|
||||
"HeaderConfirmRevokeApiKey": "Revoke API Key",
|
||||
"HeaderConnectionFailure": "Connection Failure",
|
||||
"HeaderConnectToServer": "Connect to Server",
|
||||
"HeaderContainerProfile": "Container Profile",
|
||||
"HeaderContainerProfileHelp": "Container profiles indicate the limitations of a device when playing specific formats. If a limitation applies then the media will be transcoded, even if the format is configured for direct playback.",
|
||||
"HeaderContinueListening": "Continue Listening",
|
||||
"HeaderContinueWatching": "Continue Watching",
|
||||
"HeaderContinueReading": "Continue Reading",
|
||||
"HeaderCustomDlnaProfiles": "Custom Profiles",
|
||||
"HeaderDateIssued": "Date Issued",
|
||||
"HeaderDefaultRecordingSettings": "Default Recording Settings",
|
||||
"HeaderDeleteDevice": "Delete Device",
|
||||
@ -365,8 +359,6 @@
|
||||
"HeaderDeveloperInfo": "Developer Info",
|
||||
"HeaderDeviceAccess": "Device Access",
|
||||
"HeaderDevices": "Devices",
|
||||
"HeaderDirectPlayProfile": "Direct Playback Profile",
|
||||
"HeaderDirectPlayProfileHelp": "Add direct playback profiles to indicate which formats the device can handle natively.",
|
||||
"HeaderDownloadSync": "Download & Sync",
|
||||
"HeaderDummyChapter": "Chapter Images",
|
||||
"HeaderDVR": "DVR",
|
||||
@ -383,14 +375,9 @@
|
||||
"HeaderFrequentlyPlayed": "Frequently Played",
|
||||
"HeaderGuestCast": "Guest Stars",
|
||||
"HeaderGuideProviders": "TV Guide Data Providers",
|
||||
"HeaderHttpHeaders": "HTTP Headers",
|
||||
"HeaderHttpsSettings": "HTTPS Settings",
|
||||
"HeaderIdentification": "Identification",
|
||||
"HeaderIdentificationCriteriaHelp": "Enter at least one identification criteria.",
|
||||
"HeaderIdentificationHeader": "Identification Header",
|
||||
"HeaderIdentifyItemHelp": "Enter one or more search criteria. Remove criteria to increase search results.",
|
||||
"HeaderImageOptions": "Image Options",
|
||||
"HeaderImageSettings": "Image Settings",
|
||||
"HeaderInstall": "Install",
|
||||
"HeaderInstantMix": "Instant Mix",
|
||||
"HeaderKeepRecording": "Keep Recording",
|
||||
@ -439,8 +426,6 @@
|
||||
"HeaderPluginInstallation": "Plugin Installation",
|
||||
"HeaderPortRanges": "Firewall and Proxy Settings",
|
||||
"HeaderPreferredMetadataLanguage": "Preferred Metadata Language",
|
||||
"HeaderProfileInformation": "Profile Information",
|
||||
"HeaderProfileServerSettingsHelp": "These values control how the server will present itself to clients.",
|
||||
"HeaderRecentlyPlayed": "Recently Played",
|
||||
"HeaderRecordingMetadataSaving": "Recording Metadata",
|
||||
"HeaderRecordingOptions": "Recording Options",
|
||||
@ -449,8 +434,6 @@
|
||||
"HeaderRemoteControl": "Remote Control",
|
||||
"HeaderRemoveMediaFolder": "Remove Media Folder",
|
||||
"HeaderRemoveMediaLocation": "Remove Media Location",
|
||||
"HeaderResponseProfile": "Response Profile",
|
||||
"HeaderResponseProfileHelp": "Response profiles provide a way to customize information sent to the device when playing certain kinds of media.",
|
||||
"HeaderRevisionHistory": "Revision History",
|
||||
"HeaderRunningTasks": "Running Tasks",
|
||||
"HeaderScenes": "Scenes",
|
||||
@ -468,7 +451,6 @@
|
||||
"HeaderSeriesOptions": "Series Options",
|
||||
"HeaderSeriesStatus": "Series Status",
|
||||
"HeaderServerAddressSettings": "Server Address Settings",
|
||||
"HeaderServerSettings": "Server Settings",
|
||||
"HeaderSetupLibrary": "Setup your media libraries",
|
||||
"HeaderSortBy": "Sort By",
|
||||
"HeaderSortOrder": "Sort Order",
|
||||
@ -478,20 +460,14 @@
|
||||
"HeaderStopRecording": "Stop Recording",
|
||||
"HeaderSubtitleAppearance": "Subtitle Appearance",
|
||||
"HeaderSubtitleDownloads": "Subtitle Downloads",
|
||||
"HeaderSubtitleProfile": "Subtitle Profile",
|
||||
"HeaderSubtitleProfiles": "Subtitle Profiles",
|
||||
"HeaderSubtitleProfilesHelp": "Subtitle profiles describe the subtitle formats supported by the device.",
|
||||
"HeaderSyncPlayEnabled": "SyncPlay enabled",
|
||||
"HeaderSyncPlaySelectGroup": "Join a group",
|
||||
"HeaderSyncPlaySettings": "SyncPlay Settings",
|
||||
"HeaderSyncPlayPlaybackSettings": "Playback",
|
||||
"HeaderSyncPlayTimeSyncSettings": "Time sync",
|
||||
"HeaderSystemDlnaProfiles": "System Profiles",
|
||||
"HeaderTaskTriggers": "Task Triggers",
|
||||
"HeaderThisUserIsCurrentlyDisabled": "This user is currently disabled",
|
||||
"HeaderTracks": "Tracks",
|
||||
"HeaderTranscodingProfile": "Transcoding Profile",
|
||||
"HeaderTranscodingProfileHelp": "Add transcoding profiles to indicate which formats should be used when transcoding is required.",
|
||||
"HeaderTunerDevices": "Tuner Devices",
|
||||
"HeaderTuners": "Tuners",
|
||||
"HeaderTypeImageFetchers": "Image fetchers ({0})",
|
||||
@ -506,9 +482,6 @@
|
||||
"HeaderVideos": "Videos",
|
||||
"HeaderVideoType": "Video Type",
|
||||
"HeaderVideoTypes": "Video Types",
|
||||
"HeaderXmlDocumentAttribute": "XML Document Attribute",
|
||||
"HeaderXmlDocumentAttributes": "XML Document Attributes",
|
||||
"HeaderXmlSettings": "XML Settings",
|
||||
"HeaderYears": "Years",
|
||||
"Help": "Help",
|
||||
"Hide": "Hide",
|
||||
@ -541,12 +514,7 @@
|
||||
"LabelAirsBeforeSeason": "Airs before season",
|
||||
"LabelAirTime": "Air time",
|
||||
"LabelAlbum": "Album",
|
||||
"LabelAlbumArtHelp": "PN used for album art, within the 'dlna:profileID' attribute on 'upnp:albumArtURI'. Some devices require a specific value, regardless of the size of the image.",
|
||||
"LabelAlbumArtists": "Album artists",
|
||||
"LabelAlbumArtMaxHeight": "Album art max height",
|
||||
"LabelAlbumArtMaxResHelp": "Maximum resolution of album art exposed via the 'upnp:albumArtURI' property.",
|
||||
"LabelAlbumArtMaxWidth": "Album art max width",
|
||||
"LabelAlbumArtPN": "Album art PN",
|
||||
"LabelAlbumGain": "Album Gain",
|
||||
"LabelAllowedRemoteAddresses": "Remote IP address filter",
|
||||
"LabelAllowedRemoteAddressesMode": "Remote IP address filter mode",
|
||||
@ -618,12 +586,8 @@
|
||||
"LabelDay": "Day of week",
|
||||
"LabelDeathDate": "Death date",
|
||||
"LabelDefaultScreen": "Default screen",
|
||||
"LabelDefaultUser": "Default user",
|
||||
"LabelDefaultUserHelp": "Determine which user library should be displayed on connected devices. This can be overridden for each device using profiles.",
|
||||
"LabelDeinterlaceMethod": "Deinterlacing method",
|
||||
"LabelDeveloper": "Developer",
|
||||
"LabelDeviceDescription": "Device description",
|
||||
"LabelDidlMode": "DIDL mode",
|
||||
"LabelDisableCustomCss": "Disable custom CSS code for theming/branding provided from the server.",
|
||||
"LabelDiscNumber": "Disc number",
|
||||
"LabelDisplayLanguage": "Display language",
|
||||
@ -645,22 +609,10 @@
|
||||
"LabelChapterImageResolution": "Resolution",
|
||||
"LabelChapterImageResolutionHelp": "The resolution of the extracted chapter images. Changing this will have no effect on existing dummy chapters.",
|
||||
"LabelDynamicExternalId": "{0} Id",
|
||||
"LabelEmbedAlbumArtDidl": "Embed album art in DIDL",
|
||||
"LabelEmbedAlbumArtDidlHelp": "Some devices prefer this method for getting the album art. Others may fail to play with this option enabled.",
|
||||
"LabelEnableAudioVbr": "Enable VBR audio encoding",
|
||||
"LabelEnableAudioVbrHelp": "Variable bitrate offers better quality to average bitrate ratio, but in some rare cases may cause buffering and compatibility issues.",
|
||||
"LabelEnableAutomaticPortMap": "Enable automatic port mapping",
|
||||
"LabelEnableAutomaticPortMapHelp": "Automatically forward public ports on your router to local ports on your server via UPnP. This may not work with some router models or network configurations. Changes will not apply until after a server restart.",
|
||||
"LabelEnableBlastAliveMessages": "Blast alive messages",
|
||||
"LabelEnableBlastAliveMessagesHelp": "Enable this if the server is not detected reliably by other UPnP devices on your network.",
|
||||
"LabelEnableDlnaClientDiscoveryInterval": "Client discovery interval",
|
||||
"LabelEnableDlnaClientDiscoveryIntervalHelp": "Determine the duration in seconds between two SSDP searches.",
|
||||
"LabelEnableDlnaDebugLogging": "Enable DLNA debug logging",
|
||||
"LabelEnableDlnaDebugLoggingHelp": "Create large log files and should only be used as needed for troubleshooting purposes.",
|
||||
"LabelEnableDlnaPlayTo": "Enable 'Play To' DLNA feature",
|
||||
"LabelEnableDlnaPlayToHelp": "Detect devices within your network and offer the ability to control them remotely.",
|
||||
"LabelEnableDlnaServer": "Enable DLNA server",
|
||||
"LabelEnableDlnaServerHelp": "Allow UPnP devices on your network to browse and play content.",
|
||||
"LabelEnableHardwareDecodingFor": "Enable hardware decoding for",
|
||||
"LabelEnableHttps": "Enable HTTPS",
|
||||
"LabelEnableHttpsHelp": "Listen on the configured HTTPS port. A valid certificate must also be supplied for this to take effect.",
|
||||
@ -672,8 +624,6 @@
|
||||
"LabelEnableLUFSScanHelp": "Clients can normalize audio playback to get equal loudness across tracks. This will make library scans longer and take more resources.",
|
||||
"LabelEnableRealtimeMonitor": "Enable real time monitoring",
|
||||
"LabelEnableRealtimeMonitorHelp": "Changes to files will be processed immediately on supported file systems.",
|
||||
"LabelEnableSingleImageInDidlLimit": "Limit to single embedded image",
|
||||
"LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within DIDL.",
|
||||
"LabelEncoderPreset": "Encoding preset",
|
||||
"LabelEndDate": "End date",
|
||||
"LabelEpisodeNumber": "Episode number",
|
||||
@ -702,10 +652,6 @@
|
||||
"LabelHomeScreenSectionValue": "Home screen section {0}",
|
||||
"LabelHttpsPort": "Local HTTPS port number",
|
||||
"LabelHttpsPortHelp": "The TCP port number for the HTTPS server.",
|
||||
"LabelIconMaxHeight": "Icon maximum height",
|
||||
"LabelIconMaxResHelp": "Maximum resolution of icons exposed via the 'upnp:icon' property.",
|
||||
"LabelIconMaxWidth": "Icon maximum width",
|
||||
"LabelIdentificationFieldHelp": "A case-insensitive substring or regex expression.",
|
||||
"LabelImageFetchersHelp": "Enable and rank your preferred image fetchers in order of priority.",
|
||||
"LabelImageType": "Image type",
|
||||
"LabelImportOnlyFavoriteChannels": "Restrict to channels marked as favorite",
|
||||
@ -741,9 +687,6 @@
|
||||
"LabelLoginDisclaimer": "Login disclaimer",
|
||||
"LabelLoginDisclaimerHelp": "A message that will be displayed at the bottom of the login page.",
|
||||
"LabelLogs": "Logs",
|
||||
"LabelManufacturer": "Manufacturer",
|
||||
"LabelManufacturerUrl": "Manufacturer URL",
|
||||
"LabelMatchType": "Match type",
|
||||
"LabelMaxAudiobookResume": "Audiobook remaining minutes to resume",
|
||||
"LabelMaxAudiobookResumeHelp": "Titles are assumed fully played if stopped when the remaining duration is less than this value.",
|
||||
"LabelMaxBackdropsPerItem": "Maximum number of backdrops per item",
|
||||
@ -753,8 +696,6 @@
|
||||
"LabelMaxParentalRating": "Maximum allowed parental rating",
|
||||
"LabelMaxResumePercentage": "Maximum resume percentage",
|
||||
"LabelMaxResumePercentageHelp": "Titles are assumed fully played if stopped after this time.",
|
||||
"LabelMaxStreamingBitrate": "Maximum streaming quality",
|
||||
"LabelMaxStreamingBitrateHelp": "Specify a maximum bitrate when streaming.",
|
||||
"LabelMessageText": "Message text",
|
||||
"LabelMessageTitle": "Message title",
|
||||
"LabelMetadata": "Metadata",
|
||||
@ -766,7 +707,6 @@
|
||||
"LabelMetadataReadersHelp": "Rank your preferred local metadata sources in order of priority. The first file found will be read.",
|
||||
"LabelMetadataSavers": "Metadata savers",
|
||||
"LabelMetadataSaversHelp": "Pick the file formats to use when saving your metadata.",
|
||||
"LabelMethod": "Method",
|
||||
"LabelMinAudiobookResume": "Minimum Audiobook resume in minutes",
|
||||
"LabelMinAudiobookResumeHelp": "Titles are assumed unplayed if stopped before this time.",
|
||||
"LabelMinBackdropDownloadWidth": "Minimum backdrop download width",
|
||||
@ -774,16 +714,10 @@
|
||||
"LabelMinResumeDurationHelp": "The shortest video length in seconds that will save playback location and let you resume.",
|
||||
"LabelMinResumePercentage": "Minimum resume percentage",
|
||||
"LabelMinResumePercentageHelp": "Titles are assumed unplayed if stopped before this time.",
|
||||
"LabelModelDescription": "Model description",
|
||||
"LabelModelName": "Model name",
|
||||
"LabelModelNumber": "Model number",
|
||||
"LabelModelUrl": "Model URL",
|
||||
"LabelMovieCategories": "Movie categories",
|
||||
"LabelMoviePrefix": "Movie prefix",
|
||||
"LabelMoviePrefixHelp": "If a prefix is applied to movie titles, enter it here so the server can handle it properly.",
|
||||
"LabelMovieRecordingPath": "Movie recording path",
|
||||
"LabelMusicStreamingTranscodingBitrate": "Music transcoding bitrate",
|
||||
"LabelMusicStreamingTranscodingBitrateHelp": "Specify a maximum bitrate when streaming music.",
|
||||
"LabelName": "Name",
|
||||
"LabelNewName": "New name",
|
||||
"LabelNewPassword": "New password",
|
||||
@ -823,15 +757,8 @@
|
||||
"LabelPostProcessorArgumentsHelp": "Use {path} as the path to the recording file.",
|
||||
"LabelPreferredDisplayLanguage": "Preferred display language",
|
||||
"LabelPreferredSubtitleLanguage": "Preferred subtitle language",
|
||||
"LabelProfileAudioCodecs": "Audio codecs",
|
||||
"LabelProfileCodecs": "Codecs",
|
||||
"LabelProfileCodecsHelp": "Separated by comma. This can be left empty to apply to all codecs.",
|
||||
"LabelProfileContainer": "Container",
|
||||
"LabelProfileContainersHelp": "Separated by comma. This can be left empty to apply to all containers.",
|
||||
"LabelProfileVideoCodecs": "Video codecs",
|
||||
"LabelProtocol": "Protocol",
|
||||
"LabelProtocolInfo": "Protocol info",
|
||||
"LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device.",
|
||||
"LabelPublicHttpPort": "Public HTTP port number",
|
||||
"LabelPublicHttpPortHelp": "The public port number that should be mapped to the local HTTP port.",
|
||||
"LabelPublicHttpsPort": "Public HTTPS port number",
|
||||
@ -862,7 +789,6 @@
|
||||
"LabelSelectFolderGroups": "Automatically group content from the following folders into views such as 'Movies', 'Music' and 'TV'",
|
||||
"LabelSelectFolderGroupsHelp": "Folders that are unchecked will be displayed by themselves in their own view.",
|
||||
"LabelSelectVersionToInstall": "Select version to install",
|
||||
"LabelSerialNumber": "Serial number",
|
||||
"LabelSeriesRecordingPath": "Series recording path",
|
||||
"LabelServerHost": "Host",
|
||||
"LabelServerHostHelp": "192.168.1.100:8096 or https://myserver.com",
|
||||
@ -879,8 +805,6 @@
|
||||
"LabelSkipIfGraphicalSubsPresentHelp": "Keeping text versions of subtitles will result in more efficient delivery and decrease the likelihood of video transcoding.",
|
||||
"LabelSlowResponseEnabled": "Log a warning message if the server was slow to answer",
|
||||
"LabelSlowResponseTime": "Time in ms after which a response is considered slow",
|
||||
"LabelSonyAggregationFlags": "Sony aggregation flags",
|
||||
"LabelSonyAggregationFlagsHelp": "Determine the content of the 'aggregationFlags' element in the 'urn:schemas-sonycom:av' namespace.",
|
||||
"LabelSortBy": "Sort by",
|
||||
"LabelSortName": "Sort name",
|
||||
"LabelSortOrder": "Sort order",
|
||||
@ -895,10 +819,8 @@
|
||||
"LabelStopWhenPossible": "Stop when possible",
|
||||
"LabelStreamType": "Stream type",
|
||||
"LabelSubtitleDownloaders": "Subtitle downloaders",
|
||||
"LabelSubtitleFormatHelp": "Example: srt",
|
||||
"LabelSubtitlePlaybackMode": "Subtitle mode",
|
||||
"LabelSubtitleVerticalPosition": "Vertical position",
|
||||
"LabelSupportedMediaTypes": "Supported Media Types",
|
||||
"LabelSyncPlayAccess": "SyncPlay access",
|
||||
"LabelSyncPlayAccessCreateAndJoinGroups": "Allow user to create and join groups",
|
||||
"LabelSyncPlayAccessJoinGroups": "Allow user to join groups",
|
||||
@ -973,8 +895,6 @@
|
||||
"LabelUnstable": "Unstable",
|
||||
"LabelUser": "User",
|
||||
"LabelUserAgent": "User agent",
|
||||
"LabelUserLibrary": "User library",
|
||||
"LabelUserLibraryHelp": "Select which user library to display to the device. Leave empty to inherit the default setting.",
|
||||
"LabelUserLoginAttemptsBeforeLockout": "Failed login tries before user is locked out",
|
||||
"LabelUserMaxActiveSessions": "Maximum number of simultaneous user sessions",
|
||||
"LabelUsername": "Username",
|
||||
@ -990,10 +910,6 @@
|
||||
"LabelVideoResolution": "Video resolution",
|
||||
"LabelWeb": "Web",
|
||||
"LabelWebVersion": "Web version",
|
||||
"LabelXDlnaCap": "Device Capability ID",
|
||||
"LabelXDlnaCapHelp": "Determine the content of the 'X_DLNACAP' element in the 'urn:schemas-dlna-org:device-1-0' namespace.",
|
||||
"LabelXDlnaDoc": "Device Class ID",
|
||||
"LabelXDlnaDocHelp": "Determine the content of the 'X_DLNADOC' element in the 'urn:schemas-dlna-org:device-1-0' namespace.",
|
||||
"LabelYear": "Year",
|
||||
"LabelYoureDone": "You're Done!",
|
||||
"LabelZipCode": "Zip Code",
|
||||
@ -1070,7 +986,6 @@
|
||||
"MessageConfirmAppExit": "Do you want to exit?",
|
||||
"MessageConfirmDeleteGuideProvider": "Are you sure you wish to delete this guide provider?",
|
||||
"MessageConfirmDeleteTunerDevice": "Are you sure you wish to delete this device?",
|
||||
"MessageConfirmProfileDeletion": "Are you sure you wish to delete this profile?",
|
||||
"MessageConfirmRecordingCancellation": "Cancel recording?",
|
||||
"MessageConfirmRemoveMediaLocation": "Are you sure you wish to remove this location?",
|
||||
"MessageConfirmRestart": "Are you sure you wish to restart Jellyfin?",
|
||||
@ -1215,7 +1130,6 @@
|
||||
"OptionAutomaticallyGroupSeries": "Automatically merge series that are spread across multiple folders",
|
||||
"OptionAutomaticallyGroupSeriesHelp": "Series that are spread across multiple folders within this library will be automatically merged into a single series.",
|
||||
"OptionBluray": "BD",
|
||||
"OptionCaptionInfoExSamsung": "CaptionInfoEx (Samsung)",
|
||||
"OptionCommunityRating": "Community Rating",
|
||||
"OptionCriticRating": "Critics Rating",
|
||||
"OptionDaily": "Daily",
|
||||
@ -1230,28 +1144,19 @@
|
||||
"OptionDisplayFolderView": "Display a folder view to show plain media folders",
|
||||
"OptionDisplayFolderViewHelp": "Display folders alongside your other media libraries. This can be useful if you'd like to have a plain folder view.",
|
||||
"OptionDvd": "DVD",
|
||||
"OptionEmbedSubtitles": "Embed within container",
|
||||
"OptionEnableAccessFromAllDevices": "Enable access from all devices",
|
||||
"OptionEnableAccessToAllChannels": "Enable access to all channels",
|
||||
"OptionEnableAccessToAllLibraries": "Enable access to all libraries",
|
||||
"OptionEnableExternalContentInSuggestions": "Enable external content in suggestions",
|
||||
"OptionEnableExternalContentInSuggestionsHelp": "Allow internet trailers and live TV programs to be included within suggested content.",
|
||||
"OptionEnableForAllTuners": "Enable for all tuner devices",
|
||||
"OptionEnableM2tsMode": "Enable M2TS mode",
|
||||
"OptionEnableM2tsModeHelp": "Enable M2TS mode when encoding to MPEG-TS.",
|
||||
"OptionEquals": "Equals",
|
||||
"OptionEstimateContentLength": "Estimate content length when transcoding",
|
||||
"OptionEveryday": "Every day",
|
||||
"OptionExternallyDownloaded": "External download",
|
||||
"OptionExtractChapterImage": "Enable chapter image extraction",
|
||||
"OptionForceRemoteSourceTranscoding": "Force transcoding of remote media sources such as Live TV",
|
||||
"OptionHasThemeSong": "Theme Song",
|
||||
"OptionHasThemeVideo": "Theme Video",
|
||||
"OptionHideUser": "Hide this user from login screens",
|
||||
"OptionHideUserFromLoginHelp": "Useful for private or hidden administrator accounts. The user will need to sign in manually by entering their username and password.",
|
||||
"OptionHlsSegmentedSubtitles": "HLS segmented subtitles",
|
||||
"OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests",
|
||||
"OptionIgnoreTranscodeByteRangeRequestsHelp": "These requests will be honored but will ignore the byte range header.",
|
||||
"OptionImdbRating": "IMDb Rating",
|
||||
"OptionIsHD": "HD",
|
||||
"OptionIsSD": "SD",
|
||||
@ -1264,27 +1169,16 @@
|
||||
"OptionNew": "New…",
|
||||
"OptionOnInterval": "On an interval",
|
||||
"OptionParentalRating": "Parental Rating",
|
||||
"OptionPlainStorageFolders": "Display all folders as plain storage folders",
|
||||
"OptionPlainStorageFoldersHelp": "All folders are represented in DIDL as 'object.container.storageFolder' instead of a more specific type, such as 'object.container.person.musicArtist'.",
|
||||
"OptionPlainVideoItems": "Display all videos as plain video items",
|
||||
"OptionPlainVideoItemsHelp": "All videos are represented in DIDL as 'object.item.videoItem' instead of a more specific type, such as 'object.item.videoItem.movie'.",
|
||||
"OptionPlayCount": "Play Count",
|
||||
"OptionPremiereDate": "Premiere Date",
|
||||
"OptionProtocolHls": "HTTP Live Streaming (HLS)",
|
||||
"OptionProtocolHttp": "HTTP",
|
||||
"OptionRandom": "Random",
|
||||
"OptionRegex": "Regex",
|
||||
"OptionReleaseDate": "Release Date",
|
||||
"OptionReportByteRangeSeekingWhenTranscoding": "Report that the server supports byte seeking when transcoding",
|
||||
"OptionReportByteRangeSeekingWhenTranscodingHelp": "This is required for some devices that don't time seek very well.",
|
||||
"OptionRequirePerfectSubtitleMatch": "Only download subtitles that are a perfect match for video files",
|
||||
"OptionRequirePerfectSubtitleMatchHelp": "Requiring a perfect match will filter subtitles to include only those that have been tested and verified with your exact video file. Unchecking this will increase the likelihood of subtitles being downloaded, but will increase the chances of mistimed or incorrect subtitle text.",
|
||||
"OptionResElement": "'res' element",
|
||||
"OptionResumable": "Resumable",
|
||||
"OptionSaveMetadataAsHidden": "Save metadata and images as hidden files",
|
||||
"OptionSaveMetadataAsHiddenHelp": "Changing this will apply to new metadata saved going forward. Existing metadata files will be updated the next time they are saved by the server.",
|
||||
"OptionSpecialEpisode": "Specials",
|
||||
"OptionSubstring": "Substring",
|
||||
"OptionTrackName": "Track Name",
|
||||
"OptionTvdbRating": "TheTVDB Rating",
|
||||
"OptionUnairedEpisode": "Unaired Episodes",
|
||||
@ -1501,14 +1395,10 @@
|
||||
"Sunday": "Sunday",
|
||||
"SyncPlayAccessHelp": "The SyncPlay feature enables to sync playback with other devices. Select the level of access this user has to the SyncPlay.",
|
||||
"SyncPlayGroupDefaultTitle": "{0}'s group",
|
||||
"SystemDlnaProfilesHelp": "System profiles are read-only. Changes to a system profile will be saved to a new custom profile.",
|
||||
"TabAccess": "Access",
|
||||
"TabAdvanced": "Advanced",
|
||||
"TabCatalog": "Catalog",
|
||||
"TabCodecs": "Codecs",
|
||||
"TabContainers": "Containers",
|
||||
"TabDashboard": "Dashboard",
|
||||
"TabDirectPlay": "Direct Playback",
|
||||
"TabLatest": "Recently Added",
|
||||
"TabLogs": "Logs",
|
||||
"TabMusic": "Music",
|
||||
@ -1519,9 +1409,7 @@
|
||||
"TabOther": "Other",
|
||||
"TabParentalControl": "Parental Control",
|
||||
"TabPlugins": "Plugins",
|
||||
"TabProfiles": "Profiles",
|
||||
"TabRepositories": "Repositories",
|
||||
"TabResponses": "Responses",
|
||||
"TabScheduledTasks": "Scheduled Tasks",
|
||||
"TabServer": "Server",
|
||||
"TabStreaming": "Streaming",
|
||||
@ -1578,10 +1466,6 @@
|
||||
"UserMenu": "User Menu",
|
||||
"UserProfilesIntro": "Jellyfin includes support for user profiles with granular display settings, play state, and parental controls.",
|
||||
"ValueAlbumCount": "{0} albums",
|
||||
"ValueAudioCodec": "Audio Codec: {0}",
|
||||
"ValueCodec": "Codec: {0}",
|
||||
"ValueConditions": "Conditions: {0}",
|
||||
"ValueContainer": "Container: {0}",
|
||||
"ValueDiscNumber": "Disc {0}",
|
||||
"ValueEpisodeCount": "{0} episodes",
|
||||
"ValueMinutes": "{0} min",
|
||||
@ -1599,7 +1483,6 @@
|
||||
"ValueSpecialEpisodeName": "Special - {0}",
|
||||
"ValueTimeLimitMultiHour": "Time limit: {0} hours",
|
||||
"ValueTimeLimitSingleHour": "Time limit: 1 hour",
|
||||
"ValueVideoCodec": "Video Codec: {0}",
|
||||
"Vertical": "Vertical",
|
||||
"Video": "Video",
|
||||
"VideoAudio": "Video Audio",
|
||||
@ -1615,7 +1498,6 @@
|
||||
"WriteAccessRequired": "Jellyfin requires write access to this folder. Please ensure write access and try again.",
|
||||
"Writer": "Writer",
|
||||
"Writers": "Writers",
|
||||
"XmlDocumentAttributeListHelp": "These attributes are applied to the root element of every XML response.",
|
||||
"XmlTvKidsCategoriesHelp": "Programs with these categories will be displayed as programs for children. Separate multiple with '|'.",
|
||||
"XmlTvMovieCategoriesHelp": "Programs with these categories will be displayed as movies. Separate multiple with '|'.",
|
||||
"XmlTvNewsCategoriesHelp": "Programs with these categories will be displayed as news programs. Separate multiple with '|'.",
|
||||
|
Loading…
Reference in New Issue
Block a user