diff --git a/.ci/azure-pipelines-build.yml b/.ci/azure-pipelines-build.yml index 128fe54605..fb08254216 100644 --- a/.ci/azure-pipelines-build.yml +++ b/.ci/azure-pipelines-build.yml @@ -8,8 +8,6 @@ jobs: BuildConfiguration: development Production: BuildConfiguration: production - Standalone: - BuildConfiguration: standalone pool: vmImage: 'ubuntu-latest' @@ -21,15 +19,15 @@ jobs: versionSpec: '12.x' - task: Cache@2 - displayName: 'Check Cache' + displayName: 'Cache node_modules' inputs: key: 'yarn | yarn.lock' path: 'node_modules' - cacheHitVar: CACHE_RESTORED - script: 'yarn install --frozen-lockfile' displayName: 'Install Dependencies' - condition: ne(variables.CACHE_RESTORED, 'true') + env: + SKIP_PREPARE: 'true' - script: 'yarn build:development' displayName: 'Build Development' @@ -39,10 +37,6 @@ jobs: displayName: 'Build Production' condition: eq(variables['BuildConfiguration'], 'production') - - script: 'yarn build:standalone' - displayName: 'Build Standalone' - condition: eq(variables['BuildConfiguration'], 'standalone') - - script: 'test -d dist' displayName: 'Check Build' diff --git a/.ci/azure-pipelines-lint.yml b/.ci/azure-pipelines-lint.yml index 1e4bddbd04..8d9efbd73a 100644 --- a/.ci/azure-pipelines-lint.yml +++ b/.ci/azure-pipelines-lint.yml @@ -12,15 +12,15 @@ jobs: versionSpec: '12.x' - task: Cache@2 - displayName: 'Check Cache' + displayName: 'Cache node_modules' inputs: key: 'yarn | yarn.lock' path: 'node_modules' - cacheHitVar: CACHE_RESTORED - script: 'yarn install --frozen-lockfile' displayName: 'Install Dependencies' - condition: ne(variables.CACHE_RESTORED, 'true') + env: + SKIP_PREPARE: 'true' - script: 'yarn run lint --quiet' displayName: 'Run ESLint' diff --git a/.eslintrc.js b/.eslintrc.js index e5ee2dfe86..aabfd633f8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,7 +25,6 @@ module.exports = { 'eslint:recommended', // 'plugin:promise/recommended', 'plugin:import/errors', - 'plugin:import/warnings', 'plugin:eslint-comments/recommended', 'plugin:compat/recommended' ], @@ -79,16 +78,12 @@ module.exports = { // Dependency globals '$': 'readonly', 'jQuery': 'readonly', - 'requirejs': 'readonly', // Jellyfin globals 'ApiClient': 'writable', - 'AppInfo': 'writable', 'chrome': 'writable', 'DlnaProfilePage': 'writable', - 'Dashboard': 'writable', 'DashboardPage': 'writable', 'Emby': 'readonly', - 'Events': 'writable', 'getParameterByName': 'writable', 'getWindowLocationSearch': 'writable', 'Globalize': 'writable', @@ -98,8 +93,6 @@ module.exports = { 'LinkParser': 'writable', 'LiveTvHelpers': 'writable', 'MetadataEditor': 'writable', - 'pageClassOn': 'writable', - 'pageIdOn': 'writable', 'PlaylistViewer': 'writable', 'UserParentalControlPage': 'writable', 'Windows': 'readonly' diff --git a/package.json b/package.json index 45d1a83fe7..c7da5adb11 100644 --- a/package.json +++ b/package.json @@ -10,14 +10,13 @@ "@babel/eslint-plugin": "^7.12.1", "@babel/plugin-proposal-class-properties": "^7.10.1", "@babel/plugin-proposal-private-methods": "^7.12.1", - "@babel/plugin-transform-modules-amd": "^7.12.1", - "@babel/polyfill": "^7.12.1", "@babel/preset-env": "^7.12.1", "autoprefixer": "^9.8.6", "babel-loader": "^8.2.1", "browser-sync": "^2.26.13", + "clean-webpack-plugin": "^3.0.0", "confusing-browser-globals": "^1.0.10", - "copy-webpack-plugin": "^5.1.1", + "copy-webpack-plugin": "^6.0.3", "css-loader": "^5.0.1", "cssnano": "^4.1.10", "del": "^6.0.0", @@ -26,6 +25,7 @@ "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-promise": "^4.2.1", + "expose-loader": "^1.0.1", "file-loader": "^6.2.0", "gulp": "^4.0.2", "gulp-babel": "^8.0.0", @@ -40,19 +40,24 @@ "gulp-sass": "^4.0.2", "gulp-sourcemaps": "^3.0.0", "gulp-terser": "^1.4.1", + "html-loader": "^1.1.0", "html-webpack-plugin": "^4.5.0", "lazypipe": "^1.0.2", "node-sass": "^5.0.0", "postcss-loader": "^3.0.0", "postcss-preset-env": "^6.7.0", + "source-map-loader": "^1.1.1", "style-loader": "^2.0.0", "stylelint": "^13.7.2", "stylelint-config-rational-order": "^0.1.2", "stylelint-no-browser-hacks": "^1.2.1", "stylelint-order": "^4.1.0", "webpack": "^5.4.0", + "webpack-cli": "^4.0.0", + "webpack-dev-server": "^3.11.0", "webpack-merge": "^4.2.2", "webpack-stream": "^6.1.1", + "workbox-webpack-plugin": "^5.1.4", "worker-plugin": "^5.0.0" }, "dependencies": { @@ -62,7 +67,6 @@ "core-js": "^3.7.0", "date-fns": "^2.16.1", "epubjs": "^0.3.85", - "pdfjs-dist": "2.5.207", "fast-text-encoding": "^1.0.3", "flv.js": "^1.5.0", "headroom.js": "^0.12.0", @@ -70,6 +74,7 @@ "howler": "^2.2.1", "intersection-observer": "^0.11.0", "jellyfin-apiclient": "^1.4.2", + "jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto", "jquery": "^3.5.1", "jstree": "^3.3.10", "libarchive.js": "^1.3.0", @@ -77,302 +82,31 @@ "material-design-icons-iconfont": "^6.1.0", "native-promise-only": "^0.8.0-a", "page": "^1.11.6", - "query-string": "^6.13.7", + "pdfjs-dist": "2.5.207", "resize-observer-polyfill": "^1.5.1", + "sass": "^1.29.0", + "sass-loader": "^10.0.5", "screenfull": "^5.0.2", "sortablejs": "^1.12.0", "swiper": "^6.3.5", "webcomponents.js": "^0.7.24", - "whatwg-fetch": "^3.5.0" + "whatwg-fetch": "^3.5.0", + "workbox-core": "^5.1.4", + "workbox-precaching": "^5.1.4" }, "babel": { "presets": [ - "@babel/preset-env" + [ + "@babel/preset-env", + { + "useBuiltIns": "usage", + "corejs": 3 + } + ] ], - "overrides": [ - { - "test": [ - "src/components/accessSchedule/accessSchedule.js", - "src/components/actionSheet/actionSheet.js", - "src/components/activitylog.js", - "src/components/alert.js", - "src/components/alphaPicker/alphaPicker.js", - "src/components/appFooter/appFooter.js", - "src/components/apphost.js", - "src/components/appRouter.js", - "src/components/autoFocuser.js", - "src/components/backdrop/backdrop.js", - "src/components/cardbuilder/cardBuilder.js", - "src/components/cardbuilder/chaptercardbuilder.js", - "src/components/cardbuilder/peoplecardbuilder.js", - "src/components/channelMapper/channelMapper.js", - "src/components/collectionEditor/collectionEditor.js", - "src/components/confirm/confirm.js", - "src/components/dialog/dialog.js", - "src/components/dialogHelper/dialogHelper.js", - "src/components/directorybrowser/directorybrowser.js", - "src/components/displaySettings/displaySettings.js", - "src/components/favoriteitems.js", - "src/components/fetchhelper.js", - "src/components/filterdialog/filterdialog.js", - "src/components/filtermenu/filtermenu.js", - "src/components/focusManager.js", - "src/components/groupedcards.js", - "src/components/guide/guide.js", - "src/components/guide/guide-settings.js", - "src/components/homeScreenSettings/homeScreenSettings.js", - "src/components/homesections/homesections.js", - "src/components/htmlMediaHelper.js", - "src/components/imageOptionsEditor/imageOptionsEditor.js", - "src/components/images/imageLoader.js", - "src/components/imageDownloader/imageDownloader.js", - "src/components/imageeditor/imageeditor.js", - "src/components/imageUploader/imageUploader.js", - "src/components/indicators/indicators.js", - "src/components/itemContextMenu.js", - "src/components/itemHelper.js", - "src/components/itemidentifier/itemidentifier.js", - "src/components/itemMediaInfo/itemMediaInfo.js", - "src/components/itemsrefresher.js", - "src/components/layoutManager.js", - "src/components/lazyLoader/lazyLoaderIntersectionObserver.js", - "src/components/libraryoptionseditor/libraryoptionseditor.js", - "src/components/listview/listview.js", - "src/components/loading/loading.js", - "src/components/maintabsmanager.js", - "src/components/mediainfo/mediainfo.js", - "src/components/mediaLibraryCreator/mediaLibraryCreator.js", - "src/components/mediaLibraryEditor/mediaLibraryEditor.js", - "src/components/metadataEditor/metadataEditor.js", - "src/components/metadataEditor/personEditor.js", - "src/components/multiSelect/multiSelect.js", - "src/components/notifications/notifications.js", - "src/components/nowPlayingBar/nowPlayingBar.js", - "src/components/packageManager.js", - "src/components/playback/brightnessosd.js", - "src/components/playback/mediasession.js", - "src/components/playback/nowplayinghelper.js", - "src/components/playback/playbackorientation.js", - "src/components/playback/playbackmanager.js", - "src/components/playback/playerSelectionMenu.js", - "src/components/playback/playersettingsmenu.js", - "src/components/playback/playmethodhelper.js", - "src/components/playback/playqueuemanager.js", - "src/components/playback/remotecontrolautoplay.js", - "src/components/playback/volumeosd.js", - "src/components/playbackSettings/playbackSettings.js", - "src/components/playerstats/playerstats.js", - "src/components/playlisteditor/playlisteditor.js", - "src/components/playmenu.js", - "src/components/pluginManager.js", - "src/components/prompt/prompt.js", - "src/components/qualityOptions.js", - "src/components/quickConnectSettings/quickConnectSettings.js", - "src/components/recordingcreator/recordingbutton.js", - "src/components/recordingcreator/recordingcreator.js", - "src/components/recordingcreator/seriesrecordingeditor.js", - "src/components/recordingcreator/recordinghelper.js", - "src/components/refreshdialog/refreshdialog.js", - "src/components/recordingcreator/recordingeditor.js", - "src/components/recordingcreator/recordingfields.js", - "src/components/remotecontrol/remotecontrol.js", - "src/components/sanatizefilename.js", - "src/components/scrollManager.js", - "src/plugins/experimentalWarnings/plugin.js", - "src/plugins/sessionPlayer/plugin.js", - "src/plugins/htmlAudioPlayer/plugin.js", - "src/plugins/comicsPlayer/plugin.js", - "src/plugins/chromecastPlayer/plugin.js", - "src/components/slideshow/slideshow.js", - "src/components/sortmenu/sortmenu.js", - "src/plugins/htmlVideoPlayer/plugin.js", - "src/plugins/logoScreensaver/plugin.js", - "src/plugins/playAccessValidation/plugin.js", - "src/components/search/searchfields.js", - "src/components/search/searchresults.js", - "src/components/settingshelper.js", - "src/components/shortcuts.js", - "src/components/subtitleeditor/subtitleeditor.js", - "src/components/subtitlesync/subtitlesync.js", - "src/components/subtitlesettings/subtitleappearancehelper.js", - "src/components/subtitlesettings/subtitlesettings.js", - "src/components/syncPlay/groupSelectionMenu.js", - "src/components/syncPlay/playbackPermissionManager.js", - "src/components/syncPlay/syncPlayManager.js", - "src/components/syncPlay/timeSyncManager.js", - "src/components/themeMediaPlayer.js", - "src/components/tabbedview/tabbedview.js", - "src/components/viewManager/viewManager.js", - "src/components/tvproviders/schedulesdirect.js", - "src/components/tvproviders/xmltv.js", - "src/components/toast/toast.js", - "src/components/tunerPicker.js", - "src/components/upnextdialog/upnextdialog.js", - "src/components/userdatabuttons/userdatabuttons.js", - "src/components/viewContainer.js", - "src/components/viewSettings/viewSettings.js", - "src/components/castSenderApi.js", - "src/controllers/session/addServer/index.js", - "src/controllers/session/forgotPassword/index.js", - "src/controllers/session/resetPassword/index.js", - "src/controllers/session/login/index.js", - "src/controllers/session/selectServer/index.js", - "src/controllers/dashboard/apikeys.js", - "src/controllers/dashboard/dashboard.js", - "src/controllers/dashboard/devices/device.js", - "src/controllers/dashboard/devices/devices.js", - "src/controllers/dashboard/dlna/profile.js", - "src/controllers/dashboard/dlna/profiles.js", - "src/controllers/dashboard/dlna/settings.js", - "src/controllers/dashboard/encodingsettings.js", - "src/controllers/dashboard/general.js", - "src/controllers/dashboard/librarydisplay.js", - "src/controllers/dashboard/logs.js", - "src/controllers/music/musicalbums.js", - "src/controllers/music/musicartists.js", - "src/controllers/music/musicgenres.js", - "src/controllers/music/musicplaylists.js", - "src/controllers/music/musicrecommended.js", - "src/controllers/music/songs.js", - "src/controllers/dashboard/library.js", - "src/controllers/dashboard/metadataImages.js", - "src/controllers/dashboard/metadatanfo.js", - "src/controllers/dashboard/networking.js", - "src/controllers/dashboard/notifications/notification/index.js", - "src/controllers/dashboard/notifications/notifications/index.js", - "src/controllers/dashboard/playback.js", - "src/controllers/dashboard/plugins/add/index.js", - "src/controllers/dashboard/plugins/installed/index.js", - "src/controllers/dashboard/plugins/available/index.js", - "src/controllers/dashboard/plugins/repositories/index.js", - "src/controllers/dashboard/quickConnect.js", - "src/controllers/dashboard/scheduledtasks/scheduledtask.js", - "src/controllers/dashboard/scheduledtasks/scheduledtasks.js", - "src/controllers/dashboard/serveractivity.js", - "src/controllers/dashboard/streaming.js", - "src/controllers/dashboard/users/useredit.js", - "src/controllers/dashboard/users/userlibraryaccess.js", - "src/controllers/dashboard/users/usernew.js", - "src/controllers/dashboard/users/userparentalcontrol.js", - "src/controllers/dashboard/users/userpasswordpage.js", - "src/controllers/dashboard/users/userprofilespage.js", - "src/controllers/home.js", - "src/controllers/list.js", - "src/controllers/edititemmetadata.js", - "src/controllers/favorites.js", - "src/controllers/hometab.js", - "src/controllers/movies/moviecollections.js", - "src/controllers/movies/moviegenres.js", - "src/controllers/movies/movies.js", - "src/controllers/movies/moviesrecommended.js", - "src/controllers/movies/movietrailers.js", - "src/controllers/playback/nowplaying.js", - "src/controllers/playback/videoosd.js", - "src/controllers/itemDetails/index.js", - "src/controllers/playback/queue/index.js", - "src/controllers/playback/video/index.js", - "src/controllers/searchpage.js", - "src/controllers/livetv/livetvguide.js", - "src/controllers/livetvtuner.js", - "src/controllers/livetv/livetvsuggested.js", - "src/controllers/livetvstatus.js", - "src/controllers/livetvguideprovider.js", - "src/controllers/livetvsettings.js", - "src/controllers/livetv/livetvrecordings.js", - "src/controllers/livetv/livetvschedule.js", - "src/controllers/livetv/livetvseriestimers.js", - "src/controllers/livetv/livetvchannels.js", - "src/controllers/shows/episodes.js", - "src/controllers/shows/tvgenres.js", - "src/controllers/shows/tvlatest.js", - "src/controllers/shows/tvrecommended.js", - "src/controllers/shows/tvshows.js", - "src/controllers/shows/tvstudios.js", - "src/controllers/shows/tvupcoming.js", - "src/controllers/user/display/index.js", - "src/controllers/user/home/index.js", - "src/controllers/user/menu/index.js", - "src/controllers/user/playback/index.js", - "src/controllers/user/profile/index.js", - "src/controllers/user/quickConnect/index.js", - "src/controllers/user/subtitles/index.js", - "src/controllers/wizard/finish/index.js", - "src/controllers/wizard/remote/index.js", - "src/controllers/wizard/settings/index.js", - "src/controllers/wizard/start/index.js", - "src/controllers/wizard/user/index.js", - "src/elements/emby-button/emby-button.js", - "src/elements/emby-button/paper-icon-button-light.js", - "src/elements/emby-checkbox/emby-checkbox.js", - "src/elements/emby-collapse/emby-collapse.js", - "src/elements/emby-input/emby-input.js", - "src/elements/emby-itemrefreshindicator/emby-itemrefreshindicator.js", - "src/elements/emby-itemscontainer/emby-itemscontainer.js", - "src/elements/emby-playstatebutton/emby-playstatebutton.js", - "src/elements/emby-programcell/emby-programcell.js", - "src/elements/emby-progressbar/emby-progressbar.js", - "src/elements/emby-progressring/emby-progressring.js", - "src/elements/emby-radio/emby-radio.js", - "src/elements/emby-ratingbutton/emby-ratingbutton.js", - "src/elements/emby-scrollbuttons/emby-scrollbuttons.js", - "src/elements/emby-scroller/emby-scroller.js", - "src/elements/emby-select/emby-select.js", - "src/elements/emby-slider/emby-slider.js", - "src/elements/emby-tabs/emby-tabs.js", - "src/elements/emby-textarea/emby-textarea.js", - "src/elements/emby-toggle/emby-toggle.js", - "src/libraries/screensavermanager.js", - "src/libraries/navdrawer/navdrawer.js", - "src/libraries/scroller.js", - "src/plugins/backdropScreensaver/plugin.js", - "src/plugins/bookPlayer/plugin.js", - "src/plugins/pdfPlayer/plugin.js", - "src/plugins/bookPlayer/tableOfContents.js", - "src/plugins/chromecastPlayer/chromecastHelper.js", - "src/plugins/photoPlayer/plugin.js", - "src/plugins/youtubePlayer/plugin.js", - "src/scripts/alphanumericshortcuts.js", - "src/scripts/autoBackdrops.js", - "src/scripts/autocast.js", - "src/scripts/browser.js", - "src/scripts/clientUtils.js", - "src/scripts/datetime.js", - "src/scripts/deleteHelper.js", - "src/scripts/dfnshelper.js", - "src/scripts/dom.js", - "src/scripts/editorsidebar.js", - "src/scripts/fileDownloader.js", - "src/scripts/filesystem.js", - "src/scripts/globalize.js", - "src/scripts/imagehelper.js", - "src/scripts/itembynamedetailpage.js", - "src/scripts/inputManager.js", - "src/scripts/autoThemes.js", - "src/scripts/themeManager.js", - "src/scripts/keyboardNavigation.js", - "src/scripts/libraryMenu.js", - "src/scripts/libraryBrowser.js", - "src/scripts/livetvcomponents.js", - "src/scripts/mouseManager.js", - "src/scripts/multiDownload.js", - "src/scripts/playlists.js", - "src/scripts/scrollHelper.js", - "src/scripts/serverNotifications.js", - "src/scripts/routes.js", - "src/scripts/settings/appSettings.js", - "src/scripts/settings/userSettings.js", - "src/scripts/settings/webSettings.js", - "src/scripts/shell.js", - "src/scripts/taskbutton.js", - "src/scripts/themeLoader.js", - "src/scripts/touchHelper.js" - ], - "plugins": [ - "@babel/plugin-transform-modules-amd", - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-private-methods" - ] - } + "plugins": [ + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-private-methods" ] }, "browserslist": [ @@ -393,11 +127,10 @@ ], "scripts": { "start": "yarn serve", - "serve": "gulp serve --development", - "prepare": "gulp --production", - "build:development": "gulp --development", - "build:production": "gulp --production", - "build:standalone": "gulp standalone --development", + "serve": "webpack serve --config webpack.dev.js", + "prepare": "./scripts/prepare.sh", + "build:development": "webpack --config webpack.dev.js", + "build:production": "webpack --config webpack.prod.js", "lint": "eslint \".\"", "stylelint": "stylelint \"src/**/*.css\"" } diff --git a/scripts/prepare.sh b/scripts/prepare.sh new file mode 100755 index 0000000000..bde12b36a5 --- /dev/null +++ b/scripts/prepare.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +if [ -z "${SKIP_PREPARE}" ]; then + webpack --config webpack.prod.js +fi diff --git a/src/bundle.js b/src/bundle.js deleted file mode 100644 index 2693ede52b..0000000000 --- a/src/bundle.js +++ /dev/null @@ -1,182 +0,0 @@ -/** - * require.js module definitions bundled by webpack - */ -// Use define from require.js not webpack's define -const _define = window.define; - -// fetch -const fetch = require('whatwg-fetch'); -_define('fetch', function() { - return fetch; -}); - -// Blurhash -const blurhash = require('blurhash'); -_define('blurhash', function() { - return blurhash; -}); - -// query-string -const query = require('query-string'); -_define('queryString', function() { - return query; -}); - -// flvjs -const flvjs = require('flv.js/dist/flv').default; -_define('flvjs', function() { - return flvjs; -}); - -// jstree -const jstree = require('jstree'); -require('jstree/dist/themes/default/style.css'); -_define('jstree', function() { - return jstree; -}); - -// jquery -const jquery = require('jquery'); -_define('jQuery', function() { - return jquery; -}); - -// hlsjs -const hlsjs = require('hls.js'); -_define('hlsjs', function() { - return hlsjs; -}); - -// howler -const howler = require('howler'); -_define('howler', function() { - return howler; -}); - -// resize-observer-polyfill -const resize = require('resize-observer-polyfill').default; -_define('resize-observer-polyfill', function() { - return resize; -}); - -// swiper -const swiper = require('swiper/swiper-bundle'); -require('swiper/swiper-bundle.css'); -_define('swiper', function() { - return swiper; -}); - -// sortable -const sortable = require('sortablejs').default; -_define('sortable', function() { - return sortable; -}); - -// webcomponents -const webcomponents = require('webcomponents.js/webcomponents-lite'); -_define('webcomponents', function() { - return webcomponents; -}); - -// libass-wasm -const libassWasm = require('libass-wasm'); -_define('JavascriptSubtitlesOctopus', function() { - return libassWasm; -}); - -// material-icons -const materialIcons = require('material-design-icons-iconfont/dist/material-design-icons.css'); -_define('material-icons', function() { - return materialIcons; -}); - -const epubjs = require('epubjs'); -_define('epubjs', function () { - return epubjs; -}); - -const pdfjs = require('pdfjs-dist/build/pdf'); -_define('pdfjs', function () { - return pdfjs; -}); - -// page.js -const page = require('page'); -_define('page', function() { - return page; -}); - -// core-js -const polyfill = require('@babel/polyfill/dist/polyfill'); -_define('polyfill', function () { - return polyfill; -}); - -// domtokenlist-shim -const classlist = require('classlist.js'); -_define('classlist-polyfill', function () { - return classlist; -}); - -// Date-FNS -const dateFns = require('date-fns'); -_define('date-fns', function () { - return dateFns; -}); - -const dateFnsLocale = require('date-fns/locale'); -_define('date-fns/locale', function () { - return dateFnsLocale; -}); - -const fast_text_encoding = require('fast-text-encoding'); -_define('fast-text-encoding', function () { - return fast_text_encoding; -}); - -// intersection-observer -const intersection_observer = require('intersection-observer'); -_define('intersection-observer', function () { - return intersection_observer; -}); - -// screenfull -const screenfull = require('screenfull'); -_define('screenfull', function () { - return screenfull; -}); - -// headroom.js -const headroom = require('headroom.js/dist/headroom'); -_define('headroom', function () { - return headroom; -}); - -// apiclient -const apiclient = require('jellyfin-apiclient'); - -_define('apiclient', function () { - return apiclient.ApiClient; -}); - -_define('events', function () { - return apiclient.Events; -}); - -_define('credentialprovider', function () { - return apiclient.Credentials; -}); - -_define('connectionManagerFactory', function () { - return apiclient.ConnectionManager; -}); - -_define('appStorage', function () { - return apiclient.AppStorage; -}); - -// libarchive.js -const libarchive = require('libarchive.js'); -_define('libarchive', function () { - return libarchive; -}); diff --git a/src/components/AppInfo.js b/src/components/AppInfo.js new file mode 100644 index 0000000000..a89c55d0b1 --- /dev/null +++ b/src/components/AppInfo.js @@ -0,0 +1,4 @@ + +export default { + isNativeApp: false +}; diff --git a/src/components/ServerConnections.js b/src/components/ServerConnections.js new file mode 100644 index 0000000000..316f42558d --- /dev/null +++ b/src/components/ServerConnections.js @@ -0,0 +1,82 @@ +import { ConnectionManager, Credentials, ApiClient, Events } from 'jellyfin-apiclient'; +import { appHost } from './apphost'; +import Dashboard from '../scripts/clientUtils'; +import AppInfo from './AppInfo'; +import { setUserInfo } from '../scripts/settings/userSettings'; + +class ServerConnections extends ConnectionManager { + constructor() { + super(...arguments); + this.localApiClient = null; + + Events.on(this, 'localusersignedout', function () { + setUserInfo(null, null); + }); + } + + initApiClient() { + if (!AppInfo.isNativeApp) { + console.debug('creating ApiClient singleton'); + + const apiClient = new ApiClient( + Dashboard.serverAddress(), + appHost.appName(), + appHost.appVersion(), + appHost.deviceName(), + appHost.deviceId() + ); + + apiClient.enableAutomaticNetworking = false; + apiClient.manualAddressOnly = true; + + this.addApiClient(apiClient); + + this.setLocalApiClient(apiClient); + + console.debug('loaded ApiClient singleton'); + } + } + + setLocalApiClient(apiClient) { + if (apiClient) { + this.localApiClient = apiClient; + window.ApiClient = apiClient; + } + } + + getLocalApiClient() { + return this.localApiClient; + } + + currentApiClient() { + let apiClient = this.getLocalApiClient(); + + if (!apiClient) { + const server = this.getLastUsedServer(); + + if (server) { + apiClient = this.getApiClient(server.Id); + } + } + + return apiClient; + } + + onLocalUserSignedIn(user) { + const apiClient = this.getApiClient(user.ServerId); + this.setLocalApiClient(apiClient); + return setUserInfo(user.Id, apiClient); + } +} + +const credentials = new Credentials(); + +const capabilities = Dashboard.capabilities(appHost); + +export default new ServerConnections( + credentials, + appHost.appName(), + appHost.appVersion(), + appHost.deviceName(), + appHost.deviceId(), + capabilities); diff --git a/src/components/accessSchedule/accessSchedule.js b/src/components/accessSchedule/accessSchedule.js index b513766d0b..9e0e3d5cf9 100644 --- a/src/components/accessSchedule/accessSchedule.js +++ b/src/components/accessSchedule/accessSchedule.js @@ -1,3 +1,4 @@ + /* eslint-disable indent */ /** @@ -5,12 +6,12 @@ * @module components/accessSchedule/accessSchedule */ -import dialogHelper from 'dialogHelper'; -import datetime from 'datetime'; -import globalize from 'globalize'; -import 'emby-select'; -import 'paper-icon-button-light'; -import 'formDialogStyle'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import datetime from '../../scripts/datetime'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../formdialog.css'; function getDisplayTime(hours) { let minutes = 0; @@ -59,7 +60,7 @@ import 'formDialogStyle'; export function show(options) { return new Promise((resolve, reject) => { - import('text!./accessSchedule.template.html').then(({default: template}) => { + import('./accessSchedule.template.html').then(({default: template}) => { const dlg = dialogHelper.createDialog({ removeOnClose: true, size: 'small' diff --git a/src/components/actionSheet/actionSheet.js b/src/components/actionSheet/actionSheet.js index be84cf0a06..85df1b2c60 100644 --- a/src/components/actionSheet/actionSheet.js +++ b/src/components/actionSheet/actionSheet.js @@ -1,12 +1,12 @@ -import dialogHelper from 'dialogHelper'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import dom from 'dom'; -import 'emby-button'; -import 'css!./actionSheet'; -import 'material-icons'; -import 'scrollStyles'; -import 'listViewStyle'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import dom from '../../scripts/dom'; +import '../../elements/emby-button/emby-button'; +import './actionSheet.css'; +import 'material-design-icons-iconfont'; +import '../../assets/css/scrollstyles.css'; +import '../../components/listview/listview.css'; function getOffsets(elems) { const results = []; @@ -71,7 +71,7 @@ function getPosition(options, dlg) { } function centerFocus(elem, horiz, on) { - import('scrollHelper').then(({default: scrollHelper}) => { + import('../../scripts/scrollHelper').then((scrollHelper) => { const fn = on ? 'on' : 'off'; scrollHelper.centerFocus[fn](elem, horiz); }); diff --git a/src/components/activitylog.js b/src/components/activitylog.js index 3878d03355..78967b3a34 100644 --- a/src/components/activitylog.js +++ b/src/components/activitylog.js @@ -1,11 +1,13 @@ -import events from 'events'; -import globalize from 'globalize'; -import dom from 'dom'; +import { Events } from 'jellyfin-apiclient'; +import globalize from '../scripts/globalize'; +import dom from '../scripts/dom'; import * as datefns from 'date-fns'; -import dfnshelper from 'dfnshelper'; -import serverNotifications from 'serverNotifications'; -import 'emby-button'; -import 'listViewStyle'; +import dfnshelper from '../scripts/dfnshelper'; +import serverNotifications from '../scripts/serverNotifications'; +import '../elements/emby-button/emby-button'; +import './listview/listview.css'; +import ServerConnections from './ServerConnections'; +import alert from './alert'; /* eslint-disable indent */ @@ -127,10 +129,8 @@ import 'listViewStyle'; } function showItemOverview(item) { - import('alert').then(({default: alert}) => { - alert({ - text: item.Overview - }); + alert({ + text: item.Overview }); } @@ -140,11 +140,11 @@ class ActivityLog { const element = options.element; element.classList.add('activityLogListWidget'); element.addEventListener('click', onListClick.bind(this)); - const apiClient = window.connectionManager.getApiClient(options.serverId); + const apiClient = ServerConnections.getApiClient(options.serverId); reloadData(this, element, apiClient); const onUpdate = onActivityLogUpdate.bind(this); this.updateFn = onUpdate; - events.on(serverNotifications, 'ActivityLogEntry', onUpdate); + Events.on(serverNotifications, 'ActivityLogEntry', onUpdate); apiClient.sendMessage('ActivityLogEntryStart', '0,1500'); } destroy() { @@ -152,13 +152,13 @@ class ActivityLog { if (options) { options.element.classList.remove('activityLogListWidget'); - window.connectionManager.getApiClient(options.serverId).sendMessage('ActivityLogEntryStop', '0,1500'); + ServerConnections.getApiClient(options.serverId).sendMessage('ActivityLogEntryStop', '0,1500'); } const onUpdate = this.updateFn; if (onUpdate) { - events.off(serverNotifications, 'ActivityLogEntry', onUpdate); + Events.off(serverNotifications, 'ActivityLogEntry', onUpdate); } this.items = null; diff --git a/src/components/alert.js b/src/components/alert.js index 1420c7f428..2938cb7c70 100644 --- a/src/components/alert.js +++ b/src/components/alert.js @@ -1,6 +1,7 @@ -import browser from 'browser'; -import dialog from 'dialog'; -import globalize from 'globalize'; + +import browser from '../scripts/browser'; +import dialog from './dialog/dialog'; +import globalize from '../scripts/globalize'; /* eslint-disable indent */ diff --git a/src/components/alphaPicker/alphaPicker.js b/src/components/alphaPicker/alphaPicker.js index 95b5881677..9caa1b015c 100644 --- a/src/components/alphaPicker/alphaPicker.js +++ b/src/components/alphaPicker/alphaPicker.js @@ -5,12 +5,12 @@ * @module components/alphaPicker/alphaPicker */ -import focusManager from 'focusManager'; -import layoutManager from 'layoutManager'; -import dom from 'dom'; -import 'css!./style.css'; -import 'paper-icon-button-light'; -import 'material-icons'; +import focusManager from '../focusManager'; +import layoutManager from '../layoutManager'; +import dom from '../../scripts/dom'; +import './style.css'; +import '../../elements/emby-button/paper-icon-button-light'; +import 'material-design-icons-iconfont'; const selectedButtonClass = 'alphaPickerButton-selected'; diff --git a/src/components/appFooter/appFooter.js b/src/components/appFooter/appFooter.js index 260aea8280..3b65824490 100644 --- a/src/components/appFooter/appFooter.js +++ b/src/components/appFooter/appFooter.js @@ -1,4 +1,4 @@ -import 'css!./appFooter'; +import './appFooter.css'; function render(options) { const elem = document.createElement('div'); @@ -33,4 +33,4 @@ class appFooter { } } -export default appFooter; +export default new appFooter({}); diff --git a/src/components/appRouter.js b/src/components/appRouter.js index a6bb6da618..76a76e516a 100644 --- a/src/components/appRouter.js +++ b/src/components/appRouter.js @@ -1,13 +1,17 @@ -import appHost from 'apphost'; -import appSettings from 'appSettings'; -import backdrop from 'backdrop'; -import browser from 'browser'; -import events from 'events'; -import globalize from 'globalize'; -import itemHelper from 'itemHelper'; -import loading from 'loading'; +import { appHost } from './apphost'; +import appSettings from '../scripts/settings/appSettings'; +import backdrop from './backdrop/backdrop'; +import browser from '../scripts/browser'; +import { Events } from 'jellyfin-apiclient'; +import globalize from '../scripts/globalize'; +import itemHelper from './itemHelper'; +import loading from './loading/loading'; import page from 'page'; -import viewManager from 'viewManager'; +import viewManager from './viewManager/viewManager'; +import AppInfo from './AppInfo'; +import Dashboard from '../scripts/clientUtils'; +import ServerConnections from './ServerConnections'; +import alert from './alert'; class AppRouter { allRoutes = []; @@ -94,7 +98,7 @@ class AppRouter { beginConnectionWizard() { backdrop.clearBackdrop(); loading.show(); - window.connectionManager.connect({ + ServerConnections.connect({ enableAutoLogin: appSettings.enableAutoLogin() }).then((result) => { this.handleConnectionResult(result); @@ -150,10 +154,10 @@ class AppRouter { loading.show(); this.initApiClients(); - events.on(appHost, 'beforeexit', this.onBeforeExit); - events.on(appHost, 'resume', this.onAppResume); + Events.on(appHost, 'beforeexit', this.onBeforeExit); + Events.on(appHost, 'resume', this.onAppResume); - window.connectionManager.connect({ + ServerConnections.connect({ enableAutoLogin: appSettings.enableAutoLogin() }).then((result) => { this.firstConnectionResult = result; @@ -209,7 +213,7 @@ class AppRouter { showItem(item, serverId, options) { // TODO: Refactor this so it only gets items, not strings. if (typeof (item) === 'string') { - const apiClient = serverId ? window.connectionManager.getApiClient(serverId) : window.connectionManager.currentApiClient(); + const apiClient = serverId ? ServerConnections.getApiClient(serverId) : ServerConnections.currentApiClient(); apiClient.getItem(apiClient.getCurrentUserId(), item).then((itemObject) => { this.showItem(itemObject, options); }); @@ -268,7 +272,7 @@ class AppRouter { switch (result.State) { case 'SignedIn': loading.hide(); - Emby.Page.goHome(); + this.goHome(); break; case 'ServerSignIn': result.ApiClient.getPublicUsers().then((users) => { @@ -286,13 +290,11 @@ class AppRouter { this.showWelcome(); break; case 'ServerUpdateNeeded': - import('alert').then(({default: alert}) =>{ - alert({ - text: globalize.translate('ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin'), - html: globalize.translate('ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin') - }).then(() => { - this.showSelectServer(); - }); + alert({ + text: globalize.translate('ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin'), + html: globalize.translate('ServerUpdateNeeded', 'https://github.com/jellyfin/jellyfin') + }).then(() => { + this.showSelectServer(); }); break; default: @@ -308,22 +310,20 @@ class AppRouter { url = route.contentPath || route.path; } - if (url.includes('configurationpage')) { - url = ApiClient.getUrl('/web' + url); - } else if (url.indexOf('://') === -1) { - // Put a slash at the beginning but make sure to avoid a double slash - if (url.indexOf('/') !== 0) { - url = '/' + url; - } - - url = this.baseUrl() + url; - } - if (ctx.querystring && route.enableContentQueryString) { url += '?' + ctx.querystring; } - import('text!' + url).then(({default: html}) => { + let promise; + if (route.serverRequest) { + const apiClient = ServerConnections.currentApiClient(); + url = apiClient.getUrl(`/web${url}`); + promise = apiClient.get(url); + } else { + promise = import(/* webpackChunkName: "[request]" */ `../controllers/${url}`); + } + + promise.then((html) => { this.loadContent(ctx, route, html, request); }); } @@ -340,7 +340,7 @@ class AppRouter { }; if (route.controller) { - import('controllers/' + route.controller).then(onInitComplete); + import('../controllers/' + route.controller).then(onInitComplete); } else { onInitComplete(); } @@ -407,9 +407,7 @@ class AppRouter { this.forcedLogoutMsg = null; if (msg) { - import('alert').then((alert) => { - alert(msg); - }); + alert(msg); } } @@ -484,8 +482,8 @@ class AppRouter { newApiClient.getMaxBandwidth = this.getMaxBandwidth; } - events.off(newApiClient, 'requestfail', this.onRequestFail); - events.on(newApiClient, 'requestfail', this.onRequestFail); + Events.off(newApiClient, 'requestfail', this.onRequestFail); + Events.on(newApiClient, 'requestfail', this.onRequestFail); } initApiClient(apiClient, instance) { @@ -493,15 +491,15 @@ class AppRouter { } initApiClients() { - window.connectionManager.getApiClients().forEach((apiClient) => { + ServerConnections.getApiClients().forEach((apiClient) => { this.initApiClient(apiClient, this); }); - events.on(window.connectionManager, 'apiclientcreated', this.onApiClientCreated); + Events.on(ServerConnections, 'apiclientcreated', this.onApiClientCreated); } onAppResume() { - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); if (apiClient) { apiClient.ensureWebSocket(); @@ -519,7 +517,7 @@ class AppRouter { } } - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); const pathname = ctx.pathname.toLowerCase(); console.debug('appRouter - processing path request ' + pathname); @@ -546,7 +544,7 @@ class AppRouter { if (route.isDefaultRoute) { console.debug('appRouter - loading skin home page'); - Emby.Page.goHome(); + this.goHome(); return; } else if (route.roles) { this.validateRoles(apiClient, route.roles).then(() => { @@ -846,4 +844,8 @@ class AppRouter { } } -export default new AppRouter(); +export const appRouter = new AppRouter(); + +window.Emby = window.Emby || {}; + +window.Emby.Page = appRouter; diff --git a/src/components/apphost.js b/src/components/apphost.js index df2f7c2d2c..8e8cb15b32 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -1,9 +1,11 @@ -import appSettings from 'appSettings'; -import browser from 'browser'; -import events from 'events'; -import * as htmlMediaHelper from 'htmlMediaHelper'; -import * as webSettings from 'webSettings'; -import globalize from 'globalize'; + +import appSettings from '../scripts/settings/appSettings'; +import browser from '../scripts/browser'; +import { Events } from 'jellyfin-apiclient'; +import * as htmlMediaHelper from '../components/htmlMediaHelper'; +import * as webSettings from '../scripts/settings/webSettings'; +import globalize from '../scripts/globalize'; +import profileBuilder from '../scripts/browserDeviceProfile'; function getBaseProfileOptions(item) { const disableHlsVideoAudioCodecs = []; @@ -26,19 +28,16 @@ function getBaseProfileOptions(item) { function getDeviceProfile(item, options = {}) { return new Promise(function (resolve) { - import('browserdeviceprofile').then(({default: profileBuilder}) => { - let profile; + let profile; - if (window.NativeShell) { - profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); - } else { - const builderOpts = getBaseProfileOptions(item); - builderOpts.enableSsaRender = (item && !options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats'); - profile = profileBuilder(builderOpts); - } + if (window.NativeShell) { + profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); + } else { + const builderOpts = getBaseProfileOptions(item); + profile = profileBuilder(builderOpts); + } - resolve(profile); - }); + resolve(profile); }); } @@ -57,60 +56,62 @@ function generateDeviceId() { if (keys.push(navigator.userAgent), keys.push(new Date().getTime()), window.btoa) { const result = replaceAll(btoa(keys.join('|')), '=', '1'); - return Promise.resolve(result); + return result; } - return Promise.resolve(new Date().getTime()); + return new Date().getTime(); } function getDeviceId() { - const key = '_deviceId2'; - const deviceId = appSettings.get(key); + if (!deviceId) { + const key = '_deviceId2'; - if (deviceId) { - return Promise.resolve(deviceId); + deviceId = appSettings.get(key); + + if (!deviceId) { + deviceId = generateDeviceId(); + appSettings.set(key, deviceId); + } } - return generateDeviceId().then(function (deviceId) { - appSettings.set(key, deviceId); - return deviceId; - }); + return deviceId; } function getDeviceName() { - let deviceName; - if (browser.tizen) { - deviceName = 'Samsung Smart TV'; - } else if (browser.web0s) { - deviceName = 'LG Smart TV'; - } else if (browser.operaTv) { - deviceName = 'Opera TV'; - } else if (browser.xboxOne) { - deviceName = 'Xbox One'; - } else if (browser.ps4) { - deviceName = 'Sony PS4'; - } else if (browser.chrome) { - deviceName = 'Chrome'; - } else if (browser.edgeChromium) { - deviceName = 'Edge Chromium'; - } else if (browser.edge) { - deviceName = 'Edge'; - } else if (browser.firefox) { - deviceName = 'Firefox'; - } else if (browser.opera) { - deviceName = 'Opera'; - } else if (browser.safari) { - deviceName = 'Safari'; - } else { - deviceName = 'Web Browser'; - } + if (!deviceName) { + if (browser.tizen) { + deviceName = 'Samsung Smart TV'; + } else if (browser.web0s) { + deviceName = 'LG Smart TV'; + } else if (browser.operaTv) { + deviceName = 'Opera TV'; + } else if (browser.xboxOne) { + deviceName = 'Xbox One'; + } else if (browser.ps4) { + deviceName = 'Sony PS4'; + } else if (browser.chrome) { + deviceName = 'Chrome'; + } else if (browser.edgeChromium) { + deviceName = 'Edge Chromium'; + } else if (browser.edge) { + deviceName = 'Edge'; + } else if (browser.firefox) { + deviceName = 'Firefox'; + } else if (browser.opera) { + deviceName = 'Opera'; + } else if (browser.safari) { + deviceName = 'Safari'; + } else { + deviceName = 'Web Browser'; + } - if (browser.ipad) { - deviceName += ' iPad'; - } else if (browser.iphone) { - deviceName += ' iPhone'; - } else if (browser.android) { - deviceName += ' Android'; + if (browser.ipad) { + deviceName += ' iPad'; + } else if (browser.iphone) { + deviceName += ' iPhone'; + } else if (browser.android) { + deviceName += ' Android'; + } } return deviceName; @@ -172,7 +173,7 @@ function supportsCue() { function onAppVisible() { if (isHidden) { isHidden = false; - events.trigger(appHost, 'resume'); + Events.trigger(appHost, 'resume'); } } @@ -296,7 +297,7 @@ function askForExit() { return; } - import('actionsheet').then(({default: actionsheet}) => { + import('../components/actionSheet/actionSheet').then((actionsheet) => { exitPromise = actionsheet.show({ title: globalize.translate('MessageConfirmAppExit'), items: [ @@ -318,7 +319,7 @@ let deviceName; const appName = 'Jellyfin Web'; const appVersion = '10.7.0'; -const appHost = { +export const appHost = { getWindowState: function () { return document.windowState || 'Normal'; }, @@ -353,16 +354,16 @@ const appHost = { return window.NativeShell.AppHost.init(); } - deviceName = getDeviceName(); - getDeviceId().then(function (id) { - deviceId = id; - }); + return { + deviceId: getDeviceId(), + deviceName: getDeviceName() + }; }, deviceName: function () { - return window.NativeShell ? window.NativeShell.AppHost.deviceName() : deviceName; + return window.NativeShell ? window.NativeShell.AppHost.deviceName() : getDeviceName(); }, deviceId: function () { - return window.NativeShell ? window.NativeShell.AppHost.deviceId() : deviceId; + return window.NativeShell ? window.NativeShell.AppHost.deviceId() : getDeviceId(); }, appName: function () { return window.NativeShell ? window.NativeShell.AppHost.appName() : appName; @@ -407,4 +408,5 @@ if (window.addEventListener) { window.addEventListener('blur', onAppHidden); } -export default appHost; +// load app host on module load +appHost.init(); diff --git a/src/components/autoFocuser.js b/src/components/autoFocuser.js index 0a10cabd2f..aa88e0c294 100644 --- a/src/components/autoFocuser.js +++ b/src/components/autoFocuser.js @@ -5,8 +5,8 @@ * @module components/autoFocuser */ -import focusManager from 'focusManager'; -import layoutManager from 'layoutManager'; +import focusManager from './focusManager'; +import layoutManager from './layoutManager'; /** * Previously selected element. diff --git a/src/components/backdrop/backdrop.js b/src/components/backdrop/backdrop.js index 83888b81b9..a2fe54de19 100644 --- a/src/components/backdrop/backdrop.js +++ b/src/components/backdrop/backdrop.js @@ -1,8 +1,9 @@ -import browser from 'browser'; -import playbackManager from 'playbackManager'; -import dom from 'dom'; -import * as userSettings from 'userSettings'; -import 'css!./backdrop'; +import browser from '../../scripts/browser'; +import { playbackManager } from '../playback/playbackmanager'; +import dom from '../../scripts/dom'; +import * as userSettings from '../../scripts/settings/userSettings'; +import './backdrop.css'; +import ServerConnections from '../ServerConnections'; /* eslint-disable indent */ @@ -176,7 +177,7 @@ import 'css!./backdrop'; function getItemImageUrls(item, imageOptions) { imageOptions = imageOptions || {}; - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); if (item.BackdropImageTags && item.BackdropImageTags.length > 0) { return item.BackdropImageTags.map((imgTag, index) => { return apiClient.getScaledImageUrl(item.BackdropItemId || item.Id, Object.assign(imageOptions, { diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index 63b2e26adb..4095bea48f 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -5,21 +5,22 @@ * @module components/cardBuilder/cardBuilder */ -import datetime from 'datetime'; -import imageLoader from 'imageLoader'; -import itemHelper from 'itemHelper'; -import focusManager from 'focusManager'; -import indicators from 'indicators'; -import globalize from 'globalize'; -import layoutManager from 'layoutManager'; -import dom from 'dom'; -import browser from 'browser'; -import playbackManager from 'playbackManager'; -import itemShortcuts from 'itemShortcuts'; -import imageHelper from 'scripts/imagehelper'; -import 'css!./card'; -import 'paper-icon-button-light'; -import 'programStyles'; +import datetime from '../../scripts/datetime'; +import imageLoader from '../images/imageLoader'; +import itemHelper from '../itemHelper'; +import focusManager from '../focusManager'; +import indicators from '../indicators/indicators'; +import globalize from '../../scripts/globalize'; +import layoutManager from '../layoutManager'; +import dom from '../../scripts/dom'; +import browser from '../../scripts/browser'; +import { playbackManager } from '../playback/playbackmanager'; +import itemShortcuts from '../shortcuts'; +import imageHelper from '../../scripts/imagehelper'; +import './card.css'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../guide/programs.css'; +import ServerConnections from '../ServerConnections'; const enableFocusTransform = !browser.slow && !browser.edge; @@ -370,7 +371,7 @@ import 'programStyles'; if (serverId !== lastServerId) { lastServerId = serverId; - apiClient = window.connectionManager.getApiClient(lastServerId); + apiClient = ServerConnections.getApiClient(lastServerId); } if (options.indexBy) { @@ -1121,7 +1122,7 @@ import 'programStyles'; if (!refreshIndicatorLoaded) { refreshIndicatorLoaded = true; /* eslint-disable-next-line @babel/no-unused-expressions */ - import('emby-itemrefreshindicator'); + import('../../elements/emby-itemrefreshindicator/emby-itemrefreshindicator'); } } @@ -1453,7 +1454,7 @@ import 'programStyles'; if (itemHelper.canMarkPlayed(item)) { /* eslint-disable-next-line @babel/no-unused-expressions */ - import('emby-playstatebutton'); + import('../../elements/emby-playstatebutton/emby-playstatebutton'); html += ''; } @@ -1461,7 +1462,7 @@ import 'programStyles'; const likes = userData.Likes == null ? '' : userData.Likes; /* eslint-disable-next-line @babel/no-unused-expressions */ - import('emby-ratingbutton'); + import('../../elements/emby-ratingbutton/emby-ratingbutton'); html += ''; } diff --git a/src/components/cardbuilder/chaptercardbuilder.js b/src/components/cardbuilder/chaptercardbuilder.js index 35ae2b0cdd..e364056e14 100644 --- a/src/components/cardbuilder/chaptercardbuilder.js +++ b/src/components/cardbuilder/chaptercardbuilder.js @@ -5,10 +5,11 @@ * @module components/cardBuilder/chaptercardbuilder */ -import datetime from 'datetime'; -import imageLoader from 'imageLoader'; -import layoutManager from 'layoutManager'; -import browser from 'browser'; +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; @@ -47,7 +48,7 @@ import browser from 'browser'; let html = ''; let itemsInRow = 0; - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); for (let i = 0, length = chapters.length; i < length; i++) { if (options.rows && itemsInRow === 0) { diff --git a/src/components/cardbuilder/peoplecardbuilder.js b/src/components/cardbuilder/peoplecardbuilder.js index 5fc9e8ade5..00b8f0fb89 100644 --- a/src/components/cardbuilder/peoplecardbuilder.js +++ b/src/components/cardbuilder/peoplecardbuilder.js @@ -5,7 +5,7 @@ * @module components/cardBuilder/peoplecardbuilder */ -import cardBuilder from 'cardBuilder'; +import cardBuilder from './cardBuilder'; export function buildPeopleCards(items, options) { options = Object.assign(options || {}, { diff --git a/src/components/channelMapper/channelMapper.js b/src/components/channelMapper/channelMapper.js index 294f9e223a..bedab5421b 100644 --- a/src/components/channelMapper/channelMapper.js +++ b/src/components/channelMapper/channelMapper.js @@ -1,21 +1,22 @@ -import dom from 'dom'; -import dialogHelper from 'dialogHelper'; -import loading from 'loading'; -import globalize from 'globalize'; -import actionsheet from 'actionsheet'; -import 'emby-input'; -import 'paper-icon-button-light'; -import 'emby-button'; -import 'listViewStyle'; -import 'material-icons'; -import 'formDialogStyle'; +import dom from '../../scripts/dom'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import loading from '../loading/loading'; +import globalize from '../../scripts/globalize'; +import actionsheet from '../actionSheet/actionSheet'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-button/emby-button'; +import '../listview/listview.css'; +import 'material-design-icons-iconfont'; +import '../formdialog.css'; +import ServerConnections from '../ServerConnections'; export default class channelMapper { constructor(options) { function mapChannel(button, channelId, providerChannelId) { loading.show(); const providerId = options.providerId; - window.connectionManager.getApiClient(options.serverId).ajax({ + ServerConnections.getApiClient(options.serverId).ajax({ type: 'POST', url: ApiClient.getUrl('LiveTv/ChannelMappings'), data: JSON.stringify({ @@ -58,7 +59,7 @@ export default class channelMapper { } function getChannelMappingOptions(serverId, providerId) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.getJSON(apiClient.getUrl('LiveTv/ChannelMappingOptions', { providerId: providerId })); diff --git a/src/components/collectionEditor/collectionEditor.js b/src/components/collectionEditor/collectionEditor.js index 2d0d025929..33e6fdecca 100644 --- a/src/components/collectionEditor/collectionEditor.js +++ b/src/components/collectionEditor/collectionEditor.js @@ -1,17 +1,19 @@ -import dom from 'dom'; -import dialogHelper from 'dialogHelper'; -import loading from 'loading'; -import layoutManager from 'layoutManager'; -import appRouter from 'appRouter'; -import globalize from 'globalize'; -import 'emby-checkbox'; -import 'emby-input'; -import 'paper-icon-button-light'; -import 'emby-select'; -import 'material-icons'; -import 'css!./../formdialog'; -import 'emby-button'; -import 'flexStyles'; +import dom from '../../scripts/dom'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import loading from '../loading/loading'; +import layoutManager from '../layoutManager'; +import { appRouter } from '../appRouter'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-select/emby-select'; +import 'material-design-icons-iconfont'; +import '../formdialog.css'; +import '../../assets/css/flexstyles.scss'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; /* eslint-disable indent */ @@ -24,7 +26,7 @@ import 'flexStyles'; const collectionId = panel.querySelector('#selectCollectionToAddTo').value; - const apiClient = window.connectionManager.getApiClient(currentServerId); + const apiClient = ServerConnections.getApiClient(currentServerId); if (collectionId) { addToCollection(apiClient, panel, collectionId); @@ -80,9 +82,7 @@ import 'flexStyles'; dlg.submitted = true; dialogHelper.close(dlg); - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageItemsAdded')); - }); + toast(globalize.translate('MessageItemsAdded')); }); } @@ -105,7 +105,7 @@ import 'flexStyles'; EnableTotalRecordCount: false }; - const apiClient = window.connectionManager.getApiClient(currentServerId); + const apiClient = ServerConnections.getApiClient(currentServerId); apiClient.getItems(apiClient.getCurrentUserId(), options).then(result => { let html = ''; @@ -199,7 +199,7 @@ import 'flexStyles'; } function centerFocus(elem, horiz, on) { - import('scrollHelper').then((scrollHelper) => { + import('../../scripts/scrollHelper').then((scrollHelper) => { const fn = on ? 'on' : 'off'; scrollHelper.centerFocus[fn](elem, horiz); }); diff --git a/src/components/confirm/confirm.js b/src/components/confirm/confirm.js index eca612ccb8..1978324e7c 100644 --- a/src/components/confirm/confirm.js +++ b/src/components/confirm/confirm.js @@ -1,69 +1,65 @@ -import browser from 'browser'; -import dialog from 'dialog'; -import globalize from 'globalize'; +import browser from '../../scripts/browser'; +import dialog from '../dialog/dialog'; +import globalize from '../../scripts/globalize'; -/* eslint-disable indent */ -export default (() => { - function replaceAll(str, find, replace) { - return str.split(find).join(replace); +function replaceAll(str, find, replace) { + return str.split(find).join(replace); +} + +function nativeConfirm(options) { + if (typeof options === 'string') { + options = { + title: '', + text: options + }; } - if (browser.tv && window.confirm) { - // Use the native confirm dialog - return options => { - if (typeof options === 'string') { - options = { - title: '', - text: options - }; - } + const text = replaceAll(options.text || '', '
', '\n'); + const result = window.confirm(text); - const text = replaceAll(options.text || '', '
', '\n'); - const result = window.confirm(text); + if (result) { + return Promise.resolve(); + } else { + return Promise.reject(); + } +} - if (result) { - return Promise.resolve(); - } else { - return Promise.reject(); - } +function customConfirm(text, title) { + let options; + if (typeof text === 'string') { + options = { + title: title, + text: text }; } else { - // Use our own dialog - return (text, title) => { - let options; - if (typeof text === 'string') { - options = { - title: title, - text: text - }; - } else { - options = text; - } - - const items = []; - - items.push({ - name: options.cancelText || globalize.translate('ButtonCancel'), - id: 'cancel', - type: 'cancel' - }); - - items.push({ - name: options.confirmText || globalize.translate('ButtonOk'), - id: 'ok', - type: options.primary === 'delete' ? 'delete' : 'submit' - }); - - options.buttons = items; - - return dialog.show(options).then(result => { - if (result === 'ok') { - return Promise.resolve(); - } - - return Promise.reject(); - }); - }; + options = text; } -})(); -/* eslint-enable indent */ + + const items = []; + + items.push({ + name: options.cancelText || globalize.translate('ButtonCancel'), + id: 'cancel', + type: 'cancel' + }); + + items.push({ + name: options.confirmText || globalize.translate('ButtonOk'), + id: 'ok', + type: options.primary === 'delete' ? 'delete' : 'submit' + }); + + options.buttons = items; + + return dialog.show(options).then(result => { + if (result === 'ok') { + return Promise.resolve(); + } + + return Promise.reject(); + }); +} + +const confirm = browser.tv && window.confirm ? nativeConfirm : customConfirm; + +export default confirm; diff --git a/src/components/dialog/dialog.js b/src/components/dialog/dialog.js index 1b13900d85..ee97fff8a1 100644 --- a/src/components/dialog/dialog.js +++ b/src/components/dialog/dialog.js @@ -1,14 +1,14 @@ -import dialogHelper from 'dialogHelper'; -import dom from 'dom'; -import layoutManager from 'layoutManager'; -import scrollHelper from 'scrollHelper'; -import globalize from 'globalize'; -import 'material-icons'; -import 'emby-button'; -import 'paper-icon-button-light'; -import 'emby-input'; -import 'formDialogStyle'; -import 'flexStyles'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import dom from '../../scripts/dom'; +import layoutManager from '../layoutManager'; +import scrollHelper from '../../scripts/scrollHelper'; +import globalize from '../../scripts/globalize'; +import 'material-design-icons-iconfont'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-input/emby-input'; +import '../formdialog.css'; +import '../../assets/css/flexstyles.scss'; /* eslint-disable indent */ @@ -128,7 +128,7 @@ import 'flexStyles'; options = text; } - const { default: template } = await import('text!./dialog.template.html'); + const { default: template } = await import('./dialog.template.html'); return new Promise((resolve, reject) => { showDialog(options, template).then(resolve, reject); }); diff --git a/src/components/dialogHelper/dialogHelper.js b/src/components/dialogHelper/dialogHelper.js index eb46d98b12..20c658df4e 100644 --- a/src/components/dialogHelper/dialogHelper.js +++ b/src/components/dialogHelper/dialogHelper.js @@ -1,11 +1,11 @@ -import appRouter from 'appRouter'; -import focusManager from 'focusManager'; -import browser from 'browser'; -import layoutManager from 'layoutManager'; -import inputManager from 'inputManager'; -import dom from 'dom'; -import 'css!./dialoghelper.css'; -import 'scrollStyles'; +import { appRouter } from '../appRouter'; +import focusManager from '../focusManager'; +import browser from '../../scripts/browser'; +import layoutManager from '../layoutManager'; +import inputManager from '../../scripts/inputManager'; +import dom from '../../scripts/dom'; +import './dialoghelper.css'; +import '../../assets/css/scrollstyles.css'; /* eslint-disable indent */ @@ -354,7 +354,7 @@ import 'scrollStyles'; } function centerFocus(elem, horiz, on) { - import('scrollHelper').then((scrollHelper) => { + import('../../scripts/scrollHelper').then((scrollHelper) => { const fn = on ? 'on' : 'off'; scrollHelper.centerFocus[fn](elem, horiz); }); diff --git a/src/components/directorybrowser/directorybrowser.js b/src/components/directorybrowser/directorybrowser.js index 3dd3302b28..2f8a2cadd1 100644 --- a/src/components/directorybrowser/directorybrowser.js +++ b/src/components/directorybrowser/directorybrowser.js @@ -1,13 +1,14 @@ -import loading from 'loading'; -import dialogHelper from 'dialogHelper'; -import dom from 'dom'; -import globalize from 'globalize'; -import 'listViewStyle'; -import 'emby-input'; -import 'paper-icon-button-light'; -import 'css!./directorybrowser'; -import 'formDialogStyle'; -import 'emby-button'; +import loading from '../loading/loading'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import dom from '../../scripts/dom'; +import globalize from '../../scripts/globalize'; +import '../listview/listview.css'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-button/paper-icon-button-light'; +import './directorybrowser.css'; +import '../formdialog.css'; +import '../../elements/emby-button/emby-button'; +import alert from '../alert'; /* eslint-disable indent */ @@ -157,9 +158,7 @@ import 'emby-button'; } function alertTextWithOptions(options) { - import('alert').then(({default: alert}) => { - alert(options); - }); + alert(options); } function validatePath(path, validateWriteable, apiClient) { diff --git a/src/components/displaySettings/displaySettings.js b/src/components/displaySettings/displaySettings.js index efaab16b3f..9d7292547d 100644 --- a/src/components/displaySettings/displaySettings.js +++ b/src/components/displaySettings/displaySettings.js @@ -1,16 +1,18 @@ -import browser from 'browser'; -import layoutManager from 'layoutManager'; -import pluginManager from 'pluginManager'; -import appHost from 'apphost'; -import focusManager from 'focusManager'; -import datetime from 'datetime'; -import globalize from 'globalize'; -import loading from 'loading'; -import skinManager from 'skinManager'; -import events from 'events'; -import 'emby-select'; -import 'emby-checkbox'; -import 'emby-button'; +import browser from '../../scripts/browser'; +import layoutManager from '../layoutManager'; +import { pluginManager } from '../pluginManager'; +import { appHost } from '../apphost'; +import focusManager from '../focusManager'; +import datetime from '../../scripts/datetime'; +import globalize from '../../scripts/globalize'; +import loading from '../loading/loading'; +import skinManager from '../../scripts/themeManager'; +import { Events } from 'jellyfin-apiclient'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-button/emby-button'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; /* eslint-disable indent */ @@ -168,11 +170,9 @@ import 'emby-button'; saveUser(context, user, userSettings, apiClient).then(() => { loading.hide(); if (enableSaveConfirmation) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('SettingsSaved')); - }); + toast(globalize.translate('SettingsSaved')); } - events.trigger(instance, 'saved'); + Events.trigger(instance, 'saved'); }, () => { loading.hide(); }); @@ -181,7 +181,7 @@ import 'emby-button'; function onSubmit(e) { const self = this; - const apiClient = window.connectionManager.getApiClient(self.options.serverId); + const apiClient = ServerConnections.getApiClient(self.options.serverId); const userId = self.options.userId; const userSettings = self.options.userSettings; @@ -198,7 +198,7 @@ import 'emby-button'; } async function embed(options, self) { - const { default: template } = await import('text!./displaySettings.template.html'); + const { default: template } = await import('./displaySettings.template.html'); options.element.innerHTML = globalize.translateHtml(template, 'core'); options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self)); if (options.enableSaveButton) { @@ -220,7 +220,7 @@ import 'emby-button'; loading.show(); const userId = self.options.userId; - const apiClient = window.connectionManager.getApiClient(self.options.serverId); + const apiClient = ServerConnections.getApiClient(self.options.serverId); const userSettings = self.options.userSettings; return apiClient.getUser(userId).then(user => { diff --git a/src/components/favoriteitems.js b/src/components/favoriteitems.js index 86cd050216..cb1b61c43f 100644 --- a/src/components/favoriteitems.js +++ b/src/components/favoriteitems.js @@ -1,12 +1,12 @@ -import loading from 'loading'; -import cardBuilder from 'cardBuilder'; -import dom from 'dom'; -import appHost from 'apphost'; -import imageLoader from 'imageLoader'; -import globalize from 'globalize'; -import layoutManager from 'layoutManager'; -import 'scrollStyles'; -import 'emby-itemscontainer'; +import loading from './loading/loading'; +import cardBuilder from './cardbuilder/cardBuilder'; +import dom from '../scripts/dom'; +import { appHost } from './apphost'; +import imageLoader from './images/imageLoader'; +import globalize from '../scripts/globalize'; +import layoutManager from './layoutManager'; +import '../assets/css/scrollstyles.css'; +import '../elements/emby-itemscontainer/emby-itemscontainer'; /* eslint-disable indent */ diff --git a/src/components/filterdialog/filterdialog.js b/src/components/filterdialog/filterdialog.js index d11edb40a2..c8b81066d2 100644 --- a/src/components/filterdialog/filterdialog.js +++ b/src/components/filterdialog/filterdialog.js @@ -1,10 +1,11 @@ -import dom from 'dom'; -import dialogHelper from 'dialogHelper'; -import globalize from 'globalize'; -import events from 'events'; -import 'emby-checkbox'; -import 'emby-collapse'; -import 'css!./style.css'; +import dom from '../../scripts/dom'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import globalize from '../../scripts/globalize'; +import { Events } from 'jellyfin-apiclient'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-collapse/emby-collapse'; +import './style.css'; +import ServerConnections from '../ServerConnections'; /* eslint-disable indent */ function renderOptions(context, selector, cssClass, items, isCheckedFn) { @@ -108,7 +109,7 @@ import 'css!./style.css'; * @param instance {FilterDialog} An instance of FilterDialog */ function triggerChange(instance) { - events.trigger(instance, 'filterchange'); + Events.trigger(instance, 'filterchange'); } function setVisibility(context, options) { @@ -401,7 +402,7 @@ import 'css!./style.css'; } show() { - return import('text!./filterdialog.template.html').then(({default: template}) => { + return import('./filterdialog.template.html').then(({default: template}) => { return new Promise((resolve) => { const dlg = dialogHelper.createDialog({ removeOnClose: true, @@ -419,7 +420,7 @@ import 'css!./style.css'; this.bindEvents(dlg); if (enableDynamicFilters(this.options.mode)) { dlg.classList.add('dynamicFilterDialog'); - const apiClient = window.connectionManager.getApiClient(this.options.serverId); + const apiClient = ServerConnections.getApiClient(this.options.serverId); loadDynamicFilters(dlg, apiClient, apiClient.getCurrentUserId(), this.options.query); } }); diff --git a/src/components/filtermenu/filtermenu.js b/src/components/filtermenu/filtermenu.js index 7cef08303d..f4f6ef2d9b 100644 --- a/src/components/filtermenu/filtermenu.js +++ b/src/components/filtermenu/filtermenu.js @@ -1,18 +1,19 @@ -import dom from 'dom'; -import focusManager from 'focusManager'; -import dialogHelper from 'dialogHelper'; -import inputManager from 'inputManager'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import * as userSettings from 'userSettings'; -import 'emby-checkbox'; -import 'emby-input'; -import 'paper-icon-button-light'; -import 'emby-select'; -import 'material-icons'; -import 'css!./../formdialog'; -import 'emby-button'; -import 'flexStyles'; +import dom from '../../scripts/dom'; +import focusManager from '../focusManager'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import inputManager from '../../scripts/inputManager'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import * as userSettings from '../../scripts/settings/userSettings'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-select/emby-select'; +import 'material-design-icons-iconfont'; +import '../formdialog.css'; +import '../../assets/css/flexstyles.scss'; +import ServerConnections from '../ServerConnections'; function onSubmit(e) { e.preventDefault(); @@ -80,7 +81,7 @@ function moveCheckboxFocus(elem, offset) { } } function centerFocus(elem, horiz, on) { - import('scrollHelper').then(({ default: scrollHelper }) => { + import('../../scripts/scrollHelper').then((scrollHelper) => { const fn = on ? 'on' : 'off'; scrollHelper.centerFocus[fn](elem, horiz); }); @@ -193,7 +194,7 @@ function initEditor(context, settings) { } } function loadDynamicFilters(context, options) { - const apiClient = window.connectionManager.getApiClient(options.serverId); + const apiClient = ServerConnections.getApiClient(options.serverId); const filterMenuOptions = Object.assign(options.filterMenuOptions, { @@ -209,7 +210,7 @@ function loadDynamicFilters(context, options) { class FilterMenu { show(options) { return new Promise( (resolve, reject) => { - import('text!./filtermenu.template.html').then(({ default: template }) => { + import('./filtermenu.template.html').then(({ default: template }) => { const dialogOptions = { removeOnClose: true, scrollY: false diff --git a/src/components/focusManager.js b/src/components/focusManager.js index d45984bf58..b0fc188516 100644 --- a/src/components/focusManager.js +++ b/src/components/focusManager.js @@ -1,7 +1,7 @@ /* eslint-disable indent */ -import dom from 'dom'; -import scrollManager from 'scrollManager'; +import dom from '../scripts/dom'; +import scrollManager from './scrollManager'; const scopes = []; function pushScope(elem) { diff --git a/src/components/groupedcards.js b/src/components/groupedcards.js index 947b3b8569..13158016ac 100644 --- a/src/components/groupedcards.js +++ b/src/components/groupedcards.js @@ -1,12 +1,14 @@ /* eslint-disable indent */ -import dom from 'dom'; -import appRouter from 'appRouter'; +import dom from '../scripts/dom'; +import { appRouter } from './appRouter'; +import Dashboard from '../scripts/clientUtils'; +import ServerConnections from './ServerConnections'; function onGroupedCardClick(e, card) { const itemId = card.getAttribute('data-id'); const serverId = card.getAttribute('data-serverid'); - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); const userId = apiClient.getCurrentUserId(); const playedIndicator = card.querySelector('.playedIndicator'); const playedIndicatorHtml = playedIndicator ? playedIndicator.innerHTML : null; diff --git a/src/components/guide/guide-settings.js b/src/components/guide/guide-settings.js index 35f0d3e06e..8132ac3bb3 100644 --- a/src/components/guide/guide-settings.js +++ b/src/components/guide/guide-settings.js @@ -1,12 +1,12 @@ -import dialogHelper from 'dialogHelper'; -import globalize from 'globalize'; -import * as userSettings from 'userSettings'; -import layoutManager from 'layoutManager'; -import scrollHelper from 'scrollHelper'; -import 'emby-checkbox'; -import 'emby-radio'; -import 'css!./../formdialog'; -import 'material-icons'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import globalize from '../../scripts/globalize'; +import * as userSettings from '../../scripts/settings/userSettings'; +import layoutManager from '../layoutManager'; +import scrollHelper from '../../scripts/scrollHelper'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-radio/emby-radio'; +import '../formdialog.css'; +import 'material-design-icons-iconfont'; function saveCategories(context, options) { const categories = []; @@ -88,7 +88,7 @@ function showEditor(options) { return new Promise(function (resolve, reject) { let settingsChanged = false; - import('text!./guide-settings.template.html').then(({ default: template }) => { + import('./guide-settings.template.html').then(({ default: template }) => { const dialogOptions = { removeOnClose: true, scrollY: false diff --git a/src/components/guide/guide.js b/src/components/guide/guide.js index a5ed55e67c..7ae0b8409f 100644 --- a/src/components/guide/guide.js +++ b/src/components/guide/guide.js @@ -1,32 +1,33 @@ -import inputManager from 'inputManager'; -import browser from 'browser'; -import globalize from 'globalize'; -import scrollHelper from 'scrollHelper'; -import serverNotifications from 'serverNotifications'; -import loading from 'loading'; -import datetime from 'datetime'; -import focusManager from 'focusManager'; -import playbackManager from 'playbackManager'; -import * as userSettings from 'userSettings'; -import imageLoader from 'imageLoader'; -import events from 'events'; -import layoutManager from 'layoutManager'; -import itemShortcuts from 'itemShortcuts'; -import dom from 'dom'; -import 'css!./guide.css'; -import 'programStyles'; -import 'material-icons'; -import 'scrollStyles'; -import 'emby-programcell'; -import 'emby-button'; -import 'paper-icon-button-light'; -import 'emby-tabs'; -import 'emby-scroller'; -import 'flexStyles'; -import 'webcomponents'; +import inputManager from '../../scripts/inputManager'; +import browser from '../../scripts/browser'; +import globalize from '../../scripts/globalize'; +import { Events } from 'jellyfin-apiclient'; +import scrollHelper from '../../scripts/scrollHelper'; +import serverNotifications from '../../scripts/serverNotifications'; +import loading from '../loading/loading'; +import datetime from '../../scripts/datetime'; +import focusManager from '../focusManager'; +import { playbackManager } from '../playback/playbackmanager'; +import * as userSettings from '../../scripts/settings/userSettings'; +import imageLoader from '../images/imageLoader'; +import layoutManager from '../layoutManager'; +import itemShortcuts from '../shortcuts'; +import dom from '../../scripts/dom'; +import './guide.css'; +import './programs.css'; +import 'material-design-icons-iconfont'; +import '../../assets/css/scrollstyles.css'; +import '../../elements/emby-programcell/emby-programcell'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-tabs/emby-tabs'; +import '../../elements/emby-scroller/emby-scroller'; +import '../../assets/css/flexstyles.scss'; +import 'webcomponents.js/webcomponents-lite'; +import ServerConnections from '../ServerConnections'; function showViewSettings(instance) { - import('guide-settings-dialog').then(({default: guideSettingsDialog}) => { + import('./guide-settings').then(({default: guideSettingsDialog}) => { guideSettingsDialog.show(instance.categoryOptions).then(function () { instance.refresh(); }); @@ -164,10 +165,10 @@ function Guide(options) { self.destroy = function () { stopAutoRefresh(); - events.off(serverNotifications, 'TimerCreated', onTimerCreated); - events.off(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); - events.off(serverNotifications, 'TimerCancelled', onTimerCancelled); - events.off(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); + Events.off(serverNotifications, 'TimerCreated', onTimerCreated); + Events.off(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); + Events.off(serverNotifications, 'TimerCancelled', onTimerCancelled); + Events.off(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); setScrollEvents(options.element, false); itemShortcuts.off(options.element); @@ -212,7 +213,7 @@ function Guide(options) { } function reloadGuide(context, newStartDate, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { - const apiClient = window.connectionManager.getApiClient(options.serverId); + const apiClient = ServerConnections.getApiClient(options.serverId); const channelQuery = { @@ -872,7 +873,7 @@ function Guide(options) { function reloadPage(page) { showLoading(); - const apiClient = window.connectionManager.getApiClient(options.serverId); + const apiClient = ServerConnections.getApiClient(options.serverId); apiClient.getLiveTvGuideInfo().then(function (guideInfo) { setDateRange(page, guideInfo); @@ -1001,7 +1002,7 @@ function Guide(options) { const item = items[id]; if (item) { - events.trigger(self, 'focus', [ + Events.trigger(self, 'focus', [ { item: item }]); @@ -1091,7 +1092,7 @@ function Guide(options) { } } - import('text!./tvguide.template.html').then(({default: template}) => { + import('./tvguide.template.html').then(({default: template}) => { const context = options.element; context.classList.add('tvguide'); @@ -1183,12 +1184,12 @@ function Guide(options) { setScrollEvents(context, true); itemShortcuts.on(context); - events.trigger(self, 'load'); + Events.trigger(self, 'load'); - events.on(serverNotifications, 'TimerCreated', onTimerCreated); - events.on(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); - events.on(serverNotifications, 'TimerCancelled', onTimerCancelled); - events.on(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); + Events.on(serverNotifications, 'TimerCreated', onTimerCreated); + Events.on(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); + Events.on(serverNotifications, 'TimerCancelled', onTimerCancelled); + Events.on(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); self.refresh(); }); diff --git a/src/components/homeScreenSettings/homeScreenSettings.js b/src/components/homeScreenSettings/homeScreenSettings.js index 5138935408..89edf29486 100644 --- a/src/components/homeScreenSettings/homeScreenSettings.js +++ b/src/components/homeScreenSettings/homeScreenSettings.js @@ -1,13 +1,16 @@ -import layoutManager from 'layoutManager'; -import focusManager from 'focusManager'; -import globalize from 'globalize'; -import loading from 'loading'; -import homeSections from 'homeSections'; -import dom from 'dom'; -import events from 'events'; -import 'listViewStyle'; -import 'emby-select'; -import 'emby-checkbox'; + +import layoutManager from '../layoutManager'; +import focusManager from '../focusManager'; +import globalize from '../../scripts/globalize'; +import loading from '../loading/loading'; +import { Events } from 'jellyfin-apiclient'; +import homeSections from '../homesections/homesections'; +import dom from '../../scripts/dom'; +import '../listview/listview.css'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-checkbox/emby-checkbox'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; /* eslint-disable indent */ @@ -369,12 +372,10 @@ import 'emby-checkbox'; saveUser(context, user, userSettings, apiClient).then(() => { loading.hide(); if (enableSaveConfirmation) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('SettingsSaved')); - }); + toast(globalize.translate('SettingsSaved')); } - events.trigger(instance, 'saved'); + Events.trigger(instance, 'saved'); }, () => { loading.hide(); }); @@ -383,7 +384,7 @@ import 'emby-checkbox'; function onSubmit(e) { const self = this; - const apiClient = window.connectionManager.getApiClient(self.options.serverId); + const apiClient = ServerConnections.getApiClient(self.options.serverId); const userId = self.options.userId; const userSettings = self.options.userSettings; @@ -417,7 +418,7 @@ import 'emby-checkbox'; } function embed(options, self) { - return import('text!./homeScreenSettings.template.html').then(({default: template}) => { + return import('./homeScreenSettings.template.html').then(({default: template}) => { for (let i = 1; i <= numConfigurableSections; i++) { template = template.replace(`{section${i}label}`, globalize.translate('LabelHomeScreenSectionValue', i)); } @@ -455,7 +456,7 @@ import 'emby-checkbox'; loading.show(); const userId = self.options.userId; - const apiClient = window.connectionManager.getApiClient(self.options.serverId); + const apiClient = ServerConnections.getApiClient(self.options.serverId); const userSettings = self.options.userSettings; apiClient.getUser(userId).then(user => { diff --git a/src/components/homesections/homesections.js b/src/components/homesections/homesections.js index 758773689b..6561b8a9e0 100644 --- a/src/components/homesections/homesections.js +++ b/src/components/homesections/homesections.js @@ -1,15 +1,17 @@ -import cardBuilder from 'cardBuilder'; -import dom from 'dom'; -import layoutManager from 'layoutManager'; -import imageLoader from 'imageLoader'; -import globalize from 'globalize'; -import appRouter from 'appRouter'; -import imageHelper from 'scripts/imagehelper'; -import 'paper-icon-button-light'; -import 'emby-itemscontainer'; -import 'emby-scroller'; -import 'emby-button'; -import 'css!./homesections'; +import cardBuilder from '../cardbuilder/cardBuilder'; +import dom from '../../scripts/dom'; +import layoutManager from '../layoutManager'; +import imageLoader from '../images/imageLoader'; +import globalize from '../../scripts/globalize'; +import { appRouter } from '../appRouter'; +import imageHelper from '../../scripts/imagehelper'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-itemscontainer/emby-itemscontainer'; +import '../../elements/emby-scroller/emby-scroller'; +import '../../elements/emby-button/emby-button'; +import './homesections.css'; +import Dashboard from '../../scripts/clientUtils'; +import ServerConnections from '../ServerConnections'; /* eslint-disable indent */ @@ -211,7 +213,7 @@ import 'css!./homesections'; function getFetchLatestItemsFn(serverId, parentId, collectionType) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); let limit = 16; if (enableScrollX()) { @@ -367,7 +369,7 @@ import 'css!./homesections'; function getContinueWatchingFetchFn(serverId) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); const screenWidth = dom.getWindowSize().innerWidth; let limit; @@ -440,7 +442,7 @@ import 'css!./homesections'; function getContinueListeningFetchFn(serverId) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); const screenWidth = dom.getWindowSize().innerWidth; let limit; @@ -513,7 +515,7 @@ import 'css!./homesections'; function getOnNowFetchFn(serverId) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.getLiveTvRecommendedPrograms({ userId: apiClient.getCurrentUserId(), IsAiring: true, @@ -656,7 +658,7 @@ import 'css!./homesections'; function getNextUpFetchFn(serverId) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.getNextUpEpisodes({ Limit: enableScrollX() ? 24 : 15, Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo,Path', @@ -727,7 +729,7 @@ import 'css!./homesections'; function getLatestRecordingsFetchFn(serverId, activeRecordingsOnly) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.getLiveTvRecordings({ userId: apiClient.getCurrentUserId(), Limit: enableScrollX() ? 12 : 5, diff --git a/src/components/htmlMediaHelper.js b/src/components/htmlMediaHelper.js index be506b449d..4f803aa4b6 100644 --- a/src/components/htmlMediaHelper.js +++ b/src/components/htmlMediaHelper.js @@ -1,8 +1,9 @@ + /* eslint-disable indent */ -import appSettings from 'appSettings' ; -import browser from 'browser'; -import events from 'events'; +import appSettings from '../scripts/settings/appSettings' ; +import browser from '../scripts/browser'; +import { Events } from 'jellyfin-apiclient'; export function getSavedVolume() { return appSettings.get('volume') || 1; @@ -114,7 +115,7 @@ import events from 'events'; instance.destroyCustomTrack(instance._mediaElement); } - events.trigger(instance, 'error', [ + Events.trigger(instance, 'error', [ { type: type } @@ -359,7 +360,7 @@ import events from 'events'; src: instance._currentSrc }; - events.trigger(instance, 'stopped', [stopInfo]); + Events.trigger(instance, 'stopped', [stopInfo]); instance._currentTime = null; instance._currentSrc = null; diff --git a/src/components/imageDownloader/imageDownloader.js b/src/components/imageDownloader/imageDownloader.js index 1ec459ff83..18dd849aec 100644 --- a/src/components/imageDownloader/imageDownloader.js +++ b/src/components/imageDownloader/imageDownloader.js @@ -1,17 +1,18 @@ -import dom from 'dom'; -import loading from 'loading'; -import appHost from 'apphost'; -import dialogHelper from 'dialogHelper'; -import imageLoader from 'imageLoader'; -import browser from 'browser'; -import layoutManager from 'layoutManager'; -import scrollHelper from 'scrollHelper'; -import globalize from 'globalize'; -import 'emby-checkbox'; -import 'paper-icon-button-light'; -import 'emby-button'; -import 'formDialogStyle'; -import 'cardStyle'; +import dom from '../../scripts/dom'; +import loading from '../loading/loading'; +import { appHost } from '../apphost'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import imageLoader from '../images/imageLoader'; +import browser from '../../scripts/browser'; +import layoutManager from '../layoutManager'; +import scrollHelper from '../../scripts/scrollHelper'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-button/emby-button'; +import '../formdialog.css'; +import '../cardbuilder/card.css'; +import ServerConnections from '../ServerConnections'; /* eslint-disable indent */ @@ -315,8 +316,8 @@ import 'cardStyle'; function showEditor(itemId, serverId, itemType) { loading.show(); - import('text!./imageDownloader.template.html').then(({default: template}) => { - const apiClient = window.connectionManager.getApiClient(serverId); + import('./imageDownloader.template.html').then(({default: template}) => { + const apiClient = ServerConnections.getApiClient(serverId); currentItemId = itemId; currentItemType = itemType; diff --git a/src/components/imageOptionsEditor/imageOptionsEditor.js b/src/components/imageOptionsEditor/imageOptionsEditor.js index d112dd65cc..a220a65c5d 100644 --- a/src/components/imageOptionsEditor/imageOptionsEditor.js +++ b/src/components/imageOptionsEditor/imageOptionsEditor.js @@ -5,12 +5,12 @@ * @module components/imageOptionsEditor/imageOptionsEditor */ -import globalize from 'globalize'; -import dom from 'dom'; -import dialogHelper from 'dialogHelper'; -import 'emby-checkbox'; -import 'emby-select'; -import 'emby-input'; +import globalize from '../../scripts/globalize'; +import dom from '../../scripts/dom'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-input/emby-input'; function getDefaultImageConfig(itemType, type) { return { diff --git a/src/components/imageUploader/imageUploader.js b/src/components/imageUploader/imageUploader.js index e89cbda2ae..10beff5cab 100644 --- a/src/components/imageUploader/imageUploader.js +++ b/src/components/imageUploader/imageUploader.js @@ -5,16 +5,18 @@ * @module components/imageUploader/imageUploader */ -import dialogHelper from 'dialogHelper'; -import dom from 'dom'; -import loading from 'loading'; -import scrollHelper from 'scrollHelper'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import 'emby-button'; -import 'emby-select'; -import 'formDialogStyle'; -import 'css!./style'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import dom from '../../scripts/dom'; +import loading from '../loading/loading'; +import scrollHelper from '../../scripts/scrollHelper'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-select/emby-select'; +import '../formdialog.css'; +import './style.css'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; let currentItemId; let currentServerId; @@ -26,16 +28,12 @@ import 'css!./style'; switch (evt.target.error.code) { case evt.target.error.NOT_FOUND_ERR: - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageFileReadError')); - }); + toast(globalize.translate('MessageFileReadError')); break; case evt.target.error.ABORT_ERR: break; // noop default: - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageFileReadError')); - }); + toast(globalize.translate('MessageFileReadError')); break; } } @@ -87,9 +85,7 @@ import 'css!./style'; } if (!file.type.startsWith('image/')) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageImageFileTypeAllowed')); - }); + toast(globalize.translate('MessageImageFileTypeAllowed')); e.preventDefault(); return false; } @@ -100,14 +96,12 @@ import 'css!./style'; const imageType = dlg.querySelector('#selectImageType').value; if (imageType === 'None') { - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageImageTypeNotSelected')); - }); + toast(globalize.translate('MessageImageTypeNotSelected')); e.preventDefault(); return false; } - window.connectionManager.getApiClient(currentServerId).uploadItemImage(currentItemId, imageType, file).then(() => { + ServerConnections.getApiClient(currentServerId).uploadItemImage(currentItemId, imageType, file).then(() => { dlg.querySelector('#uploadImage').value = ''; loading.hide(); @@ -134,7 +128,7 @@ import 'css!./style'; function showEditor(options, resolve) { options = options || {}; - return import('text!./imageUploader.template.html').then(({default: template}) => { + return import('./imageUploader.template.html').then(({default: template}) => { currentItemId = options.itemId; currentServerId = options.serverId; diff --git a/src/components/imageeditor/imageeditor.js b/src/components/imageeditor/imageeditor.js index e5b59cfb22..69e9bcb8c7 100644 --- a/src/components/imageeditor/imageeditor.js +++ b/src/components/imageeditor/imageeditor.js @@ -1,18 +1,21 @@ -import dialogHelper from 'dialogHelper'; -import loading from 'loading'; -import dom from 'dom'; -import layoutManager from 'layoutManager'; -import focusManager from 'focusManager'; -import globalize from 'globalize'; -import scrollHelper from 'scrollHelper'; -import imageLoader from 'imageLoader'; -import browser from 'browser'; -import appHost from 'apphost'; -import 'cardStyle'; -import 'formDialogStyle'; -import 'emby-button'; -import 'paper-icon-button-light'; -import 'css!./imageeditor'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import loading from '../loading/loading'; +import dom from '../../scripts/dom'; +import layoutManager from '../layoutManager'; +import focusManager from '../focusManager'; +import globalize from '../../scripts/globalize'; +import scrollHelper from '../../scripts/scrollHelper'; +import imageLoader from '../images/imageLoader'; +import browser from '../../scripts/browser'; +import { appHost } from '../apphost'; +import '../cardbuilder/card.css'; +import '../formdialog.css'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import './imageeditor.css'; +import ServerConnections from '../ServerConnections'; +import alert from '../alert'; +import confirm from '../confirm/confirm'; /* eslint-disable indent */ @@ -35,10 +38,10 @@ import 'css!./imageeditor'; let apiClient; if (item) { - apiClient = window.connectionManager.getApiClient(item.ServerId); + apiClient = ServerConnections.getApiClient(item.ServerId); reloadItem(page, item, apiClient, focusContext); } else { - apiClient = window.connectionManager.getApiClient(currentItem.ServerId); + apiClient = ServerConnections.getApiClient(currentItem.ServerId); apiClient.getItem(apiClient.getCurrentUserId(), currentItem.Id).then(function (item) { reloadItem(page, item, apiClient, focusContext); }); @@ -199,15 +202,11 @@ import 'css!./imageeditor'; return; } - import('confirm').then(({default: confirm}) => { - confirm({ - - text: globalize.translate('ConfirmDeleteImage'), - confirmText: globalize.translate('Delete'), - primary: 'delete' - - }).then(afterConfirm); - }); + confirm({ + text: globalize.translate('ConfirmDeleteImage'), + confirmText: globalize.translate('Delete'), + primary: 'delete' + }).then(afterConfirm); } function moveImage(context, apiClient, itemId, type, index, newIndex, focusContext) { @@ -215,9 +214,7 @@ import 'css!./imageeditor'; hasChanges = true; reload(context, null, focusContext); }, function () { - import('alert').then(({default: alert}) => { - alert(globalize.translate('ErrorDefault')); - }); + alert(globalize.translate('ErrorDefault')); }); } @@ -281,7 +278,7 @@ import 'css!./imageeditor'; } function showImageDownloader(page, imageType) { - import('imageDownloader').then(({default: ImageDownloader}) => { + import('../imageDownloader/imageDownloader').then((ImageDownloader) => { ImageDownloader.show(currentItem.Id, currentItem.ServerId, currentItem.Type, imageType).then(function () { hasChanges = true; reload(page); @@ -292,14 +289,14 @@ import 'css!./imageeditor'; function showActionSheet(context, imageCard) { const itemId = imageCard.getAttribute('data-id'); const serverId = imageCard.getAttribute('data-serverid'); - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); const type = imageCard.getAttribute('data-imagetype'); const index = parseInt(imageCard.getAttribute('data-index')); const providerCount = parseInt(imageCard.getAttribute('data-providers')); const numImages = parseInt(imageCard.getAttribute('data-numimages')); - import('actionsheet').then(({default: actionSheet}) => { + import('../actionSheet/actionSheet').then(({default: actionSheet}) => { const commands = []; commands.push({ @@ -370,7 +367,7 @@ import 'css!./imageeditor'; addListeners(context, 'btnOpenUploadMenu', 'click', function () { const imageType = this.getAttribute('data-imagetype'); - import('imageUploader').then(({default: imageUploader}) => { + import('../imageUploader/imageUploader').then(({default: imageUploader}) => { imageUploader.show({ theme: options.theme, @@ -403,7 +400,7 @@ import 'css!./imageeditor'; const type = this.getAttribute('data-imagetype'); let index = this.getAttribute('data-index'); index = index === 'null' ? null : parseInt(index); - const apiClient = window.connectionManager.getApiClient(currentItem.ServerId); + const apiClient = ServerConnections.getApiClient(currentItem.ServerId); deleteImage(context, currentItem.Id, type, index, apiClient, true); }); @@ -411,7 +408,7 @@ import 'css!./imageeditor'; const type = this.getAttribute('data-imagetype'); const index = this.getAttribute('data-index'); const newIndex = this.getAttribute('data-newindex'); - const apiClient = window.connectionManager.getApiClient(currentItem.ServerId); + const apiClient = ServerConnections.getApiClient(currentItem.ServerId); moveImage(context, apiClient, currentItem.Id, type, index, newIndex, dom.parentWithClass(this, 'itemsContainer')); }); } @@ -422,8 +419,8 @@ import 'css!./imageeditor'; loading.show(); - import('text!./imageeditor.template.html').then(({default: template}) => { - const apiClient = window.connectionManager.getApiClient(serverId); + import('./imageeditor.template.html').then(({default: template}) => { + const apiClient = ServerConnections.getApiClient(serverId); apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { const dialogOptions = { removeOnClose: true diff --git a/src/components/images/imageLoader.js b/src/components/images/imageLoader.js index 9dc708098d..1811b117e1 100644 --- a/src/components/images/imageLoader.js +++ b/src/components/images/imageLoader.js @@ -1,7 +1,7 @@ -import * as lazyLoader from 'lazyLoader'; -import * as userSettings from 'userSettings'; -import * as blurhash from 'blurhash'; -import 'css!./style'; +import * as lazyLoader from '../lazyLoader/lazyLoaderIntersectionObserver'; +import * as userSettings from '../../scripts/settings/userSettings'; +import { decode, isBlurhashValid } from 'blurhash'; +import './style.css'; /* eslint-disable indent */ export function lazyImage(elem, source = elem.getAttribute('data-src')) { @@ -13,7 +13,7 @@ import 'css!./style'; } function itemBlurhashing(target, blurhashstr) { - if (blurhash.isBlurhashValid(blurhashstr)) { + if (isBlurhashValid(blurhashstr)) { // Although the default values recommended by Blurhash developers is 32x32, a size of 18x18 seems to be the sweet spot for us, // improving the performance and reducing the memory usage, while retaining almost full blur quality. // Lower values had more visible pixelation @@ -21,7 +21,7 @@ import 'css!./style'; const height = 18; let pixels; try { - pixels = blurhash.decode(blurhashstr, width, height); + pixels = decode(blurhashstr, width, height); } catch (err) { console.error('Blurhash decode error: ', err); target.classList.add('non-blurhashable'); @@ -133,6 +133,7 @@ import 'css!./style'; } } } + lazyLoader.lazyChildren(elem, fillImage); } diff --git a/src/components/indicators/indicators.js b/src/components/indicators/indicators.js index bbd672ef72..5f8a8691d1 100644 --- a/src/components/indicators/indicators.js +++ b/src/components/indicators/indicators.js @@ -1,8 +1,8 @@ -import datetime from 'datetime'; -import itemHelper from 'itemHelper'; -import 'emby-progressbar'; -import 'css!./indicators.css'; -import 'material-icons'; +import datetime from '../../scripts/datetime'; +import itemHelper from '../itemHelper'; +import '../../elements/emby-progressbar/emby-progressbar'; +import './indicators.css'; +import 'material-design-icons-iconfont'; export function enableProgressIndicator(item) { if (item.MediaType === 'Video' && item.Type !== 'TvChannel') { diff --git a/src/components/itemContextMenu.js b/src/components/itemContextMenu.js index f36f623d9f..c8664512ef 100644 --- a/src/components/itemContextMenu.js +++ b/src/components/itemContextMenu.js @@ -1,10 +1,12 @@ -import appHost from 'apphost'; -import globalize from 'globalize'; -import itemHelper from 'itemHelper'; -import appRouter from 'appRouter'; -import playbackManager from 'playbackManager'; -import browser from 'browser'; -import actionsheet from 'actionsheet'; +import browser from '../scripts/browser'; +import globalize from '../scripts/globalize'; +import actionsheet from './actionSheet/actionSheet'; +import { appHost } from './apphost'; +import { appRouter } from './appRouter'; +import itemHelper from './itemHelper'; +import { playbackManager } from './playback/playbackmanager'; +import ServerConnections from './ServerConnections'; +import toast from './toast/toast'; /* eslint-disable indent */ export function getCommands(options) { @@ -329,12 +331,12 @@ import actionsheet from 'actionsheet'; function executeCommand(item, id, options) { const itemId = item.Id; const serverId = item.ServerId; - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return new Promise(function (resolve, reject) { switch (id) { case 'addtocollection': - import('collectionEditor').then(({default: collectionEditor}) => { + import('./collectionEditor/collectionEditor').then(({default: collectionEditor}) => { new collectionEditor({ items: [itemId], serverId: serverId @@ -342,7 +344,7 @@ import actionsheet from 'actionsheet'; }); break; case 'addtoplaylist': - import('playlistEditor').then(({default: playlistEditor}) => { + import('./playlisteditor/playlisteditor').then(({default: playlistEditor}) => { new playlistEditor({ items: [itemId], serverId: serverId @@ -350,7 +352,7 @@ import actionsheet from 'actionsheet'; }); break; case 'download': - import('fileDownloader').then((fileDownloader) => { + import('../scripts/fileDownloader').then((fileDownloader) => { const downloadHref = apiClient.getItemDownloadUrl(itemId); fileDownloader.download([{ url: downloadHref, @@ -372,9 +374,7 @@ import actionsheet from 'actionsheet'; textArea.select(); if (document.execCommand('copy')) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('CopyStreamURLSuccess')); - }); + toast(globalize.translate('CopyStreamURLSuccess')); } else { prompt(globalize.translate('CopyStreamURL'), downloadHref); } @@ -387,9 +387,7 @@ import actionsheet from 'actionsheet'; } else { /* eslint-disable-next-line compat/compat */ navigator.clipboard.writeText(downloadHref).then(function () { - import('toast').then(({default: toast}) => { - toast(globalize.translate('CopyStreamURLSuccess')); - }); + toast(globalize.translate('CopyStreamURLSuccess')); }).catch(function () { textAreaCopy(); }); @@ -398,7 +396,7 @@ import actionsheet from 'actionsheet'; break; } case 'editsubtitles': - import('subtitleEditor').then(({default: subtitleEditor}) => { + import('./subtitleeditor/subtitleeditor').then(({default: subtitleEditor}) => { subtitleEditor.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; @@ -406,7 +404,7 @@ import actionsheet from 'actionsheet'; editItem(apiClient, item).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); break; case 'editimages': - import('imageEditor').then(({default: imageEditor}) => { + import('./imageeditor/imageeditor').then((imageEditor) => { imageEditor.show({ itemId: itemId, serverId: serverId @@ -414,12 +412,12 @@ import actionsheet from 'actionsheet'; }); break; case 'identify': - import('itemIdentifier').then(({default: itemIdentifier}) => { + import('./itemidentifier/itemidentifier').then((itemIdentifier) => { itemIdentifier.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; case 'moremediainfo': - import('itemMediaInfo').then(({default: itemMediaInfo}) => { + import('./itemMediaInfo/itemMediaInfo').then((itemMediaInfo) => { itemMediaInfo.show(itemId, serverId).then(getResolveFunction(resolve, id), getResolveFunction(resolve, id)); }); break; @@ -454,7 +452,7 @@ import actionsheet from 'actionsheet'; playbackManager.clearQueue(); break; case 'record': - import('recordingCreator').then(({default: recordingCreator}) => { + import('./recordingcreator/recordingcreator').then(({default: recordingCreator}) => { recordingCreator.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; @@ -525,7 +523,7 @@ import actionsheet from 'actionsheet'; } function deleteTimer(apiClient, item, resolve, command) { - import('recordingHelper').then(({default: recordingHelper}) => { + import('./recordingcreator/recordinghelper').then(({default: recordingHelper}) => { const timerId = item.TimerId || item.Id; recordingHelper.cancelTimerWithConfirmation(timerId, item.ServerId).then(function () { getResolveFunction(resolve, command, true)(); @@ -534,7 +532,7 @@ import actionsheet from 'actionsheet'; } function deleteSeriesTimer(apiClient, item, resolve, command) { - import('recordingHelper').then(({default: recordingHelper}) => { + import('./recordingcreator/recordinghelper').then(({default: recordingHelper}) => { recordingHelper.cancelSeriesTimerWithConfirmation(item.Id, item.ServerId).then(function () { getResolveFunction(resolve, command, true)(); }); @@ -568,15 +566,15 @@ import actionsheet from 'actionsheet'; const serverId = apiClient.serverInfo().Id; if (item.Type === 'Timer') { - import('recordingEditor').then(({default: recordingEditor}) => { + import('./recordingcreator/recordingeditor').then(({default: recordingEditor}) => { recordingEditor.show(item.Id, serverId).then(resolve, reject); }); } else if (item.Type === 'SeriesTimer') { - import('seriesRecordingEditor').then(({default: recordingEditor}) => { + import('./recordingcreator/seriesrecordingeditor').then(({default: recordingEditor}) => { recordingEditor.show(item.Id, serverId).then(resolve, reject); }); } else { - import('metadataEditor').then(({default: metadataEditor}) => { + import('./metadataEditor/metadataEditor').then(({default: metadataEditor}) => { metadataEditor.show(item.Id, serverId).then(resolve, reject); }); } @@ -585,7 +583,7 @@ import actionsheet from 'actionsheet'; function deleteItem(apiClient, item) { return new Promise(function (resolve, reject) { - import('deleteHelper').then(({default: deleteHelper}) => { + import('../scripts/deleteHelper').then((deleteHelper) => { deleteHelper.deleteItem({ item: item, navigate: false @@ -597,7 +595,7 @@ import actionsheet from 'actionsheet'; } function refresh(apiClient, item) { - import('refreshDialog').then(({default: refreshDialog}) => { + import('./refreshdialog/refreshdialog').then(({default: refreshDialog}) => { new refreshDialog({ itemIds: [item.Id], serverId: apiClient.serverInfo().Id, diff --git a/src/components/itemHelper.js b/src/components/itemHelper.js index 7d1ac0e110..de771546b0 100644 --- a/src/components/itemHelper.js +++ b/src/components/itemHelper.js @@ -1,5 +1,5 @@ -import appHost from 'apphost'; -import globalize from 'globalize'; +import { appHost } from './apphost'; +import globalize from '../scripts/globalize'; export function getDisplayName(item, options = {}) { if (!item) { diff --git a/src/components/itemMediaInfo/itemMediaInfo.js b/src/components/itemMediaInfo/itemMediaInfo.js index bd3a157dab..2094bcdacc 100644 --- a/src/components/itemMediaInfo/itemMediaInfo.js +++ b/src/components/itemMediaInfo/itemMediaInfo.js @@ -5,17 +5,18 @@ * @module components/itemMediaInfo/itemMediaInfo */ -import dialogHelper from 'dialogHelper'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import loading from 'loading'; -import 'emby-select'; -import 'listViewStyle'; -import 'paper-icon-button-light'; -import 'css!./../formdialog'; -import 'material-icons'; -import 'emby-button'; -import 'flexStyles'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import loading from '../loading/loading'; +import '../../elements/emby-select/emby-select'; +import '../listview/listview.css'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../formdialog.css'; +import 'material-design-icons-iconfont'; +import '../../assets/css/flexstyles.scss'; +import ServerConnections from '../ServerConnections'; function setMediaInfo(user, page, item) { let html = item.MediaSources.map(version => { @@ -162,7 +163,7 @@ import 'flexStyles'; } function loadMediaInfo(itemId, serverId, template) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(item => { const dialogOptions = { size: 'small', @@ -193,7 +194,7 @@ import 'flexStyles'; export function show(itemId, serverId) { loading.show(); - return import('text!./itemMediaInfo.template.html').then(({default: template}) => { + return import('./itemMediaInfo.template.html').then(({default: template}) => { return new Promise((resolve, reject) => { loadMediaInfo(itemId, serverId, template).then(resolve, reject); }); diff --git a/src/components/itemidentifier/itemidentifier.js b/src/components/itemidentifier/itemidentifier.js index 956cbb4f64..382226478f 100644 --- a/src/components/itemidentifier/itemidentifier.js +++ b/src/components/itemidentifier/itemidentifier.js @@ -5,19 +5,21 @@ * @module components/itemidentifier/itemidentifier */ -import dialogHelper from 'dialogHelper'; -import loading from 'loading'; -import globalize from 'globalize'; -import scrollHelper from 'scrollHelper'; -import layoutManager from 'layoutManager'; -import focusManager from 'focusManager'; -import browser from 'browser'; -import 'emby-input'; -import 'emby-checkbox'; -import 'paper-icon-button-light'; -import 'css!./../formdialog'; -import 'material-icons'; -import 'cardStyle'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import loading from '../loading/loading'; +import globalize from '../../scripts/globalize'; +import scrollHelper from '../../scripts/scrollHelper'; +import layoutManager from '../layoutManager'; +import focusManager from '../focusManager'; +import browser from '../../scripts/browser'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../formdialog.css'; +import 'material-design-icons-iconfont'; +import '../cardbuilder/card.css'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; const enableFocusTransform = !browser.slow && !browser.edge; @@ -30,7 +32,7 @@ import 'cardStyle'; let currentSearchResult; function getApiClient() { - return window.connectionManager.getApiClient(currentServerId); + return ServerConnections.getApiClient(currentServerId); } function searchForIdentificationResults(page) { @@ -67,9 +69,7 @@ import 'cardStyle'; } if (!hasId && !lookupInfo.Name) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('PleaseEnterNameOrId')); - }); + toast(globalize.translate('PleaseEnterNameOrId')); return; } @@ -334,7 +334,7 @@ import 'cardStyle'; function showEditor(itemId) { loading.show(); - return import('text!./itemidentifier.template.html').then(({default: template}) => { + return import('./itemidentifier.template.html').then(({default: template}) => { const apiClient = getApiClient(); apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(item => { @@ -416,7 +416,7 @@ import 'cardStyle'; currentItem = null; currentItemType = itemType; - return import('text!./itemidentifier.template.html').then(({default: template}) => { + return import('./itemidentifier.template.html').then(({default: template}) => { const dialogOptions = { size: 'small', removeOnClose: true, diff --git a/src/components/itemsrefresher.js b/src/components/itemsrefresher.js index 3883e6e490..b5a73f5c45 100644 --- a/src/components/itemsrefresher.js +++ b/src/components/itemsrefresher.js @@ -1,6 +1,6 @@ -import playbackManager from 'playbackManager'; -import serverNotifications from 'serverNotifications'; -import events from 'events'; +import { playbackManager } from './playback/playbackmanager'; +import serverNotifications from '../scripts/serverNotifications'; +import { Events } from 'jellyfin-apiclient'; function onUserDataChanged(e, apiClient, userData) { const instance = this; @@ -110,7 +110,7 @@ function onPlaybackStopped(e, stopInfo) { function addNotificationEvent(instance, name, handler, owner) { const localHandler = handler.bind(instance); owner = owner || serverNotifications; - events.on(owner, name, localHandler); + Events.on(owner, name, localHandler); instance['event_' + name] = localHandler; } @@ -118,7 +118,7 @@ function removeNotificationEvent(instance, name, owner) { const handler = instance['event_' + name]; if (handler) { owner = owner || serverNotifications; - events.off(owner, name, handler); + Events.off(owner, name, handler); instance['event_' + name] = null; } } diff --git a/src/components/layoutManager.js b/src/components/layoutManager.js index 88a7265f8b..7268b3914b 100644 --- a/src/components/layoutManager.js +++ b/src/components/layoutManager.js @@ -1,6 +1,8 @@ -import browser from 'browser'; -import appSettings from 'appSettings'; -import events from 'events'; + +import { appHost } from './apphost'; +import browser from '../scripts/browser'; +import appSettings from '../scripts/settings/appSettings'; +import { Events } from 'jellyfin-apiclient'; function setLayout(instance, layout, selectedLayout) { if (layout === selectedLayout) { @@ -30,10 +32,10 @@ class LayoutManager { } } - events.trigger(this, 'modechange'); + Events.trigger(this, 'modechange'); } - getSavedLayout(layout) { + getSavedLayout() { return appSettings.get('layout'); } @@ -58,4 +60,12 @@ class LayoutManager { } } -export default new LayoutManager(); +const layoutManager = new LayoutManager(); + +if (appHost.getDefaultLayout) { + layoutManager.defaultLayout = appHost.getDefaultLayout(); +} + +layoutManager.init(); + +export default layoutManager; diff --git a/src/components/libraryoptionseditor/libraryoptionseditor.js b/src/components/libraryoptionseditor/libraryoptionseditor.js index 7146307d7f..b84eb67bf6 100644 --- a/src/components/libraryoptionseditor/libraryoptionseditor.js +++ b/src/components/libraryoptionseditor/libraryoptionseditor.js @@ -5,11 +5,11 @@ * @module components/libraryoptionseditor/libraryoptionseditor */ -import globalize from 'globalize'; -import dom from 'dom'; -import 'emby-checkbox'; -import 'emby-select'; -import 'emby-input'; +import globalize from '../../scripts/globalize'; +import dom from '../../scripts/dom'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-input/emby-input'; function populateLanguages(parent) { return ApiClient.getCultures().then(languages => { @@ -304,7 +304,7 @@ import 'emby-input'; } function showImageOptionsForType(type) { - import('imageoptionseditor').then(({default: ImageOptionsEditor}) => { + import('../imageOptionsEditor/imageOptionsEditor').then(({default: ImageOptionsEditor}) => { let typeOptions = getTypeOptions(currentLibraryOptions, type); if (!typeOptions) { typeOptions = { @@ -362,8 +362,9 @@ import 'emby-input'; currentAvailableOptions = null; const isNewLibrary = libraryOptions === null; isNewLibrary && parent.classList.add('newlibrary'); - const response = await fetch('components/libraryoptionseditor/libraryoptionseditor.template.html'); - const template = await response.text(); + + const { default: template } = await import('./libraryoptionseditor.template.html'); + parent.innerHTML = globalize.translateHtml(template); populateRefreshInterval(parent.querySelector('#selectAutoRefreshInterval')); const promises = [populateLanguages(parent), populateCountries(parent.querySelector('#selectCountry'))]; diff --git a/src/components/listview/listview.js b/src/components/listview/listview.js index e0fbc2fd2e..96ffcd0ad3 100644 --- a/src/components/listview/listview.js +++ b/src/components/listview/listview.js @@ -5,16 +5,17 @@ * @module components/listview/listview */ -import itemHelper from 'itemHelper'; -import mediaInfo from 'mediaInfo'; -import indicators from 'indicators'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import datetime from 'datetime'; -import cardBuilder from 'cardBuilder'; -import 'css!./listview'; -import 'emby-ratingbutton'; -import 'emby-playstatebutton'; +import itemHelper from '../itemHelper'; +import mediaInfo from '../mediainfo/mediainfo'; +import indicators from '../indicators/indicators'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import datetime from '../../scripts/datetime'; +import cardBuilder from '../cardbuilder/cardBuilder'; +import './listview.css'; +import '../../elements/emby-ratingbutton/emby-ratingbutton'; +import '../../elements/emby-playstatebutton/emby-playstatebutton'; +import ServerConnections from '../ServerConnections'; function getIndex(item, options) { if (options.index === 'disc') { @@ -76,7 +77,7 @@ import 'emby-playstatebutton'; } function getImageUrl(item, width) { - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); let itemId; const options = { @@ -105,7 +106,7 @@ import 'emby-playstatebutton'; } function getChannelImageUrl(item, width) { - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); const options = { maxWidth: width, type: 'Primary' diff --git a/src/components/loading/loading.js b/src/components/loading/loading.js index 8237611373..466173952c 100644 --- a/src/components/loading/loading.js +++ b/src/components/loading/loading.js @@ -1,4 +1,4 @@ -import 'css!./loading'; +import './loading.css'; let loadingElem; let layer1; diff --git a/src/components/maintabsmanager.js b/src/components/maintabsmanager.js index 1be1cf622f..1c63b53bed 100644 --- a/src/components/maintabsmanager.js +++ b/src/components/maintabsmanager.js @@ -1,8 +1,8 @@ -import dom from 'dom'; -import browser from 'browser'; -import events from 'events'; -import 'emby-tabs'; -import 'emby-button'; +import dom from '../scripts/dom'; +import browser from '../scripts/browser'; +import { Events } from 'jellyfin-apiclient'; +import '../elements/emby-tabs/emby-tabs'; +import '../elements/emby-button/emby-button'; /* eslint-disable indent */ @@ -65,11 +65,11 @@ import 'emby-button'; } }; - import('touchHelper').then(({default: TouchHelper}) => { + import('../scripts/touchHelper').then(({default: TouchHelper}) => { const touchHelper = new TouchHelper(view.parentNode.parentNode); - events.on(touchHelper, 'swipeleft', onSwipeLeft); - events.on(touchHelper, 'swiperight', onSwipeRight); + Events.on(touchHelper, 'swipeleft', onSwipeLeft); + Events.on(touchHelper, 'swiperight', onSwipeRight); view.addEventListener('viewdestroy', function () { touchHelper.destroy(); diff --git a/src/components/mediaLibraryCreator/mediaLibraryCreator.js b/src/components/mediaLibraryCreator/mediaLibraryCreator.js index 4e0d7b026c..e19f78ab67 100644 --- a/src/components/mediaLibraryCreator/mediaLibraryCreator.js +++ b/src/components/mediaLibraryCreator/mediaLibraryCreator.js @@ -5,20 +5,22 @@ * @module components/mediaLibraryCreator/mediaLibraryCreator */ -import loading from 'loading'; -import dialogHelper from 'dialogHelper'; -import dom from 'dom'; -import $ from 'jQuery'; -import libraryoptionseditor from 'components/libraryoptionseditor/libraryoptionseditor'; -import globalize from 'globalize'; -import 'emby-toggle'; -import 'emby-input'; -import 'emby-select'; -import 'paper-icon-button-light'; -import 'listViewStyle'; -import 'formDialogStyle'; -import 'emby-button'; -import 'flexStyles'; +import loading from '../loading/loading'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import dom from '../../scripts/dom'; +import 'jquery'; +import libraryoptionseditor from '../libraryoptionseditor/libraryoptionseditor'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-toggle/emby-toggle'; +import '../listview/listview.css'; +import '../formdialog.css'; +import '../../assets/css/flexstyles.scss'; +import toast from '../toast/toast'; +import alert from '../alert'; function onAddLibrary() { if (isCreating) { @@ -26,11 +28,9 @@ import 'flexStyles'; } if (pathInfos.length == 0) { - import('alert').then(({default: alert}) => { - alert({ - text: globalize.translate('PleaseAddAtLeastOneFolder'), - type: 'error' - }); + alert({ + text: globalize.translate('PleaseAddAtLeastOneFolder'), + type: 'error' }); return false; @@ -54,9 +54,7 @@ import 'flexStyles'; loading.hide(); dialogHelper.close(dlg); }, () => { - import('toast').then(({default: toast}) => { - toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder')); - }); + toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder')); isCreating = false; loading.hide(); @@ -109,7 +107,7 @@ import 'flexStyles'; function onAddButtonClick() { const page = dom.parentWithClass(this, 'dlg-librarycreator'); - import('directorybrowser').then(({default: directoryBrowser}) => { + import('../directorybrowser/directorybrowser').then(({default: directoryBrowser}) => { const picker = new directoryBrowser(); picker.show({ enableNetworkSharePath: true, @@ -200,7 +198,7 @@ export class showEditor { currentOptions = options; currentResolve = resolve; hasChanges = false; - import('text!./components/mediaLibraryCreator/mediaLibraryCreator.template.html').then(({default: template}) => { + import('./mediaLibraryCreator.template.html').then(({default: template}) => { const dlg = dialogHelper.createDialog({ size: 'small', modal: false, diff --git a/src/components/mediaLibraryEditor/mediaLibraryEditor.js b/src/components/mediaLibraryEditor/mediaLibraryEditor.js index 13d264f4c9..68fbd73b9b 100644 --- a/src/components/mediaLibraryEditor/mediaLibraryEditor.js +++ b/src/components/mediaLibraryEditor/mediaLibraryEditor.js @@ -5,18 +5,20 @@ * @module components/mediaLibraryEditor/mediaLibraryEditor */ -import jQuery from 'jQuery'; -import loading from 'loading'; -import dialogHelper from 'dialogHelper'; -import dom from 'dom'; -import libraryoptionseditor from 'components/libraryoptionseditor/libraryoptionseditor'; -import globalize from 'globalize'; -import 'emby-button'; -import 'listViewStyle'; -import 'paper-icon-button-light'; -import 'formDialogStyle'; -import 'emby-toggle'; -import 'flexStyles'; +import 'jquery'; +import loading from '../loading/loading'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import dom from '../../scripts/dom'; +import libraryoptionseditor from '../libraryoptionseditor/libraryoptionseditor'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-button/emby-button'; +import '../listview/listview.css'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../formdialog.css'; +import '../../elements/emby-toggle/emby-toggle'; +import '../../assets/css/flexstyles.scss'; +import toast from '../toast/toast'; +import confirm from '../confirm/confirm'; function onEditLibrary() { if (isCreating) { @@ -47,9 +49,7 @@ import 'flexStyles'; hasChanges = true; refreshLibraryFromServer(page); }, () => { - import('toast').then(({default: toast}) => { - toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder')); - }); + toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder')); }); } @@ -62,9 +62,7 @@ import 'flexStyles'; hasChanges = true; refreshLibraryFromServer(page); }, () => { - import('toast').then(({default: toast}) => { - toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder')); - }); + toast(globalize.translate('ErrorAddingMediaPathToVirtualFolder')); }); } @@ -72,22 +70,18 @@ import 'flexStyles'; const button = btnRemovePath; const virtualFolder = currentOptions.library; - import('confirm').then(({default: confirm}) => { - confirm({ - title: globalize.translate('HeaderRemoveMediaLocation'), - text: globalize.translate('MessageConfirmRemoveMediaLocation'), - confirmText: globalize.translate('Delete'), - primary: 'delete' - }).then(() => { - const refreshAfterChange = currentOptions.refresh; - ApiClient.removeMediaPath(virtualFolder.Name, location, refreshAfterChange).then(() => { - hasChanges = true; - refreshLibraryFromServer(dom.parentWithClass(button, 'dlg-libraryeditor')); - }, () => { - import('toast').then(({default: toast}) => { - toast(globalize.translate('ErrorDefault')); - }); - }); + confirm({ + title: globalize.translate('HeaderRemoveMediaLocation'), + text: globalize.translate('MessageConfirmRemoveMediaLocation'), + confirmText: globalize.translate('Delete'), + primary: 'delete' + }).then(() => { + const refreshAfterChange = currentOptions.refresh; + ApiClient.removeMediaPath(virtualFolder.Name, location, refreshAfterChange).then(() => { + hasChanges = true; + refreshLibraryFromServer(dom.parentWithClass(button, 'dlg-libraryeditor')); + }, () => { + toast(globalize.translate('ErrorDefault')); }); }); } @@ -167,7 +161,7 @@ import 'flexStyles'; } function showDirectoryBrowser(context, originalPath, networkPath) { - import('directorybrowser').then(({default: directoryBrowser}) => { + import('../directorybrowser/directorybrowser').then(({default: directoryBrowser}) => { const picker = new directoryBrowser(); picker.show({ enableNetworkSharePath: true, @@ -215,7 +209,7 @@ export class showEditor { currentOptions = options; currentDeferred = deferred; hasChanges = false; - import('text!./components/mediaLibraryEditor/mediaLibraryEditor.template.html').then(({default: template}) => { + import('./mediaLibraryEditor.template.html').then(({default: template}) => { const dlg = dialogHelper.createDialog({ size: 'small', modal: false, diff --git a/src/components/mediainfo/mediainfo.js b/src/components/mediainfo/mediainfo.js index d5da29d3bc..73193f3758 100644 --- a/src/components/mediainfo/mediainfo.js +++ b/src/components/mediainfo/mediainfo.js @@ -1,12 +1,12 @@ -import datetime from 'datetime'; -import globalize from 'globalize'; -import appRouter from 'appRouter'; -import itemHelper from 'itemHelper'; -import indicators from 'indicators'; -import 'material-icons'; -import 'css!./mediainfo.css'; -import 'programStyles'; -import 'emby-button'; +import datetime from '../../scripts/datetime'; +import globalize from '../../scripts/globalize'; +import { appRouter } from '../appRouter'; +import itemHelper from '../itemHelper'; +import indicators from '../indicators/indicators'; +import 'material-design-icons-iconfont'; +import './mediainfo.css'; +import '../guide/programs.css'; +import '../../elements/emby-button/emby-button'; /* eslint-disable indent */ function getTimerIndicator(item) { diff --git a/src/components/metadataEditor/metadataEditor.js b/src/components/metadataEditor/metadataEditor.js index b768e77c4f..71bf1bc00b 100644 --- a/src/components/metadataEditor/metadataEditor.js +++ b/src/components/metadataEditor/metadataEditor.js @@ -1,21 +1,24 @@ -import dom from 'dom'; -import layoutManager from 'layoutManager'; -import dialogHelper from 'dialogHelper'; -import datetime from 'datetime'; -import loading from 'loading'; -import focusManager from 'focusManager'; -import globalize from 'globalize'; -import shell from 'shell'; -import 'emby-checkbox'; -import 'emby-input'; -import 'emby-select'; -import 'listViewStyle'; -import 'emby-textarea'; -import 'emby-button'; -import 'paper-icon-button-light'; -import 'css!./../formdialog'; -import 'clearButtonStyle'; -import 'flexStyles'; +import dom from '../../scripts/dom'; +import layoutManager from '../layoutManager'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import datetime from '../../scripts/datetime'; +import loading from '../loading/loading'; +import focusManager from '../focusManager'; +import globalize from '../../scripts/globalize'; +import shell from '../../scripts/shell'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-select/emby-select'; +import '../listview/listview.css'; +import '../../elements/emby-textarea/emby-textarea'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../formdialog.css'; +import '../../assets/css/clearbutton.scss'; +import '../../assets/css/flexstyles.scss'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; +import { appRouter } from '../appRouter'; /* eslint-disable indent */ @@ -35,9 +38,7 @@ import 'flexStyles'; function submitUpdatedItem(form, item) { function afterContentTypeUpdated() { - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageItemSaved')); - }); + toast(globalize.translate('MessageItemSaved')); loading.hide(); closeDialog(true); @@ -207,7 +208,7 @@ import 'flexStyles'; } function addElementToList(source, sortCallback) { - import('prompt').then(({default: prompt}) => { + import('../prompt/prompt').then(({default: prompt}) => { prompt({ label: 'Value:' }).then(function (text) { @@ -225,7 +226,7 @@ import 'flexStyles'; } function editPerson(context, person, index) { - import('personEditor').then(({default: personEditor}) => { + import('./personEditor').then(({default: personEditor}) => { personEditor.show(person).then(function (updatedPerson) { const isNew = index === -1; @@ -244,14 +245,12 @@ import 'flexStyles'; if (parentId) { reload(context, parentId, item.ServerId); } else { - import('appRouter').then(({default: appRouter}) => { - appRouter.goHome(); - }); + appRouter.goHome(); } } function showMoreMenu(context, button, user) { - import('itemContextMenu').then(({default: itemContextMenu}) => { + import('../itemContextMenu').then(({default: itemContextMenu}) => { const item = currentItem; itemContextMenu.show({ @@ -289,7 +288,7 @@ import 'flexStyles'; } function getApiClient() { - return window.connectionManager.getApiClient(currentItem.ServerId); + return ServerConnections.getApiClient(currentItem.ServerId); } function bindAll(elems, eventName, fn) { @@ -369,7 +368,7 @@ import 'flexStyles'; } function getItem(itemId, serverId) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); if (itemId) { return apiClient.getItem(apiClient.getCurrentUserId(), itemId); @@ -379,7 +378,7 @@ import 'flexStyles'; } function getEditorConfig(itemId, serverId) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); if (itemId) { return apiClient.getJSON(apiClient.getUrl('Items/' + itemId + '/MetadataEditor')); @@ -1020,7 +1019,7 @@ import 'flexStyles'; } function centerFocus(elem, horiz, on) { - import('scrollHelper').then(({default: scrollHelper}) => { + import('../../scripts/scrollHelper').then((scrollHelper) => { const fn = on ? 'on' : 'off'; scrollHelper.centerFocus[fn](elem, horiz); }); @@ -1029,7 +1028,7 @@ import 'flexStyles'; function show(itemId, serverId, resolve, reject) { loading.show(); - import('text!./metadataEditor.template.html').then(({default: template}) => { + import('./metadataEditor.template.html').then(({default: template}) => { const dialogOptions = { removeOnClose: true, scrollY: false @@ -1067,7 +1066,7 @@ import 'flexStyles'; currentContext = dlg; - init(dlg, window.connectionManager.getApiClient(serverId)); + init(dlg, ServerConnections.getApiClient(serverId)); reload(dlg, itemId, serverId); }); @@ -1084,7 +1083,7 @@ import 'flexStyles'; return new Promise(function (resolve, reject) { loading.show(); - import('text!./metadataEditor.template.html').then(({default: template}) => { + import('./metadataEditor.template.html').then(({default: template}) => { elem.innerHTML = globalize.translateHtml(template, 'core'); elem.querySelector('.formDialogFooter').classList.remove('formDialogFooter'); @@ -1094,7 +1093,7 @@ import 'flexStyles'; currentContext = elem; - init(elem, window.connectionManager.getApiClient(serverId)); + init(elem, ServerConnections.getApiClient(serverId)); reload(elem, itemId, serverId); focusManager.autoFocus(elem); diff --git a/src/components/metadataEditor/personEditor.js b/src/components/metadataEditor/personEditor.js index 8326971247..f64f7330d7 100644 --- a/src/components/metadataEditor/personEditor.js +++ b/src/components/metadataEditor/personEditor.js @@ -1,15 +1,16 @@ -import dialogHelper from 'dialogHelper'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import 'paper-icon-button-light'; -import 'emby-input'; -import 'emby-select'; -import 'css!./../formdialog'; + +import dialogHelper from '../dialogHelper/dialogHelper'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-select/emby-select'; +import '../formdialog.css'; /* eslint-disable indent */ function centerFocus(elem, horiz, on) { - import('scrollHelper').then(({default: scrollHelper}) => { + import('../../scripts/scrollHelper').then((scrollHelper) => { const fn = on ? 'on' : 'off'; scrollHelper.centerFocus[fn](elem, horiz); }); @@ -17,7 +18,7 @@ import 'css!./../formdialog'; function show(person) { return new Promise(function (resolve, reject) { - import('text!./personEditor.template.html').then(({default: template}) => { + import('./personEditor.template.html').then(({default: template}) => { const dialogOptions = { removeOnClose: true, scrollY: false diff --git a/src/components/multiSelect/multiSelect.css b/src/components/multiSelect/multiSelect.css index e9c66c57a4..39b50aa17e 100644 --- a/src/components/multiSelect/multiSelect.css +++ b/src/components/multiSelect/multiSelect.css @@ -1,3 +1,4 @@ + .itemSelectionPanel { position: absolute; bottom: 0; diff --git a/src/components/multiSelect/multiSelect.js b/src/components/multiSelect/multiSelect.js index e7ce440f06..8cfc838d09 100644 --- a/src/components/multiSelect/multiSelect.js +++ b/src/components/multiSelect/multiSelect.js @@ -1,9 +1,13 @@ -import browser from 'browser'; -import appHost from 'apphost'; -import loading from 'loading'; -import globalize from 'globalize'; -import dom from 'dom'; -import 'css!./multiSelect'; +import browser from '../../scripts/browser'; +import { appHost } from '../apphost'; +import loading from '../loading/loading'; +import globalize from '../../scripts/globalize'; +import dom from '../../scripts/dom'; +import './multiSelect.css'; +import ServerConnections from '../ServerConnections'; +import alert from '../alert'; +import playlistEditor from '../playlisteditor/playlisteditor'; +import confirm from '../confirm/confirm'; /* eslint-disable indent */ @@ -137,10 +141,8 @@ import 'css!./multiSelect'; } function alertText(options) { - return new Promise((resolve, reject) => { - import('alert').then(({default: alert}) => { - alert(options).then(resolve, resolve); - }); + return new Promise((resolve) => { + alert(options).then(resolve, resolve); }); } @@ -154,22 +156,20 @@ import 'css!./multiSelect'; title = globalize.translate('HeaderDeleteItems'); } - import('confirm').then(({default: confirm}) => { - confirm(msg, title).then(() => { - const promises = itemIds.map(itemId => { - apiClient.deleteItem(itemId); - }); + confirm(msg, title).then(() => { + const promises = itemIds.map(itemId => { + apiClient.deleteItem(itemId); + }); - Promise.all(promises).then(resolve, () => { - alertText(globalize.translate('ErrorDeletingItem')).then(reject, reject); - }); - }, reject); - }); + Promise.all(promises).then(resolve, () => { + alertText(globalize.translate('ErrorDeletingItem')).then(reject, reject); + }); + }, reject); }); } function showMenuForSelectedItems(e) { - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); apiClient.getCurrentUser().then(user => { const menuItems = []; @@ -229,7 +229,7 @@ import 'css!./multiSelect'; icon: 'refresh' }); - import('actionsheet').then(({default: actionsheet}) => { + import('../actionSheet/actionSheet').then((actionsheet) => { actionsheet.show({ items: menuItems, positionTo: e.target, @@ -239,7 +239,7 @@ import 'css!./multiSelect'; switch (id) { case 'addtocollection': - import('collectionEditor').then(({default: collectionEditor}) => { + import('../collectionEditor/collectionEditor').then(({default: collectionEditor}) => { new collectionEditor({ items: items, serverId: serverId @@ -249,11 +249,9 @@ import 'css!./multiSelect'; dispatchNeedsRefresh(); break; case 'playlist': - import('playlistEditor').then(({default: playlistEditor}) => { - new playlistEditor({ - items: items, - serverId: serverId - }); + new playlistEditor({ + items: items, + serverId: serverId }); hideSelections(); dispatchNeedsRefresh(); @@ -281,7 +279,7 @@ import 'css!./multiSelect'; dispatchNeedsRefresh(); break; case 'refresh': - import('refreshDialog').then(({default: refreshDialog}) => { + import('../refreshdialog/refreshdialog').then(({default: refreshDialog}) => { new refreshDialog({ itemIds: items, serverId: serverId @@ -317,11 +315,8 @@ import 'css!./multiSelect'; function combineVersions(apiClient, selection) { if (selection.length < 2) { - import('alert').then(({default: alert}) => { - alert({ - - text: globalize.translate('PleaseSelectTwoItems') - }); + alert({ + text: globalize.translate('PleaseSelectTwoItems') }); return; } @@ -341,7 +336,7 @@ import 'css!./multiSelect'; } function showSelections(initialCard) { - import('emby-checkbox').then(() => { + import('../../elements/emby-checkbox/emby-checkbox').then(() => { const cards = document.querySelectorAll('.card'); for (let i = 0, length = cards.length; i < length; i++) { showSelection(cards[i], initialCard === cards[i]); diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 7f0e68f1d9..0267c378ce 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -1,7 +1,7 @@ -import serverNotifications from 'serverNotifications'; -import playbackManager from 'playbackManager'; -import events from 'events'; -import globalize from 'globalize'; +import serverNotifications from '../../scripts/serverNotifications'; +import { playbackManager } from '../playback/playbackmanager'; +import { Events } from 'jellyfin-apiclient'; +import globalize from '../../scripts/globalize'; function onOneDocumentClick() { document.removeEventListener('click', onOneDocumentClick); @@ -199,27 +199,27 @@ function showPackageInstallNotification(apiClient, installation, status) { }); } -events.on(serverNotifications, 'LibraryChanged', function (e, apiClient, data) { +Events.on(serverNotifications, 'LibraryChanged', function (e, apiClient, data) { onLibraryChanged(data, apiClient); }); -events.on(serverNotifications, 'PackageInstallationCompleted', function (e, apiClient, data) { +Events.on(serverNotifications, 'PackageInstallationCompleted', function (e, apiClient, data) { showPackageInstallNotification(apiClient, data, 'completed'); }); -events.on(serverNotifications, 'PackageInstallationFailed', function (e, apiClient, data) { +Events.on(serverNotifications, 'PackageInstallationFailed', function (e, apiClient, data) { showPackageInstallNotification(apiClient, data, 'failed'); }); -events.on(serverNotifications, 'PackageInstallationCancelled', function (e, apiClient, data) { +Events.on(serverNotifications, 'PackageInstallationCancelled', function (e, apiClient, data) { showPackageInstallNotification(apiClient, data, 'cancelled'); }); -events.on(serverNotifications, 'PackageInstalling', function (e, apiClient, data) { +Events.on(serverNotifications, 'PackageInstalling', function (e, apiClient, data) { showPackageInstallNotification(apiClient, data, 'progress'); }); -events.on(serverNotifications, 'ServerShuttingDown', function (e, apiClient, data) { +Events.on(serverNotifications, 'ServerShuttingDown', function (e, apiClient, data) { const serverId = apiClient.serverInfo().Id; const notification = { tag: 'restart' + serverId, @@ -228,7 +228,7 @@ events.on(serverNotifications, 'ServerShuttingDown', function (e, apiClient, dat showNotification(notification, 0, apiClient); }); -events.on(serverNotifications, 'ServerRestarting', function (e, apiClient, data) { +Events.on(serverNotifications, 'ServerRestarting', function (e, apiClient, data) { const serverId = apiClient.serverInfo().Id; const notification = { tag: 'restart' + serverId, @@ -237,7 +237,7 @@ events.on(serverNotifications, 'ServerRestarting', function (e, apiClient, data) showNotification(notification, 0, apiClient); }); -events.on(serverNotifications, 'RestartRequired', function (e, apiClient) { +Events.on(serverNotifications, 'RestartRequired', function (e, apiClient) { const serverId = apiClient.serverInfo().Id; const notification = { tag: 'restart' + serverId, diff --git a/src/components/nowPlayingBar/nowPlayingBar.js b/src/components/nowPlayingBar/nowPlayingBar.js index 6218197700..992c73a543 100644 --- a/src/components/nowPlayingBar/nowPlayingBar.js +++ b/src/components/nowPlayingBar/nowPlayingBar.js @@ -1,15 +1,21 @@ -import datetime from 'datetime'; -import events from 'events'; -import browser from 'browser'; -import imageLoader from 'imageLoader'; -import layoutManager from 'layoutManager'; -import playbackManager from 'playbackManager'; -import nowPlayingHelper from 'nowPlayingHelper'; -import appHost from 'apphost'; -import dom from 'dom'; -import itemContextMenu from 'itemContextMenu'; -import 'paper-icon-button-light'; -import 'emby-ratingbutton'; +import datetime from '../../scripts/datetime'; +import { Events } from 'jellyfin-apiclient'; +import browser from '../../scripts/browser'; +import imageLoader from '../images/imageLoader'; +import layoutManager from '../layoutManager'; +import { playbackManager } from '../playback/playbackmanager'; +import nowPlayingHelper from '../playback/nowplayinghelper'; +import { appHost } from '../apphost'; +import dom from '../../scripts/dom'; +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.css'; +import '../../elements/emby-slider/emby-slider'; +import { appRouter } from '../appRouter'; /* eslint-disable indent */ @@ -243,52 +249,42 @@ import 'emby-ratingbutton'; } function showRemoteControl() { - import('appRouter').then(({default: appRouter}) => { - appRouter.showNowPlaying(); - }); + appRouter.showNowPlaying(); } let nowPlayingBarElement; function getNowPlayingBar() { if (nowPlayingBarElement) { - return Promise.resolve(nowPlayingBarElement); + return nowPlayingBarElement; } - return new Promise(function (resolve, reject) { - Promise.all([ - import('appFooter-shared'), - import('itemShortcuts'), - import('css!./nowPlayingBar.css'), - import('emby-slider') - ]) - .then(([appfooter, itemShortcuts]) => { - const parentContainer = appfooter.element; - nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); + const parentContainer = appFooter.element; + nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); - if (nowPlayingBarElement) { - resolve(nowPlayingBarElement); - return; - } + if (nowPlayingBarElement) { + return nowPlayingBarElement; + } - parentContainer.insertAdjacentHTML('afterbegin', getNowPlayingBarHtml()); - nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); + parentContainer.insertAdjacentHTML('afterbegin', getNowPlayingBarHtml()); + window.CustomElements.upgradeSubtree(parentContainer); - if (layoutManager.mobile) { - hideButton(nowPlayingBarElement.querySelector('.btnShuffleQueue')); - hideButton(nowPlayingBarElement.querySelector('.nowPlayingBarCenter')); - } + nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); - if (browser.safari && browser.slow) { - // Not handled well here. The wrong elements receive events, bar doesn't update quickly enough, etc. - nowPlayingBarElement.classList.add('noMediaProgress'); - } + if (layoutManager.mobile) { + hideButton(nowPlayingBarElement.querySelector('.btnShuffleQueue')); + hideButton(nowPlayingBarElement.querySelector('.nowPlayingBarCenter')); + } - itemShortcuts.on(nowPlayingBarElement); + if (browser.safari && browser.slow) { + // Not handled well here. The wrong elements receive events, bar doesn't update quickly enough, etc. + nowPlayingBarElement.classList.add('noMediaProgress'); + } - bindEvents(nowPlayingBarElement); - resolve(nowPlayingBarElement); - }); - }); + itemShortcuts.on(nowPlayingBarElement); + + bindEvents(nowPlayingBarElement); + + return nowPlayingBarElement; } function showButton(button) { @@ -451,7 +447,7 @@ import 'emby-ratingbutton'; if (item.SeriesPrimaryImageTag) { options.tag = item.SeriesPrimaryImageTag; - return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); + return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); } } @@ -459,12 +455,12 @@ import 'emby-ratingbutton'; if (item.SeriesThumbImageTag) { options.tag = item.SeriesThumbImageTag; - return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); + return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); } if (item.ParentThumbImageTag) { options.tag = item.ParentThumbImageTag; - return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options); + return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options); } } @@ -481,12 +477,12 @@ import 'emby-ratingbutton'; if (item.ImageTags && item.ImageTags[options.type]) { options.tag = item.ImageTags[options.type]; - return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.PrimaryImageItemId || item.Id, options); + return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.PrimaryImageItemId || item.Id, options); } if (item.AlbumId && item.AlbumPrimaryImageTag) { options.tag = item.AlbumPrimaryImageTag; - return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options); + return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options); } return null; @@ -547,7 +543,7 @@ import 'emby-ratingbutton'; if (nowPlayingItem.Id) { if (isRefreshing) { - const apiClient = window.connectionManager.getApiClient(nowPlayingItem.ServerId); + const apiClient = ServerConnections.getApiClient(nowPlayingItem.ServerId); apiClient.getItem(apiClient.getCurrentUserId(), nowPlayingItem.Id).then(function (item) { const userData = item.UserData || {}; const likes = userData.Likes == null ? '' : userData.Likes; @@ -620,7 +616,7 @@ import 'emby-ratingbutton'; return; } - getNowPlayingBar().then(slideUp); + slideUp(getNowPlayingBar()); } function hideNowPlayingBar() { @@ -681,9 +677,8 @@ import 'emby-ratingbutton'; return; } - getNowPlayingBar().then(function () { - updatePlayerStateInternal(event, state, player); - }); + getNowPlayingBar(); + updatePlayerStateInternal(event, state, player); } function onTimeUpdate(e) { @@ -707,15 +702,15 @@ import 'emby-ratingbutton'; const player = currentPlayer; if (player) { - events.off(player, 'playbackstart', onPlaybackStart); - events.off(player, 'statechange', onPlaybackStart); - events.off(player, 'repeatmodechange', onRepeatModeChange); - events.off(player, 'shufflequeuemodechange', onQueueShuffleModeChange); - events.off(player, 'playbackstop', onPlaybackStopped); - events.off(player, 'volumechange', onVolumeChanged); - events.off(player, 'pause', onPlayPauseStateChanged); - events.off(player, 'unpause', onPlayPauseStateChanged); - events.off(player, 'timeupdate', onTimeUpdate); + Events.off(player, 'playbackstart', onPlaybackStart); + Events.off(player, 'statechange', onPlaybackStart); + Events.off(player, 'repeatmodechange', onRepeatModeChange); + Events.off(player, 'shufflequeuemodechange', onQueueShuffleModeChange); + Events.off(player, 'playbackstop', onPlaybackStopped); + Events.off(player, 'volumechange', onVolumeChanged); + Events.off(player, 'pause', onPlayPauseStateChanged); + Events.off(player, 'unpause', onPlayPauseStateChanged); + Events.off(player, 'timeupdate', onTimeUpdate); currentPlayer = null; hideNowPlayingBar(); @@ -753,18 +748,18 @@ import 'emby-ratingbutton'; refreshFromPlayer(player); - events.on(player, 'playbackstart', onPlaybackStart); - events.on(player, 'statechange', onPlaybackStart); - events.on(player, 'repeatmodechange', onRepeatModeChange); - events.on(player, 'shufflequeuemodechange', onQueueShuffleModeChange); - events.on(player, 'playbackstop', onPlaybackStopped); - events.on(player, 'volumechange', onVolumeChanged); - events.on(player, 'pause', onPlayPauseStateChanged); - events.on(player, 'unpause', onPlayPauseStateChanged); - events.on(player, 'timeupdate', onTimeUpdate); + Events.on(player, 'playbackstart', onPlaybackStart); + Events.on(player, 'statechange', onPlaybackStart); + Events.on(player, 'repeatmodechange', onRepeatModeChange); + Events.on(player, 'shufflequeuemodechange', onQueueShuffleModeChange); + Events.on(player, 'playbackstop', onPlaybackStopped); + Events.on(player, 'volumechange', onVolumeChanged); + Events.on(player, 'pause', onPlayPauseStateChanged); + Events.on(player, 'unpause', onPlayPauseStateChanged); + Events.on(player, 'timeupdate', onTimeUpdate); } - events.on(playbackManager, 'playerchange', function () { + Events.on(playbackManager, 'playerchange', function () { bindToPlayer(playbackManager.getCurrentPlayer()); }); diff --git a/src/components/packageManager.js b/src/components/packageManager.js index c4b4701e9d..3890577b24 100644 --- a/src/components/packageManager.js +++ b/src/components/packageManager.js @@ -1,5 +1,5 @@ -import appSettings from 'appSettings'; -import pluginManager from 'pluginManager'; +import appSettings from '../scripts/settings/appSettings'; +import { pluginManager } from './pluginManager'; /* eslint-disable indent */ class PackageManager { diff --git a/src/components/playback/brightnessosd.js b/src/components/playback/brightnessosd.js index 78c40d10c1..e6c4b82f2e 100644 --- a/src/components/playback/brightnessosd.js +++ b/src/components/playback/brightnessosd.js @@ -1,9 +1,9 @@ -import events from 'events'; -import playbackManager from 'playbackManager'; -import dom from 'dom'; -import browser from 'browser'; -import 'css!./iconosd'; -import 'material-icons'; +import { Events } from 'jellyfin-apiclient'; +import { playbackManager } from './playbackmanager'; +import dom from '../../scripts/dom'; +import browser from '../../scripts/browser'; +import './iconosd.css'; +import 'material-design-icons-iconfont'; let currentPlayer; let osdElement; @@ -121,8 +121,8 @@ function releaseCurrentPlayer() { const player = currentPlayer; if (player) { - events.off(player, 'brightnesschange', onBrightnessChanged); - events.off(player, 'playbackstop', hideOsd); + Events.off(player, 'brightnesschange', onBrightnessChanged); + Events.off(player, 'playbackstop', hideOsd); currentPlayer = null; } } @@ -151,11 +151,11 @@ function bindToPlayer(player) { } hideOsd(); - events.on(player, 'brightnesschange', onBrightnessChanged); - events.on(player, 'playbackstop', hideOsd); + Events.on(player, 'brightnesschange', onBrightnessChanged); + Events.on(player, 'playbackstop', hideOsd); } -events.on(playbackManager, 'playerchange', function () { +Events.on(playbackManager, 'playerchange', function () { bindToPlayer(playbackManager.getCurrentPlayer()); }); diff --git a/src/components/playback/mediasession.js b/src/components/playback/mediasession.js index b75fa69a8c..9cfb9f3b0b 100644 --- a/src/components/playback/mediasession.js +++ b/src/components/playback/mediasession.js @@ -1,7 +1,9 @@ -import playbackManager from 'playbackManager'; -import nowPlayingHelper from 'nowPlayingHelper'; -import shell from 'shell'; -import events from 'events'; +import { playbackManager } from '../playback/playbackmanager'; +import nowPlayingHelper from '../playback/nowplayinghelper'; +import { Events } from 'jellyfin-apiclient'; +import ServerConnections from '../ServerConnections'; +import shell from '../../scripts/shell'; + /* eslint-disable indent */ // Reports media playback to the device for lock screen control @@ -16,16 +18,16 @@ import events from 'events'; } else if (options.type === 'Primary' && item.SeriesPrimaryImageTag) { options.tag = item.SeriesPrimaryImageTag; - return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); + return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); } else if (options.type === 'Thumb') { if (item.SeriesThumbImageTag) { options.tag = item.SeriesThumbImageTag; - return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); + return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.SeriesId, options); } else if (item.ParentThumbImageTag) { options.tag = item.ParentThumbImageTag; - return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options); + return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.ParentThumbItemId, options); } } @@ -38,11 +40,11 @@ import events from 'events'; if (item.ImageTags && item.ImageTags[options.type]) { options.tag = item.ImageTags[options.type]; - return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.Id, options); + return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.Id, options); } else if (item.AlbumId && item.AlbumPrimaryImageTag) { options.tag = item.AlbumPrimaryImageTag; - return window.connectionManager.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options); + return ServerConnections.getApiClient(item.ServerId).getScaledImageUrl(item.AlbumId, options); } return null; @@ -164,12 +166,12 @@ import events from 'events'; function releaseCurrentPlayer() { if (currentPlayer) { - events.off(currentPlayer, 'playbackstart', onPlaybackStart); - events.off(currentPlayer, 'playbackstop', onPlaybackStopped); - events.off(currentPlayer, 'unpause', onGeneralEvent); - events.off(currentPlayer, 'pause', onGeneralEvent); - events.off(currentPlayer, 'statechange', onStateChanged); - events.off(currentPlayer, 'timeupdate', onGeneralEvent); + Events.off(currentPlayer, 'playbackstart', onPlaybackStart); + Events.off(currentPlayer, 'playbackstop', onPlaybackStopped); + Events.off(currentPlayer, 'unpause', onGeneralEvent); + Events.off(currentPlayer, 'pause', onGeneralEvent); + Events.off(currentPlayer, 'statechange', onStateChanged); + Events.off(currentPlayer, 'timeupdate', onGeneralEvent); currentPlayer = null; @@ -198,12 +200,12 @@ import events from 'events'; const state = playbackManager.getPlayerState(player); updatePlayerState(player, state, 'init'); - events.on(currentPlayer, 'playbackstart', onPlaybackStart); - events.on(currentPlayer, 'playbackstop', onPlaybackStopped); - events.on(currentPlayer, 'unpause', onGeneralEvent); - events.on(currentPlayer, 'pause', onGeneralEvent); - events.on(currentPlayer, 'statechange', onStateChanged); - events.on(currentPlayer, 'timeupdate', onGeneralEvent); + Events.on(currentPlayer, 'playbackstart', onPlaybackStart); + Events.on(currentPlayer, 'playbackstop', onPlaybackStopped); + Events.on(currentPlayer, 'unpause', onGeneralEvent); + Events.on(currentPlayer, 'pause', onGeneralEvent); + Events.on(currentPlayer, 'statechange', onStateChanged); + Events.on(currentPlayer, 'timeupdate', onGeneralEvent); } function execute(name) { @@ -251,7 +253,7 @@ import events from 'events'; }); } - events.on(playbackManager, 'playerchange', function () { + Events.on(playbackManager, 'playerchange', function () { bindToPlayer(playbackManager.getCurrentPlayer()); }); diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index a68663a769..dbf2a37813 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -1,14 +1,16 @@ -import events from 'events'; -import datetime from 'datetime'; -import appSettings from 'appSettings'; -import itemHelper from 'itemHelper'; -import pluginManager from 'pluginManager'; -import PlayQueueManager from 'playQueueManager'; -import * as userSettings from 'userSettings'; -import globalize from 'globalize'; -import loading from 'loading'; -import appHost from 'apphost'; -import screenfull from 'screenfull'; +import { Events } from 'jellyfin-apiclient'; +import datetime from '../../scripts/datetime'; +import appSettings from '../../scripts/settings/appSettings'; +import itemHelper from '../itemHelper'; +import { pluginManager } from '../pluginManager'; +import PlayQueueManager from './playqueuemanager'; +import * as userSettings from '../../scripts/settings/userSettings'; +import globalize from '../../scripts/globalize'; +import loading from '../loading/loading'; +import { appHost } from '../apphost'; +import * as Screenfull from 'screenfull'; +import ServerConnections from '../ServerConnections'; +import alert from '../alert'; function enableLocalPlaylistManagement(player) { if (player.getPlaylist) { @@ -23,14 +25,14 @@ function enableLocalPlaylistManagement(player) { } function bindToFullscreenChange(player) { - if (screenfull.isEnabled) { - screenfull.on('change', function () { - events.trigger(player, 'fullscreenchange'); + if (Screenfull.isEnabled) { + Screenfull.on('change', function () { + Events.trigger(player, 'fullscreenchange'); }); } else { // iOS Safari document.addEventListener('webkitfullscreenchange', function () { - events.trigger(player, 'fullscreenchange'); + Events.trigger(player, 'fullscreenchange'); }, false); } } @@ -46,14 +48,14 @@ function triggerPlayerChange(playbackManagerInstance, newPlayer, newTarget, prev } } - events.trigger(playbackManagerInstance, 'playerchange', [newPlayer, newTarget, previousPlayer]); + Events.trigger(playbackManagerInstance, 'playerchange', [newPlayer, newTarget, previousPlayer]); } function reportPlayback(playbackManagerInstance, state, player, reportPlaylist, serverId, method, progressEventName) { if (!serverId) { // Not a server item // We can expand on this later and possibly report them - events.trigger(playbackManagerInstance, 'reportplayback', [false]); + Events.trigger(playbackManagerInstance, 'reportplayback', [false]); return; } @@ -68,11 +70,11 @@ function reportPlayback(playbackManagerInstance, state, player, reportPlaylist, addPlaylistToPlaybackReport(playbackManagerInstance, info, player, serverId); } - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); const reportPlaybackPromise = apiClient[method](info); // Notify that report has been sent reportPlaybackPromise.then(() => { - events.trigger(playbackManagerInstance, 'reportplayback', [true]); + Events.trigger(playbackManagerInstance, 'reportplayback', [true]); }); } @@ -105,7 +107,7 @@ function normalizeName(t) { } function getItemsForPlayback(serverId, query) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); if (query.Ids && query.Ids.split(',').length === 1) { const itemId = query.Ids.split(','); @@ -619,7 +621,7 @@ function supportsDirectPlay(apiClient, item, mediaSource) { } else if (mediaSource.Protocol === 'File') { return new Promise(function (resolve, reject) { // Determine if the file can be accessed directly - import('filesystem').then((filesystem) => { + import('../../scripts/filesystem').then((filesystem) => { const method = isFolderRip ? 'directoryExists' : 'fileExists'; @@ -647,11 +649,9 @@ function validatePlaybackInfoResult(instance, result) { } function showPlaybackInfoErrorMessage(instance, errorCode) { - import('alert').then(({ default: alert }) => { - alert({ - text: globalize.translate(errorCode), - title: globalize.translate('HeaderPlaybackError') - }); + alert({ + text: globalize.translate(errorCode), + title: globalize.translate('HeaderPlaybackError') }); } @@ -852,13 +852,13 @@ class PlaybackManager { player.tryPair(targetInfo) : Promise.resolve(); - events.trigger(self, 'pairing'); + Events.trigger(self, 'pairing'); promise.then(function () { - events.trigger(self, 'paired'); + Events.trigger(self, 'paired'); setCurrentPlayerInternal(player, targetInfo); }, function () { - events.trigger(self, 'pairerror'); + Events.trigger(self, 'pairerror'); if (currentPairingId === targetInfo.id) { currentPairingId = null; } @@ -869,7 +869,7 @@ class PlaybackManager { const promises = players.filter(displayPlayerIndividually).map(getPlayerTargets); return Promise.all(promises).then(function (responses) { - return window.connectionManager.currentApiClient().getCurrentUser().then(function (user) { + return ServerConnections.currentApiClient().getCurrentUser().then(function (user) { const targets = []; targets.push({ @@ -1161,7 +1161,7 @@ class PlaybackManager { if (!brightnessOsdLoaded) { brightnessOsdLoaded = true; // TODO: Have this trigger an event instead to get the osd out of here - import('brightnessOsd').then(); + import('./brightnessosd').then(); } player.setBrightness(val); } @@ -1367,7 +1367,7 @@ class PlaybackManager { function getSavedMaxStreamingBitrate(apiClient, mediaType) { if (!apiClient) { // This should hopefully never happen - apiClient = window.connectionManager.currentApiClient(); + apiClient = ServerConnections.currentApiClient(); } const endpointInfo = apiClient.getSavedEndpointInfo() || {}; @@ -1390,7 +1390,7 @@ class PlaybackManager { const mediaType = playerData.streamInfo ? playerData.streamInfo.mediaType : null; const currentItem = self.currentItem(player); - const apiClient = currentItem ? window.connectionManager.getApiClient(currentItem.ServerId) : window.connectionManager.currentApiClient(); + const apiClient = currentItem ? ServerConnections.getApiClient(currentItem.ServerId) : ServerConnections.currentApiClient(); return getSavedMaxStreamingBitrate(apiClient, mediaType); }; @@ -1404,7 +1404,7 @@ class PlaybackManager { const mediaType = playerData.streamInfo ? playerData.streamInfo.mediaType : null; const currentItem = self.currentItem(player); - const apiClient = currentItem ? window.connectionManager.getApiClient(currentItem.ServerId) : window.connectionManager.currentApiClient(); + const apiClient = currentItem ? ServerConnections.getApiClient(currentItem.ServerId) : ServerConnections.currentApiClient(); const endpointInfo = apiClient.getSavedEndpointInfo() || {}; return appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType); @@ -1416,7 +1416,7 @@ class PlaybackManager { return player.setMaxStreamingBitrate(options); } - const apiClient = window.connectionManager.getApiClient(self.currentItem(player).ServerId); + const apiClient = ServerConnections.getApiClient(self.currentItem(player).ServerId); apiClient.getEndpointInfo().then(function (endpointInfo) { const playerData = getPlayerData(player); @@ -1447,12 +1447,12 @@ class PlaybackManager { return player.isFullscreen(); } - if (!screenfull.isEnabled) { + if (!Screenfull.isEnabled) { // iOS Safari return document.webkitIsFullScreen; } - return screenfull.isFullscreen; + return Screenfull.isFullscreen; }; self.toggleFullscreen = function (player) { @@ -1461,8 +1461,8 @@ class PlaybackManager { return player.toggleFullscreen(); } - if (screenfull.isEnabled) { - screenfull.toggle(); + if (Screenfull.isEnabled) { + Screenfull.toggle(); } else { // iOS Safari if (document.webkitIsFullScreen && document.webkitCancelFullscreen) { @@ -1678,7 +1678,7 @@ class PlaybackManager { const subtitleStreamIndex = params.SubtitleStreamIndex == null ? getPlayerData(player).subtitleStreamIndex : params.SubtitleStreamIndex; let currentMediaSource = self.currentMediaSource(player); - const apiClient = window.connectionManager.getApiClient(currentItem.ServerId); + const apiClient = ServerConnections.getApiClient(currentItem.ServerId); if (ticks) { ticks = parseInt(ticks); @@ -1834,7 +1834,7 @@ class PlaybackManager { }, queryOptions)); } else if (firstItem.Type === 'Episode' && items.length === 1 && getPlayer(firstItem, options).supportsProgress !== false) { promise = new Promise(function (resolve, reject) { - const apiClient = window.connectionManager.getApiClient(firstItem.ServerId); + const apiClient = ServerConnections.getApiClient(firstItem.ServerId); apiClient.getCurrentUser().then(function (user) { if (!user.Configuration.EnableNextEpisodeAutoPlay || !firstItem.SeriesId) { @@ -2065,7 +2065,7 @@ class PlaybackManager { return playOther(items, options, user); } - const apiClient = window.connectionManager.getApiClient(firstItem.ServerId); + const apiClient = ServerConnections.getApiClient(firstItem.ServerId); return getIntros(firstItem, apiClient, options).then(function (introsResult) { const introItems = introsResult.Items; @@ -2128,14 +2128,14 @@ class PlaybackManager { const mediaType = item.MediaType; const onBitrateDetectionFailure = function () { - return playAfterBitrateDetect(getSavedMaxStreamingBitrate(window.connectionManager.getApiClient(item.ServerId), mediaType), item, playOptions, onPlaybackStartedFn); + return playAfterBitrateDetect(getSavedMaxStreamingBitrate(ServerConnections.getApiClient(item.ServerId), mediaType), item, playOptions, onPlaybackStartedFn); }; if (!isServerItem(item) || itemHelper.isLocalItem(item)) { return onBitrateDetectionFailure(); } - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); apiClient.getEndpointInfo().then(function (endpointInfo) { if ((mediaType === 'Video' || mediaType === 'Audio') && appSettings.enableAutomaticBitrateDetection(endpointInfo.IsInNetwork, mediaType)) { return apiClient.detectBitrate().then(function (bitrate) { @@ -2158,7 +2158,7 @@ class PlaybackManager { removeCurrentPlayer(player); } - events.trigger(self, 'playbackcancelled'); + Events.trigger(self, 'playbackcancelled'); return Promise.reject(); } @@ -2254,7 +2254,7 @@ class PlaybackManager { return Promise.all([promise, player.getDeviceProfile(item)]).then(function (responses) { const deviceProfile = responses[1]; - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); const mediaSourceId = playOptions.mediaSourceId; const audioStreamIndex = playOptions.audioStreamIndex; @@ -2299,11 +2299,11 @@ class PlaybackManager { const startPosition = options.startPositionTicks || 0; const mediaType = options.mediaType || item.MediaType; const player = getPlayer(item, options); - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); // Call this just to ensure the value is recorded, it is needed with getSavedMaxStreamingBitrate return apiClient.getEndpointInfo().then(function () { - const maxBitrate = getSavedMaxStreamingBitrate(window.connectionManager.getApiClient(item.ServerId), mediaType); + const maxBitrate = getSavedMaxStreamingBitrate(ServerConnections.getApiClient(item.ServerId), mediaType); return player.getDeviceProfile(item).then(function (deviceProfile) { return getPlaybackMediaSource(player, apiClient, deviceProfile, maxBitrate, item, startPosition, options.mediaSourceId, options.audioStreamIndex, options.subtitleStreamIndex).then(function (mediaSource) { @@ -2319,11 +2319,11 @@ class PlaybackManager { const mediaType = options.mediaType || item.MediaType; // TODO: Remove the true forceLocalPlayer hack const player = getPlayer(item, options, true); - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); // Call this just to ensure the value is recorded, it is needed with getSavedMaxStreamingBitrate return apiClient.getEndpointInfo().then(function () { - const maxBitrate = getSavedMaxStreamingBitrate(window.connectionManager.getApiClient(item.ServerId), mediaType); + const maxBitrate = getSavedMaxStreamingBitrate(ServerConnections.getApiClient(item.ServerId), mediaType); return player.getDeviceProfile(item).then(function (deviceProfile) { return getPlaybackInfo(player, apiClient, item, deviceProfile, maxBitrate, startPosition, false, null, null, null, null).then(function (playbackInfoResult) { @@ -2548,7 +2548,7 @@ class PlaybackManager { const isCurrentIndex = removeResult.isCurrentIndex; - events.trigger(player, 'playlistitemremove', [ + Events.trigger(player, 'playlistitemremove', [ { playlistItemIds: playlistItemIds } @@ -2573,7 +2573,7 @@ class PlaybackManager { return; } - events.trigger(player, 'playlistitemmove', [ + Events.trigger(player, 'playlistitemmove', [ { playlistItemId: moveResult.playlistItemId, newIndex: moveResult.newIndex @@ -2707,7 +2707,7 @@ class PlaybackManager { const queueDirectToPlayer = player && !enableLocalPlaylistManagement(player); if (queueDirectToPlayer) { - const apiClient = window.connectionManager.getApiClient(items[0].ServerId); + const apiClient = ServerConnections.getApiClient(items[0].ServerId); player.getDeviceProfile(items[0]).then(function (profile) { setStreamUrls(items, profile, self.getMaxStreamingBitrate(player), apiClient, 0).then(function () { @@ -2727,7 +2727,7 @@ class PlaybackManager { } else { self._playQueueManager.queue(items); } - events.trigger(player, 'playlistitemadd'); + Events.trigger(player, 'playlistitemadd'); } function onPlayerProgressInterval() { @@ -2779,8 +2779,8 @@ class PlaybackManager { state.IsFirstItem = isFirstItem; state.IsFullscreen = fullscreen; - events.trigger(player, 'playbackstart', [state]); - events.trigger(self, 'playbackstart', [player, state]); + Events.trigger(player, 'playbackstart', [state]); + Events.trigger(self, 'playbackstart', [player, state]); // only used internally as a safeguard to avoid reporting other events to the server before playback start streamInfo.started = true; @@ -2810,8 +2810,8 @@ class PlaybackManager { state.IsFirstItem = isFirstItem; state.IsFullscreen = fullscreen; - events.trigger(player, 'playbackstart', [state]); - events.trigger(self, 'playbackstart', [player, state]); + Events.trigger(player, 'playbackstart', [state]); + Events.trigger(self, 'playbackstart', [player, state]); // only used internally as a safeguard to avoid reporting other events to the server before playback start streamInfo.started = true; @@ -2850,8 +2850,8 @@ class PlaybackManager { state.NextItem = playbackStopInfo.nextItem; - events.trigger(player, 'playbackstop', [state]); - events.trigger(self, 'playbackstop', [playbackStopInfo]); + Events.trigger(player, 'playbackstop', [state]); + Events.trigger(self, 'playbackstop', [playbackStopInfo]); const nextItemPlayOptions = nextItem ? (nextItem.item.playOptions || getDefaultPlayOptions()) : getDefaultPlayOptions(); const newPlayer = nextItem ? getPlayer(nextItem.item, nextItemPlayOptions) : null; @@ -2952,8 +2952,8 @@ class PlaybackManager { self._playQueueManager.reset(); } - events.trigger(player, 'playbackstop', [state]); - events.trigger(self, 'playbackstop', [playbackStopInfo]); + Events.trigger(player, 'playbackstop', [state]); + Events.trigger(self, 'playbackstop', [playbackStopInfo]); const nextItemPlayOptions = nextItem ? (nextItem.item.playOptions || getDefaultPlayOptions()) : getDefaultPlayOptions(); const newPlayer = nextItem ? getPlayer(nextItem.item, nextItemPlayOptions) : null; @@ -2999,7 +2999,7 @@ class PlaybackManager { reportPlayback(self, state, activePlayer, true, serverId, 'reportPlaybackStopped'); } - events.trigger(self, 'playbackstop', [{ + Events.trigger(self, 'playbackstop', [{ player: activePlayer, state: state, nextItem: newItem, @@ -3010,8 +3010,8 @@ class PlaybackManager { function bindStopped(player) { if (enableLocalPlaylistManagement(player)) { - events.off(player, 'stopped', onPlaybackStopped); - events.on(player, 'stopped', onPlaybackStopped); + Events.off(player, 'stopped', onPlaybackStopped); + Events.on(player, 'stopped', onPlaybackStopped); } } @@ -3061,7 +3061,7 @@ class PlaybackManager { } function unbindStopped(player) { - events.off(player, 'stopped', onPlaybackStopped); + Events.off(player, 'stopped', onPlaybackStopped); } function initLegacyVolumeMethods(player) { @@ -3090,28 +3090,28 @@ class PlaybackManager { } if (enableLocalPlaylistManagement(player)) { - events.on(player, 'error', onPlaybackError); - events.on(player, 'timeupdate', onPlaybackTimeUpdate); - events.on(player, 'pause', onPlaybackPause); - events.on(player, 'unpause', onPlaybackUnpause); - events.on(player, 'volumechange', onPlaybackVolumeChange); - events.on(player, 'repeatmodechange', onRepeatModeChange); - events.on(player, 'shufflequeuemodechange', onShuffleQueueModeChange); - events.on(player, 'playlistitemmove', onPlaylistItemMove); - events.on(player, 'playlistitemremove', onPlaylistItemRemove); - events.on(player, 'playlistitemadd', onPlaylistItemAdd); + Events.on(player, 'error', onPlaybackError); + Events.on(player, 'timeupdate', onPlaybackTimeUpdate); + Events.on(player, 'pause', onPlaybackPause); + Events.on(player, 'unpause', onPlaybackUnpause); + Events.on(player, 'volumechange', onPlaybackVolumeChange); + Events.on(player, 'repeatmodechange', onRepeatModeChange); + Events.on(player, 'shufflequeuemodechange', onShuffleQueueModeChange); + Events.on(player, 'playlistitemmove', onPlaylistItemMove); + Events.on(player, 'playlistitemremove', onPlaylistItemRemove); + Events.on(player, 'playlistitemadd', onPlaylistItemAdd); } else if (player.isLocalPlayer) { - events.on(player, 'itemstarted', onPlaybackStartedFromSelfManagingPlayer); - events.on(player, 'itemstopped', onPlaybackStoppedFromSelfManagingPlayer); - events.on(player, 'timeupdate', onPlaybackTimeUpdate); - events.on(player, 'pause', onPlaybackPause); - events.on(player, 'unpause', onPlaybackUnpause); - events.on(player, 'volumechange', onPlaybackVolumeChange); - events.on(player, 'repeatmodechange', onRepeatModeChange); - events.on(player, 'shufflequeuemodechange', onShuffleQueueModeChange); - events.on(player, 'playlistitemmove', onPlaylistItemMove); - events.on(player, 'playlistitemremove', onPlaylistItemRemove); - events.on(player, 'playlistitemadd', onPlaylistItemAdd); + Events.on(player, 'itemstarted', onPlaybackStartedFromSelfManagingPlayer); + Events.on(player, 'itemstopped', onPlaybackStoppedFromSelfManagingPlayer); + Events.on(player, 'timeupdate', onPlaybackTimeUpdate); + Events.on(player, 'pause', onPlaybackPause); + Events.on(player, 'unpause', onPlaybackUnpause); + Events.on(player, 'volumechange', onPlaybackVolumeChange); + Events.on(player, 'repeatmodechange', onRepeatModeChange); + Events.on(player, 'shufflequeuemodechange', onShuffleQueueModeChange); + Events.on(player, 'playlistitemmove', onPlaylistItemMove); + Events.on(player, 'playlistitemremove', onPlaylistItemRemove); + Events.on(player, 'playlistitemadd', onPlaylistItemAdd); } if (player.isLocalPlayer) { @@ -3120,13 +3120,13 @@ class PlaybackManager { bindStopped(player); } - events.on(pluginManager, 'registered', function (e, plugin) { + Events.on(pluginManager, 'registered', function (e, plugin) { if (plugin.type === 'mediaplayer') { initMediaPlayer(plugin); } }); - pluginManager.ofType('mediaplayer').map(initMediaPlayer); + pluginManager.ofType('mediaplayer').forEach(initMediaPlayer); function sendProgressUpdate(player, progressEventName, reportPlaylist) { if (!player) { @@ -3157,15 +3157,15 @@ class PlaybackManager { streamInfo.lastMediaInfoQuery = new Date().getTime(); - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); if (!apiClient.isMinServerVersion('3.2.70.7')) { return; } - window.connectionManager.getApiClient(serverId).getLiveStreamMediaInfo(liveStreamId).then(function (info) { + ServerConnections.getApiClient(serverId).getLiveStreamMediaInfo(liveStreamId).then(function (info) { mediaSource.MediaStreams = info.MediaStreams; - events.trigger(player, 'mediastreamschange'); + Events.trigger(player, 'mediastreamschange'); }, function () { }); } @@ -3190,9 +3190,9 @@ class PlaybackManager { }; if (appHost.supports('remotecontrol')) { - import('serverNotifications').then(({ default: serverNotifications }) => { - events.on(serverNotifications, 'ServerShuttingDown', self.setDefaultPlayerActive.bind(self)); - events.on(serverNotifications, 'ServerRestarting', self.setDefaultPlayerActive.bind(self)); + import('../../scripts/serverNotifications').then(({ default: serverNotifications }) => { + Events.on(serverNotifications, 'ServerShuttingDown', self.setDefaultPlayerActive.bind(self)); + Events.on(serverNotifications, 'ServerRestarting', self.setDefaultPlayerActive.bind(self)); }); } } @@ -3220,7 +3220,7 @@ class PlaybackManager { return Promise.reject(); } - const apiClient = window.connectionManager.getApiClient(nextItem.item.ServerId); + const apiClient = ServerConnections.getApiClient(nextItem.item.ServerId); return apiClient.getItem(apiClient.getCurrentUserId(), nextItem.item.Id); } @@ -3361,7 +3361,7 @@ class PlaybackManager { return player.playTrailers(item); } - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); const instance = this; @@ -3393,7 +3393,7 @@ class PlaybackManager { } getSubtitleUrl(textStream, serverId) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return !textStream.IsExternalUrl ? apiClient.getUrl(textStream.DeliveryUrl) : textStream.DeliveryUrl; } @@ -3473,7 +3473,7 @@ class PlaybackManager { return player.instantMix(item); } - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); const options = {}; options.UserId = apiClient.getCurrentUserId(); @@ -3588,7 +3588,7 @@ class PlaybackManager { } this._playQueueManager.setRepeatMode(value); - events.trigger(player, 'repeatmodechange'); + Events.trigger(player, 'repeatmodechange'); } getRepeatMode(player = this._currentPlayer) { @@ -3605,7 +3605,7 @@ class PlaybackManager { } this._playQueueManager.setShuffleMode(value); - events.trigger(player, 'shufflequeuemodechange'); + Events.trigger(player, 'shufflequeuemodechange'); } getQueueShuffleMode(player = this._currentPlayer) { @@ -3633,7 +3633,7 @@ class PlaybackManager { } else { this._playQueueManager.toggleShuffleMode(); } - events.trigger(player, 'shufflequeuemodechange'); + Events.trigger(player, 'shufflequeuemodechange'); } clearQueue(clearCurrentItem = false, player = this._currentPlayer) { @@ -3642,7 +3642,7 @@ class PlaybackManager { } this._playQueueManager.clearPlaylist(clearCurrentItem); - events.trigger(player, 'playlistitemremove'); + Events.trigger(player, 'playlistitemremove'); } trySetActiveDeviceName(name) { @@ -3757,4 +3757,12 @@ class PlaybackManager { } } -export default new PlaybackManager(); +export const playbackManager = new PlaybackManager(); + +window.addEventListener('beforeunload', function () { + try { + playbackManager.onAppClose(); + } catch (err) { + console.error('error in onAppClose: ' + err); + } +}); diff --git a/src/components/playback/playbackorientation.js b/src/components/playback/playbackorientation.js index f585f25ae1..d763726b10 100644 --- a/src/components/playback/playbackorientation.js +++ b/src/components/playback/playbackorientation.js @@ -1,6 +1,7 @@ -import playbackManager from 'playbackManager'; -import layoutManager from 'layoutManager'; -import events from 'events'; + +import { playbackManager } from './playbackmanager'; +import layoutManager from '../layoutManager'; +import { Events } from 'jellyfin-apiclient'; let orientationLocked; @@ -13,7 +14,7 @@ function onOrientationChangeError(err) { console.error('error locking orientation: ' + err); } -events.on(playbackManager, 'playbackstart', function (e, player, state) { +Events.on(playbackManager, 'playbackstart', function (e, player, state) { const isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player); if (isLocalVideo && layoutManager.mobile) { @@ -36,7 +37,7 @@ events.on(playbackManager, 'playbackstart', function (e, player, state) { } }); -events.on(playbackManager, 'playbackstop', function (e, playbackStopInfo) { +Events.on(playbackManager, 'playbackstop', function (e, playbackStopInfo) { if (orientationLocked && !playbackStopInfo.nextMediaType) { /* eslint-disable-next-line compat/compat */ const unlockOrientation = window.screen.unlockOrientation || window.screen.mozUnlockOrientation || window.screen.msUnlockOrientation || (window.screen.orientation && window.screen.orientation.unlock); diff --git a/src/components/playback/playerSelectionMenu.js b/src/components/playback/playerSelectionMenu.js index 38ff399a9e..35871c1fe8 100644 --- a/src/components/playback/playerSelectionMenu.js +++ b/src/components/playback/playerSelectionMenu.js @@ -1,12 +1,16 @@ -import appSettings from 'appSettings'; -import events from 'events'; -import browser from 'browser'; -import loading from 'loading'; -import playbackManager from 'playbackManager'; -import appRouter from 'appRouter'; -import globalize from 'globalize'; -import appHost from 'apphost'; -import * as autocast from 'autocast'; +import appSettings from '../../scripts/settings/appSettings'; +import { Events } from 'jellyfin-apiclient'; +import browser from '../../scripts/browser'; +import loading from '../loading/loading'; +import { playbackManager } from '../playback/playbackmanager'; +import { appRouter } from '../appRouter'; +import globalize from '../../scripts/globalize'; +import { appHost } from '../apphost'; +import { enable, isEnabled, supported } from '../../scripts/autocast'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-button/emby-button'; +import dialog from '../dialog/dialog'; +import dialogHelper from '../dialogHelper/dialogHelper'; function mirrorItem(info, player) { const item = info.item; @@ -108,7 +112,7 @@ export function show(button) { }; }); - import('actionsheet').then(({default: actionsheet}) => { + import('../actionSheet/actionSheet').then((actionsheet) => { loading.hide(); const menuOptions = { @@ -140,54 +144,45 @@ export function show(button) { } function showActivePlayerMenu(playerInfo) { - Promise.all([ - import('dialogHelper'), - import('dialog'), - import('emby-checkbox'), - import('emby-button') - ]).then(([dialogHelper]) => { - showActivePlayerMenuInternal(dialogHelper, playerInfo); - }); + showActivePlayerMenuInternal(playerInfo); } function disconnectFromPlayer(currentDeviceName) { if (playbackManager.getSupportedCommands().indexOf('EndSession') !== -1) { - import('dialog').then(({default: dialog}) => { - const menuItems = []; + const menuItems = []; - menuItems.push({ - name: globalize.translate('Yes'), - id: 'yes' - }); - menuItems.push({ - name: globalize.translate('No'), - id: 'no' - }); + menuItems.push({ + name: globalize.translate('Yes'), + id: 'yes' + }); + menuItems.push({ + name: globalize.translate('No'), + id: 'no' + }); - dialog({ - buttons: menuItems, - text: globalize.translate('ConfirmEndPlayerSession', currentDeviceName) + dialog.show({ + buttons: menuItems, + text: globalize.translate('ConfirmEndPlayerSession', currentDeviceName) - }).then(function (id) { - switch (id) { - case 'yes': - playbackManager.getCurrentPlayer().endSession(); - playbackManager.setDefaultPlayerActive(); - break; - case 'no': - playbackManager.setDefaultPlayerActive(); - break; - default: - break; - } - }); + }).then(function (id) { + switch (id) { + case 'yes': + playbackManager.getCurrentPlayer().endSession(); + playbackManager.setDefaultPlayerActive(); + break; + case 'no': + playbackManager.setDefaultPlayerActive(); + break; + default: + break; + } }); } else { playbackManager.setDefaultPlayerActive(); } } -function showActivePlayerMenuInternal(dialogHelper, playerInfo) { +function showActivePlayerMenuInternal(playerInfo) { let html = ''; const dialogOptions = { @@ -222,9 +217,9 @@ function showActivePlayerMenuInternal(dialogHelper, playerInfo) { html += ''; - if (autocast.supported()) { + if (supported()) { html += '
'; @@ -285,7 +280,7 @@ function onMirrorChange() { } function onAutoCastChange() { - autocast.enable(this.checked); + enable(this.checked); } document.addEventListener('viewshow', function (e) { @@ -300,21 +295,21 @@ document.addEventListener('viewshow', function (e) { } }); -events.on(appSettings, 'change', function (e, name) { +Events.on(appSettings, 'change', function (e, name) { if (name === 'displaymirror') { mirrorIfEnabled(); } }); -events.on(playbackManager, 'pairing', function (e) { +Events.on(playbackManager, 'pairing', function (e) { loading.show(); }); -events.on(playbackManager, 'paired', function (e) { +Events.on(playbackManager, 'paired', function (e) { loading.hide(); }); -events.on(playbackManager, 'pairerror', function (e) { +Events.on(playbackManager, 'pairerror', function (e) { loading.hide(); }); diff --git a/src/components/playback/playersettingsmenu.js b/src/components/playback/playersettingsmenu.js index 40819f62ea..cc05d6e704 100644 --- a/src/components/playback/playersettingsmenu.js +++ b/src/components/playback/playersettingsmenu.js @@ -1,7 +1,8 @@ -import actionsheet from 'actionsheet'; -import playbackManager from 'playbackManager'; -import globalize from 'globalize'; -import qualityoptions from 'qualityoptions'; +import actionsheet from '../actionSheet/actionSheet'; +import { playbackManager } from '../playback/playbackmanager'; +import globalize from '../../scripts/globalize'; +import qualityoptions from '../qualityOptions'; +import ServerConnections from '../ServerConnections'; function showQualityMenu(player, btn) { const videoStream = playbackManager.currentMediaSource(player).MediaStreams.filter(function (stream) { @@ -32,18 +33,18 @@ function showQualityMenu(player, btn) { return opt; }); - let selectedId = options.filter(function (o) { + const selectedId = options.filter(function (o) { return o.selected; }); - selectedId = selectedId.length ? selectedId[0].bitrate : null; + const selectedBitrate = selectedId.length ? selectedId[0].bitrate : null; return actionsheet.show({ items: menuItems, positionTo: btn }).then(function (id) { const bitrate = parseInt(id); - if (bitrate !== selectedId) { + if (bitrate !== selectedBitrate) { playbackManager.setMaxStreamingBitrate({ enableAutomaticBitrateDetection: bitrate ? false : true, maxBitrate: bitrate @@ -250,7 +251,7 @@ export function show(options) { return showWithUser(options, player, null); } - const apiClient = window.connectionManager.getApiClient(currentItem.ServerId); + const apiClient = ServerConnections.getApiClient(currentItem.ServerId); return apiClient.getCurrentUser().then(function (user) { return showWithUser(options, player, user); }); diff --git a/src/components/playback/playmethodhelper.js b/src/components/playback/playmethodhelper.js index 50210aa2f2..1e7002b45d 100644 --- a/src/components/playback/playmethodhelper.js +++ b/src/components/playback/playmethodhelper.js @@ -3,7 +3,9 @@ export function getDisplayPlayMethod(session) { return null; } - if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect) { + if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect && session.TranscodingInfo.IsAudioDirect) { + return 'Remux'; + } else if (session.TranscodingInfo && session.TranscodingInfo.IsVideoDirect) { return 'DirectStream'; } else if (session.PlayState.PlayMethod === 'Transcode') { return 'Transcode'; diff --git a/src/components/playback/remotecontrolautoplay.js b/src/components/playback/remotecontrolautoplay.js index c0adb57a45..6d862a5491 100644 --- a/src/components/playback/remotecontrolautoplay.js +++ b/src/components/playback/remotecontrolautoplay.js @@ -1,5 +1,5 @@ -import events from 'events'; -import playbackManager from 'playbackManager'; +import { Events } from 'jellyfin-apiclient'; +import { playbackManager } from '../playback/playbackmanager'; function transferPlayback(oldPlayer, newPlayer) { const state = playbackManager.getPlayerState(oldPlayer); @@ -26,7 +26,7 @@ function transferPlayback(oldPlayer, newPlayer) { }); } -events.on(playbackManager, 'playerchange', (e, newPlayer, newTarget, oldPlayer) => { +Events.on(playbackManager, 'playerchange', (e, newPlayer, newTarget, oldPlayer) => { if (!oldPlayer || !newPlayer) { return; } diff --git a/src/components/playback/volumeosd.js b/src/components/playback/volumeosd.js index c35914b192..3269b289d0 100644 --- a/src/components/playback/volumeosd.js +++ b/src/components/playback/volumeosd.js @@ -1,9 +1,10 @@ -import events from 'events'; -import playbackManager from 'playbackManager'; -import dom from 'dom'; -import browser from 'browser'; -import 'css!./iconosd'; -import 'material-icons'; + +import { Events } from 'jellyfin-apiclient'; +import { playbackManager } from './playbackmanager'; +import dom from '../../scripts/dom'; +import browser from '../../scripts/browser'; +import './iconosd.css'; +import 'material-design-icons-iconfont'; let currentPlayer; let osdElement; @@ -111,8 +112,8 @@ function releaseCurrentPlayer() { const player = currentPlayer; if (player) { - events.off(player, 'volumechange', onVolumeChanged); - events.off(player, 'playbackstop', hideOsd); + Events.off(player, 'volumechange', onVolumeChanged); + Events.off(player, 'playbackstop', hideOsd); currentPlayer = null; } } @@ -141,11 +142,11 @@ function bindToPlayer(player) { } hideOsd(); - events.on(player, 'volumechange', onVolumeChanged); - events.on(player, 'playbackstop', hideOsd); + Events.on(player, 'volumechange', onVolumeChanged); + Events.on(player, 'playbackstop', hideOsd); } -events.on(playbackManager, 'playerchange', function () { +Events.on(playbackManager, 'playerchange', function () { bindToPlayer(playbackManager.getCurrentPlayer()); }); diff --git a/src/components/playbackSettings/playbackSettings.js b/src/components/playbackSettings/playbackSettings.js index 782e3d38e1..6e6fa73647 100644 --- a/src/components/playbackSettings/playbackSettings.js +++ b/src/components/playbackSettings/playbackSettings.js @@ -1,13 +1,15 @@ -import browser from 'browser'; -import appSettings from 'appSettings'; -import appHost from 'apphost'; -import focusManager from 'focusManager'; -import qualityoptions from 'qualityoptions'; -import globalize from 'globalize'; -import loading from 'loading'; -import events from 'events'; -import 'emby-select'; -import 'emby-checkbox'; +import browser from '../../scripts/browser'; +import appSettings from '../../scripts/settings/appSettings'; +import { appHost } from '../apphost'; +import focusManager from '../focusManager'; +import qualityoptions from '../qualityOptions'; +import globalize from '../../scripts/globalize'; +import loading from '../loading/loading'; +import { Events } from 'jellyfin-apiclient'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-checkbox/emby-checkbox'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; /* eslint-disable indent */ @@ -145,6 +147,8 @@ import 'emby-checkbox'; showHideQualityFields(context, user, apiClient); + context.querySelector('#selectAllowedAudioChannels').value = userSettings.allowedAudioChannels(); + apiClient.getCultures().then(allCultures => { populateLanguages(context.querySelector('#selectAudioLanguage'), allCultures); @@ -187,6 +191,7 @@ import 'emby-checkbox'; } context.querySelector('.chkPlayDefaultAudioTrack').checked = user.Configuration.PlayDefaultAudioTrack || false; + context.querySelector('.chkPreferFmp4HlsContainer').checked = userSettings.preferFmp4HlsContainer(); context.querySelector('.chkEnableCinemaMode').checked = userSettings.enableCinemaMode(); context.querySelector('.chkEnableNextVideoOverlay').checked = userSettings.enableNextVideoInfoOverlay(); context.querySelector('.chkExternalVideoPlayer').checked = appSettings.enableSystemExternalPlayers(); @@ -222,10 +227,11 @@ import 'emby-checkbox'; setMaxBitrateFromField(context.querySelector('.selectVideoInternetQuality'), false, 'Video'); setMaxBitrateFromField(context.querySelector('.selectMusicInternetQuality'), false, 'Audio'); + userSettingsInstance.allowedAudioChannels(context.querySelector('#selectAllowedAudioChannels').value); user.Configuration.AudioLanguagePreference = context.querySelector('#selectAudioLanguage').value; user.Configuration.PlayDefaultAudioTrack = context.querySelector('.chkPlayDefaultAudioTrack').checked; user.Configuration.EnableNextEpisodeAutoPlay = context.querySelector('.chkEpisodeAutoPlay').checked; - + userSettingsInstance.preferFmp4HlsContainer(context.querySelector('.chkPreferFmp4HlsContainer').checked); userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked); userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked); @@ -243,12 +249,10 @@ import 'emby-checkbox'; saveUser(context, user, userSettings, apiClient).then(() => { loading.hide(); if (enableSaveConfirmation) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('SettingsSaved')); - }); + toast(globalize.translate('SettingsSaved')); } - events.trigger(instance, 'saved'); + Events.trigger(instance, 'saved'); }, () => { loading.hide(); }); @@ -257,7 +261,7 @@ import 'emby-checkbox'; function onSubmit(e) { const self = this; - const apiClient = window.connectionManager.getApiClient(self.options.serverId); + const apiClient = ServerConnections.getApiClient(self.options.serverId); const userId = self.options.userId; const userSettings = self.options.userSettings; @@ -274,7 +278,7 @@ import 'emby-checkbox'; } function embed(options, self) { - return import('text!./playbackSettings.template.html').then(({default: template}) => { + return import('./playbackSettings.template.html').then(({default: template}) => { options.element.innerHTML = globalize.translateHtml(template, 'core'); options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self)); @@ -304,7 +308,7 @@ import 'emby-checkbox'; loading.show(); const userId = self.options.userId; - const apiClient = window.connectionManager.getApiClient(self.options.serverId); + const apiClient = ServerConnections.getApiClient(self.options.serverId); const userSettings = self.options.userSettings; apiClient.getUser(userId).then(user => { diff --git a/src/components/playbackSettings/playbackSettings.template.html b/src/components/playbackSettings/playbackSettings.template.html index d10b069bb2..ae6429ed95 100644 --- a/src/components/playbackSettings/playbackSettings.template.html +++ b/src/components/playbackSettings/playbackSettings.template.html @@ -4,6 +4,16 @@ ${HeaderAudioSettings} +
+ +
+
@@ -49,6 +59,14 @@ ${TabAdvanced} +
+ +
${PreferFmp4HlsContainerHelp}
+
+
'; @@ -473,7 +475,7 @@ import 'emby-itemscontainer'; // how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences if (!nowPlayingItem) { return { - html: globalize.translate('LastSeen', datefns.formatDistanceToNow(Date.parse(session.LastActivityDate), dfnshelper.localeWithSuffix)), + html: globalize.translate('LastSeen', formatDistanceToNow(Date.parse(session.LastActivityDate), localeWithSuffix)), image: imgUrl }; } @@ -720,33 +722,29 @@ import 'emby-itemscontainer'; }); }, restart: function (btn) { - import('confirm').then(({default: confirm}) => { - confirm({ - title: globalize.translate('Restart'), - text: globalize.translate('MessageConfirmRestart'), - confirmText: globalize.translate('Restart'), - primary: 'delete' - }).then(function () { - const page = dom.parentWithClass(btn, 'page'); - page.querySelector('#btnRestartServer').disabled = true; - page.querySelector('#btnShutdown').disabled = true; - ApiClient.restartServer(); - }); + confirm({ + title: globalize.translate('Restart'), + text: globalize.translate('MessageConfirmRestart'), + confirmText: globalize.translate('Restart'), + primary: 'delete' + }).then(function () { + const page = dom.parentWithClass(btn, 'page'); + page.querySelector('#btnRestartServer').disabled = true; + page.querySelector('#btnShutdown').disabled = true; + ApiClient.restartServer(); }); }, shutdown: function (btn) { - import('confirm').then(({default: confirm}) => { - confirm({ - title: globalize.translate('ButtonShutdown'), - text: globalize.translate('MessageConfirmShutdown'), - confirmText: globalize.translate('ButtonShutdown'), - primary: 'delete' - }).then(function () { - const page = dom.parentWithClass(btn, 'page'); - page.querySelector('#btnRestartServer').disabled = true; - page.querySelector('#btnShutdown').disabled = true; - ApiClient.shutdownServer(); - }); + confirm({ + title: globalize.translate('ButtonShutdown'), + text: globalize.translate('MessageConfirmShutdown'), + confirmText: globalize.translate('ButtonShutdown'), + primary: 'delete' + }).then(function () { + const page = dom.parentWithClass(btn, 'page'); + page.querySelector('#btnRestartServer').disabled = true; + page.querySelector('#btnShutdown').disabled = true; + ApiClient.shutdownServer(); }); } }; @@ -799,13 +797,13 @@ import 'emby-itemscontainer'; loading.show(); pollForInfo(page, apiClient); DashboardPage.startInterval(apiClient); - events.on(serverNotifications, 'RestartRequired', onRestartRequired); - events.on(serverNotifications, 'ServerShuttingDown', onServerShuttingDown); - events.on(serverNotifications, 'ServerRestarting', onServerRestarting); - events.on(serverNotifications, 'PackageInstalling', onPackageInstalling); - events.on(serverNotifications, 'PackageInstallationCompleted', onPackageInstallationCompleted); - events.on(serverNotifications, 'Sessions', onSessionsUpdate); - events.on(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate); + Events.on(serverNotifications, 'RestartRequired', onRestartRequired); + Events.on(serverNotifications, 'ServerShuttingDown', onServerShuttingDown); + Events.on(serverNotifications, 'ServerRestarting', onServerRestarting); + Events.on(serverNotifications, 'PackageInstalling', onPackageInstalling); + Events.on(serverNotifications, 'PackageInstallationCompleted', onPackageInstallationCompleted); + Events.on(serverNotifications, 'Sessions', onSessionsUpdate); + Events.on(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate); DashboardPage.lastAppUpdateCheck = null; reloadSystemInfo(page, ApiClient); @@ -839,13 +837,13 @@ import 'emby-itemscontainer'; const apiClient = ApiClient; const page = this; - events.off(serverNotifications, 'RestartRequired', onRestartRequired); - events.off(serverNotifications, 'ServerShuttingDown', onServerShuttingDown); - events.off(serverNotifications, 'ServerRestarting', onServerRestarting); - events.off(serverNotifications, 'PackageInstalling', onPackageInstalling); - events.off(serverNotifications, 'PackageInstallationCompleted', onPackageInstallationCompleted); - events.off(serverNotifications, 'Sessions', onSessionsUpdate); - events.off(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate); + Events.off(serverNotifications, 'RestartRequired', onRestartRequired); + Events.off(serverNotifications, 'ServerShuttingDown', onServerShuttingDown); + Events.off(serverNotifications, 'ServerRestarting', onServerRestarting); + Events.off(serverNotifications, 'PackageInstalling', onPackageInstalling); + Events.off(serverNotifications, 'PackageInstallationCompleted', onPackageInstallationCompleted); + Events.off(serverNotifications, 'Sessions', onSessionsUpdate); + Events.off(serverNotifications, 'ScheduledTasksInfo', onScheduledTasksUpdate); if (apiClient) { DashboardPage.stopInterval(apiClient); diff --git a/src/controllers/dashboard/devices/device.js b/src/controllers/dashboard/devices/device.js index 17e28b9bdb..18404d605f 100644 --- a/src/controllers/dashboard/devices/device.js +++ b/src/controllers/dashboard/devices/device.js @@ -1,7 +1,8 @@ -import loading from 'loading'; -import dom from 'dom'; -import 'emby-input'; -import 'emby-button'; +import loading from '../../../components/loading/loading'; +import dom from '../../../scripts/dom'; +import '../../../elements/emby-input/emby-input'; +import '../../../elements/emby-button/emby-button'; +import Dashboard from '../../../scripts/clientUtils'; /* eslint-disable indent */ diff --git a/src/controllers/dashboard/devices/devices.js b/src/controllers/dashboard/devices/devices.js index c6e7281645..e1eb99e677 100644 --- a/src/controllers/dashboard/devices/devices.js +++ b/src/controllers/dashboard/devices/devices.js @@ -1,12 +1,14 @@ -import loading from 'loading'; -import dom from 'dom'; -import globalize from 'globalize'; -import imageHelper from 'scripts/imagehelper'; -import * as datefns from 'date-fns'; -import dfnshelper from 'dfnshelper'; -import 'emby-button'; -import 'emby-itemscontainer'; -import 'cardStyle'; +import loading from '../../../components/loading/loading'; +import dom from '../../../scripts/dom'; +import globalize from '../../../scripts/globalize'; +import imageHelper from '../../../scripts/imagehelper'; +import { formatDistanceToNow } from 'date-fns'; +import { localeWithSuffix } from '../../../scripts/dfnshelper'; +import '../../../elements/emby-button/emby-button'; +import '../../../elements/emby-itemscontainer/emby-itemscontainer'; +import '../../../components/cardbuilder/card.css'; +import Dashboard from '../../../scripts/clientUtils'; +import confirm from '../../../components/confirm/confirm'; /* eslint-disable indent */ @@ -20,14 +22,12 @@ import 'cardStyle'; function deleteAllDevices(page) { const msg = globalize.translate('DeleteDevicesConfirmation'); - require(['confirm'], async function (confirm) { - await confirm({ - text: msg, - title: globalize.translate('HeaderDeleteDevices'), - confirmText: globalize.translate('ButtonDelete'), - primary: 'delete' - }); - + confirm({ + text: msg, + title: globalize.translate('HeaderDeleteDevices'), + confirmText: globalize.translate('ButtonDelete'), + primary: 'delete' + }).then(async () => { loading.show(); await Promise.all( deviceIds.filter(canDelete).map((id) => ApiClient.deleteDevice(id)) @@ -39,17 +39,15 @@ import 'cardStyle'; function deleteDevice(page, id) { const msg = globalize.translate('DeleteDeviceConfirmation'); - import('confirm').then(({default: confirm}) => { - confirm({ - text: msg, - title: globalize.translate('HeaderDeleteDevice'), - confirmText: globalize.translate('Delete'), - primary: 'delete' - }).then(async () => { - loading.show(); - await ApiClient.deleteDevice(id); - loadData(page); - }); + confirm({ + text: msg, + title: globalize.translate('HeaderDeleteDevice'), + confirmText: globalize.translate('Delete'), + primary: 'delete' + }).then(async () => { + loading.show(); + await ApiClient.deleteDevice(id); + loadData(page); }); } @@ -72,7 +70,7 @@ import 'cardStyle'; }); } - import('actionsheet').then(({default: actionsheet}) => { + import('../../../components/actionSheet/actionSheet').then(({default: actionsheet}) => { actionsheet.show({ items: menuItems, positionTo: btn, @@ -128,7 +126,7 @@ import 'cardStyle'; if (device.LastUserName) { deviceHtml += device.LastUserName; - deviceHtml += ', ' + datefns.formatDistanceToNow(Date.parse(device.DateLastActivity), dfnshelper.localeWithSuffix); + deviceHtml += ', ' + formatDistanceToNow(Date.parse(device.DateLastActivity), localeWithSuffix); } deviceHtml += ' '; diff --git a/src/controllers/dashboard/dlna/profile.js b/src/controllers/dashboard/dlna/profile.js index 478b5ca878..74b4f2a25c 100644 --- a/src/controllers/dashboard/dlna/profile.js +++ b/src/controllers/dashboard/dlna/profile.js @@ -1,11 +1,13 @@ -import $ from 'jQuery'; -import loading from 'loading'; -import globalize from 'globalize'; -import 'emby-select'; -import 'emby-button'; -import 'emby-input'; -import 'emby-checkbox'; -import 'listViewStyle'; +import 'jquery'; +import loading from '../../../components/loading/loading'; +import globalize from '../../../scripts/globalize'; +import '../../../elements/emby-select/emby-select'; +import '../../../elements/emby-button/emby-button'; +import '../../../elements/emby-input/emby-input'; +import '../../../elements/emby-checkbox/emby-checkbox'; +import '../../../components/listview/listview.css'; +import Dashboard from '../../../scripts/clientUtils'; +import toast from '../../../components/toast/toast'; /* eslint-disable indent */ @@ -633,9 +635,7 @@ import 'listViewStyle'; data: JSON.stringify(profile), contentType: 'application/json' }).then(function () { - import('toast').then(({default: toast}) => { - toast('Settings saved.'); - }); + toast('Settings saved.'); }, Dashboard.processErrorResponse); } else { ApiClient.ajax({ diff --git a/src/controllers/dashboard/dlna/profiles.js b/src/controllers/dashboard/dlna/profiles.js index 4eb830df6f..e507fc4e7c 100644 --- a/src/controllers/dashboard/dlna/profiles.js +++ b/src/controllers/dashboard/dlna/profiles.js @@ -1,9 +1,10 @@ -import $ from 'jQuery'; -import globalize from 'globalize'; -import loading from 'loading'; -import libraryMenu from 'libraryMenu'; -import 'listViewStyle'; -import 'emby-button'; +import 'jquery'; +import globalize from '../../../scripts/globalize'; +import loading from '../../../components/loading/loading'; +import libraryMenu from '../../../scripts/libraryMenu'; +import '../../../components/listview/listview.css'; +import '../../../elements/emby-button/emby-button'; +import confirm from '../../../components/confirm/confirm'; /* eslint-disable indent */ @@ -64,16 +65,14 @@ import 'emby-button'; } function deleteProfile(page, id) { - import('confirm').then(({default: confirm}) => { - confirm(globalize.translate('MessageConfirmProfileDeletion'), globalize.translate('HeaderConfirmProfileDeletion')).then(function () { - loading.show(); - ApiClient.ajax({ - type: 'DELETE', - url: ApiClient.getUrl('Dlna/Profiles/' + id) - }).then(function () { - loading.hide(); - loadProfiles(page); - }); + confirm(globalize.translate('MessageConfirmProfileDeletion'), globalize.translate('HeaderConfirmProfileDeletion')).then(function () { + loading.show(); + ApiClient.ajax({ + type: 'DELETE', + url: ApiClient.getUrl('Dlna/Profiles/' + id) + }).then(function () { + loading.hide(); + loadProfiles(page); }); }); } diff --git a/src/controllers/dashboard/dlna/settings.js b/src/controllers/dashboard/dlna/settings.js index fb93441a55..33c35b9644 100644 --- a/src/controllers/dashboard/dlna/settings.js +++ b/src/controllers/dashboard/dlna/settings.js @@ -1,7 +1,8 @@ -import $ from 'jQuery'; -import loading from 'loading'; -import libraryMenu from 'libraryMenu'; -import globalize from 'globalize'; +import 'jquery'; +import loading from '../../../components/loading/loading'; +import libraryMenu from '../../../scripts/libraryMenu'; +import globalize from '../../../scripts/globalize'; +import Dashboard from '../../../scripts/clientUtils'; /* eslint-disable indent */ diff --git a/src/controllers/dashboard/encodingsettings.html b/src/controllers/dashboard/encodingsettings.html index 7a43d67e46..4c7b8c3a57 100644 --- a/src/controllers/dashboard/encodingsettings.html +++ b/src/controllers/dashboard/encodingsettings.html @@ -94,6 +94,15 @@ +
+
+ +
+
+
+
+ +
${H264CrfHelp}
-