mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-15 18:08:17 -07:00
Merge branch 'master' into hadicharara/added-support-for-rtl-layouts
This commit is contained in:
commit
104ad71ea7
27
.eslintrc.js
27
.eslintrc.js
@ -34,7 +34,7 @@ module.exports = {
|
||||
'plugin:sonarjs/recommended'
|
||||
],
|
||||
rules: {
|
||||
'array-callback-return': ['error'],
|
||||
'array-callback-return': ['error', { 'checkForEach': true }],
|
||||
'block-spacing': ['error'],
|
||||
'brace-style': ['error', '1tbs', { 'allowSingleLine': true }],
|
||||
'comma-dangle': ['error', 'never'],
|
||||
@ -51,19 +51,22 @@ module.exports = {
|
||||
'no-floating-decimal': ['error'],
|
||||
'no-multi-spaces': ['error'],
|
||||
'no-multiple-empty-lines': ['error', { 'max': 1 }],
|
||||
'no-nested-ternary': ['error'],
|
||||
'no-restricted-globals': ['error'].concat(restrictedGlobals),
|
||||
'no-return-assign': ['error'],
|
||||
'no-return-await': ['error'],
|
||||
'no-sequences': ['error', { 'allowInParentheses': false }],
|
||||
'no-trailing-spaces': ['error'],
|
||||
'@babel/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
|
||||
'no-useless-constructor': ['error'],
|
||||
'no-var': ['error'],
|
||||
'no-void': ['error', { 'allowAsStatement': true }],
|
||||
'no-nested-ternary': ['error'],
|
||||
'no-warning-comments': ['warn', { 'terms': ['fixme', 'hack', 'xxx'] }],
|
||||
'one-var': ['error', 'never'],
|
||||
'padded-blocks': ['error', 'never'],
|
||||
'prefer-const': ['error', { 'destructuring': 'all' }],
|
||||
'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],
|
||||
'@babel/semi': ['error'],
|
||||
'no-var': ['error'],
|
||||
'space-before-blocks': ['error'],
|
||||
'space-infix-ops': 'error',
|
||||
'yoda': 'error',
|
||||
@ -72,18 +75,8 @@ module.exports = {
|
||||
|
||||
'sonarjs/cognitive-complexity': ['warn'],
|
||||
// TODO: Enable the following rules and fix issues
|
||||
'sonarjs/max-switch-cases': ['off'],
|
||||
'sonarjs/no-collapsible-if': ['off'],
|
||||
'sonarjs/no-duplicate-string': ['off'],
|
||||
'sonarjs/no-duplicated-branches': ['off'],
|
||||
'sonarjs/no-gratuitous-expressions': ['off'],
|
||||
'sonarjs/no-identical-functions': ['off'],
|
||||
'sonarjs/no-nested-switch': ['off'],
|
||||
'sonarjs/no-redundant-jump': ['off'],
|
||||
'sonarjs/no-small-switch': ['off'],
|
||||
'sonarjs/no-unused-collection': ['off'],
|
||||
'sonarjs/prefer-object-literal': ['off'],
|
||||
'sonarjs/prefer-single-boolean-return': ['off']
|
||||
'sonarjs/prefer-object-literal': ['off']
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
@ -261,7 +254,11 @@ module.exports = {
|
||||
'plugin:react/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
'plugin:jsx-a11y/recommended'
|
||||
]
|
||||
],
|
||||
rules: {
|
||||
'no-useless-constructor': ['off'],
|
||||
'@typescript-eslint/no-useless-constructor': ['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@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@e0e5ded33cabb451ae0a9768fc7b0410bad9ad44 # tag=v2
|
||||
uses: github/codeql-action/init@807578363a7869ca324a79039e6db9c843e0e100 # tag=v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-extended
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@e0e5ded33cabb451ae0a9768fc7b0410bad9ad44 # tag=v2
|
||||
uses: github/codeql-action/autobuild@807578363a7869ca324a79039e6db9c843e0e100 # tag=v2
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@e0e5ded33cabb451ae0a9768fc7b0410bad9ad44 # tag=v2
|
||||
uses: github/codeql-action/analyze@807578363a7869ca324a79039e6db9c843e0e100 # tag=v2
|
||||
|
2
.github/workflows/commands.yml
vendored
2
.github/workflows/commands.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
comment-id: ${{ github.event.comment.id }}
|
||||
reactions: '+1'
|
||||
- name: Checkout the latest code
|
||||
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3.0.2
|
||||
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3.1.0
|
||||
with:
|
||||
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
42
.github/workflows/lint.yml
vendored
42
.github/workflows/lint.yml
vendored
@ -20,19 +20,7 @@ jobs:
|
||||
with:
|
||||
node-version: 16
|
||||
check-latest: true
|
||||
|
||||
- name: Get npm cache directory path
|
||||
id: npm-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(npm config get cache)"
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3.0.10
|
||||
id: npm-cache
|
||||
with:
|
||||
path: ${{ steps.npm-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-npm-
|
||||
cache: npm
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: npm ci --no-audit
|
||||
@ -55,19 +43,7 @@ jobs:
|
||||
with:
|
||||
node-version: 16
|
||||
check-latest: true
|
||||
|
||||
- name: Get npm cache directory path
|
||||
id: npm-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(npm config get cache)"
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3.0.10
|
||||
id: npm-cache
|
||||
with:
|
||||
path: ${{ steps.npm-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-npm-
|
||||
cache: npm
|
||||
|
||||
- name: Set up stylelint matcher
|
||||
uses: xt0rted/stylelint-problem-matcher@34db1b874c0452909f0696aedef70b723870a583 # tag=v1
|
||||
@ -93,19 +69,7 @@ jobs:
|
||||
with:
|
||||
node-version: 16
|
||||
check-latest: true
|
||||
|
||||
- name: Get npm cache directory path
|
||||
id: npm-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(npm config get cache)"
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@56461b9eb0f8438fd15c7a9968e3c9ebb18ceff1 # tag=v3.0.10
|
||||
id: npm-cache
|
||||
with:
|
||||
path: ${{ steps.npm-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-npm-
|
||||
cache: npm
|
||||
|
||||
- name: Set up stylelint matcher
|
||||
uses: xt0rted/stylelint-problem-matcher@34db1b874c0452909f0696aedef70b723870a583 # tag=v1
|
||||
|
2
.github/workflows/repo-stale.yaml
vendored
2
.github/workflows/repo-stale.yaml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ contains(github.repository, 'jellyfin/') }}
|
||||
steps:
|
||||
- uses: actions/stale@3de2653986ebd134983c79fe2be5d45cc3d9f4e1 # tag=v6.0.0
|
||||
- uses: actions/stale@5ebf00ea0e4c1561e9b43a292ed34424fb1d4578 # tag=v6.0.1
|
||||
with:
|
||||
repo-token: ${{ secrets.JF_BOT_TOKEN }}
|
||||
days-before-stale: 120
|
||||
|
@ -52,6 +52,7 @@
|
||||
- [MinecraftPlaye](https://github.com/MinecraftPlaye)
|
||||
- [Matthew Jones](https://github.com/matthew-jones-uk)
|
||||
- [taku0](https://github.com/taku0)
|
||||
- [Viperinius](https://github.com/Viperinius)
|
||||
- [is343](https://github.com/is343)
|
||||
- [Meet Pandya](https://github.com/meet-k-pandya)
|
||||
|
||||
|
10
cssnano.config.js
Normal file
10
cssnano.config.js
Normal file
@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
preset: [
|
||||
'default',
|
||||
// Turn off `mergeLonghand` because it combines `padding-*` and `margin-*`,
|
||||
// breaking fallback styles.
|
||||
// https://github.com/cssnano/cssnano/issues/1163
|
||||
// https://github.com/cssnano/cssnano/issues/1192
|
||||
{ mergeLonghand: false }
|
||||
]
|
||||
};
|
576
package-lock.json
generated
576
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
34
package.json
34
package.json
@ -5,21 +5,21 @@
|
||||
"repository": "https://github.com/jellyfin/jellyfin-web",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.19.1",
|
||||
"@babel/core": "7.19.3",
|
||||
"@babel/eslint-parser": "7.19.1",
|
||||
"@babel/eslint-plugin": "7.19.1",
|
||||
"@babel/plugin-proposal-class-properties": "7.18.6",
|
||||
"@babel/plugin-proposal-private-methods": "7.18.6",
|
||||
"@babel/plugin-transform-modules-umd": "7.18.6",
|
||||
"@babel/preset-env": "7.19.1",
|
||||
"@babel/preset-env": "7.19.3",
|
||||
"@babel/preset-react": "7.18.6",
|
||||
"@babel/preset-typescript": "7.18.6",
|
||||
"@types/escape-html": "1.0.2",
|
||||
"@types/lodash-es": "4.17.6",
|
||||
"@types/react": "17.0.50",
|
||||
"@types/react-dom": "17.0.17",
|
||||
"@typescript-eslint/eslint-plugin": "5.38.0",
|
||||
"@typescript-eslint/parser": "5.38.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.39.0",
|
||||
"@typescript-eslint/parser": "5.39.0",
|
||||
"@uupaa/dynamic-import-polyfill": "1.0.2",
|
||||
"autoprefixer": "10.4.12",
|
||||
"babel-loader": "8.2.5",
|
||||
@ -42,21 +42,21 @@
|
||||
"html-loader": "4.2.0",
|
||||
"html-webpack-plugin": "5.5.0",
|
||||
"mini-css-extract-plugin": "2.6.1",
|
||||
"postcss": "8.4.16",
|
||||
"postcss": "8.4.17",
|
||||
"postcss-loader": "7.0.1",
|
||||
"postcss-preset-env": "7.8.2",
|
||||
"postcss-scss": "4.0.5",
|
||||
"sass": "1.55.0",
|
||||
"sass-loader": "13.0.2",
|
||||
"source-map-loader": "3.0.1",
|
||||
"source-map-loader": "4.0.0",
|
||||
"style-loader": "3.3.1",
|
||||
"stylelint": "14.12.1",
|
||||
"stylelint": "14.13.0",
|
||||
"stylelint-config-rational-order": "0.1.2",
|
||||
"stylelint-no-browser-hacks": "1.2.1",
|
||||
"stylelint-order": "5.0.0",
|
||||
"stylelint-scss": "4.3.0",
|
||||
"ts-loader": "9.4.1",
|
||||
"typescript": "4.8.3",
|
||||
"typescript": "4.8.4",
|
||||
"webpack": "5.74.0",
|
||||
"webpack-cli": "4.10.0",
|
||||
"webpack-dev-server": "4.11.1",
|
||||
@ -66,20 +66,20 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/noto-sans": "4.5.11",
|
||||
"@fontsource/noto-sans-hk": "4.5.11",
|
||||
"@fontsource/noto-sans-jp": "4.5.11",
|
||||
"@fontsource/noto-sans-kr": "4.5.11",
|
||||
"@fontsource/noto-sans-sc": "4.5.11",
|
||||
"@fontsource/noto-sans-tc": "4.5.11",
|
||||
"@fontsource/noto-sans-hk": "4.5.12",
|
||||
"@fontsource/noto-sans-jp": "4.5.12",
|
||||
"@fontsource/noto-sans-kr": "4.5.12",
|
||||
"@fontsource/noto-sans-sc": "4.5.12",
|
||||
"@fontsource/noto-sans-tc": "4.5.12",
|
||||
"@jellyfin/libass-wasm": "4.1.1",
|
||||
"@jellyfin/sdk": "0.7.0",
|
||||
"blurhash": "2.0.0",
|
||||
"blurhash": "2.0.3",
|
||||
"classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
|
||||
"classnames": "2.3.2",
|
||||
"core-js": "3.25.3",
|
||||
"core-js": "3.25.5",
|
||||
"date-fns": "2.29.3",
|
||||
"dompurify": "2.4.0",
|
||||
"epubjs": "0.3.93",
|
||||
"epubjs": "0.4.2",
|
||||
"escape-html": "1.0.3",
|
||||
"fast-text-encoding": "1.0.6",
|
||||
"flv.js": "1.6.2",
|
||||
@ -92,7 +92,7 @@
|
||||
"jstree": "3.3.12",
|
||||
"libarchive.js": "1.3.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"marked": "4.1.0",
|
||||
"marked": "4.1.1",
|
||||
"material-design-icons-iconfont": "6.7.0",
|
||||
"native-promise-only": "0.8.1",
|
||||
"pdfjs-dist": "2.16.105",
|
||||
|
1
src/assets/img/devices/home-assistant.svg
Normal file
1
src/assets/img/devices/home-assistant.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.0 KiB |
@ -87,12 +87,10 @@ class AppRouter {
|
||||
|
||||
path = path.replace(this.baseUrl(), '');
|
||||
|
||||
if (this.currentRouteInfo && this.currentRouteInfo.path === path) {
|
||||
// can't use this with home right now due to the back menu
|
||||
if (this.currentRouteInfo.route.type !== 'home') {
|
||||
loading.hide();
|
||||
return Promise.resolve();
|
||||
}
|
||||
// can't use this with home right now due to the back menu
|
||||
if (this.currentRouteInfo?.path === path && this.currentRouteInfo.route.type !== 'home') {
|
||||
loading.hide();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
this.promiseShow = new Promise((resolve) => {
|
||||
@ -351,15 +349,13 @@ class AppRouter {
|
||||
onRequestFail(_e, data) {
|
||||
const apiClient = this;
|
||||
|
||||
if (data.status === 403) {
|
||||
if (data.errorCode === 'ParentalControl') {
|
||||
const isCurrentAllowed = appRouter.currentRouteInfo ? (appRouter.currentRouteInfo.route.anonymous || appRouter.currentRouteInfo.route.startup) : true;
|
||||
if (data.status === 403 && data.errorCode === 'ParentalControl') {
|
||||
const isCurrentAllowed = appRouter.currentRouteInfo ? (appRouter.currentRouteInfo.route.anonymous || appRouter.currentRouteInfo.route.startup) : true;
|
||||
|
||||
// Bounce to the login screen, but not if a password entry fails, obviously
|
||||
if (!isCurrentAllowed) {
|
||||
appRouter.showForcedLogoutMessage(globalize.translate('AccessRestrictedTryAgainLater'));
|
||||
appRouter.showLocalLogin(apiClient.serverId());
|
||||
}
|
||||
// Bounce to the login screen, but not if a password entry fails, obviously
|
||||
if (!isCurrentAllowed) {
|
||||
appRouter.showForcedLogoutMessage(globalize.translate('AccessRestrictedTryAgainLater'));
|
||||
appRouter.showLocalLogin(apiClient.serverId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -166,11 +166,7 @@ function supportsHtmlMediaAutoplay() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.mobile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !browser.mobile;
|
||||
}
|
||||
|
||||
function supportsCue() {
|
||||
|
@ -10,24 +10,13 @@ import './backdrop.scss';
|
||||
/* eslint-disable indent */
|
||||
|
||||
function enableAnimation() {
|
||||
if (browser.slow) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !browser.slow;
|
||||
}
|
||||
|
||||
function enableRotation() {
|
||||
if (browser.tv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Causes high cpu usage
|
||||
if (browser.firefox) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !browser.tv
|
||||
// Causes high cpu usage
|
||||
&& !browser.firefox;
|
||||
}
|
||||
|
||||
class Backdrop {
|
||||
|
@ -390,7 +390,8 @@ import { appRouter } from '../appRouter';
|
||||
} else if (options.indexBy === 'ProductionYear') {
|
||||
newIndexValue = item.ProductionYear;
|
||||
} else if (options.indexBy === 'CommunityRating') {
|
||||
newIndexValue = item.CommunityRating ? (Math.floor(item.CommunityRating) + (item.CommunityRating % 1 >= 0.5 ? 0.5 : 0)) + '+' : null;
|
||||
const roundedRatingDecimal = item.CommunityRating % 1 >= 0.5 ? 0.5 : 0;
|
||||
newIndexValue = item.CommunityRating ? (Math.floor(item.CommunityRating) + roundedRatingDecimal) + '+' : null;
|
||||
}
|
||||
|
||||
if (newIndexValue !== currentIndexValue) {
|
||||
@ -512,6 +513,7 @@ import { appRouter } from '../appRouter';
|
||||
let imgType = null;
|
||||
let itemId = null;
|
||||
|
||||
/* eslint-disable sonarjs/no-duplicated-branches */
|
||||
if (options.preferThumb && item.ImageTags && item.ImageTags.Thumb) {
|
||||
imgType = 'Thumb';
|
||||
imgTag = item.ImageTags.Thumb;
|
||||
@ -608,6 +610,7 @@ import { appRouter } from '../appRouter';
|
||||
imgTag = item.ParentBackdropImageTags[0];
|
||||
itemId = item.ParentBackdropItemId;
|
||||
}
|
||||
/* eslint-enable sonarjs/no-duplicated-branches */
|
||||
|
||||
if (!itemId) {
|
||||
itemId = item.Id;
|
||||
@ -790,10 +793,8 @@ import { appRouter } from '../appRouter';
|
||||
|
||||
const showOtherText = isOuterFooter ? !overlayText : overlayText;
|
||||
|
||||
if (isOuterFooter && options.cardLayout && layoutManager.mobile) {
|
||||
if (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>`;
|
||||
}
|
||||
if (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>`;
|
||||
}
|
||||
|
||||
const cssClass = options.centerText ? 'cardText cardTextCentered' : 'cardText';
|
||||
@ -803,33 +804,31 @@ import { appRouter } from '../appRouter';
|
||||
const parentTitleUnderneath = item.Type === 'MusicAlbum' || item.Type === 'Audio' || item.Type === 'MusicVideo';
|
||||
let titleAdded;
|
||||
|
||||
if (showOtherText) {
|
||||
if ((options.showParentTitle || options.showParentTitleOrTitle) && !parentTitleUnderneath) {
|
||||
if (isOuterFooter && item.Type === 'Episode' && item.SeriesName) {
|
||||
if (item.SeriesId) {
|
||||
lines.push(getTextActionButton({
|
||||
Id: item.SeriesId,
|
||||
ServerId: serverId,
|
||||
Name: item.SeriesName,
|
||||
Type: 'Series',
|
||||
IsFolder: true
|
||||
}));
|
||||
} else {
|
||||
lines.push(escapeHtml(item.SeriesName));
|
||||
if (showOtherText && (options.showParentTitle || options.showParentTitleOrTitle) && !parentTitleUnderneath) {
|
||||
if (isOuterFooter && item.Type === 'Episode' && item.SeriesName) {
|
||||
if (item.SeriesId) {
|
||||
lines.push(getTextActionButton({
|
||||
Id: item.SeriesId,
|
||||
ServerId: serverId,
|
||||
Name: item.SeriesName,
|
||||
Type: 'Series',
|
||||
IsFolder: true
|
||||
}));
|
||||
} else {
|
||||
lines.push(escapeHtml(item.SeriesName));
|
||||
}
|
||||
} else {
|
||||
if (isUsingLiveTvNaming(item)) {
|
||||
lines.push(escapeHtml(item.Name));
|
||||
|
||||
if (!item.EpisodeTitle && !item.IndexNumber) {
|
||||
titleAdded = true;
|
||||
}
|
||||
} else {
|
||||
if (isUsingLiveTvNaming(item)) {
|
||||
lines.push(escapeHtml(item.Name));
|
||||
const parentTitle = item.SeriesName || item.Series || item.Album || item.AlbumArtist || '';
|
||||
|
||||
if (!item.EpisodeTitle && !item.IndexNumber) {
|
||||
titleAdded = true;
|
||||
}
|
||||
} else {
|
||||
const parentTitle = item.SeriesName || item.Series || item.Album || item.AlbumArtist || '';
|
||||
|
||||
if (parentTitle || showTitle) {
|
||||
lines.push(escapeHtml(parentTitle));
|
||||
}
|
||||
if (parentTitle || showTitle) {
|
||||
lines.push(escapeHtml(parentTitle));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -987,10 +986,8 @@ import { appRouter } from '../appRouter';
|
||||
}
|
||||
}
|
||||
|
||||
if (options.showPersonRoleOrType) {
|
||||
if (item.Role) {
|
||||
lines.push(globalize.translate('PersonRole', escapeHtml(item.Role)));
|
||||
}
|
||||
if (options.showPersonRoleOrType && item.Role) {
|
||||
lines.push(globalize.translate('PersonRole', escapeHtml(item.Role)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1010,13 +1007,11 @@ import { appRouter } from '../appRouter';
|
||||
html += progressHtml;
|
||||
}
|
||||
|
||||
if (html) {
|
||||
if (!isOuterFooter || logoUrl || options.cardLayout) {
|
||||
html = '<div class="' + footerClass + '">' + html;
|
||||
if (html && (!isOuterFooter || logoUrl || options.cardLayout)) {
|
||||
html = '<div class="' + footerClass + '">' + html;
|
||||
|
||||
//cardFooter
|
||||
html += '</div>';
|
||||
}
|
||||
//cardFooter
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
return html;
|
||||
|
@ -34,10 +34,8 @@ import ServerConnections from '../ServerConnections';
|
||||
|
||||
let shape = (options.backdropShape || 'backdrop');
|
||||
|
||||
if (videoStream.Width && videoStream.Height) {
|
||||
if ((videoStream.Width / videoStream.Height) <= 1.2) {
|
||||
shape = (options.squareShape || 'square');
|
||||
}
|
||||
if (videoStream.Width && videoStream.Height && (videoStream.Width / videoStream.Height) <= 1.2) {
|
||||
shape = (options.squareShape || 'square');
|
||||
}
|
||||
|
||||
className += ` ${shape}Card`;
|
||||
|
@ -87,7 +87,8 @@ export default class channelMapper {
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += `<button class="btnMap autoSize" is="paper-icon-button-light" type="button" data-id="${channel.Id}" data-providerid="${channel.ProviderChannelId}"><span class="material-icons mode_edit" aria-hidden="true"></span></button>`;
|
||||
return html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function getEditorHtml() {
|
||||
@ -100,7 +101,8 @@ export default class channelMapper {
|
||||
html += '</div>';
|
||||
html += '</form>';
|
||||
html += '</div>';
|
||||
return html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function initEditor(dlg, options) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { UserDto } from '@jellyfin/sdk/lib/generated-client';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { localeWithSuffix } from '../../../scripts/dfnshelper';
|
||||
import { getLocaleWithSuffix } from '../../../scripts/dfnshelper';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import cardBuilder from '../../cardbuilder/cardBuilder';
|
||||
import IconButtonElement from '../../../elements/IconButtonElement';
|
||||
@ -23,7 +23,7 @@ type IProps = {
|
||||
|
||||
const getLastSeenText = (lastActivityDate?: string | null) => {
|
||||
if (lastActivityDate) {
|
||||
return globalize.translate('LastSeen', formatDistanceToNow(Date.parse(lastActivityDate), localeWithSuffix));
|
||||
return globalize.translate('LastSeen', formatDistanceToNow(Date.parse(lastActivityDate), getLocaleWithSuffix()));
|
||||
}
|
||||
|
||||
return '';
|
||||
|
@ -57,6 +57,7 @@ import '../../assets/css/scrollstyles.scss';
|
||||
|
||||
if ((shouldClose || !isOpened(dlg)) && unlisten) {
|
||||
unlisten();
|
||||
unlisten = null;
|
||||
}
|
||||
|
||||
if (shouldClose) {
|
||||
@ -64,6 +65,22 @@ import '../../assets/css/scrollstyles.scss';
|
||||
}
|
||||
}
|
||||
|
||||
function finishClose() {
|
||||
if (unlisten) {
|
||||
unlisten();
|
||||
unlisten = null;
|
||||
}
|
||||
|
||||
dlg.dispatchEvent(new CustomEvent('close', {
|
||||
bubbles: false,
|
||||
cancelable: false
|
||||
}));
|
||||
|
||||
resolve({
|
||||
element: dlg
|
||||
});
|
||||
}
|
||||
|
||||
function onBackCommand(e) {
|
||||
if (e.detail.command === 'back') {
|
||||
e.preventDefault();
|
||||
@ -79,6 +96,7 @@ import '../../assets/css/scrollstyles.scss';
|
||||
|
||||
if (unlisten) {
|
||||
unlisten();
|
||||
unlisten = null;
|
||||
}
|
||||
|
||||
removeBackdrop(dlg);
|
||||
@ -92,9 +110,13 @@ import '../../assets/css/scrollstyles.scss';
|
||||
const state = history.location.state || {};
|
||||
if (state.dialogs?.length > 0) {
|
||||
if (state.dialogs[state.dialogs.length - 1] === hash) {
|
||||
unlisten = history.listen(finishClose);
|
||||
history.back();
|
||||
} else if (state.dialogs.includes(hash)) {
|
||||
console.warn('[dialogHelper] dialog "%s" was closed, but is not the last dialog opened', hash);
|
||||
|
||||
unlisten = history.listen(finishClose);
|
||||
|
||||
// Remove the closed dialog hash from the history state
|
||||
history.replace(
|
||||
`${history.location.pathname}${history.location.search}`,
|
||||
@ -123,18 +145,9 @@ import '../../assets/css/scrollstyles.scss';
|
||||
}
|
||||
}
|
||||
|
||||
//resolve();
|
||||
// if we just called history.back(), then use a timeout to allow the history events to fire first
|
||||
setTimeout(() => {
|
||||
dlg.dispatchEvent(new CustomEvent('close', {
|
||||
bubbles: false,
|
||||
cancelable: false
|
||||
}));
|
||||
|
||||
resolve({
|
||||
element: dlg
|
||||
});
|
||||
}, 1);
|
||||
if (!unlisten) {
|
||||
finishClose();
|
||||
}
|
||||
}
|
||||
|
||||
dlg.addEventListener('_close', onDialogClosed);
|
||||
@ -262,6 +275,11 @@ import '../../assets/css/scrollstyles.scss';
|
||||
}
|
||||
}
|
||||
|
||||
const getAnimationEndHandler = (dlg, callback) => function handler() {
|
||||
dom.removeEventListener(dlg, dom.whichAnimationEvent(), handler, { once: true });
|
||||
callback();
|
||||
};
|
||||
|
||||
function animateDialogOpen(dlg) {
|
||||
const onAnimationFinish = () => {
|
||||
focusManager.pushScope(dlg);
|
||||
@ -277,15 +295,11 @@ import '../../assets/css/scrollstyles.scss';
|
||||
};
|
||||
|
||||
if (enableAnimation()) {
|
||||
const onFinish = () => {
|
||||
dom.removeEventListener(dlg, dom.whichAnimationEvent(), onFinish, {
|
||||
once: true
|
||||
});
|
||||
onAnimationFinish();
|
||||
};
|
||||
dom.addEventListener(dlg, dom.whichAnimationEvent(), onFinish, {
|
||||
once: true
|
||||
});
|
||||
dom.addEventListener(
|
||||
dlg,
|
||||
dom.whichAnimationEvent(),
|
||||
getAnimationEndHandler(dlg, onAnimationFinish),
|
||||
{ once: true });
|
||||
|
||||
return;
|
||||
}
|
||||
@ -311,15 +325,12 @@ import '../../assets/css/scrollstyles.scss';
|
||||
animated = false;
|
||||
break;
|
||||
}
|
||||
const onFinish = () => {
|
||||
dom.removeEventListener(dlg, dom.whichAnimationEvent(), onFinish, {
|
||||
once: true
|
||||
});
|
||||
onAnimationFinish();
|
||||
};
|
||||
dom.addEventListener(dlg, dom.whichAnimationEvent(), onFinish, {
|
||||
once: true
|
||||
});
|
||||
|
||||
dom.addEventListener(
|
||||
dlg,
|
||||
dom.whichAnimationEvent(),
|
||||
getAnimationEndHandler(dlg, onAnimationFinish),
|
||||
{ once: true });
|
||||
|
||||
if (animated) {
|
||||
return;
|
||||
|
@ -26,6 +26,7 @@
|
||||
<option value="es_DO">Español (Dominicana)</option>
|
||||
<option value="es-MX">Español (México)</option>
|
||||
<option value="et">Eesti</option>
|
||||
<option value="eu">Basque</option>
|
||||
<option value="fa">فارسی</option>
|
||||
<option value="fi">Suomi</option>
|
||||
<option value="fil">Filipino</option>
|
||||
|
@ -132,6 +132,7 @@ function saveValues(context, settings, settingsKey) {
|
||||
seriesStatuses.push(elems[i].getAttribute('data-filter'));
|
||||
}
|
||||
}
|
||||
userSettings.setFilter(`${settingsKey}-filter-SeriesStatus`, seriesStatuses.join(','));
|
||||
|
||||
// Genres
|
||||
const genres = [];
|
||||
|
@ -56,15 +56,8 @@ import scrollManager from './scrollManager';
|
||||
}).join(',') + ',.focusable';
|
||||
|
||||
function isFocusable(elem) {
|
||||
if (focusableTagNames.indexOf(elem.tagName) !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (elem.classList && elem.classList.contains('focusable')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return focusableTagNames.indexOf(elem.tagName) !== -1
|
||||
|| (elem.classList?.contains('focusable'));
|
||||
}
|
||||
|
||||
function normalizeFocusable(elem, originalElement) {
|
||||
@ -97,11 +90,7 @@ import scrollManager from './scrollManager';
|
||||
// Determines if a focusable element can be focused at a given point in time
|
||||
function isCurrentlyFocusableInternal(elem) {
|
||||
// http://stackoverflow.com/questions/19669786/check-if-element-is-visible-in-dom
|
||||
if (elem.offsetParent === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return elem.offsetParent !== null;
|
||||
}
|
||||
|
||||
// Determines if a focusable element can be focused at a given point in time
|
||||
@ -384,10 +373,11 @@ import scrollManager from './scrollManager';
|
||||
// See if there's a focusable container, and if so, send the focus command to that
|
||||
if (activeElement) {
|
||||
const nearestElementFocusableParent = dom.parentWithClass(nearestElement, 'focusable');
|
||||
if (nearestElementFocusableParent && nearestElementFocusableParent !== nearestElement) {
|
||||
if (focusableContainer !== nearestElementFocusableParent) {
|
||||
nearestElement = nearestElementFocusableParent;
|
||||
}
|
||||
if (nearestElementFocusableParent
|
||||
&& nearestElementFocusableParent !== nearestElement
|
||||
&& focusableContainer !== nearestElementFocusableParent
|
||||
) {
|
||||
nearestElement = nearestElementFocusableParent;
|
||||
}
|
||||
}
|
||||
focus(nearestElement);
|
||||
|
@ -26,12 +26,8 @@ import { Events } from 'jellyfin-apiclient';
|
||||
function canPlayNativeHls() {
|
||||
const media = document.createElement('video');
|
||||
|
||||
if (media.canPlayType('application/x-mpegURL').replace(/no/, '') ||
|
||||
media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return !!(media.canPlayType('application/x-mpegURL').replace(/no/, '') ||
|
||||
media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, ''));
|
||||
}
|
||||
|
||||
export function enableHlsJsPlayer(runTimeTicks, mediaType) {
|
||||
@ -123,11 +119,10 @@ import { Events } from 'jellyfin-apiclient';
|
||||
}
|
||||
|
||||
export function isValidDuration(duration) {
|
||||
if (duration && !isNaN(duration) && duration !== Number.POSITIVE_INFINITY && duration !== Number.NEGATIVE_INFINITY) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return duration
|
||||
&& !isNaN(duration)
|
||||
&& duration !== Number.POSITIVE_INFINITY
|
||||
&& duration !== Number.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
function setCurrentTimeIfNeeded(element, seconds) {
|
||||
@ -159,11 +154,15 @@ import { Events } from 'jellyfin-apiclient';
|
||||
// (but rewinding cannot happen as the first event with media of non-empty duration)
|
||||
console.debug(`seeking to ${seconds} on ${e.type} event`);
|
||||
setCurrentTimeIfNeeded(element, seconds);
|
||||
events.forEach(name => element.removeEventListener(name, onMediaChange));
|
||||
events.forEach(name => {
|
||||
element.removeEventListener(name, onMediaChange);
|
||||
});
|
||||
if (onMediaReady) onMediaReady();
|
||||
}
|
||||
};
|
||||
events.forEach(name => element.addEventListener(name, onMediaChange));
|
||||
events.forEach(name => {
|
||||
element.addEventListener(name, onMediaChange);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -273,28 +272,23 @@ import { Events } from 'jellyfin-apiclient';
|
||||
hls.on(Hls.Events.ERROR, function (event, data) {
|
||||
console.error('HLS Error: Type: ' + data.type + ' Details: ' + (data.details || '') + ' Fatal: ' + (data.fatal || false));
|
||||
|
||||
switch (data.type) {
|
||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||
// try to recover network error
|
||||
if (data.response && data.response.code && data.response.code >= 400) {
|
||||
console.debug('hls.js response error code: ' + data.response.code);
|
||||
// try to recover network error
|
||||
if (data.type === Hls.ErrorTypes.NETWORK_ERROR
|
||||
&& data.response?.code && data.response.code >= 400
|
||||
) {
|
||||
console.debug('hls.js response error code: ' + data.response.code);
|
||||
|
||||
// Trigger failure differently depending on whether this is prior to start of playback, or after
|
||||
hls.destroy();
|
||||
// Trigger failure differently depending on whether this is prior to start of playback, or after
|
||||
hls.destroy();
|
||||
|
||||
if (reject) {
|
||||
reject('servererror');
|
||||
reject = null;
|
||||
} else {
|
||||
onErrorInternal(instance, 'servererror');
|
||||
}
|
||||
if (reject) {
|
||||
reject('servererror');
|
||||
reject = null;
|
||||
} else {
|
||||
onErrorInternal(instance, 'servererror');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.fatal) {
|
||||
|
@ -5,15 +5,9 @@ import './indicators.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
|
||||
export function enableProgressIndicator(item) {
|
||||
if (item.MediaType === 'Video' && item.Type !== 'TvChannel') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.Type === 'AudioBook' || item.Type === 'AudioPodcast') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return (item.MediaType === 'Video' && item.Type !== 'TvChannel')
|
||||
|| item.Type === 'AudioBook'
|
||||
|| item.Type === 'AudioPodcast';
|
||||
}
|
||||
|
||||
export function getProgressHtml(pct, options) {
|
||||
|
@ -72,26 +72,25 @@ import toast from './toast/toast';
|
||||
}
|
||||
}
|
||||
|
||||
if (item.IsFolder || item.Type === 'MusicArtist' || item.Type === 'MusicGenre') {
|
||||
if (item.CollectionType !== 'livetv') {
|
||||
if (options.shuffle !== false) {
|
||||
commands.push({
|
||||
name: globalize.translate('Shuffle'),
|
||||
id: 'shuffle',
|
||||
icon: 'shuffle'
|
||||
});
|
||||
}
|
||||
}
|
||||
if ((item.IsFolder || item.Type === 'MusicArtist' || item.Type === 'MusicGenre')
|
||||
&& item.CollectionType !== 'livetv'
|
||||
&& options.shuffle !== false
|
||||
) {
|
||||
commands.push({
|
||||
name: globalize.translate('Shuffle'),
|
||||
id: 'shuffle',
|
||||
icon: 'shuffle'
|
||||
});
|
||||
}
|
||||
|
||||
if (item.MediaType === 'Audio' || item.Type === 'MusicAlbum' || item.Type === 'MusicArtist' || item.Type === 'MusicGenre') {
|
||||
if (options.instantMix !== false && !itemHelper.isLocalItem(item)) {
|
||||
commands.push({
|
||||
name: globalize.translate('InstantMix'),
|
||||
id: 'instantmix',
|
||||
icon: 'explore'
|
||||
});
|
||||
}
|
||||
if ((item.MediaType === 'Audio' || item.Type === 'MusicAlbum' || item.Type === 'MusicArtist' || item.Type === 'MusicGenre')
|
||||
&& options.instantMix !== false && !itemHelper.isLocalItem(item)
|
||||
) {
|
||||
commands.push({
|
||||
name: globalize.translate('InstantMix'),
|
||||
id: 'instantmix',
|
||||
icon: 'explore'
|
||||
});
|
||||
}
|
||||
|
||||
if (commands.length) {
|
||||
@ -180,57 +179,49 @@ import toast from './toast/toast';
|
||||
}
|
||||
|
||||
const canEdit = itemHelper.canEdit(user, item);
|
||||
if (canEdit) {
|
||||
if (options.edit !== false && item.Type !== 'SeriesTimer') {
|
||||
const text = (item.Type === 'Timer' || item.Type === 'SeriesTimer') ? globalize.translate('Edit') : globalize.translate('EditMetadata');
|
||||
commands.push({
|
||||
name: text,
|
||||
id: 'edit',
|
||||
icon: 'edit'
|
||||
});
|
||||
}
|
||||
if (canEdit && options.edit !== false && item.Type !== 'SeriesTimer') {
|
||||
const text = (item.Type === 'Timer' || item.Type === 'SeriesTimer') ? globalize.translate('Edit') : globalize.translate('EditMetadata');
|
||||
commands.push({
|
||||
name: text,
|
||||
id: 'edit',
|
||||
icon: 'edit'
|
||||
});
|
||||
}
|
||||
|
||||
if (itemHelper.canEditImages(user, item)) {
|
||||
if (options.editImages !== false) {
|
||||
commands.push({
|
||||
name: globalize.translate('EditImages'),
|
||||
id: 'editimages',
|
||||
icon: 'image'
|
||||
});
|
||||
}
|
||||
if (itemHelper.canEditImages(user, item) && options.editImages !== false) {
|
||||
commands.push({
|
||||
name: globalize.translate('EditImages'),
|
||||
id: 'editimages',
|
||||
icon: 'image'
|
||||
});
|
||||
}
|
||||
|
||||
if (canEdit) {
|
||||
if (item.MediaType === 'Video' && item.Type !== 'TvChannel' && item.Type !== 'Program' && item.LocationType !== 'Virtual' && !(item.Type === 'Recording' && item.Status !== 'Completed')) {
|
||||
if (options.editSubtitles !== false) {
|
||||
commands.push({
|
||||
name: globalize.translate('EditSubtitles'),
|
||||
id: 'editsubtitles',
|
||||
icon: 'closed_caption'
|
||||
});
|
||||
}
|
||||
}
|
||||
if (canEdit && item.MediaType === 'Video' && item.Type !== 'TvChannel' && item.Type !== 'Program'
|
||||
&& item.LocationType !== 'Virtual'
|
||||
&& !(item.Type === 'Recording' && item.Status !== 'Completed')
|
||||
&& options.editSubtitles !== false
|
||||
) {
|
||||
commands.push({
|
||||
name: globalize.translate('EditSubtitles'),
|
||||
id: 'editsubtitles',
|
||||
icon: 'closed_caption'
|
||||
});
|
||||
}
|
||||
|
||||
if (options.identify !== false) {
|
||||
if (itemHelper.canIdentify(user, item)) {
|
||||
commands.push({
|
||||
name: globalize.translate('Identify'),
|
||||
id: 'identify',
|
||||
icon: 'edit'
|
||||
});
|
||||
}
|
||||
if (options.identify !== false && itemHelper.canIdentify(user, item)) {
|
||||
commands.push({
|
||||
name: globalize.translate('Identify'),
|
||||
id: 'identify',
|
||||
icon: 'edit'
|
||||
});
|
||||
}
|
||||
|
||||
if (item.MediaSources) {
|
||||
if (options.moremediainfo !== false) {
|
||||
commands.push({
|
||||
name: globalize.translate('MoreMediaInfo'),
|
||||
id: 'moremediainfo',
|
||||
icon: 'info'
|
||||
});
|
||||
}
|
||||
if (item.MediaSources && options.moremediainfo !== false) {
|
||||
commands.push({
|
||||
name: globalize.translate('MoreMediaInfo'),
|
||||
id: 'moremediainfo',
|
||||
icon: 'info'
|
||||
});
|
||||
}
|
||||
|
||||
if (item.Type === 'Program' && options.record !== false) {
|
||||
@ -240,11 +231,7 @@ import toast from './toast/toast';
|
||||
id: 'record',
|
||||
icon: 'fiber_manual_record'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (item.Type === 'Program' && options.record !== false) {
|
||||
if (!item.TimerId) {
|
||||
} else {
|
||||
commands.push({
|
||||
name: globalize.translate('Record'),
|
||||
id: 'record',
|
||||
@ -277,26 +264,20 @@ import toast from './toast/toast';
|
||||
});
|
||||
}
|
||||
|
||||
if (!restrictOptions) {
|
||||
if (options.share === true) {
|
||||
if (itemHelper.canShare(item, user)) {
|
||||
commands.push({
|
||||
name: globalize.translate('Share'),
|
||||
id: 'share',
|
||||
icon: 'share'
|
||||
});
|
||||
}
|
||||
}
|
||||
if (!restrictOptions && options.share === true && itemHelper.canShare(item, user)) {
|
||||
commands.push({
|
||||
name: globalize.translate('Share'),
|
||||
id: 'share',
|
||||
icon: 'share'
|
||||
});
|
||||
}
|
||||
|
||||
if (options.sync !== false) {
|
||||
if (itemHelper.canSync(user, item)) {
|
||||
commands.push({
|
||||
name: globalize.translate('Sync'),
|
||||
id: 'sync',
|
||||
icon: 'sync'
|
||||
});
|
||||
}
|
||||
if (options.sync !== false && itemHelper.canSync(user, item)) {
|
||||
commands.push({
|
||||
name: globalize.translate('Sync'),
|
||||
id: 'sync',
|
||||
icon: 'sync'
|
||||
});
|
||||
}
|
||||
|
||||
if (options.openAlbum !== false && item.AlbumId && item.MediaType !== 'Photo') {
|
||||
|
@ -48,10 +48,8 @@ export function getDisplayName(item, options = {}) {
|
||||
export function supportsAddingToCollection(item) {
|
||||
const invalidTypes = ['Genre', 'MusicGenre', 'Studio', 'UserView', 'CollectionFolder', 'Audio', 'Program', 'Timer', 'SeriesTimer'];
|
||||
|
||||
if (item.Type === 'Recording') {
|
||||
if (item.Status !== 'Completed') {
|
||||
return false;
|
||||
}
|
||||
if (item.Type === 'Recording' && item.Status !== 'Completed') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !item.CollectionType && invalidTypes.indexOf(item.Type) === -1 && item.MediaType !== 'Photo' && !isLocalItem(item);
|
||||
@ -74,10 +72,8 @@ export function supportsAddingToPlaylist(item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.Type === 'Recording') {
|
||||
if (item.Status !== 'Completed') {
|
||||
return false;
|
||||
}
|
||||
if (item.Type === 'Recording' && item.Status !== 'Completed') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isLocalItem(item)) {
|
||||
@ -109,10 +105,8 @@ export function canEdit(user, item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.Type === 'Recording') {
|
||||
if (item.Status !== 'Completed') {
|
||||
return false;
|
||||
}
|
||||
if (item.Type === 'Recording' && item.Status !== 'Completed') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isLocalItem(item)) {
|
||||
@ -123,33 +117,23 @@ export function canEdit(user, item) {
|
||||
}
|
||||
|
||||
export function isLocalItem(item) {
|
||||
if (item && item.Id && typeof item.Id === 'string' && item.Id.indexOf('local') === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return item && item.Id && typeof item.Id === 'string' && item.Id.indexOf('local') === 0;
|
||||
}
|
||||
|
||||
export function canIdentify (user, item) {
|
||||
const itemType = item.Type;
|
||||
|
||||
if (itemType === 'Movie' ||
|
||||
itemType === 'Trailer' ||
|
||||
itemType === 'Series' ||
|
||||
itemType === 'BoxSet' ||
|
||||
itemType === 'Person' ||
|
||||
itemType === 'Book' ||
|
||||
itemType === 'MusicAlbum' ||
|
||||
itemType === 'MusicArtist' ||
|
||||
itemType === 'MusicVideo') {
|
||||
if (user.Policy.IsAdministrator) {
|
||||
if (!isLocalItem(item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return (itemType === 'Movie'
|
||||
|| itemType === 'Trailer'
|
||||
|| itemType === 'Series'
|
||||
|| itemType === 'BoxSet'
|
||||
|| itemType === 'Person'
|
||||
|| itemType === 'Book'
|
||||
|| itemType === 'MusicAlbum'
|
||||
|| itemType === 'MusicArtist'
|
||||
|| itemType === 'MusicVideo')
|
||||
&& user.Policy.IsAdministrator
|
||||
&& !isLocalItem(item);
|
||||
}
|
||||
|
||||
export function canEditImages (user, item) {
|
||||
@ -160,17 +144,11 @@ export function canEditImages (user, item) {
|
||||
}
|
||||
|
||||
if (itemType === 'UserView') {
|
||||
if (user.Policy.IsAdministrator) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return !!user.Policy.IsAdministrator;
|
||||
}
|
||||
|
||||
if (item.Type === 'Recording') {
|
||||
if (item.Status !== 'Completed') {
|
||||
return false;
|
||||
}
|
||||
if (item.Type === 'Recording' && item.Status !== 'Completed') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return itemType !== 'Timer' && itemType !== 'SeriesTimer' && canEdit(user, item) && !isLocalItem(item);
|
||||
@ -201,10 +179,8 @@ export function canShare (item, user) {
|
||||
if (item.Type === 'SeriesTimer') {
|
||||
return false;
|
||||
}
|
||||
if (item.Type === 'Recording') {
|
||||
if (item.Status !== 'Completed') {
|
||||
return false;
|
||||
}
|
||||
if (item.Type === 'Recording' && item.Status !== 'Completed') {
|
||||
return false;
|
||||
}
|
||||
if (isLocalItem(item)) {
|
||||
return false;
|
||||
@ -234,29 +210,21 @@ export function canMarkPlayed (item) {
|
||||
}
|
||||
}
|
||||
|
||||
if (item.Type === 'Series' ||
|
||||
item.Type === 'Season' ||
|
||||
item.Type === 'BoxSet' ||
|
||||
item.MediaType === 'Book' ||
|
||||
item.MediaType === 'Recording') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return item.Type === 'Series'
|
||||
|| item.Type === 'Season'
|
||||
|| item.Type === 'BoxSet'
|
||||
|| item.MediaType === 'Book'
|
||||
|| item.MediaType === 'Recording';
|
||||
}
|
||||
|
||||
export function canRate (item) {
|
||||
if (item.Type === 'Program'
|
||||
|| item.Type === 'Timer'
|
||||
|| item.Type === 'SeriesTimer'
|
||||
|| item.Type === 'CollectionFolder'
|
||||
|| item.Type === 'UserView'
|
||||
|| item.Type === 'Channel'
|
||||
|| !item.UserData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return item.Type !== 'Program'
|
||||
&& item.Type !== 'Timer'
|
||||
&& item.Type !== 'SeriesTimer'
|
||||
&& item.Type !== 'CollectionFolder'
|
||||
&& item.Type !== 'UserView'
|
||||
&& item.Type !== 'Channel'
|
||||
&& item.UserData;
|
||||
}
|
||||
|
||||
export function canConvert (item, user) {
|
||||
@ -287,11 +255,7 @@ export function canConvert (item, user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.IsPlaceHolder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !item.IsPlaceHolder;
|
||||
}
|
||||
|
||||
export function canRefreshMetadata (item, user) {
|
||||
@ -301,11 +265,10 @@ export function canRefreshMetadata (item, user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.Type !== 'Timer' && item.Type !== 'SeriesTimer' && item.Type !== 'Program' && item.Type !== 'TvChannel' && !(item.Type === 'Recording' && item.Status !== 'Completed')) {
|
||||
if (!isLocalItem(item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return item.Type !== 'Timer' && item.Type !== 'SeriesTimer' && item.Type !== 'Program'
|
||||
&& item.Type !== 'TvChannel'
|
||||
&& !(item.Type === 'Recording' && item.Status !== 'Completed')
|
||||
&& !isLocalItem(item);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -321,14 +284,12 @@ export function supportsMediaSourceSelection (item) {
|
||||
if (!item.MediaSources || (item.MediaSources.length === 1 && item.MediaSources[0].Type === 'Placeholder')) {
|
||||
return false;
|
||||
}
|
||||
if (item.EnableMediaSourceDisplay === false) {
|
||||
return false;
|
||||
}
|
||||
if (item.EnableMediaSourceDisplay == null && item.SourceType && item.SourceType !== 'Library') {
|
||||
return false;
|
||||
|
||||
if (item.EnableMediaSourceDisplay != null) {
|
||||
return !!item.EnableMediaSourceDisplay;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !item.SourceType || item.SourceType === 'Library';
|
||||
}
|
||||
|
||||
export function sortTracks (trackA, trackB) {
|
||||
|
@ -4,13 +4,12 @@ import { Events } from 'jellyfin-apiclient';
|
||||
|
||||
function onUserDataChanged() {
|
||||
const instance = this;
|
||||
|
||||
const eventsToMonitor = getEventsToMonitor(instance);
|
||||
|
||||
// TODO: Check user data change reason?
|
||||
if (eventsToMonitor.indexOf('markfavorite') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
} else if (eventsToMonitor.indexOf('markplayed') !== -1) {
|
||||
if (eventsToMonitor.indexOf('markfavorite') !== -1
|
||||
|| eventsToMonitor.indexOf('markplayed') !== -1
|
||||
) {
|
||||
instance.notifyRefreshNeeded();
|
||||
}
|
||||
}
|
||||
@ -25,37 +24,18 @@ function getEventsToMonitor(instance) {
|
||||
return [];
|
||||
}
|
||||
|
||||
function onTimerCreated() {
|
||||
function notifyTimerRefresh() {
|
||||
const instance = this;
|
||||
|
||||
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function onSeriesTimerCreated() {
|
||||
function notifySeriesTimerRefresh() {
|
||||
const instance = this;
|
||||
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function onTimerCancelled() {
|
||||
const instance = this;
|
||||
|
||||
if (getEventsToMonitor(instance).indexOf('timers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function onSeriesTimerCancelled() {
|
||||
const instance = this;
|
||||
if (getEventsToMonitor(instance).indexOf('seriestimers') !== -1) {
|
||||
instance.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,16 +74,14 @@ function onPlaybackStopped(e, stopInfo) {
|
||||
const state = stopInfo.state;
|
||||
|
||||
const eventsToMonitor = getEventsToMonitor(instance);
|
||||
if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Video') {
|
||||
if (state.NowPlayingItem?.MediaType === 'Video') {
|
||||
if (eventsToMonitor.indexOf('videoplayback') !== -1) {
|
||||
instance.notifyRefreshNeeded(true);
|
||||
return;
|
||||
}
|
||||
} else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') {
|
||||
if (eventsToMonitor.indexOf('audioplayback') !== -1) {
|
||||
instance.notifyRefreshNeeded(true);
|
||||
return;
|
||||
}
|
||||
} else if (state.NowPlayingItem?.MediaType === 'Audio' && eventsToMonitor.indexOf('audioplayback') !== -1) {
|
||||
instance.notifyRefreshNeeded(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,10 +106,10 @@ class ItemsRefresher {
|
||||
this.options = options || {};
|
||||
|
||||
addNotificationEvent(this, 'UserDataChanged', onUserDataChanged);
|
||||
addNotificationEvent(this, 'TimerCreated', onTimerCreated);
|
||||
addNotificationEvent(this, 'SeriesTimerCreated', onSeriesTimerCreated);
|
||||
addNotificationEvent(this, 'TimerCancelled', onTimerCancelled);
|
||||
addNotificationEvent(this, 'SeriesTimerCancelled', onSeriesTimerCancelled);
|
||||
addNotificationEvent(this, 'TimerCreated', notifyTimerRefresh);
|
||||
addNotificationEvent(this, 'SeriesTimerCreated', notifySeriesTimerRefresh);
|
||||
addNotificationEvent(this, 'TimerCancelled', notifyTimerRefresh);
|
||||
addNotificationEvent(this, 'SeriesTimerCancelled', notifySeriesTimerRefresh);
|
||||
addNotificationEvent(this, 'LibraryChanged', onLibraryChanged);
|
||||
addNotificationEvent(this, 'playbackstop', onPlaybackStopped, playbackManager);
|
||||
}
|
||||
|
@ -383,7 +383,6 @@ import template from './libraryoptionseditor.template.html';
|
||||
return setContentType(parent, contentType).then(function() {
|
||||
libraryOptions && setLibraryOptions(parent, libraryOptions);
|
||||
bindEvents(parent);
|
||||
return;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -328,10 +328,8 @@ import ServerConnections from '../ServerConnections';
|
||||
textlines.push(datetime.getDisplayTime(datetime.parseISO8601Date(item.StartDate)));
|
||||
}
|
||||
|
||||
if (options.showChannel) {
|
||||
if (item.ChannelName) {
|
||||
textlines.push(item.ChannelName);
|
||||
}
|
||||
if (options.showChannel && item.ChannelName) {
|
||||
textlines.push(item.ChannelName);
|
||||
}
|
||||
|
||||
let parentTitle = null;
|
||||
@ -370,10 +368,8 @@ import ServerConnections from '../ServerConnections';
|
||||
}
|
||||
|
||||
if (item.IsFolder) {
|
||||
if (options.artist !== false) {
|
||||
if (item.AlbumArtist && item.Type === 'MusicAlbum') {
|
||||
if (options.artist !== false && item.AlbumArtist && item.Type === 'MusicAlbum') {
|
||||
textlines.push(item.AlbumArtist);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (options.artist) {
|
||||
@ -386,10 +382,8 @@ import ServerConnections from '../ServerConnections';
|
||||
}
|
||||
}
|
||||
|
||||
if (item.Type === 'TvChannel') {
|
||||
if (item.CurrentProgram) {
|
||||
textlines.push(itemHelper.getDisplayName(item.CurrentProgram));
|
||||
}
|
||||
if (item.Type === 'TvChannel' && item.CurrentProgram) {
|
||||
textlines.push(itemHelper.getDisplayName(item.CurrentProgram));
|
||||
}
|
||||
|
||||
cssClass = 'listItemBody';
|
||||
@ -405,19 +399,17 @@ import ServerConnections from '../ServerConnections';
|
||||
|
||||
html += getTextLinesHtml(textlines, isLargeStyle);
|
||||
|
||||
if (options.mediaInfo !== false) {
|
||||
if (!enableSideMediaInfo) {
|
||||
const mediaInfoClass = 'secondary listItemMediaInfo listItemBodyText';
|
||||
if (options.mediaInfo !== false && !enableSideMediaInfo) {
|
||||
const mediaInfoClass = 'secondary listItemMediaInfo listItemBodyText';
|
||||
|
||||
html += `<div class="${mediaInfoClass}">`;
|
||||
html += mediaInfo.getPrimaryMediaInfoHtml(item, {
|
||||
episodeTitle: false,
|
||||
originalAirDate: false,
|
||||
subtitles: false
|
||||
html += `<div class="${mediaInfoClass}">`;
|
||||
html += mediaInfo.getPrimaryMediaInfoHtml(item, {
|
||||
episodeTitle: false,
|
||||
originalAirDate: false,
|
||||
subtitles: false
|
||||
|
||||
});
|
||||
html += '</div>';
|
||||
}
|
||||
});
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
if (enableOverview && item.Overview) {
|
||||
@ -428,20 +420,18 @@ import ServerConnections from '../ServerConnections';
|
||||
|
||||
html += '</div>';
|
||||
|
||||
if (options.mediaInfo !== false) {
|
||||
if (enableSideMediaInfo) {
|
||||
html += '<div class="secondary listItemMediaInfo">';
|
||||
html += mediaInfo.getPrimaryMediaInfoHtml(item, {
|
||||
if (options.mediaInfo !== false && enableSideMediaInfo) {
|
||||
html += '<div class="secondary listItemMediaInfo">';
|
||||
html += mediaInfo.getPrimaryMediaInfoHtml(item, {
|
||||
|
||||
year: false,
|
||||
container: false,
|
||||
episodeTitle: false,
|
||||
criticRating: false,
|
||||
endsAt: false
|
||||
year: false,
|
||||
container: false,
|
||||
episodeTitle: false,
|
||||
criticRating: false,
|
||||
endsAt: false
|
||||
|
||||
});
|
||||
html += '</div>';
|
||||
}
|
||||
});
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
if (!options.recordButton && (item.Type === 'Timer' || item.Type === 'Program')) {
|
||||
|
@ -129,17 +129,18 @@ import '../../elements/emby-button/emby-button';
|
||||
}
|
||||
}
|
||||
|
||||
if ((item.Type === 'Episode' || item.MediaType === 'Photo') && options.originalAirDate !== false) {
|
||||
if (item.PremiereDate) {
|
||||
try {
|
||||
//don't modify date to locale if episode. Only Dates (not times) are stored, or editable in the edit metadata dialog
|
||||
date = datetime.parseISO8601Date(item.PremiereDate, item.Type !== 'Episode');
|
||||
if ((item.Type === 'Episode' || item.MediaType === 'Photo')
|
||||
&& options.originalAirDate !== false
|
||||
&& item.PremiereDate
|
||||
) {
|
||||
try {
|
||||
//don't modify date to locale if episode. Only Dates (not times) are stored, or editable in the edit metadata dialog
|
||||
date = datetime.parseISO8601Date(item.PremiereDate, item.Type !== 'Episode');
|
||||
|
||||
text = datetime.toLocaleDateString(date);
|
||||
miscInfo.push(text);
|
||||
} catch (e) {
|
||||
console.error('error parsing date:', item.PremiereDate);
|
||||
}
|
||||
text = datetime.toLocaleDateString(date);
|
||||
miscInfo.push(text);
|
||||
} catch (e) {
|
||||
console.error('error parsing date:', item.PremiereDate);
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,17 +240,17 @@ import '../../elements/emby-button/emby-button';
|
||||
}
|
||||
}
|
||||
|
||||
if (options.year !== false) {
|
||||
if (item.Type !== 'Series' && item.Type !== 'Episode' && item.Type !== 'Person' && item.MediaType !== 'Photo' && item.Type !== 'Program' && item.Type !== 'Season') {
|
||||
if (item.ProductionYear) {
|
||||
miscInfo.push(item.ProductionYear);
|
||||
} else if (item.PremiereDate) {
|
||||
try {
|
||||
text = datetime.toLocaleString(datetime.parseISO8601Date(item.PremiereDate).getFullYear(), {useGrouping: false});
|
||||
miscInfo.push(text);
|
||||
} catch (e) {
|
||||
console.error('error parsing date:', item.PremiereDate);
|
||||
}
|
||||
if (options.year !== false && item.Type !== 'Series' && item.Type !== 'Episode' && item.Type !== 'Person'
|
||||
&& item.MediaType !== 'Photo' && item.Type !== 'Program' && item.Type !== 'Season'
|
||||
) {
|
||||
if (item.ProductionYear) {
|
||||
miscInfo.push(item.ProductionYear);
|
||||
} else if (item.PremiereDate) {
|
||||
try {
|
||||
text = datetime.toLocaleString(datetime.parseISO8601Date(item.PremiereDate).getFullYear(), {useGrouping: false});
|
||||
miscInfo.push(text);
|
||||
} catch (e) {
|
||||
console.error('error parsing date:', item.PremiereDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -314,14 +315,12 @@ import '../../elements/emby-button/emby-button';
|
||||
}
|
||||
|
||||
export function getEndsAt(item) {
|
||||
if (item.MediaType === 'Video' && item.RunTimeTicks) {
|
||||
if (!item.StartDate) {
|
||||
let endDate = new Date().getTime() + (item.RunTimeTicks / 10000);
|
||||
endDate = new Date(endDate);
|
||||
if (item.MediaType === 'Video' && item.RunTimeTicks && !item.StartDate) {
|
||||
let endDate = new Date().getTime() + (item.RunTimeTicks / 10000);
|
||||
endDate = new Date(endDate);
|
||||
|
||||
const displayTime = datetime.getDisplayTime(endDate);
|
||||
return globalize.translate('EndsAtValue', displayTime);
|
||||
}
|
||||
const displayTime = datetime.getDisplayTime(endDate);
|
||||
return globalize.translate('EndsAtValue', displayTime);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -444,12 +444,10 @@ import { appRouter } from '../appRouter';
|
||||
options = options || {};
|
||||
options.type = options.type || 'Primary';
|
||||
|
||||
if (options.type === 'Primary') {
|
||||
if (item.SeriesPrimaryImageTag) {
|
||||
options.tag = item.SeriesPrimaryImageTag;
|
||||
if (options.type === 'Primary' && item.SeriesPrimaryImageTag) {
|
||||
options.tag = item.SeriesPrimaryImageTag;
|
||||
|
||||
return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options);
|
||||
}
|
||||
return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options);
|
||||
}
|
||||
|
||||
if (options.type === 'Thumb') {
|
||||
|
@ -19,11 +19,7 @@ function enableLocalPlaylistManagement(player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.isLocalPlayer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return player.isLocalPlayer;
|
||||
}
|
||||
|
||||
function bindToFullscreenChange(player) {
|
||||
@ -44,10 +40,8 @@ function triggerPlayerChange(playbackManagerInstance, newPlayer, newTarget, prev
|
||||
return;
|
||||
}
|
||||
|
||||
if (newTarget && previousTargetInfo) {
|
||||
if (newTarget.id === previousTargetInfo.id) {
|
||||
return;
|
||||
}
|
||||
if (newTarget && previousTargetInfo && newTarget.id === previousTargetInfo.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
Events.trigger(playbackManagerInstance, 'playerchange', [newPlayer, newTarget, previousPlayer]);
|
||||
@ -227,11 +221,7 @@ function getParam(name, url) {
|
||||
}
|
||||
|
||||
function isAutomaticPlayer(player) {
|
||||
if (player.isLocalPlayer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return player.isLocalPlayer;
|
||||
}
|
||||
|
||||
function getAutomaticPlayers(instance, forceLocalPlayer) {
|
||||
@ -246,10 +236,7 @@ function getAutomaticPlayers(instance, forceLocalPlayer) {
|
||||
}
|
||||
|
||||
function isServerItem(item) {
|
||||
if (!item.Id) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return !!item.Id;
|
||||
}
|
||||
|
||||
function enableIntros(item) {
|
||||
@ -501,10 +488,10 @@ function getPlaybackInfo(player,
|
||||
}
|
||||
|
||||
// lastly, enforce player overrides for special situations
|
||||
if (query.EnableDirectStream !== false) {
|
||||
if (player.supportsPlayMethod && !player.supportsPlayMethod('DirectStream', item)) {
|
||||
query.EnableDirectStream = false;
|
||||
}
|
||||
if (query.EnableDirectStream !== false
|
||||
&& player.supportsPlayMethod && !player.supportsPlayMethod('DirectStream', item)
|
||||
) {
|
||||
query.EnableDirectStream = false;
|
||||
}
|
||||
|
||||
if (player.getDirectPlayProtocols) {
|
||||
@ -569,10 +556,10 @@ function getLiveStream(player, apiClient, item, playSessionId, deviceProfile, ma
|
||||
}
|
||||
|
||||
// lastly, enforce player overrides for special situations
|
||||
if (query.EnableDirectStream !== false) {
|
||||
if (player.supportsPlayMethod && !player.supportsPlayMethod('DirectStream', item)) {
|
||||
query.EnableDirectStream = false;
|
||||
}
|
||||
if (query.EnableDirectStream !== false
|
||||
&& player.supportsPlayMethod && !player.supportsPlayMethod('DirectStream', item)
|
||||
) {
|
||||
query.EnableDirectStream = false;
|
||||
}
|
||||
|
||||
return apiClient.ajax({
|
||||
@ -963,10 +950,8 @@ class PlaybackManager {
|
||||
self.isPlaying = function (player) {
|
||||
player = player || self._currentPlayer;
|
||||
|
||||
if (player) {
|
||||
if (player.isPlaying) {
|
||||
return player.isPlaying();
|
||||
}
|
||||
if (player?.isPlaying) {
|
||||
return player.isPlaying();
|
||||
}
|
||||
|
||||
return player != null && player.currentSrc() != null;
|
||||
@ -975,10 +960,8 @@ class PlaybackManager {
|
||||
self.isPlayingMediaType = function (mediaType, player) {
|
||||
player = player || self._currentPlayer;
|
||||
|
||||
if (player) {
|
||||
if (player.isPlaying) {
|
||||
return player.isPlaying(mediaType);
|
||||
}
|
||||
if (player?.isPlaying) {
|
||||
return player.isPlaying(mediaType);
|
||||
}
|
||||
|
||||
if (self.isPlaying(player)) {
|
||||
@ -1027,10 +1010,8 @@ class PlaybackManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.LocationType === 'Virtual') {
|
||||
if (itemType !== 'Program') {
|
||||
return false;
|
||||
}
|
||||
if (item.LocationType === 'Virtual' && itemType !== 'Program') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (itemType === 'Program') {
|
||||
@ -2486,8 +2467,8 @@ class PlaybackManager {
|
||||
playMethod = 'DirectPlay';
|
||||
} else if (mediaSource.StreamUrl) {
|
||||
// Only used for audio
|
||||
playMethod = 'Transcode';
|
||||
mediaUrl = mediaSource.StreamUrl;
|
||||
// Use the default playMethod value of Transcode
|
||||
} else if (mediaSource.SupportsDirectPlay || mediaSource.SupportsDirectStream) {
|
||||
directOptions = {
|
||||
Static: true,
|
||||
@ -3015,11 +2996,8 @@ class PlaybackManager {
|
||||
|
||||
function enablePlaybackRetryWithTranscoding(streamInfo, errorType, currentlyPreventsVideoStreamCopy, currentlyPreventsAudioStreamCopy) {
|
||||
// mediadecodeerror, medianotsupported, network, servererror
|
||||
if (streamInfo.mediaSource.SupportsTranscoding && (!currentlyPreventsVideoStreamCopy || !currentlyPreventsAudioStreamCopy)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return streamInfo.mediaSource.SupportsTranscoding
|
||||
&& (!currentlyPreventsVideoStreamCopy || !currentlyPreventsAudioStreamCopy);
|
||||
}
|
||||
|
||||
function onPlaybackError(e, error) {
|
||||
@ -3300,10 +3278,10 @@ class PlaybackManager {
|
||||
reportPlayback(self, state, player, reportPlaylist, serverId, 'reportPlaybackProgress', progressEventName);
|
||||
}
|
||||
|
||||
if (streamInfo && streamInfo.liveStreamId) {
|
||||
if (new Date().getTime() - (streamInfo.lastMediaInfoQuery || 0) >= 600000) {
|
||||
getLiveStreamMediaInfo(player, streamInfo, self.currentMediaSource(player), streamInfo.liveStreamId, serverId);
|
||||
}
|
||||
if (streamInfo?.liveStreamId
|
||||
&& (new Date().getTime() - (streamInfo.lastMediaInfoQuery || 0) >= 600000)
|
||||
) {
|
||||
getLiveStreamMediaInfo(player, streamInfo, self.currentMediaSource(player), streamInfo.liveStreamId, serverId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3568,10 +3546,8 @@ class PlaybackManager {
|
||||
}
|
||||
|
||||
getBufferedRanges(player = this._currentPlayer) {
|
||||
if (player) {
|
||||
if (player.getBufferedRanges) {
|
||||
return player.getBufferedRanges();
|
||||
}
|
||||
if (player?.getBufferedRanges) {
|
||||
return player.getBufferedRanges();
|
||||
}
|
||||
|
||||
return [];
|
||||
@ -3842,19 +3818,15 @@ class PlaybackManager {
|
||||
|
||||
removeActivePlayer(name) {
|
||||
const playerInfo = this.getPlayerInfo();
|
||||
if (playerInfo) {
|
||||
if (playerInfo.name === name) {
|
||||
this.setDefaultPlayerActive();
|
||||
}
|
||||
if (playerInfo?.name === name) {
|
||||
this.setDefaultPlayerActive();
|
||||
}
|
||||
}
|
||||
|
||||
removeActiveTarget(id) {
|
||||
const playerInfo = this.getPlayerInfo();
|
||||
if (playerInfo) {
|
||||
if (playerInfo.id === id) {
|
||||
this.setDefaultPlayerActive();
|
||||
}
|
||||
if (playerInfo?.id === id) {
|
||||
this.setDefaultPlayerActive();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,10 +29,8 @@ function mirrorIfEnabled(info) {
|
||||
if (info && playbackManager.enableDisplayMirroring()) {
|
||||
const getPlayerInfo = playbackManager.getPlayerInfo();
|
||||
|
||||
if (getPlayerInfo) {
|
||||
if (!getPlayerInfo.isLocalPlayer && getPlayerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
|
||||
mirrorItem(info, playbackManager.getCurrentPlayer());
|
||||
}
|
||||
if (getPlayerInfo && !getPlayerInfo.isLocalPlayer && getPlayerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
|
||||
mirrorItem(info, playbackManager.getCurrentPlayer());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,11 +83,9 @@ function getIcon(target) {
|
||||
export function show(button) {
|
||||
const currentPlayerInfo = playbackManager.getPlayerInfo();
|
||||
|
||||
if (currentPlayerInfo) {
|
||||
if (!currentPlayerInfo.isLocalPlayer) {
|
||||
showActivePlayerMenu(currentPlayerInfo);
|
||||
return;
|
||||
}
|
||||
if (currentPlayerInfo && !currentPlayerInfo.isLocalPlayer) {
|
||||
showActivePlayerMenu(currentPlayerInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPlayerId = currentPlayerInfo ? currentPlayerInfo.id : null;
|
||||
@ -299,7 +295,6 @@ document.addEventListener('viewshow', function (e) {
|
||||
mirrorIfEnabled({
|
||||
item: item
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -238,7 +238,6 @@ function showWithUser(options, player, user) {
|
||||
|
||||
return actionsheet.show({
|
||||
items: menuItems,
|
||||
resolveOnClick: true,
|
||||
positionTo: options.positionTo
|
||||
}).then(function (id) {
|
||||
return handleSelectedOption(id, options, player);
|
||||
|
@ -4,7 +4,7 @@ import globalize from '../../scripts/globalize';
|
||||
import layoutManager from '../layoutManager';
|
||||
import { playbackManager } from '../playback/playbackmanager';
|
||||
import playMethodHelper from '../playback/playmethodhelper';
|
||||
import SyncPlay from '../../components/syncPlay/core';
|
||||
import SyncPlay from '../../plugins/syncPlay/core';
|
||||
import './playerstats.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
||||
|
@ -4,7 +4,7 @@ import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import loading from '../loading/loading';
|
||||
import layoutManager from '../layoutManager';
|
||||
import { playbackManager } from '../playback/playbackmanager';
|
||||
import SyncPlay from '../../components/syncPlay/core';
|
||||
import SyncPlay from '../../plugins/syncPlay/core';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import { appRouter } from '../appRouter';
|
||||
import globalize from '../../scripts/globalize';
|
||||
|
@ -106,10 +106,8 @@ function getIndicatorIcon(item) {
|
||||
return 'fiber_manual_record';
|
||||
}
|
||||
|
||||
if (item.SeriesTimerId) {
|
||||
if (status !== 'Cancelled') {
|
||||
return 'fiber_smart_record';
|
||||
}
|
||||
if (item.SeriesTimerId && status !== 'Cancelled') {
|
||||
return 'fiber_smart_record';
|
||||
}
|
||||
|
||||
return 'fiber_manual_record';
|
||||
|
@ -129,7 +129,6 @@ function executeCloseAction(action, programId, serverId) {
|
||||
serverId: serverId
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,40 +61,20 @@ function fetchData(instance) {
|
||||
|
||||
function onTimerChangedExternally(e, apiClient, data) {
|
||||
const options = this.options;
|
||||
let refresh = false;
|
||||
|
||||
if (data.Id) {
|
||||
if (this.TimerId === data.Id) {
|
||||
refresh = true;
|
||||
}
|
||||
}
|
||||
if (data.ProgramId && options) {
|
||||
if (options.programId === data.ProgramId) {
|
||||
refresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (refresh) {
|
||||
if ((data.Id && this.TimerId === data.Id)
|
||||
|| (data.ProgramId && options && options.programId === data.ProgramId)
|
||||
) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
function onSeriesTimerChangedExternally(e, apiClient, data) {
|
||||
const options = this.options;
|
||||
let refresh = false;
|
||||
|
||||
if (data.Id) {
|
||||
if (this.SeriesTimerId === data.Id) {
|
||||
refresh = true;
|
||||
}
|
||||
}
|
||||
if (data.ProgramId && options) {
|
||||
if (options.programId === data.ProgramId) {
|
||||
refresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (refresh) {
|
||||
if ((data.Id && this.SeriesTimerId === data.Id)
|
||||
|| (data.ProgramId && options && options.programId === data.ProgramId)
|
||||
) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
@ -479,11 +479,7 @@ import layoutManager from './layoutManager';
|
||||
* Returns true if smooth scroll must be used.
|
||||
*/
|
||||
function useSmoothScroll() {
|
||||
if (browser.tizen) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return !!browser.tizen;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,11 +46,9 @@ function getImageUrl(item, options, apiClient) {
|
||||
return apiClient.getScaledImageUrl(item.Id, options);
|
||||
}
|
||||
|
||||
if (options.type === 'Primary') {
|
||||
if (item.AlbumId && item.AlbumPrimaryImageTag) {
|
||||
options.tag = item.AlbumPrimaryImageTag;
|
||||
return apiClient.getScaledImageUrl(item.AlbumId, options);
|
||||
}
|
||||
if (options.type === 'Primary' && item.AlbumId && item.AlbumPrimaryImageTag) {
|
||||
options.tag = item.AlbumPrimaryImageTag;
|
||||
return apiClient.getScaledImageUrl(item.AlbumId, options);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -114,10 +114,8 @@ function fillSubtitleList(context, item) {
|
||||
itemHtml += '</a>';
|
||||
itemHtml += '</div>';
|
||||
|
||||
if (!layoutManager.tv) {
|
||||
if (s.Path) {
|
||||
itemHtml += '<button is="paper-icon-button-light" data-index="' + s.Index + '" title="' + globalize.translate('Delete') + '" class="btnDelete listItemButton"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
}
|
||||
if (!layoutManager.tv && s.Path) {
|
||||
itemHtml += '<button is="paper-icon-button-light" data-index="' + s.Index + '" title="' + globalize.translate('Delete') + '" class="btnDelete listItemButton"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
}
|
||||
|
||||
itemHtml += '</' + tagName + '>';
|
||||
@ -336,12 +334,8 @@ function showDownloadOptions(button, context, subtitleId) {
|
||||
positionTo: button
|
||||
|
||||
}).then(function (id) {
|
||||
switch (id) {
|
||||
case 'download':
|
||||
downloadRemoteSubtitles(context, subtitleId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (id === 'download') {
|
||||
downloadRemoteSubtitles(context, subtitleId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -144,33 +144,32 @@ class SubtitleSync {
|
||||
}
|
||||
|
||||
toggle(action) {
|
||||
if (action && !['hide', 'forceToHide'].includes(action)) {
|
||||
console.warn('SubtitleSync.toggle called with invalid action', action);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player && playbackManager.supportSubtitleOffset(player)) {
|
||||
/* eslint-disable no-fallthrough */
|
||||
switch (action) {
|
||||
case undefined:
|
||||
// if showing subtitle sync is enabled and if there is an external subtitle stream enabled
|
||||
if (playbackManager.isShowingSubtitleOffsetEnabled(player) && playbackManager.canHandleOffsetOnCurrentSubtitle(player)) {
|
||||
// if no subtitle offset is defined or element has focus (offset being defined)
|
||||
if (!(playbackManager.getPlayerSubtitleOffset(player) || subtitleSyncTextField.hasFocus)) {
|
||||
// set default offset to '0' = 50%
|
||||
subtitleSyncSlider.value = '50';
|
||||
subtitleSyncTextField.textContent = '0s';
|
||||
playbackManager.setSubtitleOffset(0, player);
|
||||
}
|
||||
// show subtitle sync
|
||||
subtitleSyncContainer.classList.remove('hide');
|
||||
break; // stop here
|
||||
} // else continue and hide
|
||||
case 'hide':
|
||||
// only break if element has focus
|
||||
if (subtitleSyncTextField.hasFocus) {
|
||||
break;
|
||||
if (!action) {
|
||||
// if showing subtitle sync is enabled and if there is an external subtitle stream enabled
|
||||
if (playbackManager.isShowingSubtitleOffsetEnabled(player) && playbackManager.canHandleOffsetOnCurrentSubtitle(player)) {
|
||||
// if no subtitle offset is defined or element has focus (offset being defined)
|
||||
if (!(playbackManager.getPlayerSubtitleOffset(player) || subtitleSyncTextField.hasFocus)) {
|
||||
// set default offset to '0' = 50%
|
||||
subtitleSyncSlider.value = '50';
|
||||
subtitleSyncTextField.textContent = '0s';
|
||||
playbackManager.setSubtitleOffset(0, player);
|
||||
}
|
||||
case 'forceToHide':
|
||||
subtitleSyncContainer.classList.add('hide');
|
||||
break;
|
||||
// show subtitle sync
|
||||
subtitleSyncContainer.classList.remove('hide');
|
||||
return;
|
||||
}
|
||||
} else if (action === 'hide' && subtitleSyncTextField.hasFocus) {
|
||||
// do not hide if element has focus
|
||||
return;
|
||||
}
|
||||
/* eslint-enable no-fallthrough */
|
||||
|
||||
subtitleSyncContainer.classList.add('hide');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,8 @@ function getEditorHtml() {
|
||||
html += '<div is="emby-itemscontainer" class="results vertical-wrap">';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function getDeviceHtml(device) {
|
||||
@ -61,11 +62,12 @@ function getDeviceHtml(device) {
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html += '</button>';
|
||||
html += '</button>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function getTunerName(providerId) {
|
||||
switch (providerId = providerId.toLowerCase()) {
|
||||
switch (providerId.toLowerCase()) {
|
||||
case 'm3u':
|
||||
return 'M3U';
|
||||
|
||||
|
@ -203,7 +203,7 @@ export default function (page, providerId, options) {
|
||||
}
|
||||
|
||||
function getTunerName(providerId) {
|
||||
switch (providerId = providerId.toLowerCase()) {
|
||||
switch (providerId.toLowerCase()) {
|
||||
case 'm3u':
|
||||
return 'M3U Playlist';
|
||||
case 'hdhomerun':
|
||||
|
@ -106,7 +106,7 @@ export default function (page, providerId, options) {
|
||||
}
|
||||
|
||||
function getTunerName(providerId) {
|
||||
switch (providerId = providerId.toLowerCase()) {
|
||||
switch (providerId.toLowerCase()) {
|
||||
case 'm3u':
|
||||
return 'M3U Playlist';
|
||||
case 'hdhomerun':
|
||||
|
@ -103,9 +103,9 @@ function getViewEventDetail(view, {state, url, options = {}}, isRestored) {
|
||||
const searchParams = new URLSearchParams(url.substring(index + 1));
|
||||
const params = {};
|
||||
|
||||
searchParams.forEach((value, key) =>
|
||||
params[key] = value
|
||||
);
|
||||
searchParams.forEach((value, key) => {
|
||||
params[key] = value;
|
||||
});
|
||||
|
||||
return {
|
||||
detail: {
|
||||
|
@ -44,6 +44,7 @@
|
||||
"pdfPlayer/plugin",
|
||||
"logoScreensaver/plugin",
|
||||
"sessionPlayer/plugin",
|
||||
"chromecastPlayer/plugin"
|
||||
"chromecastPlayer/plugin",
|
||||
"syncPlay/plugin"
|
||||
]
|
||||
}
|
||||
|
@ -37,7 +37,8 @@ import { pageIdOn } from '../../utils/dashboard';
|
||||
const date = datetime.parseISO8601Date(item.DateCreated, true);
|
||||
html += datetime.toLocaleDateString(date) + ' ' + datetime.getDisplayTime(date);
|
||||
html += '</td>';
|
||||
return html += '</tr>';
|
||||
html += '</tr>';
|
||||
return html;
|
||||
}).join('');
|
||||
page.querySelector('.resultBody').innerHTML = rows;
|
||||
loading.hide();
|
||||
|
@ -6,7 +6,7 @@ import serverNotifications from '../../scripts/serverNotifications';
|
||||
import dom from '../../scripts/dom';
|
||||
import globalize from '../../scripts/globalize';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { localeWithSuffix } from '../../scripts/dfnshelper';
|
||||
import { getLocaleWithSuffix } from '../../scripts/dfnshelper';
|
||||
import loading from '../../components/loading/loading';
|
||||
import playMethodHelper from '../../components/playback/playmethodhelper';
|
||||
import cardBuilder from '../../components/cardbuilder/cardBuilder';
|
||||
@ -458,7 +458,7 @@ import confirm from '../../components/confirm/confirm';
|
||||
|
||||
html += ' / ';
|
||||
|
||||
if (nowPlayingItem && nowPlayingItem.RunTimeTicks) {
|
||||
if (nowPlayingItem.RunTimeTicks) {
|
||||
html += datetime.getDisplayRunningTime(nowPlayingItem.RunTimeTicks);
|
||||
} else {
|
||||
html += '0:00';
|
||||
@ -477,7 +477,7 @@ import confirm from '../../components/confirm/confirm';
|
||||
// how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences
|
||||
if (!nowPlayingItem) {
|
||||
return {
|
||||
html: globalize.translate('LastSeen', formatDistanceToNow(Date.parse(session.LastActivityDate), localeWithSuffix)),
|
||||
html: globalize.translate('LastSeen', formatDistanceToNow(Date.parse(session.LastActivityDate), getLocaleWithSuffix())),
|
||||
image: imgUrl
|
||||
};
|
||||
}
|
||||
@ -747,14 +747,7 @@ import confirm from '../../components/confirm/confirm';
|
||||
console.debug('onServerRestarting not implemented', evt, apiClient);
|
||||
}
|
||||
|
||||
function onPackageInstalling(evt, apiClient) {
|
||||
if (apiClient.serverId() === serverId) {
|
||||
pollForInfo(view, apiClient);
|
||||
reloadSystemInfo(view, apiClient);
|
||||
}
|
||||
}
|
||||
|
||||
function onPackageInstallationCompleted(evt, apiClient) {
|
||||
function onPackageInstall(_, apiClient) {
|
||||
if (apiClient.serverId() === serverId) {
|
||||
pollForInfo(view, apiClient);
|
||||
reloadSystemInfo(view, apiClient);
|
||||
@ -786,8 +779,8 @@ import confirm from '../../components/confirm/confirm';
|
||||
Events.on(serverNotifications, 'RestartRequired', onRestartRequired);
|
||||
Events.on(serverNotifications, 'ServerShuttingDown', onServerShuttingDown);
|
||||
Events.on(serverNotifications, 'ServerRestarting', onServerRestarting);
|
||||
Events.on(serverNotifications, 'PackageInstalling', onPackageInstalling);
|
||||
Events.on(serverNotifications, 'PackageInstallationCompleted', onPackageInstallationCompleted);
|
||||
Events.on(serverNotifications, 'PackageInstalling', onPackageInstall);
|
||||
Events.on(serverNotifications, 'PackageInstallationCompleted', onPackageInstall);
|
||||
Events.on(serverNotifications, 'Sessions', onSessionsUpdate);
|
||||
Events.on(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate);
|
||||
DashboardPage.lastAppUpdateCheck = null;
|
||||
@ -800,13 +793,11 @@ import confirm from '../../components/confirm/confirm';
|
||||
});
|
||||
}
|
||||
|
||||
if (ApiClient.isMinServerVersion('3.4.1.25')) {
|
||||
if (!page.serverActivityLog) {
|
||||
page.serverActivityLog = new ActivityLog({
|
||||
serverId: ApiClient.serverId(),
|
||||
element: page.querySelector('.serverActivityItems')
|
||||
});
|
||||
}
|
||||
if (!page.serverActivityLog) {
|
||||
page.serverActivityLog = new ActivityLog({
|
||||
serverId: ApiClient.serverId(),
|
||||
element: page.querySelector('.serverActivityItems')
|
||||
});
|
||||
}
|
||||
|
||||
refreshActiveRecordings(view, apiClient);
|
||||
@ -826,8 +817,8 @@ import confirm from '../../components/confirm/confirm';
|
||||
Events.off(serverNotifications, 'RestartRequired', onRestartRequired);
|
||||
Events.off(serverNotifications, 'ServerShuttingDown', onServerShuttingDown);
|
||||
Events.off(serverNotifications, 'ServerRestarting', onServerRestarting);
|
||||
Events.off(serverNotifications, 'PackageInstalling', onPackageInstalling);
|
||||
Events.off(serverNotifications, 'PackageInstallationCompleted', onPackageInstallationCompleted);
|
||||
Events.off(serverNotifications, 'PackageInstalling', onPackageInstall);
|
||||
Events.off(serverNotifications, 'PackageInstallationCompleted', onPackageInstall);
|
||||
Events.off(serverNotifications, 'Sessions', onSessionsUpdate);
|
||||
Events.off(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate);
|
||||
|
||||
|
@ -5,7 +5,7 @@ import dom from '../../../scripts/dom';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import imageHelper from '../../../scripts/imagehelper';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { localeWithSuffix } from '../../../scripts/dfnshelper';
|
||||
import { getLocaleWithSuffix } from '../../../scripts/dfnshelper';
|
||||
import '../../../elements/emby-button/emby-button';
|
||||
import '../../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../../components/cardbuilder/card.scss';
|
||||
@ -91,6 +91,8 @@ import confirm from '../../../components/confirm/confirm';
|
||||
}
|
||||
|
||||
function load(page, devices) {
|
||||
const localeWithSuffix = getLocaleWithSuffix();
|
||||
|
||||
let html = '';
|
||||
html += devices.map(function (device) {
|
||||
let deviceHtml = '';
|
||||
|
@ -149,7 +149,8 @@ import { getParameterByName } from '../../../utils/url.ts';
|
||||
li += '<h3 class="listItemBodyText">' + escapeHtml(h.Name + ' = ' + (h.Value || '')) + '</h3>';
|
||||
li += '</div>';
|
||||
li += '<button type="button" is="paper-icon-button-light" class="btnDeleteXmlAttribute listItemButton" data-index="0"><span class="material-icons delete" aria-hidden="true"></span></button>';
|
||||
return li += '</div>';
|
||||
li += '</div>';
|
||||
return li;
|
||||
}).join('') + '</div>';
|
||||
const elem = $('.xmlDocumentAttributeList', page).html(html).trigger('create');
|
||||
$('.btnDeleteXmlAttribute', elem).on('click', function () {
|
||||
|
@ -129,7 +129,8 @@ function getPluginHtml(plugin, options, installedPlugins) {
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function getTabs() {
|
||||
|
@ -4,7 +4,7 @@ import { Events } from 'jellyfin-apiclient';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import serverNotifications from '../../../scripts/serverNotifications';
|
||||
import { formatDistance, formatDistanceToNow } from 'date-fns';
|
||||
import { getLocale, localeWithSuffix } from '../../../scripts/dfnshelper';
|
||||
import { getLocale, getLocaleWithSuffix } from '../../../scripts/dfnshelper';
|
||||
import '../../../components/listview/listview.scss';
|
||||
import '../../../elements/emby-button/emby-button';
|
||||
|
||||
@ -86,7 +86,7 @@ import '../../../elements/emby-button/emby-button';
|
||||
if (task.LastExecutionResult) {
|
||||
const endtime = Date.parse(task.LastExecutionResult.EndTimeUtc);
|
||||
const starttime = Date.parse(task.LastExecutionResult.StartTimeUtc);
|
||||
html += globalize.translate('LabelScheduledTaskLastRan', formatDistanceToNow(endtime, localeWithSuffix),
|
||||
html += globalize.translate('LabelScheduledTaskLastRan', formatDistanceToNow(endtime, getLocaleWithSuffix()),
|
||||
formatDistance(starttime, endtime, { locale: getLocale() }));
|
||||
if (task.LastExecutionResult.Status === 'Failed') {
|
||||
html += " <span style='color:#FF0000;'>(" + globalize.translate('LabelFailed') + ')</span>';
|
||||
|
@ -170,7 +170,6 @@ function renderSeriesTimerEditor(page, item, apiClient, user) {
|
||||
|
||||
page.querySelector('.seriesTimerScheduleSection').classList.add('hide');
|
||||
hideAll(page, 'btnCancelSeriesTimer');
|
||||
return;
|
||||
}
|
||||
|
||||
function renderTrackSelections(page, instance, item, forceReload) {
|
||||
@ -206,8 +205,12 @@ function renderTrackSelections(page, instance, item, forceReload) {
|
||||
});
|
||||
|
||||
mediaSources = [];
|
||||
resolutionNames.forEach(v => mediaSources.push(v));
|
||||
sourceNames.forEach(v => mediaSources.push(v));
|
||||
resolutionNames.forEach(v => {
|
||||
mediaSources.push(v);
|
||||
});
|
||||
sourceNames.forEach(v => {
|
||||
mediaSources.push(v);
|
||||
});
|
||||
|
||||
instance._currentPlaybackMediaSources = mediaSources;
|
||||
|
||||
|
@ -720,8 +720,6 @@ class ItemsView {
|
||||
if (params.type === 'Video') {
|
||||
return globalize.translate('Videos');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function play() {
|
||||
|
@ -66,11 +66,9 @@ function renderRecordingFolders(context, promise) {
|
||||
|
||||
function onMoreClick() {
|
||||
const type = this.getAttribute('data-type');
|
||||
const serverId = ApiClient.serverId();
|
||||
|
||||
switch (type) {
|
||||
case 'latest':
|
||||
Dashboard.navigate('list.html?type=Recordings&serverId=' + serverId);
|
||||
if (type === 'latest') {
|
||||
Dashboard.navigate('list.html?type=Recordings&serverId=' + ApiClient.serverId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,8 @@ function getDeviceHtml(device) {
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
return html += '</div>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function renderDevices(page, devices) {
|
||||
@ -191,7 +192,7 @@ function deleteProvider(page, id) {
|
||||
}
|
||||
|
||||
function getTunerName(providerId) {
|
||||
switch (providerId = providerId.toLowerCase()) {
|
||||
switch (providerId.toLowerCase()) {
|
||||
case 'm3u':
|
||||
return 'M3U';
|
||||
case 'hdhomerun':
|
||||
@ -206,7 +207,7 @@ function getTunerName(providerId) {
|
||||
}
|
||||
|
||||
function getProviderName(providerId) {
|
||||
switch (providerId = providerId.toLowerCase()) {
|
||||
switch (providerId.toLowerCase()) {
|
||||
case 'schedulesdirect':
|
||||
return 'Schedules Direct';
|
||||
case 'xmltv':
|
||||
@ -217,7 +218,7 @@ function getProviderName(providerId) {
|
||||
}
|
||||
|
||||
function getProviderConfigurationUrl(providerId) {
|
||||
switch (providerId = providerId.toLowerCase()) {
|
||||
switch (providerId.toLowerCase()) {
|
||||
case 'xmltv':
|
||||
return '#/livetvguideprovider.html?type=xmltv';
|
||||
case 'schedulesdirect':
|
||||
|
@ -374,10 +374,9 @@ import Dashboard from '../../utils/dashboard';
|
||||
}
|
||||
|
||||
function onInputCommand(e) {
|
||||
switch (e.detail.command) {
|
||||
case 'search':
|
||||
e.preventDefault();
|
||||
Dashboard.navigate('search.html?collectionType=movies&parentId=' + params.topParentId);
|
||||
if (e.detail.command === 'search') {
|
||||
e.preventDefault();
|
||||
Dashboard.navigate('search.html?collectionType=movies&parentId=' + params.topParentId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,10 +348,9 @@ import Dashboard from '../../utils/dashboard';
|
||||
}
|
||||
|
||||
function onInputCommand(e) {
|
||||
switch (e.detail.command) {
|
||||
case 'search':
|
||||
e.preventDefault();
|
||||
Dashboard.navigate('search.html?collectionType=music&parentId=' + params.topParentId);
|
||||
if (e.detail.command === 'search') {
|
||||
e.preventDefault();
|
||||
Dashboard.navigate('search.html?collectionType=music&parentId=' + params.topParentId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
<div class="flex flex-direction-row align-items-center" dir="ltr">
|
||||
<div class="osdTextContainer startTimeText osdPositionText" style="margin: 0 .25em 0 0;"></div>
|
||||
<div class="sliderContainer flex-grow" style="margin: .5em 0 .25em;">
|
||||
<div class="sliderMarkerContainer"></div>
|
||||
<input type="range" step=".01" min="0" max="100" value="0" is="emby-slider" class="osdPositionSlider" data-slider-keep-progress="true" />
|
||||
</div>
|
||||
<div class="osdTextContainer endTimeText osdDurationText" style="margin: 0 0 0 .25em;"></div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import escapeHtml from 'escape-html';
|
||||
import { playbackManager } from '../../../components/playback/playbackmanager';
|
||||
import SyncPlay from '../../../components/syncPlay/core';
|
||||
import SyncPlay from '../../../plugins/syncPlay/core';
|
||||
import browser from '../../../scripts/browser';
|
||||
import dom from '../../../scripts/dom';
|
||||
import inputManager from '../../../scripts/inputManager';
|
||||
@ -1204,17 +1204,6 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components
|
||||
resetIdle();
|
||||
}
|
||||
|
||||
function onWindowTouchStart(e) {
|
||||
clickedElement = e.target;
|
||||
mouseIsDown = true;
|
||||
resetIdle();
|
||||
}
|
||||
|
||||
function onWindowTouchEnd() {
|
||||
mouseIsDown = false;
|
||||
resetIdle();
|
||||
}
|
||||
|
||||
function onWindowDragEnd() {
|
||||
// mousedown -> dragstart -> dragend !!! no mouseup :(
|
||||
mouseIsDown = false;
|
||||
@ -1370,12 +1359,12 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(window, 'touchstart', onWindowTouchStart, {
|
||||
dom.addEventListener(window, 'touchstart', onWindowMouseDown, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
['touchend', 'touchcancel'].forEach((event) => {
|
||||
dom.addEventListener(window, event, onWindowTouchEnd, {
|
||||
dom.addEventListener(window, event, onWindowMouseUp, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
@ -1411,12 +1400,12 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(window, 'touchstart', onWindowTouchStart, {
|
||||
dom.removeEventListener(window, 'touchstart', onWindowMouseDown, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
['touchend', 'touchcancel'].forEach((event) => {
|
||||
dom.removeEventListener(window, event, onWindowTouchEnd, {
|
||||
dom.removeEventListener(window, event, onWindowMouseUp, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
@ -1574,6 +1563,25 @@ import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components
|
||||
return '<h1 class="sliderBubbleText">' + datetime.getDisplayRunningTime(ticks) + '</h1>';
|
||||
};
|
||||
|
||||
nowPlayingPositionSlider.getMarkerInfo = function () {
|
||||
const markers = [];
|
||||
|
||||
const item = currentItem;
|
||||
|
||||
// use markers based on chapters
|
||||
if (item?.Chapters?.length) {
|
||||
item.Chapters.forEach(currentChapter => {
|
||||
markers.push({
|
||||
className: 'chapterMarker',
|
||||
name: currentChapter.Name,
|
||||
progress: currentChapter.StartPositionTicks / item.RunTimeTicks
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return markers;
|
||||
};
|
||||
|
||||
view.querySelector('.btnPreviousTrack').addEventListener('click', function () {
|
||||
playbackManager.previousTrack(currentPlayer);
|
||||
});
|
||||
|
@ -35,7 +35,6 @@ import Dashboard from '../../../utils/dashboard';
|
||||
Dashboard.navigate('forgotpasswordpin.html');
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,10 +333,9 @@ import autoFocuser from '../../components/autoFocuser';
|
||||
}
|
||||
|
||||
function onInputCommand(e) {
|
||||
switch (e.detail.command) {
|
||||
case 'search':
|
||||
e.preventDefault();
|
||||
Dashboard.navigate('search.html?collectionType=tv&parentId=' + params.topParentId);
|
||||
if (e.detail.command === 'search') {
|
||||
e.preventDefault();
|
||||
Dashboard.navigate('search.html?collectionType=tv&parentId=' + params.topParentId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,13 +85,9 @@ import 'webcomponents.js/webcomponents-lite';
|
||||
passive: true
|
||||
});
|
||||
|
||||
if (browser.orsay) {
|
||||
if (this === document.activeElement) {
|
||||
//Make sure the IME pops up if this is the first/default element on the page
|
||||
if (document.attachIME) {
|
||||
document.attachIME(this);
|
||||
}
|
||||
}
|
||||
//Make sure the IME pops up if this is the first/default element on the page
|
||||
if (browser.orsay && this === document.activeElement && document.attachIME) {
|
||||
document.attachIME(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -21,10 +21,8 @@ import Sortable from 'sortablejs';
|
||||
const itemsContainer = this;
|
||||
const multiSelect = itemsContainer.multiSelect;
|
||||
|
||||
if (multiSelect) {
|
||||
if (multiSelect.onContainerClick.call(itemsContainer, e) === false) {
|
||||
return;
|
||||
}
|
||||
if (multiSelect?.onContainerClick.call(itemsContainer, e) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
itemShortcuts.onClick.call(itemsContainer, e);
|
||||
@ -155,9 +153,9 @@ import Sortable from 'sortablejs';
|
||||
const eventsToMonitor = getEventsToMonitor(itemsContainer);
|
||||
|
||||
// TODO: Check user data change reason?
|
||||
if (eventsToMonitor.indexOf('markfavorite') !== -1) {
|
||||
itemsContainer.notifyRefreshNeeded();
|
||||
} else if (eventsToMonitor.indexOf('markplayed') !== -1) {
|
||||
if (eventsToMonitor.indexOf('markfavorite') !== -1
|
||||
|| eventsToMonitor.indexOf('markplayed') !== -1
|
||||
) {
|
||||
itemsContainer.notifyRefreshNeeded();
|
||||
}
|
||||
}
|
||||
@ -192,7 +190,6 @@ import Sortable from 'sortablejs';
|
||||
const itemsContainer = this;
|
||||
if (getEventsToMonitor(itemsContainer).indexOf('seriestimers') !== -1) {
|
||||
itemsContainer.notifyRefreshNeeded();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,11 +256,9 @@ import Sortable from 'sortablejs';
|
||||
itemsContainer.notifyRefreshNeeded(true);
|
||||
return;
|
||||
}
|
||||
} else if (state.NowPlayingItem && state.NowPlayingItem.MediaType === 'Audio') {
|
||||
if (eventsToMonitor.indexOf('audioplayback') !== -1) {
|
||||
itemsContainer.notifyRefreshNeeded(true);
|
||||
return;
|
||||
}
|
||||
} else if (state.NowPlayingItem?.MediaType === 'Audio' && eventsToMonitor.indexOf('audioplayback') !== -1) {
|
||||
itemsContainer.notifyRefreshNeeded(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,10 +293,8 @@ import Sortable from 'sortablejs';
|
||||
}
|
||||
}
|
||||
|
||||
if (layoutManager.desktop || layoutManager.mobile) {
|
||||
if (this.getAttribute('data-multiselect') !== 'false') {
|
||||
this.enableMultiSelect(true);
|
||||
}
|
||||
if (layoutManager.desktop || layoutManager.mobile && this.getAttribute('data-multiselect') !== 'false') {
|
||||
this.enableMultiSelect(true);
|
||||
}
|
||||
|
||||
if (layoutManager.tv) {
|
||||
|
@ -63,18 +63,6 @@ import ServerConnections from '../../components/ServerConnections';
|
||||
}
|
||||
|
||||
button.classList.add('ratingbutton-withrating');
|
||||
} else if (likes) {
|
||||
if (icon) {
|
||||
icon.classList.add('favorite');
|
||||
icon.classList.remove('ratingbutton-icon-withrating');
|
||||
}
|
||||
button.classList.remove('ratingbutton-withrating');
|
||||
} else if (likes === false) {
|
||||
if (icon) {
|
||||
icon.classList.add('favorite');
|
||||
icon.classList.remove('ratingbutton-icon-withrating');
|
||||
}
|
||||
button.classList.remove('ratingbutton-withrating');
|
||||
} else {
|
||||
if (icon) {
|
||||
icon.classList.add('favorite');
|
||||
|
@ -23,11 +23,7 @@ import 'webcomponents.js/webcomponents-lite';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (layoutManager.tv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !layoutManager.tv;
|
||||
}
|
||||
|
||||
function triggerChange(select) {
|
||||
|
@ -105,6 +105,13 @@ import globalize from '../../scripts/globalize';
|
||||
fraction *= 100;
|
||||
backgroundLower.style.width = fraction + '%';
|
||||
}
|
||||
|
||||
if (range.markerContainerElement) {
|
||||
if (!range.triedAddingMarkers) {
|
||||
addMarkers(range);
|
||||
}
|
||||
updateMarkers(range, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -136,6 +143,70 @@ import globalize from '../../scripts/globalize';
|
||||
});
|
||||
}
|
||||
|
||||
function setMarker(range, valueMarker, marker, valueProgress) {
|
||||
requestAnimationFrame(function () {
|
||||
const bubbleTrackRect = range.sliderBubbleTrack.getBoundingClientRect();
|
||||
const markerRect = marker.getBoundingClientRect();
|
||||
|
||||
if (!bubbleTrackRect.width || !markerRect.width) {
|
||||
// width is not set, most probably because the OSD is currently hidden
|
||||
return;
|
||||
}
|
||||
|
||||
let markerPos = (bubbleTrackRect.width * valueMarker / 100) - markerRect.width / 2;
|
||||
markerPos = Math.min(Math.max(markerPos, - markerRect.width / 2), bubbleTrackRect.width - markerRect.width / 2);
|
||||
|
||||
marker.style.left = markerPos + 'px';
|
||||
|
||||
if (valueProgress >= valueMarker) {
|
||||
marker.classList.remove('unwatched');
|
||||
marker.classList.add('watched');
|
||||
} else {
|
||||
marker.classList.add('unwatched');
|
||||
marker.classList.remove('watched');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateMarkers(range, currentValue) {
|
||||
if (range.markerInfo && range.markerInfo.length && range.markerElements && range.markerElements.length) {
|
||||
for (let i = 0, length = range.markerElements.length; i < length; i++) {
|
||||
if (range.markerInfo.length > i) {
|
||||
setMarker(range, mapFractionToValue(range, range.markerInfo[i].progress), range.markerElements[i], currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addMarkers(range) {
|
||||
range.markerInfo = [];
|
||||
if (range.getMarkerInfo) {
|
||||
range.markerInfo = range.getMarkerInfo();
|
||||
}
|
||||
|
||||
function getMarkerHtml(markerInfo) {
|
||||
let markerTypeSpecificClasses = '';
|
||||
|
||||
if (markerInfo.className === 'chapterMarker') {
|
||||
markerTypeSpecificClasses = markerInfo.className;
|
||||
|
||||
if (typeof markerInfo.name === 'string' && markerInfo.name.length) {
|
||||
// limit the class length in case the name contains half a novel
|
||||
markerTypeSpecificClasses = `${markerInfo.className} marker-${markerInfo.name.substring(0, 100).toLowerCase().replace(' ', '-')}`;
|
||||
}
|
||||
}
|
||||
|
||||
return `<span class="material-icons sliderMarker ${markerTypeSpecificClasses}" aria-hidden="true"></span>`;
|
||||
}
|
||||
|
||||
range.markerInfo.forEach(info => {
|
||||
range.markerContainerElement.insertAdjacentHTML('beforeend', getMarkerHtml(info));
|
||||
});
|
||||
|
||||
range.markerElements = range.markerContainerElement.querySelectorAll('.sliderMarker');
|
||||
range.triedAddingMarkers = true;
|
||||
}
|
||||
|
||||
EmbySliderPrototype.attachedCallback = function () {
|
||||
if (this.getAttribute('data-embyslider') === 'true') {
|
||||
return;
|
||||
@ -193,7 +264,9 @@ import globalize from '../../scripts/globalize';
|
||||
this.backgroundUpper = containerElement.querySelector('.mdl-slider-background-upper');
|
||||
const sliderBubble = containerElement.querySelector('.sliderBubble');
|
||||
|
||||
let hasHideClass = sliderBubble.classList.contains('hide');
|
||||
let hasHideBubbleClass = sliderBubble.classList.contains('hide');
|
||||
|
||||
this.markerContainerElement = containerElement.querySelector('.sliderMarkerContainer');
|
||||
|
||||
dom.addEventListener(this, 'input', function () {
|
||||
this.dragging = true;
|
||||
@ -205,9 +278,9 @@ import globalize from '../../scripts/globalize';
|
||||
const bubbleValue = mapValueToFraction(this, this.value) * 100;
|
||||
updateBubble(this, bubbleValue, sliderBubble);
|
||||
|
||||
if (hasHideClass) {
|
||||
if (hasHideBubbleClass) {
|
||||
sliderBubble.classList.remove('hide');
|
||||
hasHideClass = false;
|
||||
hasHideBubbleClass = false;
|
||||
}
|
||||
}, {
|
||||
passive: true
|
||||
@ -221,7 +294,7 @@ import globalize from '../../scripts/globalize';
|
||||
}
|
||||
|
||||
sliderBubble.classList.add('hide');
|
||||
hasHideClass = true;
|
||||
hasHideBubbleClass = true;
|
||||
}, {
|
||||
passive: true
|
||||
});
|
||||
@ -233,9 +306,9 @@ import globalize from '../../scripts/globalize';
|
||||
|
||||
updateBubble(this, bubbleValue, sliderBubble);
|
||||
|
||||
if (hasHideClass) {
|
||||
if (hasHideBubbleClass) {
|
||||
sliderBubble.classList.remove('hide');
|
||||
hasHideClass = false;
|
||||
hasHideBubbleClass = false;
|
||||
}
|
||||
}
|
||||
}, {
|
||||
@ -245,7 +318,7 @@ import globalize from '../../scripts/globalize';
|
||||
/* eslint-disable-next-line compat/compat */
|
||||
dom.addEventListener(this, (window.PointerEvent ? 'pointerleave' : 'mouseleave'), function () {
|
||||
sliderBubble.classList.add('hide');
|
||||
hasHideClass = true;
|
||||
hasHideBubbleClass = true;
|
||||
}, {
|
||||
passive: true
|
||||
});
|
||||
@ -452,10 +525,8 @@ import globalize from '../../scripts/globalize';
|
||||
}
|
||||
|
||||
for (const range of ranges) {
|
||||
if (position != null) {
|
||||
if (position >= range.end) {
|
||||
continue;
|
||||
}
|
||||
if (position != null && position >= range.end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
setRange(elem, range.start, range.end);
|
||||
|
@ -266,3 +266,25 @@
|
||||
display: block;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
||||
.sliderMarkerContainer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 0.54em; /* half of slider thumb size */
|
||||
}
|
||||
|
||||
.sliderMarker {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 0.5em;
|
||||
transform: translate3d(0, 25%, 0);
|
||||
}
|
||||
|
||||
.sliderMarker.unwatched {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.sliderMarker.watched {
|
||||
background-color: #00a4dc;
|
||||
}
|
||||
|
@ -272,10 +272,8 @@ import '../../assets/css/scrollstyles.scss';
|
||||
let sibling = elem[method];
|
||||
|
||||
while (sibling) {
|
||||
if (sibling.classList.contains(buttonClass)) {
|
||||
if (!sibling.classList.contains('hide')) {
|
||||
return sibling;
|
||||
}
|
||||
if (sibling.classList.contains(buttonClass) && !sibling.classList.contains('hide')) {
|
||||
return sibling;
|
||||
}
|
||||
|
||||
sibling = sibling[method];
|
||||
|
@ -35,18 +35,13 @@ import './legacy/domParserTextHtml';
|
||||
import './legacy/focusPreventScroll';
|
||||
import './legacy/htmlMediaElement';
|
||||
import './legacy/vendorStyles';
|
||||
import SyncPlay from './components/syncPlay/core';
|
||||
import { playbackManager } from './components/playback/playbackmanager';
|
||||
import SyncPlayNoActivePlayer from './components/syncPlay/ui/players/NoActivePlayer';
|
||||
import SyncPlayHtmlVideoPlayer from './components/syncPlay/ui/players/HtmlVideoPlayer';
|
||||
import SyncPlayHtmlAudioPlayer from './components/syncPlay/ui/players/HtmlAudioPlayer';
|
||||
import { currentSettings } from './scripts/settings/userSettings';
|
||||
import taskButton from './scripts/taskbutton';
|
||||
import { HistoryRouter } from './components/HistoryRouter.tsx';
|
||||
import AppRoutes from './routes/index.tsx';
|
||||
|
||||
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', '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 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) {
|
||||
return {
|
||||
lang: language,
|
||||
@ -84,10 +79,10 @@ function init() {
|
||||
}
|
||||
|
||||
function onGlobalizeInit() {
|
||||
if (window.appMode === 'android') {
|
||||
if (window.location.href.toString().toLowerCase().indexOf('start=backgroundsync') !== -1) {
|
||||
return onAppReady();
|
||||
}
|
||||
if (window.appMode === 'android'
|
||||
&& window.location.href.toString().toLowerCase().indexOf('start=backgroundsync') !== -1
|
||||
) {
|
||||
return onAppReady();
|
||||
}
|
||||
|
||||
document.title = globalize.translateHtml(document.title, 'core');
|
||||
@ -102,10 +97,7 @@ function onGlobalizeInit() {
|
||||
|
||||
import('./assets/css/librarybrowser.scss');
|
||||
|
||||
loadPlugins().then(function () {
|
||||
initSyncPlay();
|
||||
onAppReady();
|
||||
});
|
||||
loadPlugins().then(onAppReady);
|
||||
}
|
||||
|
||||
function loadPlugins() {
|
||||
@ -137,27 +129,6 @@ function loadPlugins() {
|
||||
});
|
||||
}
|
||||
|
||||
function initSyncPlay() {
|
||||
// Register player wrappers.
|
||||
SyncPlay.PlayerFactory.setDefaultWrapper(SyncPlayNoActivePlayer);
|
||||
SyncPlay.PlayerFactory.registerWrapper(SyncPlayHtmlVideoPlayer);
|
||||
SyncPlay.PlayerFactory.registerWrapper(SyncPlayHtmlAudioPlayer);
|
||||
|
||||
// Listen for player changes.
|
||||
Events.on(playbackManager, 'playerchange', (event, newPlayer, newTarget, oldPlayer) => {
|
||||
SyncPlay.Manager.onPlayerChange(newPlayer, newTarget, oldPlayer);
|
||||
});
|
||||
|
||||
// Start SyncPlay.
|
||||
const apiClient = ServerConnections.currentApiClient();
|
||||
if (apiClient) SyncPlay.Manager.init(apiClient);
|
||||
|
||||
// FIXME: Multiple apiClients?
|
||||
Events.on(ServerConnections, 'apiclientcreated', (e, newApiClient) => SyncPlay.Manager.init(newApiClient));
|
||||
Events.on(ServerConnections, 'localusersignedin', () => SyncPlay.Manager.updateApiClient(ServerConnections.currentApiClient()));
|
||||
Events.on(ServerConnections, 'localusersignedout', () => SyncPlay.Manager.updateApiClient(ServerConnections.currentApiClient()));
|
||||
}
|
||||
|
||||
async function onAppReady() {
|
||||
console.debug('begin onAppReady');
|
||||
|
||||
|
@ -303,34 +303,32 @@ class NavDrawer {
|
||||
setEdgeSwipeEnabled(enabled) {
|
||||
const options = this.options;
|
||||
|
||||
if (!options.disableEdgeSwipe) {
|
||||
if (browser.touch) {
|
||||
if (enabled) {
|
||||
if (!this._edgeSwipeEnabled) {
|
||||
this._edgeSwipeEnabled = true;
|
||||
dom.addEventListener(this.edgeContainer, 'touchstart', this.onEdgeTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(this.edgeContainer, 'touchend', this.onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(this.edgeContainer, 'touchcancel', this.onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (this._edgeSwipeEnabled) {
|
||||
this._edgeSwipeEnabled = false;
|
||||
dom.removeEventListener(this.edgeContainer, 'touchstart', this.onEdgeTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(this.edgeContainer, 'touchend', this.onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(this.edgeContainer, 'touchcancel', this.onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
if (!options.disableEdgeSwipe && browser.touch) {
|
||||
if (enabled) {
|
||||
if (!this._edgeSwipeEnabled) {
|
||||
this._edgeSwipeEnabled = true;
|
||||
dom.addEventListener(this.edgeContainer, 'touchstart', this.onEdgeTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(this.edgeContainer, 'touchend', this.onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(this.edgeContainer, 'touchcancel', this.onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (this._edgeSwipeEnabled) {
|
||||
this._edgeSwipeEnabled = false;
|
||||
dom.removeEventListener(this.edgeContainer, 'touchstart', this.onEdgeTouchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(this.edgeContainer, 'touchend', this.onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(this.edgeContainer, 'touchcancel', this.onEdgeTouchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -291,10 +291,8 @@ const scrollerFactory = function (frame, options) {
|
||||
|
||||
const now = new Date().getTime();
|
||||
|
||||
if (o.autoImmediate) {
|
||||
if (!immediate && (now - (lastAnimate || 0)) <= 50) {
|
||||
immediate = true;
|
||||
}
|
||||
if (o.autoImmediate && !immediate && (now - (lastAnimate || 0)) <= 50) {
|
||||
immediate = true;
|
||||
}
|
||||
|
||||
if (!immediate && o.skipSlideToWhenVisible && fullItemPos && fullItemPos.isVisible) {
|
||||
@ -482,7 +480,8 @@ const scrollerFactory = function (frame, options) {
|
||||
*/
|
||||
function dragHandler(event) {
|
||||
dragging.released = event.type === 'mouseup' || event.type === 'touchend';
|
||||
const pointer = dragging.touch ? event[dragging.released ? 'changedTouches' : 'touches'][0] : event;
|
||||
const eventName = dragging.released ? 'changedTouches' : 'touches';
|
||||
const pointer = dragging.touch ? event[eventName][0] : event;
|
||||
dragging.pathX = pointer.pageX - dragging.initX;
|
||||
dragging.pathY = pointer.pageY - dragging.initY;
|
||||
dragging.path = Math.sqrt(Math.pow(dragging.pathX, 2) + Math.pow(dragging.pathY, 2));
|
||||
@ -808,15 +807,13 @@ const scrollerFactory = function (frame, options) {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
} else if (o.horizontal) {
|
||||
} else if (o.horizontal && o.mouseWheel) {
|
||||
// Don't bind to mouse events with vertical scroll since the mouse wheel can handle this natively
|
||||
|
||||
if (o.mouseWheel) {
|
||||
// Scrolling navigation
|
||||
dom.addEventListener(scrollSource, wheelEvent, scrollHandler, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
// Scrolling navigation
|
||||
dom.addEventListener(scrollSource, wheelEvent, scrollHandler, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
|
||||
dom.addEventListener(frame, 'click', onFrameClick, {
|
||||
|
@ -339,11 +339,7 @@ export class BookPlayer {
|
||||
}
|
||||
|
||||
canPlayItem(item) {
|
||||
if (item.Path && item.Path.endsWith('epub')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return item.Path && item.Path.endsWith('epub');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,11 +452,9 @@ function normalizeImages(state) {
|
||||
if (state && state.NowPlayingItem) {
|
||||
const item = state.NowPlayingItem;
|
||||
|
||||
if (!item.ImageTags || !item.ImageTags.Primary) {
|
||||
if (item.PrimaryImageTag) {
|
||||
item.ImageTags = item.ImageTags || {};
|
||||
item.ImageTags.Primary = item.PrimaryImageTag;
|
||||
}
|
||||
if ((!item.ImageTags || !item.ImageTags.Primary) && item.PrimaryImageTag) {
|
||||
item.ImageTags = item.ImageTags || {};
|
||||
item.ImageTags.Primary = item.PrimaryImageTag;
|
||||
}
|
||||
if (item.BackdropImageTag && item.BackdropItemId === item.Id) {
|
||||
item.BackdropImageTags = [item.BackdropImageTag];
|
||||
|
@ -172,10 +172,8 @@ export class ComicsPlayer {
|
||||
|
||||
onWindowKeyUp(e) {
|
||||
const key = keyboardnavigation.getKeyName(e);
|
||||
switch (key) {
|
||||
case 'Escape':
|
||||
this.stop();
|
||||
break;
|
||||
if (key === 'Escape') {
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,11 +356,7 @@ export class ComicsPlayer {
|
||||
}
|
||||
|
||||
canPlayItem(item) {
|
||||
if (item.Path && (item.Path.endsWith('cbz') || item.Path.endsWith('cbr'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return item.Path && (item.Path.endsWith('cbz') || item.Path.endsWith('cbr'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,13 +41,9 @@ function cancelFadeTimeout() {
|
||||
}
|
||||
|
||||
function supportsFade() {
|
||||
if (browser.tv) {
|
||||
// Not working on tizen.
|
||||
// We could possibly enable on other tv's, but all smart tv browsers tend to be pretty primitive
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
// Not working on tizen.
|
||||
// We could possibly enable on other tv's, but all smart tv browsers tend to be pretty primitive
|
||||
return !browser.tv;
|
||||
}
|
||||
|
||||
function requireHlsPlayer(callback) {
|
||||
@ -417,10 +413,7 @@ class HtmlAudioPlayer {
|
||||
|
||||
// This is a retry after error
|
||||
resume() {
|
||||
const mediaElement = this._mediaElement;
|
||||
if (mediaElement) {
|
||||
mediaElement.play();
|
||||
}
|
||||
this.unpause();
|
||||
}
|
||||
|
||||
unpause() {
|
||||
|
@ -67,16 +67,12 @@ function tryRemoveElement(elem) {
|
||||
}
|
||||
|
||||
function enableNativeTrackSupport(currentSrc, track) {
|
||||
if (track) {
|
||||
if (track.DeliveryMethod === 'Embed') {
|
||||
return true;
|
||||
}
|
||||
if (track?.DeliveryMethod === 'Embed') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.firefox) {
|
||||
if ((currentSrc || '').toLowerCase().includes('.m3u8')) {
|
||||
return false;
|
||||
}
|
||||
if (browser.firefox && (currentSrc || '').toLowerCase().includes('.m3u8')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (browser.ps4) {
|
||||
@ -92,11 +88,9 @@ function tryRemoveElement(elem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (browser.iOS) {
|
||||
if (browser.iOS && (browser.iosVersion || 10) < 10) {
|
||||
// works in the browser but not the native app
|
||||
if ((browser.iosVersion || 10) < 10) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (track) {
|
||||
@ -279,10 +273,6 @@ function tryRemoveElement(elem) {
|
||||
* @type {any | undefined}
|
||||
*/
|
||||
#lastProfile;
|
||||
/**
|
||||
* @type {MutationObserver | IntersectionObserver | undefined} (Unclear observer typing)
|
||||
*/
|
||||
#resizeObserver;
|
||||
|
||||
constructor() {
|
||||
if (browser.edgeUwp) {
|
||||
@ -969,11 +959,6 @@ function tryRemoveElement(elem) {
|
||||
* @private
|
||||
*/
|
||||
destroyCustomTrack(videoElement) {
|
||||
if (this.#resizeObserver) {
|
||||
this.#resizeObserver.disconnect();
|
||||
this.#resizeObserver = null;
|
||||
}
|
||||
|
||||
if (this.#videoSubtitlesElem) {
|
||||
const subtitlesContainer = this.#videoSubtitlesElem.parentNode;
|
||||
if (subtitlesContainer) {
|
||||
@ -1497,14 +1482,14 @@ function tryRemoveElement(elem) {
|
||||
if (
|
||||
// Check non-standard Safari PiP support
|
||||
typeof video.webkitSupportsPresentationMode === 'function' && video.webkitSupportsPresentationMode('picture-in-picture') && typeof video.webkitSetPresentationMode === 'function'
|
||||
// Check non-standard Windows PiP support
|
||||
|| (window.Windows
|
||||
&& Windows.UI.ViewManagement.ApplicationView.getForCurrentView()
|
||||
.isViewModeSupported(Windows.UI.ViewManagement.ApplicationViewMode.compactOverlay))
|
||||
// Check standard PiP support
|
||||
|| document.pictureInPictureEnabled
|
||||
) {
|
||||
list.push('PictureInPicture');
|
||||
} else if (window.Windows) {
|
||||
if (Windows.UI.ViewManagement.ApplicationView.getForCurrentView().isViewModeSupported(Windows.UI.ViewManagement.ApplicationViewMode.compactOverlay)) {
|
||||
list.push('PictureInPicture');
|
||||
}
|
||||
}
|
||||
|
||||
if (browser.safari || browser.iOS || browser.iPad) {
|
||||
@ -1565,13 +1550,7 @@ function tryRemoveElement(elem) {
|
||||
}
|
||||
|
||||
const video = this.#mediaElement;
|
||||
if (video) {
|
||||
if (video.audioTracks) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return !!video?.audioTracks;
|
||||
}
|
||||
|
||||
static onPictureInPictureError(err) {
|
||||
@ -1703,10 +1682,7 @@ function tryRemoveElement(elem) {
|
||||
|
||||
// This is a retry after error
|
||||
resume() {
|
||||
const mediaElement = this.#mediaElement;
|
||||
if (mediaElement) {
|
||||
mediaElement.play();
|
||||
}
|
||||
this.unpause();
|
||||
}
|
||||
|
||||
unpause() {
|
||||
|
@ -304,11 +304,7 @@ export class PdfPlayer {
|
||||
}
|
||||
|
||||
canPlayItem(item) {
|
||||
if (item.Path && item.Path.endsWith('pdf')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return item.Path && item.Path.endsWith('pdf');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,11 +159,9 @@ function normalizeImages(state, apiClient) {
|
||||
if (state && state.NowPlayingItem) {
|
||||
const item = state.NowPlayingItem;
|
||||
|
||||
if (!item.ImageTags || !item.ImageTags.Primary) {
|
||||
if (item.PrimaryImageTag) {
|
||||
item.ImageTags = item.ImageTags || {};
|
||||
item.ImageTags.Primary = item.PrimaryImageTag;
|
||||
}
|
||||
if (!item.ImageTags || !item.ImageTags.Primary && item.PrimaryImageTag) {
|
||||
item.ImageTags = item.ImageTags || {};
|
||||
item.ImageTags.Primary = item.PrimaryImageTag;
|
||||
}
|
||||
if (item.BackdropImageTag && item.BackdropItemId === item.Id) {
|
||||
item.BackdropImageTags = [item.BackdropImageTag];
|
||||
|
@ -9,7 +9,7 @@ import TimeSyncCore from './timeSync/TimeSyncCore';
|
||||
import PlaybackCore from './PlaybackCore';
|
||||
import QueueCore from './QueueCore';
|
||||
import Controller from './Controller';
|
||||
import toast from '../../toast/toast';
|
||||
import toast from '../../../components/toast/toast';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
|
||||
/**
|
@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import toast from '../../toast/toast';
|
||||
import toast from '../../../components/toast/toast';
|
||||
import * as Helper from './Helper';
|
||||
|
||||
/**
|
||||
@ -193,7 +193,6 @@ class QueueCore {
|
||||
}
|
||||
|
||||
this.manager.haltGroupPlayback(apiClient);
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ class PlayerFactory {
|
||||
|
||||
/**
|
||||
* Registers a wrapper to the list of players that can be managed.
|
||||
* @param {GenericPlayer} wrapperClass The wrapper to register.
|
||||
* @param {typeof GenericPlayer} wrapperClass The wrapper to register.
|
||||
*/
|
||||
registerWrapper(wrapperClass) {
|
||||
console.debug('SyncPlay WrapperFactory registerWrapper:', wrapperClass.type);
|
||||
@ -25,7 +25,7 @@ class PlayerFactory {
|
||||
|
||||
/**
|
||||
* Sets the default player wrapper.
|
||||
* @param {GenericPlayer} wrapperClass The wrapper.
|
||||
* @param {typeof GenericPlayer} wrapperClass The wrapper.
|
||||
*/
|
||||
setDefaultWrapper(wrapperClass) {
|
||||
console.debug('SyncPlay WrapperFactory setDefaultWrapper:', wrapperClass.type);
|
@ -9,10 +9,6 @@ import TimeSync from './TimeSync';
|
||||
* Class that manages time syncing with server.
|
||||
*/
|
||||
class TimeSyncServer extends TimeSync {
|
||||
constructor(syncPlayManager) {
|
||||
super(syncPlayManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a ping request to the server.
|
||||
*/
|
49
src/plugins/syncPlay/plugin.ts
Normal file
49
src/plugins/syncPlay/plugin.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { Events } from 'jellyfin-apiclient';
|
||||
|
||||
import { playbackManager } from '../../components/playback/playbackmanager';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
import SyncPlay from './core';
|
||||
import SyncPlayNoActivePlayer from './ui/players/NoActivePlayer';
|
||||
import SyncPlayHtmlVideoPlayer from './ui/players/HtmlVideoPlayer';
|
||||
import SyncPlayHtmlAudioPlayer from './ui/players/HtmlAudioPlayer';
|
||||
|
||||
class SyncPlayPlugin {
|
||||
name: string;
|
||||
id: string;
|
||||
type: string;
|
||||
priority: number;
|
||||
|
||||
constructor() {
|
||||
this.name = 'SyncPlay Plugin';
|
||||
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.priority = 1;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Register player wrappers.
|
||||
SyncPlay.PlayerFactory.setDefaultWrapper(SyncPlayNoActivePlayer);
|
||||
SyncPlay.PlayerFactory.registerWrapper(SyncPlayHtmlVideoPlayer);
|
||||
SyncPlay.PlayerFactory.registerWrapper(SyncPlayHtmlAudioPlayer);
|
||||
|
||||
// Listen for player changes.
|
||||
Events.on(playbackManager, 'playerchange', (_, newPlayer) => {
|
||||
SyncPlay.Manager.onPlayerChange(newPlayer);
|
||||
});
|
||||
|
||||
// Start SyncPlay.
|
||||
const apiClient = ServerConnections.currentApiClient();
|
||||
if (apiClient) SyncPlay.Manager.init(apiClient);
|
||||
|
||||
// FIXME: Multiple apiClients?
|
||||
Events.on(ServerConnections, 'apiclientcreated', (_, newApiClient) => SyncPlay.Manager.init(newApiClient));
|
||||
Events.on(ServerConnections, 'localusersignedin', () => SyncPlay.Manager.updateApiClient(ServerConnections.currentApiClient()));
|
||||
Events.on(ServerConnections, 'localusersignedout', () => SyncPlay.Manager.updateApiClient(ServerConnections.currentApiClient()));
|
||||
}
|
||||
}
|
||||
|
||||
export default SyncPlayPlugin;
|
@ -1,12 +1,12 @@
|
||||
import { Events } from 'jellyfin-apiclient';
|
||||
import SyncPlay from '../core';
|
||||
import SyncPlaySettingsEditor from './settings/SettingsEditor';
|
||||
import loading from '../../loading/loading';
|
||||
import toast from '../../toast/toast';
|
||||
import actionsheet from '../../actionSheet/actionSheet';
|
||||
import loading from '../../../components/loading/loading';
|
||||
import toast from '../../../components/toast/toast';
|
||||
import actionsheet from '../../../components/actionSheet/actionSheet';
|
||||
import globalize from '../../../scripts/globalize';
|
||||
import playbackPermissionManager from './playbackPermissionManager';
|
||||
import ServerConnections from '../../ServerConnections';
|
||||
import ServerConnections from '../../../components/ServerConnections';
|
||||
import './groupSelectionMenu.scss';
|
||||
|
||||
/**
|
@ -1,4 +1,4 @@
|
||||
import { appHost } from '../../apphost';
|
||||
import { appHost } from '../../../components/apphost';
|
||||
|
||||
/**
|
||||
* Creates an audio element that plays a silent sound.
|
@ -10,10 +10,6 @@ import HtmlVideoPlayer from './HtmlVideoPlayer';
|
||||
*/
|
||||
class HtmlAudioPlayer extends HtmlVideoPlayer {
|
||||
static type = 'htmlaudioplayer';
|
||||
|
||||
constructor(player, syncPlayManager) {
|
||||
super(player, syncPlayManager);
|
||||
}
|
||||
}
|
||||
|
||||
export default HtmlAudioPlayer;
|
@ -3,7 +3,7 @@
|
||||
* @module components/syncPlay/ui/players/NoActivePlayer
|
||||
*/
|
||||
|
||||
import { playbackManager } from '../../../playback/playbackmanager';
|
||||
import { playbackManager } from '../../../../components/playback/playbackmanager';
|
||||
import SyncPlay from '../../core';
|
||||
import QueueManager from './QueueManager';
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user