Merge pull request #6795 from thornbill/connection-manager
Use local copy of connection manager
This commit is contained in:
@@ -81,28 +81,29 @@ Jellyfin Web is the frontend used for most of the clients available for end user
|
||||
.
|
||||
└── src
|
||||
├── apps
|
||||
│ ├── dashboard # Admin dashboard app
|
||||
│ ├── experimental # New experimental app
|
||||
│ ├── stable # Classic (stable) app
|
||||
│ └── wizard # Startup wizard app
|
||||
├── assets # Static assets
|
||||
├── components # Higher order visual components and React components
|
||||
├── constants # Common constant values
|
||||
├── controllers # Legacy page views and controllers 🧹 ❌
|
||||
├── elements # Basic webcomponents and React equivalents 🧹
|
||||
├── hooks # Custom React hooks
|
||||
├── lib # Reusable libraries
|
||||
│ ├── globalize # Custom localization library
|
||||
│ ├── legacy # Polyfills for legacy browsers
|
||||
│ ├── navdrawer # Navigation drawer library for classic layout
|
||||
│ └── scroller # Content scrolling library
|
||||
├── plugins # Client plugins (features dynamically loaded at runtime)
|
||||
├── scripts # Random assortment of visual components and utilities 🐉 ❌
|
||||
├── strings # Translation files (only commit changes to en-us.json)
|
||||
├── styles # Common app Sass stylesheets
|
||||
├── themes # Sass and MUI themes
|
||||
├── types # Common TypeScript interfaces/types
|
||||
└── utils # Utility functions
|
||||
│ ├── dashboard # Admin dashboard app
|
||||
│ ├── experimental # New experimental app
|
||||
│ ├── stable # Classic (stable) app
|
||||
│ └── wizard # Startup wizard app
|
||||
├── assets # Static assets
|
||||
├── components # Higher order visual components and React components
|
||||
├── constants # Common constant values
|
||||
├── controllers # Legacy page views and controllers 🧹 ❌
|
||||
├── elements # Basic webcomponents and React equivalents 🧹
|
||||
├── hooks # Custom React hooks
|
||||
├── lib # Reusable libraries
|
||||
│ ├── globalize # Custom localization library
|
||||
│ ├── jellyfin-apiclient # Supporting code for the deprecated apiclient package
|
||||
│ ├── legacy # Polyfills for legacy browsers
|
||||
│ ├── navdrawer # Navigation drawer library for classic layout
|
||||
│ └── scroller # Content scrolling library
|
||||
├── plugins # Client plugins (features dynamically loaded at runtime)
|
||||
├── scripts # Random assortment of visual components and utilities 🐉 ❌
|
||||
├── strings # Translation files (only commit changes to en-us.json)
|
||||
├── styles # Common app Sass stylesheets
|
||||
├── themes # Sass and MUI themes
|
||||
├── types # Common TypeScript interfaces/types
|
||||
└── utils # Utility functions
|
||||
```
|
||||
|
||||
- ❌ — Deprecated, do **not** create new files here
|
||||
|
||||
Vendored
+1
-2
@@ -1,4 +1,3 @@
|
||||
// TODO: Move to jellyfin-apiclient
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
declare module 'jellyfin-apiclient' {
|
||||
import type {
|
||||
@@ -68,7 +67,7 @@ declare module 'jellyfin-apiclient' {
|
||||
UtcTimeResponse,
|
||||
VirtualFolderInfo
|
||||
} from '@jellyfin/sdk/lib/generated-client';
|
||||
import { ConnectionState } from './utils/jellyfin-apiclient/ConnectionState';
|
||||
import type { ConnectionState } from 'lib/jellyfin-apiclient';
|
||||
|
||||
class ApiClient {
|
||||
constructor(serverAddress: string, appName: string, appVersion: string, deviceName: string, deviceId: string);
|
||||
|
||||
@@ -17,12 +17,12 @@ import imageHelper from 'utils/image';
|
||||
import indicators from 'components/indicators/indicators';
|
||||
import taskButton from 'scripts/taskbutton';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import alert from 'components/alert';
|
||||
import confirm from 'components/confirm/confirm';
|
||||
import { getDefaultBackgroundClass } from 'components/cardbuilder/cardBuilderUtils';
|
||||
|
||||
import { getSystemInfoQuery } from 'hooks/useSystemInfo';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
|
||||
|
||||
@@ -18,10 +18,10 @@ import { getBrandingOptionsQuery, QUERY_KEY, useBrandingOptions } from 'apps/das
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
import Image from 'components/Image';
|
||||
import Page from 'components/Page';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { SPLASHSCREEN_URL } from 'constants/branding';
|
||||
import { useApi } from 'hooks/useApi';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
import { ActionData } from 'types/actionData';
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
import Page from 'components/Page';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { getConfigurationApi } from '@jellyfin/sdk/lib/utils/api/configuration-api';
|
||||
import { QUERY_KEY as CONFIG_QUERY_KEY, useConfiguration } from 'hooks/useConfiguration';
|
||||
import { QUERY_KEY as NAMED_CONFIG_QUERY_KEY, NamedConfiguration, useNamedConfiguration } from 'hooks/useNamedConfiguration';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { type ActionFunctionArgs, Form, useActionData, useNavigation } from 'react-router-dom';
|
||||
import { ActionData } from 'types/actionData';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
|
||||
@@ -12,9 +12,9 @@ import { useCultures } from 'apps/dashboard/features/libraries/api/useCultures';
|
||||
import { getImageResolutionOptions } from 'apps/dashboard/features/libraries/utils/metadataOptions';
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
import Page from 'components/Page';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { QUERY_KEY, useConfiguration } from 'hooks/useConfiguration';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import React from 'react';
|
||||
import { type ActionFunctionArgs, Form, useActionData, useNavigation } from 'react-router-dom';
|
||||
import { ActionData } from 'types/actionData';
|
||||
|
||||
@@ -12,11 +12,11 @@ import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
import Page from 'components/Page';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import SimpleAlert from 'components/SimpleAlert';
|
||||
import { QUERY_KEY, useNamedConfiguration } from 'hooks/useNamedConfiguration';
|
||||
import { useUsers } from 'hooks/useUsers';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { type ActionFunctionArgs, Form, useActionData, useNavigation } from 'react-router-dom';
|
||||
import { ActionData } from 'types/actionData';
|
||||
|
||||
@@ -3,6 +3,7 @@ import { getConfigurationApi } from '@jellyfin/sdk/lib/utils/api/configuration-a
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
import Page from 'components/Page';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
@@ -12,7 +13,6 @@ import Checkbox from '@mui/material/Checkbox';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { type ActionFunctionArgs, Form, useActionData, useNavigation } from 'react-router-dom';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { useServerLogs } from 'apps/dashboard/features/logs/api/useServerLogs';
|
||||
import { useConfiguration } from 'hooks/useConfiguration';
|
||||
import type { ServerConfiguration } from '@jellyfin/sdk/lib/generated-client/models/server-configuration';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import Page from 'components/Page';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
@@ -11,7 +12,6 @@ import { type ActionFunctionArgs, Form, useActionData, useNavigation } from 'rea
|
||||
import { ActionData } from 'types/actionData';
|
||||
import { QUERY_KEY, useConfiguration } from 'hooks/useConfiguration';
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { getConfigurationApi } from '@jellyfin/sdk/lib/utils/api/configuration-api';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import Page from 'components/Page';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
@@ -8,7 +9,6 @@ import Stack from '@mui/material/Stack';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { type ActionFunctionArgs, Form, useActionData, useNavigation } from 'react-router-dom';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { getConfigurationApi } from '@jellyfin/sdk/lib/utils/api/configuration-api';
|
||||
import { QUERY_KEY, useConfiguration } from 'hooks/useConfiguration';
|
||||
import Loading from 'components/loading/LoadingComponent';
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { type ActionFunctionArgs, Form, useActionData, useNavigation } from 'react-router-dom';
|
||||
import { QUERY_KEY, useConfiguration } from 'hooks/useConfiguration';
|
||||
import Page from 'components/Page';
|
||||
@@ -16,7 +17,6 @@ import MenuItem from '@mui/material/MenuItem';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Button from '@mui/material/Button';
|
||||
import Alert from '@mui/material/Alert';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { getConfigurationApi } from '@jellyfin/sdk/lib/utils/api/configuration-api';
|
||||
import { TrickplayScanBehavior } from '@jellyfin/sdk/lib/generated-client/models/trickplay-scan-behavior';
|
||||
import { ProcessPriorityClass } from '@jellyfin/sdk/lib/generated-client/models/process-priority-class';
|
||||
|
||||
@@ -11,6 +11,7 @@ import Page from 'components/Page';
|
||||
import { QUERY_KEY, useConfiguration } from 'hooks/useConfiguration';
|
||||
import { useSystemInfo } from 'hooks/useSystemInfo';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { type ActionFunctionArgs, Form, useActionData, useNavigation } from 'react-router-dom';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
@@ -21,7 +22,6 @@ import Checkbox from '@mui/material/Checkbox';
|
||||
import Button from '@mui/material/Button';
|
||||
import Link from '@mui/material/Link';
|
||||
import DirectoryBrowser from 'components/directorybrowser/directorybrowser';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { getConfigurationApi } from '@jellyfin/sdk/lib/utils/api/configuration-api';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
import { ActionData } from 'types/actionData';
|
||||
|
||||
@@ -17,7 +17,7 @@ import CheckBoxElement from '../../../../elements/CheckBoxElement';
|
||||
import SelectElement from '../../../../elements/SelectElement';
|
||||
import Page from '../../../../components/Page';
|
||||
import prompt from '../../../../components/prompt/prompt';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
type NamedItem = {
|
||||
name: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
||||
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import type { ItemDto } from 'types/base/models/item-dto';
|
||||
|
||||
interface ImageOptions {
|
||||
|
||||
@@ -4,8 +4,8 @@ import { MediaSegmentType } from '@jellyfin/sdk/lib/generated-client/models/medi
|
||||
import { getMediaSegmentsApi } from '@jellyfin/sdk/lib/utils/api/media-segments-api';
|
||||
|
||||
import type { PlaybackManager } from 'components/playback/playbackmanager';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { TICKS_PER_MILLISECOND, TICKS_PER_SECOND } from 'constants/time';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { currentSettings as userSettings } from 'scripts/settings/userSettings';
|
||||
import type { PlayerState } from 'types/playbackStopInfo';
|
||||
import type { Event } from 'utils/events';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import loading from 'components/loading/loading';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
function onFinish() {
|
||||
loading.show();
|
||||
|
||||
@@ -3,8 +3,8 @@ import escapeHtml from 'escape-html';
|
||||
import { getDefaultBackgroundClass } from 'components/cardbuilder/cardBuilderUtils';
|
||||
import confirm from 'components/confirm/confirm';
|
||||
import loading from 'components/loading/loading';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import dom from 'scripts/dom';
|
||||
import taskButton from 'scripts/taskbutton';
|
||||
import Dashboard, { pageClassOn, pageIdOn } from 'utils/dashboard';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import loading from 'components/loading/loading';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
|
||||
import 'elements/emby-checkbox/emby-checkbox';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import loading from 'components/loading/loading';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
|
||||
import 'elements/emby-button/emby-button';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import loading from 'components/loading/loading';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
import dom from 'scripts/dom';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import loading from 'components/loading/loading';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import toast from 'components/toast/toast';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
|
||||
import 'styles/dashboard.scss';
|
||||
|
||||
@@ -3,11 +3,10 @@ import { Outlet, useLocation, useNavigate } from 'react-router-dom';
|
||||
import type { ApiClient, ConnectResponse } from 'jellyfin-apiclient';
|
||||
|
||||
import globalize from 'lib/globalize';
|
||||
import { ConnectionState } from 'utils/jellyfin-apiclient/ConnectionState';
|
||||
import { ConnectionState, ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
import alert from './alert';
|
||||
import Loading from './loading/LoadingComponent';
|
||||
import ServerConnections from './ServerConnections';
|
||||
|
||||
enum AccessLevel {
|
||||
/** Requires a user with administrator access */
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { FunctionComponent, useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import ServerConnections from './ServerConnections';
|
||||
import viewManager from './viewManager/viewManager';
|
||||
import globalize from '../lib/globalize';
|
||||
import type { RestoreViewFailResponse } from '../types/viewManager';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import type { RestoreViewFailResponse } from 'types/viewManager';
|
||||
|
||||
interface ServerContentPageProps {
|
||||
view: string
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import escapeHtml from 'escape-html';
|
||||
import Events from '../utils/events.ts';
|
||||
import globalize from '../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import dom from '../scripts/dom';
|
||||
import { formatRelative } from 'date-fns';
|
||||
import serverNotifications from '../scripts/serverNotifications';
|
||||
import '../elements/emby-button/emby-button';
|
||||
import './listview/listview.scss';
|
||||
import ServerConnections from './ServerConnections';
|
||||
import alert from './alert';
|
||||
import { getLocale } from '../utils/dateFnsLocale.ts';
|
||||
import { toBoolean } from '../utils/string.ts';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import browser from '../../scripts/browser';
|
||||
import { playbackManager } from '../playback/playbackmanager';
|
||||
import dom from '../../scripts/dom';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
||||
import './backdrop.scss';
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import browser from 'scripts/browser';
|
||||
import datetime from 'scripts/datetime';
|
||||
import dom from 'scripts/dom';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
||||
import { getItemTypeIcon, getLibraryIcon } from 'utils/image';
|
||||
|
||||
@@ -22,7 +23,6 @@ import itemHelper from '../itemHelper';
|
||||
import layoutManager from '../layoutManager';
|
||||
import { playbackManager } from '../playback/playbackmanager';
|
||||
import { appRouter } from '../router/appRouter';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import itemShortcuts from '../shortcuts';
|
||||
|
||||
import 'elements/emby-button/paper-icon-button-light';
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
*/
|
||||
|
||||
import escapeHtml from 'escape-html';
|
||||
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import datetime from '../../scripts/datetime';
|
||||
import imageLoader from '../images/imageLoader';
|
||||
import layoutManager from '../layoutManager';
|
||||
import browser from '../../scripts/browser';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
||||
const enableFocusTransform = !browser.slow && !browser.edge;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import dom from '../../scripts/dom';
|
||||
import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import loading from '../loading/loading';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import actionsheet from '../actionSheet/actionSheet';
|
||||
import '../../elements/emby-input/emby-input';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
@@ -10,7 +11,6 @@ import '../../elements/emby-button/emby-button';
|
||||
import '../listview/listview.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../formdialog.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
||||
export default class ChannelMapper {
|
||||
constructor(options) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import loading from '../loading/loading';
|
||||
import layoutManager from '../layoutManager';
|
||||
import { appRouter } from '../router/appRouter';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
@@ -13,7 +14,6 @@ import '../../elements/emby-select/emby-select';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../formdialog.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
|
||||
let currentServerId;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { appHost } from '../apphost';
|
||||
import focusManager from '../focusManager';
|
||||
import datetime from '../../scripts/datetime';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import loading from '../loading/loading';
|
||||
import skinManager from '../../scripts/themeManager';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
@@ -14,7 +15,6 @@ import '../../elements/emby-select/emby-select';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-textarea/emby-textarea';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import template from './displaySettings.template.html';
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import dom from '../../scripts/dom';
|
||||
import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import union from 'lodash-es/union';
|
||||
import Events from '../../utils/events.ts';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../elements/emby-collapse/emby-collapse';
|
||||
import './style.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './filterdialog.template.html';
|
||||
import { stopMultiSelect } from '../../components/multiSelect/multiSelect';
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import inputManager from '../../scripts/inputManager';
|
||||
import layoutManager from '../layoutManager';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../elements/emby-input/emby-input';
|
||||
@@ -14,7 +15,6 @@ import '../../elements/emby-select/emby-select';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../formdialog.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './filtermenu.template.html';
|
||||
|
||||
function onSubmit(e) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import dom from '../scripts/dom';
|
||||
import { appRouter } from './router/appRouter';
|
||||
import Dashboard from '../utils/dashboard';
|
||||
import ServerConnections from './ServerConnections';
|
||||
|
||||
function onGroupedCardClick(e, card) {
|
||||
const itemId = card.getAttribute('data-id');
|
||||
|
||||
@@ -2,6 +2,7 @@ import escapeHtml from 'escape-html';
|
||||
import inputManager from '../../scripts/inputManager';
|
||||
import browser from '../../scripts/browser';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Events from '../../utils/events.ts';
|
||||
import scrollHelper from '../../scripts/scrollHelper';
|
||||
import serverNotifications from '../../scripts/serverNotifications';
|
||||
@@ -25,7 +26,6 @@ import '../../elements/emby-tabs/emby-tabs';
|
||||
import '../../elements/emby-scroller/emby-scroller';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import 'webcomponents.js/webcomponents-lite';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './tvguide.template.html';
|
||||
|
||||
function showViewSettings(instance) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import escapeHtml from 'escape-html';
|
||||
|
||||
import { getUserViewsQuery } from 'hooks/useUserViews';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
|
||||
@@ -15,7 +16,6 @@ import dom from '../../scripts/dom';
|
||||
import '../listview/listview.scss';
|
||||
import '../../elements/emby-select/emby-select';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import template from './homeScreenSettings.template.html';
|
||||
import { LibraryTab } from '../../types/libraryTab.ts';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base-item-dto';
|
||||
import type { ApiClient } from 'jellyfin-apiclient';
|
||||
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
import type { SectionContainerElement, SectionOptions } from './section';
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import type { ApiClient } from 'jellyfin-apiclient';
|
||||
import { appRouter } from 'components/router/appRouter';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { getBackdropShape } from 'utils/card';
|
||||
|
||||
import type { SectionContainerElement, SectionOptions } from './section';
|
||||
|
||||
@@ -4,8 +4,8 @@ import type { ApiClient } from 'jellyfin-apiclient';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import { appRouter } from 'components/router/appRouter';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import type { UserSettings } from 'scripts/settings/userSettings';
|
||||
import { getBackdropShape } from 'utils/card';
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collec
|
||||
import escapeHtml from 'escape-html';
|
||||
import type { ApiClient } from 'jellyfin-apiclient';
|
||||
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import { appRouter } from 'components/router/appRouter';
|
||||
import globalize from 'lib/globalize';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
||||
|
||||
import type { SectionContainerElement, SectionOptions } from './section';
|
||||
|
||||
@@ -2,9 +2,9 @@ import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models/base
|
||||
import type { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
import type { ApiClient } from 'jellyfin-apiclient';
|
||||
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import type { UserSettings } from 'scripts/settings/userSettings';
|
||||
import { getBackdropShape, getPortraitShape } from 'utils/card';
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ import browser from '../../scripts/browser';
|
||||
import layoutManager from '../layoutManager';
|
||||
import scrollHelper from '../../scripts/scrollHelper';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../formdialog.scss';
|
||||
import '../cardbuilder/card.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './imageDownloader.template.html';
|
||||
|
||||
const enableFocusTransform = !browser.slow && !browser.edge;
|
||||
|
||||
@@ -10,11 +10,12 @@ import loading from '../loading/loading';
|
||||
import scrollHelper from '../../scripts/scrollHelper';
|
||||
import layoutManager from '../layoutManager';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-select/emby-select';
|
||||
import '../formdialog.scss';
|
||||
import './style.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import template from './imageUploader.template.html';
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import dom from '../../scripts/dom';
|
||||
import layoutManager from '../layoutManager';
|
||||
import focusManager from '../focusManager';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import scrollHelper from '../../scripts/scrollHelper';
|
||||
import imageLoader from '../images/imageLoader';
|
||||
import browser from '../../scripts/browser';
|
||||
@@ -13,7 +14,6 @@ import '../formdialog.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import './imageeditor.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import alert from '../alert';
|
||||
import confirm from '../confirm/confirm';
|
||||
import template from './imageeditor.template.html';
|
||||
|
||||
@@ -2,12 +2,12 @@ import browser from '../scripts/browser';
|
||||
import { copy } from '../scripts/clipboard';
|
||||
import dom from '../scripts/dom';
|
||||
import globalize from '../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import actionsheet from './actionSheet/actionSheet';
|
||||
import { appHost } from './apphost';
|
||||
import { appRouter } from './router/appRouter';
|
||||
import itemHelper, { canEditPlaylist } from './itemHelper';
|
||||
import { playbackManager } from './playback/playbackmanager';
|
||||
import ServerConnections from './ServerConnections';
|
||||
import toast from './toast/toast';
|
||||
import * as userSettings from '../scripts/settings/userSettings';
|
||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { getPlaylistsApi } from '@jellyfin/sdk/lib/utils/api/playlists-api';
|
||||
|
||||
import { appHost } from './apphost';
|
||||
import globalize from 'lib/globalize';
|
||||
import ServerConnections from './ServerConnections';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
|
||||
export function getDisplayName(item, options = {}) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import toast from '../toast/toast';
|
||||
import { copy } from '../../scripts/clipboard';
|
||||
import dom from '../../scripts/dom';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import itemHelper from '../../components/itemHelper';
|
||||
import loading from '../loading/loading';
|
||||
import '../../elements/emby-select/emby-select';
|
||||
@@ -20,7 +21,6 @@ import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../formdialog.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './itemMediaInfo.template.html';
|
||||
|
||||
// Do not add extra spaces between tags - they will be copied into the result
|
||||
|
||||
@@ -8,6 +8,7 @@ import escapeHtml from 'escape-html';
|
||||
import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import loading from '../loading/loading';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import scrollHelper from '../../scripts/scrollHelper';
|
||||
import layoutManager from '../layoutManager';
|
||||
import focusManager from '../focusManager';
|
||||
@@ -18,7 +19,6 @@ import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../formdialog.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../cardbuilder/card.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import template from './itemidentifier.template.html';
|
||||
import datetime from '../../scripts/datetime';
|
||||
|
||||
@@ -10,12 +10,12 @@ import mediaInfo from '../mediainfo/mediainfo';
|
||||
import indicators from '../indicators/indicators';
|
||||
import layoutManager from '../layoutManager';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import datetime from '../../scripts/datetime';
|
||||
import cardBuilder from '../cardbuilder/cardBuilder';
|
||||
import './listview.scss';
|
||||
import '../../elements/emby-ratingbutton/emby-ratingbutton';
|
||||
import '../../elements/emby-playstatebutton/emby-playstatebutton';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import { getDefaultBackgroundClass } from '../cardbuilder/cardBuilderUtils';
|
||||
import markdownIt from 'markdown-it';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
@@ -5,6 +5,7 @@ import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import layoutManager from '../layoutManager';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import loading from '../loading/loading';
|
||||
import focusManager from '../focusManager';
|
||||
import dom from '../../scripts/dom';
|
||||
@@ -16,7 +17,6 @@ import 'material-design-icons-iconfont';
|
||||
import './lyricseditor.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import template from './lyricseditor.template.html';
|
||||
import templatePreview from './lyricspreview.template.html';
|
||||
|
||||
@@ -3,12 +3,12 @@ import escapeHtml from 'escape-html';
|
||||
import { getLyricsApi } from '@jellyfin/sdk/lib/utils/api/lyrics-api';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
import dialogHelper from '../../components/dialogHelper/dialogHelper';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import dom from '../../scripts/dom';
|
||||
import loading from '../../components/loading/loading';
|
||||
import scrollHelper from '../../scripts/scrollHelper';
|
||||
import layoutManager from '../layoutManager';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import template from './lyricsuploader.template.html';
|
||||
import toast from '../toast/toast';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
|
||||
@@ -6,6 +6,8 @@ import datetime from '../../scripts/datetime';
|
||||
import loading from '../loading/loading';
|
||||
import focusManager from '../focusManager';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../elements/emby-input/emby-input';
|
||||
import '../../elements/emby-select/emby-select';
|
||||
@@ -17,7 +19,6 @@ import '../formdialog.scss';
|
||||
import '../../styles/clearbutton.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import './style.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import { appRouter } from '../router/appRouter';
|
||||
import template from './metadataEditor.template.html';
|
||||
|
||||
@@ -2,9 +2,9 @@ import browser from '../../scripts/browser';
|
||||
import { appHost } from '../apphost';
|
||||
import loading from '../loading/loading';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import dom from '../../scripts/dom';
|
||||
import './multiSelect.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import alert from '../alert';
|
||||
import confirm from '../confirm/confirm';
|
||||
import itemHelper from '../itemHelper';
|
||||
|
||||
@@ -2,8 +2,8 @@ import serverNotifications from '../../scripts/serverNotifications';
|
||||
import { playbackManager } from '../playback/playbackmanager';
|
||||
import Events from '../../utils/events.ts';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { getItems } from '../../utils/jellyfin-apiclient/getItems.ts';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
|
||||
import NotificationIcon from './notificationicon.png';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getImageUrl } from 'apps/stable/features/playback/utils/image';
|
||||
import { getItemTextLines } from 'apps/stable/features/playback/utils/itemText';
|
||||
import { appRouter, isLyricsPage } from 'components/router/appRouter';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
import datetime from '../../scripts/datetime';
|
||||
import Events from '../../utils/events.ts';
|
||||
@@ -14,7 +15,6 @@ import globalize from 'lib/globalize';
|
||||
import itemContextMenu from '../itemContextMenu';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../elements/emby-ratingbutton/emby-ratingbutton';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import appFooter from '../appFooter/appFooter';
|
||||
import itemShortcuts from '../shortcuts';
|
||||
import './nowPlayingBar.scss';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import { getItemQuery } from 'hooks/useItem';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind.js';
|
||||
import { PlaybackErrorCode } from '@jellyfin/sdk/lib/generated-client/models/playback-error-code.js';
|
||||
import { getMediaInfoApi } from '@jellyfin/sdk/lib/utils/api/media-info-api';
|
||||
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
|
||||
@@ -14,7 +15,6 @@ import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import globalize from '../../lib/globalize';
|
||||
import loading from '../loading/loading';
|
||||
import { appHost } from '../apphost';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import alert from '../alert';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
import { includesAny } from '../../utils/container.ts';
|
||||
@@ -24,10 +24,10 @@ import { getItemBackdropImageUrl } from '../../utils/jellyfin-apiclient/backdrop
|
||||
import { PlayerEvent } from 'apps/stable/features/playback/constants/playerEvent';
|
||||
import { bindMediaSegmentManager } from 'apps/stable/features/playback/utils/mediaSegmentManager';
|
||||
import { bindMediaSessionSubscriber } from 'apps/stable/features/playback/utils/mediaSessionSubscriber';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { MediaError } from 'types/mediaError';
|
||||
import { getMediaError } from 'utils/mediaError';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind.js';
|
||||
import { bindSkipSegment } from './skipsegment.ts';
|
||||
|
||||
const UNLIMITED_ITEMS = -1;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import actionsheet from '../actionSheet/actionSheet';
|
||||
import { playbackManager } from '../playback/playbackmanager';
|
||||
import globalize from '../../lib/globalize';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import qualityoptions from '../qualityOptions';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
||||
function showQualityMenu(player, btn) {
|
||||
const videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import escapeHTML from 'escape-html';
|
||||
|
||||
import { MediaSegmentAction } from 'apps/stable/features/playback/constants/mediaSegmentAction';
|
||||
import { getId, getMediaSegmentAction } from 'apps/stable/features/playback/utils/mediaSegmentSettings';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
import appSettings from '../../scripts/settings/appSettings';
|
||||
import { appHost } from '../apphost';
|
||||
@@ -12,7 +13,6 @@ import qualityoptions from '../qualityOptions';
|
||||
import globalize from '../../lib/globalize';
|
||||
import loading from '../loading/loading';
|
||||
import Events from '../../utils/events.ts';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import template from './playbackSettings.template.html';
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Events from '../../utils/events.ts';
|
||||
import layoutManager from '../layoutManager';
|
||||
import { playbackManager } from '../playback/playbackmanager';
|
||||
import playMethodHelper from '../playback/playmethodhelper';
|
||||
import { pluginManager } from '../pluginManager';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
|
||||
import './playerstats.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
||||
function init(instance) {
|
||||
const parent = document.createElement('div');
|
||||
|
||||
@@ -8,6 +8,7 @@ import escapeHtml from 'escape-html';
|
||||
import toast from 'components/toast/toast';
|
||||
import dom from 'scripts/dom';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { currentSettings as userSettings } from 'scripts/settings/userSettings';
|
||||
import { PluginType } from 'types/plugin';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
@@ -19,7 +20,6 @@ import layoutManager from '../layoutManager';
|
||||
import { playbackManager } from '../playback/playbackmanager';
|
||||
import { pluginManager } from '../pluginManager';
|
||||
import { appRouter } from '../router/appRouter';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
||||
import 'elements/emby-button/emby-button';
|
||||
import 'elements/emby-input/emby-input';
|
||||
|
||||
@@ -8,8 +8,8 @@ import { appRouter } from './router/appRouter';
|
||||
import * as inputManager from '../scripts/inputManager';
|
||||
import toast from '../components/toast/toast';
|
||||
import confirm from '../components/confirm/confirm';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import * as dashboard from '../utils/dashboard';
|
||||
import ServerConnections from '../components/ServerConnections';
|
||||
|
||||
// TODO: replace with each plugin version
|
||||
const cacheParam = new Date().getTime();
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import dom from '../../scripts/dom';
|
||||
import recordingHelper from './recordinghelper';
|
||||
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import './recordingfields.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
||||
function onRecordingButtonClick() {
|
||||
const item = this.item;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import layoutManager from '../layoutManager';
|
||||
import mediaInfo from '../mediainfo/mediainfo';
|
||||
import loading from '../loading/loading';
|
||||
@@ -8,6 +9,7 @@ import datetime from '../../scripts/datetime';
|
||||
import imageLoader from '../images/imageLoader';
|
||||
import RecordingFields from './recordingfields';
|
||||
import Events from '../../utils/events.ts';
|
||||
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
@@ -16,7 +18,6 @@ import '../../elements/emby-input/emby-input';
|
||||
import '../formdialog.scss';
|
||||
import './recordingcreator.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import { playbackManager } from '../playback/playbackmanager';
|
||||
import template from './recordingcreator.template.html';
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
|
||||
import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import layoutManager from '../layoutManager';
|
||||
import loading from '../loading/loading';
|
||||
import scrollHelper from '../../scripts/scrollHelper';
|
||||
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-collapse/emby-collapse';
|
||||
@@ -13,7 +15,6 @@ import '../formdialog.scss';
|
||||
import './recordingcreator.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './recordingeditor.template.html';
|
||||
|
||||
let currentDialog;
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Events from '../../utils/events.ts';
|
||||
import serverNotifications from '../../scripts/serverNotifications';
|
||||
import loading from '../loading/loading';
|
||||
import dom from '../../scripts/dom';
|
||||
import recordingHelper from './recordinghelper';
|
||||
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import './recordingfields.scss';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import template from './recordingfields.template.html';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import globalize from '../../lib/globalize';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import loading from '../loading/loading';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import confirm from '../confirm/confirm';
|
||||
import dialog from '../dialog/dialog';
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import layoutManager from '../layoutManager';
|
||||
import loading from '../loading/loading';
|
||||
import scrollHelper from '../../scripts/scrollHelper';
|
||||
import datetime from '../../scripts/datetime';
|
||||
|
||||
import '../../styles/scrollstyles.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
@@ -14,7 +16,6 @@ import '../formdialog.scss';
|
||||
import './recordingcreator.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import template from './seriesrecordingeditor.template.html';
|
||||
|
||||
let currentDialog;
|
||||
|
||||
@@ -3,6 +3,8 @@ import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import loading from '../loading/loading';
|
||||
import layoutManager from '../layoutManager';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
import '../../elements/emby-input/emby-input';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
@@ -10,7 +12,6 @@ import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../elements/emby-select/emby-select';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../formdialog.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
|
||||
function getEditorHtml() {
|
||||
|
||||
@@ -11,9 +11,11 @@ import { playbackManager } from '../playback/playbackmanager';
|
||||
import Events from '../../utils/events.ts';
|
||||
import { appHost } from '../apphost';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import layoutManager from '../layoutManager';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import itemContextMenu from '../itemContextMenu';
|
||||
|
||||
import '../cardbuilder/card.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
@@ -21,7 +23,6 @@ import '../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import './remotecontrol.scss';
|
||||
import '../../elements/emby-ratingbutton/emby-ratingbutton';
|
||||
import '../../elements/emby-slider/emby-slider';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import { appRouter } from '../router/appRouter';
|
||||
import { getDefaultBackgroundClass } from '../cardbuilder/cardBuilderUtils';
|
||||
|
||||
@@ -4,12 +4,12 @@ import { setBackdropTransparency } from '../backdrop/backdrop';
|
||||
import globalize from '../../lib/globalize';
|
||||
import itemHelper from '../itemHelper';
|
||||
import loading from '../loading/loading';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import alert from '../alert';
|
||||
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
import { getItemQuery } from 'hooks/useItem';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
import { history } from 'RootAppRouter';
|
||||
|
||||
/** Pages of "no return" (when "Go back" should behave differently, probably quitting the application). */
|
||||
|
||||
@@ -8,9 +8,9 @@ import { playbackManager } from './playback/playbackmanager';
|
||||
import inputManager from '../scripts/inputManager';
|
||||
import { appRouter } from './router/appRouter';
|
||||
import globalize from '../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import dom from '../scripts/dom';
|
||||
import recordingHelper from './recordingcreator/recordinghelper';
|
||||
import ServerConnections from './ServerConnections';
|
||||
import toast from './toast/toast';
|
||||
import * as userSettings from '../scripts/settings/userSettings';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
|
||||
@@ -3,16 +3,17 @@
|
||||
* @module components/slideshow/slideshow
|
||||
*/
|
||||
import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import inputManager from '../../scripts/inputManager';
|
||||
import layoutManager from '../layoutManager';
|
||||
import focusManager from '../focusManager';
|
||||
import browser from '../../scripts/browser';
|
||||
import { appHost } from '../apphost';
|
||||
import dom from '../../scripts/dom';
|
||||
|
||||
import './style.scss';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import screenfull from 'screenfull';
|
||||
import { randomInt } from '../../utils/number.ts';
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@ import { appHost } from '../apphost';
|
||||
import dialogHelper from '../dialogHelper/dialogHelper';
|
||||
import layoutManager from '../layoutManager';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import loading from '../loading/loading';
|
||||
import focusManager from '../focusManager';
|
||||
import dom from '../../scripts/dom';
|
||||
|
||||
import '../../elements/emby-select/emby-select';
|
||||
import '../listview/listview.scss';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
@@ -15,7 +17,6 @@ import 'material-design-icons-iconfont';
|
||||
import './subtitleeditor.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import confirm from '../confirm/confirm';
|
||||
import template from './subtitleeditor.template.html';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { appHost } from '../apphost';
|
||||
import appSettings from '../../scripts/settings/appSettings';
|
||||
import focusManager from '../focusManager';
|
||||
@@ -8,6 +9,7 @@ import subtitleAppearanceHelper from './subtitleappearancehelper';
|
||||
import settingsHelper from '../settingshelper';
|
||||
import dom from '../../scripts/dom';
|
||||
import Events from '../../utils/events.ts';
|
||||
|
||||
import '../listview/listview.scss';
|
||||
import '../../elements/emby-select/emby-select';
|
||||
import '../../elements/emby-slider/emby-slider';
|
||||
@@ -15,7 +17,6 @@ import '../../elements/emby-input/emby-input';
|
||||
import '../../elements/emby-checkbox/emby-checkbox';
|
||||
import '../../styles/flexstyles.scss';
|
||||
import './subtitlesettings.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import toast from '../toast/toast';
|
||||
import template from './subtitlesettings.template.html';
|
||||
|
||||
|
||||
@@ -3,14 +3,15 @@ import escapeHtml from 'escape-html';
|
||||
import { getSubtitleApi } from '@jellyfin/sdk/lib/utils/api/subtitle-api';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
import dialogHelper from '../../components/dialogHelper/dialogHelper';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
import dom from '../../scripts/dom';
|
||||
import loading from '../../components/loading/loading';
|
||||
import scrollHelper from '../../scripts/scrollHelper';
|
||||
import layoutManager from '../layoutManager';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import template from './subtitleuploader.template.html';
|
||||
import toast from '../toast/toast';
|
||||
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../elements/emby-select/emby-select';
|
||||
import '../formdialog.scss';
|
||||
|
||||
@@ -3,6 +3,7 @@ import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type'
|
||||
import { getLibraryApi } from '@jellyfin/sdk/lib/utils/api/library-api';
|
||||
|
||||
import { getItemQuery } from 'hooks/useItem';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { currentSettings as userSettings } from 'scripts/settings/userSettings';
|
||||
import { ItemKind } from 'types/base/models/item-kind';
|
||||
import Events from 'utils/events.ts';
|
||||
@@ -10,7 +11,6 @@ import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
import { queryClient } from 'utils/query/queryClient';
|
||||
|
||||
import { playbackManager } from './playback/playbackmanager';
|
||||
import ServerConnections from './ServerConnections';
|
||||
|
||||
let currentOwnerId;
|
||||
let currentThemeIds = [];
|
||||
|
||||
@@ -5,8 +5,9 @@ import mediaInfo from '../mediainfo/mediainfo';
|
||||
import layoutManager from '../layoutManager';
|
||||
import focusManager from '../focusManager';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import itemHelper from '../itemHelper';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
||||
import './upnextdialog.scss';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import '../../styles/flexstyles.scss';
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import dom from '../../scripts/dom';
|
||||
import itemHelper from '../itemHelper';
|
||||
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../../elements/emby-button/emby-button';
|
||||
import './userdatabuttons.scss';
|
||||
import ServerConnections from '../ServerConnections';
|
||||
|
||||
const userDataMethods = {
|
||||
markPlayed: markPlayed,
|
||||
|
||||
@@ -2,9 +2,9 @@ import cardBuilder from 'components/cardbuilder/cardBuilder';
|
||||
import focusManager from 'components/focusManager';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import { appRouter } from 'components/router/appRouter';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import dom from 'scripts/dom';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import { getBackdropShape, getPortraitShape, getSquareShape } from 'utils/card';
|
||||
import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by';
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ import * as userSettings from '../scripts/settings/userSettings';
|
||||
import loading from '../components/loading/loading';
|
||||
import focusManager from '../components/focusManager';
|
||||
import homeSections from '../components/homesections/homesections';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
import '../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import ServerConnections from '../components/ServerConnections';
|
||||
|
||||
class HomeTab {
|
||||
constructor(view, params) {
|
||||
|
||||
@@ -21,12 +21,12 @@ import loading from 'components/loading/loading';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import { appRouter } from 'components/router/appRouter';
|
||||
import itemShortcuts from 'components/shortcuts';
|
||||
import ServerConnections from 'components/ServerConnections';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import browser from 'scripts/browser';
|
||||
import datetime from 'scripts/datetime';
|
||||
import dom from 'scripts/dom';
|
||||
import { download } from 'scripts/fileDownloader';
|
||||
import globalize from 'lib/globalize';
|
||||
import libraryMenu from 'scripts/libraryMenu';
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import { getPortraitShape, getSquareShape } from 'utils/card';
|
||||
@@ -1379,7 +1379,6 @@ function renderChildren(page, item) {
|
||||
if (item.Type == 'MusicAlbum') {
|
||||
let showArtist = false;
|
||||
for (const track of result.Items) {
|
||||
// eslint-disable-next-line sonarjs/no-alphabetical-sort
|
||||
if (!isEqual(track.ArtistItems.map(x => x.Id).sort(), track.AlbumArtists.map(x => x.Id).sort())) {
|
||||
showArtist = true;
|
||||
break;
|
||||
|
||||
@@ -8,9 +8,9 @@ import AlphaNumericShortcuts from '../scripts/alphanumericshortcuts';
|
||||
import libraryBrowser from '../scripts/libraryBrowser';
|
||||
import { playbackManager } from '../components/playback/playbackmanager';
|
||||
import AlphaPicker from '../components/alphaPicker/alphaPicker';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import '../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../elements/emby-scroller/emby-scroller';
|
||||
import ServerConnections from '../components/ServerConnections';
|
||||
import LibraryMenu from '../scripts/libraryMenu';
|
||||
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
||||
import { ItemSortBy } from '@jellyfin/sdk/lib/generated-client/models/item-sort-by';
|
||||
|
||||
@@ -4,12 +4,12 @@ import autoFocuser from 'components/autoFocuser';
|
||||
import { appRouter } from '../components/router/appRouter';
|
||||
import layoutManager from 'components/layoutManager';
|
||||
import { playbackManager } from '../components/playback/playbackmanager';
|
||||
import ServerConnections from '../components/ServerConnections';
|
||||
import scrollManager from 'components/scrollManager';
|
||||
import focusManager from 'components/focusManager';
|
||||
|
||||
import keyboardNavigation from 'scripts/keyboardNavigation';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import LibraryMenu from 'scripts/libraryMenu';
|
||||
import Events from 'utils/events';
|
||||
|
||||
|
||||
@@ -19,10 +19,10 @@ import '../../../elements/emby-slider/emby-slider';
|
||||
import '../../../elements/emby-button/paper-icon-button-light';
|
||||
import '../../../elements/emby-ratingbutton/emby-ratingbutton';
|
||||
import '../../../styles/videoosd.scss';
|
||||
import ServerConnections from '../../../components/ServerConnections';
|
||||
import shell from '../../../scripts/shell';
|
||||
import SubtitleSync from '../../../components/subtitlesync/subtitlesync';
|
||||
import { appRouter } from '../../../components/router/appRouter';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import LibraryMenu from '../../../scripts/libraryMenu';
|
||||
import { setBackdropTransparency, TRANSPARENCY_LEVEL } from '../../../components/backdrop/backdrop';
|
||||
import { pluginManager } from '../../../components/pluginManager';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import appSettings from '../../../scripts/settings/appSettings';
|
||||
import loading from '../../../components/loading/loading';
|
||||
import globalize from '../../../lib/globalize';
|
||||
import '../../../elements/emby-button/emby-button';
|
||||
import Dashboard from '../../../utils/dashboard';
|
||||
import ServerConnections from '../../../components/ServerConnections';
|
||||
import { ConnectionState } from '../../../utils/jellyfin-apiclient/ConnectionState.ts';
|
||||
import loading from 'components/loading/loading';
|
||||
import globalize from 'lib/globalize';
|
||||
import { ConnectionState, ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import appSettings from 'scripts/settings/appSettings';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
|
||||
import 'elements/emby-button/emby-button';
|
||||
|
||||
function handleConnectionResult(page, result) {
|
||||
loading.hide();
|
||||
|
||||
@@ -11,12 +11,13 @@ import globalize from '../../../lib/globalize';
|
||||
import '../../../components/cardbuilder/card.scss';
|
||||
import '../../../elements/emby-checkbox/emby-checkbox';
|
||||
import Dashboard from '../../../utils/dashboard';
|
||||
import ServerConnections from '../../../components/ServerConnections';
|
||||
import toast from '../../../components/toast/toast';
|
||||
import dialogHelper from '../../../components/dialogHelper/dialogHelper';
|
||||
import baseAlert from '../../../components/alert';
|
||||
import './login.scss';
|
||||
import { getDefaultBackgroundClass } from '../../../components/cardbuilder/cardBuilderUtils';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
import './login.scss';
|
||||
|
||||
const enableFocusTransform = !browser.slow && !browser.edge;
|
||||
|
||||
|
||||
@@ -17,10 +17,9 @@ import '../../../elements/emby-itemscontainer/emby-itemscontainer';
|
||||
import '../../../components/cardbuilder/card.scss';
|
||||
import '../../../elements/emby-button/emby-button';
|
||||
import Dashboard from '../../../utils/dashboard';
|
||||
import ServerConnections from '../../../components/ServerConnections';
|
||||
import alert from '../../../components/alert';
|
||||
import { ConnectionState } from '../../../utils/jellyfin-apiclient/ConnectionState.ts';
|
||||
import { getDefaultBackgroundClass } from '../../../components/cardbuilder/cardBuilderUtils';
|
||||
import { ConnectionState, ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
const enableFocusTransform = !browser.slow && !browser.edge;
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import 'webcomponents.js/webcomponents-lite';
|
||||
import Sortable from 'sortablejs';
|
||||
|
||||
import itemShortcuts from '../../components/shortcuts';
|
||||
import inputManager from '../../scripts/inputManager';
|
||||
import { playbackManager } from '../../components/playback/playbackmanager';
|
||||
@@ -7,11 +10,9 @@ import browser from '../../scripts/browser';
|
||||
import dom from '../../scripts/dom';
|
||||
import loading from '../../components/loading/loading';
|
||||
import focusManager from '../../components/focusManager';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import serverNotifications from '../../scripts/serverNotifications';
|
||||
import Events from '../../utils/events.ts';
|
||||
import 'webcomponents.js/webcomponents-lite';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
import Sortable from 'sortablejs';
|
||||
|
||||
const ItemsContainerPrototype = Object.create(HTMLDivElement.prototype);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import serverNotifications from '../../scripts/serverNotifications';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Events from '../../utils/events.ts';
|
||||
import EmbyButtonPrototype from '../../elements/emby-button/emby-button';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
|
||||
function addNotificationEvent(instance, name, handler) {
|
||||
const localHandler = handler.bind(instance);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import serverNotifications from '../../scripts/serverNotifications';
|
||||
import globalize from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import Events from '../../utils/events.ts';
|
||||
import EmbyButtonPrototype from '../emby-button/emby-button';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
|
||||
function addNotificationEvent(instance, name, handler) {
|
||||
const localHandler = handler.bind(instance);
|
||||
|
||||
@@ -3,9 +3,9 @@ import type { UserDto } from '@jellyfin/sdk/lib/generated-client';
|
||||
import type { ApiClient, Event } from 'jellyfin-apiclient';
|
||||
import React, { type FC, type PropsWithChildren, createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import ServerConnections from '../components/ServerConnections';
|
||||
import events from '../utils/events';
|
||||
import { toApi } from '../utils/jellyfin-apiclient/compat';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import events from 'utils/events';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
|
||||
export interface JellyfinApiContext {
|
||||
__legacyApiClient__?: ApiClient
|
||||
|
||||
+2
-3
@@ -5,7 +5,7 @@ import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
// NOTE: We need to import this first to initialize the connection
|
||||
import ServerConnections from './components/ServerConnections';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
|
||||
import { appHost } from './components/apphost';
|
||||
import autoFocuser from './components/autoFocuser';
|
||||
@@ -23,7 +23,6 @@ import { pageClassOn, serverAddress } from './utils/dashboard';
|
||||
import Events from './utils/events';
|
||||
|
||||
import RootApp from './RootApp';
|
||||
import { history } from 'RootAppRouter';
|
||||
|
||||
// Import the button webcomponent for use throughout the site
|
||||
// NOTE: This is a bit of a hack, files should ensure the component is imported before use
|
||||
@@ -206,7 +205,7 @@ async function renderApp() {
|
||||
|
||||
const root = createRoot(container);
|
||||
root.render(
|
||||
<RootApp history={history} />
|
||||
<RootApp />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+8
-10
@@ -1,16 +1,17 @@
|
||||
// NOTE: This is used for jsdoc return type
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { Api } from '@jellyfin/sdk';
|
||||
import { MINIMUM_VERSION } from '@jellyfin/sdk/lib/versions';
|
||||
import { ConnectionManager, Credentials, ApiClient } from 'jellyfin-apiclient';
|
||||
import { Credentials, ApiClient } from 'jellyfin-apiclient';
|
||||
|
||||
import { appHost } from './apphost';
|
||||
import Dashboard from '../utils/dashboard';
|
||||
import Events from '../utils/events.ts';
|
||||
import { setUserInfo } from '../scripts/settings/userSettings';
|
||||
import appSettings from '../scripts/settings/appSettings';
|
||||
import { appHost } from 'components/apphost';
|
||||
import appSettings from 'scripts/settings/appSettings';
|
||||
import { setUserInfo } from 'scripts/settings/userSettings';
|
||||
import Dashboard from 'utils/dashboard';
|
||||
import Events from 'utils/events.ts';
|
||||
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||
|
||||
import ConnectionManager from './connectionManager';
|
||||
|
||||
const normalizeImageOptions = options => {
|
||||
if (!options.quality && (options.maxWidth || options.width || options.maxHeight || options.height || options.fillWidth || options.fillHeight)) {
|
||||
options.quality = 90;
|
||||
@@ -37,9 +38,6 @@ class ServerConnections extends ConnectionManager {
|
||||
this.localApiClient = null;
|
||||
this.firstConnection = null;
|
||||
|
||||
// Set the apiclient minimum version to match the SDK
|
||||
this._minServerVersion = MINIMUM_VERSION;
|
||||
|
||||
Events.on(this, 'localusersignedout', (_e, logoutInfo) => {
|
||||
setUserInfo(null, null);
|
||||
// Ensure the updated credentials are persisted to storage
|
||||
@@ -0,0 +1,772 @@
|
||||
import { AUTHORIZATION_HEADER } from '@jellyfin/sdk/lib/api';
|
||||
import { getAuthorizationHeader } from '@jellyfin/sdk/lib/utils';
|
||||
import { MINIMUM_VERSION } from '@jellyfin/sdk/lib/versions';
|
||||
import { ApiClient } from 'jellyfin-apiclient';
|
||||
|
||||
import events from 'utils/events';
|
||||
import { ajax } from 'utils/fetch';
|
||||
import { equalsIgnoreCase } from 'utils/string';
|
||||
|
||||
import { ConnectionMode } from './connectionMode';
|
||||
import { ConnectionState } from './connectionState';
|
||||
import { compareVersions } from './utils/compareVersions';
|
||||
|
||||
const DEFAULT_CONNECTION_TIMEOUT = 20000;
|
||||
|
||||
function getServerAddress(server, mode) {
|
||||
switch (mode) {
|
||||
case ConnectionMode.Local:
|
||||
return server.LocalAddress;
|
||||
case ConnectionMode.Manual:
|
||||
return server.ManualAddress;
|
||||
case ConnectionMode.Remote:
|
||||
return server.RemoteAddress;
|
||||
default:
|
||||
return server.ManualAddress || server.LocalAddress || server.RemoteAddress;
|
||||
}
|
||||
}
|
||||
|
||||
function updateServerInfo(server, systemInfo) {
|
||||
server.Name = systemInfo.ServerName;
|
||||
|
||||
if (systemInfo.Id) {
|
||||
server.Id = systemInfo.Id;
|
||||
}
|
||||
|
||||
if (systemInfo.LocalAddress) {
|
||||
server.LocalAddress = systemInfo.LocalAddress;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeAddress(address) {
|
||||
// Attempt to correct bad input
|
||||
address = address.trim();
|
||||
|
||||
// Seeing failures in iOS when protocol isn't lowercase
|
||||
address = address.replace('Http:', 'http:');
|
||||
address = address.replace('Https:', 'https:');
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
export default class ConnectionManager {
|
||||
constructor(credentialProvider, appName, appVersion, deviceName, deviceId, capabilities) {
|
||||
console.log('Begin ConnectionManager constructor');
|
||||
|
||||
const self = this;
|
||||
this._apiClients = [];
|
||||
|
||||
// Set the minimum version to match the SDK
|
||||
self._minServerVersion = MINIMUM_VERSION;
|
||||
|
||||
self.appVersion = () => appVersion;
|
||||
|
||||
self.appName = () => appName;
|
||||
|
||||
self.capabilities = () => capabilities;
|
||||
|
||||
self.deviceId = () => deviceId;
|
||||
|
||||
self.credentialProvider = () => credentialProvider;
|
||||
|
||||
self.getServerInfo = (id) => {
|
||||
const servers = credentialProvider.credentials().Servers;
|
||||
|
||||
return servers.filter((s) => s.Id === id)[0];
|
||||
};
|
||||
|
||||
self.getLastUsedServer = () => {
|
||||
const servers = credentialProvider.credentials().Servers;
|
||||
|
||||
servers.sort((a, b) => (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0));
|
||||
|
||||
if (!servers.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return servers[0];
|
||||
};
|
||||
|
||||
self.addApiClient = (apiClient) => {
|
||||
self._apiClients.push(apiClient);
|
||||
|
||||
const existingServers = credentialProvider
|
||||
.credentials()
|
||||
.Servers.filter(
|
||||
(s) =>
|
||||
equalsIgnoreCase(s.ManualAddress, apiClient.serverAddress())
|
||||
|| equalsIgnoreCase(s.LocalAddress, apiClient.serverAddress())
|
||||
|| equalsIgnoreCase(s.RemoteAddress, apiClient.serverAddress())
|
||||
);
|
||||
|
||||
const existingServer = existingServers.length ? existingServers[0] : apiClient.serverInfo();
|
||||
existingServer.DateLastAccessed = new Date().getTime();
|
||||
existingServer.LastConnectionMode = ConnectionMode.Manual;
|
||||
existingServer.ManualAddress = apiClient.serverAddress();
|
||||
|
||||
if (apiClient.manualAddressOnly) {
|
||||
existingServer.manualAddressOnly = true;
|
||||
}
|
||||
|
||||
apiClient.serverInfo(existingServer);
|
||||
|
||||
apiClient.onAuthenticated = (instance, result) => onAuthenticated(instance, result, {}, true);
|
||||
|
||||
if (!existingServers.length) {
|
||||
const credentials = credentialProvider.credentials();
|
||||
credentials.Servers = [existingServer];
|
||||
credentialProvider.credentials(credentials);
|
||||
}
|
||||
|
||||
events.trigger(self, 'apiclientcreated', [apiClient]);
|
||||
};
|
||||
|
||||
self.clearData = () => {
|
||||
console.log('connection manager clearing data');
|
||||
|
||||
const credentials = credentialProvider.credentials();
|
||||
credentials.Servers = [];
|
||||
credentialProvider.credentials(credentials);
|
||||
};
|
||||
|
||||
self._getOrAddApiClient = (server, serverUrl) => {
|
||||
let apiClient = self.getApiClient(server.Id);
|
||||
|
||||
if (!apiClient) {
|
||||
apiClient = new ApiClient(serverUrl, appName, appVersion, deviceName, deviceId);
|
||||
|
||||
self._apiClients.push(apiClient);
|
||||
|
||||
apiClient.serverInfo(server);
|
||||
|
||||
apiClient.onAuthenticated = (instance, result) => {
|
||||
return onAuthenticated(instance, result, {}, true);
|
||||
};
|
||||
|
||||
events.trigger(self, 'apiclientcreated', [apiClient]);
|
||||
}
|
||||
|
||||
console.log('returning instance from getOrAddApiClient');
|
||||
return apiClient;
|
||||
};
|
||||
|
||||
self.getOrCreateApiClient = (serverId) => {
|
||||
const credentials = credentialProvider.credentials();
|
||||
const servers = credentials.Servers.filter((s) => equalsIgnoreCase(s.Id, serverId));
|
||||
|
||||
if (!servers.length) {
|
||||
throw new Error(`Server not found: ${serverId}`);
|
||||
}
|
||||
|
||||
const server = servers[0];
|
||||
|
||||
return self._getOrAddApiClient(server, getServerAddress(server, server.LastConnectionMode));
|
||||
};
|
||||
|
||||
function onAuthenticated(apiClient, result, options, saveCredentials) {
|
||||
const credentials = credentialProvider.credentials();
|
||||
const servers = credentials.Servers.filter((s) => s.Id === result.ServerId);
|
||||
|
||||
const server = servers.length ? servers[0] : apiClient.serverInfo();
|
||||
|
||||
if (options.updateDateLastAccessed !== false) {
|
||||
server.DateLastAccessed = new Date().getTime();
|
||||
}
|
||||
server.Id = result.ServerId;
|
||||
|
||||
if (saveCredentials) {
|
||||
server.UserId = result.User.Id;
|
||||
server.AccessToken = result.AccessToken;
|
||||
} else {
|
||||
server.UserId = null;
|
||||
server.AccessToken = null;
|
||||
}
|
||||
|
||||
credentialProvider.addOrUpdateServer(credentials.Servers, server);
|
||||
credentialProvider.credentials(credentials);
|
||||
|
||||
// set this now before updating server info, otherwise it won't be set in time
|
||||
apiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection;
|
||||
|
||||
apiClient.serverInfo(server);
|
||||
apiClient.setAuthenticationInfo(result.AccessToken, result.User.Id);
|
||||
afterConnected(apiClient, options);
|
||||
|
||||
return onLocalUserSignIn(server, apiClient.serverAddress(), result.User);
|
||||
}
|
||||
|
||||
function afterConnected(apiClient, options = {}) {
|
||||
if (options.reportCapabilities !== false) {
|
||||
apiClient.reportCapabilities(capabilities);
|
||||
}
|
||||
apiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection;
|
||||
|
||||
if (options.enableWebSocket !== false) {
|
||||
console.log('calling apiClient.ensureWebSocket');
|
||||
|
||||
apiClient.ensureWebSocket();
|
||||
}
|
||||
}
|
||||
|
||||
function onLocalUserSignIn(server, serverUrl, user) {
|
||||
// Ensure this is created so that listeners of the event can get the apiClient instance
|
||||
self._getOrAddApiClient(server, serverUrl);
|
||||
|
||||
// This allows the app to have a single hook that fires before any other
|
||||
const promise = self.onLocalUserSignedIn ? self.onLocalUserSignedIn.call(self, user) : Promise.resolve();
|
||||
|
||||
return promise.then(() => {
|
||||
events.trigger(self, 'localusersignedin', [user]);
|
||||
});
|
||||
}
|
||||
|
||||
function validateAuthentication(server, serverUrl) {
|
||||
return ajax({
|
||||
type: 'GET',
|
||||
url: `${serverUrl}/System/Info`,
|
||||
dataType: 'json',
|
||||
headers: {
|
||||
[AUTHORIZATION_HEADER]: getAuthorizationHeader(
|
||||
{
|
||||
name: appName,
|
||||
version: appVersion
|
||||
},
|
||||
{
|
||||
id: deviceId,
|
||||
name: deviceName
|
||||
},
|
||||
server.AccessToken
|
||||
)
|
||||
}
|
||||
}).then(
|
||||
(systemInfo) => {
|
||||
updateServerInfo(server, systemInfo);
|
||||
return Promise.resolve();
|
||||
},
|
||||
() => {
|
||||
server.UserId = null;
|
||||
server.AccessToken = null;
|
||||
return Promise.resolve();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getImageUrl(localUser) {
|
||||
if (localUser && localUser.PrimaryImageTag) {
|
||||
const apiClient = self.getApiClient(localUser);
|
||||
|
||||
const url = apiClient.getUserImageUrl(localUser.Id, {
|
||||
tag: localUser.PrimaryImageTag,
|
||||
type: 'Primary'
|
||||
});
|
||||
|
||||
return {
|
||||
url,
|
||||
supportsParams: true
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
url: null,
|
||||
supportsParams: false
|
||||
};
|
||||
}
|
||||
|
||||
self.user = (apiClient) =>
|
||||
new Promise((resolve) => {
|
||||
let localUser;
|
||||
|
||||
function onLocalUserDone() {
|
||||
if (apiClient && apiClient.getCurrentUserId()) {
|
||||
apiClient.getCurrentUser().then((u) => {
|
||||
localUser = u;
|
||||
const image = getImageUrl(localUser);
|
||||
|
||||
resolve({
|
||||
localUser,
|
||||
name: localUser ? localUser.Name : null,
|
||||
imageUrl: image.url,
|
||||
supportsImageParams: image.supportsParams
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (apiClient && apiClient.getCurrentUserId()) {
|
||||
onLocalUserDone();
|
||||
}
|
||||
});
|
||||
|
||||
self.logout = () => {
|
||||
const promises = [];
|
||||
|
||||
for (let i = 0, length = self._apiClients.length; i < length; i++) {
|
||||
const apiClient = self._apiClients[i];
|
||||
|
||||
if (apiClient.accessToken()) {
|
||||
promises.push(logoutOfServer(apiClient));
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
const credentials = credentialProvider.credentials();
|
||||
|
||||
const servers = credentials.Servers.filter((u) => u.UserLinkType !== 'Guest');
|
||||
|
||||
for (let j = 0, numServers = servers.length; j < numServers; j++) {
|
||||
const server = servers[j];
|
||||
|
||||
server.UserId = null;
|
||||
server.AccessToken = null;
|
||||
server.ExchangeToken = null;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function logoutOfServer(apiClient) {
|
||||
const serverInfo = apiClient.serverInfo() || {};
|
||||
|
||||
const logoutInfo = {
|
||||
serverId: serverInfo.Id
|
||||
};
|
||||
|
||||
return apiClient.logout().then(
|
||||
() => {
|
||||
events.trigger(self, 'localusersignedout', [logoutInfo]);
|
||||
},
|
||||
() => {
|
||||
events.trigger(self, 'localusersignedout', [logoutInfo]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
self.getSavedServers = () => {
|
||||
const credentials = credentialProvider.credentials();
|
||||
|
||||
const servers = credentials.Servers.slice(0);
|
||||
|
||||
servers.sort((a, b) => (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0));
|
||||
|
||||
return servers;
|
||||
};
|
||||
|
||||
self.getAvailableServers = () => {
|
||||
console.log('Begin getAvailableServers');
|
||||
|
||||
// Clone the array
|
||||
const credentials = credentialProvider.credentials();
|
||||
|
||||
return Promise.all([findServers()]).then((responses) => {
|
||||
const foundServers = responses[0];
|
||||
const servers = credentials.Servers.slice(0);
|
||||
foundServers.forEach(server => {
|
||||
credentialProvider.addOrUpdateServer(servers, server);
|
||||
});
|
||||
|
||||
servers.sort((a, b) => (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0));
|
||||
credentials.Servers = servers;
|
||||
credentialProvider.credentials(credentials);
|
||||
|
||||
return servers;
|
||||
});
|
||||
};
|
||||
|
||||
function findServers() {
|
||||
return new Promise((resolve) => {
|
||||
const onFinish = function (foundServers) {
|
||||
const servers = foundServers.map((foundServer) => {
|
||||
const info = {
|
||||
Id: foundServer.Id,
|
||||
LocalAddress: convertEndpointAddressToManualAddress(foundServer) || foundServer.Address,
|
||||
Name: foundServer.Name
|
||||
};
|
||||
info.LastConnectionMode = info.ManualAddress ? ConnectionMode.Manual : ConnectionMode.Local;
|
||||
return info;
|
||||
});
|
||||
resolve(servers);
|
||||
};
|
||||
|
||||
if (window && window.NativeShell && typeof window.NativeShell.findServers === 'function') {
|
||||
window.NativeShell.findServers(1e3).then(onFinish, function () {
|
||||
onFinish([]);
|
||||
});
|
||||
} else {
|
||||
resolve([]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function convertEndpointAddressToManualAddress(info) {
|
||||
if (info.Address && info.EndpointAddress) {
|
||||
let address = info.EndpointAddress.split(':')[0];
|
||||
|
||||
// Determine the port, if any
|
||||
const parts = info.Address.split(':');
|
||||
if (parts.length > 1) {
|
||||
const portString = parts[parts.length - 1];
|
||||
|
||||
if (!isNaN(parseInt(portString, 10))) {
|
||||
address += `:${portString}`;
|
||||
}
|
||||
}
|
||||
|
||||
return normalizeAddress(address);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
self.connectToServers = (servers, options) => {
|
||||
console.log(`Begin connectToServers, with ${servers.length} servers`);
|
||||
|
||||
const firstServer = servers.length ? servers[0] : null;
|
||||
// See if we have any saved credentials and can auto sign in
|
||||
if (firstServer) {
|
||||
return self.connectToServer(firstServer, options).then((result) => {
|
||||
if (result.State === ConnectionState.Unavailable) {
|
||||
result.State = ConnectionState.ServerSelection;
|
||||
}
|
||||
|
||||
console.log('resolving connectToServers with result.State: ' + result.State);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve({
|
||||
Servers: servers,
|
||||
State: ConnectionState.ServerSelection
|
||||
});
|
||||
};
|
||||
|
||||
function getTryConnectPromise(url, connectionMode, state, resolve, reject) {
|
||||
console.log('getTryConnectPromise ' + url);
|
||||
|
||||
ajax({
|
||||
url: `${url}/System/Info/Public`,
|
||||
timeout: DEFAULT_CONNECTION_TIMEOUT,
|
||||
type: 'GET',
|
||||
dataType: 'json'
|
||||
}).then(
|
||||
(result) => {
|
||||
if (!state.resolved) {
|
||||
state.resolved = true;
|
||||
|
||||
console.log('Reconnect succeeded to ' + url);
|
||||
resolve({
|
||||
url: url,
|
||||
connectionMode: connectionMode,
|
||||
data: result
|
||||
});
|
||||
}
|
||||
},
|
||||
() => {
|
||||
console.log('Reconnect failed to ' + url);
|
||||
|
||||
if (!state.resolved) {
|
||||
state.rejects++;
|
||||
if (state.rejects >= state.numAddresses) {
|
||||
reject();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function tryReconnect(serverInfo) {
|
||||
const addresses = [];
|
||||
const addressesStrings = [];
|
||||
|
||||
// the timeouts are a small hack to try and ensure the remote address doesn't resolve first
|
||||
|
||||
// manualAddressOnly is used for the local web app that always connects to a fixed address
|
||||
if (
|
||||
!serverInfo.manualAddressOnly
|
||||
&& serverInfo.LocalAddress
|
||||
&& addressesStrings.indexOf(serverInfo.LocalAddress) === -1
|
||||
) {
|
||||
addresses.push({
|
||||
url: serverInfo.LocalAddress,
|
||||
mode: ConnectionMode.Local,
|
||||
timeout: 0
|
||||
});
|
||||
addressesStrings.push(addresses[addresses.length - 1].url);
|
||||
}
|
||||
if (serverInfo.ManualAddress && addressesStrings.indexOf(serverInfo.ManualAddress) === -1) {
|
||||
addresses.push({
|
||||
url: serverInfo.ManualAddress,
|
||||
mode: ConnectionMode.Manual,
|
||||
timeout: 100
|
||||
});
|
||||
addressesStrings.push(addresses[addresses.length - 1].url);
|
||||
}
|
||||
if (
|
||||
!serverInfo.manualAddressOnly
|
||||
&& serverInfo.RemoteAddress
|
||||
&& addressesStrings.indexOf(serverInfo.RemoteAddress) === -1
|
||||
) {
|
||||
addresses.push({
|
||||
url: serverInfo.RemoteAddress,
|
||||
mode: ConnectionMode.Remote,
|
||||
timeout: 200
|
||||
});
|
||||
addressesStrings.push(addresses[addresses.length - 1].url);
|
||||
}
|
||||
|
||||
console.log('tryReconnect: ' + addressesStrings.join('|'));
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const state = {};
|
||||
state.numAddresses = addresses.length;
|
||||
state.rejects = 0;
|
||||
|
||||
addresses.forEach((url) => {
|
||||
setTimeout(() => {
|
||||
if (!state.resolved) {
|
||||
getTryConnectPromise(url.url, url.mode, state, resolve, reject);
|
||||
}
|
||||
}, url.timeout);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
self.connectToServer = (server, options) => {
|
||||
console.log('begin connectToServer');
|
||||
|
||||
return new Promise((resolve) => {
|
||||
options = options || {};
|
||||
|
||||
tryReconnect(server).then(
|
||||
(result) => {
|
||||
const serverUrl = result.url;
|
||||
const connectionMode = result.connectionMode;
|
||||
result = result.data;
|
||||
|
||||
if (compareVersions(self.minServerVersion(), result.Version) === 1) {
|
||||
console.log('minServerVersion requirement not met. Server version: ' + result.Version);
|
||||
resolve({
|
||||
State: ConnectionState.ServerUpdateNeeded,
|
||||
Servers: [server]
|
||||
});
|
||||
} else if (server.Id && result.Id !== server.Id) {
|
||||
console.log(
|
||||
'http request succeeded, but found a different server Id than what was expected'
|
||||
);
|
||||
resolve({
|
||||
State: ConnectionState.Unavailable
|
||||
});
|
||||
} else {
|
||||
onSuccessfulConnection(server, result, connectionMode, serverUrl, true, resolve, options);
|
||||
}
|
||||
},
|
||||
() => {
|
||||
resolve({
|
||||
State: ConnectionState.Unavailable
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
function onSuccessfulConnection(server, systemInfo, connectionMode, serverUrl, verifyLocalAuthentication, resolve, options = {}) {
|
||||
const credentials = credentialProvider.credentials();
|
||||
|
||||
if (options.enableAutoLogin === false) {
|
||||
server.UserId = null;
|
||||
server.AccessToken = null;
|
||||
} else if (server.AccessToken && verifyLocalAuthentication) {
|
||||
void validateAuthentication(server, serverUrl).then(function () {
|
||||
onSuccessfulConnection(server, systemInfo, connectionMode, serverUrl, false, resolve, options);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
updateServerInfo(server, systemInfo);
|
||||
|
||||
server.LastConnectionMode = connectionMode;
|
||||
|
||||
if (options.updateDateLastAccessed !== false) {
|
||||
server.DateLastAccessed = new Date().getTime();
|
||||
}
|
||||
credentialProvider.addOrUpdateServer(credentials.Servers, server);
|
||||
credentialProvider.credentials(credentials);
|
||||
|
||||
const result = {
|
||||
Servers: []
|
||||
};
|
||||
|
||||
result.ApiClient = self._getOrAddApiClient(server, serverUrl);
|
||||
|
||||
result.ApiClient.setSystemInfo(systemInfo);
|
||||
result.SystemInfo = systemInfo;
|
||||
|
||||
result.State = server.AccessToken && options.enableAutoLogin !== false ? ConnectionState.SignedIn : ConnectionState.ServerSignIn;
|
||||
|
||||
result.Servers.push(server);
|
||||
|
||||
// set this now before updating server info, otherwise it won't be set in time
|
||||
result.ApiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection;
|
||||
|
||||
result.ApiClient.updateServerInfo(server, serverUrl);
|
||||
result.ApiClient.setAuthenticationInfo(server.AccessToken, server.UserId);
|
||||
|
||||
const resolveActions = function () {
|
||||
resolve(result);
|
||||
|
||||
events.trigger(self, 'connected', [result]);
|
||||
};
|
||||
|
||||
if (result.State === ConnectionState.SignedIn) {
|
||||
afterConnected(result.ApiClient, options);
|
||||
|
||||
result.ApiClient.getCurrentUser().then((user) => {
|
||||
onLocalUserSignIn(server, serverUrl, user).then(resolveActions, resolveActions);
|
||||
}, resolveActions);
|
||||
} else {
|
||||
resolveActions();
|
||||
}
|
||||
}
|
||||
|
||||
function tryConnectToAddress(address, options) {
|
||||
const server = {
|
||||
ManualAddress: address,
|
||||
LastConnectionMode: ConnectionMode.Manual
|
||||
};
|
||||
|
||||
return self.connectToServer(server, options).then((result) => {
|
||||
// connectToServer never rejects, but resolves with State=ConnectionState.Unavailable
|
||||
if (result.State === ConnectionState.Unavailable) {
|
||||
return Promise.reject();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
self.connectToAddress = function (address, options) {
|
||||
if (!address) {
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
address = normalizeAddress(address);
|
||||
|
||||
const urls = [];
|
||||
|
||||
if (/^[^:]+:\/\//.test(address)) {
|
||||
// Protocol specified - connect as is
|
||||
urls.push(address);
|
||||
} else {
|
||||
urls.push(`https://${address}`);
|
||||
urls.push(`http://${address}`);
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
|
||||
function onFail() {
|
||||
console.log(`connectToAddress ${urls[i]} failed`);
|
||||
|
||||
if (++i < urls.length) {
|
||||
return tryConnectToAddress(urls[i], options).catch(onFail);
|
||||
}
|
||||
|
||||
return Promise.resolve({
|
||||
State: ConnectionState.Unavailable
|
||||
});
|
||||
}
|
||||
|
||||
return tryConnectToAddress(urls[i], options).catch(onFail);
|
||||
};
|
||||
|
||||
self.deleteServer = (serverId) => {
|
||||
if (!serverId) {
|
||||
throw new Error('null serverId');
|
||||
}
|
||||
|
||||
let server = credentialProvider.credentials().Servers.filter((s) => s.Id === serverId);
|
||||
server = server.length ? server[0] : null;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
function onDone() {
|
||||
const credentials = credentialProvider.credentials();
|
||||
|
||||
credentials.Servers = credentials.Servers.filter((s) => s.Id !== serverId);
|
||||
|
||||
credentialProvider.credentials(credentials);
|
||||
resolve();
|
||||
}
|
||||
|
||||
if (!server.ConnectServerId) {
|
||||
onDone();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
connect(options) {
|
||||
console.log('Begin connect');
|
||||
|
||||
return this.getAvailableServers().then((servers) => {
|
||||
return this.connectToServers(servers, options);
|
||||
});
|
||||
}
|
||||
|
||||
handleMessageReceived(msg) {
|
||||
const serverId = msg.ServerId;
|
||||
if (serverId) {
|
||||
const apiClient = this.getApiClient(serverId);
|
||||
if (apiClient) {
|
||||
if (typeof msg.Data === 'string') {
|
||||
try {
|
||||
msg.Data = JSON.parse(msg.Data);
|
||||
} catch (err) {
|
||||
console.log('unable to parse json content: ' + err);
|
||||
}
|
||||
}
|
||||
|
||||
apiClient.handleMessageReceived(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getApiClients() {
|
||||
const servers = this.getSavedServers();
|
||||
|
||||
for (let i = 0, length = servers.length; i < length; i++) {
|
||||
const server = servers[i];
|
||||
if (server.Id) {
|
||||
this._getOrAddApiClient(server, getServerAddress(server, server.LastConnectionMode));
|
||||
}
|
||||
}
|
||||
|
||||
return this._apiClients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ApiClient for a given BaseItem or ServerId.
|
||||
* @param {import('@jellyfin/sdk/lib/generated-client').BaseItemDto | string | undefined} item
|
||||
* @returns {ApiClient}
|
||||
*/
|
||||
getApiClient(item) {
|
||||
if (!item) {
|
||||
throw new Error('item or serverId cannot be null');
|
||||
}
|
||||
|
||||
// Accept string + object
|
||||
if (item.ServerId) {
|
||||
item = item.ServerId;
|
||||
}
|
||||
|
||||
return this._apiClients.filter((a) => {
|
||||
const serverInfo = a.serverInfo();
|
||||
|
||||
// We have to keep this hack in here because of the addApiClient method
|
||||
return !serverInfo || serverInfo.Id === item;
|
||||
})[0];
|
||||
}
|
||||
|
||||
minServerVersion(val) {
|
||||
if (val) {
|
||||
this._minServerVersion = val;
|
||||
}
|
||||
|
||||
return this._minServerVersion;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/** The mode used to connect to a server. */
|
||||
export const enum ConnectionMode {
|
||||
Local = 0,
|
||||
Remote = 1,
|
||||
Manual = 2
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Server state values for a connected server used by jellyfin-apiclient.
|
||||
*/
|
||||
export enum ConnectionState {
|
||||
export const enum ConnectionState {
|
||||
SignedIn = 'SignedIn',
|
||||
ServerSignIn = 'ServerSignIn',
|
||||
ServerSelection = 'ServerSelection',
|
||||
@@ -0,0 +1,3 @@
|
||||
export { ConnectionMode } from './connectionMode';
|
||||
export { ConnectionState } from './connectionState';
|
||||
export { default as ServerConnections } from './ServerConnections';
|
||||
@@ -0,0 +1,22 @@
|
||||
export function compareVersions(a: string = '', b: string = '') {
|
||||
// -1 a is smaller
|
||||
// 1 a is larger
|
||||
// 0 equal
|
||||
const aParts = a.split('.');
|
||||
const bParts = b.split('.');
|
||||
|
||||
for (let i = 0, length = Math.max(aParts.length, bParts.length); i < length; i++) {
|
||||
const aVal = parseInt(aParts[i] || '0', 10);
|
||||
const bVal = parseInt(bParts[i] || '0', 10);
|
||||
|
||||
if (aVal < bVal) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (aVal > bVal) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
import { PluginType } from '../../types/plugin.ts';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import * as userSettings from 'scripts/settings/userSettings';
|
||||
import { PluginType } from 'types/plugin.ts';
|
||||
|
||||
class BackdropScreensaver {
|
||||
constructor() {
|
||||
|
||||
@@ -3,10 +3,10 @@ import 'material-design-icons-iconfont';
|
||||
import loading from '../../components/loading/loading';
|
||||
import keyboardnavigation from '../../scripts/keyboardNavigation';
|
||||
import dialogHelper from '../../components/dialogHelper/dialogHelper';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
import Screenfull from 'screenfull';
|
||||
import TableOfContents from './tableOfContents';
|
||||
import { translateHtml } from '../../lib/globalize';
|
||||
import { ServerConnections } from 'lib/jellyfin-apiclient';
|
||||
import browser from 'scripts/browser';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
import TouchHelper from 'scripts/touchHelper';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user