mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge branch 'master' into audio-normalization
This commit is contained in:
commit
8d5475a21b
143 changed files with 1557 additions and 1079 deletions
|
@ -46,6 +46,7 @@ module.exports = {
|
|||
'keyword-spacing': ['error'],
|
||||
'no-throw-literal': ['error'],
|
||||
'max-statements-per-line': ['error'],
|
||||
'max-params': ['error', 7],
|
||||
'no-duplicate-imports': ['error'],
|
||||
'no-empty-function': ['error'],
|
||||
'no-floating-decimal': ['error'],
|
||||
|
@ -67,6 +68,7 @@ module.exports = {
|
|||
'padded-blocks': ['error', 'never'],
|
||||
'prefer-const': ['error', { 'destructuring': 'all' }],
|
||||
'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],
|
||||
'radix': ['error'],
|
||||
'@babel/semi': ['error'],
|
||||
'space-before-blocks': ['error'],
|
||||
'space-infix-ops': 'error',
|
||||
|
|
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
|
@ -21,11 +21,11 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2.2.5
|
||||
uses: github/codeql-action/init@16964e90ba004cdf0cd845b866b5df21038b7723 # v2.2.6
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-extended
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2.2.5
|
||||
uses: github/codeql-action/autobuild@16964e90ba004cdf0cd845b866b5df21038b7723 # v2.2.6
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2.2.5
|
||||
uses: github/codeql-action/analyze@16964e90ba004cdf0cd845b866b5df21038b7723 # v2.2.6
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
- [edvwib](https://github.com/edvwib)
|
||||
- [Rob Farraher](https://github.com/farraherbg)
|
||||
- [TelepathicWalrus](https://github.com/TelepathicWalrus)
|
||||
- [Pier-Luc Ducharme](https://github.com/pl-ducharme)
|
||||
|
||||
# Emby Contributors
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM fedora:37
|
||||
FROM fedora:38
|
||||
|
||||
# Docker build arguments
|
||||
ARG SOURCE_DIR=/jellyfin
|
||||
|
|
951
package-lock.json
generated
951
package-lock.json
generated
File diff suppressed because it is too large
Load diff
24
package.json
24
package.json
|
@ -5,7 +5,7 @@
|
|||
"repository": "https://github.com/jellyfin/jellyfin-web",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.20.12",
|
||||
"@babel/core": "7.21.0",
|
||||
"@babel/eslint-parser": "7.19.1",
|
||||
"@babel/eslint-plugin": "7.19.1",
|
||||
"@babel/plugin-proposal-class-properties": "7.18.6",
|
||||
|
@ -13,14 +13,14 @@
|
|||
"@babel/plugin-transform-modules-umd": "7.18.6",
|
||||
"@babel/preset-env": "7.20.2",
|
||||
"@babel/preset-react": "7.18.6",
|
||||
"@babel/preset-typescript": "7.18.6",
|
||||
"@babel/preset-typescript": "7.21.0",
|
||||
"@types/escape-html": "1.0.2",
|
||||
"@types/loadable__component": "5.13.4",
|
||||
"@types/lodash-es": "4.17.6",
|
||||
"@types/react": "17.0.53",
|
||||
"@types/react-dom": "17.0.19",
|
||||
"@typescript-eslint/eslint-plugin": "5.52.0",
|
||||
"@typescript-eslint/parser": "5.52.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.54.0",
|
||||
"@typescript-eslint/parser": "5.54.0",
|
||||
"@uupaa/dynamic-import-polyfill": "1.0.2",
|
||||
"autoprefixer": "10.4.13",
|
||||
"babel-loader": "9.1.2",
|
||||
|
@ -32,7 +32,7 @@
|
|||
"css-loader": "6.7.3",
|
||||
"cssnano": "5.1.15",
|
||||
"es-check": "7.1.0",
|
||||
"eslint": "8.34.0",
|
||||
"eslint": "8.35.0",
|
||||
"eslint-plugin-compat": "4.1.2",
|
||||
"eslint-plugin-eslint-comments": "3.2.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
|
@ -49,11 +49,11 @@
|
|||
"postcss-loader": "7.0.2",
|
||||
"postcss-preset-env": "8.0.1",
|
||||
"postcss-scss": "4.0.6",
|
||||
"sass": "1.58.1",
|
||||
"sass": "1.58.3",
|
||||
"sass-loader": "13.2.0",
|
||||
"source-map-loader": "4.0.1",
|
||||
"style-loader": "3.3.1",
|
||||
"stylelint": "14.16.1",
|
||||
"stylelint": "15.2.0",
|
||||
"stylelint-config-rational-order": "0.1.2",
|
||||
"stylelint-no-browser-hacks": "1.2.1",
|
||||
"stylelint-order": "6.0.2",
|
||||
|
@ -77,10 +77,10 @@
|
|||
"@jellyfin/libass-wasm": "4.1.1",
|
||||
"@jellyfin/sdk": "unstable",
|
||||
"@loadable/component": "5.15.3",
|
||||
"blurhash": "2.0.4",
|
||||
"blurhash": "2.0.5",
|
||||
"classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
|
||||
"classnames": "2.3.2",
|
||||
"core-js": "3.28.0",
|
||||
"core-js": "3.29.0",
|
||||
"date-fns": "2.29.3",
|
||||
"dompurify": "2.4.4",
|
||||
"epubjs": "0.4.2",
|
||||
|
@ -89,11 +89,11 @@
|
|||
"flv.js": "1.6.2",
|
||||
"headroom.js": "0.12.0",
|
||||
"history": "5.3.0",
|
||||
"hls.js": "0.14.17",
|
||||
"hls.js": "1.3.4",
|
||||
"intersection-observer": "0.12.2",
|
||||
"jellyfin-apiclient": "1.10.0",
|
||||
"jquery": "3.6.3",
|
||||
"jstree": "3.3.14",
|
||||
"jstree": "3.3.15",
|
||||
"libarchive.js": "1.3.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"marked": "4.2.12",
|
||||
|
@ -102,7 +102,7 @@
|
|||
"pdfjs-dist": "2.16.105",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-router-dom": "6.8.1",
|
||||
"react-router-dom": "6.8.2",
|
||||
"resize-observer-polyfill": "1.5.1",
|
||||
"screenfull": "6.0.2",
|
||||
"sortablejs": "1.15.0",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { FunctionComponent, useEffect, useState } from 'react';
|
||||
import { Outlet, useNavigate } from 'react-router-dom';
|
||||
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
|
||||
import type { ConnectResponse } from 'jellyfin-apiclient';
|
||||
|
||||
import alert from './alert';
|
||||
|
@ -31,11 +31,11 @@ const ConnectionRequired: FunctionComponent<ConnectionRequiredProps> = ({
|
|||
isUserRequired = true
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const [ isLoading, setIsLoading ] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const bounce = async (connectionResponse: ConnectResponse) => {
|
||||
switch (connectionResponse.State) {
|
||||
case ConnectionState.SignedIn:
|
||||
|
@ -45,12 +45,12 @@ const ConnectionRequired: FunctionComponent<ConnectionRequiredProps> = ({
|
|||
return;
|
||||
case ConnectionState.ServerSignIn:
|
||||
// Bounce to the login page
|
||||
console.debug('[ConnectionRequired] not logged in, redirecting to login page');
|
||||
navigate(BounceRoutes.Login, {
|
||||
state: {
|
||||
serverid: connectionResponse.ApiClient.serverId()
|
||||
}
|
||||
});
|
||||
if (location.pathname === BounceRoutes.Login) {
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
console.debug('[ConnectionRequired] not logged in, redirecting to login page');
|
||||
navigate(`${BounceRoutes.Login}?serverid=${connectionResponse.ApiClient.serverId()}`);
|
||||
}
|
||||
return;
|
||||
case ConnectionState.ServerSelection:
|
||||
// Bounce to select server page
|
||||
|
@ -144,7 +144,7 @@ const ConnectionRequired: FunctionComponent<ConnectionRequiredProps> = ({
|
|||
};
|
||||
|
||||
validateConnection();
|
||||
}, [ isAdminRequired, isUserRequired, navigate ]);
|
||||
}, [ isAdminRequired, isUserRequired, location.pathname, navigate ]);
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading />;
|
||||
|
|
|
@ -19,7 +19,7 @@ import template from './accessSchedule.template.html';
|
|||
const pct = hours % 1;
|
||||
|
||||
if (pct) {
|
||||
minutes = parseInt(60 * pct);
|
||||
minutes = parseInt(60 * pct, 10);
|
||||
}
|
||||
|
||||
return datetime.getDisplayTime(new Date(2000, 1, 1, hours, minutes, 0, 0));
|
||||
|
|
|
@ -6,7 +6,7 @@ import dom from '../../scripts/dom';
|
|||
import '../../elements/emby-button/emby-button';
|
||||
import './actionSheet.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../components/listview/listview.scss';
|
||||
|
||||
function getOffsets(elems) {
|
||||
|
|
|
@ -64,10 +64,10 @@ import { toBoolean } from '../utils/string.ts';
|
|||
|
||||
function reloadData(instance, elem, apiClient, startIndex, limit) {
|
||||
if (startIndex == null) {
|
||||
startIndex = parseInt(elem.getAttribute('data-activitystartindex') || '0');
|
||||
startIndex = parseInt(elem.getAttribute('data-activitystartindex') || '0', 10);
|
||||
}
|
||||
|
||||
limit = limit || parseInt(elem.getAttribute('data-activitylimit') || '7');
|
||||
limit = limit || parseInt(elem.getAttribute('data-activitylimit') || '7', 10);
|
||||
const minDate = new Date();
|
||||
const hasUserId = toBoolean(elem.getAttribute('data-useractivity'), true);
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ class AppRouter {
|
|||
isBack: action === Action.Pop
|
||||
});
|
||||
} else {
|
||||
console.info('[appRouter] "%s" route not found', normalizedPath, location);
|
||||
// The route is not registered here, so it should be handled by react-router
|
||||
this.currentRouteInfo = {
|
||||
route: {},
|
||||
path: normalizedPath + location.search
|
||||
|
|
|
@ -663,7 +663,7 @@ import { appRouter } from '../appRouter';
|
|||
const character = String(str.slice(charIndex, charIndex + 1).charCodeAt());
|
||||
let sum = 0;
|
||||
for (let i = 0; i < character.length; i++) {
|
||||
sum += parseInt(character.charAt(i));
|
||||
sum += parseInt(character.charAt(i), 10);
|
||||
}
|
||||
const index = String(sum).slice(-1);
|
||||
|
||||
|
@ -773,27 +773,24 @@ import { appRouter } from '../appRouter';
|
|||
* @param {Object} item - Item used to generate the footer text.
|
||||
* @param {Object} apiClient - API client instance.
|
||||
* @param {Object} options - Options used to generate the footer text.
|
||||
* @param {string} showTitle - Flag to show the title in the footer.
|
||||
* @param {boolean} forceName - Flag to force showing the name of the item.
|
||||
* @param {boolean} overlayText - Flag to show overlay text.
|
||||
* @param {Object} imgUrl - Object representing the card's image URL.
|
||||
* @param {string} footerClass - CSS classes of the footer element.
|
||||
* @param {string} progressHtml - HTML markup of the progress bar element.
|
||||
* @param {string} logoUrl - URL of the logo for the item.
|
||||
* @param {boolean} isOuterFooter - Flag to mark the text as outer footer.
|
||||
* @param {Object} flags - Various flags for the footer
|
||||
* @param {Object} urls - Various urls for the footer
|
||||
* @returns {string} HTML markup of the card's footer text element.
|
||||
*/
|
||||
function getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerClass, progressHtml, logoUrl, isOuterFooter) {
|
||||
function getCardFooterText(item, apiClient, options, footerClass, progressHtml, flags, urls) {
|
||||
item = item.ProgramInfo || item;
|
||||
let html = '';
|
||||
|
||||
if (logoUrl) {
|
||||
html += '<div class="lazy cardFooterLogo" data-src="' + logoUrl + '"></div>';
|
||||
if (urls.logoUrl) {
|
||||
html += '<div class="lazy cardFooterLogo" data-src="' + urls.logoUrl + '"></div>';
|
||||
}
|
||||
|
||||
const showOtherText = isOuterFooter ? !overlayText : overlayText;
|
||||
const showTitle = options.showTitle === 'auto' ? true : (options.showTitle || item.Type === 'PhotoAlbum' || item.Type === 'Folder');
|
||||
const showOtherText = flags.isOuterFooter ? !flags.overlayText : flags.overlayText;
|
||||
|
||||
if (isOuterFooter && options.cardLayout && layoutManager.mobile && options.cardFooterAside !== 'none') {
|
||||
if (flags.isOuterFooter && options.cardLayout && layoutManager.mobile && options.cardFooterAside !== 'none') {
|
||||
html += `<button is="paper-icon-button-light" class="itemAction btnCardOptions cardText-secondary" data-action="menu" title="${globalize.translate('ButtonMore')}"><span class="material-icons more_vert" aria-hidden="true"></span></button>`;
|
||||
}
|
||||
|
||||
|
@ -805,7 +802,7 @@ import { appRouter } from '../appRouter';
|
|||
let titleAdded;
|
||||
|
||||
if (showOtherText && (options.showParentTitle || options.showParentTitleOrTitle) && !parentTitleUnderneath) {
|
||||
if (isOuterFooter && item.Type === 'Episode' && item.SeriesName) {
|
||||
if (flags.isOuterFooter && item.Type === 'Episode' && item.SeriesName) {
|
||||
if (item.SeriesId) {
|
||||
lines.push(getTextActionButton({
|
||||
Id: item.SeriesId,
|
||||
|
@ -835,7 +832,7 @@ import { appRouter } from '../appRouter';
|
|||
}
|
||||
|
||||
let showMediaTitle = (showTitle && !titleAdded) || (options.showParentTitleOrTitle && !lines.length);
|
||||
if (!showMediaTitle && !titleAdded && (showTitle || forceName)) {
|
||||
if (!showMediaTitle && !titleAdded && (showTitle || flags.forceName)) {
|
||||
showMediaTitle = true;
|
||||
}
|
||||
|
||||
|
@ -856,7 +853,7 @@ import { appRouter } from '../appRouter';
|
|||
|
||||
if (showOtherText) {
|
||||
if (options.showParentTitle && parentTitleUnderneath) {
|
||||
if (isOuterFooter && item.AlbumArtists && item.AlbumArtists.length) {
|
||||
if (flags.isOuterFooter && item.AlbumArtists && item.AlbumArtists.length) {
|
||||
item.AlbumArtists[0].Type = 'MusicArtist';
|
||||
item.AlbumArtists[0].IsFolder = true;
|
||||
lines.push(getTextActionButton(item.AlbumArtists[0], null, serverId));
|
||||
|
@ -991,23 +988,23 @@ import { appRouter } from '../appRouter';
|
|||
}
|
||||
}
|
||||
|
||||
if ((showTitle || !imgUrl) && forceName && overlayText && lines.length === 1) {
|
||||
if ((showTitle || !urls.imgUrl) && flags.forceName && flags.overlayText && lines.length === 1) {
|
||||
lines = [];
|
||||
}
|
||||
|
||||
if (overlayText && showTitle) {
|
||||
if (flags.overlayText && showTitle) {
|
||||
lines = [escapeHtml(item.Name)];
|
||||
}
|
||||
|
||||
const addRightTextMargin = isOuterFooter && options.cardLayout && !options.centerText && options.cardFooterAside !== 'none' && layoutManager.mobile;
|
||||
const addRightTextMargin = flags.isOuterFooter && options.cardLayout && !options.centerText && options.cardFooterAside !== 'none' && layoutManager.mobile;
|
||||
|
||||
html += getCardTextLines(lines, cssClass, !options.overlayText, isOuterFooter, options.cardLayout, addRightTextMargin, options.lines);
|
||||
html += getCardTextLines(lines, cssClass, !options.overlayText, flags.isOuterFooter, options.cardLayout, addRightTextMargin, options.lines);
|
||||
|
||||
if (progressHtml) {
|
||||
html += progressHtml;
|
||||
}
|
||||
|
||||
if (html && (!isOuterFooter || logoUrl || options.cardLayout)) {
|
||||
if (html && (!flags.isOuterFooter || urls.logoUrl || options.cardLayout)) {
|
||||
html = '<div class="' + footerClass + '">' + html;
|
||||
|
||||
//cardFooter
|
||||
|
@ -1217,7 +1214,6 @@ import { appRouter } from '../appRouter';
|
|||
|
||||
const forceName = imgInfo.forceName;
|
||||
|
||||
const showTitle = options.showTitle === 'auto' ? true : (options.showTitle || item.Type === 'PhotoAlbum' || item.Type === 'Folder');
|
||||
const overlayText = options.overlayText;
|
||||
|
||||
let cardImageContainerClass = 'cardImageContainer';
|
||||
|
@ -1265,7 +1261,7 @@ import { appRouter } from '../appRouter';
|
|||
logoUrl = null;
|
||||
|
||||
footerCssClass = progressHtml ? 'innerCardFooter fullInnerCardFooter' : 'innerCardFooter';
|
||||
innerCardFooter += getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerCssClass, progressHtml, logoUrl, false);
|
||||
innerCardFooter += getCardFooterText(item, apiClient, options, footerCssClass, progressHtml, { forceName, overlayText, isOuterFooter: false }, { imgUrl, logoUrl });
|
||||
footerOverlayed = true;
|
||||
} else if (progressHtml) {
|
||||
innerCardFooter += '<div class="innerCardFooter fullInnerCardFooter innerCardFooterClear">';
|
||||
|
@ -1292,7 +1288,7 @@ import { appRouter } from '../appRouter';
|
|||
logoUrl = null;
|
||||
}
|
||||
|
||||
outerCardFooter = getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerCssClass, progressHtml, logoUrl, true);
|
||||
outerCardFooter = getCardFooterText(item, apiClient, options, footerCssClass, progressHtml, { forceName, overlayText, isOuterFooter: true }, { imgUrl, logoUrl });
|
||||
}
|
||||
|
||||
if (outerCardFooter && !options.cardLayout) {
|
||||
|
|
|
@ -12,7 +12,7 @@ import '../../elements/emby-input/emby-input';
|
|||
import '../../elements/emby-select/emby-select';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../formdialog.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import '../../elements/emby-button/emby-button';
|
|||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../elements/emby-input/emby-input';
|
||||
import '../formdialog.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import template from './dialog.template.html';
|
||||
|
||||
/* eslint-disable indent */
|
||||
|
|
|
@ -7,7 +7,7 @@ import { toBoolean } from '../../utils/string.ts';
|
|||
import dom from '../../scripts/dom';
|
||||
|
||||
import './dialoghelper.scss';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
|
||||
/* eslint-disable indent */
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import datetime from '../../scripts/datetime';
|
|||
import globalize from '../../scripts/globalize';
|
||||
import loading from '../loading/loading';
|
||||
import skinManager from '../../scripts/themeManager';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
import Events from '../../utils/events.ts';
|
||||
import '../../elements/emby-select/emby-select';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
|
@ -35,7 +36,7 @@ import template from './displaySettings.template.html';
|
|||
|
||||
function loadScreensavers(context, userSettings) {
|
||||
const selectScreensaver = context.querySelector('.selectScreensaver');
|
||||
const options = pluginManager.ofType('screensaver').map(plugin => {
|
||||
const options = pluginManager.ofType(PluginType.Screensaver).map(plugin => {
|
||||
return {
|
||||
name: plugin.name,
|
||||
value: plugin.id
|
||||
|
|
|
@ -6,7 +6,7 @@ import imageLoader from './images/imageLoader';
|
|||
import globalize from '../scripts/globalize';
|
||||
import layoutManager from './layoutManager';
|
||||
import { getParameterByName } from '../utils/url.ts';
|
||||
import '../assets/css/scrollstyles.scss';
|
||||
import '../styles/scrollstyles.scss';
|
||||
import '../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
|
||||
/* eslint-disable indent */
|
||||
|
|
|
@ -13,7 +13,7 @@ import '../../elements/emby-button/paper-icon-button-light';
|
|||
import '../../elements/emby-select/emby-select';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../formdialog.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './filtermenu.template.html';
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import ServerConnections from './ServerConnections';
|
|||
const playedIndicator = card.querySelector('.playedIndicator');
|
||||
const playedIndicatorHtml = playedIndicator ? playedIndicator.innerHTML : null;
|
||||
const options = {
|
||||
Limit: parseInt(playedIndicatorHtml || '10'),
|
||||
Limit: parseInt(playedIndicatorHtml || '10', 10),
|
||||
Fields: 'PrimaryImageAspectRatio,DateCreated',
|
||||
ParentId: itemId,
|
||||
GroupItems: false
|
||||
|
|
|
@ -17,13 +17,13 @@ import dom from '../../scripts/dom';
|
|||
import './guide.scss';
|
||||
import './programs.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-programcell/emby-programcell';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../elements/emby-tabs/emby-tabs';
|
||||
import '../../elements/emby-scroller/emby-scroller';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import 'webcomponents.js/webcomponents-lite';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './tvguide.template.html';
|
||||
|
@ -345,7 +345,9 @@ function Guide(options) {
|
|||
}
|
||||
|
||||
apiClient.getLiveTvPrograms(programQuery).then(function (programsResult) {
|
||||
renderGuide(context, date, channelsResult.Items, programsResult.Items, renderOptions, apiClient, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender);
|
||||
const guideOptions = { focusProgramOnRender, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs };
|
||||
|
||||
renderGuide(context, date, channelsResult.Items, programsResult.Items, renderOptions, guideOptions, apiClient);
|
||||
|
||||
hideLoading();
|
||||
});
|
||||
|
@ -667,7 +669,7 @@ function Guide(options) {
|
|||
return (channelIndex * 10000000) + (start.getTime() / 60000);
|
||||
}
|
||||
|
||||
function renderGuide(context, date, channels, programs, renderOptions, apiClient, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) {
|
||||
function renderGuide(context, date, channels, programs, renderOptions, guideOptions, apiClient) {
|
||||
programs.sort(function (a, b) {
|
||||
return getProgramSortOrder(a, channels) - getProgramSortOrder(b, channels);
|
||||
});
|
||||
|
@ -689,11 +691,11 @@ function Guide(options) {
|
|||
items = {};
|
||||
renderPrograms(context, date, channels, programs, renderOptions);
|
||||
|
||||
if (focusProgramOnRender) {
|
||||
focusProgram(context, itemId, channelRowId, focusToTimeMs, startTimeOfDayMs);
|
||||
if (guideOptions.focusProgramOnRender) {
|
||||
focusProgram(context, itemId, channelRowId, guideOptions.focusToTimeMs, guideOptions.startTimeOfDayMs);
|
||||
}
|
||||
|
||||
scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs);
|
||||
scrollProgramGridToTimeMs(context, guideOptions.scrollToTimeMs, guideOptions.startTimeOfDayMs);
|
||||
}
|
||||
|
||||
function scrollProgramGridToTimeMs(context, scrollToTimeMs, startTimeOfDayMs) {
|
||||
|
@ -1147,12 +1149,12 @@ function Guide(options) {
|
|||
guideContext.querySelector('.guideDateTabs').addEventListener('tabchange', function (e) {
|
||||
const allTabButtons = e.target.querySelectorAll('.guide-date-tab-button');
|
||||
|
||||
const tabButton = allTabButtons[parseInt(e.detail.selectedTabIndex)];
|
||||
const tabButton = allTabButtons[parseInt(e.detail.selectedTabIndex, 10)];
|
||||
if (tabButton) {
|
||||
const previousButton = e.detail.previousIndex == null ? null : allTabButtons[parseInt(e.detail.previousIndex)];
|
||||
const previousButton = e.detail.previousIndex == null ? null : allTabButtons[parseInt(e.detail.previousIndex, 10)];
|
||||
|
||||
const date = new Date();
|
||||
date.setTime(parseInt(tabButton.getAttribute('data-date')));
|
||||
date.setTime(parseInt(tabButton.getAttribute('data-date'), 10));
|
||||
|
||||
const scrollWidth = programGrid.scrollWidth;
|
||||
let scrollToTimeMs;
|
||||
|
@ -1164,7 +1166,7 @@ function Guide(options) {
|
|||
|
||||
if (previousButton) {
|
||||
const previousDate = new Date();
|
||||
previousDate.setTime(parseInt(previousButton.getAttribute('data-date')));
|
||||
previousDate.setTime(parseInt(previousButton.getAttribute('data-date'), 10));
|
||||
|
||||
scrollToTimeMs += (previousDate.getHours() * 60 * 60 * 1000);
|
||||
scrollToTimeMs += (previousDate.getMinutes() * 60 * 1000);
|
||||
|
|
|
@ -96,7 +96,7 @@ import template from './imageeditor.template.html';
|
|||
return apiClient.getScaledImageUrl(item.Id || item.ItemId, options);
|
||||
}
|
||||
|
||||
function getCardHtml(image, index, numImages, apiClient, imageProviders, imageSize, tagName, enableFooterButtons) {
|
||||
function getCardHtml(image, apiClient, options) {
|
||||
// TODO move card creation code to Card component
|
||||
|
||||
let html = '';
|
||||
|
@ -106,7 +106,7 @@ import template from './imageeditor.template.html';
|
|||
|
||||
cssClass += ' backdropCard backdropCard-scalable';
|
||||
|
||||
if (tagName === 'button') {
|
||||
if (options.tagName === 'button') {
|
||||
cssClass += ' btnImageCard';
|
||||
|
||||
if (layoutManager.tv) {
|
||||
|
@ -122,7 +122,7 @@ import template from './imageeditor.template.html';
|
|||
html += '<div class="' + cssClass + '"';
|
||||
}
|
||||
|
||||
html += ' data-id="' + currentItem.Id + '" data-serverid="' + apiClient.serverId() + '" data-index="' + index + '" data-numimages="' + numImages + '" data-imagetype="' + image.ImageType + '" data-providers="' + imageProviders.length + '"';
|
||||
html += ' data-id="' + currentItem.Id + '" data-serverid="' + apiClient.serverId() + '" data-index="' + options.index + '" data-numimages="' + options.numImages + '" data-imagetype="' + image.ImageType + '" data-providers="' + options.imageProviders.length + '"';
|
||||
|
||||
html += '>';
|
||||
|
||||
|
@ -132,7 +132,7 @@ import template from './imageeditor.template.html';
|
|||
|
||||
html += '<div class="cardContent">';
|
||||
|
||||
const imageUrl = getImageUrl(currentItem, apiClient, image.ImageType, image.ImageIndex, { maxWidth: imageSize });
|
||||
const imageUrl = getImageUrl(currentItem, apiClient, image.ImageType, image.ImageIndex, { maxWidth: options.imageSize });
|
||||
|
||||
html += '<div class="cardImageContainer" style="background-image:url(\'' + imageUrl + '\');background-position:center center;background-size:contain;"></div>';
|
||||
|
||||
|
@ -151,23 +151,23 @@ import template from './imageeditor.template.html';
|
|||
}
|
||||
html += '</div>';
|
||||
|
||||
if (enableFooterButtons) {
|
||||
if (options.enableFooterButtons) {
|
||||
html += '<div class="cardText cardTextCentered">';
|
||||
|
||||
if (image.ImageType === 'Backdrop') {
|
||||
if (index > 0) {
|
||||
if (options.index > 0) {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnMoveImage autoSize" data-imagetype="' + image.ImageType + '" data-index="' + image.ImageIndex + '" data-newindex="' + (image.ImageIndex - 1) + '" title="' + globalize.translate('MoveLeft') + '"><span class="material-icons chevron_left"></span></button>';
|
||||
} else {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="autoSize" disabled title="' + globalize.translate('MoveLeft') + '"><span class="material-icons chevron_left" aria-hidden="true"></span></button>';
|
||||
}
|
||||
|
||||
if (index < numImages - 1) {
|
||||
if (options.index < options.numImages - 1) {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnMoveImage autoSize" data-imagetype="' + image.ImageType + '" data-index="' + image.ImageIndex + '" data-newindex="' + (image.ImageIndex + 1) + '" title="' + globalize.translate('MoveRight') + '"><span class="material-icons chevron_right" aria-hidden="true"></span></button>';
|
||||
} else {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="autoSize" disabled title="' + globalize.translate('MoveRight') + '"><span class="material-icons chevron_right" aria-hidden="true"></span></button>';
|
||||
}
|
||||
} else {
|
||||
if (imageProviders.length) {
|
||||
if (options.imageProviders.length) {
|
||||
html += '<button type="button" is="paper-icon-button-light" data-imagetype="' + image.ImageType + '" class="btnSearchImages autoSize" title="' + globalize.translate('Search') + '"><span class="material-icons search" aria-hidden="true"></span></button>';
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ import template from './imageeditor.template.html';
|
|||
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</' + tagName + '>';
|
||||
html += '</' + options.tagName + '>';
|
||||
|
||||
return html;
|
||||
}
|
||||
|
@ -226,7 +226,8 @@ import template from './imageeditor.template.html';
|
|||
|
||||
for (let i = 0, length = images.length; i < length; i++) {
|
||||
const image = images[i];
|
||||
html += getCardHtml(image, i, length, apiClient, imageProviders, imageSize, tagName, enableFooterButtons);
|
||||
const options = { index: i, numImages: length, imageProviders, imageSize, tagName, enableFooterButtons };
|
||||
html += getCardHtml(image, apiClient, options);
|
||||
}
|
||||
|
||||
elem.innerHTML = html;
|
||||
|
@ -277,9 +278,9 @@ import template from './imageeditor.template.html';
|
|||
const apiClient = ServerConnections.getApiClient(serverId);
|
||||
|
||||
const type = imageCard.getAttribute('data-imagetype');
|
||||
const index = parseInt(imageCard.getAttribute('data-index'));
|
||||
const providerCount = parseInt(imageCard.getAttribute('data-providers'));
|
||||
const numImages = parseInt(imageCard.getAttribute('data-numimages'));
|
||||
const index = parseInt(imageCard.getAttribute('data-index'), 10);
|
||||
const providerCount = parseInt(imageCard.getAttribute('data-providers'), 10);
|
||||
const numImages = parseInt(imageCard.getAttribute('data-numimages'), 10);
|
||||
|
||||
import('../actionSheet/actionSheet').then(({default: actionSheet}) => {
|
||||
const commands = [];
|
||||
|
@ -384,7 +385,7 @@ import template from './imageeditor.template.html';
|
|||
addListeners(context, 'btnDeleteImage', 'click', function () {
|
||||
const type = this.getAttribute('data-imagetype');
|
||||
let index = this.getAttribute('data-index');
|
||||
index = index === 'null' ? null : parseInt(index);
|
||||
index = index === 'null' ? null : parseInt(index, 10);
|
||||
const apiClient = ServerConnections.getApiClient(currentItem.ServerId);
|
||||
deleteImage(context, currentItem.Id, type, index, apiClient, true);
|
||||
});
|
||||
|
|
|
@ -20,7 +20,7 @@ import '../../elements/emby-button/emby-button';
|
|||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../formdialog.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './itemMediaInfo.template.html';
|
||||
|
||||
|
@ -138,7 +138,7 @@ const attributeDelimiterHtml = layoutManager.tv ? '' : '<span class="hide">: </s
|
|||
attributes.push(createAttribute(globalize.translate('MediaInfoChannels'), `${stream.Channels} ch`));
|
||||
}
|
||||
if (stream.BitRate) {
|
||||
attributes.push(createAttribute(globalize.translate('MediaInfoBitrate'), `${parseInt(stream.BitRate / 1000)} kbps`));
|
||||
attributes.push(createAttribute(globalize.translate('MediaInfoBitrate'), `${parseInt(stream.BitRate / 1000, 10)} kbps`));
|
||||
}
|
||||
if (stream.SampleRate) {
|
||||
attributes.push(createAttribute(globalize.translate('MediaInfoSampleRate'), `${stream.SampleRate} Hz`));
|
||||
|
|
|
@ -52,7 +52,7 @@ import datetime from '../../scripts/datetime';
|
|||
|
||||
if (value) {
|
||||
if (identifyField[i].type === 'number') {
|
||||
value = parseInt(value);
|
||||
value = parseInt(value, 10);
|
||||
}
|
||||
|
||||
lookupInfo[identifyField[i].getAttribute('data-lookup')] = value;
|
||||
|
@ -123,7 +123,7 @@ import datetime from '../../scripts/datetime';
|
|||
elem.innerHTML = html;
|
||||
|
||||
function onSearchImageClick() {
|
||||
const index = parseInt(this.getAttribute('data-index'));
|
||||
const index = parseInt(this.getAttribute('data-index'), 10);
|
||||
|
||||
const currentResult = results[index];
|
||||
|
||||
|
|
|
@ -531,7 +531,7 @@ import template from './libraryoptionseditor.template.html';
|
|||
PreferredMetadataLanguage: parent.querySelector('#selectLanguage').value,
|
||||
MetadataCountryCode: parent.querySelector('#selectCountry').value,
|
||||
SeasonZeroDisplayName: parent.querySelector('#txtSeasonZeroName').value,
|
||||
AutomaticRefreshIntervalDays: parseInt(parent.querySelector('#selectAutoRefreshInterval').value),
|
||||
AutomaticRefreshIntervalDays: parseInt(parent.querySelector('#selectAutoRefreshInterval').value, 10),
|
||||
EnableEmbeddedTitles: parent.querySelector('#chkEnableEmbeddedTitles').checked,
|
||||
EnableEmbeddedExtrasTitles: parent.querySelector('#chkEnableEmbeddedExtrasTitles').checked,
|
||||
EnableEmbeddedEpisodeInfos: parent.querySelector('#chkEnableEmbeddedEpisodeInfos').checked,
|
||||
|
|
|
@ -19,7 +19,7 @@ import '../../elements/emby-select/emby-select';
|
|||
import '../../elements/emby-toggle/emby-toggle';
|
||||
import '../listview/listview.scss';
|
||||
import '../formdialog.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import './style.scss';
|
||||
import toast from '../toast/toast';
|
||||
import alert from '../alert';
|
||||
|
@ -168,7 +168,7 @@ import template from './mediaLibraryCreator.template.html';
|
|||
|
||||
function onRemoveClick(e) {
|
||||
const button = dom.parentWithClass(e.target, 'btnRemovePath');
|
||||
const index = parseInt(button.getAttribute('data-index'));
|
||||
const index = parseInt(button.getAttribute('data-index'), 10);
|
||||
const location = pathInfos[index].Path;
|
||||
const locationLower = location.toLowerCase();
|
||||
pathInfos = pathInfos.filter(p => {
|
||||
|
|
|
@ -17,7 +17,7 @@ import '../listview/listview.scss';
|
|||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../formdialog.scss';
|
||||
import '../../elements/emby-toggle/emby-toggle';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import './style.scss';
|
||||
import toast from '../toast/toast';
|
||||
import confirm from '../confirm/confirm';
|
||||
|
@ -93,7 +93,7 @@ import template from './mediaLibraryEditor.template.html';
|
|||
const listItem = dom.parentWithClass(e.target, 'listItem');
|
||||
|
||||
if (listItem) {
|
||||
const index = parseInt(listItem.getAttribute('data-index'));
|
||||
const index = parseInt(listItem.getAttribute('data-index'), 10);
|
||||
const pathInfos = (currentOptions.library.LibraryOptions || {}).PathInfos || [];
|
||||
const pathInfo = index == null ? {} : pathInfos[index] || {};
|
||||
const originalPath = pathInfo.Path || (index == null ? null : currentOptions.library.Locations[index]);
|
||||
|
|
|
@ -15,8 +15,8 @@ import '../../elements/emby-textarea/emby-textarea';
|
|||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../formdialog.scss';
|
||||
import '../../assets/css/clearbutton.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/clearbutton.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import './style.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
|
@ -357,14 +357,14 @@ import template from './metadataEditor.template.html';
|
|||
let index;
|
||||
const btnDeletePerson = dom.parentWithClass(e.target, 'btnDeletePerson');
|
||||
if (btnDeletePerson) {
|
||||
index = parseInt(btnDeletePerson.getAttribute('data-index'));
|
||||
index = parseInt(btnDeletePerson.getAttribute('data-index'), 10);
|
||||
currentItem.People.splice(index, 1);
|
||||
populatePeople(context, currentItem.People);
|
||||
}
|
||||
|
||||
const btnEditPerson = dom.parentWithClass(e.target, 'btnEditPerson');
|
||||
if (btnEditPerson) {
|
||||
index = parseInt(btnEditPerson.getAttribute('data-index'));
|
||||
index = parseInt(btnEditPerson.getAttribute('data-index'), 10);
|
||||
editPerson(context, currentItem.People[index], index);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -114,8 +114,8 @@ import shell from '../../scripts/shell';
|
|||
const itemId = item.Id;
|
||||
|
||||
// Convert to ms
|
||||
const duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0);
|
||||
const currentTime = parseInt(playState.PositionTicks ? (playState.PositionTicks / 10000) : 0);
|
||||
const duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0, 10);
|
||||
const currentTime = parseInt(playState.PositionTicks ? (playState.PositionTicks / 10000) : 0, 10);
|
||||
|
||||
const isPaused = playState.IsPaused || false;
|
||||
const canSeek = playState.CanSeek || false;
|
||||
|
@ -247,7 +247,7 @@ import shell from '../../scripts/shell';
|
|||
navigator.mediaSession.setActionHandler('seekto', function (object) {
|
||||
const item = playbackManager.getPlayerState(currentPlayer).NowPlayingItem;
|
||||
// Convert to ms
|
||||
const duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0);
|
||||
const duration = parseInt(item.RunTimeTicks ? (item.RunTimeTicks / 10000) : 0, 10);
|
||||
const wantedTime = object.seekTime * 1000;
|
||||
playbackManager.seekPercent(wantedTime / duration * 100, currentPlayer);
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ import { appHost } from '../apphost';
|
|||
import Screenfull from 'screenfull';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import alert from '../alert';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
import { includesAny } from '../../utils/container.ts';
|
||||
|
||||
const UNLIMITED_ITEMS = -1;
|
||||
|
@ -299,20 +300,20 @@ function getAudioMaxValues(deviceProfile) {
|
|||
}
|
||||
|
||||
let startingPlaySession = new Date().getTime();
|
||||
function getAudioStreamUrl(item, transcodingProfile, directPlayContainers, maxBitrate, apiClient, maxAudioSampleRate, maxAudioBitDepth, maxAudioBitrate, startPosition) {
|
||||
function getAudioStreamUrl(item, transcodingProfile, directPlayContainers, apiClient, startPosition, maxValues) {
|
||||
const url = 'Audio/' + item.Id + '/universal';
|
||||
|
||||
startingPlaySession++;
|
||||
return apiClient.getUrl(url, {
|
||||
UserId: apiClient.getCurrentUserId(),
|
||||
DeviceId: apiClient.deviceId(),
|
||||
MaxStreamingBitrate: maxAudioBitrate || maxBitrate,
|
||||
MaxStreamingBitrate: maxValues.maxAudioBitrate || maxValues.maxBitrate,
|
||||
Container: directPlayContainers,
|
||||
TranscodingContainer: transcodingProfile.Container || null,
|
||||
TranscodingProtocol: transcodingProfile.Protocol || null,
|
||||
AudioCodec: transcodingProfile.AudioCodec,
|
||||
MaxAudioSampleRate: maxAudioSampleRate,
|
||||
MaxAudioBitDepth: maxAudioBitDepth,
|
||||
MaxAudioSampleRate: maxValues.maxAudioSampleRate,
|
||||
MaxAudioBitDepth: maxValues.maxAudioBitDepth,
|
||||
api_key: apiClient.accessToken(),
|
||||
PlaySessionId: startingPlaySession,
|
||||
StartTimeTicks: startPosition || 0,
|
||||
|
@ -344,7 +345,7 @@ function getAudioStreamUrlFromDeviceProfile(item, deviceProfile, maxBitrate, api
|
|||
|
||||
const maxValues = getAudioMaxValues(deviceProfile);
|
||||
|
||||
return getAudioStreamUrl(item, transcodingProfile, directPlayContainers, maxBitrate, apiClient, maxValues.maxAudioSampleRate, maxValues.maxAudioBitDepth, maxValues.maxAudioBitrate, startPosition);
|
||||
return getAudioStreamUrl(item, transcodingProfile, directPlayContainers, apiClient, startPosition, { maxBitrate, ...maxValues });
|
||||
}
|
||||
|
||||
function getStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPosition) {
|
||||
|
@ -377,7 +378,7 @@ function getStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPositio
|
|||
let streamUrl;
|
||||
|
||||
if (item.MediaType === 'Audio' && !itemHelper.isLocalItem(item)) {
|
||||
streamUrl = getAudioStreamUrl(item, audioTranscodingProfile, audioDirectPlayContainers, maxBitrate, apiClient, maxValues.maxAudioSampleRate, maxValues.maxAudioBitDepth, maxValues.maxAudioBitrate, startPosition);
|
||||
streamUrl = getAudioStreamUrl(item, audioTranscodingProfile, audioDirectPlayContainers, apiClient, startPosition, { maxBitrate, ...maxValues });
|
||||
}
|
||||
|
||||
streamUrls.push(streamUrl || '');
|
||||
|
@ -408,27 +409,12 @@ function setStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPositio
|
|||
});
|
||||
}
|
||||
|
||||
function getPlaybackInfo(player,
|
||||
apiClient,
|
||||
item,
|
||||
deviceProfile,
|
||||
maxBitrate,
|
||||
startPosition,
|
||||
isPlayback,
|
||||
mediaSourceId,
|
||||
audioStreamIndex,
|
||||
subtitleStreamIndex,
|
||||
liveStreamId,
|
||||
enableDirectPlay,
|
||||
enableDirectStream,
|
||||
allowVideoStreamCopy,
|
||||
allowAudioStreamCopy,
|
||||
secondarySubtitleStreamIndex) {
|
||||
function getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId, liveStreamId, options) {
|
||||
if (!itemHelper.isLocalItem(item) && item.MediaType === 'Audio' && !player.useServerPlaybackInfoForAudio) {
|
||||
return Promise.resolve({
|
||||
MediaSources: [
|
||||
{
|
||||
StreamUrl: getAudioStreamUrlFromDeviceProfile(item, deviceProfile, maxBitrate, apiClient, startPosition),
|
||||
StreamUrl: getAudioStreamUrlFromDeviceProfile(item, deviceProfile, options.maxBitrate, apiClient, options.startPosition),
|
||||
Id: item.Id,
|
||||
MediaStreams: [],
|
||||
RunTimeTicks: item.RunTimeTicks
|
||||
|
@ -446,10 +432,10 @@ function getPlaybackInfo(player,
|
|||
|
||||
const query = {
|
||||
UserId: apiClient.getCurrentUserId(),
|
||||
StartTimeTicks: startPosition || 0
|
||||
StartTimeTicks: options.startPosition || 0
|
||||
};
|
||||
|
||||
if (isPlayback) {
|
||||
if (options.isPlayback) {
|
||||
query.IsPlayback = true;
|
||||
query.AutoOpenLiveStream = true;
|
||||
} else {
|
||||
|
@ -457,27 +443,26 @@ function getPlaybackInfo(player,
|
|||
query.AutoOpenLiveStream = false;
|
||||
}
|
||||
|
||||
if (audioStreamIndex != null) {
|
||||
query.AudioStreamIndex = audioStreamIndex;
|
||||
if (options.audioStreamIndex != null) {
|
||||
query.AudioStreamIndex = options.audioStreamIndex;
|
||||
}
|
||||
if (subtitleStreamIndex != null) {
|
||||
query.SubtitleStreamIndex = subtitleStreamIndex;
|
||||
if (options.subtitleStreamIndex != null) {
|
||||
query.SubtitleStreamIndex = options.subtitleStreamIndex;
|
||||
}
|
||||
if (secondarySubtitleStreamIndex != null) {
|
||||
query.SecondarySubtitleStreamIndex = secondarySubtitleStreamIndex;
|
||||
if (options.secondarySubtitleStreamIndex != null) {
|
||||
query.SecondarySubtitleStreamIndex = options.secondarySubtitleStreamIndex;
|
||||
}
|
||||
if (enableDirectPlay != null) {
|
||||
query.EnableDirectPlay = enableDirectPlay;
|
||||
if (options.enableDirectPlay != null) {
|
||||
query.EnableDirectPlay = options.enableDirectPlay;
|
||||
}
|
||||
|
||||
if (enableDirectStream != null) {
|
||||
query.EnableDirectStream = enableDirectStream;
|
||||
if (options.enableDirectStream != null) {
|
||||
query.EnableDirectStream = options.enableDirectStream;
|
||||
}
|
||||
if (allowVideoStreamCopy != null) {
|
||||
query.AllowVideoStreamCopy = allowVideoStreamCopy;
|
||||
if (options.allowVideoStreamCopy != null) {
|
||||
query.AllowVideoStreamCopy = options.allowVideoStreamCopy;
|
||||
}
|
||||
if (allowAudioStreamCopy != null) {
|
||||
query.AllowAudioStreamCopy = allowAudioStreamCopy;
|
||||
if (options.allowAudioStreamCopy != null) {
|
||||
query.AllowAudioStreamCopy = options.allowAudioStreamCopy;
|
||||
}
|
||||
if (mediaSourceId) {
|
||||
query.MediaSourceId = mediaSourceId;
|
||||
|
@ -485,8 +470,8 @@ function getPlaybackInfo(player,
|
|||
if (liveStreamId) {
|
||||
query.LiveStreamId = liveStreamId;
|
||||
}
|
||||
if (maxBitrate) {
|
||||
query.MaxStreamingBitrate = maxBitrate;
|
||||
if (options.maxBitrate) {
|
||||
query.MaxStreamingBitrate = options.maxBitrate;
|
||||
}
|
||||
if (player.enableMediaProbe && !player.enableMediaProbe(item)) {
|
||||
query.EnableMediaProbe = false;
|
||||
|
@ -537,7 +522,7 @@ function getOptimalMediaSource(apiClient, item, versions) {
|
|||
});
|
||||
}
|
||||
|
||||
function getLiveStream(player, apiClient, item, playSessionId, deviceProfile, maxBitrate, startPosition, mediaSource, audioStreamIndex, subtitleStreamIndex) {
|
||||
function getLiveStream(player, apiClient, item, playSessionId, deviceProfile, mediaSource, options) {
|
||||
const postData = {
|
||||
DeviceProfile: deviceProfile,
|
||||
OpenToken: mediaSource.OpenToken
|
||||
|
@ -545,19 +530,19 @@ function getLiveStream(player, apiClient, item, playSessionId, deviceProfile, ma
|
|||
|
||||
const query = {
|
||||
UserId: apiClient.getCurrentUserId(),
|
||||
StartTimeTicks: startPosition || 0,
|
||||
StartTimeTicks: options.startPosition || 0,
|
||||
ItemId: item.Id,
|
||||
PlaySessionId: playSessionId
|
||||
};
|
||||
|
||||
if (maxBitrate) {
|
||||
query.MaxStreamingBitrate = maxBitrate;
|
||||
if (options.maxBitrate) {
|
||||
query.MaxStreamingBitrate = options.maxBitrate;
|
||||
}
|
||||
if (audioStreamIndex != null) {
|
||||
query.AudioStreamIndex = audioStreamIndex;
|
||||
if (options.audioStreamIndex != null) {
|
||||
query.AudioStreamIndex = options.audioStreamIndex;
|
||||
}
|
||||
if (subtitleStreamIndex != null) {
|
||||
query.SubtitleStreamIndex = subtitleStreamIndex;
|
||||
if (options.subtitleStreamIndex != null) {
|
||||
query.SubtitleStreamIndex = options.subtitleStreamIndex;
|
||||
}
|
||||
|
||||
// lastly, enforce player overrides for special situations
|
||||
|
@ -1706,7 +1691,7 @@ class PlaybackManager {
|
|||
|
||||
function changeStream(player, ticks, params) {
|
||||
if (canPlayerSeek(player) && params == null) {
|
||||
player.currentTime(parseInt(ticks / 10000));
|
||||
player.currentTime(parseInt(ticks / 10000, 10));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1730,20 +1715,33 @@ class PlaybackManager {
|
|||
const apiClient = ServerConnections.getApiClient(currentItem.ServerId);
|
||||
|
||||
if (ticks) {
|
||||
ticks = parseInt(ticks);
|
||||
ticks = parseInt(ticks, 10);
|
||||
}
|
||||
|
||||
const maxBitrate = params.MaxStreamingBitrate || self.getMaxStreamingBitrate(player);
|
||||
|
||||
const currentPlayOptions = currentItem.playOptions || getDefaultPlayOptions();
|
||||
|
||||
getPlaybackInfo(player, apiClient, currentItem, deviceProfile, maxBitrate, ticks, true, currentMediaSource.Id, audioStreamIndex, subtitleStreamIndex, liveStreamId, params.EnableDirectPlay, params.EnableDirectStream, params.AllowVideoStreamCopy, params.AllowAudioStreamCopy).then(function (result) {
|
||||
const options = {
|
||||
maxBitrate,
|
||||
startPosition: ticks,
|
||||
isPlayback: true,
|
||||
audioStreamIndex,
|
||||
subtitleStreamIndex,
|
||||
enableDirectPlay: params.EnableDirectPlay,
|
||||
enableDirectStream: params.EnableDirectStream,
|
||||
allowVideoStreamCopy: params.AllowVideoStreamCopy,
|
||||
allowAudioStreamCopy: params.AllowAudioStreamCopy
|
||||
};
|
||||
|
||||
getPlaybackInfo(player, apiClient, currentItem, deviceProfile, currentMediaSource.Id, liveStreamId, options).then(function (result) {
|
||||
if (validatePlaybackInfoResult(self, result)) {
|
||||
currentMediaSource = result.MediaSources[0];
|
||||
|
||||
const streamInfo = createStreamInfo(apiClient, currentItem.MediaType, currentItem, currentMediaSource, ticks, player);
|
||||
streamInfo.fullscreen = currentPlayOptions.fullscreen;
|
||||
streamInfo.lastMediaInfoQuery = lastMediaInfoQuery;
|
||||
streamInfo.resetSubtitleOffset = false;
|
||||
|
||||
if (!streamInfo.url) {
|
||||
showPlaybackInfoErrorMessage(self, 'PlaybackErrorNoCompatibleStream');
|
||||
|
@ -2268,7 +2266,7 @@ class PlaybackManager {
|
|||
|
||||
function runInterceptors(item, playOptions) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const interceptors = pluginManager.ofType('preplayintercept');
|
||||
const interceptors = pluginManager.ofType(PluginType.PreplayIntercept);
|
||||
|
||||
interceptors.sort(function (a, b) {
|
||||
return (a.order || 0) - (b.order || 0);
|
||||
|
@ -2303,17 +2301,17 @@ class PlaybackManager {
|
|||
}, reject);
|
||||
}
|
||||
|
||||
function sendPlaybackListToPlayer(player, items, deviceProfile, maxBitrate, apiClient, startPositionTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, startIndex) {
|
||||
return setStreamUrls(items, deviceProfile, maxBitrate, apiClient, startPositionTicks).then(function () {
|
||||
function sendPlaybackListToPlayer(player, items, deviceProfile, apiClient, mediaSourceId, options) {
|
||||
return setStreamUrls(items, deviceProfile, options.maxBitrate, apiClient, options.startPosition).then(function () {
|
||||
loading.hide();
|
||||
|
||||
return player.play({
|
||||
items: items,
|
||||
startPositionTicks: startPositionTicks || 0,
|
||||
mediaSourceId: mediaSourceId,
|
||||
audioStreamIndex: audioStreamIndex,
|
||||
subtitleStreamIndex: subtitleStreamIndex,
|
||||
startIndex: startIndex
|
||||
items,
|
||||
startPositionTicks: options.startPosition || 0,
|
||||
mediaSourceId,
|
||||
audioStreamIndex: options.audioStreamIndex,
|
||||
subtitleStreamIndex: options.subtitleStreamIndex,
|
||||
startIndex: options.startIndex
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -2474,15 +2472,27 @@ class PlaybackManager {
|
|||
const mediaSourceId = playOptions.mediaSourceId;
|
||||
const audioStreamIndex = playOptions.audioStreamIndex;
|
||||
const subtitleStreamIndex = playOptions.subtitleStreamIndex;
|
||||
const options = {
|
||||
maxBitrate,
|
||||
startPosition,
|
||||
isPlayback: null,
|
||||
audioStreamIndex,
|
||||
subtitleStreamIndex,
|
||||
startIndex: playOptions.startIndex,
|
||||
enableDirectPlay: null,
|
||||
enableDirectStream: null,
|
||||
allowVideoStreamCopy: null,
|
||||
allowAudioStreamCopy: null
|
||||
};
|
||||
|
||||
if (player && !enableLocalPlaylistManagement(player)) {
|
||||
return sendPlaybackListToPlayer(player, playOptions.items, deviceProfile, maxBitrate, apiClient, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex, playOptions.startIndex);
|
||||
return sendPlaybackListToPlayer(player, playOptions.items, deviceProfile, apiClient, mediaSourceId, options);
|
||||
}
|
||||
|
||||
// this reference was only needed by sendPlaybackListToPlayer
|
||||
playOptions.items = null;
|
||||
|
||||
return getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex).then(async (mediaSource) => {
|
||||
return getPlaybackMediaSource(player, apiClient, deviceProfile, item, mediaSourceId, options).then(async (mediaSource) => {
|
||||
const user = await apiClient.getCurrentUser();
|
||||
autoSetNextTracks(prevSource, mediaSource, user.Configuration.RememberAudioSelections, user.Configuration.RememberSubtitleSelections);
|
||||
|
||||
|
@ -2540,7 +2550,20 @@ class PlaybackManager {
|
|||
const maxBitrate = getSavedMaxStreamingBitrate(ServerConnections.getApiClient(item.ServerId), mediaType);
|
||||
|
||||
return player.getDeviceProfile(item).then(function (deviceProfile) {
|
||||
return getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, options.mediaSourceId, options.audioStreamIndex, options.subtitleStreamIndex).then(function (mediaSource) {
|
||||
const mediaOptions = {
|
||||
maxBitrate,
|
||||
startPosition,
|
||||
isPlayback: null,
|
||||
audioStreamIndex: options.audioStreamIndex,
|
||||
subtitleStreamIndex: options.subtitleStreamIndex,
|
||||
startIndex: null,
|
||||
enableDirectPlay: null,
|
||||
enableDirectStream: null,
|
||||
allowVideoStreamCopy: null,
|
||||
allowAudioStreamCopy: null
|
||||
};
|
||||
|
||||
return getPlaybackMediaSource(player, apiClient, deviceProfile, item, options.mediaSourceId, mediaOptions).then(function (mediaSource) {
|
||||
return createStreamInfo(apiClient, item.MediaType, item, mediaSource, startPosition, player);
|
||||
});
|
||||
});
|
||||
|
@ -2560,7 +2583,19 @@ class PlaybackManager {
|
|||
const maxBitrate = getSavedMaxStreamingBitrate(ServerConnections.getApiClient(item.ServerId), mediaType);
|
||||
|
||||
return player.getDeviceProfile(item).then(function (deviceProfile) {
|
||||
return getPlaybackInfo(player, apiClient, item, deviceProfile, maxBitrate, startPosition, false, null, null, null, null).then(function (playbackInfoResult) {
|
||||
const mediaOptions = {
|
||||
maxBitrate,
|
||||
startPosition,
|
||||
isPlayback: true,
|
||||
audioStreamIndex: null,
|
||||
subtitleStreamIndex: null,
|
||||
enableDirectPlay: null,
|
||||
enableDirectStream: null,
|
||||
allowVideoStreamCopy: null,
|
||||
allowAudioStreamCopy: null
|
||||
};
|
||||
|
||||
return getPlaybackInfo(player, apiClient, item, deviceProfile, null, null, mediaOptions).then(function (playbackInfoResult) {
|
||||
return playbackInfoResult.MediaSources;
|
||||
});
|
||||
});
|
||||
|
@ -2701,13 +2736,18 @@ class PlaybackManager {
|
|||
return tracks;
|
||||
}
|
||||
|
||||
function getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, mediaSourceId, audioStreamIndex, subtitleStreamIndex) {
|
||||
return getPlaybackInfo(player, apiClient, item, deviceProfile, maxBitrate, startPosition, true, mediaSourceId, audioStreamIndex, subtitleStreamIndex, null).then(function (playbackInfoResult) {
|
||||
function getPlaybackMediaSource(player, apiClient, deviceProfile, item, mediaSourceId, options) {
|
||||
options.isPlayback = true;
|
||||
|
||||
return getPlaybackInfo(player, apiClient, item, deviceProfile, mediaSourceId, null, options).then(function (playbackInfoResult) {
|
||||
if (validatePlaybackInfoResult(self, playbackInfoResult)) {
|
||||
return getOptimalMediaSource(apiClient, item, playbackInfoResult.MediaSources).then(function (mediaSource) {
|
||||
if (mediaSource) {
|
||||
if (mediaSource.RequiresOpening && !mediaSource.LiveStreamId) {
|
||||
return getLiveStream(player, apiClient, item, playbackInfoResult.PlaySessionId, deviceProfile, maxBitrate, startPosition, mediaSource, null, null).then(function (openLiveStreamResult) {
|
||||
options.audioStreamIndex = null;
|
||||
options.subtitleStreamIndex = null;
|
||||
|
||||
return getLiveStream(player, apiClient, item, playbackInfoResult.PlaySessionId, deviceProfile, mediaSource, options).then(function (openLiveStreamResult) {
|
||||
return supportsDirectPlay(apiClient, item, openLiveStreamResult.MediaSource).then(function (result) {
|
||||
openLiveStreamResult.MediaSource.enableDirectPlay = result;
|
||||
return openLiveStreamResult.MediaSource;
|
||||
|
@ -3387,12 +3427,12 @@ class PlaybackManager {
|
|||
}
|
||||
|
||||
Events.on(pluginManager, 'registered', function (e, plugin) {
|
||||
if (plugin.type === 'mediaplayer') {
|
||||
if (plugin.type === PluginType.MediaPlayer) {
|
||||
initMediaPlayer(plugin);
|
||||
}
|
||||
});
|
||||
|
||||
pluginManager.ofType('mediaplayer').forEach(initMediaPlayer);
|
||||
pluginManager.ofType(PluginType.MediaPlayer).forEach(initMediaPlayer);
|
||||
|
||||
function sendProgressUpdate(player, progressEventName, reportPlaylist) {
|
||||
if (!player) {
|
||||
|
@ -3423,12 +3463,6 @@ class PlaybackManager {
|
|||
|
||||
streamInfo.lastMediaInfoQuery = new Date().getTime();
|
||||
|
||||
const apiClient = ServerConnections.getApiClient(serverId);
|
||||
|
||||
if (!apiClient.isMinServerVersion('3.2.70.7')) {
|
||||
return;
|
||||
}
|
||||
|
||||
ServerConnections.getApiClient(serverId).getLiveStreamMediaInfo(liveStreamId).then(function (info) {
|
||||
mediaSource.MediaStreams = info.MediaStreams;
|
||||
Events.trigger(player, 'mediastreamschange');
|
||||
|
@ -3613,7 +3647,7 @@ class PlaybackManager {
|
|||
|
||||
percent /= 100;
|
||||
ticks *= percent;
|
||||
this.seek(parseInt(ticks), player);
|
||||
this.seek(parseInt(ticks, 10), player);
|
||||
}
|
||||
|
||||
seekMs(ms, player = this._currentPlayer) {
|
||||
|
@ -4000,13 +4034,13 @@ class PlaybackManager {
|
|||
this.setBrightness(cmd.Arguments.Brightness, player);
|
||||
break;
|
||||
case 'SetAudioStreamIndex':
|
||||
this.setAudioStreamIndex(parseInt(cmd.Arguments.Index), player);
|
||||
this.setAudioStreamIndex(parseInt(cmd.Arguments.Index, 10), player);
|
||||
break;
|
||||
case 'SetSubtitleStreamIndex':
|
||||
this.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index), player);
|
||||
this.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index, 10), player);
|
||||
break;
|
||||
case 'SetMaxStreamingBitrate':
|
||||
this.setMaxStreamingBitrate(parseInt(cmd.Arguments.Bitrate), player);
|
||||
this.setMaxStreamingBitrate(parseInt(cmd.Arguments.Bitrate, 10), player);
|
||||
break;
|
||||
case 'ToggleFullscreen':
|
||||
this.toggleFullscreen(player);
|
||||
|
|
|
@ -43,7 +43,7 @@ function showQualityMenu(player, btn) {
|
|||
items: menuItems,
|
||||
positionTo: btn
|
||||
}).then(function (id) {
|
||||
const bitrate = parseInt(id);
|
||||
const bitrate = parseInt(id, 10);
|
||||
if (bitrate !== selectedBitrate) {
|
||||
playbackManager.setMaxStreamingBitrate({
|
||||
enableAutomaticBitrateDetection: bitrate ? false : true,
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/*eslint prefer-const: "error"*/
|
||||
|
||||
let currentId = 0;
|
||||
function addUniquePlaylistItemId(item) {
|
||||
if (!item.PlaylistItemId) {
|
||||
|
|
|
@ -8,6 +8,8 @@ import { appRouter } from '../components/appRouter';
|
|||
import * as inputManager from '../scripts/inputManager';
|
||||
import toast from '../components/toast/toast';
|
||||
import confirm from '../components/confirm/confirm';
|
||||
import * as dashboard from '../utils/dashboard';
|
||||
import ServerConnections from '../components/ServerConnections';
|
||||
|
||||
// TODO: replace with each plugin version
|
||||
const cacheParam = new Date().getTime();
|
||||
|
@ -86,7 +88,9 @@ class PluginManager {
|
|||
appRouter,
|
||||
inputManager,
|
||||
toast,
|
||||
confirm
|
||||
confirm,
|
||||
dashboard,
|
||||
ServerConnections
|
||||
});
|
||||
} else {
|
||||
console.debug(`Loading plugin (via dynamic import): ${pluginSpec}`);
|
||||
|
|
|
@ -4,7 +4,7 @@ import globalize from '../../scripts/globalize';
|
|||
import layoutManager from '../layoutManager';
|
||||
import loading from '../loading/loading';
|
||||
import scrollHelper from '../../scripts/scrollHelper';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-collapse/emby-collapse';
|
||||
import '../../elements/emby-input/emby-input';
|
||||
|
@ -12,7 +12,7 @@ import '../../elements/emby-button/paper-icon-button-light';
|
|||
import '../formdialog.scss';
|
||||
import './recordingcreator.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './recordingeditor.template.html';
|
||||
|
||||
|
|
|
@ -7,13 +7,11 @@ import recordingHelper from './recordinghelper';
|
|||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import './recordingfields.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import template from './recordingfields.template.html';
|
||||
|
||||
/*eslint prefer-const: "error"*/
|
||||
|
||||
function loadData(parent, program) {
|
||||
if (program.IsSeries) {
|
||||
parent.querySelector('.recordSeriesContainer').classList.remove('hide');
|
||||
|
|
|
@ -5,8 +5,6 @@ import toast from '../toast/toast';
|
|||
import confirm from '../confirm/confirm';
|
||||
import dialog from '../dialog/dialog';
|
||||
|
||||
/*eslint prefer-const: "error"*/
|
||||
|
||||
function changeRecordingToSeries(apiClient, timerId, programId, confirmTimerCancellation) {
|
||||
loading.show();
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import layoutManager from '../layoutManager';
|
|||
import loading from '../loading/loading';
|
||||
import scrollHelper from '../../scripts/scrollHelper';
|
||||
import datetime from '../../scripts/datetime';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../elements/emby-input/emby-input';
|
||||
|
@ -13,12 +13,10 @@ import '../../elements/emby-button/paper-icon-button-light';
|
|||
import '../formdialog.scss';
|
||||
import './recordingcreator.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './seriesrecordingeditor.template.html';
|
||||
|
||||
/*eslint prefer-const: "error"*/
|
||||
|
||||
let currentDialog;
|
||||
let recordingUpdated = false;
|
||||
let recordingDeleted = false;
|
||||
|
|
|
@ -13,8 +13,6 @@ import '../formdialog.scss';
|
|||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
|
||||
/*eslint prefer-const: "error"*/
|
||||
|
||||
function getEditorHtml() {
|
||||
let html = '';
|
||||
|
||||
|
|
|
@ -20,8 +20,6 @@ import ServerConnections from '../ServerConnections';
|
|||
import toast from '../toast/toast';
|
||||
import { appRouter } from '../appRouter';
|
||||
|
||||
/*eslint prefer-const: "error"*/
|
||||
|
||||
let showMuteButton = true;
|
||||
let showVolumeSlider = true;
|
||||
|
||||
|
@ -46,7 +44,7 @@ function showAudioMenu(context, player, button) {
|
|||
items: menuItems,
|
||||
positionTo: button,
|
||||
callback: function (id) {
|
||||
playbackManager.setAudioStreamIndex(parseInt(id), player);
|
||||
playbackManager.setAudioStreamIndex(parseInt(id, 10), player);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -78,7 +76,7 @@ function showSubtitleMenu(context, player, button) {
|
|||
items: menuItems,
|
||||
positionTo: button,
|
||||
callback: function (id) {
|
||||
playbackManager.setSubtitleStreamIndex(parseInt(id), player);
|
||||
playbackManager.setSubtitleStreamIndex(parseInt(id, 10), player);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@ import globalize from '../../scripts/globalize';
|
|||
import 'material-design-icons-iconfont';
|
||||
|
||||
import '../../elements/emby-input/emby-input';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import './searchfields.scss';
|
||||
import layoutManager from '../layoutManager';
|
||||
import browser from '../../scripts/browser';
|
||||
|
|
|
@ -150,7 +150,7 @@ import toast from './toast/toast';
|
|||
StartDate: card.getAttribute('data-startdate'),
|
||||
EndDate: card.getAttribute('data-enddate'),
|
||||
UserData: {
|
||||
PlaybackPositionTicks: parseInt(card.getAttribute('data-positionticks') || '0')
|
||||
PlaybackPositionTicks: parseInt(card.getAttribute('data-positionticks') || '0', 10)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ import toast from './toast/toast';
|
|||
ServerId: serverId
|
||||
});
|
||||
} else if (action === 'play' || action === 'resume') {
|
||||
const startPositionTicks = parseInt(card.getAttribute('data-positionticks') || '0');
|
||||
const startPositionTicks = parseInt(card.getAttribute('data-positionticks') || '0', 10);
|
||||
|
||||
if (playbackManager.canPlay(item)) {
|
||||
playbackManager.play({
|
||||
|
|
|
@ -7,7 +7,7 @@ import '../../elements/emby-button/paper-icon-button-light';
|
|||
import 'material-design-icons-iconfont';
|
||||
import '../formdialog.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import template from './sortmenu.template.html';
|
||||
|
||||
function onSubmit(e) {
|
||||
|
|
|
@ -14,7 +14,7 @@ import '../formdialog.scss';
|
|||
import 'material-design-icons-iconfont';
|
||||
import './subtitleeditor.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import confirm from '../confirm/confirm';
|
||||
|
|
|
@ -13,7 +13,7 @@ import '../../elements/emby-select/emby-select';
|
|||
import '../../elements/emby-slider/emby-slider';
|
||||
import '../../elements/emby-input/emby-input';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import './subtitlesettings.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
|
|
|
@ -101,14 +101,16 @@
|
|||
|
||||
<div class="selectContainer hide">
|
||||
<select is="emby-select" id="selectTextColor" label="${LabelTextColor}">
|
||||
<option value="#ffffff">${White}</option>
|
||||
<option value="#ffff00">${Yellow}</option>
|
||||
<option value="#008000">${Green}</option>
|
||||
<option value="#00ffff">${Cyan}</option>
|
||||
<option value="#0000ff">${Blue}</option>
|
||||
<option value="#ff00ff">${Magenta}</option>
|
||||
<option value="#ff0000">${Red}</option>
|
||||
<option value="#000000">${Black}</option>
|
||||
<option value="#ffffff">${SubtitleWhite}</option>
|
||||
<option value="#d3d3d3">${SubtitleLightGray}</option>
|
||||
<option value="#808080">${SubtitleGray}</option>
|
||||
<option value="#ffff00">${SubtitleYellow}</option>
|
||||
<option value="#008000">${SubtitleGreen}</option>
|
||||
<option value="#00ffff">${SubtitleCyan}</option>
|
||||
<option value="#0000ff">${SubtitleBlue}</option>
|
||||
<option value="#ff00ff">${SubtitleMagenta}</option>
|
||||
<option value="#ff0000">${SubtitleRed}</option>
|
||||
<option value="#000000">${SubtitleBlack}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import '../listview/listview.scss';
|
|||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../elements/emby-select/emby-select';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import './style.scss';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import Events from '../../utils/events.ts';
|
||||
|
|
|
@ -8,7 +8,7 @@ import globalize from '../../scripts/globalize';
|
|||
import itemHelper from '../itemHelper';
|
||||
import './upnextdialog.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
|
||||
/* eslint-disable indent */
|
||||
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
|
||||
.upNextDialog-countdownText {
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.upNextDialog-nextVideoText,
|
||||
.upNextDialog-title {
|
||||
width: 25.5em;
|
||||
white-space: nowrap;
|
||||
|
|
|
@ -12,7 +12,10 @@ const userDataMethods = {
|
|||
markFavorite: markFavorite
|
||||
};
|
||||
|
||||
function getUserDataButtonHtml(method, itemId, serverId, buttonCssClass, iconCssClass, icon, tooltip, style) {
|
||||
function getUserDataButtonHtml(method, itemId, serverId, icon, tooltip, style, classes) {
|
||||
let buttonCssClass = classes.buttonCssClass;
|
||||
let iconCssClass = classes.iconCssClass;
|
||||
|
||||
if (style === 'fab-mini') {
|
||||
style = 'fab';
|
||||
buttonCssClass = buttonCssClass ? (buttonCssClass + ' mini') : 'mini';
|
||||
|
@ -96,7 +99,7 @@ function getIconsHtml(options) {
|
|||
}
|
||||
|
||||
const iconCssClass = options.iconCssClass;
|
||||
|
||||
const classes = { buttonCssClass: btnCssClass, iconCssClass: iconCssClass };
|
||||
const serverId = item.ServerId;
|
||||
|
||||
if (includePlayed !== false) {
|
||||
|
@ -104,18 +107,21 @@ function getIconsHtml(options) {
|
|||
|
||||
if (itemHelper.canMarkPlayed(item)) {
|
||||
if (userData.Played) {
|
||||
html += getUserDataButtonHtml('markPlayed', itemId, serverId, btnCssClass + ' btnUserDataOn', iconCssClass, 'check', tooltipPlayed, style);
|
||||
const buttonCssClass = classes.buttonCssClass + ' btnUserDataOn';
|
||||
html += getUserDataButtonHtml('markPlayed', itemId, serverId, 'check', tooltipPlayed, style, { buttonCssClass, ...classes });
|
||||
} else {
|
||||
html += getUserDataButtonHtml('markPlayed', itemId, serverId, btnCssClass, iconCssClass, 'check', tooltipPlayed, style);
|
||||
html += getUserDataButtonHtml('markPlayed', itemId, serverId, 'check', tooltipPlayed, style, classes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tooltipFavorite = globalize.translate('Favorite');
|
||||
if (userData.IsFavorite) {
|
||||
html += getUserDataButtonHtml('markFavorite', itemId, serverId, btnCssClass + ' btnUserData btnUserDataOn', iconCssClass, 'favorite', tooltipFavorite, style);
|
||||
const buttonCssClass = classes.buttonCssClass + ' btnUserData btnUserDataOn';
|
||||
html += getUserDataButtonHtml('markFavorite', itemId, serverId, 'favorite', tooltipFavorite, style, { buttonCssClass, ...classes });
|
||||
} else {
|
||||
html += getUserDataButtonHtml('markFavorite', itemId, serverId, btnCssClass + ' btnUserData', iconCssClass, 'favorite', tooltipFavorite, style);
|
||||
classes.buttonCssClass += ' btnUserData';
|
||||
html += getUserDataButtonHtml('markFavorite', itemId, serverId, 'favorite', tooltipFavorite, style, classes);
|
||||
}
|
||||
|
||||
return html;
|
||||
|
|
|
@ -9,7 +9,7 @@ import '../../elements/emby-button/paper-icon-button-light';
|
|||
import '../../elements/emby-select/emby-select';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../formdialog.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import template from './viewSettings.template.html';
|
||||
|
||||
function onSubmit(e) {
|
||||
|
|
|
@ -16,7 +16,7 @@ import imageHelper from '../../scripts/imagehelper';
|
|||
import indicators from '../../components/indicators/indicators';
|
||||
import '../../components/listview/listview.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import taskButton from '../../scripts/taskbutton';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
|
|
|
@ -54,15 +54,11 @@ import confirm from '../../../components/confirm/confirm';
|
|||
}
|
||||
|
||||
function showDeviceMenu(view, btn, deviceId) {
|
||||
const menuItems = [];
|
||||
|
||||
if (canEdit) {
|
||||
menuItems.push({
|
||||
name: globalize.translate('Edit'),
|
||||
id: 'open',
|
||||
icon: 'mode_edit'
|
||||
});
|
||||
}
|
||||
const menuItems = [{
|
||||
name: globalize.translate('Edit'),
|
||||
id: 'open',
|
||||
icon: 'mode_edit'
|
||||
}];
|
||||
|
||||
if (canDelete(deviceId)) {
|
||||
menuItems.push({
|
||||
|
@ -100,7 +96,7 @@ import confirm from '../../../components/confirm/confirm';
|
|||
deviceHtml += '<div class="cardBox visualCardBox">';
|
||||
deviceHtml += '<div class="cardScalable">';
|
||||
deviceHtml += '<div class="cardPadder cardPadder-backdrop"></div>';
|
||||
deviceHtml += `<a is="emby-linkbutton" href="${canEdit ? '#/device.html?id=' + device.Id : '#'}" class="cardContent cardImageContainer ${cardBuilder.getDefaultBackgroundClass()}">`;
|
||||
deviceHtml += `<a is="emby-linkbutton" href="#/device.html?id=${device.Id}" class="cardContent cardImageContainer ${cardBuilder.getDefaultBackgroundClass()}">`;
|
||||
const iconUrl = imageHelper.getDeviceIcon(device);
|
||||
|
||||
if (iconUrl) {
|
||||
|
@ -114,7 +110,7 @@ import confirm from '../../../components/confirm/confirm';
|
|||
deviceHtml += '</div>';
|
||||
deviceHtml += '<div class="cardFooter">';
|
||||
|
||||
if (canEdit || canDelete(device.Id)) {
|
||||
if (canDelete(device.Id)) {
|
||||
if (globalize.getIsRTL())
|
||||
deviceHtml += '<div style="text-align:left; float:left;padding-top:5px;">';
|
||||
else
|
||||
|
@ -155,7 +151,6 @@ import confirm from '../../../components/confirm/confirm';
|
|||
});
|
||||
}
|
||||
|
||||
const canEdit = ApiClient.isMinServerVersion('3.4.1.31');
|
||||
export default function (view) {
|
||||
view.querySelector('.devicesList').addEventListener('click', function (e) {
|
||||
const btnDeviceMenu = dom.parentWithClass(e.target, 'btnDeviceMenu');
|
||||
|
|
|
@ -100,7 +100,7 @@ import { getParameterByName } from '../../../utils/url.ts';
|
|||
}).join('') + '</div>';
|
||||
const elem = $('.httpHeaderIdentificationList', page).html(html).trigger('create');
|
||||
$('.btnDeleteIdentificationHeader', elem).on('click', function () {
|
||||
const itemIndex = parseInt(this.getAttribute('data-index'));
|
||||
const itemIndex = parseInt(this.getAttribute('data-index'), 10);
|
||||
currentProfile.Identification.Headers.splice(itemIndex, 1);
|
||||
renderIdentificationHeaders(page, currentProfile.Identification.Headers);
|
||||
});
|
||||
|
@ -154,7 +154,7 @@ import { getParameterByName } from '../../../utils/url.ts';
|
|||
}).join('') + '</div>';
|
||||
const elem = $('.xmlDocumentAttributeList', page).html(html).trigger('create');
|
||||
$('.btnDeleteXmlAttribute', elem).on('click', function () {
|
||||
const itemIndex = parseInt(this.getAttribute('data-index'));
|
||||
const itemIndex = parseInt(this.getAttribute('data-index'), 10);
|
||||
currentProfile.XmlRootAttributes.splice(itemIndex, 1);
|
||||
renderXmlDocumentAttributes(page, currentProfile.XmlRootAttributes);
|
||||
});
|
||||
|
@ -198,12 +198,12 @@ import { getParameterByName } from '../../../utils/url.ts';
|
|||
}).join('') + '</div>';
|
||||
const elem = $('.subtitleProfileList', page).html(html).trigger('create');
|
||||
$('.btnDeleteProfile', elem).on('click', function () {
|
||||
const itemIndex = parseInt(this.getAttribute('data-index'));
|
||||
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'));
|
||||
const itemIndex = parseInt(this.getAttribute('data-index'), 10);
|
||||
editSubtitleProfile(page, currentProfile.SubtitleProfiles[itemIndex]);
|
||||
});
|
||||
}
|
||||
|
@ -292,7 +292,7 @@ import { getParameterByName } from '../../../utils/url.ts';
|
|||
deleteDirectPlayProfile(page, index);
|
||||
});
|
||||
$('.lnkEditSubProfile', elem).on('click', function () {
|
||||
const index = parseInt(this.getAttribute('data-profileindex'));
|
||||
const index = parseInt(this.getAttribute('data-profileindex'), 10);
|
||||
editDirectPlayProfile(page, currentProfile.DirectPlayProfiles[index]);
|
||||
});
|
||||
}
|
||||
|
@ -353,7 +353,7 @@ import { getParameterByName } from '../../../utils/url.ts';
|
|||
deleteTranscodingProfile(page, index);
|
||||
});
|
||||
$('.lnkEditSubProfile', elem).on('click', function () {
|
||||
const index = parseInt(this.getAttribute('data-profileindex'));
|
||||
const index = parseInt(this.getAttribute('data-profileindex'), 10);
|
||||
editTranscodingProfile(page, currentProfile.TranscodingProfiles[index]);
|
||||
});
|
||||
}
|
||||
|
@ -437,7 +437,7 @@ import { getParameterByName } from '../../../utils/url.ts';
|
|||
deleteContainerProfile(page, index);
|
||||
});
|
||||
$('.lnkEditSubProfile', elem).on('click', function () {
|
||||
const index = parseInt(this.getAttribute('data-profileindex'));
|
||||
const index = parseInt(this.getAttribute('data-profileindex'), 10);
|
||||
editContainerProfile(page, currentProfile.ContainerProfiles[index]);
|
||||
});
|
||||
}
|
||||
|
@ -509,7 +509,7 @@ import { getParameterByName } from '../../../utils/url.ts';
|
|||
deleteCodecProfile(page, index);
|
||||
});
|
||||
$('.lnkEditSubProfile', elem).on('click', function () {
|
||||
const index = parseInt(this.getAttribute('data-profileindex'));
|
||||
const index = parseInt(this.getAttribute('data-profileindex'), 10);
|
||||
editCodecProfile(page, currentProfile.CodecProfiles[index]);
|
||||
});
|
||||
}
|
||||
|
@ -589,7 +589,7 @@ import { getParameterByName } from '../../../utils/url.ts';
|
|||
deleteResponseProfile(page, index);
|
||||
});
|
||||
$('.lnkEditSubProfile', elem).on('click', function () {
|
||||
const index = parseInt(this.getAttribute('data-profileindex'));
|
||||
const index = parseInt(this.getAttribute('data-profileindex'), 10);
|
||||
editResponseProfile(page, currentProfile.ResponseProfiles[index]);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -98,8 +98,8 @@ import alert from '../../components/alert';
|
|||
config.VppTonemappingBrightness = form.querySelector('#txtVppTonemappingBrightness').value;
|
||||
config.VppTonemappingContrast = form.querySelector('#txtVppTonemappingContrast').value;
|
||||
config.EncoderPreset = form.querySelector('#selectEncoderPreset').value;
|
||||
config.H264Crf = parseInt(form.querySelector('#txtH264Crf').value || '0');
|
||||
config.H265Crf = parseInt(form.querySelector('#txtH265Crf').value || '0');
|
||||
config.H264Crf = parseInt(form.querySelector('#txtH264Crf').value || '0', 10);
|
||||
config.H265Crf = parseInt(form.querySelector('#txtH265Crf').value || '0', 10);
|
||||
config.DeinterlaceMethod = form.querySelector('#selectDeinterlaceMethod').value;
|
||||
config.DeinterlaceDoubleRate = form.querySelector('#chkDoubleRateDeinterlacing').checked;
|
||||
config.EnableSubtitleExtraction = form.querySelector('#chkEnableSubtitleExtraction').checked;
|
||||
|
|
|
@ -92,7 +92,7 @@ import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
|||
|
||||
function showCardMenu(page, elem, virtualFolders) {
|
||||
const card = dom.parentWithClass(elem, 'card');
|
||||
const index = parseInt(card.getAttribute('data-index'));
|
||||
const index = parseInt(card.getAttribute('data-index'), 10);
|
||||
const virtualFolder = virtualFolders[index];
|
||||
const menuItems = [];
|
||||
menuItems.push({
|
||||
|
@ -192,7 +192,7 @@ import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
|||
});
|
||||
$('.editLibrary', divVirtualFolders).on('click', function () {
|
||||
const card = $(this).parents('.card')[0];
|
||||
const index = parseInt(card.getAttribute('data-index'));
|
||||
const index = parseInt(card.getAttribute('data-index'), 10);
|
||||
const virtualFolder = virtualFolders[index];
|
||||
|
||||
if (virtualFolder.ItemId) {
|
||||
|
|
|
@ -3,7 +3,7 @@ import loading from '../../components/loading/loading';
|
|||
import globalize from '../../scripts/globalize';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../components/listview/listview.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
import alert from '../../components/alert';
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<div class="fieldDescription">${LabelDummyChapterDurationHelp}</div>
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="valDummyChapterCount" label="${LabelDummyChapterCount}" min="1"></input>
|
||||
<input is="emby-input" type="number" id="valDummyChapterCount" label="${LabelDummyChapterCount}" min="0"></input>
|
||||
<div class="fieldDescription">${LabelDummyChapterCountHelp}</div>
|
||||
</div>
|
||||
<div class="selectContainer">
|
||||
|
|
|
@ -68,7 +68,7 @@ function renderPackage(pkg, installedPlugins, page) {
|
|||
|
||||
if (installedPlugin) {
|
||||
const currentVersionText = globalize.translate('MessageYouHaveVersionInstalled', '<strong>' + installedPlugin.Version + '</strong>');
|
||||
$('#pCurrentVersion', page).show().text(currentVersionText);
|
||||
$('#pCurrentVersion', page).show().html(currentVersionText);
|
||||
} else {
|
||||
$('#pCurrentVersion', page).hide().text('');
|
||||
}
|
||||
|
|
|
@ -235,7 +235,7 @@ import { getParameterByName } from '../../../utils/url.ts';
|
|||
const btnDeleteTrigger = dom.parentWithClass(e.target, 'btnDeleteTrigger');
|
||||
|
||||
if (btnDeleteTrigger) {
|
||||
ScheduledTaskPage.confirmDeleteTrigger(view, parseInt(btnDeleteTrigger.getAttribute('data-index')));
|
||||
ScheduledTaskPage.confirmDeleteTrigger(view, parseInt(btnDeleteTrigger.getAttribute('data-index'), 10));
|
||||
}
|
||||
});
|
||||
view.addEventListener('viewshow', function () {
|
||||
|
|
|
@ -15,7 +15,7 @@ import Dashboard from '../../utils/dashboard';
|
|||
loading.show();
|
||||
const form = this;
|
||||
ApiClient.getServerConfiguration().then(function (config) {
|
||||
config.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($('#txtRemoteClientBitrateLimit', form).val() || '0'));
|
||||
config.RemoteClientBitrateLimit = parseInt(1e6 * parseFloat($('#txtRemoteClientBitrateLimit', form).val() || '0'), 10);
|
||||
ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { intervalToDuration } from 'date-fns';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { marked } from 'marked';
|
||||
import escapeHtml from 'escape-html';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
|
||||
|
@ -22,7 +23,7 @@ import libraryMenu from '../../scripts/libraryMenu';
|
|||
import globalize from '../../scripts/globalize';
|
||||
import browser from '../../scripts/browser';
|
||||
import { playbackManager } from '../../components/playback/playbackmanager';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
|
@ -877,7 +878,7 @@ function renderOverview(page, item) {
|
|||
const overviewElements = page.querySelectorAll('.overview');
|
||||
|
||||
if (overviewElements.length > 0) {
|
||||
const overview = DOMPurify.sanitize(item.Overview || '');
|
||||
const overview = DOMPurify.sanitize(marked(item.Overview || ''));
|
||||
|
||||
if (overview) {
|
||||
for (const overviewElemnt of overviewElements) {
|
||||
|
@ -1063,7 +1064,7 @@ function renderTagline(page, item) {
|
|||
}
|
||||
}
|
||||
|
||||
function renderDetails(page, item, apiClient, context, isStatic) {
|
||||
function renderDetails(page, item, apiClient, context) {
|
||||
renderSimilarItems(page, item, context);
|
||||
renderMoreFromSeason(page, item, apiClient);
|
||||
renderMoreFromArtist(page, item, apiClient);
|
||||
|
@ -1083,7 +1084,7 @@ function renderDetails(page, item, apiClient, context, isStatic) {
|
|||
}
|
||||
|
||||
renderTags(page, item);
|
||||
renderSeriesAirTime(page, item, isStatic);
|
||||
renderSeriesAirTime(page, item);
|
||||
}
|
||||
|
||||
function enableScrollX() {
|
||||
|
@ -1156,12 +1157,7 @@ function renderMoreFromArtist(view, item, apiClient) {
|
|||
const section = view.querySelector('.moreFromArtistSection');
|
||||
|
||||
if (section) {
|
||||
if (item.Type === 'MusicArtist') {
|
||||
if (!apiClient.isMinServerVersion('3.4.1.19')) {
|
||||
section.classList.add('hide');
|
||||
return;
|
||||
}
|
||||
} else if (item.Type !== 'MusicAlbum' || !item.AlbumArtists || !item.AlbumArtists.length) {
|
||||
if (item.Type !== 'MusicArtist' && (item.Type !== 'MusicAlbum' || !item.AlbumArtists || !item.AlbumArtists.length)) {
|
||||
section.classList.add('hide');
|
||||
return;
|
||||
}
|
||||
|
@ -1262,7 +1258,7 @@ function renderSimilarItems(page, item, context) {
|
|||
}
|
||||
}
|
||||
|
||||
function renderSeriesAirTime(page, item, isStatic) {
|
||||
function renderSeriesAirTime(page, item) {
|
||||
const seriesAirTime = page.querySelector('#seriesAirTime');
|
||||
if (item.Type != 'Series') {
|
||||
seriesAirTime.classList.add('hide');
|
||||
|
@ -1281,19 +1277,6 @@ function renderSeriesAirTime(page, item, isStatic) {
|
|||
if (item.AirTime) {
|
||||
html += ' at ' + item.AirTime;
|
||||
}
|
||||
if (item.Studios.length) {
|
||||
if (isStatic) {
|
||||
html += ' on ' + escapeHtml(item.Studios[0].Name);
|
||||
} else {
|
||||
const context = inferContext(item);
|
||||
const href = appRouter.getRouteUrl(item.Studios[0], {
|
||||
context: context,
|
||||
itemType: 'Studio',
|
||||
serverId: item.ServerId
|
||||
});
|
||||
html += ' on <a class="textlink button-link" is="emby-linkbutton" href="' + href + '">' + escapeHtml(item.Studios[0].Name) + '</a>';
|
||||
}
|
||||
}
|
||||
if (html) {
|
||||
html = (item.Status == 'Ended' ? 'Aired ' : 'Airs ') + html;
|
||||
seriesAirTime.innerHTML = html;
|
||||
|
|
|
@ -6,7 +6,7 @@ import globalize from '../../scripts/globalize';
|
|||
import * as mainTabsManager from '../../components/maintabsmanager';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../elements/emby-tabs/emby-tabs';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
|
@ -222,17 +222,17 @@ export default function (view, params) {
|
|||
}
|
||||
|
||||
function onBeforeTabChange(evt) {
|
||||
preLoadTab(view, parseInt(evt.detail.selectedTabIndex));
|
||||
preLoadTab(view, parseInt(evt.detail.selectedTabIndex, 10));
|
||||
}
|
||||
|
||||
function onTabChange(evt) {
|
||||
const previousTabController = tabControllers[parseInt(evt.detail.previousIndex)];
|
||||
const previousTabController = tabControllers[parseInt(evt.detail.previousIndex, 10)];
|
||||
|
||||
if (previousTabController && previousTabController.onHide) {
|
||||
previousTabController.onHide();
|
||||
}
|
||||
|
||||
loadTab(view, parseInt(evt.detail.selectedTabIndex));
|
||||
loadTab(view, parseInt(evt.detail.selectedTabIndex, 10));
|
||||
}
|
||||
|
||||
function getTabContainers() {
|
||||
|
@ -339,7 +339,7 @@ export default function (view, params) {
|
|||
|
||||
let isViewRestored;
|
||||
const self = this;
|
||||
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex('livetv'));
|
||||
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex('livetv'), 10);
|
||||
let initialTabIndex = currentTabIndex;
|
||||
let lastFullRender = 0;
|
||||
[].forEach.call(view.querySelectorAll('.sectionTitleTextButton-programs'), function (link) {
|
||||
|
|
|
@ -7,7 +7,7 @@ import layoutManager from '../components/layoutManager';
|
|||
import loading from '../components/loading/loading';
|
||||
import browser from '../scripts/browser';
|
||||
import '../components/listview/listview.scss';
|
||||
import '../assets/css/flexstyles.scss';
|
||||
import '../styles/flexstyles.scss';
|
||||
import '../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../components/cardbuilder/card.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
|
|
|
@ -9,11 +9,11 @@ import imageLoader from '../../components/images/imageLoader';
|
|||
import libraryMenu from '../../scripts/libraryMenu';
|
||||
import * as mainTabsManager from '../../components/maintabsmanager';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../elements/emby-tabs/emby-tabs';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
|
||||
/* eslint-disable indent */
|
||||
|
@ -245,11 +245,11 @@ import Dashboard from '../../utils/dashboard';
|
|||
}
|
||||
|
||||
function onBeforeTabChange(e) {
|
||||
preLoadTab(view, parseInt(e.detail.selectedTabIndex));
|
||||
preLoadTab(view, parseInt(e.detail.selectedTabIndex, 10));
|
||||
}
|
||||
|
||||
function onTabChange(e) {
|
||||
loadTab(view, parseInt(e.detail.selectedTabIndex));
|
||||
loadTab(view, parseInt(e.detail.selectedTabIndex, 10));
|
||||
}
|
||||
|
||||
function getTabContainers() {
|
||||
|
@ -350,7 +350,7 @@ import Dashboard from '../../utils/dashboard';
|
|||
}
|
||||
}
|
||||
|
||||
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId));
|
||||
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId), 10);
|
||||
const suggestionsTabIndex = 1;
|
||||
|
||||
this.initTab = function () {
|
||||
|
|
|
@ -15,10 +15,10 @@ import { appHost } from '../../../components/apphost';
|
|||
import layoutManager from '../../../components/layoutManager';
|
||||
import * as userSettings from '../../../scripts/settings/userSettings';
|
||||
import keyboardnavigation from '../../../scripts/keyboardNavigation';
|
||||
import '../../../assets/css/scrollstyles.scss';
|
||||
import '../../../styles/scrollstyles.scss';
|
||||
import '../../../elements/emby-slider/emby-slider';
|
||||
import '../../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../../assets/css/videoosd.scss';
|
||||
import '../../../styles/videoosd.scss';
|
||||
import ServerConnections from '../../../components/ServerConnections';
|
||||
import shell from '../../../scripts/shell';
|
||||
import SubtitleSync from '../../../components/subtitlesync/subtitlesync';
|
||||
|
@ -975,7 +975,7 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components
|
|||
title: globalize.translate('Audio'),
|
||||
positionTo: positionTo
|
||||
}).then(function (id) {
|
||||
const index = parseInt(id);
|
||||
const index = parseInt(id, 10);
|
||||
|
||||
if (index !== currentIndex) {
|
||||
playbackManager.setAudioStreamIndex(index, player);
|
||||
|
@ -1022,7 +1022,7 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components
|
|||
positionTo
|
||||
}).then(function (id) {
|
||||
if (id) {
|
||||
const index = parseInt(id);
|
||||
const index = parseInt(id, 10);
|
||||
if (index !== currentIndex) {
|
||||
playbackManager.setSecondarySubtitleStreamIndex(index, player);
|
||||
}
|
||||
|
@ -1099,7 +1099,7 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components
|
|||
console.error(e);
|
||||
}
|
||||
} else {
|
||||
const index = parseInt(id);
|
||||
const index = parseInt(id, 10);
|
||||
|
||||
if (index !== currentIndex) {
|
||||
playbackManager.setSubtitleStreamIndex(index, player);
|
||||
|
@ -1633,7 +1633,7 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components
|
|||
ms /= 100;
|
||||
ms *= value;
|
||||
ms += programStartDateMs;
|
||||
return '<h1 class="sliderBubbleText">' + getDisplayTimeWithoutAmPm(new Date(parseInt(ms)), true) + '</h1>';
|
||||
return '<h1 class="sliderBubbleText">' + getDisplayTimeWithoutAmPm(new Date(parseInt(ms, 10)), true) + '</h1>';
|
||||
}
|
||||
|
||||
return '--:--';
|
||||
|
|
|
@ -10,7 +10,7 @@ import actionSheet from '../../../components/actionSheet/actionSheet';
|
|||
import dom from '../../../scripts/dom';
|
||||
import browser from '../../../scripts/browser';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../../../assets/css/flexstyles.scss';
|
||||
import '../../../styles/flexstyles.scss';
|
||||
import '../../../elements/emby-scroller/emby-scroller';
|
||||
import '../../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../../components/cardbuilder/card.scss';
|
||||
|
|
|
@ -9,7 +9,7 @@ import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
|||
import { playbackManager } from '../../components/playback/playbackmanager';
|
||||
import * as mainTabsManager from '../../components/maintabsmanager';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import Dashboard from '../../utils/dashboard';
|
||||
|
@ -224,11 +224,11 @@ import autoFocuser from '../../components/autoFocuser';
|
|||
|
||||
export default function (view, params) {
|
||||
function onBeforeTabChange(e) {
|
||||
preLoadTab(view, parseInt(e.detail.selectedTabIndex));
|
||||
preLoadTab(view, parseInt(e.detail.selectedTabIndex, 10));
|
||||
}
|
||||
|
||||
function onTabChange(e) {
|
||||
const newIndex = parseInt(e.detail.selectedTabIndex);
|
||||
const newIndex = parseInt(e.detail.selectedTabIndex, 10);
|
||||
loadTab(view, newIndex);
|
||||
}
|
||||
|
||||
|
@ -340,7 +340,7 @@ import autoFocuser from '../../components/autoFocuser';
|
|||
}
|
||||
|
||||
const self = this;
|
||||
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId));
|
||||
let currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId), 10);
|
||||
const suggestionsTabIndex = 1;
|
||||
|
||||
self.initTab = function () {
|
||||
|
|
|
@ -4,7 +4,7 @@ import datetime from '../../scripts/datetime';
|
|||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
import imageLoader from '../../components/images/imageLoader';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
|
||||
/* eslint-disable indent */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import loading from '../../../components/loading/loading';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import '../../../assets/css/dashboard.scss';
|
||||
import '../../../styles/dashboard.scss';
|
||||
import '../../../elements/emby-input/emby-input';
|
||||
import '../../../elements/emby-button/emby-button';
|
||||
import Dashboard from '../../../utils/dashboard';
|
||||
|
|
|
@ -414,7 +414,7 @@ import Sortable from 'sortablejs';
|
|||
clearRefreshInterval(itemsContainer);
|
||||
|
||||
if (!intervalMs) {
|
||||
intervalMs = parseInt(itemsContainer.getAttribute('data-refreshinterval') || '0');
|
||||
intervalMs = parseInt(itemsContainer.getAttribute('data-refreshinterval') || '0', 10);
|
||||
}
|
||||
|
||||
if (intervalMs) {
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
const ProgressBarPrototype = Object.create(HTMLDivElement.prototype);
|
||||
|
||||
function onAutoTimeProgress() {
|
||||
const start = parseInt(this.getAttribute('data-starttime'));
|
||||
const end = parseInt(this.getAttribute('data-endtime'));
|
||||
const start = parseInt(this.getAttribute('data-starttime'), 10);
|
||||
const end = parseInt(this.getAttribute('data-endtime'), 10);
|
||||
|
||||
const now = new Date().getTime();
|
||||
const total = end - start;
|
||||
|
|
|
@ -91,7 +91,7 @@ const EmbyScrollButtonsPrototype = Object.create(HTMLDivElement.prototype);
|
|||
return 0;
|
||||
}
|
||||
|
||||
value = parseInt(value);
|
||||
value = parseInt(value, 10);
|
||||
if (isNaN(value)) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ const Scroller: FC<ScrollerProps> = ({
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (isNaN(parseInt(value))) {
|
||||
if (isNaN(parseInt(value, 10))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import browser from '../../scripts/browser';
|
|||
import focusManager from '../../components/focusManager';
|
||||
import layoutManager from '../../components/layoutManager';
|
||||
import './emby-tabs.scss';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
|
||||
/* eslint-disable indent */
|
||||
const EmbyTabs = Object.create(HTMLDivElement.prototype);
|
||||
|
@ -75,11 +75,11 @@ import '../../assets/css/scrollstyles.scss';
|
|||
current.classList.remove(activeButtonClass);
|
||||
}
|
||||
|
||||
const previousIndex = current ? parseInt(current.getAttribute('data-index')) : null;
|
||||
const previousIndex = current ? parseInt(current.getAttribute('data-index'), 10) : null;
|
||||
|
||||
setActiveTabButton(tabButton);
|
||||
|
||||
const index = parseInt(tabButton.getAttribute('data-index'));
|
||||
const index = parseInt(tabButton.getAttribute('data-index'), 10);
|
||||
|
||||
triggerBeforeTabChange(tabs, index, previousIndex);
|
||||
|
||||
|
@ -194,7 +194,7 @@ import '../../assets/css/scrollstyles.scss';
|
|||
initScroller(this);
|
||||
|
||||
const current = this.querySelector('.' + activeButtonClass);
|
||||
const currentIndex = current ? parseInt(current.getAttribute('data-index')) : parseInt(this.getAttribute('data-index') || '0');
|
||||
const currentIndex = current ? parseInt(current.getAttribute('data-index'), 10) : parseInt(this.getAttribute('data-index') || '0', 10);
|
||||
|
||||
if (currentIndex !== -1) {
|
||||
this.selectedTabIndex = currentIndex;
|
||||
|
|
|
@ -14,7 +14,7 @@ function calculateOffset(textarea) {
|
|||
let offset = 0;
|
||||
|
||||
for (let i = 0; i < props.length; i++) {
|
||||
offset += parseInt(style[props[i]]);
|
||||
offset += parseInt(style[props[i]], 10);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'intersection-observer';
|
|||
import 'classlist.js';
|
||||
import 'whatwg-fetch';
|
||||
import 'resize-observer-polyfill';
|
||||
import './assets/css/site.scss';
|
||||
import './styles/site.scss';
|
||||
import React, { StrictMode } from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import Events from './utils/events.ts';
|
||||
|
@ -24,7 +24,6 @@ import { appRouter, history } from './components/appRouter';
|
|||
import './elements/emby-button/emby-button';
|
||||
import './scripts/autoThemes';
|
||||
import './scripts/libraryMenu';
|
||||
import './scripts/routes';
|
||||
import './components/themeMediaPlayer';
|
||||
import './scripts/autoBackdrops';
|
||||
import { pageClassOn, serverAddress } from './utils/dashboard';
|
||||
|
@ -39,6 +38,10 @@ import { currentSettings } from './scripts/settings/userSettings';
|
|||
import taskButton from './scripts/taskbutton';
|
||||
import App from './App.tsx';
|
||||
|
||||
import './styles/livetv.scss';
|
||||
import './styles/dashboard.scss';
|
||||
import './styles/detailtable.scss';
|
||||
|
||||
function loadCoreDictionary() {
|
||||
const languages = ['af', 'ar', 'be-by', 'bg-bg', 'bn_bd', 'ca', 'cs', 'cy', 'da', 'de', 'el', 'en-gb', 'en-us', 'eo', 'es', 'es_419', 'es-ar', 'es_do', 'es-mx', 'et', 'eu', 'fa', 'fi', 'fil', 'fr', 'fr-ca', 'gl', 'gsw', 'he', 'hi-in', 'hr', 'hu', 'id', 'it', 'ja', 'kk', 'ko', 'lt-lt', 'lv', 'mr', 'ms', 'nb', 'nl', 'nn', 'pl', 'pr', 'pt', 'pt-br', 'pt-pt', 'ro', 'ru', 'sk', 'sl-si', 'sq', 'sv', 'ta', 'th', 'tr', 'uk', 'ur_pk', 'vi', 'zh-cn', 'zh-hk', 'zh-tw'];
|
||||
const translations = languages.map(function (language) {
|
||||
|
@ -89,13 +92,13 @@ function onGlobalizeInit() {
|
|||
|
||||
if (browser.tv && !browser.android) {
|
||||
console.debug('using system fonts with explicit sizes');
|
||||
import('./assets/css/fonts.sized.scss');
|
||||
import('./styles/fonts.sized.scss');
|
||||
} else {
|
||||
console.debug('using default fonts');
|
||||
import('./assets/css/fonts.scss');
|
||||
import('./styles/fonts.scss');
|
||||
}
|
||||
|
||||
import('./assets/css/librarybrowser.scss');
|
||||
import('./styles/librarybrowser.scss');
|
||||
|
||||
loadPlugins().then(onAppReady);
|
||||
}
|
||||
|
@ -135,7 +138,7 @@ async function onAppReady() {
|
|||
console.debug('onAppReady: loading dependencies');
|
||||
|
||||
if (browser.iOS) {
|
||||
import('./assets/css/ios.scss');
|
||||
import('./styles/ios.scss');
|
||||
}
|
||||
|
||||
Events.on(appHost, 'resume', () => {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import browser from '../../scripts/browser';
|
||||
import dom from '../../scripts/dom';
|
||||
import './navdrawer.scss';
|
||||
import '../../assets/css/scrollstyles.scss';
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import globalize from '../../scripts/globalize';
|
||||
|
||||
function getTouches(e) {
|
||||
|
|
|
@ -8,7 +8,7 @@ import layoutManager from '../components/layoutManager';
|
|||
import dom from '../scripts/dom';
|
||||
import focusManager from '../components/focusManager';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
import '../assets/css/scrollstyles.scss';
|
||||
import '../styles/scrollstyles.scss';
|
||||
import globalize from '../scripts/globalize';
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
/* eslint-disable indent */
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
|
||||
class BackdropScreensaver {
|
||||
constructor() {
|
||||
this.name = 'Backdrop ScreenSaver';
|
||||
this.type = 'screensaver';
|
||||
this.type = PluginType.Screensaver;
|
||||
this.id = 'backdropscreensaver';
|
||||
this.supportsAnonymous = false;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import TableOfContents from './tableOfContents';
|
|||
import dom from '../../scripts/dom';
|
||||
import { translateHtml } from '../../scripts/globalize';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
import Events from '../../utils/events.ts';
|
||||
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
|
@ -19,7 +20,7 @@ import './style.scss';
|
|||
export class BookPlayer {
|
||||
constructor() {
|
||||
this.name = 'Book Player';
|
||||
this.type = 'mediaplayer';
|
||||
this.type = PluginType.MediaPlayer;
|
||||
this.id = 'bookplayer';
|
||||
this.priority = 1;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import globalize from '../../scripts/globalize';
|
|||
import castSenderApiLoader from './castSenderApi';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
import alert from '../../components/alert';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
import Events from '../../utils/events.ts';
|
||||
|
||||
// Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js
|
||||
|
@ -569,7 +570,7 @@ class ChromecastPlayer {
|
|||
constructor() {
|
||||
// playbackManager needs this
|
||||
this.name = PlayerName;
|
||||
this.type = 'mediaplayer';
|
||||
this.type = PluginType.MediaPlayer;
|
||||
this.id = 'chromecast';
|
||||
this.isLocalPlayer = false;
|
||||
this.lastPlayerData = {};
|
||||
|
@ -683,7 +684,7 @@ class ChromecastPlayer {
|
|||
}
|
||||
|
||||
seek(position) {
|
||||
position = parseInt(position);
|
||||
position = parseInt(position, 10);
|
||||
|
||||
position = position / 10000000;
|
||||
|
||||
|
|
|
@ -6,13 +6,14 @@ import keyboardnavigation from '../../scripts/keyboardNavigation';
|
|||
import { appRouter } from '../../components/appRouter';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
|
||||
import './style.scss';
|
||||
|
||||
export class ComicsPlayer {
|
||||
constructor() {
|
||||
this.name = 'Comics Player';
|
||||
this.type = 'mediaplayer';
|
||||
this.type = PluginType.MediaPlayer;
|
||||
this.id = 'comicsplayer';
|
||||
this.priority = 1;
|
||||
this.imageMap = new Map();
|
||||
|
|
|
@ -2,6 +2,7 @@ import globalize from '../../scripts/globalize';
|
|||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import { appHost } from '../../components/apphost';
|
||||
import alert from '../../components/alert';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
|
||||
// TODO: Replace with date-fns
|
||||
// https://stackoverflow.com/questions/6117814/get-week-of-year-in-javascript-like-in-php
|
||||
|
@ -46,7 +47,7 @@ function showIsoMessage() {
|
|||
class ExpirementalPlaybackWarnings {
|
||||
constructor() {
|
||||
this.name = 'Experimental playback warnings';
|
||||
this.type = 'preplayintercept';
|
||||
this.type = PluginType.PreplayIntercept;
|
||||
this.id = 'expirementalplaybackwarnings';
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { appHost } from '../../components/apphost';
|
|||
import * as htmlMediaHelper from '../../components/htmlMediaHelper';
|
||||
import profileBuilder from '../../scripts/browserDeviceProfile';
|
||||
import { getIncludeCorsCredentials } from '../../scripts/settings/webSettings';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
import Events from '../../utils/events.ts';
|
||||
|
||||
function getDefaultProfile() {
|
||||
|
@ -48,6 +49,9 @@ function supportsFade() {
|
|||
|
||||
function requireHlsPlayer(callback) {
|
||||
import('hls.js').then(({ default: hls }) => {
|
||||
hls.DefaultConfig.lowLatencyMode = false;
|
||||
hls.DefaultConfig.backBufferLength = Infinity;
|
||||
hls.DefaultConfig.liveBackBufferLength = 90;
|
||||
window.Hls = hls;
|
||||
callback();
|
||||
});
|
||||
|
@ -85,7 +89,7 @@ class HtmlAudioPlayer {
|
|||
const self = this;
|
||||
|
||||
self.name = 'Html Audio Player';
|
||||
self.type = 'mediaplayer';
|
||||
self.type = PluginType.MediaPlayer;
|
||||
self.id = 'htmlaudioplayer';
|
||||
|
||||
// Let any players created by plugins take priority
|
||||
|
|
|
@ -30,8 +30,10 @@ import ServerConnections from '../../components/ServerConnections';
|
|||
import profileBuilder, { canPlaySecondaryAudio } from '../../scripts/browserDeviceProfile';
|
||||
import { getIncludeCorsCredentials } from '../../scripts/settings/webSettings';
|
||||
import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../components/backdrop/backdrop';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
import Events from '../../utils/events.ts';
|
||||
import { includesAny } from '../../utils/container.ts';
|
||||
import debounce from 'lodash-es/debounce';
|
||||
|
||||
/**
|
||||
* Returns resolved URL.
|
||||
|
@ -106,6 +108,9 @@ function tryRemoveElement(elem) {
|
|||
|
||||
function requireHlsPlayer(callback) {
|
||||
import('hls.js').then(({default: hls}) => {
|
||||
hls.DefaultConfig.lowLatencyMode = false;
|
||||
hls.DefaultConfig.backBufferLength = Infinity;
|
||||
hls.DefaultConfig.liveBackBufferLength = 90;
|
||||
window.Hls = hls;
|
||||
callback();
|
||||
});
|
||||
|
@ -166,7 +171,7 @@ function tryRemoveElement(elem) {
|
|||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
type = 'mediaplayer';
|
||||
type = PluginType.MediaPlayer;
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
|
@ -378,7 +383,7 @@ function tryRemoveElement(elem) {
|
|||
|
||||
this.#currentTime = null;
|
||||
|
||||
this.resetSubtitleOffset();
|
||||
if (options.resetSubtitleOffset !== false) this.resetSubtitleOffset();
|
||||
|
||||
return this.createMediaElement(options).then(elem => {
|
||||
return this.updateVideoUrl(options).then(() => {
|
||||
|
@ -571,7 +576,12 @@ function tryRemoveElement(elem) {
|
|||
}
|
||||
}
|
||||
|
||||
setSubtitleOffset(offset) {
|
||||
setSubtitleOffset = debounce(this._setSubtitleOffset, 100);
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_setSubtitleOffset(offset) {
|
||||
const offsetValue = parseFloat(offset);
|
||||
|
||||
// if .ass currently rendering
|
||||
|
@ -620,6 +630,41 @@ function tryRemoveElement(elem) {
|
|||
return relativeOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* These browsers will not clear the existing active cue when setting an offset
|
||||
* for native TextTracks.
|
||||
* Any previous text tracks that are on the screen when the offset changes will remain next
|
||||
* to the new tracks until they reach the end time of the new offset's instance of the track.
|
||||
*/
|
||||
requiresHidingActiveCuesOnOffsetChange() {
|
||||
return !!browser.firefox;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
hideTextTrackWithActiveCues(currentTrack) {
|
||||
if (currentTrack.activeCues) {
|
||||
currentTrack.mode = 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the active cue to clear by disabling then re-enabling the track.
|
||||
* The track mode is reverted inside of a 0ms timeout to free up the track
|
||||
* and allow it to disable and clear the active cue.
|
||||
* @private
|
||||
*/
|
||||
forceClearTextTrackActiveCues(currentTrack) {
|
||||
if (currentTrack.activeCues) {
|
||||
currentTrack.mode = 'disabled';
|
||||
setTimeout(() => {
|
||||
currentTrack.mode = 'showing';
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
@ -629,11 +674,21 @@ function tryRemoveElement(elem) {
|
|||
if (offsetValue === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldClearActiveCues = this.requiresHidingActiveCuesOnOffsetChange();
|
||||
if (shouldClearActiveCues) {
|
||||
this.hideTextTrackWithActiveCues(currentTrack);
|
||||
}
|
||||
|
||||
Array.from(currentTrack.cues)
|
||||
.forEach(function (cue) {
|
||||
cue.startTime -= offsetValue;
|
||||
cue.endTime -= offsetValue;
|
||||
});
|
||||
|
||||
if (shouldClearActiveCues) {
|
||||
this.forceClearTextTrackActiveCues(currentTrack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -771,6 +826,8 @@ function tryRemoveElement(elem) {
|
|||
}
|
||||
|
||||
destroy() {
|
||||
this.setSubtitleOffset.cancel();
|
||||
|
||||
destroyHlsPlayer(this);
|
||||
destroyFlvPlayer(this);
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { PluginType } from '../../types/plugin.ts';
|
||||
|
||||
export default function () {
|
||||
const self = this;
|
||||
|
||||
self.name = 'Logo ScreenSaver';
|
||||
self.type = 'screensaver';
|
||||
self.type = PluginType.Screensaver;
|
||||
self.id = 'logoscreensaver';
|
||||
self.supportsAnonymous = true;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import keyboardnavigation from '../../scripts/keyboardNavigation';
|
|||
import dialogHelper from '../../components/dialogHelper/dialogHelper';
|
||||
import dom from '../../scripts/dom';
|
||||
import { appRouter } from '../../components/appRouter';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
import Events from '../../utils/events.ts';
|
||||
|
||||
import './style.scss';
|
||||
|
@ -12,7 +13,7 @@ import '../../elements/emby-button/paper-icon-button-light';
|
|||
export class PdfPlayer {
|
||||
constructor() {
|
||||
this.name = 'PDF Player';
|
||||
this.type = 'mediaplayer';
|
||||
this.type = PluginType.MediaPlayer;
|
||||
this.id = 'pdfplayer';
|
||||
this.priority = 1;
|
||||
|
||||
|
@ -261,7 +262,7 @@ export class PdfPlayer {
|
|||
for (const page of pages) {
|
||||
if (!this.pages[page]) {
|
||||
this.pages[page] = document.createElement('canvas');
|
||||
this.renderPage(this.pages[page], parseInt(page.slice(4)));
|
||||
this.renderPage(this.pages[page], parseInt(page.slice(4), 10));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import ServerConnections from '../../components/ServerConnections';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
|
||||
export default class PhotoPlayer {
|
||||
constructor() {
|
||||
this.name = 'Photo Player';
|
||||
this.type = 'mediaplayer';
|
||||
this.type = PluginType.MediaPlayer;
|
||||
this.id = 'photoplayer';
|
||||
this.priority = 1;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import globalize from '../../scripts/globalize';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
import alert from '../../components/alert';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
|
||||
function showErrorMessage() {
|
||||
return alert(globalize.translate('MessagePlayAccessRestricted'));
|
||||
|
@ -9,7 +10,7 @@ function showErrorMessage() {
|
|||
class PlayAccessValidation {
|
||||
constructor() {
|
||||
this.name = 'Playback validation';
|
||||
this.type = 'preplayintercept';
|
||||
this.type = PluginType.PreplayIntercept;
|
||||
this.id = 'playaccessvalidation';
|
||||
this.order = -2;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { playbackManager } from '../../components/playback/playbackmanager';
|
||||
import serverNotifications from '../../scripts/serverNotifications';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
import Events from '../../utils/events.ts';
|
||||
|
||||
function getActivePlayerId() {
|
||||
|
@ -181,7 +182,7 @@ class SessionPlayer {
|
|||
const self = this;
|
||||
|
||||
this.name = 'Remote Control';
|
||||
this.type = 'mediaplayer';
|
||||
this.type = PluginType.MediaPlayer;
|
||||
this.isLocalPlayer = false;
|
||||
this.id = 'remoteplayer';
|
||||
|
||||
|
|
|
@ -254,7 +254,7 @@ class Manager {
|
|||
if (typeof cmd.When === 'string') {
|
||||
cmd.When = new Date(cmd.When);
|
||||
cmd.EmittedAt = new Date(cmd.EmittedAt);
|
||||
cmd.PositionTicks = cmd.PositionTicks ? parseInt(cmd.PositionTicks) : null;
|
||||
cmd.PositionTicks = cmd.PositionTicks ? parseInt(cmd.PositionTicks, 10) : null;
|
||||
}
|
||||
|
||||
if (!this.isSyncPlayEnabled()) {
|
||||
|
|
|
@ -5,8 +5,9 @@ import SyncPlay from './core';
|
|||
import SyncPlayNoActivePlayer from './ui/players/NoActivePlayer';
|
||||
import SyncPlayHtmlVideoPlayer from './ui/players/HtmlVideoPlayer';
|
||||
import SyncPlayHtmlAudioPlayer from './ui/players/HtmlAudioPlayer';
|
||||
import { Plugin, PluginType } from '../../types/plugin';
|
||||
|
||||
class SyncPlayPlugin {
|
||||
class SyncPlayPlugin implements Plugin {
|
||||
name: string;
|
||||
id: string;
|
||||
type: string;
|
||||
|
@ -17,7 +18,7 @@ class SyncPlayPlugin {
|
|||
this.id = 'syncplay';
|
||||
// NOTE: This should probably be a "mediaplayer" so the playback manager can handle playback logic, but
|
||||
// SyncPlay needs refactored so it does not have an independent playback manager.
|
||||
this.type = 'syncplay';
|
||||
this.type = PluginType.SyncPlay;
|
||||
this.priority = 1;
|
||||
|
||||
this.init();
|
||||
|
|
|
@ -2,6 +2,7 @@ import browser from '../../scripts/browser';
|
|||
import { appRouter } from '../../components/appRouter';
|
||||
import loading from '../../components/loading/loading';
|
||||
import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../components/backdrop/backdrop';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
import Events from '../../utils/events.ts';
|
||||
|
||||
/* globals YT */
|
||||
|
@ -197,7 +198,7 @@ function setCurrentSrc(instance, elem, options) {
|
|||
class YoutubePlayer {
|
||||
constructor() {
|
||||
this.name = 'Youtube Player';
|
||||
this.type = 'mediaplayer';
|
||||
this.type = PluginType.MediaPlayer;
|
||||
this.id = 'youtubeplayer';
|
||||
|
||||
// Let any players created by plugins take priority
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Navigate, Route, Routes } from 'react-router-dom';
|
|||
import { ASYNC_ADMIN_ROUTES, ASYNC_USER_ROUTES, toAsyncPageRoute } from './asyncRoutes';
|
||||
import ConnectionRequired from '../components/ConnectionRequired';
|
||||
import ServerContentPage from '../components/ServerContentPage';
|
||||
import { LEGACY_ADMIN_ROUTES, LEGACY_USER_ROUTES, toViewManagerPageRoute } from './legacyRoutes';
|
||||
import { LEGACY_ADMIN_ROUTES, LEGACY_PUBLIC_ROUTES, LEGACY_USER_ROUTES, toViewManagerPageRoute } from './legacyRoutes';
|
||||
|
||||
const AppRoutes = () => (
|
||||
<Routes>
|
||||
|
@ -28,6 +28,8 @@ const AppRoutes = () => (
|
|||
{/* Public routes */}
|
||||
<Route path='/' element={<ConnectionRequired isUserRequired={false} />}>
|
||||
<Route index element={<Navigate replace to='/home.html' />} />
|
||||
|
||||
{LEGACY_PUBLIC_ROUTES.map(toViewManagerPageRoute)}
|
||||
</Route>
|
||||
|
||||
{/* Suppress warnings for unhandled routes */}
|
||||
|
|
|
@ -21,4 +21,5 @@ export function toViewManagerPageRoute(route: LegacyRoute) {
|
|||
}
|
||||
|
||||
export * from './admin';
|
||||
export * from './public';
|
||||
export * from './user';
|
||||
|
|
81
src/routes/legacyRoutes/public.ts
Normal file
81
src/routes/legacyRoutes/public.ts
Normal file
|
@ -0,0 +1,81 @@
|
|||
import { LegacyRoute } from '.';
|
||||
|
||||
export const LEGACY_PUBLIC_ROUTES: LegacyRoute[] = [
|
||||
{
|
||||
path: 'addserver.html',
|
||||
pageProps: {
|
||||
controller: 'session/addServer/index',
|
||||
view: 'session/addServer/index.html'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'selectserver.html',
|
||||
pageProps: {
|
||||
controller: 'session/selectServer/index',
|
||||
view: 'session/selectServer/index.html'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'login.html',
|
||||
pageProps: {
|
||||
controller: 'session/login/index',
|
||||
view: 'session/login/index.html'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'forgotpassword.html',
|
||||
pageProps: {
|
||||
controller: 'session/forgotPassword/index',
|
||||
view: 'session/forgotPassword/index.html'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'forgotpasswordpin.html',
|
||||
pageProps: {
|
||||
controller: 'session/resetPassword/index',
|
||||
view: 'session/resetPassword/index.html'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'wizardremoteaccess.html',
|
||||
pageProps: {
|
||||
controller: 'wizard/remote/index',
|
||||
view: 'wizard/remote/index.html'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'wizardfinish.html',
|
||||
pageProps: {
|
||||
controller: 'wizard/finish/index',
|
||||
view: 'wizard/finish/index.html'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'wizardlibrary.html',
|
||||
pageProps: {
|
||||
controller: 'dashboard/library',
|
||||
view: 'wizard/library.html'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'wizardsettings.html',
|
||||
pageProps: {
|
||||
controller: 'wizard/settings/index',
|
||||
view: 'wizard/settings/index.html'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'wizardstart.html',
|
||||
pageProps: {
|
||||
controller: 'wizard/start/index',
|
||||
view: 'wizard/start/index.html'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'wizarduser.html',
|
||||
pageProps: {
|
||||
controller: 'wizard/user/index',
|
||||
view: 'wizard/user/index.html'
|
||||
}
|
||||
}
|
||||
];
|
|
@ -55,7 +55,7 @@ const getTabs = () => {
|
|||
|
||||
const Movies: FC = () => {
|
||||
const [ searchParams ] = useSearchParams();
|
||||
const currentTabIndex = parseInt(searchParams.get('tab') || getDefaultTabIndex(searchParams.get('topParentId')).toString());
|
||||
const currentTabIndex = parseInt(searchParams.get('tab') || getDefaultTabIndex(searchParams.get('topParentId')).toString(), 10);
|
||||
const [ selectedIndex, setSelectedIndex ] = useState(currentTabIndex);
|
||||
const element = useRef<HTMLDivElement>(null);
|
||||
|
||||
|
@ -95,7 +95,7 @@ const Movies: FC = () => {
|
|||
};
|
||||
|
||||
const onTabChange = useCallback((e: { detail: { selectedTabIndex: string; }; }) => {
|
||||
const newIndex = parseInt(e.detail.selectedTabIndex);
|
||||
const newIndex = parseInt(e.detail.selectedTabIndex, 10);
|
||||
setSelectedIndex(newIndex);
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -228,8 +228,8 @@ const UserEdit: FunctionComponent = () => {
|
|||
user.Policy.EnableContentDownloading = (page.querySelector('.chkEnableDownloading') as HTMLInputElement).checked;
|
||||
user.Policy.EnableRemoteAccess = (page.querySelector('.chkRemoteAccess') as HTMLInputElement).checked;
|
||||
user.Policy.RemoteClientBitrateLimit = Math.floor(1e6 * parseFloat((page.querySelector('#txtRemoteClientBitrateLimit') as HTMLInputElement).value || '0'));
|
||||
user.Policy.LoginAttemptsBeforeLockout = parseInt((page.querySelector('#txtLoginAttemptsBeforeLockout') as HTMLInputElement).value || '0');
|
||||
user.Policy.MaxActiveSessions = parseInt((page.querySelector('#txtMaxActiveSessions') as HTMLInputElement).value || '0');
|
||||
user.Policy.LoginAttemptsBeforeLockout = parseInt((page.querySelector('#txtLoginAttemptsBeforeLockout') as HTMLInputElement).value || '0', 10);
|
||||
user.Policy.MaxActiveSessions = parseInt((page.querySelector('#txtMaxActiveSessions') as HTMLInputElement).value || '0', 10);
|
||||
user.Policy.AuthenticationProviderId = (page.querySelector('#selectLoginProvider') as HTMLSelectElement).value;
|
||||
user.Policy.PasswordResetProviderId = (page.querySelector('#selectPasswordResetProvider') as HTMLSelectElement).value;
|
||||
user.Policy.EnableContentDeletion = (page.querySelector('.chkEnableDeleteAllFolders') as HTMLInputElement).checked;
|
||||
|
|
|
@ -11,7 +11,7 @@ import '../../elements/emby-button/emby-button';
|
|||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../components/cardbuilder/card.scss';
|
||||
import '../../components/indicators/indicators.scss';
|
||||
import '../../assets/css/flexstyles.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import Page from '../../components/Page';
|
||||
|
||||
type MenuEntry = {
|
||||
|
|
|
@ -224,7 +224,7 @@ const uaMatch = function (ua) {
|
|||
|
||||
version = version || match[2] || '0';
|
||||
|
||||
let versionMajor = parseInt(version.split('.')[0]);
|
||||
let versionMajor = parseInt(version.split('.')[0], 10);
|
||||
|
||||
if (isNaN(versionMajor)) {
|
||||
versionMajor = 0;
|
||||
|
@ -295,7 +295,7 @@ if (browser.web0s) {
|
|||
delete browser.safari;
|
||||
|
||||
const v = (navigator.appVersion).match(/Tizen (\d+).(\d+)/);
|
||||
browser.tizenVersion = parseInt(v[1]);
|
||||
browser.tizenVersion = parseInt(v[1], 10);
|
||||
} else {
|
||||
browser.orsay = userAgent.toLowerCase().indexOf('smarthub') !== -1;
|
||||
}
|
||||
|
|
|
@ -303,7 +303,7 @@ import { getParameterByName } from '../utils/url.ts';
|
|||
updateEditorNode(this, item);
|
||||
}).on('pagebeforeshow', '.metadataEditorPage', function () {
|
||||
/* eslint-disable-next-line @babel/no-unused-expressions */
|
||||
import('../assets/css/metadataeditor.scss');
|
||||
import('../styles/metadataeditor.scss');
|
||||
}).on('pagebeforeshow', '.metadataEditorPage', function () {
|
||||
const page = this;
|
||||
Dashboard.getCurrentUser().then(function (user) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue