diff --git a/.ci/azure-pipelines-build.yml b/.ci/azure-pipelines-build.yml index 128fe546055..fb08254216a 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 1e4bddbd047..8d9efbd73a2 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 e5ee2dfe86c..aabfd633f8f 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/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000000..f94934b4471 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,31 @@ +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + schedule: + - cron: '30 7 * * 6' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + queries: +security-extended + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 1bdf1cd9039..9f9be018b3e 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -41,6 +41,7 @@ - [Cromefire_](https://github.com/cromefire) - [Orry Verducci](https://github.com/orryverducci) - [Camc314](https://github.com/camc314) + - [danieladov](https://github.com/danieladov) # Emby Contributors diff --git a/README.md b/README.md index ca42965dd9f..1108ec9f211 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Jellyfin Web is the frontend used for most of the clients available for end user ### Dependencies -- [Node.js](https://nodejs.org/en/download/) +- [Node.js](https://nodejs.org/en/download) - [Yarn 1.22.4](https://classic.yarnpkg.com/en/docs/install) - Gulp-cli @@ -69,14 +69,8 @@ Jellyfin Web is the frontend used for most of the clients available for end user yarn serve ``` -4. Build the client with sourcemaps. +4. Build the client with sourcemaps available. ```sh yarn build:development ``` - - You can build a nginx compatible version as well. - - ```sh - yarn build:standalone - ``` diff --git a/gulpfile.js b/gulpfile.js index 8b407ec2aa5..7d1184dbdd5 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -2,7 +2,6 @@ const { src, dest, series, parallel, watch } = require('gulp'); const browserSync = require('browser-sync').create(); const del = require('del'); const babel = require('gulp-babel'); -const concat = require('gulp-concat'); const terser = require('gulp-terser'); const htmlmin = require('gulp-htmlmin'); const imagemin = require('gulp-imagemin'); @@ -16,7 +15,6 @@ const stream = require('webpack-stream'); const inject = require('gulp-inject'); const postcss = require('gulp-postcss'); const sass = require('gulp-sass'); -const gulpif = require('gulp-if'); const lazypipe = require('lazypipe'); sass.compiler = require('node-sass'); @@ -30,10 +28,7 @@ if (mode.production()) { const options = { javascript: { - query: ['src/**/*.js', '!src/bundle.js', '!src/standalone.js', '!src/scripts/apploader.js'] - }, - apploader: { - query: ['src/standalone.js', 'src/scripts/apploader.js'] + query: ['src/**/*.js', '!src/bundle.js'] }, css: { query: ['src/**/*.css', 'src/**/*.scss'] @@ -68,8 +63,6 @@ function serve() { } }); - watch(options.apploader.query, apploader(true)); - watch('src/bundle.js', webpack); watch(options.css.query).on('all', function (event, path) { @@ -131,20 +124,6 @@ function javascript(query) { .pipe(browserSync.stream()); } -function apploader(standalone) { - function task() { - return src(options.apploader.query, { base: './src/' }) - .pipe(gulpif(standalone, concat('scripts/apploader.js'))) - .pipe(pipelineJavascript()) - .pipe(dest('dist/')) - .pipe(browserSync.stream()); - } - - task.displayName = 'apploader'; - - return task; -} - function webpack() { return stream(config) .pipe(dest('dist/')) @@ -195,10 +174,5 @@ function injectBundle() { .pipe(browserSync.stream()); } -function build(standalone) { - return series(clean, parallel(javascript, apploader(standalone), webpack, css, html, images, copy)); -} - -exports.default = series(build(false), injectBundle); -exports.standalone = series(build(true), injectBundle); -exports.serve = series(exports.standalone, serve); +exports.default = series(clean, parallel(javascript, webpack, css, html, images, copy), injectBundle); +exports.serve = series(exports.default, serve); diff --git a/package.json b/package.json index d46aedc6f84..c7da5adb116 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", @@ -78,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": [ @@ -394,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 00000000000..bde12b36a59 --- /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 3dcb95442e0..00000000000 --- a/src/bundle.js +++ /dev/null @@ -1,188 +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; -}); - -// noto font -const noto = require('jellyfin-noto'); -_define('jellyfin-noto', function () { - return noto; -}); - -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/ServerConnections.js b/src/components/ServerConnections.js new file mode 100644 index 00000000000..0242e549cdb --- /dev/null +++ b/src/components/ServerConnections.js @@ -0,0 +1,79 @@ +import { ConnectionManager, Credentials, ApiClient, Events } from 'jellyfin-apiclient'; +import { appHost } from './apphost'; +import Dashboard from '../scripts/clientUtils'; +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(server) { + console.debug('creating ApiClient singleton'); + + const apiClient = new ApiClient( + server, + 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 b513766d0b6..9e0e3d5cf99 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 be84cf0a06b..85df1b2c608 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 3878d033550..78967b3a34a 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 1420c7f428c..2938cb7c708 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 95b58816771..9caa1b015cb 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/alphaPicker/style.css b/src/components/alphaPicker/style.css index 4e94c0f7546..8feb3e50e07 100644 --- a/src/components/alphaPicker/style.css +++ b/src/components/alphaPicker/style.css @@ -12,7 +12,6 @@ .alphaPicker-fixed { position: fixed; bottom: 5.5em; - z-index: 999999; } .alphaPickerRow { diff --git a/src/components/appFooter/appFooter.js b/src/components/appFooter/appFooter.js index 260aea82808..3b658244904 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 a6bb6da618d..03000ebaf6c 100644 --- a/src/components/appRouter.js +++ b/src/components/appRouter.js @@ -1,13 +1,16 @@ -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 Dashboard from '../scripts/clientUtils'; +import ServerConnections from './ServerConnections'; +import alert from './alert'; class AppRouter { allRoutes = []; @@ -76,11 +79,7 @@ class AppRouter { } showSelectServer() { - Dashboard.navigate(AppInfo.isNativeApp ? 'selectserver.html' : 'login.html'); - } - - showWelcome() { - Dashboard.navigate(AppInfo.isNativeApp ? 'selectserver.html' : 'login.html'); + Dashboard.navigate('selectserver.html'); } showSettings() { @@ -94,7 +93,7 @@ class AppRouter { beginConnectionWizard() { backdrop.clearBackdrop(); loading.show(); - window.connectionManager.connect({ + ServerConnections.connect({ enableAutoLogin: appSettings.enableAutoLogin() }).then((result) => { this.handleConnectionResult(result); @@ -150,10 +149,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 +208,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 +267,7 @@ class AppRouter { switch (result.State) { case 'SignedIn': loading.hide(); - Emby.Page.goHome(); + this.goHome(); break; case 'ServerSignIn': result.ApiClient.getPublicUsers().then((users) => { @@ -282,17 +281,12 @@ class AppRouter { case 'ServerSelection': this.showSelectServer(); break; - case 'ConnectSignIn': - 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 +302,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 +332,7 @@ class AppRouter { }; if (route.controller) { - import('controllers/' + route.controller).then(onInitComplete); + import('../controllers/' + route.controller).then(onInitComplete); } else { onInitComplete(); } @@ -407,9 +399,7 @@ class AppRouter { this.forcedLogoutMsg = null; if (msg) { - import('alert').then((alert) => { - alert(msg); - }); + alert(msg); } } @@ -484,8 +474,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 +483,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(); @@ -510,25 +500,35 @@ class AppRouter { authenticate(ctx, route, callback) { const firstResult = this.firstConnectionResult; - if (firstResult) { - this.firstConnectionResult = null; - if (firstResult.State !== 'SignedIn' && !route.anonymous) { - this.handleConnectionResult(firstResult); - return; - } + this.firstConnectionResult = null; + if (firstResult && firstResult.State === 'ServerSignIn') { + const url = ApiClient.serverAddress() + '/System/Info/Public'; + fetch(url).then(response => { + if (!response.ok) return Promise.reject('fetch failed'); + return response.json(); + }).then(data => { + if (data !== null && data.StartupWizardCompleted === false) { + Dashboard.navigate('wizardstart.html'); + } else { + this.handleConnectionResult(firstResult); + } + }).catch(error => { + console.error(error); + }); + + return; } - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); const pathname = ctx.pathname.toLowerCase(); - console.debug('appRouter - processing path request ' + pathname); - + console.debug('processing path request: ' + pathname); const isCurrentRouteStartup = this.currentRouteInfo ? this.currentRouteInfo.route.startup : true; const shouldExitApp = ctx.isBack && route.isDefaultRoute && isCurrentRouteStartup; if (!shouldExitApp && (!apiClient || !apiClient.isLoggedIn()) && !route.anonymous) { - console.debug('appRouter - route does not allow anonymous access, redirecting to login'); + console.debug('route does not allow anonymous access: redirecting to login'); this.beginConnectionWizard(); return; } @@ -536,17 +536,17 @@ class AppRouter { if (shouldExitApp) { if (appHost.supports('exit')) { appHost.exit(); - return; } + return; } if (apiClient && apiClient.isLoggedIn()) { - console.debug('appRouter - user is authenticated'); + console.debug('user is authenticated'); if (route.isDefaultRoute) { - console.debug('appRouter - loading skin home page'); - Emby.Page.goHome(); + console.debug('loading home page'); + this.goHome(); return; } else if (route.roles) { this.validateRoles(apiClient, route.roles).then(() => { @@ -556,7 +556,7 @@ class AppRouter { } } - console.debug('appRouter - proceeding to ' + pathname); + console.debug('proceeding to page: ' + pathname); callback(); } @@ -846,4 +846,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 df2f7c2d2c2..8e8cb15b32e 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; - - 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); - } + let profile; - resolve(profile); - }); + if (window.NativeShell) { + profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); + } else { + const builderOpts = getBaseProfileOptions(item); + profile = profileBuilder(builderOpts); + } + + 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 0a10cabd2f4..aa88e0c2944 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 83888b81b92..a2fe54de195 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 63b2e26adb0..4095bea48fb 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 35ae2b0cdda..e364056e145 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 5fc9e8ade56..00b8f0fb89e 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 294f9e223a2..bedab5421bd 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 2d0d0259291..33e6fdecca2 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 eca612ccb85..1978324e7cd 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; - } + options = text; + } - const items = []; + const items = []; - items.push({ - name: options.cancelText || globalize.translate('ButtonCancel'), - id: 'cancel', - type: 'cancel' - }); + 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' - }); + items.push({ + name: options.confirmText || globalize.translate('ButtonOk'), + id: 'ok', + type: options.primary === 'delete' ? 'delete' : 'submit' + }); - options.buttons = items; + options.buttons = items; - return dialog.show(options).then(result => { - if (result === 'ok') { - return Promise.resolve(); - } + return dialog.show(options).then(result => { + if (result === 'ok') { + return Promise.resolve(); + } - return Promise.reject(); - }); - }; - } -})(); -/* eslint-enable indent */ + 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 1b13900d853..ee97fff8a1b 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 eb46d98b122..cdaf47996f8 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 */ @@ -310,10 +310,6 @@ import 'scrollStyles'; const supportsOverscrollBehavior = 'overscroll-behavior-y' in document.body.style; function shouldLockDocumentScroll(options) { - if (supportsOverscrollBehavior && (options.size || !browser.touch)) { - return false; - } - if (options.lockScroll != null) { return options.lockScroll; } @@ -322,6 +318,10 @@ import 'scrollStyles'; return true; } + if (supportsOverscrollBehavior && (options.size || !browser.touch)) { + return false; + } + if (options.size) { return true; } @@ -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 3dd3302b288..2f8a2cadd18 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 efaab16b3f2..9d7292547db 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 86cd050216a..cb1b61c43f8 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 d11edb40a23..c8b81066d2d 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 7cef08303da..f4f6ef2d9bb 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 d45984bf581..b0fc1885167 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 947b3b85698..13158016ac5 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 35f0d3e06ef..8132ac3bb3a 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 a5ed55e67cc..7ae0b8409fc 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 51389354081..89edf29486a 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 758773689b1..6561b8a9e06 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 be506b449d4..4f803aa4b61 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 1ec459ff83f..18dd849aecb 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 d112dd65cc9..a220a65c5dd 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 e89cbda2ae5..10beff5cab6 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 e5b59cfb228..69e9bcb8c74 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 9dc708098d2..1811b117e17 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 bbd672ef72e..5f8a8691d1d 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 f36f623d9ff..c8664512ef1 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 7d1ac0e1101..de771546b0d 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 bd3a157dab8..2094bcdacc8 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 956cbb4f647..382226478fa 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 3883e6e490d..b5a73f5c454 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 88a7265f8b1..7268b3914b1 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 7146307d7fb..9b20cfff25c 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'))]; @@ -376,13 +377,6 @@ import 'emby-input'; }); } - export function setAdvancedVisible(parent, visible) { - const elems = parent.querySelectorAll('.advanced'); - for (let i = 0; i < elems.length; i++) { - visible ? elems[i].classList.remove('advancedHide') : elems[i].classList.add('advancedHide'); - } - } - export function setContentType(parent, contentType) { if (contentType === 'homevideos' || contentType === 'photos') { parent.querySelector('.chkEnablePhotosContainer').classList.remove('hide'); @@ -589,6 +583,5 @@ export default { embed: embed, setContentType: setContentType, getLibraryOptions: getLibraryOptions, - setLibraryOptions: setLibraryOptions, - setAdvancedVisible: setAdvancedVisible + setLibraryOptions: setLibraryOptions }; diff --git a/src/components/libraryoptionseditor/libraryoptionseditor.template.html b/src/components/libraryoptionseditor/libraryoptionseditor.template.html index 5bf162478d7..bd70bcb54ed 100644 --- a/src/components/libraryoptionseditor/libraryoptionseditor.template.html +++ b/src/components/libraryoptionseditor/libraryoptionseditor.template.html @@ -1,8 +1,3 @@ -

${HeaderLibrarySettings}

diff --git a/src/components/listview/listview.js b/src/components/listview/listview.js index e0fbc2fd2eb..96ffcd0ad32 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 8237611373e..466173952c8 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 1be1cf622f3..1c63b53bed9 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 4e0d7b026ca..60803945dc6 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(); @@ -98,18 +96,12 @@ import 'flexStyles'; page.querySelector('.btnAddFolder').addEventListener('click', onAddButtonClick); page.querySelector('.btnSubmit').addEventListener('click', onAddLibrary); page.querySelector('.folderList').addEventListener('click', onRemoveClick); - page.querySelector('.chkAdvanced').addEventListener('change', onToggleAdvancedChange); - } - - function onToggleAdvancedChange() { - const dlg = dom.parentWithClass(this, 'dlg-librarycreator'); - libraryoptionseditor.setAdvancedVisible(dlg.querySelector('.libraryOptions'), this.checked); } 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, @@ -190,7 +182,6 @@ import 'flexStyles'; function initLibraryOptions(dlg) { libraryoptionseditor.embed(dlg.querySelector('.libraryOptions')).then(() => { $('#selectCollectionType', dlg).trigger('change'); - onToggleAdvancedChange.call(dlg.querySelector('.chkAdvanced')); }); } @@ -200,7 +191,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/mediaLibraryCreator/mediaLibraryCreator.template.html b/src/components/mediaLibraryCreator/mediaLibraryCreator.template.html index 4d84544d06c..c5571b54d23 100644 --- a/src/components/mediaLibraryCreator/mediaLibraryCreator.template.html +++ b/src/components/mediaLibraryCreator/mediaLibraryCreator.template.html @@ -5,12 +5,6 @@

${ButtonAddMediaLibrary}

-
- -
diff --git a/src/components/mediaLibraryEditor/mediaLibraryEditor.js b/src/components/mediaLibraryEditor/mediaLibraryEditor.js index 13d264f4c9b..1cb6e26ac7b 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, @@ -189,20 +183,12 @@ import 'flexStyles'; }); } - function onToggleAdvancedChange() { - const dlg = dom.parentWithClass(this, 'dlg-libraryeditor'); - libraryoptionseditor.setAdvancedVisible(dlg.querySelector('.libraryOptions'), this.checked); - } - function initEditor(dlg, options) { renderLibrary(dlg, options); dlg.querySelector('.btnAddFolder').addEventListener('click', onAddButtonClick); dlg.querySelector('.folderList').addEventListener('click', onListItemClick); - dlg.querySelector('.chkAdvanced').addEventListener('change', onToggleAdvancedChange); dlg.querySelector('.btnSubmit').addEventListener('click', onEditLibrary); - libraryoptionseditor.embed(dlg.querySelector('.libraryOptions'), options.library.CollectionType, options.library.LibraryOptions).then(() => { - onToggleAdvancedChange.call(dlg.querySelector('.chkAdvanced')); - }); + libraryoptionseditor.embed(dlg.querySelector('.libraryOptions'), options.library.CollectionType, options.library.LibraryOptions); } function onDialogClosed() { @@ -215,7 +201,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/mediaLibraryEditor/mediaLibraryEditor.template.html b/src/components/mediaLibraryEditor/mediaLibraryEditor.template.html index 1f2581612d2..732851d4af6 100644 --- a/src/components/mediaLibraryEditor/mediaLibraryEditor.template.html +++ b/src/components/mediaLibraryEditor/mediaLibraryEditor.template.html @@ -9,13 +9,6 @@

${ChangingMetadataImageSettingsNewContent}
-
- -
-

${Folders}

diff --git a/src/components/mediainfo/mediainfo.js b/src/components/mediainfo/mediainfo.js index d5da29d3bc6..73193f3758f 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 b768e77c4fb..71bf1bc00b6 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 83269712477..f64f7330d79 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 e9c66c57a4c..39b50aa17e6 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 e7ce440f062..8cfc838d09e 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 7f0e68f1d9d..0267c378ce3 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 62181977005..be8895ca6a1 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 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'); - - if (nowPlayingBarElement) { - resolve(nowPlayingBarElement); - return; - } + return nowPlayingBarElement; + } - parentContainer.insertAdjacentHTML('afterbegin', getNowPlayingBarHtml()); - nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); + const parentContainer = appFooter.element; + nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); - if (layoutManager.mobile) { - hideButton(nowPlayingBarElement.querySelector('.btnShuffleQueue')); - hideButton(nowPlayingBarElement.querySelector('.nowPlayingBarCenter')); - } + if (nowPlayingBarElement) { + return 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'); - } + parentContainer.insertAdjacentHTML('afterbegin', getNowPlayingBarHtml()); + window.CustomElements.upgradeSubtree(parentContainer); - itemShortcuts.on(nowPlayingBarElement); + nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar'); - bindEvents(nowPlayingBarElement); - resolve(nowPlayingBarElement); - }); - }); + if (layoutManager.mobile) { + hideButton(nowPlayingBarElement.querySelector('.btnShuffleQueue')); + hideButton(nowPlayingBarElement.querySelector('.nowPlayingBarCenter')); + } + + 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'); + } + + 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() { @@ -664,7 +660,7 @@ import 'emby-ratingbutton'; console.debug('nowplaying event: ' + event.type); const player = this; - if (!state.NowPlayingItem || layoutManager.tv) { + if (!state.NowPlayingItem || layoutManager.tv || !state.IsFullscreen) { hideNowPlayingBar(); return; } @@ -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 c4b4701e9df..3890577b244 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 78c40d10c16..e6c4b82f2eb 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 b75fa69a8cd..9cfb9f3b0ba 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 a68663a7690..a2e07fe8fa9 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(); } @@ -2240,13 +2240,14 @@ class PlaybackManager { const streamInfo = createStreamInfoFromUrlItem(item); streamInfo.fullscreen = playOptions.fullscreen; getPlayerData(player).isChangingStream = false; - return player.play(streamInfo).then(function () { + return player.play(streamInfo).then(() => { loading.hide(); onPlaybackStartedFn(); onPlaybackStarted(player, playOptions, streamInfo); - }, function () { - // TODO: show error message + }).catch((errorCode) => { self.stop(player); + loading.hide(); + showPlaybackInfoErrorMessage(self, errorCode || 'ErrorDefault'); }); }); } @@ -2254,7 +2255,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 +2300,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 +2320,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 +2549,7 @@ class PlaybackManager { const isCurrentIndex = removeResult.isCurrentIndex; - events.trigger(player, 'playlistitemremove', [ + Events.trigger(player, 'playlistitemremove', [ { playlistItemIds: playlistItemIds } @@ -2573,7 +2574,7 @@ class PlaybackManager { return; } - events.trigger(player, 'playlistitemmove', [ + Events.trigger(player, 'playlistitemmove', [ { playlistItemId: moveResult.playlistItemId, newIndex: moveResult.newIndex @@ -2707,7 +2708,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 +2728,7 @@ class PlaybackManager { } else { self._playQueueManager.queue(items); } - events.trigger(player, 'playlistitemadd'); + Events.trigger(player, 'playlistitemadd'); } function onPlayerProgressInterval() { @@ -2779,8 +2780,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 +2811,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 +2851,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; @@ -2935,7 +2936,7 @@ class PlaybackManager { state.NextMediaType = nextMediaType; - if (isServerItem(streamInfo.item)) { + if (streamInfo && isServerItem(streamInfo.item)) { if (player.supportsProgress === false && state.PlayState && !state.PlayState.PositionTicks) { state.PlayState.PositionTicks = streamInfo.item.RunTimeTicks; } @@ -2952,8 +2953,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 +3000,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 +3011,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 +3062,7 @@ class PlaybackManager { } function unbindStopped(player) { - events.off(player, 'stopped', onPlaybackStopped); + Events.off(player, 'stopped', onPlaybackStopped); } function initLegacyVolumeMethods(player) { @@ -3090,28 +3091,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 +3121,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 +3158,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 +3191,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 +3221,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 +3362,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 +3394,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 +3474,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 +3589,7 @@ class PlaybackManager { } this._playQueueManager.setRepeatMode(value); - events.trigger(player, 'repeatmodechange'); + Events.trigger(player, 'repeatmodechange'); } getRepeatMode(player = this._currentPlayer) { @@ -3605,7 +3606,7 @@ class PlaybackManager { } this._playQueueManager.setShuffleMode(value); - events.trigger(player, 'shufflequeuemodechange'); + Events.trigger(player, 'shufflequeuemodechange'); } getQueueShuffleMode(player = this._currentPlayer) { @@ -3633,7 +3634,7 @@ class PlaybackManager { } else { this._playQueueManager.toggleShuffleMode(); } - events.trigger(player, 'shufflequeuemodechange'); + Events.trigger(player, 'shufflequeuemodechange'); } clearQueue(clearCurrentItem = false, player = this._currentPlayer) { @@ -3642,7 +3643,7 @@ class PlaybackManager { } this._playQueueManager.clearPlaylist(clearCurrentItem); - events.trigger(player, 'playlistitemremove'); + Events.trigger(player, 'playlistitemremove'); } trySetActiveDeviceName(name) { @@ -3757,4 +3758,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 f585f25ae1d..d763726b100 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 38ff399a9e5..35871c1fe8b 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 = []; - - menuItems.push({ - name: globalize.translate('Yes'), - id: 'yes' - }); - menuItems.push({ - name: globalize.translate('No'), - id: 'no' - }); - - dialog({ - 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; - } - }); + const menuItems = []; + + menuItems.push({ + name: globalize.translate('Yes'), + id: 'yes' + }); + menuItems.push({ + name: globalize.translate('No'), + id: 'no' + }); + + 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; + } }); } 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 40819f62eaa..cc05d6e7046 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 50210aa2f2f..1e7002b45d0 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 c0adb57a454..6d862a5491a 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 c35914b192c..3269b289d06 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 782e3d38e18..6e6fa736478 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 d10b069bb28..ae6429ed953 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}
+
+
'; @@ -187,6 +196,10 @@ export default function (options) { if (appHost.supports('sharing')) { html += getIcon('share', 'btnShare slideshowButton', true); } + if (screenfull.isEnabled) { + html += getIcon('fullscreen', 'btnFullscreen', true); + html += getIcon('fullscreen_exit', 'btnFullscreenExit hide', true); + } html += '
'; } @@ -215,6 +228,22 @@ export default function (options) { if (btnShare) { btnShare.addEventListener('click', share); } + + const btnFullscreen = dialog.querySelector('.btnFullscreen'); + if (btnFullscreen) { + btnFullscreen.addEventListener('click', fullscreen); + } + + const btnFullscreenExit = dialog.querySelector('.btnFullscreenExit'); + if (btnFullscreenExit) { + btnFullscreenExit.addEventListener('click', fullscreenExit); + } + + if (screenfull.isEnabled) { + screenfull.on('change', function () { + toggleFullscreenButtons(screenfull.isFullscreen); + }); + } } setUserScalable(true); @@ -230,6 +259,16 @@ export default function (options) { dialog.addEventListener('close', onDialogClosed); loadSwiper(dialog, options); + + if (layoutManager.desktop) { + const topActionButtons = dialog.querySelector('.topActionButtons'); + if (topActionButtons) topActionButtons.classList.add('hide'); + } + + const btnSlideshowPrevious = dialog.querySelector('.btnSlideshowPrevious'); + if (btnSlideshowPrevious) btnSlideshowPrevious.classList.add('hide'); + const btnSlideshowNext = dialog.querySelector('.btnSlideshowNext'); + if (btnSlideshowNext) btnSlideshowNext.classList.add('hide'); } /** @@ -300,45 +339,43 @@ export default function (options) { slides = currentOptions.items; } - import('swiper').then(({default: Swiper}) => { - swiperInstance = new Swiper(dialog.querySelector('.slideshowSwiperContainer'), { - direction: 'horizontal', - // Loop is disabled due to the virtual slides option not supporting it. - loop: false, - zoom: { - minRatio: 1, - toggle: true - }, - autoplay: !options.interactive, - keyboard: { - enabled: true - }, - preloadImages: true, - slidesPerView: 1, - slidesPerColumn: 1, - initialSlide: options.startIndex || 0, - speed: 240, - navigation: { - nextEl: '.btnSlideshowNext', - prevEl: '.btnSlideshowPrevious' - }, - // Virtual slides reduce memory consumption for large libraries while allowing preloading of images; - virtual: { - slides: slides, - cache: true, - renderSlide: getSwiperSlideHtml, - addSlidesBefore: 1, - addSlidesAfter: 1 - } - }); - - swiperInstance.on('autoplayStart', onAutoplayStart); - swiperInstance.on('autoplayStop', onAutoplayStop); - - if (useFakeZoomImage) { - swiperInstance.on('zoomChange', onZoomChange); + swiperInstance = new Swiper(dialog.querySelector('.slideshowSwiperContainer'), { + direction: 'horizontal', + // Loop is disabled due to the virtual slides option not supporting it. + loop: false, + zoom: { + minRatio: 1, + toggle: true + }, + autoplay: !options.interactive, + keyboard: { + enabled: true + }, + preloadImages: true, + slidesPerView: 1, + slidesPerColumn: 1, + initialSlide: options.startIndex || 0, + speed: 240, + navigation: { + nextEl: '.btnSlideshowNext', + prevEl: '.btnSlideshowPrevious' + }, + // Virtual slides reduce memory consumption for large libraries while allowing preloading of images; + virtual: { + slides: slides, + cache: true, + renderSlide: getSwiperSlideHtml, + addSlidesBefore: 1, + addSlidesAfter: 1 } }); + + swiperInstance.on('autoplayStart', onAutoplayStart); + swiperInstance.on('autoplayStop', onAutoplayStop); + + if (useFakeZoomImage) { + swiperInstance.on('zoomChange', onZoomChange); + } } /** @@ -431,7 +468,7 @@ export default function (options) { function download() { const imageInfo = getCurrentImageInfo(); - import('fileDownloader').then(({default: fileDownloader}) => { + import('../../scripts/fileDownloader').then((fileDownloader) => { fileDownloader.download([imageInfo]); }); } @@ -447,6 +484,35 @@ export default function (options) { }); } + /** + * Goes to fullscreen using screenfull plugin + */ + function fullscreen() { + if (!screenfull.isFullscreen) screenfull.request(); + toggleFullscreenButtons(true); + } + + /** + * Exits fullscreen using screenfull plugin + */ + function fullscreenExit() { + if (screenfull.isFullscreen) screenfull.exit(); + toggleFullscreenButtons(false); + } + + /** + * Updates the display of fullscreen buttons + * @param {boolean} isFullscreen - Whether the wanted state of buttons is fullscreen or not + */ + function toggleFullscreenButtons(isFullscreen) { + const btnFullscreen = dialog.querySelector('.btnFullscreen'); + const btnFullscreenExit = dialog.querySelector('.btnFullscreenExit'); + if (btnFullscreen) + btnFullscreen.classList.toggle('hide', isFullscreen); + if (btnFullscreenExit) + btnFullscreenExit.classList.toggle('hide', !isFullscreen); + } + /** * Starts the autoplay feature of the Swiper instance. */ @@ -481,6 +547,9 @@ export default function (options) { * Closes the dialog and destroys the Swiper instance. */ function onDialogClosed() { + // Exits fullscreen + fullscreenExit(); + const swiper = swiperInstance; if (swiper) { swiper.destroy(true, true); @@ -490,9 +559,6 @@ export default function (options) { inputManager.off(window, onInputCommand); /* eslint-disable-next-line compat/compat */ document.removeEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove); - // Shows page scrollbar - document.body.classList.remove('hide-scroll'); - document.body.classList.add('force-scroll'); } /** @@ -501,9 +567,19 @@ export default function (options) { function showOsd() { const bottom = dialog.querySelector('.slideshowBottomBar'); if (bottom) { - slideUpToShow(bottom); - startHideTimer(); + slideToShow(bottom, 'down'); } + + const topActionButtons = dialog.querySelector('.topActionButtons'); + if (topActionButtons) slideToShow(topActionButtons, 'up'); + + const left = dialog.querySelector('.btnSlideshowPrevious'); + if (left) slideToShow(left, 'left'); + + const right = dialog.querySelector('.btnSlideshowNext'); + if (right) slideToShow(right, 'right'); + + startHideTimer(); } /** @@ -512,8 +588,17 @@ export default function (options) { function hideOsd() { const bottom = dialog.querySelector('.slideshowBottomBar'); if (bottom) { - slideDownToHide(bottom); + slideToHide(bottom, 'down'); } + + const topActionButtons = dialog.querySelector('.topActionButtons'); + if (topActionButtons) slideToHide(topActionButtons, 'up'); + + const left = dialog.querySelector('.btnSlideshowPrevious'); + if (left) slideToHide(left, 'left'); + + const right = dialog.querySelector('.btnSlideshowNext'); + if (right) slideToHide(right, 'right'); } /** @@ -535,10 +620,31 @@ export default function (options) { } /** - * Shows the OSD by sliding it into view. - * @param {HTMLElement} element - Element containing the OSD. + * + * @param {string} hiddenPosition - Position of the hidden element compared to when it's visible ('down', 'up', 'left', 'right') + * @param {*} fadingOut - Whether it is fading out or in + * @param {HTMLElement} element - Element to fade. + * @returns {Array} Array of keyframes + */ + function keyframesSlide(hiddenPosition, fadingOut, element) { + const visible = { transform: 'translate(0,0)', opacity: '1' }; + const invisible = { opacity: '.3' }; + + if (hiddenPosition === 'up' || hiddenPosition === 'down') { + invisible['transform'] = 'translate3d(0,' + element.offsetHeight * (hiddenPosition === 'down' ? 1 : -1) + 'px,0)'; + } else if (hiddenPosition === 'left' || hiddenPosition === 'right') { + invisible['transform'] = 'translate3d(' + element.offsetWidth * (hiddenPosition === 'right' ? 1 : -1) + 'px,0,0)'; + } + + return fadingOut ? [visible, invisible] : [invisible, visible]; + } + + /** + * Shows the element by sliding it into view. + * @param {HTMLElement} element - Element to show. + * @param {string} slideFrom - Direction to slide from ('down', 'up', 'left', 'right') */ - function slideUpToShow(element) { + function slideToShow(element, slideFrom) { if (!element.classList.contains('hide')) { return; } @@ -546,7 +652,8 @@ export default function (options) { element.classList.remove('hide'); const onFinish = function () { - focusManager.focus(element.querySelector('.btnSlideshowPause')); + const btnSlideshowPause = element.querySelector('.btnSlideshowPause'); + if (btnSlideshowPause) focusManager.focus(btnSlideshowPause); }; if (!element.animate) { @@ -555,20 +662,18 @@ export default function (options) { } requestAnimationFrame(function () { - const keyframes = [ - { transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 0 }, - { transform: 'translate3d(0,0,0)', opacity: '1', offset: 1 } - ]; + const keyframes = keyframesSlide(slideFrom, false, element); const timing = { duration: 300, iterations: 1, easing: 'ease-out' }; element.animate(keyframes, timing).onfinish = onFinish; }); } /** - * Hides the OSD by sliding it out of view. - * @param {HTMLElement} element - Element containing the OSD. + * Hides the element by sliding it out of view. + * @param {HTMLElement} element - Element to hide. + * @param {string} slideInto - Direction to slide into ('down', 'up', 'left', 'right') */ - function slideDownToHide(element) { + function slideToHide(element, slideInto) { if (element.classList.contains('hide')) { return; } @@ -583,10 +688,7 @@ export default function (options) { } requestAnimationFrame(function () { - const keyframes = [ - { transform: 'translate3d(0,0,0)', opacity: '1', offset: 0 }, - { transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 1 } - ]; + const keyframes = keyframesSlide(slideInto, true, element); const timing = { duration: 300, iterations: 1, easing: 'ease-out' }; element.animate(keyframes, timing).onfinish = onFinish; }); @@ -656,9 +758,6 @@ export default function (options) { */ self.show = function () { createElements(options); - // Hides page scrollbar - document.body.classList.remove('force-scroll'); - document.body.classList.add('hide-scroll'); }; /** diff --git a/src/components/sortmenu/sortmenu.js b/src/components/sortmenu/sortmenu.js index d38d98c0909..0ec21d3b774 100644 --- a/src/components/sortmenu/sortmenu.js +++ b/src/components/sortmenu/sortmenu.js @@ -1,13 +1,13 @@ -import dialogHelper from 'dialogHelper'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import * as userSettings from 'userSettings'; -import 'emby-select'; -import 'paper-icon-button-light'; -import 'material-icons'; -import 'css!./../formdialog'; -import 'emby-button'; -import 'flexStyles'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import * as userSettings from '../../scripts/settings/userSettings'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-button/paper-icon-button-light'; +import 'material-design-icons-iconfont'; +import '../formdialog.css'; +import '../../elements/emby-button/emby-button'; +import '../../assets/css/flexstyles.scss'; function onSubmit(e) { e.preventDefault(); @@ -22,7 +22,7 @@ function initEditor(context, settings) { } 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); }); @@ -44,7 +44,7 @@ function saveValues(context, settingsKey) { class SortMenu { show(options) { return new Promise(function (resolve, reject) { - import('text!./sortmenu.template.html').then(({default: template}) => { + import('./sortmenu.template.html').then(({default: template}) => { const dialogOptions = { removeOnClose: true, scrollY: false diff --git a/src/components/subtitleeditor/subtitleeditor.js b/src/components/subtitleeditor/subtitleeditor.js index 8e6fb497d92..980a5ef7357 100644 --- a/src/components/subtitleeditor/subtitleeditor.js +++ b/src/components/subtitleeditor/subtitleeditor.js @@ -1,19 +1,22 @@ -import appHost from 'apphost'; -import dialogHelper from 'dialogHelper'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import * as userSettings from 'userSettings'; -import loading from 'loading'; -import focusManager from 'focusManager'; -import dom from 'dom'; -import 'emby-select'; -import 'listViewStyle'; -import 'paper-icon-button-light'; -import 'css!./../formdialog'; -import 'material-icons'; -import 'css!./subtitleeditor'; -import 'emby-button'; -import 'flexStyles'; +import { appHost } from '../apphost'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import * as userSettings from '../../scripts/settings/userSettings'; +import loading from '../loading/loading'; +import focusManager from '../focusManager'; +import dom from '../../scripts/dom'; +import '../../elements/emby-select/emby-select'; +import '../listview/listview.css'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../formdialog.css'; +import 'material-design-icons-iconfont'; +import './subtitleeditor.css'; +import '../../elements/emby-button/emby-button'; +import '../../assets/css/flexstyles.scss'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; +import confirm from '../confirm/confirm'; let currentItem; let hasChanges; @@ -21,7 +24,7 @@ let hasChanges; function downloadRemoteSubtitles(context, id) { const url = 'Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + id; - const apiClient = window.connectionManager.getApiClient(currentItem.ServerId); + const apiClient = ServerConnections.getApiClient(currentItem.ServerId); apiClient.ajax({ type: 'POST', @@ -30,9 +33,7 @@ function downloadRemoteSubtitles(context, id) { }).then(function () { hasChanges = true; - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageDownloadQueued')); - }); + toast(globalize.translate('MessageDownloadQueued')); focusManager.autoFocus(context); }); @@ -41,31 +42,29 @@ function downloadRemoteSubtitles(context, id) { function deleteLocalSubtitle(context, index) { const msg = globalize.translate('MessageAreYouSureDeleteSubtitles'); - import('confirm').then(({default: confirm}) => { - confirm({ + confirm({ - title: globalize.translate('ConfirmDeletion'), - text: msg, - confirmText: globalize.translate('Delete'), - primary: 'delete' + title: globalize.translate('ConfirmDeletion'), + text: msg, + confirmText: globalize.translate('Delete'), + primary: 'delete' - }).then(function () { - loading.show(); + }).then(function () { + loading.show(); - const itemId = currentItem.Id; - const url = 'Videos/' + itemId + '/Subtitles/' + index; + const itemId = currentItem.Id; + const url = 'Videos/' + itemId + '/Subtitles/' + index; - const apiClient = window.connectionManager.getApiClient(currentItem.ServerId); + const apiClient = ServerConnections.getApiClient(currentItem.ServerId); - apiClient.ajax({ + apiClient.ajax({ - type: 'DELETE', - url: apiClient.getUrl(url) + type: 'DELETE', + url: apiClient.getUrl(url) - }).then(function () { - hasChanges = true; - reload(context, apiClient, itemId); - }); + }).then(function () { + hasChanges = true; + reload(context, apiClient, itemId); }); }); } @@ -243,7 +242,7 @@ function searchForSubtitles(context, language) { loading.show(); - const apiClient = window.connectionManager.getApiClient(currentItem.ServerId); + const apiClient = ServerConnections.getApiClient(currentItem.ServerId); const url = apiClient.getUrl('Items/' + currentItem.Id + '/RemoteSearch/Subtitles/' + language); apiClient.getJSON(url).then(function (results) { @@ -329,7 +328,7 @@ function showDownloadOptions(button, context, subtitleId) { id: 'download' }); - import('actionsheet').then(({default: actionsheet}) => { + import('../actionSheet/actionSheet').then((actionsheet) => { actionsheet.show({ items: items, positionTo: button @@ -347,16 +346,38 @@ function showDownloadOptions(button, context, subtitleId) { } function centerFocus(elem, horiz, on) { - import('scrollHelper').then(({default: scrollHelper}) => { + import('../../scripts/scrollHelper').then(({default: scrollHelper}) => { const fn = on ? 'on' : 'off'; scrollHelper.centerFocus[fn](elem, horiz); }); } +function onOpenUploadMenu(e) { + const dialog = dom.parentWithClass(e.target, 'subtitleEditorDialog'); + const selectLanguage = dialog.querySelector('#selectLanguage'); + const apiClient = ServerConnections.getApiClient(currentItem.ServerId); + + import('../subtitleuploader/subtitleuploader').then(({default: subtitleUploader}) => { + subtitleUploader.show({ + languages: { + list: selectLanguage.innerHTML, + value: selectLanguage.value + }, + itemId: currentItem.Id, + serverId: currentItem.ServerId + }).then(function (hasChanged) { + if (hasChanged) { + hasChanges = true; + reload(dialog, apiClient, currentItem.Id); + } + }); + }); +} + function showEditorInternal(itemId, serverId, template) { hasChanges = false; - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { const dialogOptions = { removeOnClose: true, @@ -380,6 +401,8 @@ function showEditorInternal(itemId, serverId, template) { dlg.querySelector('.subtitleSearchForm').addEventListener('submit', onSearchSubmit); + dlg.querySelector('.btnOpenUploadMenu').addEventListener('click', onOpenUploadMenu); + const btnSubmit = dlg.querySelector('.btnSubmit'); if (layoutManager.tv) { @@ -431,7 +454,7 @@ function showEditor(itemId, serverId) { loading.show(); return new Promise(function (resolve, reject) { - import('text!./subtitleeditor.template.html').then(({default: template}) => { + import('./subtitleeditor.template.html').then(({default: template}) => { showEditorInternal(itemId, serverId, template).then(resolve, reject); }); }); diff --git a/src/components/subtitleeditor/subtitleeditor.template.html b/src/components/subtitleeditor/subtitleeditor.template.html index 7471972f590..b01942c995d 100644 --- a/src/components/subtitleeditor/subtitleeditor.template.html +++ b/src/components/subtitleeditor/subtitleeditor.template.html @@ -18,6 +18,7 @@

${SearchForSubtitles}

+
diff --git a/src/components/subtitlesettings/subtitlesettings.js b/src/components/subtitlesettings/subtitlesettings.js index 0e2c4e36619..f49734b1434 100644 --- a/src/components/subtitlesettings/subtitlesettings.js +++ b/src/components/subtitlesettings/subtitlesettings.js @@ -1,20 +1,22 @@ -import globalize from 'globalize'; -import appHost from 'apphost'; -import appSettings from 'appSettings'; -import focusManager from 'focusManager'; -import layoutManager from 'layoutManager'; -import loading from 'loading'; -import subtitleAppearanceHelper from 'subtitleAppearanceHelper'; -import settingsHelper from 'settingsHelper'; -import dom from 'dom'; -import events from 'events'; -import 'listViewStyle'; -import 'emby-select'; -import 'emby-slider'; -import 'emby-input'; -import 'emby-checkbox'; -import 'flexStyles'; -import 'css!./subtitlesettings'; +import globalize from '../../scripts/globalize'; +import { appHost } from '../apphost'; +import appSettings from '../../scripts/settings/appSettings'; +import focusManager from '../focusManager'; +import layoutManager from '../layoutManager'; +import loading from '../loading/loading'; +import subtitleAppearanceHelper from './subtitleappearancehelper'; +import settingsHelper from '../settingshelper'; +import dom from '../../scripts/dom'; +import { Events } from 'jellyfin-apiclient'; +import '../listview/listview.css'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-slider/emby-slider'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../assets/css/flexstyles.scss'; +import './subtitlesettings.css'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; /** * Subtitle settings. @@ -87,12 +89,10 @@ function save(instance, context, userId, userSettings, apiClient, enableSaveConf saveUser(context, user, userSettings, instance.appearanceKey, apiClient).then(function () { 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'); }, function () { loading.hide(); }); @@ -158,7 +158,7 @@ function hideSubtitlePreview(persistent) { } function embed(options, self) { - import('text!./subtitlesettings.template.html').then(({default: template}) => { + import('./subtitlesettings.template.html').then(({default: template}) => { options.element.classList.add('subtitlesettings'); options.element.innerHTML = globalize.translateHtml(template, 'core'); @@ -231,7 +231,7 @@ export class SubtitleSettings { 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(function (user) { @@ -255,7 +255,7 @@ export class SubtitleSettings { 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; diff --git a/src/components/subtitlesync/subtitlesync.js b/src/components/subtitlesync/subtitlesync.js index efb2087a1be..d3477932c38 100644 --- a/src/components/subtitlesync/subtitlesync.js +++ b/src/components/subtitlesync/subtitlesync.js @@ -1,7 +1,8 @@ -import playbackManager from 'playbackManager'; -import layoutManager from 'layoutManager'; -import template from 'text!./subtitlesync.template.html'; -import 'css!./subtitlesync'; + +import { playbackManager } from '../playback/playbackmanager'; +import layoutManager from '../layoutManager'; +import template from './subtitlesync.template.html'; +import './subtitlesync.css'; let player; let subtitleSyncSlider; diff --git a/src/components/subtitleuploader/style.css b/src/components/subtitleuploader/style.css new file mode 100644 index 00000000000..c9d5eb980d5 --- /dev/null +++ b/src/components/subtitleuploader/style.css @@ -0,0 +1,11 @@ +.subtitleEditor-dropZone { + border: 0.2em dashed currentcolor; + border-radius: 0.25em; + + text-align: center; + position: relative; + height: 12em; + display: flex; + align-items: center; + justify-content: center; +} diff --git a/src/components/subtitleuploader/subtitleuploader.js b/src/components/subtitleuploader/subtitleuploader.js new file mode 100644 index 00000000000..1c9b8cfb244 --- /dev/null +++ b/src/components/subtitleuploader/subtitleuploader.js @@ -0,0 +1,172 @@ +import dialogHelper from '../../components/dialogHelper/dialogHelper'; +import ServerConnections from '../ServerConnections'; +import dom from '../../scripts/dom'; +import loading from '../../components/loading/loading'; +import scrollHelper from '../../libraries/scroller'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import template from './subtitleuploader.template.html'; +import toast from '../toast/toast'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-select/emby-select'; +import '../formdialog.css'; +import './style.css'; + +let currentItemId; +let currentServerId; +let currentFile; +let hasChanges = false; + +function onFileReaderError(evt) { + loading.hide(); + + const error = evt.target.error; + if (error.code !== error.ABORT_ERR) { + toast(globalize.translate('MessageFileReadError')); + } +} + +function isValidSubtitleFile(file) { + return file && ['.sub', '.srt', '.vtt', '.ass', '.ssa'] + .some(function(ext) { + return file.name.endsWith(ext); + }); +} + +function setFiles(page, files) { + const file = files[0]; + + if (!isValidSubtitleFile(file)) { + page.querySelector('#subtitleOutput').innerHTML = ''; + page.querySelector('#fldUpload').classList.add('hide'); + page.querySelector('#labelDropSubtitle').classList.remove('hide'); + currentFile = null; + return; + } + + currentFile = file; + + const reader = new FileReader(); + + reader.onerror = onFileReaderError; + reader.onloadstart = function () { + page.querySelector('#fldUpload').classList.add('hide'); + }; + reader.onabort = function () { + loading.hide(); + console.debug('File read cancelled'); + }; + + // Closure to capture the file information. + reader.onload = (function (theFile) { + return function () { + // Render file. + const html = 'subtitles' + escape(theFile.name) + ''; + + page.querySelector('#subtitleOutput').innerHTML = html; + page.querySelector('#fldUpload').classList.remove('hide'); + page.querySelector('#labelDropSubtitle').classList.add('hide'); + }; + })(file); + + // Read in the subtitle file as a data URL. + reader.readAsDataURL(file); +} + +function onSubmit(e) { + const file = currentFile; + + if (!isValidSubtitleFile(file)) { + toast(globalize.translate('MessageSubtitleFileTypeAllowed')); + e.preventDefault(); + return; + } + + loading.show(); + + const dlg = dom.parentWithClass(this, 'dialog'); + const language = dlg.querySelector('#selectLanguage').value; + const isForced = dlg.querySelector('#chkIsForced').checked; + + ServerConnections.getApiClient(currentServerId).uploadItemSubtitle(currentItemId, language, isForced, file).then(function () { + dlg.querySelector('#uploadSubtitle').value = ''; + loading.hide(); + hasChanges = true; + dialogHelper.close(dlg); + }); + + e.preventDefault(); +} + +function initEditor(page) { + page.querySelector('.uploadSubtitleForm').addEventListener('submit', onSubmit); + page.querySelector('#uploadSubtitle').addEventListener('change', function () { + setFiles(page, this.files); + }); + page.querySelector('.btnBrowse').addEventListener('click', function () { + page.querySelector('#uploadSubtitle').click(); + }); +} + +function showEditor(options, resolve, reject) { + options = options || {}; + currentItemId = options.itemId; + currentServerId = options.serverId; + + const dialogOptions = { + removeOnClose: true, + scrollY: false + }; + + if (layoutManager.tv) { + dialogOptions.size = 'fullscreen'; + } else { + dialogOptions.size = 'small'; + } + + const dlg = dialogHelper.createDialog(dialogOptions); + + dlg.classList.add('formDialog'); + dlg.classList.add('subtitleUploaderDialog'); + + dlg.innerHTML = globalize.translateHtml(template, 'core'); + + if (layoutManager.tv) { + scrollHelper.centerFocus.on(dlg, false); + } + + // Has to be assigned a z-index after the call to .open() + dlg.addEventListener('close', function () { + if (layoutManager.tv) { + scrollHelper.centerFocus.off(dlg, false); + } + loading.hide(); + resolve(hasChanges); + }); + + dialogHelper.open(dlg); + + initEditor(dlg); + + const selectLanguage = dlg.querySelector('#selectLanguage'); + + if (options.languages) { + selectLanguage.innerHTML = options.languages.list || null; + selectLanguage.value = options.languages.value || null; + } + + dlg.querySelector('.btnCancel').addEventListener('click', function () { + dialogHelper.close(dlg); + }); +} + +export function show(options) { + return new Promise(function (resolve, reject) { + hasChanges = false; + showEditor(options, resolve, reject); + }); +} + +export default { + show: show +}; diff --git a/src/components/subtitleuploader/subtitleuploader.template.html b/src/components/subtitleuploader/subtitleuploader.template.html new file mode 100644 index 00000000000..3f4944de57f --- /dev/null +++ b/src/components/subtitleuploader/subtitleuploader.template.html @@ -0,0 +1,45 @@ +
+ +

+ ${HeaderUploadSubtitle} +

+
+ +
+
+ +
+ +
+

${HeaderAddUpdateSubtitle}

+ + +
+
+
+
${LabelDropSubtitleHere}
+ + +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
diff --git a/src/components/syncPlay/groupSelectionMenu.js b/src/components/syncPlay/groupSelectionMenu.js index b554817b8c2..5168558bb22 100644 --- a/src/components/syncPlay/groupSelectionMenu.js +++ b/src/components/syncPlay/groupSelectionMenu.js @@ -1,11 +1,12 @@ -import events from 'events'; -import playbackManager from 'playbackManager'; -import syncPlayManager from 'syncPlayManager'; -import loading from 'loading'; -import toast from 'toast'; -import actionsheet from 'actionsheet'; -import globalize from 'globalize'; -import playbackPermissionManager from 'playbackPermissionManager'; +import { Events } from 'jellyfin-apiclient'; +import { playbackManager } from '../playback/playbackmanager'; +import syncPlayManager from './syncPlayManager'; +import loading from '../loading/loading'; +import toast from '../toast/toast'; +import actionsheet from '../actionSheet/actionSheet'; +import globalize from '../../scripts/globalize'; +import playbackPermissionManager from './playbackPermissionManager'; +import ServerConnections from '../ServerConnections'; /** * Gets active player id. @@ -150,7 +151,7 @@ function showLeaveGroupSelection (button, user, apiClient) { // Register to SyncPlay events let syncPlayEnabled = false; -events.on(syncPlayManager, 'enabled', function (e, enabled) { +Events.on(syncPlayManager, 'enabled', function (e, enabled) { syncPlayEnabled = enabled; }); @@ -171,8 +172,8 @@ export function show (button) { }); }); - const apiClient = window.connectionManager.currentApiClient(); - window.connectionManager.user(apiClient).then((user) => { + const apiClient = ServerConnections.currentApiClient(); + ServerConnections.user(apiClient).then((user) => { if (syncPlayEnabled) { showLeaveGroupSelection(button, user, apiClient); } else { diff --git a/src/components/syncPlay/syncPlayManager.js b/src/components/syncPlay/syncPlayManager.js index c72d20a1113..2db5f3817b9 100644 --- a/src/components/syncPlay/syncPlayManager.js +++ b/src/components/syncPlay/syncPlayManager.js @@ -3,11 +3,12 @@ * @module components/syncPlay/syncPlayManager */ -import events from 'events'; -import playbackManager from 'playbackManager'; -import timeSyncManager from 'timeSyncManager'; -import toast from 'toast'; -import globalize from 'globalize'; +import { Events } from 'jellyfin-apiclient'; +import { playbackManager } from '../playback/playbackmanager'; +import timeSyncManager from './timeSyncManager'; +import toast from '../toast/toast'; +import globalize from '../../scripts/globalize'; +import ServerConnections from '../ServerConnections'; /** * Waits for an event to be triggered on an object. An optional timeout can specified after which the promise is rejected. @@ -25,13 +26,13 @@ function waitForEventOnce(emitter, eventType, timeout) { }, timeout); } const callback = () => { - events.off(emitter, eventType, callback); + Events.off(emitter, eventType, callback); if (rejectTimeout) { clearTimeout(rejectTimeout); } resolve(arguments); }; - events.on(emitter, eventType, callback); + Events.on(emitter, eventType, callback); }); } @@ -92,25 +93,25 @@ class SyncPlayManager { this.roundTripDuration = 0; this.notifySyncPlayReady = false; - events.on(playbackManager, 'playbackstart', (player, state) => { + Events.on(playbackManager, 'playbackstart', (player, state) => { this.onPlaybackStart(player, state); }); - events.on(playbackManager, 'playbackstop', (stopInfo) => { + Events.on(playbackManager, 'playbackstop', (stopInfo) => { this.onPlaybackStop(stopInfo); }); - events.on(playbackManager, 'playerchange', () => { + Events.on(playbackManager, 'playerchange', () => { this.onPlayerChange(); }); this.bindToPlayer(playbackManager.getCurrentPlayer()); - events.on(this, 'timeupdate', (event) => { + Events.on(this, 'timeupdate', (event) => { this.syncPlaybackTime(); }); - events.on(timeSyncManager, 'update', (event, error, timeOffset, ping) => { + Events.on(timeSyncManager, 'update', (event, error, timeOffset, ping) => { if (error) { console.debug('SyncPlay, time update issue', error); return; @@ -121,13 +122,13 @@ class SyncPlayManager { if (this.notifySyncPlayReady) { this.syncPlayReady = true; - events.trigger(this, 'ready'); + Events.trigger(this, 'ready'); this.notifySyncPlayReady = false; } // Report ping if (this.syncEnabled) { - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); const sessionId = getActivePlayerId(); if (!sessionId) { @@ -149,14 +150,14 @@ class SyncPlayManager { * Called when playback starts. */ onPlaybackStart (player, state) { - events.trigger(this, 'playbackstart', [player, state]); + Events.trigger(this, 'playbackstart', [player, state]); } /** * Called when playback stops. */ onPlaybackStop (stopInfo) { - events.trigger(this, 'playbackstop', [stopInfo]); + Events.trigger(this, 'playbackstop', [stopInfo]); if (this.isSyncPlayEnabled()) { this.disableSyncPlay(false); } @@ -167,21 +168,21 @@ class SyncPlayManager { */ onPlayerChange () { this.bindToPlayer(playbackManager.getCurrentPlayer()); - events.trigger(this, 'playerchange', [this.currentPlayer]); + Events.trigger(this, 'playerchange', [this.currentPlayer]); } /** * Called when playback unpauses. */ onPlayerUnpause () { - events.trigger(this, 'unpause', [this.currentPlayer]); + Events.trigger(this, 'unpause', [this.currentPlayer]); } /** * Called when playback pauses. */ onPlayerPause() { - events.trigger(this, 'pause', [this.currentPlayer]); + Events.trigger(this, 'pause', [this.currentPlayer]); } /** @@ -191,7 +192,7 @@ class SyncPlayManager { onTimeUpdate (e) { // NOTICE: this event is unreliable, at least in Safari // which just stops firing the event after a while. - events.trigger(this, 'timeupdate', [e]); + Events.trigger(this, 'timeupdate', [e]); } /** @@ -200,7 +201,7 @@ class SyncPlayManager { onPlaying () { // TODO: implement group wait this.lastPlaybackWaiting = null; - events.trigger(this, 'playing'); + Events.trigger(this, 'playing'); } /** @@ -212,7 +213,7 @@ class SyncPlayManager { this.lastPlaybackWaiting = new Date(); } - events.trigger(this, 'waiting'); + Events.trigger(this, 'waiting'); } /** @@ -260,11 +261,11 @@ class SyncPlayManager { self.onWaiting(); }; - events.on(player, 'unpause', this._onPlayerUnpause); - events.on(player, 'pause', this._onPlayerPause); - events.on(player, 'timeupdate', this._onTimeUpdate); - events.on(player, 'playing', this._onPlaying); - events.on(player, 'waiting', this._onWaiting); + Events.on(player, 'unpause', this._onPlayerUnpause); + Events.on(player, 'pause', this._onPlayerPause); + Events.on(player, 'timeupdate', this._onTimeUpdate); + Events.on(player, 'playing', this._onPlaying); + Events.on(player, 'waiting', this._onWaiting); // Save player current PlaybackRate value if (player.supports && player.supports('PlaybackRate')) { @@ -278,11 +279,11 @@ class SyncPlayManager { releaseCurrentPlayer () { const player = this.currentPlayer; if (player) { - events.off(player, 'unpause', this._onPlayerUnpause); - events.off(player, 'pause', this._onPlayerPause); - events.off(player, 'timeupdate', this._onTimeUpdate); - events.off(player, 'playing', this._onPlaying); - events.off(player, 'waiting', this._onWaiting); + Events.off(player, 'unpause', this._onPlayerUnpause); + Events.off(player, 'pause', this._onPlayerPause); + Events.off(player, 'timeupdate', this._onTimeUpdate); + Events.off(player, 'playing', this._onPlaying); + Events.off(player, 'waiting', this._onWaiting); // Restore player original PlaybackRate value if (this.playbackRateSupported) { player.setPlaybackRate(this.localPlayerPlaybackRate); @@ -486,7 +487,7 @@ class SyncPlayManager { enableSyncPlay (apiClient, enabledAt, showMessage = false) { this.syncPlayEnabledAt = enabledAt; this.injectPlaybackManager(); - events.trigger(this, 'enabled', [true]); + Events.trigger(this, 'enabled', [true]); waitForEventOnce(this, 'ready').then(() => { this.processCommand(this.queuedCommand, apiClient); @@ -515,7 +516,7 @@ class SyncPlayManager { this.lastCommand = null; this.queuedCommand = null; this.syncEnabled = false; - events.trigger(this, 'enabled', [false]); + Events.trigger(this, 'enabled', [false]); this.restorePlaybackManager(); if (showMessage) { @@ -659,7 +660,7 @@ class SyncPlayManager { * Overrides PlaybackManager's unpause method. */ playRequest (player) { - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); apiClient.requestSyncPlayStart(); } @@ -667,7 +668,7 @@ class SyncPlayManager { * Overrides PlaybackManager's pause method. */ pauseRequest (player) { - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); apiClient.requestSyncPlayPause(); // Pause locally as well, to give the user some little control playbackManager._localUnpause(player); @@ -677,7 +678,7 @@ class SyncPlayManager { * Overrides PlaybackManager's seek method. */ seekRequest (PositionTicks, player) { - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); apiClient.requestSyncPlaySeek({ PositionTicks: PositionTicks }); @@ -814,7 +815,7 @@ class SyncPlayManager { */ showSyncIcon (syncMethod) { this.syncMethod = syncMethod; - events.trigger(this, 'syncing', [true, this.syncMethod]); + Events.trigger(this, 'syncing', [true, this.syncMethod]); } /** @@ -822,7 +823,7 @@ class SyncPlayManager { */ clearSyncIcon () { this.syncMethod = 'None'; - events.trigger(this, 'syncing', [false, this.syncMethod]); + Events.trigger(this, 'syncing', [false, this.syncMethod]); } /** diff --git a/src/components/syncPlay/timeSyncManager.js b/src/components/syncPlay/timeSyncManager.js index 6ded631de3b..78c160824d4 100644 --- a/src/components/syncPlay/timeSyncManager.js +++ b/src/components/syncPlay/timeSyncManager.js @@ -3,7 +3,8 @@ * @module components/syncPlay/timeSyncManager */ -import events from 'events'; +import { Events } from 'jellyfin-apiclient'; +import ServerConnections from '../ServerConnections'; /** * Time estimation @@ -113,7 +114,7 @@ class TimeSyncManager { if (!this.poller) { this.poller = setTimeout(() => { this.poller = null; - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); const requestSent = new Date(); apiClient.getServerTime().then((response) => { const responseReceived = new Date(); @@ -131,11 +132,11 @@ class TimeSyncManager { this.pings++; } - events.trigger(this, 'update', [null, this.getTimeOffset(), this.getPing()]); + Events.trigger(this, 'update', [null, this.getTimeOffset(), this.getPing()]); }); }).catch((error) => { console.error(error); - events.trigger(this, 'update', [error, null, null]); + Events.trigger(this, 'update', [error, null, null]); }).finally(() => { this.requestPing(); }); diff --git a/src/components/tabbedview/tabbedview.js b/src/components/tabbedview/tabbedview.js index 4e7ccb65297..efc9fb37c56 100644 --- a/src/components/tabbedview/tabbedview.js +++ b/src/components/tabbedview/tabbedview.js @@ -1,7 +1,8 @@ -import backdrop from 'backdrop'; -import * as mainTabsManager from 'mainTabsManager'; -import layoutManager from 'layoutManager'; -import 'emby-tabs'; +import backdrop from '../backdrop/backdrop'; +import * as mainTabsManager from '../maintabsmanager'; +import layoutManager from '../layoutManager'; +import '../../elements/emby-tabs/emby-tabs'; +import { appRouter } from '../appRouter'; function onViewDestroy(e) { const tabControllers = this.tabControllers; @@ -109,7 +110,7 @@ class TabbedView { } } setTitle() { - Emby.Page.setTitle(''); + appRouter.setTitle(''); } } diff --git a/src/components/themeMediaPlayer.js b/src/components/themeMediaPlayer.js index 8f288a53489..5a07b219d8e 100644 --- a/src/components/themeMediaPlayer.js +++ b/src/components/themeMediaPlayer.js @@ -1,5 +1,7 @@ -import playbackManager from 'playbackManager'; -import * as userSettings from 'userSettings'; +import { playbackManager } from './playback/playbackmanager'; +import * as userSettings from '../scripts/settings/userSettings'; +import { Events } from 'jellyfin-apiclient'; +import ServerConnections from './ServerConnections'; let currentOwnerId; let currentThemeIds = []; @@ -61,7 +63,7 @@ function loadThemeMedia(item) { return; } - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); apiClient.getThemeMedia(apiClient.getCurrentUserId(), item.Id, true).then(function (themeMediaResult) { const ownerId = themeMediaResult.ThemeVideosResult.Items.length ? themeMediaResult.ThemeVideosResult.OwnerId : themeMediaResult.ThemeSongsResult.OwnerId; diff --git a/src/components/toast/toast.js b/src/components/toast/toast.js index d9718806725..75bb5ed2ed1 100644 --- a/src/components/toast/toast.js +++ b/src/components/toast/toast.js @@ -1,4 +1,4 @@ -import 'css!./toast'; +import './toast.css'; function remove(elem) { setTimeout(function () { diff --git a/src/components/tunerPicker.js b/src/components/tunerPicker.js index 2e7629a3c50..b85df66ac8a 100644 --- a/src/components/tunerPicker.js +++ b/src/components/tunerPicker.js @@ -1,16 +1,17 @@ -import dialogHelper from 'dialogHelper'; -import dom from 'dom'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import loading from 'loading'; -import browser from 'browser'; -import focusManager from 'focusManager'; -import scrollHelper from 'scrollHelper'; -import 'material-icons'; -import 'formDialogStyle'; -import 'emby-button'; -import 'emby-itemscontainer'; -import 'cardStyle'; +import dialogHelper from './dialogHelper/dialogHelper'; +import dom from '../scripts/dom'; +import layoutManager from './layoutManager'; +import globalize from '../scripts/globalize'; +import loading from './loading/loading'; +import browser from '../scripts/browser'; +import focusManager from './focusManager'; +import scrollHelper from '../scripts/scrollHelper'; +import 'material-design-icons-iconfont'; +import './formdialog.css'; +import '../elements/emby-button/emby-button'; +import '../elements/emby-itemscontainer/emby-itemscontainer'; +import './cardbuilder/card.css'; +import ServerConnections from './ServerConnections'; const enableFocusTransform = !browser.slow && !browser.edge; @@ -162,7 +163,7 @@ function tunerPicker() { scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false); } - const apiClient = window.connectionManager.getApiClient(options.serverId); + const apiClient = ServerConnections.getApiClient(options.serverId); discoverDevices(dlg, apiClient); if (layoutManager.tv) { diff --git a/src/components/tvproviders/schedulesdirect.js b/src/components/tvproviders/schedulesdirect.js index 80de91bd806..8b386566b24 100644 --- a/src/components/tvproviders/schedulesdirect.js +++ b/src/components/tvproviders/schedulesdirect.js @@ -1,13 +1,15 @@ -import $ from 'jQuery'; -import loading from 'loading'; -import globalize from 'globalize'; -import 'emby-checkbox'; -import 'emby-input'; -import 'listViewStyle'; -import 'paper-icon-button-light'; -import 'emby-select'; -import 'emby-button'; -import 'flexStyles'; +import 'jquery'; +import loading from '../loading/loading'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-input/emby-input'; +import '../listview/listview.css'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-button/emby-button'; +import '../../assets/css/flexstyles.scss'; +import Dashboard from '../../scripts/clientUtils'; +import { Events } from 'jellyfin-apiclient'; export default function (page, providerId, options) { function reload() { diff --git a/src/components/tvproviders/xmltv.js b/src/components/tvproviders/xmltv.js index a75b29eeb3b..0220aba6112 100644 --- a/src/components/tvproviders/xmltv.js +++ b/src/components/tvproviders/xmltv.js @@ -1,10 +1,12 @@ -import $ from 'jQuery'; -import loading from 'loading'; -import globalize from 'globalize'; -import 'emby-checkbox'; -import 'emby-input'; -import 'listViewStyle'; -import 'paper-icon-button-light'; +import 'jquery'; +import loading from '../loading/loading'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-input/emby-input'; +import '../listview/listview.css'; +import '../../elements/emby-button/paper-icon-button-light'; +import Dashboard from '../../scripts/clientUtils'; +import { Events } from 'jellyfin-apiclient'; export default function (page, providerId, options) { function getListingProvider(config, id) { @@ -143,7 +145,7 @@ export default function (page, providerId, options) { function onSelectPathClick(e) { const page = $(e.target).parents('.xmltvForm')[0]; - import('directorybrowser').then(({default: directoryBrowser}) => { + import('../directorybrowser/directorybrowser').then(({default: directoryBrowser}) => { const picker = new directoryBrowser(); picker.show({ includeFiles: true, diff --git a/src/components/upnextdialog/upnextdialog.js b/src/components/upnextdialog/upnextdialog.js index 45ac72b1398..702fe4dcf6c 100644 --- a/src/components/upnextdialog/upnextdialog.js +++ b/src/components/upnextdialog/upnextdialog.js @@ -1,14 +1,14 @@ -import dom from 'dom'; -import playbackManager from 'playbackManager'; -import events from 'events'; -import mediaInfo from 'mediaInfo'; -import layoutManager from 'layoutManager'; -import focusManager from 'focusManager'; -import globalize from 'globalize'; -import itemHelper from 'itemHelper'; -import 'css!./upnextdialog'; -import 'emby-button'; -import 'flexStyles'; +import dom from '../../scripts/dom'; +import { playbackManager } from '../playback/playbackmanager'; +import { Events } from 'jellyfin-apiclient'; +import mediaInfo from '../mediainfo/mediainfo'; +import layoutManager from '../layoutManager'; +import focusManager from '../focusManager'; +import globalize from '../../scripts/globalize'; +import itemHelper from '../itemHelper'; +import './upnextdialog.css'; +import '../../elements/emby-button/emby-button'; +import '../../assets/css/flexstyles.scss'; /* eslint-disable indent */ @@ -136,7 +136,7 @@ import 'flexStyles'; elem.classList.add('hide'); clearHideAnimationEventListeners(instance, elem); - events.trigger(instance, 'hide'); + Events.trigger(instance, 'hide'); } function hideComingUpNext() { diff --git a/src/components/userdatabuttons/userdatabuttons.js b/src/components/userdatabuttons/userdatabuttons.js index 6c0cbb8a29b..9fe5546fcbd 100644 --- a/src/components/userdatabuttons/userdatabuttons.js +++ b/src/components/userdatabuttons/userdatabuttons.js @@ -1,10 +1,11 @@ -import globalize from 'globalize'; -import dom from 'dom'; -import itemHelper from 'itemHelper'; -import 'paper-icon-button-light'; -import 'material-icons'; -import 'emby-button'; -import 'css!./userdatabuttons'; +import globalize from '../../scripts/globalize'; +import dom from '../../scripts/dom'; +import itemHelper from '../itemHelper'; +import '../../elements/emby-button/paper-icon-button-light'; +import 'material-design-icons-iconfont'; +import '../../elements/emby-button/emby-button'; +import './userdatabuttons.css'; +import ServerConnections from '../ServerConnections'; const userDataMethods = { markPlayed: markPlayed, @@ -187,12 +188,12 @@ function markPlayed(link) { } function likes(id, serverId, isLiked) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.updateUserItemRating(apiClient.getCurrentUserId(), id, isLiked); } function played(id, serverId, isPlayed) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); const method = isPlayed ? 'markPlayed' : 'markUnplayed'; @@ -200,13 +201,13 @@ function played(id, serverId, isPlayed) { } function favorite(id, serverId, isFavorite) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.updateFavoriteStatus(apiClient.getCurrentUserId(), id, isFavorite); } function clearLike(id, serverId) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.clearUserItemRating(apiClient.getCurrentUserId(), id); } diff --git a/src/components/viewContainer.js b/src/components/viewContainer.js index 6f9059cb10a..7bec82ee751 100644 --- a/src/components/viewContainer.js +++ b/src/components/viewContainer.js @@ -1,4 +1,6 @@ -import 'css!components/viewManager/viewContainer'; +import './viewManager/viewContainer.css'; +import Dashboard from '../scripts/clientUtils'; + /* eslint-disable indent */ function setControllerClass(view, options) { @@ -15,7 +17,7 @@ import 'css!components/viewManager/viewContainer'; controllerUrl = Dashboard.getPluginUrl(controllerUrl); const apiUrl = ApiClient.getUrl('/web/' + controllerUrl); - return import(apiUrl).then((ControllerFactory) => { + return import(/* webpackIgnore: true */ apiUrl).then((ControllerFactory) => { options.controllerFactory = ControllerFactory; }); } diff --git a/src/components/viewManager/viewManager.js b/src/components/viewManager/viewManager.js index 00c3018590c..22faa3a3bda 100644 --- a/src/components/viewManager/viewManager.js +++ b/src/components/viewManager/viewManager.js @@ -1,7 +1,6 @@ -import viewContainer from 'viewContainer'; -import focusManager from 'focusManager'; -import queryString from 'queryString'; -import layoutManager from 'layoutManager'; +import viewContainer from '../viewContainer'; +import focusManager from '../focusManager'; +import layoutManager from '../layoutManager'; let currentView; let dispatchPageEvents; @@ -98,21 +97,25 @@ function dispatchViewEvent(view, eventInfo, eventName, isCancellable) { return eventResult; } -function getViewEventDetail(view, options, isRestore) { - const url = options.url; +function getViewEventDetail(view, {state, url, options = {}}, isRestored) { const index = url.indexOf('?'); - const params = index === -1 ? {} : queryString.parse(url.substring(index + 1)); + // eslint-disable-next-line compat/compat + const searchParams = new URLSearchParams(url.substring(index + 1)); + const params = {}; + + searchParams.forEach((value, key) => + params[key] = value + ); return { detail: { type: view.getAttribute('data-type'), properties: getProperties(view), - params: params, - isRestored: isRestore, - state: options.state, - + params, + isRestored, + state, // The route options - options: options.options || {} + options }, bubbles: true, cancelable: false @@ -169,4 +172,7 @@ class ViewManager { } } -export default new ViewManager(); +const viewManager = new ViewManager(); +viewManager.dispatchPageEvents(true); + +export default viewManager; diff --git a/src/components/viewSettings/viewSettings.js b/src/components/viewSettings/viewSettings.js index fd5b5c3f9e8..0f32f4f6ac8 100644 --- a/src/components/viewSettings/viewSettings.js +++ b/src/components/viewSettings/viewSettings.js @@ -1,15 +1,15 @@ -import dialogHelper from 'dialogHelper'; -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 dialogHelper from '../dialogHelper/dialogHelper'; +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'; function onSubmit(e) { e.preventDefault(); @@ -38,7 +38,7 @@ function saveValues(context, settings, settingsKey) { } 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); }); @@ -59,7 +59,7 @@ class ViewSettings { } show(options) { return new Promise(function (resolve, reject) { - import('text!./viewSettings.template.html').then(({default: template}) => { + import('./viewSettings.template.html').then(({default: template}) => { const dialogOptions = { removeOnClose: true, scrollY: false diff --git a/src/config.json b/src/config.json index 5e5ffe2ed1b..b896b5f068d 100644 --- a/src/config.json +++ b/src/config.json @@ -22,19 +22,20 @@ "id": "wmc" } ], + "servers": [], "plugins": [ - "plugins/playAccessValidation/plugin", - "plugins/experimentalWarnings/plugin", - "plugins/htmlAudioPlayer/plugin", - "plugins/htmlVideoPlayer/plugin", - "plugins/photoPlayer/plugin", - "plugins/comicsPlayer/plugin", - "plugins/bookPlayer/plugin", - "plugins/youtubePlayer/plugin", - "plugins/backdropScreensaver/plugin", - "plugins/pdfPlayer/plugin", - "plugins/logoScreensaver/plugin", - "plugins/sessionPlayer/plugin", - "plugins/chromecastPlayer/plugin" + "playAccessValidation/plugin", + "experimentalWarnings/plugin", + "htmlAudioPlayer/plugin", + "htmlVideoPlayer/plugin", + "photoPlayer/plugin", + "comicsPlayer/plugin", + "bookPlayer/plugin", + "youtubePlayer/plugin", + "backdropScreensaver/plugin", + "pdfPlayer/plugin", + "logoScreensaver/plugin", + "sessionPlayer/plugin", + "chromecastPlayer/plugin" ] } diff --git a/src/controllers/dashboard/apikeys.js b/src/controllers/dashboard/apikeys.js index 2b526aa8cc5..fcee86f827d 100644 --- a/src/controllers/dashboard/apikeys.js +++ b/src/controllers/dashboard/apikeys.js @@ -1,21 +1,21 @@ -import datetime from 'datetime'; -import loading from 'loading'; -import dom from 'dom'; -import globalize from 'globalize'; -import 'emby-button'; +import datetime from '../../scripts/datetime'; +import loading from '../../components/loading/loading'; +import dom from '../../scripts/dom'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-button/emby-button'; +import confirm from '../../components/confirm/confirm'; +import { pageIdOn } from '../../scripts/clientUtils'; /* eslint-disable indent */ function revoke(page, key) { - import('confirm').then(({default: confirm}) => { - confirm(globalize.translate('MessageConfirmRevokeApiKey'), globalize.translate('HeaderConfirmRevokeApiKey')).then(function () { - loading.show(); - ApiClient.ajax({ - type: 'DELETE', - url: ApiClient.getUrl('Auth/Keys/' + key) - }).then(function () { - loadData(page); - }); + confirm(globalize.translate('MessageConfirmRevokeApiKey'), globalize.translate('HeaderConfirmRevokeApiKey')).then(function () { + loading.show(); + ApiClient.ajax({ + type: 'DELETE', + url: ApiClient.getUrl('Auth/Keys/' + key) + }).then(function () { + loadData(page); }); }); } @@ -51,7 +51,7 @@ import 'emby-button'; } function showNewKeyPrompt(page) { - import('prompt').then(({default: prompt}) => { + import('../../components/prompt/prompt').then(({default: prompt}) => { prompt({ title: globalize.translate('HeaderNewApiKey'), label: globalize.translate('LabelAppName'), diff --git a/src/controllers/dashboard/dashboard.js b/src/controllers/dashboard/dashboard.js index 855ce3ff032..2bfecee080b 100644 --- a/src/controllers/dashboard/dashboard.js +++ b/src/controllers/dashboard/dashboard.js @@ -1,66 +1,73 @@ -import datetime from 'datetime'; -import events from 'events'; -import itemHelper from 'itemHelper'; -import serverNotifications from 'serverNotifications'; -import dom from 'dom'; -import taskButton from 'scripts/taskbutton'; -import globalize from 'globalize'; -import * as datefns from 'date-fns'; -import dfnshelper from 'dfnshelper'; -import loading from 'loading'; -import playMethodHelper from 'playMethodHelper'; -import cardBuilder from 'cardBuilder'; -import imageLoader from 'imageLoader'; -import ActivityLog from 'components/activitylog'; -import imageHelper from 'scripts/imagehelper'; -import indicators from 'indicators'; -import 'listViewStyle'; -import 'emby-button'; -import 'flexStyles'; -import 'emby-itemscontainer'; +import datetime from '../../scripts/datetime'; +import { Events } from 'jellyfin-apiclient'; +import itemHelper from '../../components/itemHelper'; +import serverNotifications from '../../scripts/serverNotifications'; +import dom from '../../scripts/dom'; +import globalize from '../../scripts/globalize'; +import { formatDistanceToNow } from 'date-fns'; +import { localeWithSuffix } from '../../scripts/dfnshelper'; +import loading from '../../components/loading/loading'; +import playMethodHelper from '../../components/playback/playmethodhelper'; +import cardBuilder from '../../components/cardbuilder/cardBuilder'; +import imageLoader from '../../components/images/imageLoader'; +import ActivityLog from '../../components/activitylog'; +import imageHelper from '../../scripts/imagehelper'; +import indicators from '../../components/indicators/indicators'; +import '../../components/listview/listview.css'; +import '../../elements/emby-button/emby-button'; +import '../../assets/css/flexstyles.scss'; +import '../../elements/emby-itemscontainer/emby-itemscontainer'; +import taskButton from '../../scripts/taskbutton'; +import Dashboard from '../../scripts/clientUtils'; +import ServerConnections from '../../components/ServerConnections'; +import alert from '../../components/alert'; +import confirm from '../../components/confirm/confirm'; /* eslint-disable indent */ function showPlaybackInfo(btn, session) { - import('alert').then(({default: alert}) => { - let title; - const text = []; - const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session); + let title; + const text = []; + const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session); + + if (displayPlayMethod === 'Remux') { + title = globalize.translate('Remuxing'); + text.push(globalize.translate('RemuxHelp1')); + text.push('
'); + text.push(globalize.translate('RemuxHelp2')); + } else if (displayPlayMethod === 'DirectStream') { + title = globalize.translate('DirectStreaming'); + text.push(globalize.translate('DirectStreamHelp1')); + text.push('
'); + text.push(globalize.translate('DirectStreamHelp2')); + } else if (displayPlayMethod === 'Transcode') { + title = globalize.translate('Transcoding'); + text.push(globalize.translate('MediaIsBeingConverted')); - if (displayPlayMethod === 'DirectStream') { - title = globalize.translate('DirectStreaming'); - text.push(globalize.translate('DirectStreamHelp1')); + if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length) { text.push('
'); - text.push(globalize.translate('DirectStreamHelp2')); - } else if (displayPlayMethod === 'Transcode') { - title = globalize.translate('Transcoding'); - text.push(globalize.translate('MediaIsBeingConverted')); - - if (session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length) { - text.push('
'); - text.push(globalize.translate('LabelReasonForTranscoding')); - session.TranscodingInfo.TranscodeReasons.forEach(function (transcodeReason) { - text.push(globalize.translate(transcodeReason)); - }); - } + text.push(globalize.translate('LabelReasonForTranscoding')); + session.TranscodingInfo.TranscodeReasons.forEach(function (transcodeReason) { + text.push(globalize.translate(transcodeReason)); + }); } + } - alert({ - text: text.join('
'), - title: title - }); + alert({ + text: text.join('
'), + title: title }); } function showSendMessageForm(btn, session) { - import('prompt').then(({default: prompt}) => { + import('../../components/prompt/prompt').then(({default: prompt}) => { prompt({ title: globalize.translate('HeaderSendMessage'), label: globalize.translate('LabelMessageText'), confirmText: globalize.translate('ButtonSend') }).then(function (text) { if (text) { - window.connectionManager.getApiClient(session.ServerId).sendMessageCommand(session.Id, { + ServerConnections.getApiClient(session.ServerId).sendMessageCommand(session.Id, { Text: text, TimeoutMs: 5e3 }); @@ -70,10 +77,10 @@ import 'emby-itemscontainer'; } function showOptionsMenu(btn, session) { - import('actionsheet').then(({default: actionsheet}) => { + import('../../components/actionSheet/actionSheet').then(({default: actionsheet}) => { const menuItems = []; - if (session.ServerId && session.DeviceId !== window.connectionManager.deviceId()) { + if (session.ServerId && session.DeviceId !== ServerConnections.deviceId()) { menuItems.push({ name: globalize.translate('SendMessage'), id: 'sendmessage' @@ -123,9 +130,9 @@ import 'emby-itemscontainer'; } else if (btn.classList.contains('btnSessionSendMessage')) { showSendMessageForm(btn, session); } else if (btn.classList.contains('btnSessionStop')) { - window.connectionManager.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'Stop'); + ServerConnections.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'Stop'); } else if (btn.classList.contains('btnSessionPlayPause') && session.PlayState) { - window.connectionManager.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'PlayPause'); + ServerConnections.getApiClient(session.ServerId).sendPlayStateCommand(session.Id, 'PlayPause'); } } } @@ -313,7 +320,7 @@ import 'emby-itemscontainer'; btnCssClass = session.TranscodingInfo && session.TranscodingInfo.TranscodeReasons && session.TranscodingInfo.TranscodeReasons.length ? '' : ' hide'; html += ''; - btnCssClass = session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1 && session.DeviceId !== window.connectionManager.deviceId() ? '' : ' hide'; + btnCssClass = session.ServerId && session.SupportedCommands.indexOf('DisplayMessage') !== -1 && session.DeviceId !== ServerConnections.deviceId() ? '' : ' hide'; html += ''; html += '
'; @@ -395,7 +402,11 @@ import 'emby-itemscontainer'; let showTranscodingInfo = false; const displayPlayMethod = playMethodHelper.getDisplayPlayMethod(session); - if (displayPlayMethod === 'DirectStream') { + if (displayPlayMethod === 'DirectPlay') { + html += globalize.translate('DirectPlaying'); + } else if (displayPlayMethod === 'Remux') { + html += globalize.translate('Remuxing'); + } else if (displayPlayMethod === 'DirectStream') { html += globalize.translate('DirectStreaming'); } else if (displayPlayMethod === 'Transcode') { html += globalize.translate('Transcoding'); @@ -405,8 +416,6 @@ import 'emby-itemscontainer'; } showTranscodingInfo = true; - } else if (displayPlayMethod === 'DirectPlay') { - html += globalize.translate('DirectPlaying'); } if (showTranscodingInfo) { @@ -422,20 +431,20 @@ import 'emby-itemscontainer'; } if (session.TranscodingInfo.Container) { - line.push(session.TranscodingInfo.Container); + line.push(session.TranscodingInfo.Container.toUpperCase()); } if (session.TranscodingInfo.VideoCodec) { - line.push(session.TranscodingInfo.VideoCodec); + line.push(session.TranscodingInfo.VideoCodec.toUpperCase()); } if (session.TranscodingInfo.AudioCodec && session.TranscodingInfo.AudioCodec != session.TranscodingInfo.Container) { - line.push(session.TranscodingInfo.AudioCodec); + line.push(session.TranscodingInfo.AudioCodec.toUpperCase()); } } if (line.length) { - html += ' - ' + line.join(' '); + html += '

' + line.join(' '); } } @@ -473,7 +482,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 +729,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 +804,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 +844,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 17e28b9bdb1..18404d605fc 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 c6e7281645c..e1eb99e677b 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 478b5ca878a..74b4f2a25c7 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 4eb830df6ff..e507fc4e7c6 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 fb93441a55f..33c35b96444 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 7a43d67e46e..4c7b8c3a57a 100644 --- a/src/controllers/dashboard/encodingsettings.html +++ b/src/controllers/dashboard/encodingsettings.html @@ -94,6 +94,15 @@

${LabelEnableHardwareDecodingFor}

+
+
+ +
+
+
+
+ +
${H264CrfHelp}
-
+ ${LabelCreateHttpPortMap} + +
${LabelCreateHttpPortMapHelp}
+
${LabelPublicHttpPortHelp}
@@ -114,6 +121,75 @@

${TabNetworking}

${LabelPublicHttpsPortHelp}
+ +
+

${HeaderNetworking}

+
+ +
${LabelEnableIP4Help}
+
+
+ +
${LabelEnableIP6Help}
+
+
+ + +
+

${HeaderAutoDiscovery}

+
+ +
${LabelAutomaticDiscoveryHelp}
+
+
+ +
+

${HeaderPortRanges}

+ +
+ +
${LabelPublishedServerUriHelp}
+
+
+ +
${LabelUDPPortRangeHelp}
+
+
+ +
${LabelHDHomerunPortRangeHelp}
+
+
+ +
+

${HeaderDebugging}

+
+ +
${LabelEnableSSDPTracingHelp}
+
+
+ +
${LabelSSDPTracingFilterHelp}
+
+
+ +
${LabelAutoDiscoveryTracingHelp}
+
+
- -
diff --git a/src/controllers/playback/video/index.js b/src/controllers/playback/video/index.js index 492f4aeee91..0cc37fa985e 100644 --- a/src/controllers/playback/video/index.js +++ b/src/controllers/playback/video/index.js @@ -1,22 +1,26 @@ -import playbackManager from 'playbackManager'; -import dom from 'dom'; -import inputManager from 'inputManager'; -import mouseManager from 'mouseManager'; -import datetime from 'datetime'; -import itemHelper from 'itemHelper'; -import mediaInfo from 'mediaInfo'; -import focusManager from 'focusManager'; -import events from 'events'; -import browser from 'browser'; -import globalize from 'globalize'; -import appHost from 'apphost'; -import layoutManager from 'layoutManager'; -import * as userSettings from 'userSettings'; -import keyboardnavigation from 'keyboardnavigation'; -import 'scrollStyles'; -import 'emby-slider'; -import 'paper-icon-button-light'; -import 'css!assets/css/videoosd'; +import { playbackManager } from '../../../components/playback/playbackmanager'; +import dom from '../../../scripts/dom'; +import inputManager from '../../../scripts/inputManager'; +import mouseManager from '../../../scripts/mouseManager'; +import datetime from '../../../scripts/datetime'; +import itemHelper from '../../../components/itemHelper'; +import mediaInfo from '../../../components/mediainfo/mediainfo'; +import focusManager from '../../../components/focusManager'; +import { Events } from 'jellyfin-apiclient'; +import browser from '../../../scripts/browser'; +import globalize from '../../../scripts/globalize'; +import { appHost } from '../../../components/apphost'; +import layoutManager from '../../../components/layoutManager'; +import * as userSettings from '../../../scripts/settings/userSettings'; +import keyboardnavigation from '../../../scripts/keyboardNavigation'; +import '../../../assets/css/scrollstyles.css'; +import '../../../elements/emby-slider/emby-slider'; +import '../../../elements/emby-button/paper-icon-button-light'; +import '../../../assets/css/videoosd.css'; +import ServerConnections from '../../../components/ServerConnections'; +import shell from '../../../scripts/shell'; +import SubtitleSync from '../../../components/subtitlesync/subtitlesync'; +import { appRouter } from '../../../components/appRouter'; /* eslint-disable indent */ @@ -73,7 +77,7 @@ import 'css!assets/css/videoosd'; function getDisplayItem(item) { if (item.Type === 'TvChannel') { - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); return apiClient.getItem(apiClient.getCurrentUserId(), item.Id).then(function (refreshedItem) { return { originalItem: refreshedItem, @@ -97,9 +101,9 @@ import 'css!assets/css/videoosd'; return void view.querySelector('.btnRecord').classList.add('hide'); } - window.connectionManager.getApiClient(item.ServerId).getCurrentUser().then(function (user) { + ServerConnections.getApiClient(item.ServerId).getCurrentUser().then(function (user) { if (user.Policy.EnableLiveTvManagement) { - import('recordingButton').then(({default: RecordingButton}) => { + import('../../../components/recordingcreator/recordingbutton').then(({default: RecordingButton}) => { if (recordingButtonManager) { return void recordingButtonManager.refreshItem(item); } @@ -190,7 +194,7 @@ import 'css!assets/css/videoosd'; currentItem = item; if (!item) { updateRecordingButton(null); - Emby.Page.setTitle(''); + appRouter.setTitle(''); nowPlayingVolumeSlider.disabled = true; nowPlayingPositionSlider.disabled = true; btnFastForward.disabled = true; @@ -238,7 +242,7 @@ import 'css!assets/css/videoosd'; itemName = parentName || ''; } - Emby.Page.setTitle(itemName); + appRouter.setTitle(itemName); const documentTitle = parentName || (item ? item.Name : null); @@ -510,7 +514,7 @@ import 'css!assets/css/videoosd'; if (state.NextMediaType !== 'Video') { view.removeEventListener('viewbeforehide', onViewHideStopPlayback); - Emby.Page.back(); + appRouter.back(); } } @@ -540,16 +544,16 @@ import 'css!assets/css/videoosd'; onStateChanged.call(player, { type: 'init' }, state); - events.on(player, 'playbackstart', onPlaybackStart); - 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, 'fullscreenchange', updateFullscreenIcon); - events.on(player, 'mediastreamschange', onMediaStreamsChanged); - events.on(player, 'beginFetch', onBeginFetch); - events.on(player, 'endFetch', onEndFetch); + Events.on(player, 'playbackstart', onPlaybackStart); + 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, 'fullscreenchange', updateFullscreenIcon); + Events.on(player, 'mediastreamschange', onMediaStreamsChanged); + Events.on(player, 'beginFetch', onBeginFetch); + Events.on(player, 'endFetch', onEndFetch); resetUpNextDialog(); if (player.isFetching) { @@ -564,14 +568,14 @@ import 'css!assets/css/videoosd'; const player = currentPlayer; if (player) { - events.off(player, 'playbackstart', onPlaybackStart); - 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, 'fullscreenchange', updateFullscreenIcon); - events.off(player, 'mediastreamschange', onMediaStreamsChanged); + Events.off(player, 'playbackstart', onPlaybackStart); + 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, 'fullscreenchange', updateFullscreenIcon); + Events.off(player, 'mediastreamschange', onMediaStreamsChanged); currentPlayer = null; } } @@ -613,7 +617,7 @@ import 'css!assets/css/videoosd'; } function showComingUpNext(player) { - import('upNextDialog').then(({default: UpNextDialog}) => { + import('../../../components/upnextdialog/upnextdialog').then(({default: UpNextDialog}) => { if (!(currentVisibleMenu || currentUpNextDialog)) { currentVisibleMenu = 'upnext'; comingUpNextDisplayed = true; @@ -623,7 +627,7 @@ import 'css!assets/css/videoosd'; player: player, nextItem: nextItem }); - events.on(currentUpNextDialog, 'hide', onUpNextHidden); + Events.on(currentUpNextDialog, 'hide', onUpNextHidden); }, onUpNextHidden); } }); @@ -760,7 +764,7 @@ import 'css!assets/css/videoosd'; } if (runtimeTicks && positionTicks != null && currentRuntimeTicks && !enableProgressByTimeOfDay && currentItem.RunTimeTicks && currentItem.Type !== 'Recording') { - endsAtText.innerHTML = '  -  ' + mediaInfo.getEndsAtFromPosition(runtimeTicks, positionTicks, true); + endsAtText.innerHTML = '    ' + mediaInfo.getEndsAtFromPosition(runtimeTicks, positionTicks, true); } else { endsAtText.innerHTML = ''; } @@ -770,8 +774,20 @@ import 'css!assets/css/videoosd'; nowPlayingPositionSlider.setBufferedRanges(bufferedRanges, runtimeTicks, positionTicks); } - updateTimeText(nowPlayingPositionText, positionTicks); - updateTimeText(nowPlayingDurationText, runtimeTicks, true); + if (positionTicks >= 0) { + updateTimeText(nowPlayingPositionText, positionTicks); + nowPlayingPositionText.classList.remove('hide'); + } else { + nowPlayingPositionText.classList.add('hide'); + } + + const leftTicks = runtimeTicks - positionTicks; + if (leftTicks >= 0) { + updateTimeText(nowPlayingDurationText, leftTicks); + nowPlayingDurationText.classList.remove('hide'); + } else { + nowPlayingPositionText.classList.add('hide'); + } } } @@ -852,7 +868,7 @@ import 'css!assets/css/videoosd'; function onSettingsButtonClick(e) { const btn = this; - import('playerSettingsMenu').then(({default: playerSettingsMenu}) => { + import('../../../components/playback/playersettingsmenu').then((playerSettingsMenu) => { const player = currentPlayer; if (player) { @@ -889,7 +905,7 @@ import 'css!assets/css/videoosd'; } function toggleStats() { - import('playerStats').then(({default: PlayerStats}) => { + import('../../../components/playerstats/playerstats').then(({default: PlayerStats}) => { const player = currentPlayer; if (player) { @@ -929,7 +945,7 @@ import 'css!assets/css/videoosd'; }); const positionTo = this; - import('actionsheet').then(({default: actionsheet}) => { + import('../../../components/actionSheet/actionSheet').then(({default: actionsheet}) => { actionsheet.show({ items: menuItems, title: globalize.translate('Audio'), @@ -975,7 +991,7 @@ import 'css!assets/css/videoosd'; }); const positionTo = this; - import('actionsheet').then(({default: actionsheet}) => { + import('../../../components/actionSheet/actionSheet').then(({default: actionsheet}) => { actionsheet.show({ title: globalize.translate('Subtitles'), items: menuItems, @@ -997,14 +1013,12 @@ import 'css!assets/css/videoosd'; } function toggleSubtitleSync(action) { - import('subtitleSync').then(({default: SubtitleSync}) => { - const player = currentPlayer; - if (subtitleSyncOverlay) { - subtitleSyncOverlay.toggle(action); - } else if (player) { - subtitleSyncOverlay = new SubtitleSync(player); - } - }); + const player = currentPlayer; + if (subtitleSyncOverlay) { + subtitleSyncOverlay.toggle(action); + } else if (player) { + subtitleSyncOverlay = new SubtitleSync(player); + } } function destroySubtitleSync() { @@ -1228,9 +1242,7 @@ import 'css!assets/css/videoosd'; let playPauseClickTimeout; function onViewHideStopPlayback() { if (playbackManager.isPlayingVideo()) { - import('shell').then(({default: shell}) => { - shell.disableFullscreen(); - }); + shell.disableFullscreen(); clearTimeout(playPauseClickTimeout); const player = currentPlayer; @@ -1248,9 +1260,7 @@ import 'css!assets/css/videoosd'; } } - import('shell').then(({default: shell}) => { - shell.enableFullscreen(); - }); + shell.enableFullscreen(); let currentPlayer; let comingUpNextDisplayed; @@ -1295,11 +1305,11 @@ import 'css!assets/css/videoosd'; view.addEventListener('viewbeforeshow', function (e) { headerElement.classList.add('osdHeader'); - Emby.Page.setTransparency('full'); + appRouter.setTransparency('full'); }); view.addEventListener('viewshow', function (e) { try { - events.on(playbackManager, 'playerchange', onPlayerChange); + Events.on(playbackManager, 'playerchange', onPlayerChange); bindToPlayer(playbackManager.getCurrentPlayer()); /* eslint-disable-next-line compat/compat */ dom.addEventListener(document, window.PointerEvent ? 'pointermove' : 'mousemove', onPointerMove, { @@ -1337,9 +1347,7 @@ import 'css!assets/css/videoosd'; passive: true }); } catch (e) { - import('appRouter').then(({default: appRouter}) => { - appRouter.goHome(); - }); + appRouter.goHome(); } }); view.addEventListener('viewbeforehide', function () { @@ -1384,7 +1392,7 @@ import 'css!assets/css/videoosd'; passive: true }); inputManager.off(window, onInputCommand); - events.off(playbackManager, 'playerchange', onPlayerChange); + Events.off(playbackManager, 'playerchange', onPlayerChange); releaseCurrentPlayer(); }); view.querySelector('.btnFullscreen').addEventListener('click', function () { @@ -1515,7 +1523,7 @@ import 'css!assets/css/videoosd'; const item = currentItem; if (item && item.Chapters && item.Chapters.length && item.Chapters[0].ImageTag) { - const html = getChapterBubbleHtml(window.connectionManager.getApiClient(item.ServerId), item, item.Chapters, ticks); + const html = getChapterBubbleHtml(ServerConnections.getApiClient(item.ServerId), item, item.Chapters, ticks); if (html) { return html; @@ -1548,15 +1556,15 @@ import 'css!assets/css/videoosd'; if (browser.touch) { (function () { - import('touchHelper').then(({default: TouchHelper}) => { + import('../../../scripts/touchHelper').then(({default: TouchHelper}) => { self.touchHelper = new TouchHelper(view, { swipeYThreshold: 30, triggerOnMove: true, preventDefaultOnMove: true, ignoreTagNames: ['BUTTON', 'INPUT', 'TEXTAREA'] }); - events.on(self.touchHelper, 'swipeup', onVerticalSwipe); - events.on(self.touchHelper, 'swipedown', onVerticalSwipe); + Events.on(self.touchHelper, 'swipeup', onVerticalSwipe); + Events.on(self.touchHelper, 'swipedown', onVerticalSwipe); }); })(); } diff --git a/src/controllers/searchpage.js b/src/controllers/searchpage.js index ffb7fbac0b0..b96c6f4b104 100644 --- a/src/controllers/searchpage.js +++ b/src/controllers/searchpage.js @@ -1,6 +1,6 @@ -import SearchFields from 'searchFields'; -import SearchResults from 'searchResults'; -import events from 'events'; +import SearchFields from '../components/search/searchfields'; +import SearchResults from '../components/search/searchresults'; +import { Events } from 'jellyfin-apiclient'; export default function (view, params) { function onSearch(e, value) { @@ -19,7 +19,7 @@ export default function (view, params) { parentId: params.parentId, collectionType: params.collectionType }); - events.on(self.searchFields, 'search', onSearch); + Events.on(self.searchFields, 'search', onSearch); } }); view.addEventListener('viewdestroy', function () { diff --git a/src/controllers/session/addServer/index.js b/src/controllers/session/addServer/index.js index 472d1274a94..aabfdbcca8b 100644 --- a/src/controllers/session/addServer/index.js +++ b/src/controllers/session/addServer/index.js @@ -1,7 +1,9 @@ -import appSettings from 'appSettings'; -import loading from 'loading'; -import globalize from 'globalize'; -import 'emby-button'; +import appSettings from '../../../scripts/settings/appSettings'; +import loading from '../../../components/loading/loading'; +import globalize from '../../../scripts/globalize'; +import '../../../elements/emby-button/emby-button'; +import Dashboard from '../../../scripts/clientUtils'; +import ServerConnections from '../../../components/ServerConnections'; /* eslint-disable indent */ @@ -36,7 +38,7 @@ import 'emby-button'; function submitServer(page) { loading.show(); const host = page.querySelector('#txtServerHost').value; - window.connectionManager.connectToAddress(host, { + ServerConnections.connectToAddress(host, { enableAutoLogin: appSettings.enableAutoLogin() }).then(function(result) { handleConnectionResult(page, result); @@ -51,7 +53,7 @@ import 'emby-button'; view.querySelector('.addServerForm').addEventListener('submit', onServerSubmit); view.querySelector('.btnCancel').addEventListener('click', goBack); - import('autoFocuser').then(({default: autoFocuser}) => { + import('../../../components/autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(view); }); @@ -62,7 +64,7 @@ import 'emby-button'; } function goBack() { - import('appRouter').then(({default: appRouter}) => { + import('../../../components/appRouter').then(({default: appRouter}) => { appRouter.back(); }); } diff --git a/src/controllers/session/forgotPassword/index.js b/src/controllers/session/forgotPassword/index.js index 5b92e255da5..203605b8bb9 100644 --- a/src/controllers/session/forgotPassword/index.js +++ b/src/controllers/session/forgotPassword/index.js @@ -1,4 +1,5 @@ -import globalize from 'globalize'; +import globalize from '../../../scripts/globalize'; +import Dashboard from '../../../scripts/clientUtils'; /* eslint-disable indent */ diff --git a/src/controllers/session/login/index.js b/src/controllers/session/login/index.js index 1592e6b112d..35191a1954d 100644 --- a/src/controllers/session/login/index.js +++ b/src/controllers/session/login/index.js @@ -1,13 +1,16 @@ -import appHost from 'apphost'; -import appSettings from 'appSettings'; -import dom from 'dom'; -import loading from 'loading'; -import layoutManager from 'layoutManager'; -import libraryMenu from 'libraryMenu'; -import browser from 'browser'; -import globalize from 'globalize'; -import 'cardStyle'; -import 'emby-checkbox'; +import { appHost } from '../../../components/apphost'; +import appSettings from '../../../scripts/settings/appSettings'; +import dom from '../../../scripts/dom'; +import loading from '../../../components/loading/loading'; +import layoutManager from '../../../components/layoutManager'; +import libraryMenu from '../../../scripts/libraryMenu'; +import browser from '../../../scripts/browser'; +import globalize from '../../../scripts/globalize'; +import '../../../components/cardbuilder/card.css'; +import '../../../elements/emby-checkbox/emby-checkbox'; +import Dashboard from '../../../scripts/clientUtils'; +import ServerConnections from '../../../components/ServerConnections'; +import toast from '../../../components/toast/toast'; /* eslint-disable indent */ @@ -27,10 +30,8 @@ import 'emby-checkbox'; const UnauthorizedOrForbidden = [401, 403]; if (UnauthorizedOrForbidden.includes(response.status)) { - import('toast').then(({default: toast}) => { - const messageKey = response.status === 401 ? 'MessageInvalidUser' : 'MessageUnauthorizedUser'; - toast(globalize.translate(messageKey)); - }); + const messageKey = response.status === 401 ? 'MessageInvalidUser' : 'MessageUnauthorizedUser'; + toast(globalize.translate(messageKey)); } else { Dashboard.alert({ message: globalize.translate('MessageUnableToConnectToServer'), @@ -191,7 +192,7 @@ import 'emby-checkbox'; const serverId = params.serverid; if (serverId) { - return window.connectionManager.getOrCreateApiClient(serverId); + return ServerConnections.getOrCreateApiClient(serverId); } return ApiClient; @@ -202,7 +203,7 @@ import 'emby-checkbox'; view.querySelector('.manualLoginForm').classList.add('hide'); view.querySelector('.btnManual').classList.remove('hide'); - import('autoFocuser').then(({default: autoFocuser}) => { + import('../../../components/autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(view); }); } diff --git a/src/controllers/session/resetPassword/index.js b/src/controllers/session/resetPassword/index.js index d4f7df5bbd6..cc84d76fae2 100644 --- a/src/controllers/session/resetPassword/index.js +++ b/src/controllers/session/resetPassword/index.js @@ -1,4 +1,5 @@ -import globalize from 'globalize'; +import globalize from '../../../scripts/globalize'; +import Dashboard from '../../../scripts/clientUtils'; /* eslint-disable indent */ diff --git a/src/controllers/session/selectServer/index.js b/src/controllers/session/selectServer/index.js index 6a590fc3188..32674a8062e 100644 --- a/src/controllers/session/selectServer/index.js +++ b/src/controllers/session/selectServer/index.js @@ -1,19 +1,22 @@ -import loading from 'loading'; -import appRouter from 'appRouter'; -import layoutManager from 'layoutManager'; -import libraryMenu from 'libraryMenu'; -import appSettings from 'appSettings'; -import focusManager from 'focusManager'; -import globalize from 'globalize'; -import actionSheet from 'actionsheet'; -import dom from 'dom'; -import browser from 'browser'; -import 'material-icons'; -import 'flexStyles'; -import 'emby-scroller'; -import 'emby-itemscontainer'; -import 'cardStyle'; -import 'emby-button'; +import loading from '../../../components/loading/loading'; +import { appRouter } from '../../../components/appRouter'; +import layoutManager from '../../../components/layoutManager'; +import libraryMenu from '../../../scripts/libraryMenu'; +import appSettings from '../../../scripts/settings/appSettings'; +import focusManager from '../../../components/focusManager'; +import globalize from '../../../scripts/globalize'; +import actionSheet from '../../../components/actionSheet/actionSheet'; +import dom from '../../../scripts/dom'; +import browser from '../../../scripts/browser'; +import 'material-design-icons-iconfont'; +import '../../../assets/css/flexstyles.scss'; +import '../../../elements/emby-scroller/emby-scroller'; +import '../../../elements/emby-itemscontainer/emby-itemscontainer'; +import '../../../components/cardbuilder/card.css'; +import '../../../elements/emby-button/emby-button'; +import Dashboard from '../../../scripts/clientUtils'; +import ServerConnections from '../../../components/ServerConnections'; +import alert from '../../../components/alert'; /* eslint-disable indent */ @@ -100,9 +103,7 @@ import 'emby-button'; } function alertTextWithOptions(options) { - import('alert').then(({default: alert}) => { - alert(options); - }); + alert(options); } function showServerConnectionFailure() { @@ -112,7 +113,7 @@ import 'emby-button'; export default function (view, params) { function connectToServer(server) { loading.show(); - window.connectionManager.connectToServer(server, { + ServerConnections.connectToServer(server, { enableAutoLogin: appSettings.enableAutoLogin() }).then(function (result) { loading.hide(); @@ -144,7 +145,7 @@ import 'emby-button'; function deleteServer(server) { loading.show(); - window.connectionManager.deleteServer(server.Id).then(function () { + ServerConnections.deleteServer(server.Id).then(function () { loading.hide(); loadServers(); }); @@ -186,7 +187,7 @@ import 'emby-button'; function loadServers() { loading.show(); - window.connectionManager.getAvailableServers().then(onServersRetrieved); + ServerConnections.getAvailableServers().then(onServersRetrieved); } let servers; diff --git a/src/controllers/shows/episodes.js b/src/controllers/shows/episodes.js index 6dd633d7b0f..3dd08e3f760 100644 --- a/src/controllers/shows/episodes.js +++ b/src/controllers/shows/episodes.js @@ -1,12 +1,13 @@ -import loading from 'loading'; -import events from 'events'; -import libraryBrowser from 'libraryBrowser'; -import imageLoader from 'imageLoader'; -import listView from 'listView'; -import cardBuilder from 'cardBuilder'; -import * as userSettings from 'userSettings'; -import globalize from 'globalize'; -import 'emby-itemscontainer'; +import loading from '../../components/loading/loading'; +import { Events } from 'jellyfin-apiclient'; +import libraryBrowser from '../../scripts/libraryBrowser'; +import imageLoader from '../../components/images/imageLoader'; +import listView from '../../components/listview/listview'; +import cardBuilder from '../../components/cardbuilder/cardBuilder'; +import * as userSettings from '../../scripts/settings/userSettings'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-itemscontainer/emby-itemscontainer'; +import Dashboard from '../../scripts/clientUtils'; /* eslint-disable indent */ @@ -160,7 +161,7 @@ import 'emby-itemscontainer'; loading.hide(); isLoading = false; - import('autoFocuser').then(({default: autoFocuser}) => { + import('../../components/autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(page); }); }); @@ -171,13 +172,13 @@ import 'emby-itemscontainer'; let isLoading = false; self.showFilterMenu = function () { - import('components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => { + import('../../components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => { const filterDialog = new filterDialogFactory({ query: getQuery(tabContent), mode: 'episodes', serverId: ApiClient.serverId() }); - events.on(filterDialog, 'filterchange', function () { + Events.on(filterDialog, 'filterchange', function () { reloadItems(tabContent); }); filterDialog.show(); diff --git a/src/controllers/shows/tvgenres.js b/src/controllers/shows/tvgenres.js index 05dccd38622..b29898730b4 100644 --- a/src/controllers/shows/tvgenres.js +++ b/src/controllers/shows/tvgenres.js @@ -1,11 +1,11 @@ -import layoutManager from 'layoutManager'; -import loading from 'loading'; -import libraryBrowser from 'libraryBrowser'; -import cardBuilder from 'cardBuilder'; -import lazyLoader from 'lazyLoader'; -import globalize from 'globalize'; -import appRouter from 'appRouter'; -import 'emby-button'; +import layoutManager from '../../components/layoutManager'; +import loading from '../../components/loading/loading'; +import libraryBrowser from '../../scripts/libraryBrowser'; +import cardBuilder from '../../components/cardbuilder/cardBuilder'; +import lazyLoader from '../../components/lazyLoader/lazyLoaderIntersectionObserver'; +import globalize from '../../scripts/globalize'; +import { appRouter } from '../../components/appRouter'; +import '../../elements/emby-button/emby-button'; /* eslint-disable indent */ diff --git a/src/controllers/shows/tvrecommended.js b/src/controllers/shows/tvrecommended.js index db7bef2d50e..c028a118d8a 100644 --- a/src/controllers/shows/tvrecommended.js +++ b/src/controllers/shows/tvrecommended.js @@ -1,17 +1,20 @@ -import events from 'events'; -import inputManager from 'inputManager'; -import libraryMenu from 'libraryMenu'; -import layoutManager from 'layoutManager'; -import loading from 'loading'; -import dom from 'dom'; -import * as userSettings from 'userSettings'; -import cardBuilder from 'cardBuilder'; -import playbackManager from 'playbackManager'; -import * as mainTabsManager from 'mainTabsManager'; -import globalize from 'globalize'; -import 'scrollStyles'; -import 'emby-itemscontainer'; -import 'emby-button'; + +import { Events } from 'jellyfin-apiclient'; +import inputManager from '../../scripts/inputManager'; +import libraryMenu from '../../scripts/libraryMenu'; +import layoutManager from '../../components/layoutManager'; +import loading from '../../components/loading/loading'; +import dom from '../../scripts/dom'; +import * as userSettings from '../../scripts/settings/userSettings'; +import cardBuilder from '../../components/cardbuilder/cardBuilder'; +import { playbackManager } from '../../components/playback/playbackmanager'; +import * as mainTabsManager from '../../components/maintabsmanager'; +import globalize from '../../scripts/globalize'; +import '../../assets/css/scrollstyles.css'; +import '../../elements/emby-itemscontainer/emby-itemscontainer'; +import '../../elements/emby-button/emby-button'; +import Dashboard from '../../scripts/clientUtils'; +import autoFocuser from '../../components/autoFocuser'; /* eslint-disable indent */ @@ -127,9 +130,7 @@ import 'emby-button'; }); loading.hide(); - import('autoFocuser').then(({default: autoFocuser}) => { - autoFocuser.autoFocus(view); - }); + autoFocuser.autoFocus(view); }); } @@ -168,9 +169,7 @@ import 'emby-button'; }); loading.hide(); - import('autoFocuser').then(({default: autoFocuser}) => { - autoFocuser.autoFocus(view); - }); + autoFocuser.autoFocus(view); }); } @@ -209,9 +208,7 @@ import 'emby-button'; }); loading.hide(); - import('autoFocuser').then(({default: autoFocuser}) => { - autoFocuser.autoFocus(view); - }); + autoFocuser.autoFocus(view); }); } @@ -246,31 +243,31 @@ import 'emby-button'; switch (index) { case 0: - depends = 'controllers/shows/tvshows'; + depends = 'tvshows'; break; case 1: - depends = 'controllers/shows/tvrecommended'; + depends = 'tvrecommended'; break; case 2: - depends = 'controllers/shows/tvupcoming'; + depends = 'tvupcoming'; break; case 3: - depends = 'controllers/shows/tvgenres'; + depends = 'tvgenres'; break; case 4: - depends = 'controllers/shows/tvstudios'; + depends = 'tvstudios'; break; case 5: - depends = 'controllers/shows/episodes'; + depends = 'episodes'; break; } - import(depends).then(({default: controllerFactory}) => { + import(`../shows/${depends}`).then(({default: controllerFactory}) => { let tabContent; if (index === 1) { @@ -373,14 +370,14 @@ import 'emby-button'; } } - events.on(playbackManager, 'playbackstop', onPlaybackStop); - events.on(ApiClient, 'message', onWebSocketMessage); + Events.on(playbackManager, 'playbackstop', onPlaybackStop); + Events.on(ApiClient, 'message', onWebSocketMessage); inputManager.on(window, onInputCommand); }); view.addEventListener('viewbeforehide', function (e) { inputManager.off(window, onInputCommand); - events.off(playbackManager, 'playbackstop', onPlaybackStop); - events.off(ApiClient, 'message', onWebSocketMessage); + Events.off(playbackManager, 'playbackstop', onPlaybackStop); + Events.off(ApiClient, 'message', onWebSocketMessage); }); view.addEventListener('viewdestroy', function (e) { tabControllers.forEach(function (t) { diff --git a/src/controllers/shows/tvshows.js b/src/controllers/shows/tvshows.js index 949c994606b..7ef60e21dbe 100644 --- a/src/controllers/shows/tvshows.js +++ b/src/controllers/shows/tvshows.js @@ -1,13 +1,13 @@ -import loading from 'loading'; -import events from 'events'; -import libraryBrowser from 'libraryBrowser'; -import imageLoader from 'imageLoader'; -import listView from 'listView'; -import cardBuilder from 'cardBuilder'; -import AlphaPicker from 'alphaPicker'; -import * as userSettings from 'userSettings'; -import globalize from 'globalize'; -import 'emby-itemscontainer'; +import loading from '../../components/loading/loading'; +import { Events } from 'jellyfin-apiclient'; +import libraryBrowser from '../../scripts/libraryBrowser'; +import imageLoader from '../../components/images/imageLoader'; +import listView from '../../components/listview/listview'; +import cardBuilder from '../../components/cardbuilder/cardBuilder'; +import AlphaPicker from '../../components/alphaPicker/alphaPicker'; +import * as userSettings from '../../scripts/settings/userSettings'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-itemscontainer/emby-itemscontainer'; /* eslint-disable indent */ @@ -190,7 +190,7 @@ import 'emby-itemscontainer'; loading.hide(); isLoading = false; - import('autoFocuser').then(({default: autoFocuser}) => { + import('../../components/autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(page); }); }); @@ -214,13 +214,13 @@ import 'emby-itemscontainer'; let isLoading = false; this.showFilterMenu = function () { - import('components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => { + import('../../components/filterdialog/filterdialog').then(({default: filterDialogFactory}) => { const filterDialog = new filterDialogFactory({ query: getQuery(tabContent), mode: 'series', serverId: ApiClient.serverId() }); - events.on(filterDialog, 'filterchange', function () { + Events.on(filterDialog, 'filterchange', function () { getQuery(tabContent).StartIndex = 0; reloadItems(tabContent); }); diff --git a/src/controllers/shows/tvstudios.js b/src/controllers/shows/tvstudios.js index 4be717fb7f5..5a0276e5f4b 100644 --- a/src/controllers/shows/tvstudios.js +++ b/src/controllers/shows/tvstudios.js @@ -1,6 +1,6 @@ -import loading from 'loading'; -import libraryBrowser from 'libraryBrowser'; -import cardBuilder from 'cardBuilder'; +import loading from '../../components/loading/loading'; +import libraryBrowser from '../../scripts/libraryBrowser'; +import cardBuilder from '../../components/cardbuilder/cardBuilder'; /* eslint-disable indent */ @@ -50,7 +50,7 @@ import cardBuilder from 'cardBuilder'; }); loading.hide(); - import('autoFocuser').then(({default: autoFocuser}) => { + import('../../components/autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(context); }); }); diff --git a/src/controllers/shows/tvupcoming.js b/src/controllers/shows/tvupcoming.js index f9df3df3434..897c02b5a8a 100644 --- a/src/controllers/shows/tvupcoming.js +++ b/src/controllers/shows/tvupcoming.js @@ -1,11 +1,11 @@ -import layoutManager from 'layoutManager'; -import loading from 'loading'; -import datetime from 'datetime'; -import cardBuilder from 'cardBuilder'; -import imageLoader from 'imageLoader'; -import globalize from 'globalize'; -import 'scrollStyles'; -import 'emby-itemscontainer'; +import layoutManager from '../../components/layoutManager'; +import loading from '../../components/loading/loading'; +import datetime from '../../scripts/datetime'; +import cardBuilder from '../../components/cardbuilder/cardBuilder'; +import imageLoader from '../../components/images/imageLoader'; +import globalize from '../../scripts/globalize'; +import '../../assets/css/scrollstyles.css'; +import '../../elements/emby-itemscontainer/emby-itemscontainer'; /* eslint-disable indent */ diff --git a/src/controllers/user/display/index.js b/src/controllers/user/display/index.js index 54f71ad5713..322da73772b 100644 --- a/src/controllers/user/display/index.js +++ b/src/controllers/user/display/index.js @@ -1,6 +1,6 @@ -import DisplaySettings from 'displaySettings'; -import * as userSettings from 'userSettings'; -import autoFocuser from 'autoFocuser'; +import DisplaySettings from '../../../components/displaySettings/displaySettings'; +import * as userSettings from '../../../scripts/settings/userSettings'; +import autoFocuser from '../../../components/autoFocuser'; /* eslint-disable indent */ diff --git a/src/controllers/user/home/index.js b/src/controllers/user/home/index.js index 539365ff97c..a1bb503adbc 100644 --- a/src/controllers/user/home/index.js +++ b/src/controllers/user/home/index.js @@ -1,7 +1,7 @@ -import HomescreenSettings from 'homescreenSettings'; -import * as userSettings from 'userSettings'; -import autoFocuser from 'autoFocuser'; -import 'listViewStyle'; +import HomescreenSettings from '../../../components/homeScreenSettings/homeScreenSettings'; +import * as userSettings from '../../../scripts/settings/userSettings'; +import autoFocuser from '../../../components/autoFocuser'; +import '../../../components/listview/listview.css'; /* eslint-disable indent */ diff --git a/src/controllers/user/menu/index.js b/src/controllers/user/menu/index.js index 88cf28a2160..cfc5b5f4a5b 100644 --- a/src/controllers/user/menu/index.js +++ b/src/controllers/user/menu/index.js @@ -1,7 +1,8 @@ -import appHost from 'apphost'; -import layoutManager from 'layoutManager'; -import 'listViewStyle'; -import 'emby-button'; +import { appHost } from '../../../components/apphost'; +import '../../../components/listview/listview.css'; +import '../../../elements/emby-button/emby-button'; +import layoutManager from '../../../components/layoutManager'; +import Dashboard from '../../../scripts/clientUtils'; export default function (view, params) { view.querySelector('.btnLogout').addEventListener('click', function () { @@ -53,7 +54,7 @@ export default function (view, params) { page.querySelector('.adminSection').classList.add('hide'); } - import('autoFocuser').then(({default: autoFocuser}) => { + import('../../../components/autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(view); }); }); diff --git a/src/controllers/user/playback/index.js b/src/controllers/user/playback/index.js index 34a5ae0b1df..a44e96ec108 100644 --- a/src/controllers/user/playback/index.js +++ b/src/controllers/user/playback/index.js @@ -1,7 +1,8 @@ -import PlaybackSettings from 'playbackSettings'; -import * as userSettings from 'userSettings'; -import autoFocuser from 'autoFocuser'; -import 'listViewStyle'; + +import PlaybackSettings from '../../../components/playbackSettings/playbackSettings'; +import * as userSettings from '../../../scripts/settings/userSettings'; +import autoFocuser from '../../../components/autoFocuser'; +import '../../../components/listview/listview.css'; /* eslint-disable indent */ diff --git a/src/controllers/user/profile/index.js b/src/controllers/user/profile/index.js index 631253d0193..4398de36e21 100644 --- a/src/controllers/user/profile/index.js +++ b/src/controllers/user/profile/index.js @@ -1,9 +1,12 @@ -import UserPasswordPage from 'controllers/dashboard/users/userpasswordpage'; -import loading from 'loading'; -import libraryMenu from 'libraryMenu'; -import appHost from 'apphost'; -import globalize from 'globalize'; -import 'emby-button'; +import UserPasswordPage from '../../dashboard/users/userpasswordpage'; +import loading from '../../../components/loading/loading'; +import libraryMenu from '../../../scripts/libraryMenu'; +import { appHost } from '../../../components/apphost'; +import globalize from '../../../scripts/globalize'; +import '../../../elements/emby-button/emby-button'; +import Dashboard from '../../../scripts/clientUtils'; +import toast from '../../../components/toast/toast'; +import confirm from '../../../components/confirm/confirm'; function reloadUser(page) { const userId = getParameterByName('userId'); @@ -40,26 +43,20 @@ function onFileReaderError(evt) { loading.hide(); switch (evt.target.error.code) { case evt.target.error.NOT_FOUND_ERR: - import('toast').then(({default: toast}) => { - toast(globalize.translate('FileNotFound')); - }); + toast(globalize.translate('FileNotFound')); break; case evt.target.error.ABORT_ERR: onFileReaderAbort(); break; case evt.target.error.NOT_READABLE_ERR: default: - import('toast').then(({default: toast}) => { - toast(globalize.translate('FileReadError')); - }); + toast(globalize.translate('FileReadError')); } } function onFileReaderAbort(evt) { loading.hide(); - import('toast').then(({default: toast}) => { - toast(globalize.translate('FileReadCancelled')); - }); + toast(globalize.translate('FileReadCancelled')); } function setFiles(page, files) { @@ -89,14 +86,12 @@ export default function (view, params) { reloadUser(view); new UserPasswordPage(view, params); view.querySelector('#btnDeleteImage').addEventListener('click', function () { - import('confirm').then(({default: confirm}) => { - confirm(globalize.translate('DeleteImageConfirmation'), globalize.translate('DeleteImage')).then(function () { - loading.show(); - const userId = getParameterByName('userId'); - ApiClient.deleteUserImage(userId, 'primary').then(function () { - loading.hide(); - reloadUser(view); - }); + confirm(globalize.translate('DeleteImageConfirmation'), globalize.translate('DeleteImage')).then(function () { + loading.show(); + const userId = getParameterByName('userId'); + ApiClient.deleteUserImage(userId, 'primary').then(function () { + loading.hide(); + reloadUser(view); }); }); }); diff --git a/src/controllers/user/quickConnect/index.js b/src/controllers/user/quickConnect/index.js index 1600765ed59..162f3d97839 100644 --- a/src/controllers/user/quickConnect/index.js +++ b/src/controllers/user/quickConnect/index.js @@ -1,6 +1,6 @@ -import QuickConnectSettings from 'quickConnectSettings'; -import globalize from 'globalize'; -import toast from 'toast'; +import QuickConnectSettings from '../../../components/quickConnectSettings/quickConnectSettings'; +import globalize from '../../../scripts/globalize'; +import toast from '../../../components/toast/toast'; export default function (view) { let quickConnectSettingsInstance = null; diff --git a/src/controllers/user/subtitles/index.js b/src/controllers/user/subtitles/index.js index efa2f1bead1..deb86535df8 100644 --- a/src/controllers/user/subtitles/index.js +++ b/src/controllers/user/subtitles/index.js @@ -1,6 +1,6 @@ -import SubtitleSettings from 'subtitleSettings'; -import * as userSettings from 'userSettings'; -import autoFocuser from 'autoFocuser'; +import SubtitleSettings from '../../../components/subtitlesettings/subtitlesettings'; +import * as userSettings from '../../../scripts/settings/userSettings'; +import autoFocuser from '../../../components/autoFocuser'; /* eslint-disable indent */ diff --git a/src/controllers/wizard/finish/index.js b/src/controllers/wizard/finish/index.js index 5451d6665c5..446d01e61c1 100644 --- a/src/controllers/wizard/finish/index.js +++ b/src/controllers/wizard/finish/index.js @@ -1,4 +1,4 @@ -import loading from 'loading'; +import loading from '../../../components/loading/loading'; function onFinish() { loading.show(); diff --git a/src/controllers/wizard/remote/index.js b/src/controllers/wizard/remote/index.js index b967d668ad8..c5689e73bb0 100644 --- a/src/controllers/wizard/remote/index.js +++ b/src/controllers/wizard/remote/index.js @@ -1,7 +1,8 @@ -import loading from 'loading'; -import 'emby-checkbox'; -import 'emby-button'; -import 'emby-select'; +import loading from '../../../components/loading/loading'; +import '../../../elements/emby-checkbox/emby-checkbox'; +import '../../../elements/emby-button/emby-button'; +import '../../../elements/emby-select/emby-select'; +import Dashboard from '../../../scripts/clientUtils'; function save(page) { loading.show(); diff --git a/src/controllers/wizard/settings/index.js b/src/controllers/wizard/settings/index.js index 6e3a82cd9b9..4a88e861f0f 100644 --- a/src/controllers/wizard/settings/index.js +++ b/src/controllers/wizard/settings/index.js @@ -1,7 +1,8 @@ -import loading from 'loading'; -import 'emby-checkbox'; -import 'emby-button'; -import 'emby-select'; +import loading from '../../../components/loading/loading'; +import '../../../elements/emby-checkbox/emby-checkbox'; +import '../../../elements/emby-button/emby-button'; +import '../../../elements/emby-select/emby-select'; +import Dashboard from '../../../scripts/clientUtils'; function save(page) { loading.show(); diff --git a/src/controllers/wizard/start/index.js b/src/controllers/wizard/start/index.js index 3cd53b4ceba..3f524a870a8 100644 --- a/src/controllers/wizard/start/index.js +++ b/src/controllers/wizard/start/index.js @@ -1,7 +1,8 @@ -import $ from 'jQuery'; -import loading from 'loading'; -import 'emby-button'; -import 'emby-select'; +import 'jquery'; +import loading from '../../../components/loading/loading'; +import '../../../elements/emby-button/emby-button'; +import '../../../elements/emby-select/emby-select'; +import Dashboard from '../../../scripts/clientUtils'; function loadPage(page, config, languageOptions) { $('#selectLocalizationLanguage', page).html(languageOptions.map(function (l) { diff --git a/src/controllers/wizard/user/index.js b/src/controllers/wizard/user/index.js index ec587fec8e4..03566ac7ee3 100644 --- a/src/controllers/wizard/user/index.js +++ b/src/controllers/wizard/user/index.js @@ -1,8 +1,10 @@ -import loading from 'loading'; -import globalize from 'globalize'; -import 'dashboardcss'; -import 'emby-input'; -import 'emby-button'; +import loading from '../../../components/loading/loading'; +import globalize from '../../../scripts/globalize'; +import '../../../assets/css/dashboard.css'; +import '../../../elements/emby-input/emby-input'; +import '../../../elements/emby-button/emby-button'; +import Dashboard from '../../../scripts/clientUtils'; +import toast from '../../../components/toast/toast'; function getApiClient() { return ApiClient; @@ -36,9 +38,7 @@ function onSubmit(e) { const form = this; if (form.querySelector('#txtManualPassword').value != form.querySelector('#txtPasswordConfirm').value) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('PasswordMatchError')); - }); + toast(globalize.translate('PasswordMatchError')); } else { submit(form); } diff --git a/src/elements/emby-button/emby-button.js b/src/elements/emby-button/emby-button.js index 213bbc8e7fc..3d911c6e37f 100644 --- a/src/elements/emby-button/emby-button.js +++ b/src/elements/emby-button/emby-button.js @@ -1,10 +1,10 @@ -import dom from 'dom'; -import layoutManager from 'layoutManager'; -import shell from 'shell'; -import appRouter from 'appRouter'; -import appHost from 'apphost'; -import 'css!./emby-button'; -import 'webcomponents'; +import 'webcomponents.js/webcomponents-lite'; +import { removeEventListener, addEventListener } from '../../scripts/dom'; +import layoutManager from '../../components/layoutManager'; +import shell from '../../scripts/shell'; +import { appRouter } from '../../components/appRouter'; +import { appHost } from '../../components/apphost'; +import './emby-button.css'; const EmbyButtonPrototype = Object.create(HTMLButtonElement.prototype); const EmbyLinkButtonPrototype = Object.create(HTMLAnchorElement.prototype); @@ -41,8 +41,8 @@ EmbyButtonPrototype.createdCallback = function () { EmbyButtonPrototype.attachedCallback = function () { if (this.tagName === 'A') { - dom.removeEventListener(this, 'click', onAnchorClick, {}); - dom.addEventListener(this, 'click', onAnchorClick, {}); + removeEventListener(this, 'click', onAnchorClick, {}); + addEventListener(this, 'click', onAnchorClick, {}); if (this.getAttribute('data-autohide') === 'true') { if (appHost.supports('externallinks')) { @@ -55,7 +55,7 @@ EmbyButtonPrototype.attachedCallback = function () { }; EmbyButtonPrototype.detachedCallback = function () { - dom.removeEventListener(this, 'click', onAnchorClick, {}); + removeEventListener(this, 'click', onAnchorClick, {}); }; EmbyLinkButtonPrototype.createdCallback = EmbyButtonPrototype.createdCallback; diff --git a/src/elements/emby-button/paper-icon-button-light.js b/src/elements/emby-button/paper-icon-button-light.js index f6c754fedbf..ff817a31b47 100644 --- a/src/elements/emby-button/paper-icon-button-light.js +++ b/src/elements/emby-button/paper-icon-button-light.js @@ -1,6 +1,6 @@ -import layoutManager from 'layoutManager'; -import 'css!./emby-button'; -import 'webcomponents'; +import layoutManager from '../../components/layoutManager'; +import './emby-button.css'; +import 'webcomponents.js/webcomponents-lite'; const EmbyButtonPrototype = Object.create(HTMLButtonElement.prototype); diff --git a/src/elements/emby-checkbox/emby-checkbox.js b/src/elements/emby-checkbox/emby-checkbox.js index d44c58ed48b..0af079ec06b 100644 --- a/src/elements/emby-checkbox/emby-checkbox.js +++ b/src/elements/emby-checkbox/emby-checkbox.js @@ -1,7 +1,7 @@ -import browser from 'browser'; -import dom from 'dom'; -import 'css!./emby-checkbox'; -import 'webcomponents'; +import browser from '../../scripts/browser'; +import dom from '../../scripts/dom'; +import './emby-checkbox.css'; +import 'webcomponents.js/webcomponents-lite'; /* eslint-disable indent */ diff --git a/src/elements/emby-collapse/emby-collapse.js b/src/elements/emby-collapse/emby-collapse.js index c87e73d48fc..ca34a485549 100644 --- a/src/elements/emby-collapse/emby-collapse.js +++ b/src/elements/emby-collapse/emby-collapse.js @@ -1,6 +1,6 @@ -import 'css!./emby-collapse'; -import 'webcomponents'; -import 'emby-button'; +import './emby-collapse.css'; +import 'webcomponents.js/webcomponents-lite'; +import '../emby-button/emby-button'; /* eslint-disable indent */ diff --git a/src/elements/emby-input/emby-input.js b/src/elements/emby-input/emby-input.js index 3a71e29a6f8..60f459f32b5 100644 --- a/src/elements/emby-input/emby-input.js +++ b/src/elements/emby-input/emby-input.js @@ -1,7 +1,7 @@ -import browser from 'browser'; -import dom from 'dom'; -import 'css!./emby-input'; -import 'webcomponents'; +import browser from '../../scripts/browser'; +import dom from '../../scripts/dom'; +import './emby-input.css'; +import 'webcomponents.js/webcomponents-lite'; /* eslint-disable indent */ diff --git a/src/elements/emby-itemrefreshindicator/emby-itemrefreshindicator.js b/src/elements/emby-itemrefreshindicator/emby-itemrefreshindicator.js index 51f3fc5be98..c3a15ce12e8 100644 --- a/src/elements/emby-itemrefreshindicator/emby-itemrefreshindicator.js +++ b/src/elements/emby-itemrefreshindicator/emby-itemrefreshindicator.js @@ -1,21 +1,21 @@ -import EmbyProgressRing from 'emby-progressring'; -import dom from 'dom'; -import serverNotifications from 'serverNotifications'; -import events from 'events'; -import 'webcomponents'; +import EmbyProgressRing from '../emby-progressring/emby-progressring'; +import dom from '../../scripts/dom'; +import serverNotifications from '../../scripts/serverNotifications'; +import { Events } from 'jellyfin-apiclient'; +import 'webcomponents.js/webcomponents-lite'; /* eslint-disable indent */ function addNotificationEvent(instance, name, handler) { const localHandler = handler.bind(instance); - events.on(serverNotifications, name, localHandler); + Events.on(serverNotifications, name, localHandler); instance[name] = localHandler; } function removeNotificationEvent(instance, name) { const handler = instance[name]; if (handler) { - events.off(serverNotifications, name, handler); + Events.off(serverNotifications, name, handler); instance[name] = null; } } diff --git a/src/elements/emby-itemscontainer/emby-itemscontainer.js b/src/elements/emby-itemscontainer/emby-itemscontainer.js index 7d8f941603b..3d84ea6bedc 100644 --- a/src/elements/emby-itemscontainer/emby-itemscontainer.js +++ b/src/elements/emby-itemscontainer/emby-itemscontainer.js @@ -1,15 +1,17 @@ -import itemShortcuts from 'itemShortcuts'; -import inputManager from 'inputManager'; -import playbackManager from 'playbackManager'; -import imageLoader from 'imageLoader'; -import layoutManager from 'layoutManager'; -import browser from 'browser'; -import dom from 'dom'; -import loading from 'loading'; -import focusManager from 'focusManager'; -import serverNotifications from 'serverNotifications'; -import events from 'events'; -import 'webcomponents'; +import itemShortcuts from '../../components/shortcuts'; +import inputManager from '../../scripts/inputManager'; +import { playbackManager } from '../../components/playback/playbackmanager'; +import imageLoader from '../../components/images/imageLoader'; +import layoutManager from '../../components/layoutManager'; +import browser from '../../scripts/browser'; +import dom from '../../scripts/dom'; +import loading from '../../components/loading/loading'; +import focusManager from '../../components/focusManager'; +import serverNotifications from '../../scripts/serverNotifications'; +import { Events } from 'jellyfin-apiclient'; +import 'webcomponents.js/webcomponents-lite'; +import ServerConnections from '../../components/ServerConnections'; +import Sortable from 'sortablejs'; /* eslint-disable indent */ @@ -72,7 +74,7 @@ import 'webcomponents'; } const self = this; - import('multiSelect').then(({default: MultiSelect}) => { + import('../../components/multiSelect/multiSelect').then(({default: MultiSelect}) => { self.multiSelect = new MultiSelect({ container: self, bindOnClick: false @@ -102,7 +104,7 @@ import 'webcomponents'; } const serverId = el.getAttribute('data-serverid'); - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); loading.show(); @@ -132,23 +134,21 @@ import 'webcomponents'; } const self = this; - import('sortable').then(({default: Sortable}) => { - self.sortable = new Sortable(self, { - draggable: '.listItem', - handle: '.listViewDragHandle', - - // dragging ended - onEnd: function (evt) { - return onDrop(evt, self); - } - }); + self.sortable = new Sortable(self, { + draggable: '.listItem', + handle: '.listViewDragHandle', + + // dragging ended + onEnd: function (evt) { + return onDrop(evt, self); + } }); }; function onUserDataChanged(e, apiClient, userData) { const itemsContainer = this; - import('cardBuilder').then(({default: cardBuilder}) => { + import('../../components/cardbuilder/cardBuilder').then((cardBuilder) => { cardBuilder.onUserDataChanged(userData, itemsContainer); }); @@ -183,7 +183,7 @@ import 'webcomponents'; // This could be null, not supported by all tv providers const newTimerId = data.Id; - import('cardBuilder').then(({default: cardBuilder}) => { + import('../../components/cardbuilder/cardBuilder').then((cardBuilder) => { cardBuilder.onTimerCreated(programId, newTimerId, itemsContainer); }); } @@ -203,7 +203,7 @@ import 'webcomponents'; return; } - import('cardBuilder').then(({default: cardBuilder}) => { + import('../../components/cardbuilder/cardBuilder').then((cardBuilder) => { cardBuilder.onTimerCancelled(data.Id, itemsContainer); }); } @@ -215,7 +215,7 @@ import 'webcomponents'; return; } - import('cardBuilder').then(({default: cardBuilder}) => { + import('../../components/cardbuilder/cardBuilder').then((cardBuilder) => { cardBuilder.onSeriesTimerCancelled(data.Id, itemsContainer); }); } @@ -270,7 +270,7 @@ import 'webcomponents'; 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; } @@ -278,7 +278,7 @@ import 'webcomponents'; 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/elements/emby-playstatebutton/emby-playstatebutton.js b/src/elements/emby-playstatebutton/emby-playstatebutton.js index 8d17ddf9ffe..d177ed0df62 100644 --- a/src/elements/emby-playstatebutton/emby-playstatebutton.js +++ b/src/elements/emby-playstatebutton/emby-playstatebutton.js @@ -1,20 +1,21 @@ -import serverNotifications from 'serverNotifications'; -import events from 'events'; -import globalize from 'globalize'; -import EmbyButtonPrototype from 'emby-button'; +import serverNotifications from '../../scripts/serverNotifications'; +import { Events } from 'jellyfin-apiclient'; +import globalize from '../../scripts/globalize'; +import EmbyButtonPrototype from '../../elements/emby-button/emby-button'; +import ServerConnections from '../../components/ServerConnections'; /* eslint-disable indent */ function addNotificationEvent(instance, name, handler) { const localHandler = handler.bind(instance); - events.on(serverNotifications, name, localHandler); + Events.on(serverNotifications, name, localHandler); instance[name] = localHandler; } function removeNotificationEvent(instance, name) { const handler = instance[name]; if (handler) { - events.off(serverNotifications, name, handler); + Events.off(serverNotifications, name, handler); instance[name] = null; } } @@ -23,7 +24,7 @@ import EmbyButtonPrototype from 'emby-button'; const button = this; const id = button.getAttribute('data-id'); const serverId = button.getAttribute('data-serverid'); - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); if (!button.classList.contains('playstatebutton-played')) { apiClient.markPlayed(apiClient.getCurrentUserId(), id, new Date()); diff --git a/src/elements/emby-progressring/emby-progressring.js b/src/elements/emby-progressring/emby-progressring.js index 63b9f73f102..af463ebe4ec 100644 --- a/src/elements/emby-progressring/emby-progressring.js +++ b/src/elements/emby-progressring/emby-progressring.js @@ -1,5 +1,5 @@ -import 'css!./emby-progressring'; -import 'webcomponents'; +import './emby-progressring.css'; +import 'webcomponents.js/webcomponents-lite'; /* eslint-disable indent */ @@ -9,7 +9,7 @@ import 'webcomponents'; this.classList.add('progressring'); const instance = this; - import('text!./emby-progressring.template.html').then(({default: template}) => { + import('./emby-progressring.template.html').then(({default: template}) => { instance.innerHTML = template; if (window.MutationObserver) { diff --git a/src/elements/emby-radio/emby-radio.js b/src/elements/emby-radio/emby-radio.js index 7c468a84a6b..46cf4989d89 100644 --- a/src/elements/emby-radio/emby-radio.js +++ b/src/elements/emby-radio/emby-radio.js @@ -1,7 +1,7 @@ -import layoutManager from 'layoutManager'; -import 'css!./emby-radio'; -import 'webcomponents'; -import browser from 'browser'; +import layoutManager from '../../components/layoutManager'; +import browser from '../../scripts/browser'; +import 'webcomponents.js/webcomponents-lite'; +import './emby-radio.css'; /* eslint-disable indent */ diff --git a/src/elements/emby-ratingbutton/emby-ratingbutton.js b/src/elements/emby-ratingbutton/emby-ratingbutton.js index 865d918b450..18ab1aa9a1d 100644 --- a/src/elements/emby-ratingbutton/emby-ratingbutton.js +++ b/src/elements/emby-ratingbutton/emby-ratingbutton.js @@ -1,20 +1,21 @@ -import serverNotifications from 'serverNotifications'; -import events from 'events'; -import globalize from 'globalize'; -import EmbyButtonPrototype from 'emby-button'; +import serverNotifications from '../../scripts/serverNotifications'; +import { Events } from 'jellyfin-apiclient'; +import globalize from '../../scripts/globalize'; +import EmbyButtonPrototype from '../emby-button/emby-button'; +import ServerConnections from '../../components/ServerConnections'; /* eslint-disable indent */ function addNotificationEvent(instance, name, handler) { const localHandler = handler.bind(instance); - events.on(serverNotifications, name, localHandler); + Events.on(serverNotifications, name, localHandler); instance[name] = localHandler; } function removeNotificationEvent(instance, name) { const handler = instance[name]; if (handler) { - events.off(serverNotifications, name, handler); + Events.off(serverNotifications, name, handler); instance[name] = null; } } @@ -27,7 +28,7 @@ import EmbyButtonPrototype from 'emby-button'; const button = this; const id = button.getAttribute('data-id'); const serverId = button.getAttribute('data-serverid'); - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); let likes = this.getAttribute('data-likes'); const isFavorite = this.getAttribute('data-isfavorite') === 'true'; diff --git a/src/elements/emby-scrollbuttons/emby-scrollbuttons.js b/src/elements/emby-scrollbuttons/emby-scrollbuttons.js index f7665c06188..639cb627cd3 100644 --- a/src/elements/emby-scrollbuttons/emby-scrollbuttons.js +++ b/src/elements/emby-scrollbuttons/emby-scrollbuttons.js @@ -1,6 +1,6 @@ -import 'css!./emby-scrollbuttons'; -import 'webcomponents'; -import 'paper-icon-button-light'; +import './emby-scrollbuttons.css'; +import 'webcomponents.js/webcomponents-lite'; +import '../emby-button/paper-icon-button-light'; /* eslint-disable indent */ diff --git a/src/elements/emby-scroller/emby-scroller.js b/src/elements/emby-scroller/emby-scroller.js index d7133e317a5..396f1284e86 100644 --- a/src/elements/emby-scroller/emby-scroller.js +++ b/src/elements/emby-scroller/emby-scroller.js @@ -1,11 +1,11 @@ -import scroller from 'scroller'; -import dom from 'dom'; -import layoutManager from 'layoutManager'; -import inputManager from 'inputManager'; -import focusManager from 'focusManager'; -import browser from 'browser'; -import 'webcomponents'; -import 'css!./emby-scroller'; +import scroller from '../../libraries/scroller'; +import dom from '../../scripts/dom'; +import layoutManager from '../../components/layoutManager'; +import inputManager from '../../scripts/inputManager'; +import focusManager from '../../components/focusManager'; +import browser from '../../scripts/browser'; +import 'webcomponents.js/webcomponents-lite'; +import './emby-scroller.css'; /* eslint-disable indent */ @@ -156,7 +156,7 @@ import 'css!./emby-scroller'; }; function loadScrollButtons(scroller) { - import('emby-scrollbuttons').then(() => { + import('../emby-scrollbuttons/emby-scrollbuttons').then(() => { scroller.insertAdjacentHTML('beforebegin', '
'); }); } diff --git a/src/elements/emby-select/emby-select.js b/src/elements/emby-select/emby-select.js index 0629a74e529..4d336a63a93 100644 --- a/src/elements/emby-select/emby-select.js +++ b/src/elements/emby-select/emby-select.js @@ -1,8 +1,8 @@ -import layoutManager from 'layoutManager'; -import browser from 'browser'; -import actionsheet from 'actionsheet'; -import 'css!./emby-select'; -import 'webcomponents'; +import layoutManager from '../../components/layoutManager'; +import browser from '../../scripts/browser'; +import actionsheet from '../../components/actionSheet/actionSheet'; +import './emby-select.css'; +import 'webcomponents.js/webcomponents-lite'; /* eslint-disable indent */ diff --git a/src/elements/emby-slider/emby-slider.css b/src/elements/emby-slider/emby-slider.css index 01221b6cae4..f7503d4fd5a 100644 --- a/src/elements/emby-slider/emby-slider.css +++ b/src/elements/emby-slider/emby-slider.css @@ -157,7 +157,7 @@ } .mdl-slider-background-flex { - background: #333; + background: rgba(255, 255, 255, 0.3); height: 0.2em; margin-top: -0.1em; width: 100%; diff --git a/src/elements/emby-slider/emby-slider.js b/src/elements/emby-slider/emby-slider.js index 555394af0db..828237a63d4 100644 --- a/src/elements/emby-slider/emby-slider.js +++ b/src/elements/emby-slider/emby-slider.js @@ -1,10 +1,10 @@ -import browser from 'browser'; -import dom from 'dom'; -import layoutManager from 'layoutManager'; -import keyboardnavigation from 'keyboardnavigation'; -import 'css!./emby-slider'; -import 'webcomponents'; -import 'emby-input'; +import browser from '../../scripts/browser'; +import dom from '../../scripts/dom'; +import layoutManager from '../../components/layoutManager'; +import keyboardnavigation from '../../scripts/keyboardNavigation'; +import './emby-slider.css'; +import 'webcomponents.js/webcomponents-lite'; +import '../emby-input/emby-input'; /* eslint-disable indent */ @@ -442,7 +442,7 @@ import 'emby-input'; position = (position / runtime) * 100; } - for (const range in ranges) { + for (const range of ranges) { if (position != null) { if (position >= range.end) { continue; diff --git a/src/elements/emby-tabs/emby-tabs.js b/src/elements/emby-tabs/emby-tabs.js index 7e16e31dd4f..ebe46f91659 100644 --- a/src/elements/emby-tabs/emby-tabs.js +++ b/src/elements/emby-tabs/emby-tabs.js @@ -1,13 +1,12 @@ -import dom from 'dom'; -import scroller from 'scroller'; -import browser from 'browser'; -import focusManager from 'focusManager'; -import 'webcomponents'; -import 'css!./emby-tabs'; -import 'scrollStyles'; +import 'webcomponents.js/webcomponents-lite'; +import dom from '../../scripts/dom'; +import scroller from '../../libraries/scroller'; +import browser from '../../scripts/browser'; +import focusManager from '../../components/focusManager'; +import './emby-tabs.css'; +import '../../assets/css/scrollstyles.css'; /* eslint-disable indent */ - const EmbyTabs = Object.create(HTMLDivElement.prototype); const buttonClass = 'emby-tab-button'; const activeButtonClass = buttonClass + '-active'; diff --git a/src/elements/emby-textarea/emby-textarea.js b/src/elements/emby-textarea/emby-textarea.js index c14724346a6..18158d8703b 100644 --- a/src/elements/emby-textarea/emby-textarea.js +++ b/src/elements/emby-textarea/emby-textarea.js @@ -1,6 +1,6 @@ -import 'css!./emby-textarea'; -import 'webcomponents'; -import 'emby-input'; +import './emby-textarea.css'; +import 'webcomponents.js/webcomponents-lite'; +import '../emby-input/emby-input'; /* eslint-disable indent */ diff --git a/src/elements/emby-toggle/emby-toggle.js b/src/elements/emby-toggle/emby-toggle.js index 5e78b38dd32..7539ae398de 100644 --- a/src/elements/emby-toggle/emby-toggle.js +++ b/src/elements/emby-toggle/emby-toggle.js @@ -1,5 +1,5 @@ -import 'css!./emby-toggle'; -import 'webcomponents'; +import './emby-toggle.css'; +import 'webcomponents.js/webcomponents-lite'; /* eslint-disable indent */ diff --git a/src/libraries/navdrawer/navdrawer.js b/src/libraries/navdrawer/navdrawer.js index 6dcf6783d12..214d86a02c5 100644 --- a/src/libraries/navdrawer/navdrawer.js +++ b/src/libraries/navdrawer/navdrawer.js @@ -3,12 +3,12 @@ */ /* eslint-disable no-var */ -import browser from 'browser'; -import dom from 'dom'; -import 'css!./navdrawer'; -import 'scrollStyles'; +import browser from '../../scripts/browser'; +import dom from '../../scripts/dom'; +import './navdrawer.css'; +import '../../assets/css/scrollstyles.css'; -export default function (options) { +export function NavigationDrawer(options) { function getTouches(e) { return e.changedTouches || e.targetTouches || e.touches; } diff --git a/src/libraries/screensavermanager.css b/src/libraries/screensavermanager.css new file mode 100644 index 00000000000..b11bbddb5e2 --- /dev/null +++ b/src/libraries/screensavermanager.css @@ -0,0 +1,4 @@ +/* own "noScroll" class to avoid conflicts and the need for a scrollbar manager */ +.screensaver-noScroll { + overflow: hidden !important; +} diff --git a/src/libraries/screensavermanager.js b/src/libraries/screensavermanager.js index 68a7dda73b8..bdb049a23cf 100644 --- a/src/libraries/screensavermanager.js +++ b/src/libraries/screensavermanager.js @@ -1,8 +1,10 @@ -import events from 'events'; -import playbackManager from 'playbackManager'; -import pluginManager from 'pluginManager'; -import inputManager from 'inputManager'; -import * as userSettings from 'userSettings'; +import { Events } from 'jellyfin-apiclient'; +import { playbackManager } from '../components/playback/playbackmanager'; +import { pluginManager } from '../components/pluginManager'; +import inputManager from '../scripts/inputManager'; +import * as userSettings from '../scripts/settings/userSettings'; +import ServerConnections from '../components/ServerConnections'; +import './screensavermanager.css'; function getMinIdleTime() { // Returns the minimum amount of idle time required before the screen saver can be displayed @@ -16,7 +18,7 @@ function getFunctionalEventIdleTime() { return new Date().getTime() - lastFunctionalEvent; } -events.on(playbackManager, 'playbackstop', function (e, stopInfo) { +Events.on(playbackManager, 'playbackstop', function (e, stopInfo) { const state = stopInfo.state; if (state.NowPlayingItem && state.NowPlayingItem.MediaType == 'Video') { lastFunctionalEvent = new Date().getTime(); @@ -52,6 +54,8 @@ function ScreenSaverManager() { console.debug('Showing screensaver ' + screensaver.name); + document.body.classList.add('screensaver-noScroll'); + screensaver.show(); activeScreenSaver = screensaver; @@ -69,7 +73,9 @@ function ScreenSaverManager() { function hide() { if (activeScreenSaver) { console.debug('Hiding screensaver'); - activeScreenSaver.hide(); + activeScreenSaver.hide().then(() => { + document.body.classList.remove('screensaver-noScroll'); + }); activeScreenSaver = null; } @@ -84,7 +90,7 @@ function ScreenSaverManager() { this.show = function () { let isLoggedIn; - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); if (apiClient && apiClient.isLoggedIn()) { isLoggedIn = true; diff --git a/src/libraries/scroller.js b/src/libraries/scroller.js index de6469c743b..f2baf515725 100644 --- a/src/libraries/scroller.js +++ b/src/libraries/scroller.js @@ -2,12 +2,12 @@ * and will be replaced soon by a Vue component. */ -import browser from 'browser'; -import layoutManager from 'layoutManager'; -import dom from 'dom'; -import focusManager from 'focusManager'; -import ResizeObserver from 'ResizeObserver'; -import 'scrollStyles'; +import browser from '../scripts/browser'; +import layoutManager from '../components/layoutManager'; +import dom from '../scripts/dom'; +import focusManager from '../components/focusManager'; +import ResizeObserver from 'resize-observer-polyfill'; +import '../assets/css/scrollstyles.css'; /** * Return type of the value. diff --git a/src/plugins/backdropScreensaver/plugin.js b/src/plugins/backdropScreensaver/plugin.js index 917d8f48a39..7db319a34e8 100644 --- a/src/plugins/backdropScreensaver/plugin.js +++ b/src/plugins/backdropScreensaver/plugin.js @@ -1,4 +1,5 @@ /* eslint-disable indent */ +import ServerConnections from '../../components/ServerConnections'; class BackdropScreensaver { constructor() { @@ -20,10 +21,10 @@ class BackdropScreensaver { Limit: 200 }; - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); apiClient.getItems(apiClient.getCurrentUserId(), query).then((result) => { if (result.Items.length) { - import('slideshow').then(({default: Slideshow}) => { + import('../../components/slideshow/slideshow').then(({default: Slideshow}) => { const newSlideShow = new Slideshow({ showTitle: true, cover: true, @@ -42,6 +43,7 @@ class BackdropScreensaver { this.currentSlideshow.hide(); this.currentSlideshow = null; } + return Promise.resolve(); } } /* eslint-enable indent */ diff --git a/src/plugins/bookPlayer/plugin.js b/src/plugins/bookPlayer/plugin.js index c56777f378c..7d7a8b89beb 100644 --- a/src/plugins/bookPlayer/plugin.js +++ b/src/plugins/bookPlayer/plugin.js @@ -1,14 +1,14 @@ -import browser from 'browser'; -import loading from 'loading'; -import keyboardnavigation from 'keyboardnavigation'; -import dialogHelper from 'dialogHelper'; -import dom from 'dom'; -import events from 'events'; -import 'css!./style'; -import 'material-icons'; -import 'paper-icon-button-light'; - +import loading from '../../components/loading/loading'; +import keyboardnavigation from '../../scripts/keyboardNavigation'; +import dialogHelper from '../../components/dialogHelper/dialogHelper'; +import '../../scripts/dom'; +import { Events } from 'jellyfin-apiclient'; +import './style.css'; +import 'material-design-icons-iconfont'; +import '../../elements/emby-button/paper-icon-button-light'; +import ServerConnections from '../../components/ServerConnections'; import TableOfContents from './tableOfContents'; +import browser from '../../scripts/browser'; export class BookPlayer { constructor() { @@ -260,7 +260,7 @@ export class BookPlayer { }; const serverId = item.ServerId; - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return new Promise((resolve, reject) => { import('epubjs').then(({default: epubjs}) => { @@ -290,7 +290,7 @@ export class BookPlayer { epubElem.style.display = 'block'; rendition.on('relocated', (locations) => { this.progress = book.locations.percentageFromCfi(locations.start.cfi); - events.trigger(this, 'timeupdate'); + Events.trigger(this, 'timeupdate'); }); loading.hide(); diff --git a/src/plugins/bookPlayer/tableOfContents.js b/src/plugins/bookPlayer/tableOfContents.js index 165c1fa9ace..db498c10fd6 100644 --- a/src/plugins/bookPlayer/tableOfContents.js +++ b/src/plugins/bookPlayer/tableOfContents.js @@ -1,4 +1,4 @@ -import dialogHelper from 'dialogHelper'; +import dialogHelper from '../../components/dialogHelper/dialogHelper'; export default class TableOfContents { constructor(bookPlayer) { diff --git a/src/plugins/chromecastPlayer/chromecastHelper.js b/src/plugins/chromecastPlayer/chromecastHelper.js deleted file mode 100644 index e92fa4471b0..00000000000 --- a/src/plugins/chromecastPlayer/chromecastHelper.js +++ /dev/null @@ -1,229 +0,0 @@ -import events from 'events'; - -// LinkParser -// -// https://github.com/ravisorg/LinkParser -// -// Locate and extract almost any URL within a string. Handles protocol-less domains, IPv4 and -// IPv6, unrecognised TLDs, and more. -// -// This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. -// http://creativecommons.org/licenses/by-sa/4.0/ -(function () { - // Original URL regex from the Android android.text.util.Linkify function, found here: - // http://stackoverflow.com/a/19696443 - // - // However there were problems with it, most probably related to the fact it was - // written in 2007, and it's been highly modified. - // - // 1) I didn't like the fact that it was tied to specific TLDs, since new ones - // are being added all the time it wouldn't be reasonable to expect developer to - // be continually updating their regular expressions. - // - // 2) It didn't allow unicode characters in the domains which are now allowed in - // many languages, (including some IDN TLDs). Again these are constantly being - // added to and it doesn't seem reasonable to hard-code them. Note this ended up - // not being possible in standard JS due to the way it handles multibyte strings. - // It is possible using XRegExp, however a big performance hit results. Disabled - // for now. - // - // 3) It didn't allow for IPv6 hostnames - // IPv6 regex from http://stackoverflow.com/a/17871737 - // - // 4) It was very poorly commented - // - // 5) It wasn't as smart as it could have been about what should be part of a - // URL and what should be part of human language. - - const protocols = '(?:(?:http|https|rtsp|ftp):\\/\\/)'; - const credentials = "(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}" // username (1-64 normal or url escaped characters) - + "(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?" // followed by optional password (: + 1-25 normal or url escaped characters) - + '\\@)'; - - // IPv6 Regex http://forums.intermapper.com/viewtopic.php?t=452 - // by Dartware, LLC is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License - // http://intermapper.com/ - const ipv6 = '(' - + '(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))' - + '|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))' - + '|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))' - + '|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' - + '|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' - + '|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' - + '|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' - + '|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))' - + ')(%.+)?'; - - const ipv4 = '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.' - + '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.' - + '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.' - + '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])'; - - // This would have been a lot cleaner if JS RegExp supported conditionals... - const linkRegExpString = - - // begin match for protocol / username / password / host - '(?:' - - // ============================ - // If we have a recognized protocol at the beginning of the URL, we're - // more relaxed about what we accept, because we assume the user wants - // this to be a URL, and we're not accidentally matching human language - + protocols + '?' - - // optional username:password@ - + credentials + '?' - - // IP address (both v4 and v6) - + '(?:' - - // IPv6 - + ipv6 - - // IPv4 - + '|' + ipv4 - - + ')' - - // end match for protocol / username / password / host - + ')' - - // optional port number - + '(?:\\:\\d{1,5})?' - - // plus optional path and query params (no unicode allowed here?) - + '(?:' - + '\\/(?:' - // some characters we'll accept because it's unlikely human language - // would use them after a URL unless they were part of the url - + '(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])' - + '|(?:\\%[a-f0-9]{2})' - // some characters are much more likely to be used AFTER a url and - // were not intended to be included in the url itself. Mostly end - // of sentence type things. It's also likely that the URL would - // still work if any of these characters were missing from the end - // because we parsed it incorrectly. For these characters to be accepted - // they must be followed by another character that we're reasonably - // sure is part of the url - + "|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})))" - + ')*' - + '|\\b|\$' - + ')'; - - // regex = XRegExp(regex,'gi'); - const linkRegExp = RegExp(linkRegExpString, 'gi'); - - const protocolRegExp = RegExp('^' + protocols, 'i'); - - // if url doesn't begin with a known protocol, add http by default - function ensureProtocol(url) { - if (!url.match(protocolRegExp)) { - url = 'http://' + url; - } - return url; - } - - // look for links in the text - const LinkParser = { - parse: function (text) { - const links = []; - let match; - - // eslint-disable-next-line no-cond-assign - while (match = linkRegExp.exec(text)) { - console.debug(match); - const txt = match[0]; - const pos = match.index; - const len = txt.length; - const url = ensureProtocol(text); - links.push({ 'pos': pos, 'text': txt, 'len': len, 'url': url }); - } - - return links; - } - - }; - - window.LinkParser = LinkParser; -})(); - -let cache = {}; - -// TODO: Replace with isIP (https://www.npmjs.com/package/is-ip) -function isValidIpAddress(address) { - const links = LinkParser.parse(address); - - return links.length == 1; -} - -// TODO: Add IPv6 support. Potentially replace with isLocalhost (https://www.npmjs.com/package/is-localhost-ip) -function isLocalIpAddress(address) { - address = address.toLowerCase(); - - if (address.includes('127.0.0.1')) { - return true; - } - if (address.includes('localhost')) { - return true; - } - - return false; -} - -export function getServerAddress(apiClient) { - const serverAddress = apiClient.serverAddress(); - - if (isValidIpAddress(serverAddress) && !isLocalIpAddress(serverAddress)) { - return Promise.resolve(serverAddress); - } - - const cachedValue = getCachedValue(serverAddress); - if (cachedValue) { - return Promise.resolve(cachedValue); - } - - return apiClient.getEndpointInfo().then(function (endpoint) { - if (endpoint.IsInNetwork) { - return apiClient.getPublicSystemInfo().then(function (info) { - let localAddress = info.LocalAddress; - if (!localAddress) { - console.debug('No valid local address returned, defaulting to external one'); - localAddress = serverAddress; - } - addToCache(serverAddress, localAddress); - return localAddress; - }); - } else { - addToCache(serverAddress, serverAddress); - return serverAddress; - } - }); -} - -function clearCache() { - cache = {}; -} - -function addToCache(key, value) { - cache[key] = { - value: value, - time: new Date().getTime() - }; -} - -function getCachedValue(key) { - const obj = cache[key]; - - if (obj && (new Date().getTime() - obj.time) < 180000) { - return obj.value; - } - - return null; -} - -events.on(window.connectionManager, 'localusersignedin', clearCache); -events.on(window.connectionManager, 'localusersignedout', clearCache); - -export default { - getServerAddress: getServerAddress -}; diff --git a/src/plugins/chromecastPlayer/plugin.js b/src/plugins/chromecastPlayer/plugin.js index f61a0055af8..41341066b37 100644 --- a/src/plugins/chromecastPlayer/plugin.js +++ b/src/plugins/chromecastPlayer/plugin.js @@ -1,9 +1,11 @@ -import appSettings from 'appSettings'; -import * as userSettings from 'userSettings'; -import playbackManager from 'playbackManager'; -import globalize from 'globalize'; -import events from 'events'; -import castSenderApiLoader from 'castSenderApiLoader'; +import appSettings from '../../scripts/settings/appSettings'; +import * as userSettings from '../../scripts/settings/userSettings'; +import { playbackManager } from '../../components/playback/playbackmanager'; +import globalize from '../../scripts/globalize'; +import { Events } from 'jellyfin-apiclient'; +import castSenderApiLoader from '../../components/castSenderApi'; +import ServerConnections from '../../components/ServerConnections'; +import alert from '../../components/alert'; // Based on https://github.com/googlecast/CastVideos-chrome/blob/master/CastVideos.js @@ -167,7 +169,7 @@ class CastPlayer { alertText(globalize.translate('MessageChromecastConnectionError'), globalize.translate('HeaderError')); }, 300); } else if (message.type) { - events.trigger(this, message.type, [message.data]); + Events.trigger(this, message.type, [message.data]); } } @@ -236,7 +238,7 @@ class CastPlayer { document.addEventListener('volumeupbutton', onVolumeUpKeyDown, false); document.addEventListener('volumedownbutton', onVolumeDownKeyDown, false); - events.trigger(this, 'connect'); + Events.trigger(this, 'connect'); this.sendMessage({ options: {}, command: 'Identify' @@ -324,11 +326,11 @@ class CastPlayer { let apiClient; if (message.options && message.options.ServerId) { - apiClient = window.connectionManager.getApiClient(message.options.ServerId); + apiClient = ServerConnections.getApiClient(message.options.ServerId); } else if (message.options && message.options.items && message.options.items.length) { - apiClient = window.connectionManager.getApiClient(message.options.items[0].ServerId); + apiClient = ServerConnections.getApiClient(message.options.items[0].ServerId); } else { - apiClient = window.connectionManager.currentApiClient(); + apiClient = ServerConnections.currentApiClient(); } message = Object.assign(message, { @@ -351,14 +353,7 @@ class CastPlayer { message.subtitleBurnIn = appSettings.get('subtitleburnin') || ''; } - return new Promise(function (resolve, reject) { - import('./chromecastHelper').then(({ default: chromecastHelper }) => { - chromecastHelper.getServerAddress(apiClient).then(function (serverAddress) { - message.serverAddress = serverAddress; - player.sendMessageInternal(message).then(resolve, reject); - }, reject); - }); - }); + return player.sendMessageInternal(message); } sendMessageInternal(message) { @@ -439,11 +434,9 @@ class CastPlayer { } function alertText(text, title) { - import('alert').then(({default: alert}) => { - alert({ - text: text, - title: title - }); + alert({ + text, + title }); } @@ -495,11 +488,11 @@ function getItemsForPlayback(apiClient, query) { } function bindEventForRelay(instance, eventName) { - events.on(instance._castPlayer, eventName, function (e, data) { + Events.on(instance._castPlayer, eventName, function (e, data) { console.debug('cc: ' + eventName); const state = instance.getPlayerStateInternal(data); - events.trigger(instance, eventName, [state]); + Events.trigger(instance, eventName, [state]); }); } @@ -514,7 +507,7 @@ function initializeChromecast() { } })); - events.on(instance._castPlayer, 'connect', function (e) { + Events.on(instance._castPlayer, 'connect', function (e) { if (currentResolve) { sendConnectionResult(true); } else { @@ -526,20 +519,20 @@ function initializeChromecast() { instance.lastPlayerData = null; }); - events.on(instance._castPlayer, 'playbackstart', function (e, data) { + Events.on(instance._castPlayer, 'playbackstart', function (e, data) { console.debug('cc: playbackstart'); instance._castPlayer.initializeCastPlayer(); const state = instance.getPlayerStateInternal(data); - events.trigger(instance, 'playbackstart', [state]); + Events.trigger(instance, 'playbackstart', [state]); }); - events.on(instance._castPlayer, 'playbackstop', function (e, data) { + Events.on(instance._castPlayer, 'playbackstop', function (e, data) { console.debug('cc: playbackstop'); let state = instance.getPlayerStateInternal(data); - events.trigger(instance, 'playbackstop', [state]); + Events.trigger(instance, 'playbackstop', [state]); state = instance.lastPlayerData.PlayState || {}; const volume = state.VolumeLevel || 0.5; @@ -552,11 +545,11 @@ function initializeChromecast() { instance.lastPlayerData.PlayState.IsMuted = mute; }); - events.on(instance._castPlayer, 'playbackprogress', function (e, data) { + Events.on(instance._castPlayer, 'playbackprogress', function (e, data) { console.debug('cc: positionchange'); const state = instance.getPlayerStateInternal(data); - events.trigger(instance, 'timeupdate', [state]); + Events.trigger(instance, 'timeupdate', [state]); }); bindEventForRelay(instance, 'timeupdate'); @@ -566,11 +559,11 @@ function initializeChromecast() { bindEventForRelay(instance, 'repeatmodechange'); bindEventForRelay(instance, 'shufflequeuemodechange'); - events.on(instance._castPlayer, 'playstatechange', function (e, data) { + Events.on(instance._castPlayer, 'playstatechange', function (e, data) { console.debug('cc: playstatechange'); const state = instance.getPlayerStateInternal(data); - events.trigger(instance, 'pause', [state]); + Events.trigger(instance, 'pause', [state]); }); } @@ -664,7 +657,7 @@ class ChromecastPlayer { console.debug(JSON.stringify(data)); if (triggerStateChange) { - events.trigger(this, 'statechange', [data]); + Events.trigger(this, 'statechange', [data]); } return data; @@ -672,7 +665,7 @@ class ChromecastPlayer { playWithCommand(options, command) { if (!options.items) { - const apiClient = window.connectionManager.getApiClient(options.serverId); + const apiClient = ServerConnections.getApiClient(options.serverId); const instance = this; return apiClient.getItem(apiClient.getCurrentUserId(), options.ids[0]).then(function (item) { @@ -984,7 +977,7 @@ class ChromecastPlayer { } shuffle(item) { - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); const userId = apiClient.getCurrentUserId(); const instance = this; @@ -997,7 +990,7 @@ class ChromecastPlayer { } instantMix(item) { - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); const userId = apiClient.getCurrentUserId(); const instance = this; @@ -1035,7 +1028,7 @@ class ChromecastPlayer { } const instance = this; - const apiClient = window.connectionManager.getApiClient(options.serverId); + const apiClient = ServerConnections.getApiClient(options.serverId); return getItemsForPlayback(apiClient, { Ids: options.ids.join(',') diff --git a/src/plugins/comicsPlayer/plugin.js b/src/plugins/comicsPlayer/plugin.js index 8cdf3db5e6c..89e110cec34 100644 --- a/src/plugins/comicsPlayer/plugin.js +++ b/src/plugins/comicsPlayer/plugin.js @@ -1,8 +1,10 @@ -import loading from 'loading'; -import dialogHelper from 'dialogHelper'; -import keyboardnavigation from 'keyboardnavigation'; -import appRouter from 'appRouter'; -import * as libarchive from 'libarchive'; +// eslint-disable-next-line import/named, import/namespace +import { Archive } from 'libarchive.js'; +import loading from '../../components/loading/loading'; +import dialogHelper from '../../components/dialogHelper/dialogHelper'; +import keyboardnavigation from '../../scripts/keyboardNavigation'; +import { appRouter } from '../../components/appRouter'; +import ServerConnections from '../../components/ServerConnections'; export class ComicsPlayer { constructor() { @@ -93,9 +95,9 @@ export class ComicsPlayer { loading.show(); const serverId = item.ServerId; - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); - libarchive.Archive.init({ + Archive.init({ workerUrl: appRouter.baseUrl() + '/libraries/worker-bundle.js' }); @@ -175,7 +177,7 @@ class ArchiveSource { } const blob = await res.blob(); - this.archive = await libarchive.Archive.open(blob); + this.archive = await Archive.open(blob); this.raw = await this.archive.getFilesArray(); this.numberOfFiles = this.raw.length; await this.archive.extractFiles(); diff --git a/src/plugins/experimentalWarnings/plugin.js b/src/plugins/experimentalWarnings/plugin.js index bc301f01afd..cae6e8b67c5 100644 --- a/src/plugins/experimentalWarnings/plugin.js +++ b/src/plugins/experimentalWarnings/plugin.js @@ -1,6 +1,7 @@ -import globalize from 'globalize'; -import * as userSettings from 'userSettings'; -import appHost from 'apphost'; +import globalize from '../../scripts/globalize'; +import * as userSettings from '../../scripts/settings/userSettings'; +import { appHost } from '../../components/apphost'; +import alert from '../../components/alert'; // TODO: Replace with date-fns // https://stackoverflow.com/questions/6117814/get-week-of-year-in-javascript-like-in-php @@ -26,13 +27,8 @@ function showMessage(text, userSettingsKey, appHostFeature) { return Promise.resolve(); } - return new Promise(function (resolve, reject) { - userSettings.set(userSettingsKey, '1', false); - - import('alert').then(({default: alert}) => { - return alert(text).then(resolve, resolve); - }); - }); + userSettings.set(userSettingsKey, '1', false); + return alert(text).catch(() => { /* ignore exceptions */ }); } function showBlurayMessage() { diff --git a/src/plugins/htmlAudioPlayer/plugin.js b/src/plugins/htmlAudioPlayer/plugin.js index 6f413fac501..975e6f8a62f 100644 --- a/src/plugins/htmlAudioPlayer/plugin.js +++ b/src/plugins/htmlAudioPlayer/plugin.js @@ -1,12 +1,11 @@ -import events from 'events'; -import browser from 'browser'; -import appHost from 'apphost'; -import * as htmlMediaHelper from 'htmlMediaHelper'; +import { Events } from 'jellyfin-apiclient'; +import browser from '../../scripts/browser'; +import { appHost } from '../../components/apphost'; +import * as htmlMediaHelper from '../../components/htmlMediaHelper'; +import profileBuilder from '../../scripts/browserDeviceProfile'; function getDefaultProfile() { - return import('browserdeviceprofile').then(({ default: profileBuilder }) => { - return profileBuilder({}); - }); + return profileBuilder({}); } let fadeTimeout; @@ -51,7 +50,7 @@ function supportsFade() { } function requireHlsPlayer(callback) { - import('hlsjs').then(({ default: hls }) => { + import('hls.js').then(({ default: hls }) => { window.Hls = hls; callback(); }); @@ -68,7 +67,7 @@ function enableHlsPlayer(url, item, mediaSource, mediaType) { // issue head request to get content type return new Promise(function (resolve, reject) { - import('fetchHelper').then((fetchHelper) => { + import('../../components/fetchhelper').then((fetchHelper) => { fetchHelper.ajax({ url: url, type: 'HEAD' @@ -251,14 +250,14 @@ class HtmlAudioPlayer { // Don't trigger events after user stop if (!self._isFadingOut) { self._currentTime = time; - events.trigger(self, 'timeupdate'); + Events.trigger(self, 'timeupdate'); } } function onVolumeChange() { if (!self._isFadingOut) { htmlMediaHelper.saveVolume(this.volume); - events.trigger(self, 'volumechange'); + Events.trigger(self, 'volumechange'); } } @@ -269,19 +268,19 @@ class HtmlAudioPlayer { htmlMediaHelper.seekOnPlaybackStart(self, e.target, self._currentPlayOptions.playerStartPositionTicks); } - events.trigger(self, 'playing'); + Events.trigger(self, 'playing'); } function onPlay(e) { - events.trigger(self, 'unpause'); + Events.trigger(self, 'unpause'); } function onPause() { - events.trigger(self, 'pause'); + Events.trigger(self, 'pause'); } function onWaiting() { - events.trigger(self, 'waiting'); + Events.trigger(self, 'waiting'); } function onError() { diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js index d2b395ffc65..1b1ec39d18e 100644 --- a/src/plugins/htmlVideoPlayer/plugin.js +++ b/src/plugins/htmlVideoPlayer/plugin.js @@ -1,10 +1,10 @@ -import browser from 'browser'; -import events from 'events'; -import appHost from 'apphost'; -import loading from 'loading'; -import dom from 'dom'; -import playbackManager from 'playbackManager'; -import appRouter from 'appRouter'; +import browser from '../../scripts/browser'; +import { Events } from 'jellyfin-apiclient'; +import { appHost } from '../../components/apphost'; +import loading from '../../components/loading/loading'; +import dom from '../../scripts/dom'; +import { playbackManager } from '../../components/playback/playbackmanager'; +import { appRouter } from '../../components/appRouter'; import { bindEventsToHlsPlayer, destroyHlsPlayer, @@ -22,10 +22,12 @@ import { getSavedVolume, isValidDuration, getBufferedRanges -} from 'htmlMediaHelper'; -import itemHelper from 'itemHelper'; -import screenfull from 'screenfull'; -import globalize from 'globalize'; +} from '../../components/htmlMediaHelper'; +import itemHelper from '../../components/itemHelper'; +import Screenfull from 'screenfull'; +import globalize from '../../scripts/globalize'; +import ServerConnections from '../../components/ServerConnections'; +import profileBuilder from '../../scripts/browserDeviceProfile'; /* eslint-disable indent */ @@ -85,7 +87,7 @@ function tryRemoveElement(elem) { } function requireHlsPlayer(callback) { - import('hlsjs').then(({default: hls}) => { + import('hls.js').then(({default: hls}) => { window.Hls = hls; callback(); }); @@ -103,13 +105,6 @@ function tryRemoveElement(elem) { }); } - function hidePrePlaybackPage() { - const animatedPage = document.querySelector('.page:not(.hide)'); - animatedPage.classList.add('hide'); - // At this point, we must hide the scrollbar placeholder, so it's not being displayed while the item is being loaded - document.body.classList.remove('force-scroll'); - } - function zoomIn(elem) { return new Promise(resolve => { const duration = 240; @@ -139,9 +134,7 @@ function tryRemoveElement(elem) { } function getDefaultProfile() { - return import('browserdeviceprofile').then(({default: profileBuilder}) => { - return profileBuilder({}); - }); + return profileBuilder({}); } export class HtmlVideoPlayer { @@ -286,7 +279,7 @@ function tryRemoveElement(elem) { incrementFetchQueue() { if (this.#fetchQueue <= 0) { this.isFetching = true; - events.trigger(this, 'beginFetch'); + Events.trigger(this, 'beginFetch'); } this.#fetchQueue++; @@ -300,7 +293,7 @@ function tryRemoveElement(elem) { if (this.#fetchQueue <= 0) { this.isFetching = false; - events.trigger(this, 'endFetch'); + Events.trigger(this, 'endFetch'); } } @@ -323,7 +316,7 @@ function tryRemoveElement(elem) { console.debug(`prefetching hls playlist: ${hlsPlaylistUrl}`); - return window.connectionManager.getApiClient(item.ServerId).ajax({ + return ServerConnections.getApiClient(item.ServerId).ajax({ type: 'GET', url: hlsPlaylistUrl @@ -362,7 +355,7 @@ function tryRemoveElement(elem) { * @private */ setSrcWithFlvJs(elem, options, url) { - return import('flvjs').then(({default: flvjs}) => { + return import('flv.js').then(({default: flvjs}) => { const flvPlayer = flvjs.createPlayer({ type: 'flv', url: url @@ -678,6 +671,7 @@ function tryRemoveElement(elem) { destroyFlvPlayer(this); appRouter.setTransparency('none'); + document.body.classList.remove('hide-scroll'); const videoElement = this.#mediaElement; @@ -704,8 +698,8 @@ function tryRemoveElement(elem) { dlg.parentNode.removeChild(dlg); } - if (screenfull.isEnabled) { - screenfull.exit(); + if (Screenfull.isEnabled) { + Screenfull.exit(); } else { // iOS Safari if (document.webkitIsFullScreen && document.webkitCancelFullscreen) { @@ -754,7 +748,7 @@ function tryRemoveElement(elem) { this.updateSubtitleText(timeMs); } - events.trigger(this, 'timeupdate'); + Events.trigger(this, 'timeupdate'); }; /** @@ -767,7 +761,7 @@ function tryRemoveElement(elem) { */ const elem = e.target; saveVolume(elem.volume); - events.trigger(this, 'volumechange'); + Events.trigger(this, 'volumechange'); }; /** @@ -826,14 +820,14 @@ function tryRemoveElement(elem) { this.onStartedAndNavigatedToOsd(); } } - events.trigger(this, 'playing'); + Events.trigger(this, 'playing'); }; /** * @private */ onPlay = () => { - events.trigger(this, 'unpause'); + Events.trigger(this, 'unpause'); }; /** @@ -859,25 +853,25 @@ function tryRemoveElement(elem) { * @private */ onClick = () => { - events.trigger(this, 'click'); + Events.trigger(this, 'click'); }; /** * @private */ onDblClick = () => { - events.trigger(this, 'dblclick'); + Events.trigger(this, 'dblclick'); }; /** * @private */ onPause = () => { - events.trigger(this, 'pause'); + Events.trigger(this, 'pause'); }; onWaiting() { - events.trigger(this, 'waiting'); + Events.trigger(this, 'waiting'); } /** @@ -1036,7 +1030,7 @@ function tryRemoveElement(elem) { // embedded font url return avaliableFonts.push(i.DeliveryUrl); }); - const apiClient = window.connectionManager.getApiClient(item); + const apiClient = ServerConnections.getApiClient(item); const fallbackFontList = apiClient.getUrl('/FallbackFont/Fonts', { api_key: apiClient.accessToken() }); @@ -1064,15 +1058,15 @@ function tryRemoveElement(elem) { resizeVariation: 0.2, renderAhead: 90 }; - import('JavascriptSubtitlesOctopus').then(({default: SubtitlesOctopus}) => { - apiClient.getNamedConfiguration('encoding').then((config) => { + import('libass-wasm').then(({default: SubtitlesOctopus}) => { + apiClient.getNamedConfiguration('encoding').then(config => { if (config.EnableFallbackFont) { - apiClient.getJSON(fallbackFontList).then((fontFiles) => { - (fontFiles || []).map(function (font) { + apiClient.getJSON(fallbackFontList).then((fontFiles = []) => { + fontFiles.forEach(font => { const fontUrl = apiClient.getUrl(`/FallbackFont/Fonts/${font.Name}`, { api_key: apiClient.accessToken() }); - return avaliableFonts.push(fontUrl); + avaliableFonts.push(fontUrl); }); this.#currentSubtitlesOctopus = new SubtitlesOctopus(options); }); @@ -1134,7 +1128,7 @@ function tryRemoveElement(elem) { * @private */ setSubtitleAppearance(elem, innerElem) { - Promise.all([import('userSettings'), import('subtitleAppearanceHelper')]).then(([userSettings, subtitleAppearanceHelper]) => { + Promise.all([import('../../scripts/settings/userSettings'), import('../../components/subtitlesettings/subtitleappearancehelper')]).then(([userSettings, subtitleAppearanceHelper]) => { subtitleAppearanceHelper.applyStyles({ text: innerElem, window: elem @@ -1155,7 +1149,7 @@ function tryRemoveElement(elem) { * @private */ setCueAppearance() { - Promise.all([import('userSettings'), import('subtitleAppearanceHelper')]).then(([userSettings, subtitleAppearanceHelper]) => { + Promise.all([import('../../scripts/settings/userSettings'), import('../../components/subtitlesettings/subtitleappearancehelper')]).then(([userSettings, subtitleAppearanceHelper]) => { const elementId = `${this.id}-cuestyle`; let styleElem = document.querySelector(`#${elementId}`); @@ -1210,7 +1204,7 @@ function tryRemoveElement(elem) { // download the track json this.fetchSubtitles(track, item).then(function (data) { - import('userSettings').then((userSettings) => { + import('../../scripts/settings/userSettings').then((userSettings) => { // show in ui console.debug(`downloaded ${data.TrackEvents.length} track events`); @@ -1302,7 +1296,7 @@ function tryRemoveElement(elem) { const dlg = document.querySelector('.videoPlayerContainer'); if (!dlg) { - return import('css!./style').then(() => { + return import('./style.css').then(() => { loading.show(); const dlg = document.createElement('div'); @@ -1348,7 +1342,8 @@ function tryRemoveElement(elem) { this.#mediaElement = videoElement; if (options.fullscreen) { - hidePrePlaybackPage(); + // At this point, we must hide the scrollbar placeholder, so it's not being displayed while the item is being loaded + document.body.classList.add('hide-scroll'); } // don't animate on smart tv's, too slow @@ -1361,8 +1356,9 @@ function tryRemoveElement(elem) { } }); } else { + // we need to hide scrollbar when starting playback from page with animated background if (options.fullscreen) { - hidePrePlaybackPage(); + document.body.classList.add('hide-scroll'); } return Promise.resolve(dlg.querySelector('video')); @@ -1539,7 +1535,7 @@ function tryRemoveElement(elem) { return false; } - static isAirPlayEnabled() { + isAirPlayEnabled() { if (document.AirPlayEnabled) { return !!document.AirplayElement; } @@ -1581,7 +1577,7 @@ function tryRemoveElement(elem) { elem.style['-webkit-filter'] = `brightness(${cssValue})`; elem.style.filter = `brightness(${cssValue})`; elem.brightnessValue = val; - events.trigger(this, 'brightnesschange'); + Events.trigger(this, 'brightnesschange'); } } @@ -1741,13 +1737,13 @@ function tryRemoveElement(elem) { getSupportedAspectRatios() { return [{ - name: 'Auto', + name: globalize.translate('Auto'), id: 'auto' }, { - name: 'Cover', + name: globalize.translate('AspectRatioCover'), id: 'cover' }, { - name: 'Fill', + name: globalize.translate('AspectRatioFill'), id: 'fill' }]; } diff --git a/src/plugins/logoScreensaver/plugin.js b/src/plugins/logoScreensaver/plugin.js index 61b8f8a6d6b..5067546c7f9 100644 --- a/src/plugins/logoScreensaver/plugin.js +++ b/src/plugins/logoScreensaver/plugin.js @@ -1,5 +1,3 @@ -import pluginManager from 'pluginManager'; - export default function () { const self = this; @@ -128,7 +126,7 @@ export default function () { } self.show = function () { - import('css!' + pluginManager.mapPath(self, 'style.css')).then(() => { + import('./style.css').then(() => { let elem = document.querySelector('.logoScreenSaver'); if (!elem) { @@ -150,16 +148,21 @@ export default function () { const elem = document.querySelector('.logoScreenSaver'); if (elem) { - const onAnimationFinish = function () { - elem.parentNode.removeChild(elem); - }; - - if (elem.animate) { - const animation = fadeOut(elem, 1); - animation.onfinish = onAnimationFinish; - } else { - onAnimationFinish(); - } + return new Promise((resolve) => { + const onAnimationFinish = function () { + elem.parentNode.removeChild(elem); + resolve(); + }; + + if (elem.animate) { + const animation = fadeOut(elem, 1); + animation.onfinish = onAnimationFinish; + } else { + onAnimationFinish(); + } + }); } + + return Promise.resolve(); }; } diff --git a/src/plugins/pdfPlayer/plugin.js b/src/plugins/pdfPlayer/plugin.js index 480eb80546e..5bface5911f 100644 --- a/src/plugins/pdfPlayer/plugin.js +++ b/src/plugins/pdfPlayer/plugin.js @@ -1,12 +1,13 @@ -import loading from 'loading'; -import keyboardnavigation from 'keyboardnavigation'; -import dialogHelper from 'dialogHelper'; -import dom from 'dom'; -import appRouter from 'appRouter'; -import events from 'events'; -import 'css!./style'; -import 'material-icons'; -import 'paper-icon-button-light'; +import ServerConnections from '../../components/ServerConnections'; +import loading from '../../components/loading/loading'; +import keyboardnavigation from '../../scripts/keyboardNavigation'; +import dialogHelper from '../../components/dialogHelper/dialogHelper'; +import dom from '../../scripts/dom'; +import { appRouter } from '../../components/appRouter'; +import './style.css'; +import '../../elements/emby-button/paper-icon-button-light'; +import { Events } from 'jellyfin-apiclient'; +import { GlobalWorkerOptions, getDocument } from 'pdfjs-dist'; export class PdfPlayer { constructor() { @@ -186,31 +187,29 @@ export class PdfPlayer { }; const serverId = item.ServerId; - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return new Promise((resolve, reject) => { - import('pdfjs').then(({default: pdfjs}) => { - const downloadHref = apiClient.getItemDownloadUrl(item.Id); - - this.bindEvents(); - pdfjs.GlobalWorkerOptions.workerSrc = appRouter.baseUrl() + '/libraries/pdf.worker.js'; - - const downloadTask = pdfjs.getDocument(downloadHref); - downloadTask.promise.then(book => { - if (this.cancellationToken) return; - this.book = book; - this.loaded = true; - - const percentageTicks = options.startPositionTicks / 10000; - if (percentageTicks !== 0) { - this.loadPage(percentageTicks); - this.progress = percentageTicks; - } else { - this.loadPage(1); - } - - return resolve(); - }); + const downloadHref = apiClient.getItemDownloadUrl(item.Id); + + this.bindEvents(); + GlobalWorkerOptions.workerSrc = appRouter.baseUrl() + '/libraries/pdf.worker.js'; + + const downloadTask = getDocument(downloadHref); + downloadTask.promise.then(book => { + if (this.cancellationToken) return; + this.book = book; + this.loaded = true; + + const percentageTicks = options.startPositionTicks / 10000; + if (percentageTicks !== 0) { + this.loadPage(percentageTicks); + this.progress = percentageTicks; + } else { + this.loadPage(1); + } + + return resolve(); }); }); } @@ -266,7 +265,7 @@ export class PdfPlayer { renderPage(canvas, number) { this.book.getPage(number).then(page => { - events.trigger(this, 'timeupdate'); + Events.trigger(this, 'timeupdate'); const original = page.getViewport({ scale: 1 }); const context = canvas.getContext('2d'); diff --git a/src/plugins/photoPlayer/plugin.js b/src/plugins/photoPlayer/plugin.js index c40477778e5..2324ef8932c 100644 --- a/src/plugins/photoPlayer/plugin.js +++ b/src/plugins/photoPlayer/plugin.js @@ -1,3 +1,4 @@ +import ServerConnections from '../../components/ServerConnections'; export default class PhotoPlayer { constructor() { @@ -9,12 +10,12 @@ export default class PhotoPlayer { play(options) { return new Promise(function (resolve, reject) { - import('slideshow').then(({default: slideshow}) => { + import('../../components/slideshow/slideshow').then(({default: Slideshow}) => { const index = options.startIndex || 0; - const apiClient = window.connectionManager.currentApiClient(); + const apiClient = ServerConnections.currentApiClient(); apiClient.getCurrentUser().then(function(result) { - const newSlideShow = new slideshow({ + const newSlideShow = new Slideshow({ showTitle: false, cover: false, items: options.items, diff --git a/src/plugins/playAccessValidation/plugin.js b/src/plugins/playAccessValidation/plugin.js index c64b63332b9..6ed64342a61 100644 --- a/src/plugins/playAccessValidation/plugin.js +++ b/src/plugins/playAccessValidation/plugin.js @@ -1,9 +1,9 @@ -import globalize from 'globalize'; +import globalize from '../../scripts/globalize'; +import ServerConnections from '../../components/ServerConnections'; +import alert from '../../components/alert'; function showErrorMessage() { - return import('alert').then(({default: alert}) => { - return alert(globalize.translate('MessagePlayAccessRestricted')); - }); + return alert(globalize.translate('MessagePlayAccessRestricted')); } class PlayAccessValidation { @@ -24,7 +24,7 @@ class PlayAccessValidation { return Promise.resolve(); } - return window.connectionManager.getApiClient(serverId).getCurrentUser().then(function (user) { + return ServerConnections.getApiClient(serverId).getCurrentUser().then(function (user) { if (user.Policy.EnableMediaPlayback) { return Promise.resolve(); } diff --git a/src/plugins/sessionPlayer/plugin.js b/src/plugins/sessionPlayer/plugin.js index 619266b01f0..b68154b19c3 100644 --- a/src/plugins/sessionPlayer/plugin.js +++ b/src/plugins/sessionPlayer/plugin.js @@ -1,6 +1,7 @@ -import playbackManager from 'playbackManager'; -import events from 'events'; -import serverNotifications from 'serverNotifications'; +import { playbackManager } from '../../components/playback/playbackmanager'; +import { Events } from 'jellyfin-apiclient'; +import serverNotifications from '../../scripts/serverNotifications'; +import ServerConnections from '../../components/ServerConnections'; function getActivePlayerId() { const info = playbackManager.getPlayerInfo(); @@ -53,10 +54,10 @@ function getCurrentApiClient(instance) { const currentServerId = instance.currentServerId; if (currentServerId) { - return window.connectionManager.getApiClient(currentServerId); + return ServerConnections.getApiClient(currentServerId); } - return window.connectionManager.currentApiClient(); + return ServerConnections.currentApiClient(); } function sendCommandByName(instance, name, options) { @@ -104,7 +105,7 @@ function processUpdatedSessions(instance, sessions, apiClient) { instance.lastPlayerData = session; for (let i = 0, length = eventNames.length; i < length; i++) { - events.trigger(instance, eventNames[i], [session]); + Events.trigger(instance, eventNames[i], [session]); } } else { instance.lastPlayerData = session; @@ -186,7 +187,7 @@ class SessionPlayer { this.isLocalPlayer = false; this.id = 'remoteplayer'; - events.on(serverNotifications, 'Sessions', function (e, apiClient, data) { + Events.on(serverNotifications, 'Sessions', function (e, apiClient, data) { processUpdatedSessions(self, data, apiClient); }); } diff --git a/src/plugins/youtubePlayer/plugin.js b/src/plugins/youtubePlayer/plugin.js index eed75a81167..6362cb3cbb4 100644 --- a/src/plugins/youtubePlayer/plugin.js +++ b/src/plugins/youtubePlayer/plugin.js @@ -1,10 +1,18 @@ -import events from 'events'; -import browser from 'browser'; -import appRouter from 'appRouter'; -import loading from 'loading'; +import { Events } from 'jellyfin-apiclient'; +import browser from '../../scripts/browser'; +import { appRouter } from '../../components/appRouter'; +import loading from '../../components/loading/loading'; /* globals YT */ +const errorCodes = { + 2: 'YoutubeBadRequest', + 5: 'YoutubePlaybackError', + 100: 'YoutubeNotFound', + 101: 'YoutubeDenied', + 150: 'YoutubeDenied' +}; + function zoomIn(elem, iterations) { const keyframes = [ { transform: 'scale3d(.2, .2, .2) ', opacity: '.6', offset: 0 }, @@ -20,7 +28,7 @@ function createMediaElement(instance, options) { const dlg = document.querySelector('.youtubePlayerContainer'); if (!dlg) { - import('css!./style').then(() => { + import('./style.css').then(() => { loading.show(); const dlg = document.createElement('div'); @@ -37,6 +45,10 @@ function createMediaElement(instance, options) { document.body.insertBefore(dlg, document.body.firstChild); instance.videoDialog = dlg; + if (options.fullscreen) { + document.body.classList.add('hide-scroll'); + } + if (options.fullscreen && dlg.animate && !browser.slow) { zoomIn(dlg, 1).onfinish = function () { resolve(videoElement); @@ -46,6 +58,11 @@ function createMediaElement(instance, options) { } }); } else { + // we need to hide scrollbar when starting playback from page with animated background + if (options.fullscreen) { + document.body.classList.add('hide-scroll'); + } + resolve(dlg.querySelector('#player')); } }); @@ -80,7 +97,7 @@ function onEndedInternal(instance) { src: instance._currentSrc }; - events.trigger(instance, 'stopped', [stopInfo]); + Events.trigger(instance, 'stopped', [stopInfo]); instance._currentSrc = null; if (instance.currentYoutubePlayer) { @@ -95,7 +112,7 @@ function onPlayerReady(event) { } function onTimeUpdate(e) { - events.trigger(this, 'timeupdate'); + Events.trigger(this, 'timeupdate'); } function onPlaying(instance, playOptions, resolve) { @@ -114,68 +131,65 @@ function onPlaying(instance, playOptions, resolve) { instance.videoDialog.classList.remove('onTop'); } - import('loading').then(({default: loading}) => { - loading.hide(); - }); + loading.hide(); } } function setCurrentSrc(instance, elem, options) { return new Promise(function (resolve, reject) { - import('queryString').then(({default: queryString}) => { - instance._currentSrc = options.url; - const params = queryString.parse(options.url.split('?')[1]); - // 3. This function creates an