diff --git a/.ci/azure-pipelines-build.yml b/.ci/azure-pipelines-build.yml index 128fe54605..fb08254216 100644 --- a/.ci/azure-pipelines-build.yml +++ b/.ci/azure-pipelines-build.yml @@ -8,8 +8,6 @@ jobs: BuildConfiguration: development Production: BuildConfiguration: production - Standalone: - BuildConfiguration: standalone pool: vmImage: 'ubuntu-latest' @@ -21,15 +19,15 @@ jobs: versionSpec: '12.x' - task: Cache@2 - displayName: 'Check Cache' + displayName: 'Cache node_modules' inputs: key: 'yarn | yarn.lock' path: 'node_modules' - cacheHitVar: CACHE_RESTORED - script: 'yarn install --frozen-lockfile' displayName: 'Install Dependencies' - condition: ne(variables.CACHE_RESTORED, 'true') + env: + SKIP_PREPARE: 'true' - script: 'yarn build:development' displayName: 'Build Development' @@ -39,10 +37,6 @@ jobs: displayName: 'Build Production' condition: eq(variables['BuildConfiguration'], 'production') - - script: 'yarn build:standalone' - displayName: 'Build Standalone' - condition: eq(variables['BuildConfiguration'], 'standalone') - - script: 'test -d dist' displayName: 'Check Build' diff --git a/.ci/azure-pipelines-lint.yml b/.ci/azure-pipelines-lint.yml index 1e4bddbd04..8d9efbd73a 100644 --- a/.ci/azure-pipelines-lint.yml +++ b/.ci/azure-pipelines-lint.yml @@ -12,15 +12,15 @@ jobs: versionSpec: '12.x' - task: Cache@2 - displayName: 'Check Cache' + displayName: 'Cache node_modules' inputs: key: 'yarn | yarn.lock' path: 'node_modules' - cacheHitVar: CACHE_RESTORED - script: 'yarn install --frozen-lockfile' displayName: 'Install Dependencies' - condition: ne(variables.CACHE_RESTORED, 'true') + env: + SKIP_PREPARE: 'true' - script: 'yarn run lint --quiet' displayName: 'Run ESLint' diff --git a/.eslintrc.js b/.eslintrc.js index e5ee2dfe86..aabfd633f8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,7 +25,6 @@ module.exports = { 'eslint:recommended', // 'plugin:promise/recommended', 'plugin:import/errors', - 'plugin:import/warnings', 'plugin:eslint-comments/recommended', 'plugin:compat/recommended' ], @@ -79,16 +78,12 @@ module.exports = { // Dependency globals '$': 'readonly', 'jQuery': 'readonly', - 'requirejs': 'readonly', // Jellyfin globals 'ApiClient': 'writable', - 'AppInfo': 'writable', 'chrome': 'writable', 'DlnaProfilePage': 'writable', - 'Dashboard': 'writable', 'DashboardPage': 'writable', 'Emby': 'readonly', - 'Events': 'writable', 'getParameterByName': 'writable', 'getWindowLocationSearch': 'writable', 'Globalize': 'writable', @@ -98,8 +93,6 @@ module.exports = { 'LinkParser': 'writable', 'LiveTvHelpers': 'writable', 'MetadataEditor': 'writable', - 'pageClassOn': 'writable', - 'pageIdOn': 'writable', 'PlaylistViewer': 'writable', 'UserParentalControlPage': 'writable', 'Windows': 'readonly' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000000..f94934b447 --- /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 1bdf1cd903..9f9be018b3 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 ca42965dd9..1108ec9f21 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/debian/conffiles b/debian/conffiles new file mode 100644 index 0000000000..a4b2c557e9 --- /dev/null +++ b/debian/conffiles @@ -0,0 +1 @@ +/usr/share/jellyfin/web/config.json diff --git a/gulpfile.js b/gulpfile.js index 8b407ec2aa..7d1184dbdd 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 03f27954a7..c7da5adb11 100644 --- a/package.json +++ b/package.json @@ -10,22 +10,22 @@ "@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.0.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", - "css-loader": "^5.0.0", + "copy-webpack-plugin": "^6.0.3", + "css-loader": "^5.0.1", "cssnano": "^4.1.10", "del": "^6.0.0", - "eslint": "^7.12.1", + "eslint": "^7.13.0", "eslint-plugin-compat": "^3.5.1", "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", @@ -38,31 +38,35 @@ "gulp-mode": "^1.0.2", "gulp-postcss": "^8.0.0", "gulp-sass": "^4.0.2", - "gulp-sourcemaps": "^2.6.5", + "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.3.2", + "webpack": "^5.4.0", + "webpack-cli": "^4.0.0", + "webpack-dev-server": "^3.11.0", "webpack-merge": "^4.2.2", - "webpack-stream": "^6.1.0", + "webpack-stream": "^6.1.1", + "workbox-webpack-plugin": "^5.1.4", "worker-plugin": "^5.0.0" }, "dependencies": { "alameda": "^1.4.0", "blurhash": "^1.1.3", "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", - "core-js": "^3.6.5", + "core-js": "^3.7.0", "date-fns": "^2.16.1", "epubjs": "^0.3.85", - "pdfjs-dist": "2.5.207", "fast-text-encoding": "^1.0.3", "flv.js": "^1.5.0", "headroom.js": "^0.12.0", @@ -70,6 +74,7 @@ "howler": "^2.2.1", "intersection-observer": "^0.11.0", "jellyfin-apiclient": "^1.4.2", + "jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto", "jquery": "^3.5.1", "jstree": "^3.3.10", "libarchive.js": "^1.3.0", @@ -77,302 +82,31 @@ "material-design-icons-iconfont": "^6.1.0", "native-promise-only": "^0.8.0-a", "page": "^1.11.6", - "query-string": "^6.13.6", + "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.4.1" + "whatwg-fetch": "^3.5.0", + "workbox-core": "^5.1.4", + "workbox-precaching": "^5.1.4" }, "babel": { "presets": [ - "@babel/preset-env" + [ + "@babel/preset-env", + { + "useBuiltIns": "usage", + "corejs": 3 + } + ] ], - "overrides": [ - { - "test": [ - "src/components/accessSchedule/accessSchedule.js", - "src/components/actionSheet/actionSheet.js", - "src/components/activitylog.js", - "src/components/alert.js", - "src/components/alphaPicker/alphaPicker.js", - "src/components/appFooter/appFooter.js", - "src/components/apphost.js", - "src/components/appRouter.js", - "src/components/autoFocuser.js", - "src/components/backdrop/backdrop.js", - "src/components/cardbuilder/cardBuilder.js", - "src/components/cardbuilder/chaptercardbuilder.js", - "src/components/cardbuilder/peoplecardbuilder.js", - "src/components/channelMapper/channelMapper.js", - "src/components/collectionEditor/collectionEditor.js", - "src/components/confirm/confirm.js", - "src/components/dialog/dialog.js", - "src/components/dialogHelper/dialogHelper.js", - "src/components/directorybrowser/directorybrowser.js", - "src/components/displaySettings/displaySettings.js", - "src/components/favoriteitems.js", - "src/components/fetchhelper.js", - "src/components/filterdialog/filterdialog.js", - "src/components/filtermenu/filtermenu.js", - "src/components/focusManager.js", - "src/components/groupedcards.js", - "src/components/guide/guide.js", - "src/components/guide/guide-settings.js", - "src/components/homeScreenSettings/homeScreenSettings.js", - "src/components/homesections/homesections.js", - "src/components/htmlMediaHelper.js", - "src/components/imageOptionsEditor/imageOptionsEditor.js", - "src/components/images/imageLoader.js", - "src/components/imageDownloader/imageDownloader.js", - "src/components/imageeditor/imageeditor.js", - "src/components/imageUploader/imageUploader.js", - "src/components/indicators/indicators.js", - "src/components/itemContextMenu.js", - "src/components/itemHelper.js", - "src/components/itemidentifier/itemidentifier.js", - "src/components/itemMediaInfo/itemMediaInfo.js", - "src/components/itemsrefresher.js", - "src/components/layoutManager.js", - "src/components/lazyLoader/lazyLoaderIntersectionObserver.js", - "src/components/libraryoptionseditor/libraryoptionseditor.js", - "src/components/listview/listview.js", - "src/components/loading/loading.js", - "src/components/maintabsmanager.js", - "src/components/mediainfo/mediainfo.js", - "src/components/mediaLibraryCreator/mediaLibraryCreator.js", - "src/components/mediaLibraryEditor/mediaLibraryEditor.js", - "src/components/metadataEditor/metadataEditor.js", - "src/components/metadataEditor/personEditor.js", - "src/components/multiSelect/multiSelect.js", - "src/components/notifications/notifications.js", - "src/components/nowPlayingBar/nowPlayingBar.js", - "src/components/packageManager.js", - "src/components/playback/brightnessosd.js", - "src/components/playback/mediasession.js", - "src/components/playback/nowplayinghelper.js", - "src/components/playback/playbackorientation.js", - "src/components/playback/playbackmanager.js", - "src/components/playback/playerSelectionMenu.js", - "src/components/playback/playersettingsmenu.js", - "src/components/playback/playmethodhelper.js", - "src/components/playback/playqueuemanager.js", - "src/components/playback/remotecontrolautoplay.js", - "src/components/playback/volumeosd.js", - "src/components/playbackSettings/playbackSettings.js", - "src/components/playerstats/playerstats.js", - "src/components/playlisteditor/playlisteditor.js", - "src/components/playmenu.js", - "src/components/pluginManager.js", - "src/components/prompt/prompt.js", - "src/components/qualityOptions.js", - "src/components/quickConnectSettings/quickConnectSettings.js", - "src/components/recordingcreator/recordingbutton.js", - "src/components/recordingcreator/recordingcreator.js", - "src/components/recordingcreator/seriesrecordingeditor.js", - "src/components/recordingcreator/recordinghelper.js", - "src/components/refreshdialog/refreshdialog.js", - "src/components/recordingcreator/recordingeditor.js", - "src/components/recordingcreator/recordingfields.js", - "src/components/remotecontrol/remotecontrol.js", - "src/components/sanatizefilename.js", - "src/components/scrollManager.js", - "src/plugins/experimentalWarnings/plugin.js", - "src/plugins/sessionPlayer/plugin.js", - "src/plugins/htmlAudioPlayer/plugin.js", - "src/plugins/comicsPlayer/plugin.js", - "src/plugins/chromecastPlayer/plugin.js", - "src/components/slideshow/slideshow.js", - "src/components/sortmenu/sortmenu.js", - "src/plugins/htmlVideoPlayer/plugin.js", - "src/plugins/logoScreensaver/plugin.js", - "src/plugins/playAccessValidation/plugin.js", - "src/components/search/searchfields.js", - "src/components/search/searchresults.js", - "src/components/settingshelper.js", - "src/components/shortcuts.js", - "src/components/subtitleeditor/subtitleeditor.js", - "src/components/subtitlesync/subtitlesync.js", - "src/components/subtitlesettings/subtitleappearancehelper.js", - "src/components/subtitlesettings/subtitlesettings.js", - "src/components/syncPlay/groupSelectionMenu.js", - "src/components/syncPlay/playbackPermissionManager.js", - "src/components/syncPlay/syncPlayManager.js", - "src/components/syncPlay/timeSyncManager.js", - "src/components/themeMediaPlayer.js", - "src/components/tabbedview/tabbedview.js", - "src/components/viewManager/viewManager.js", - "src/components/tvproviders/schedulesdirect.js", - "src/components/tvproviders/xmltv.js", - "src/components/toast/toast.js", - "src/components/tunerPicker.js", - "src/components/upnextdialog/upnextdialog.js", - "src/components/userdatabuttons/userdatabuttons.js", - "src/components/viewContainer.js", - "src/components/viewSettings/viewSettings.js", - "src/components/castSenderApi.js", - "src/controllers/session/addServer/index.js", - "src/controllers/session/forgotPassword/index.js", - "src/controllers/session/resetPassword/index.js", - "src/controllers/session/login/index.js", - "src/controllers/session/selectServer/index.js", - "src/controllers/dashboard/apikeys.js", - "src/controllers/dashboard/dashboard.js", - "src/controllers/dashboard/devices/device.js", - "src/controllers/dashboard/devices/devices.js", - "src/controllers/dashboard/dlna/profile.js", - "src/controllers/dashboard/dlna/profiles.js", - "src/controllers/dashboard/dlna/settings.js", - "src/controllers/dashboard/encodingsettings.js", - "src/controllers/dashboard/general.js", - "src/controllers/dashboard/librarydisplay.js", - "src/controllers/dashboard/logs.js", - "src/controllers/music/musicalbums.js", - "src/controllers/music/musicartists.js", - "src/controllers/music/musicgenres.js", - "src/controllers/music/musicplaylists.js", - "src/controllers/music/musicrecommended.js", - "src/controllers/music/songs.js", - "src/controllers/dashboard/library.js", - "src/controllers/dashboard/metadataImages.js", - "src/controllers/dashboard/metadatanfo.js", - "src/controllers/dashboard/networking.js", - "src/controllers/dashboard/notifications/notification/index.js", - "src/controllers/dashboard/notifications/notifications/index.js", - "src/controllers/dashboard/playback.js", - "src/controllers/dashboard/plugins/add/index.js", - "src/controllers/dashboard/plugins/installed/index.js", - "src/controllers/dashboard/plugins/available/index.js", - "src/controllers/dashboard/plugins/repositories/index.js", - "src/controllers/dashboard/quickConnect.js", - "src/controllers/dashboard/scheduledtasks/scheduledtask.js", - "src/controllers/dashboard/scheduledtasks/scheduledtasks.js", - "src/controllers/dashboard/serveractivity.js", - "src/controllers/dashboard/streaming.js", - "src/controllers/dashboard/users/useredit.js", - "src/controllers/dashboard/users/userlibraryaccess.js", - "src/controllers/dashboard/users/usernew.js", - "src/controllers/dashboard/users/userparentalcontrol.js", - "src/controllers/dashboard/users/userpasswordpage.js", - "src/controllers/dashboard/users/userprofilespage.js", - "src/controllers/home.js", - "src/controllers/list.js", - "src/controllers/edititemmetadata.js", - "src/controllers/favorites.js", - "src/controllers/hometab.js", - "src/controllers/movies/moviecollections.js", - "src/controllers/movies/moviegenres.js", - "src/controllers/movies/movies.js", - "src/controllers/movies/moviesrecommended.js", - "src/controllers/movies/movietrailers.js", - "src/controllers/playback/nowplaying.js", - "src/controllers/playback/videoosd.js", - "src/controllers/itemDetails/index.js", - "src/controllers/playback/queue/index.js", - "src/controllers/playback/video/index.js", - "src/controllers/searchpage.js", - "src/controllers/livetv/livetvguide.js", - "src/controllers/livetvtuner.js", - "src/controllers/livetv/livetvsuggested.js", - "src/controllers/livetvstatus.js", - "src/controllers/livetvguideprovider.js", - "src/controllers/livetvsettings.js", - "src/controllers/livetv/livetvrecordings.js", - "src/controllers/livetv/livetvschedule.js", - "src/controllers/livetv/livetvseriestimers.js", - "src/controllers/livetv/livetvchannels.js", - "src/controllers/shows/episodes.js", - "src/controllers/shows/tvgenres.js", - "src/controllers/shows/tvlatest.js", - "src/controllers/shows/tvrecommended.js", - "src/controllers/shows/tvshows.js", - "src/controllers/shows/tvstudios.js", - "src/controllers/shows/tvupcoming.js", - "src/controllers/user/display/index.js", - "src/controllers/user/home/index.js", - "src/controllers/user/menu/index.js", - "src/controllers/user/playback/index.js", - "src/controllers/user/profile/index.js", - "src/controllers/user/quickConnect/index.js", - "src/controllers/user/subtitles/index.js", - "src/controllers/wizard/finish/index.js", - "src/controllers/wizard/remote/index.js", - "src/controllers/wizard/settings/index.js", - "src/controllers/wizard/start/index.js", - "src/controllers/wizard/user/index.js", - "src/elements/emby-button/emby-button.js", - "src/elements/emby-button/paper-icon-button-light.js", - "src/elements/emby-checkbox/emby-checkbox.js", - "src/elements/emby-collapse/emby-collapse.js", - "src/elements/emby-input/emby-input.js", - "src/elements/emby-itemrefreshindicator/emby-itemrefreshindicator.js", - "src/elements/emby-itemscontainer/emby-itemscontainer.js", - "src/elements/emby-playstatebutton/emby-playstatebutton.js", - "src/elements/emby-programcell/emby-programcell.js", - "src/elements/emby-progressbar/emby-progressbar.js", - "src/elements/emby-progressring/emby-progressring.js", - "src/elements/emby-radio/emby-radio.js", - "src/elements/emby-ratingbutton/emby-ratingbutton.js", - "src/elements/emby-scrollbuttons/emby-scrollbuttons.js", - "src/elements/emby-scroller/emby-scroller.js", - "src/elements/emby-select/emby-select.js", - "src/elements/emby-slider/emby-slider.js", - "src/elements/emby-tabs/emby-tabs.js", - "src/elements/emby-textarea/emby-textarea.js", - "src/elements/emby-toggle/emby-toggle.js", - "src/libraries/screensavermanager.js", - "src/libraries/navdrawer/navdrawer.js", - "src/libraries/scroller.js", - "src/plugins/backdropScreensaver/plugin.js", - "src/plugins/bookPlayer/plugin.js", - "src/plugins/pdfPlayer/plugin.js", - "src/plugins/bookPlayer/tableOfContents.js", - "src/plugins/chromecastPlayer/chromecastHelper.js", - "src/plugins/photoPlayer/plugin.js", - "src/plugins/youtubePlayer/plugin.js", - "src/scripts/alphanumericshortcuts.js", - "src/scripts/autoBackdrops.js", - "src/scripts/autocast.js", - "src/scripts/browser.js", - "src/scripts/clientUtils.js", - "src/scripts/datetime.js", - "src/scripts/deleteHelper.js", - "src/scripts/dfnshelper.js", - "src/scripts/dom.js", - "src/scripts/editorsidebar.js", - "src/scripts/fileDownloader.js", - "src/scripts/filesystem.js", - "src/scripts/globalize.js", - "src/scripts/imagehelper.js", - "src/scripts/itembynamedetailpage.js", - "src/scripts/inputManager.js", - "src/scripts/autoThemes.js", - "src/scripts/themeManager.js", - "src/scripts/keyboardNavigation.js", - "src/scripts/libraryMenu.js", - "src/scripts/libraryBrowser.js", - "src/scripts/livetvcomponents.js", - "src/scripts/mouseManager.js", - "src/scripts/multiDownload.js", - "src/scripts/playlists.js", - "src/scripts/scrollHelper.js", - "src/scripts/serverNotifications.js", - "src/scripts/routes.js", - "src/scripts/settings/appSettings.js", - "src/scripts/settings/userSettings.js", - "src/scripts/settings/webSettings.js", - "src/scripts/shell.js", - "src/scripts/taskbutton.js", - "src/scripts/themeLoader.js", - "src/scripts/touchHelper.js" - ], - "plugins": [ - "@babel/plugin-transform-modules-amd", - "@babel/plugin-proposal-class-properties", - "@babel/plugin-proposal-private-methods" - ] - } + "plugins": [ + "@babel/plugin-proposal-class-properties", + "@babel/plugin-proposal-private-methods" ] }, "browserslist": [ @@ -393,11 +127,10 @@ ], "scripts": { "start": "yarn serve", - "serve": "gulp serve --development", - "prepare": "gulp --production", - "build:development": "gulp --development", - "build:production": "gulp --production", - "build:standalone": "gulp standalone --development", + "serve": "webpack serve --config webpack.dev.js", + "prepare": "./scripts/prepare.sh", + "build:development": "webpack --config webpack.dev.js", + "build:production": "webpack --config webpack.prod.js", "lint": "eslint \".\"", "stylelint": "stylelint \"src/**/*.css\"" } diff --git a/scripts/prepare.sh b/scripts/prepare.sh new file mode 100755 index 0000000000..bde12b36a5 --- /dev/null +++ b/scripts/prepare.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +if [ -z "${SKIP_PREPARE}" ]; then + webpack --config webpack.prod.js +fi diff --git a/src/bundle.js b/src/bundle.js deleted file mode 100644 index 2693ede52b..0000000000 --- a/src/bundle.js +++ /dev/null @@ -1,182 +0,0 @@ -/** - * require.js module definitions bundled by webpack - */ -// Use define from require.js not webpack's define -const _define = window.define; - -// fetch -const fetch = require('whatwg-fetch'); -_define('fetch', function() { - return fetch; -}); - -// Blurhash -const blurhash = require('blurhash'); -_define('blurhash', function() { - return blurhash; -}); - -// query-string -const query = require('query-string'); -_define('queryString', function() { - return query; -}); - -// flvjs -const flvjs = require('flv.js/dist/flv').default; -_define('flvjs', function() { - return flvjs; -}); - -// jstree -const jstree = require('jstree'); -require('jstree/dist/themes/default/style.css'); -_define('jstree', function() { - return jstree; -}); - -// jquery -const jquery = require('jquery'); -_define('jQuery', function() { - return jquery; -}); - -// hlsjs -const hlsjs = require('hls.js'); -_define('hlsjs', function() { - return hlsjs; -}); - -// howler -const howler = require('howler'); -_define('howler', function() { - return howler; -}); - -// resize-observer-polyfill -const resize = require('resize-observer-polyfill').default; -_define('resize-observer-polyfill', function() { - return resize; -}); - -// swiper -const swiper = require('swiper/swiper-bundle'); -require('swiper/swiper-bundle.css'); -_define('swiper', function() { - return swiper; -}); - -// sortable -const sortable = require('sortablejs').default; -_define('sortable', function() { - return sortable; -}); - -// webcomponents -const webcomponents = require('webcomponents.js/webcomponents-lite'); -_define('webcomponents', function() { - return webcomponents; -}); - -// libass-wasm -const libassWasm = require('libass-wasm'); -_define('JavascriptSubtitlesOctopus', function() { - return libassWasm; -}); - -// material-icons -const materialIcons = require('material-design-icons-iconfont/dist/material-design-icons.css'); -_define('material-icons', function() { - return materialIcons; -}); - -const epubjs = require('epubjs'); -_define('epubjs', function () { - return epubjs; -}); - -const pdfjs = require('pdfjs-dist/build/pdf'); -_define('pdfjs', function () { - return pdfjs; -}); - -// page.js -const page = require('page'); -_define('page', function() { - return page; -}); - -// core-js -const polyfill = require('@babel/polyfill/dist/polyfill'); -_define('polyfill', function () { - return polyfill; -}); - -// domtokenlist-shim -const classlist = require('classlist.js'); -_define('classlist-polyfill', function () { - return classlist; -}); - -// Date-FNS -const dateFns = require('date-fns'); -_define('date-fns', function () { - return dateFns; -}); - -const dateFnsLocale = require('date-fns/locale'); -_define('date-fns/locale', function () { - return dateFnsLocale; -}); - -const fast_text_encoding = require('fast-text-encoding'); -_define('fast-text-encoding', function () { - return fast_text_encoding; -}); - -// intersection-observer -const intersection_observer = require('intersection-observer'); -_define('intersection-observer', function () { - return intersection_observer; -}); - -// screenfull -const screenfull = require('screenfull'); -_define('screenfull', function () { - return screenfull; -}); - -// headroom.js -const headroom = require('headroom.js/dist/headroom'); -_define('headroom', function () { - return headroom; -}); - -// apiclient -const apiclient = require('jellyfin-apiclient'); - -_define('apiclient', function () { - return apiclient.ApiClient; -}); - -_define('events', function () { - return apiclient.Events; -}); - -_define('credentialprovider', function () { - return apiclient.Credentials; -}); - -_define('connectionManagerFactory', function () { - return apiclient.ConnectionManager; -}); - -_define('appStorage', function () { - return apiclient.AppStorage; -}); - -// libarchive.js -const libarchive = require('libarchive.js'); -_define('libarchive', function () { - return libarchive; -}); diff --git a/src/components/ServerConnections.js b/src/components/ServerConnections.js new file mode 100644 index 0000000000..0242e549cd --- /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 b513766d0b..9e0e3d5cf9 100644 --- a/src/components/accessSchedule/accessSchedule.js +++ b/src/components/accessSchedule/accessSchedule.js @@ -1,3 +1,4 @@ + /* eslint-disable indent */ /** @@ -5,12 +6,12 @@ * @module components/accessSchedule/accessSchedule */ -import dialogHelper from 'dialogHelper'; -import datetime from 'datetime'; -import globalize from 'globalize'; -import 'emby-select'; -import 'paper-icon-button-light'; -import 'formDialogStyle'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import datetime from '../../scripts/datetime'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../formdialog.css'; function getDisplayTime(hours) { let minutes = 0; @@ -59,7 +60,7 @@ import 'formDialogStyle'; export function show(options) { return new Promise((resolve, reject) => { - import('text!./accessSchedule.template.html').then(({default: template}) => { + import('./accessSchedule.template.html').then(({default: template}) => { const dlg = dialogHelper.createDialog({ removeOnClose: true, size: 'small' diff --git a/src/components/actionSheet/actionSheet.js b/src/components/actionSheet/actionSheet.js index be84cf0a06..85df1b2c60 100644 --- a/src/components/actionSheet/actionSheet.js +++ b/src/components/actionSheet/actionSheet.js @@ -1,12 +1,12 @@ -import dialogHelper from 'dialogHelper'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import dom from 'dom'; -import 'emby-button'; -import 'css!./actionSheet'; -import 'material-icons'; -import 'scrollStyles'; -import 'listViewStyle'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import dom from '../../scripts/dom'; +import '../../elements/emby-button/emby-button'; +import './actionSheet.css'; +import 'material-design-icons-iconfont'; +import '../../assets/css/scrollstyles.css'; +import '../../components/listview/listview.css'; function getOffsets(elems) { const results = []; @@ -71,7 +71,7 @@ function getPosition(options, dlg) { } function centerFocus(elem, horiz, on) { - import('scrollHelper').then(({default: scrollHelper}) => { + import('../../scripts/scrollHelper').then((scrollHelper) => { const fn = on ? 'on' : 'off'; scrollHelper.centerFocus[fn](elem, horiz); }); diff --git a/src/components/activitylog.js b/src/components/activitylog.js index 3878d03355..78967b3a34 100644 --- a/src/components/activitylog.js +++ b/src/components/activitylog.js @@ -1,11 +1,13 @@ -import events from 'events'; -import globalize from 'globalize'; -import dom from 'dom'; +import { Events } from 'jellyfin-apiclient'; +import globalize from '../scripts/globalize'; +import dom from '../scripts/dom'; import * as datefns from 'date-fns'; -import dfnshelper from 'dfnshelper'; -import serverNotifications from 'serverNotifications'; -import 'emby-button'; -import 'listViewStyle'; +import dfnshelper from '../scripts/dfnshelper'; +import serverNotifications from '../scripts/serverNotifications'; +import '../elements/emby-button/emby-button'; +import './listview/listview.css'; +import ServerConnections from './ServerConnections'; +import alert from './alert'; /* eslint-disable indent */ @@ -127,10 +129,8 @@ import 'listViewStyle'; } function showItemOverview(item) { - import('alert').then(({default: alert}) => { - alert({ - text: item.Overview - }); + alert({ + text: item.Overview }); } @@ -140,11 +140,11 @@ class ActivityLog { const element = options.element; element.classList.add('activityLogListWidget'); element.addEventListener('click', onListClick.bind(this)); - const apiClient = window.connectionManager.getApiClient(options.serverId); + const apiClient = ServerConnections.getApiClient(options.serverId); reloadData(this, element, apiClient); const onUpdate = onActivityLogUpdate.bind(this); this.updateFn = onUpdate; - events.on(serverNotifications, 'ActivityLogEntry', onUpdate); + Events.on(serverNotifications, 'ActivityLogEntry', onUpdate); apiClient.sendMessage('ActivityLogEntryStart', '0,1500'); } destroy() { @@ -152,13 +152,13 @@ class ActivityLog { if (options) { options.element.classList.remove('activityLogListWidget'); - window.connectionManager.getApiClient(options.serverId).sendMessage('ActivityLogEntryStop', '0,1500'); + ServerConnections.getApiClient(options.serverId).sendMessage('ActivityLogEntryStop', '0,1500'); } const onUpdate = this.updateFn; if (onUpdate) { - events.off(serverNotifications, 'ActivityLogEntry', onUpdate); + Events.off(serverNotifications, 'ActivityLogEntry', onUpdate); } this.items = null; diff --git a/src/components/alert.js b/src/components/alert.js index 1420c7f428..2938cb7c70 100644 --- a/src/components/alert.js +++ b/src/components/alert.js @@ -1,6 +1,7 @@ -import browser from 'browser'; -import dialog from 'dialog'; -import globalize from 'globalize'; + +import browser from '../scripts/browser'; +import dialog from './dialog/dialog'; +import globalize from '../scripts/globalize'; /* eslint-disable indent */ diff --git a/src/components/alphaPicker/alphaPicker.js b/src/components/alphaPicker/alphaPicker.js index 95b5881677..9caa1b015c 100644 --- a/src/components/alphaPicker/alphaPicker.js +++ b/src/components/alphaPicker/alphaPicker.js @@ -5,12 +5,12 @@ * @module components/alphaPicker/alphaPicker */ -import focusManager from 'focusManager'; -import layoutManager from 'layoutManager'; -import dom from 'dom'; -import 'css!./style.css'; -import 'paper-icon-button-light'; -import 'material-icons'; +import focusManager from '../focusManager'; +import layoutManager from '../layoutManager'; +import dom from '../../scripts/dom'; +import './style.css'; +import '../../elements/emby-button/paper-icon-button-light'; +import 'material-design-icons-iconfont'; const selectedButtonClass = 'alphaPickerButton-selected'; diff --git a/src/components/alphaPicker/style.css b/src/components/alphaPicker/style.css index 4e94c0f754..8feb3e50e0 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 260aea8280..3b65824490 100644 --- a/src/components/appFooter/appFooter.js +++ b/src/components/appFooter/appFooter.js @@ -1,4 +1,4 @@ -import 'css!./appFooter'; +import './appFooter.css'; function render(options) { const elem = document.createElement('div'); @@ -33,4 +33,4 @@ class appFooter { } } -export default appFooter; +export default new appFooter({}); diff --git a/src/components/appRouter.js b/src/components/appRouter.js index a6bb6da618..03000ebaf6 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 df2f7c2d2c..8e8cb15b32 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -1,9 +1,11 @@ -import appSettings from 'appSettings'; -import browser from 'browser'; -import events from 'events'; -import * as htmlMediaHelper from 'htmlMediaHelper'; -import * as webSettings from 'webSettings'; -import globalize from 'globalize'; + +import appSettings from '../scripts/settings/appSettings'; +import browser from '../scripts/browser'; +import { Events } from 'jellyfin-apiclient'; +import * as htmlMediaHelper from '../components/htmlMediaHelper'; +import * as webSettings from '../scripts/settings/webSettings'; +import globalize from '../scripts/globalize'; +import profileBuilder from '../scripts/browserDeviceProfile'; function getBaseProfileOptions(item) { const disableHlsVideoAudioCodecs = []; @@ -26,19 +28,16 @@ function getBaseProfileOptions(item) { function getDeviceProfile(item, options = {}) { return new Promise(function (resolve) { - import('browserdeviceprofile').then(({default: profileBuilder}) => { - let profile; + let profile; - if (window.NativeShell) { - profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); - } else { - const builderOpts = getBaseProfileOptions(item); - builderOpts.enableSsaRender = (item && !options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats'); - profile = profileBuilder(builderOpts); - } + if (window.NativeShell) { + profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); + } else { + const builderOpts = getBaseProfileOptions(item); + profile = profileBuilder(builderOpts); + } - resolve(profile); - }); + resolve(profile); }); } @@ -57,60 +56,62 @@ function generateDeviceId() { if (keys.push(navigator.userAgent), keys.push(new Date().getTime()), window.btoa) { const result = replaceAll(btoa(keys.join('|')), '=', '1'); - return Promise.resolve(result); + return result; } - return Promise.resolve(new Date().getTime()); + return new Date().getTime(); } function getDeviceId() { - const key = '_deviceId2'; - const deviceId = appSettings.get(key); + if (!deviceId) { + const key = '_deviceId2'; - if (deviceId) { - return Promise.resolve(deviceId); + deviceId = appSettings.get(key); + + if (!deviceId) { + deviceId = generateDeviceId(); + appSettings.set(key, deviceId); + } } - return generateDeviceId().then(function (deviceId) { - appSettings.set(key, deviceId); - return deviceId; - }); + return deviceId; } function getDeviceName() { - let deviceName; - if (browser.tizen) { - deviceName = 'Samsung Smart TV'; - } else if (browser.web0s) { - deviceName = 'LG Smart TV'; - } else if (browser.operaTv) { - deviceName = 'Opera TV'; - } else if (browser.xboxOne) { - deviceName = 'Xbox One'; - } else if (browser.ps4) { - deviceName = 'Sony PS4'; - } else if (browser.chrome) { - deviceName = 'Chrome'; - } else if (browser.edgeChromium) { - deviceName = 'Edge Chromium'; - } else if (browser.edge) { - deviceName = 'Edge'; - } else if (browser.firefox) { - deviceName = 'Firefox'; - } else if (browser.opera) { - deviceName = 'Opera'; - } else if (browser.safari) { - deviceName = 'Safari'; - } else { - deviceName = 'Web Browser'; - } + if (!deviceName) { + if (browser.tizen) { + deviceName = 'Samsung Smart TV'; + } else if (browser.web0s) { + deviceName = 'LG Smart TV'; + } else if (browser.operaTv) { + deviceName = 'Opera TV'; + } else if (browser.xboxOne) { + deviceName = 'Xbox One'; + } else if (browser.ps4) { + deviceName = 'Sony PS4'; + } else if (browser.chrome) { + deviceName = 'Chrome'; + } else if (browser.edgeChromium) { + deviceName = 'Edge Chromium'; + } else if (browser.edge) { + deviceName = 'Edge'; + } else if (browser.firefox) { + deviceName = 'Firefox'; + } else if (browser.opera) { + deviceName = 'Opera'; + } else if (browser.safari) { + deviceName = 'Safari'; + } else { + deviceName = 'Web Browser'; + } - if (browser.ipad) { - deviceName += ' iPad'; - } else if (browser.iphone) { - deviceName += ' iPhone'; - } else if (browser.android) { - deviceName += ' Android'; + if (browser.ipad) { + deviceName += ' iPad'; + } else if (browser.iphone) { + deviceName += ' iPhone'; + } else if (browser.android) { + deviceName += ' Android'; + } } return deviceName; @@ -172,7 +173,7 @@ function supportsCue() { function onAppVisible() { if (isHidden) { isHidden = false; - events.trigger(appHost, 'resume'); + Events.trigger(appHost, 'resume'); } } @@ -296,7 +297,7 @@ function askForExit() { return; } - import('actionsheet').then(({default: actionsheet}) => { + import('../components/actionSheet/actionSheet').then((actionsheet) => { exitPromise = actionsheet.show({ title: globalize.translate('MessageConfirmAppExit'), items: [ @@ -318,7 +319,7 @@ let deviceName; const appName = 'Jellyfin Web'; const appVersion = '10.7.0'; -const appHost = { +export const appHost = { getWindowState: function () { return document.windowState || 'Normal'; }, @@ -353,16 +354,16 @@ const appHost = { return window.NativeShell.AppHost.init(); } - deviceName = getDeviceName(); - getDeviceId().then(function (id) { - deviceId = id; - }); + return { + deviceId: getDeviceId(), + deviceName: getDeviceName() + }; }, deviceName: function () { - return window.NativeShell ? window.NativeShell.AppHost.deviceName() : deviceName; + return window.NativeShell ? window.NativeShell.AppHost.deviceName() : getDeviceName(); }, deviceId: function () { - return window.NativeShell ? window.NativeShell.AppHost.deviceId() : deviceId; + return window.NativeShell ? window.NativeShell.AppHost.deviceId() : getDeviceId(); }, appName: function () { return window.NativeShell ? window.NativeShell.AppHost.appName() : appName; @@ -407,4 +408,5 @@ if (window.addEventListener) { window.addEventListener('blur', onAppHidden); } -export default appHost; +// load app host on module load +appHost.init(); diff --git a/src/components/autoFocuser.js b/src/components/autoFocuser.js index 0a10cabd2f..aa88e0c294 100644 --- a/src/components/autoFocuser.js +++ b/src/components/autoFocuser.js @@ -5,8 +5,8 @@ * @module components/autoFocuser */ -import focusManager from 'focusManager'; -import layoutManager from 'layoutManager'; +import focusManager from './focusManager'; +import layoutManager from './layoutManager'; /** * Previously selected element. diff --git a/src/components/backdrop/backdrop.js b/src/components/backdrop/backdrop.js index 83888b81b9..a2fe54de19 100644 --- a/src/components/backdrop/backdrop.js +++ b/src/components/backdrop/backdrop.js @@ -1,8 +1,9 @@ -import browser from 'browser'; -import playbackManager from 'playbackManager'; -import dom from 'dom'; -import * as userSettings from 'userSettings'; -import 'css!./backdrop'; +import browser from '../../scripts/browser'; +import { playbackManager } from '../playback/playbackmanager'; +import dom from '../../scripts/dom'; +import * as userSettings from '../../scripts/settings/userSettings'; +import './backdrop.css'; +import ServerConnections from '../ServerConnections'; /* eslint-disable indent */ @@ -176,7 +177,7 @@ import 'css!./backdrop'; function getItemImageUrls(item, imageOptions) { imageOptions = imageOptions || {}; - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); if (item.BackdropImageTags && item.BackdropImageTags.length > 0) { return item.BackdropImageTags.map((imgTag, index) => { return apiClient.getScaledImageUrl(item.BackdropItemId || item.Id, Object.assign(imageOptions, { diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index 63b2e26adb..4095bea48f 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -5,21 +5,22 @@ * @module components/cardBuilder/cardBuilder */ -import datetime from 'datetime'; -import imageLoader from 'imageLoader'; -import itemHelper from 'itemHelper'; -import focusManager from 'focusManager'; -import indicators from 'indicators'; -import globalize from 'globalize'; -import layoutManager from 'layoutManager'; -import dom from 'dom'; -import browser from 'browser'; -import playbackManager from 'playbackManager'; -import itemShortcuts from 'itemShortcuts'; -import imageHelper from 'scripts/imagehelper'; -import 'css!./card'; -import 'paper-icon-button-light'; -import 'programStyles'; +import datetime from '../../scripts/datetime'; +import imageLoader from '../images/imageLoader'; +import itemHelper from '../itemHelper'; +import focusManager from '../focusManager'; +import indicators from '../indicators/indicators'; +import globalize from '../../scripts/globalize'; +import layoutManager from '../layoutManager'; +import dom from '../../scripts/dom'; +import browser from '../../scripts/browser'; +import { playbackManager } from '../playback/playbackmanager'; +import itemShortcuts from '../shortcuts'; +import imageHelper from '../../scripts/imagehelper'; +import './card.css'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../guide/programs.css'; +import ServerConnections from '../ServerConnections'; const enableFocusTransform = !browser.slow && !browser.edge; @@ -370,7 +371,7 @@ import 'programStyles'; if (serverId !== lastServerId) { lastServerId = serverId; - apiClient = window.connectionManager.getApiClient(lastServerId); + apiClient = ServerConnections.getApiClient(lastServerId); } if (options.indexBy) { @@ -1121,7 +1122,7 @@ import 'programStyles'; if (!refreshIndicatorLoaded) { refreshIndicatorLoaded = true; /* eslint-disable-next-line @babel/no-unused-expressions */ - import('emby-itemrefreshindicator'); + import('../../elements/emby-itemrefreshindicator/emby-itemrefreshindicator'); } } @@ -1453,7 +1454,7 @@ import 'programStyles'; if (itemHelper.canMarkPlayed(item)) { /* eslint-disable-next-line @babel/no-unused-expressions */ - import('emby-playstatebutton'); + import('../../elements/emby-playstatebutton/emby-playstatebutton'); html += ''; } @@ -1461,7 +1462,7 @@ import 'programStyles'; const likes = userData.Likes == null ? '' : userData.Likes; /* eslint-disable-next-line @babel/no-unused-expressions */ - import('emby-ratingbutton'); + import('../../elements/emby-ratingbutton/emby-ratingbutton'); html += ''; } diff --git a/src/components/cardbuilder/chaptercardbuilder.js b/src/components/cardbuilder/chaptercardbuilder.js index 35ae2b0cdd..e364056e14 100644 --- a/src/components/cardbuilder/chaptercardbuilder.js +++ b/src/components/cardbuilder/chaptercardbuilder.js @@ -5,10 +5,11 @@ * @module components/cardBuilder/chaptercardbuilder */ -import datetime from 'datetime'; -import imageLoader from 'imageLoader'; -import layoutManager from 'layoutManager'; -import browser from 'browser'; +import datetime from '../../scripts/datetime'; +import imageLoader from '../images/imageLoader'; +import layoutManager from '../layoutManager'; +import browser from '../../scripts/browser'; +import ServerConnections from '../ServerConnections'; const enableFocusTransform = !browser.slow && !browser.edge; @@ -47,7 +48,7 @@ import browser from 'browser'; let html = ''; let itemsInRow = 0; - const apiClient = window.connectionManager.getApiClient(item.ServerId); + const apiClient = ServerConnections.getApiClient(item.ServerId); for (let i = 0, length = chapters.length; i < length; i++) { if (options.rows && itemsInRow === 0) { diff --git a/src/components/cardbuilder/peoplecardbuilder.js b/src/components/cardbuilder/peoplecardbuilder.js index 5fc9e8ade5..00b8f0fb89 100644 --- a/src/components/cardbuilder/peoplecardbuilder.js +++ b/src/components/cardbuilder/peoplecardbuilder.js @@ -5,7 +5,7 @@ * @module components/cardBuilder/peoplecardbuilder */ -import cardBuilder from 'cardBuilder'; +import cardBuilder from './cardBuilder'; export function buildPeopleCards(items, options) { options = Object.assign(options || {}, { diff --git a/src/components/channelMapper/channelMapper.js b/src/components/channelMapper/channelMapper.js index 294f9e223a..bedab5421b 100644 --- a/src/components/channelMapper/channelMapper.js +++ b/src/components/channelMapper/channelMapper.js @@ -1,21 +1,22 @@ -import dom from 'dom'; -import dialogHelper from 'dialogHelper'; -import loading from 'loading'; -import globalize from 'globalize'; -import actionsheet from 'actionsheet'; -import 'emby-input'; -import 'paper-icon-button-light'; -import 'emby-button'; -import 'listViewStyle'; -import 'material-icons'; -import 'formDialogStyle'; +import dom from '../../scripts/dom'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import loading from '../loading/loading'; +import globalize from '../../scripts/globalize'; +import actionsheet from '../actionSheet/actionSheet'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-button/emby-button'; +import '../listview/listview.css'; +import 'material-design-icons-iconfont'; +import '../formdialog.css'; +import ServerConnections from '../ServerConnections'; export default class channelMapper { constructor(options) { function mapChannel(button, channelId, providerChannelId) { loading.show(); const providerId = options.providerId; - window.connectionManager.getApiClient(options.serverId).ajax({ + ServerConnections.getApiClient(options.serverId).ajax({ type: 'POST', url: ApiClient.getUrl('LiveTv/ChannelMappings'), data: JSON.stringify({ @@ -58,7 +59,7 @@ export default class channelMapper { } function getChannelMappingOptions(serverId, providerId) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.getJSON(apiClient.getUrl('LiveTv/ChannelMappingOptions', { providerId: providerId })); diff --git a/src/components/collectionEditor/collectionEditor.js b/src/components/collectionEditor/collectionEditor.js index 2d0d025929..33e6fdecca 100644 --- a/src/components/collectionEditor/collectionEditor.js +++ b/src/components/collectionEditor/collectionEditor.js @@ -1,17 +1,19 @@ -import dom from 'dom'; -import dialogHelper from 'dialogHelper'; -import loading from 'loading'; -import layoutManager from 'layoutManager'; -import appRouter from 'appRouter'; -import globalize from 'globalize'; -import 'emby-checkbox'; -import 'emby-input'; -import 'paper-icon-button-light'; -import 'emby-select'; -import 'material-icons'; -import 'css!./../formdialog'; -import 'emby-button'; -import 'flexStyles'; +import dom from '../../scripts/dom'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import loading from '../loading/loading'; +import layoutManager from '../layoutManager'; +import { appRouter } from '../appRouter'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-select/emby-select'; +import 'material-design-icons-iconfont'; +import '../formdialog.css'; +import '../../assets/css/flexstyles.scss'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; /* eslint-disable indent */ @@ -24,7 +26,7 @@ import 'flexStyles'; const collectionId = panel.querySelector('#selectCollectionToAddTo').value; - const apiClient = window.connectionManager.getApiClient(currentServerId); + const apiClient = ServerConnections.getApiClient(currentServerId); if (collectionId) { addToCollection(apiClient, panel, collectionId); @@ -80,9 +82,7 @@ import 'flexStyles'; dlg.submitted = true; dialogHelper.close(dlg); - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageItemsAdded')); - }); + toast(globalize.translate('MessageItemsAdded')); }); } @@ -105,7 +105,7 @@ import 'flexStyles'; EnableTotalRecordCount: false }; - const apiClient = window.connectionManager.getApiClient(currentServerId); + const apiClient = ServerConnections.getApiClient(currentServerId); apiClient.getItems(apiClient.getCurrentUserId(), options).then(result => { let html = ''; @@ -199,7 +199,7 @@ import 'flexStyles'; } function centerFocus(elem, horiz, on) { - import('scrollHelper').then((scrollHelper) => { + import('../../scripts/scrollHelper').then((scrollHelper) => { const fn = on ? 'on' : 'off'; scrollHelper.centerFocus[fn](elem, horiz); }); diff --git a/src/components/confirm/confirm.js b/src/components/confirm/confirm.js index eca612ccb8..1978324e7c 100644 --- a/src/components/confirm/confirm.js +++ b/src/components/confirm/confirm.js @@ -1,69 +1,65 @@ -import browser from 'browser'; -import dialog from 'dialog'; -import globalize from 'globalize'; +import browser from '../../scripts/browser'; +import dialog from '../dialog/dialog'; +import globalize from '../../scripts/globalize'; -/* eslint-disable indent */ -export default (() => { - function replaceAll(str, find, replace) { - return str.split(find).join(replace); +function replaceAll(str, find, replace) { + return str.split(find).join(replace); +} + +function nativeConfirm(options) { + if (typeof options === 'string') { + options = { + title: '', + text: options + }; } - if (browser.tv && window.confirm) { - // Use the native confirm dialog - return options => { - if (typeof options === 'string') { - options = { - title: '', - text: options - }; - } + const text = replaceAll(options.text || '', '
', '\n'); + const result = window.confirm(text); - const text = replaceAll(options.text || '', '
', '\n'); - const result = window.confirm(text); + if (result) { + return Promise.resolve(); + } else { + return Promise.reject(); + } +} - if (result) { - return Promise.resolve(); - } else { - return Promise.reject(); - } +function customConfirm(text, title) { + let options; + if (typeof text === 'string') { + options = { + title: title, + text: text }; } else { - // Use our own dialog - return (text, title) => { - let options; - if (typeof text === 'string') { - options = { - title: title, - text: text - }; - } else { - options = text; - } - - const items = []; - - items.push({ - name: options.cancelText || globalize.translate('ButtonCancel'), - id: 'cancel', - type: 'cancel' - }); - - items.push({ - name: options.confirmText || globalize.translate('ButtonOk'), - id: 'ok', - type: options.primary === 'delete' ? 'delete' : 'submit' - }); - - options.buttons = items; - - return dialog.show(options).then(result => { - if (result === 'ok') { - return Promise.resolve(); - } - - return Promise.reject(); - }); - }; + options = text; } -})(); -/* eslint-enable indent */ + + const items = []; + + items.push({ + name: options.cancelText || globalize.translate('ButtonCancel'), + id: 'cancel', + type: 'cancel' + }); + + items.push({ + name: options.confirmText || globalize.translate('ButtonOk'), + id: 'ok', + type: options.primary === 'delete' ? 'delete' : 'submit' + }); + + options.buttons = items; + + return dialog.show(options).then(result => { + if (result === 'ok') { + return Promise.resolve(); + } + + return Promise.reject(); + }); +} + +const confirm = browser.tv && window.confirm ? nativeConfirm : customConfirm; + +export default confirm; diff --git a/src/components/dialog/dialog.js b/src/components/dialog/dialog.js index 1b13900d85..ee97fff8a1 100644 --- a/src/components/dialog/dialog.js +++ b/src/components/dialog/dialog.js @@ -1,14 +1,14 @@ -import dialogHelper from 'dialogHelper'; -import dom from 'dom'; -import layoutManager from 'layoutManager'; -import scrollHelper from 'scrollHelper'; -import globalize from 'globalize'; -import 'material-icons'; -import 'emby-button'; -import 'paper-icon-button-light'; -import 'emby-input'; -import 'formDialogStyle'; -import 'flexStyles'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import dom from '../../scripts/dom'; +import layoutManager from '../layoutManager'; +import scrollHelper from '../../scripts/scrollHelper'; +import globalize from '../../scripts/globalize'; +import 'material-design-icons-iconfont'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-input/emby-input'; +import '../formdialog.css'; +import '../../assets/css/flexstyles.scss'; /* eslint-disable indent */ @@ -128,7 +128,7 @@ import 'flexStyles'; options = text; } - const { default: template } = await import('text!./dialog.template.html'); + const { default: template } = await import('./dialog.template.html'); return new Promise((resolve, reject) => { showDialog(options, template).then(resolve, reject); }); diff --git a/src/components/dialogHelper/dialogHelper.js b/src/components/dialogHelper/dialogHelper.js index eb46d98b12..cdaf47996f 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 3dd3302b28..2f8a2cadd1 100644 --- a/src/components/directorybrowser/directorybrowser.js +++ b/src/components/directorybrowser/directorybrowser.js @@ -1,13 +1,14 @@ -import loading from 'loading'; -import dialogHelper from 'dialogHelper'; -import dom from 'dom'; -import globalize from 'globalize'; -import 'listViewStyle'; -import 'emby-input'; -import 'paper-icon-button-light'; -import 'css!./directorybrowser'; -import 'formDialogStyle'; -import 'emby-button'; +import loading from '../loading/loading'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import dom from '../../scripts/dom'; +import globalize from '../../scripts/globalize'; +import '../listview/listview.css'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-button/paper-icon-button-light'; +import './directorybrowser.css'; +import '../formdialog.css'; +import '../../elements/emby-button/emby-button'; +import alert from '../alert'; /* eslint-disable indent */ @@ -157,9 +158,7 @@ import 'emby-button'; } function alertTextWithOptions(options) { - import('alert').then(({default: alert}) => { - alert(options); - }); + alert(options); } function validatePath(path, validateWriteable, apiClient) { diff --git a/src/components/displaySettings/displaySettings.js b/src/components/displaySettings/displaySettings.js index efaab16b3f..9d7292547d 100644 --- a/src/components/displaySettings/displaySettings.js +++ b/src/components/displaySettings/displaySettings.js @@ -1,16 +1,18 @@ -import browser from 'browser'; -import layoutManager from 'layoutManager'; -import pluginManager from 'pluginManager'; -import appHost from 'apphost'; -import focusManager from 'focusManager'; -import datetime from 'datetime'; -import globalize from 'globalize'; -import loading from 'loading'; -import skinManager from 'skinManager'; -import events from 'events'; -import 'emby-select'; -import 'emby-checkbox'; -import 'emby-button'; +import browser from '../../scripts/browser'; +import layoutManager from '../layoutManager'; +import { pluginManager } from '../pluginManager'; +import { appHost } from '../apphost'; +import focusManager from '../focusManager'; +import datetime from '../../scripts/datetime'; +import globalize from '../../scripts/globalize'; +import loading from '../loading/loading'; +import skinManager from '../../scripts/themeManager'; +import { Events } from 'jellyfin-apiclient'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-button/emby-button'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; /* eslint-disable indent */ @@ -168,11 +170,9 @@ import 'emby-button'; saveUser(context, user, userSettings, apiClient).then(() => { loading.hide(); if (enableSaveConfirmation) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('SettingsSaved')); - }); + toast(globalize.translate('SettingsSaved')); } - events.trigger(instance, 'saved'); + Events.trigger(instance, 'saved'); }, () => { loading.hide(); }); @@ -181,7 +181,7 @@ import 'emby-button'; function onSubmit(e) { const self = this; - const apiClient = window.connectionManager.getApiClient(self.options.serverId); + const apiClient = ServerConnections.getApiClient(self.options.serverId); const userId = self.options.userId; const userSettings = self.options.userSettings; @@ -198,7 +198,7 @@ import 'emby-button'; } async function embed(options, self) { - const { default: template } = await import('text!./displaySettings.template.html'); + const { default: template } = await import('./displaySettings.template.html'); options.element.innerHTML = globalize.translateHtml(template, 'core'); options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self)); if (options.enableSaveButton) { @@ -220,7 +220,7 @@ import 'emby-button'; loading.show(); const userId = self.options.userId; - const apiClient = window.connectionManager.getApiClient(self.options.serverId); + const apiClient = ServerConnections.getApiClient(self.options.serverId); const userSettings = self.options.userSettings; return apiClient.getUser(userId).then(user => { diff --git a/src/components/favoriteitems.js b/src/components/favoriteitems.js index 86cd050216..cb1b61c43f 100644 --- a/src/components/favoriteitems.js +++ b/src/components/favoriteitems.js @@ -1,12 +1,12 @@ -import loading from 'loading'; -import cardBuilder from 'cardBuilder'; -import dom from 'dom'; -import appHost from 'apphost'; -import imageLoader from 'imageLoader'; -import globalize from 'globalize'; -import layoutManager from 'layoutManager'; -import 'scrollStyles'; -import 'emby-itemscontainer'; +import loading from './loading/loading'; +import cardBuilder from './cardbuilder/cardBuilder'; +import dom from '../scripts/dom'; +import { appHost } from './apphost'; +import imageLoader from './images/imageLoader'; +import globalize from '../scripts/globalize'; +import layoutManager from './layoutManager'; +import '../assets/css/scrollstyles.css'; +import '../elements/emby-itemscontainer/emby-itemscontainer'; /* eslint-disable indent */ diff --git a/src/components/filterdialog/filterdialog.js b/src/components/filterdialog/filterdialog.js index d11edb40a2..c8b81066d2 100644 --- a/src/components/filterdialog/filterdialog.js +++ b/src/components/filterdialog/filterdialog.js @@ -1,10 +1,11 @@ -import dom from 'dom'; -import dialogHelper from 'dialogHelper'; -import globalize from 'globalize'; -import events from 'events'; -import 'emby-checkbox'; -import 'emby-collapse'; -import 'css!./style.css'; +import dom from '../../scripts/dom'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import globalize from '../../scripts/globalize'; +import { Events } from 'jellyfin-apiclient'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-collapse/emby-collapse'; +import './style.css'; +import ServerConnections from '../ServerConnections'; /* eslint-disable indent */ function renderOptions(context, selector, cssClass, items, isCheckedFn) { @@ -108,7 +109,7 @@ import 'css!./style.css'; * @param instance {FilterDialog} An instance of FilterDialog */ function triggerChange(instance) { - events.trigger(instance, 'filterchange'); + Events.trigger(instance, 'filterchange'); } function setVisibility(context, options) { @@ -401,7 +402,7 @@ import 'css!./style.css'; } show() { - return import('text!./filterdialog.template.html').then(({default: template}) => { + return import('./filterdialog.template.html').then(({default: template}) => { return new Promise((resolve) => { const dlg = dialogHelper.createDialog({ removeOnClose: true, @@ -419,7 +420,7 @@ import 'css!./style.css'; this.bindEvents(dlg); if (enableDynamicFilters(this.options.mode)) { dlg.classList.add('dynamicFilterDialog'); - const apiClient = window.connectionManager.getApiClient(this.options.serverId); + const apiClient = ServerConnections.getApiClient(this.options.serverId); loadDynamicFilters(dlg, apiClient, apiClient.getCurrentUserId(), this.options.query); } }); diff --git a/src/components/filtermenu/filtermenu.js b/src/components/filtermenu/filtermenu.js index 7cef08303d..f4f6ef2d9b 100644 --- a/src/components/filtermenu/filtermenu.js +++ b/src/components/filtermenu/filtermenu.js @@ -1,18 +1,19 @@ -import dom from 'dom'; -import focusManager from 'focusManager'; -import dialogHelper from 'dialogHelper'; -import inputManager from 'inputManager'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import * as userSettings from 'userSettings'; -import 'emby-checkbox'; -import 'emby-input'; -import 'paper-icon-button-light'; -import 'emby-select'; -import 'material-icons'; -import 'css!./../formdialog'; -import 'emby-button'; -import 'flexStyles'; +import dom from '../../scripts/dom'; +import focusManager from '../focusManager'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import inputManager from '../../scripts/inputManager'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import * as userSettings from '../../scripts/settings/userSettings'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-select/emby-select'; +import 'material-design-icons-iconfont'; +import '../formdialog.css'; +import '../../assets/css/flexstyles.scss'; +import ServerConnections from '../ServerConnections'; function onSubmit(e) { e.preventDefault(); @@ -80,7 +81,7 @@ function moveCheckboxFocus(elem, offset) { } } function centerFocus(elem, horiz, on) { - import('scrollHelper').then(({ default: scrollHelper }) => { + import('../../scripts/scrollHelper').then((scrollHelper) => { const fn = on ? 'on' : 'off'; scrollHelper.centerFocus[fn](elem, horiz); }); @@ -193,7 +194,7 @@ function initEditor(context, settings) { } } function loadDynamicFilters(context, options) { - const apiClient = window.connectionManager.getApiClient(options.serverId); + const apiClient = ServerConnections.getApiClient(options.serverId); const filterMenuOptions = Object.assign(options.filterMenuOptions, { @@ -209,7 +210,7 @@ function loadDynamicFilters(context, options) { class FilterMenu { show(options) { return new Promise( (resolve, reject) => { - import('text!./filtermenu.template.html').then(({ default: template }) => { + import('./filtermenu.template.html').then(({ default: template }) => { const dialogOptions = { removeOnClose: true, scrollY: false diff --git a/src/components/focusManager.js b/src/components/focusManager.js index d45984bf58..b0fc188516 100644 --- a/src/components/focusManager.js +++ b/src/components/focusManager.js @@ -1,7 +1,7 @@ /* eslint-disable indent */ -import dom from 'dom'; -import scrollManager from 'scrollManager'; +import dom from '../scripts/dom'; +import scrollManager from './scrollManager'; const scopes = []; function pushScope(elem) { diff --git a/src/components/groupedcards.js b/src/components/groupedcards.js index 947b3b8569..13158016ac 100644 --- a/src/components/groupedcards.js +++ b/src/components/groupedcards.js @@ -1,12 +1,14 @@ /* eslint-disable indent */ -import dom from 'dom'; -import appRouter from 'appRouter'; +import dom from '../scripts/dom'; +import { appRouter } from './appRouter'; +import Dashboard from '../scripts/clientUtils'; +import ServerConnections from './ServerConnections'; function onGroupedCardClick(e, card) { const itemId = card.getAttribute('data-id'); const serverId = card.getAttribute('data-serverid'); - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); const userId = apiClient.getCurrentUserId(); const playedIndicator = card.querySelector('.playedIndicator'); const playedIndicatorHtml = playedIndicator ? playedIndicator.innerHTML : null; diff --git a/src/components/guide/guide-settings.js b/src/components/guide/guide-settings.js index 35f0d3e06e..8132ac3bb3 100644 --- a/src/components/guide/guide-settings.js +++ b/src/components/guide/guide-settings.js @@ -1,12 +1,12 @@ -import dialogHelper from 'dialogHelper'; -import globalize from 'globalize'; -import * as userSettings from 'userSettings'; -import layoutManager from 'layoutManager'; -import scrollHelper from 'scrollHelper'; -import 'emby-checkbox'; -import 'emby-radio'; -import 'css!./../formdialog'; -import 'material-icons'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import globalize from '../../scripts/globalize'; +import * as userSettings from '../../scripts/settings/userSettings'; +import layoutManager from '../layoutManager'; +import scrollHelper from '../../scripts/scrollHelper'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-radio/emby-radio'; +import '../formdialog.css'; +import 'material-design-icons-iconfont'; function saveCategories(context, options) { const categories = []; @@ -88,7 +88,7 @@ function showEditor(options) { return new Promise(function (resolve, reject) { let settingsChanged = false; - import('text!./guide-settings.template.html').then(({ default: template }) => { + import('./guide-settings.template.html').then(({ default: template }) => { const dialogOptions = { removeOnClose: true, scrollY: false diff --git a/src/components/guide/guide.js b/src/components/guide/guide.js index a5ed55e67c..7ae0b8409f 100644 --- a/src/components/guide/guide.js +++ b/src/components/guide/guide.js @@ -1,32 +1,33 @@ -import inputManager from 'inputManager'; -import browser from 'browser'; -import globalize from 'globalize'; -import scrollHelper from 'scrollHelper'; -import serverNotifications from 'serverNotifications'; -import loading from 'loading'; -import datetime from 'datetime'; -import focusManager from 'focusManager'; -import playbackManager from 'playbackManager'; -import * as userSettings from 'userSettings'; -import imageLoader from 'imageLoader'; -import events from 'events'; -import layoutManager from 'layoutManager'; -import itemShortcuts from 'itemShortcuts'; -import dom from 'dom'; -import 'css!./guide.css'; -import 'programStyles'; -import 'material-icons'; -import 'scrollStyles'; -import 'emby-programcell'; -import 'emby-button'; -import 'paper-icon-button-light'; -import 'emby-tabs'; -import 'emby-scroller'; -import 'flexStyles'; -import 'webcomponents'; +import inputManager from '../../scripts/inputManager'; +import browser from '../../scripts/browser'; +import globalize from '../../scripts/globalize'; +import { Events } from 'jellyfin-apiclient'; +import scrollHelper from '../../scripts/scrollHelper'; +import serverNotifications from '../../scripts/serverNotifications'; +import loading from '../loading/loading'; +import datetime from '../../scripts/datetime'; +import focusManager from '../focusManager'; +import { playbackManager } from '../playback/playbackmanager'; +import * as userSettings from '../../scripts/settings/userSettings'; +import imageLoader from '../images/imageLoader'; +import layoutManager from '../layoutManager'; +import itemShortcuts from '../shortcuts'; +import dom from '../../scripts/dom'; +import './guide.css'; +import './programs.css'; +import 'material-design-icons-iconfont'; +import '../../assets/css/scrollstyles.css'; +import '../../elements/emby-programcell/emby-programcell'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-tabs/emby-tabs'; +import '../../elements/emby-scroller/emby-scroller'; +import '../../assets/css/flexstyles.scss'; +import 'webcomponents.js/webcomponents-lite'; +import ServerConnections from '../ServerConnections'; function showViewSettings(instance) { - import('guide-settings-dialog').then(({default: guideSettingsDialog}) => { + import('./guide-settings').then(({default: guideSettingsDialog}) => { guideSettingsDialog.show(instance.categoryOptions).then(function () { instance.refresh(); }); @@ -164,10 +165,10 @@ function Guide(options) { self.destroy = function () { stopAutoRefresh(); - events.off(serverNotifications, 'TimerCreated', onTimerCreated); - events.off(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); - events.off(serverNotifications, 'TimerCancelled', onTimerCancelled); - events.off(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); + Events.off(serverNotifications, 'TimerCreated', onTimerCreated); + Events.off(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); + Events.off(serverNotifications, 'TimerCancelled', onTimerCancelled); + Events.off(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); setScrollEvents(options.element, false); itemShortcuts.off(options.element); @@ -212,7 +213,7 @@ function Guide(options) { } function reloadGuide(context, newStartDate, scrollToTimeMs, focusToTimeMs, startTimeOfDayMs, focusProgramOnRender) { - const apiClient = window.connectionManager.getApiClient(options.serverId); + const apiClient = ServerConnections.getApiClient(options.serverId); const channelQuery = { @@ -872,7 +873,7 @@ function Guide(options) { function reloadPage(page) { showLoading(); - const apiClient = window.connectionManager.getApiClient(options.serverId); + const apiClient = ServerConnections.getApiClient(options.serverId); apiClient.getLiveTvGuideInfo().then(function (guideInfo) { setDateRange(page, guideInfo); @@ -1001,7 +1002,7 @@ function Guide(options) { const item = items[id]; if (item) { - events.trigger(self, 'focus', [ + Events.trigger(self, 'focus', [ { item: item }]); @@ -1091,7 +1092,7 @@ function Guide(options) { } } - import('text!./tvguide.template.html').then(({default: template}) => { + import('./tvguide.template.html').then(({default: template}) => { const context = options.element; context.classList.add('tvguide'); @@ -1183,12 +1184,12 @@ function Guide(options) { setScrollEvents(context, true); itemShortcuts.on(context); - events.trigger(self, 'load'); + Events.trigger(self, 'load'); - events.on(serverNotifications, 'TimerCreated', onTimerCreated); - events.on(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); - events.on(serverNotifications, 'TimerCancelled', onTimerCancelled); - events.on(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); + Events.on(serverNotifications, 'TimerCreated', onTimerCreated); + Events.on(serverNotifications, 'SeriesTimerCreated', onSeriesTimerCreated); + Events.on(serverNotifications, 'TimerCancelled', onTimerCancelled); + Events.on(serverNotifications, 'SeriesTimerCancelled', onSeriesTimerCancelled); self.refresh(); }); diff --git a/src/components/homeScreenSettings/homeScreenSettings.js b/src/components/homeScreenSettings/homeScreenSettings.js index 5138935408..89edf29486 100644 --- a/src/components/homeScreenSettings/homeScreenSettings.js +++ b/src/components/homeScreenSettings/homeScreenSettings.js @@ -1,13 +1,16 @@ -import layoutManager from 'layoutManager'; -import focusManager from 'focusManager'; -import globalize from 'globalize'; -import loading from 'loading'; -import homeSections from 'homeSections'; -import dom from 'dom'; -import events from 'events'; -import 'listViewStyle'; -import 'emby-select'; -import 'emby-checkbox'; + +import layoutManager from '../layoutManager'; +import focusManager from '../focusManager'; +import globalize from '../../scripts/globalize'; +import loading from '../loading/loading'; +import { Events } from 'jellyfin-apiclient'; +import homeSections from '../homesections/homesections'; +import dom from '../../scripts/dom'; +import '../listview/listview.css'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-checkbox/emby-checkbox'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; /* eslint-disable indent */ @@ -369,12 +372,10 @@ import 'emby-checkbox'; saveUser(context, user, userSettings, apiClient).then(() => { loading.hide(); if (enableSaveConfirmation) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('SettingsSaved')); - }); + toast(globalize.translate('SettingsSaved')); } - events.trigger(instance, 'saved'); + Events.trigger(instance, 'saved'); }, () => { loading.hide(); }); @@ -383,7 +384,7 @@ import 'emby-checkbox'; function onSubmit(e) { const self = this; - const apiClient = window.connectionManager.getApiClient(self.options.serverId); + const apiClient = ServerConnections.getApiClient(self.options.serverId); const userId = self.options.userId; const userSettings = self.options.userSettings; @@ -417,7 +418,7 @@ import 'emby-checkbox'; } function embed(options, self) { - return import('text!./homeScreenSettings.template.html').then(({default: template}) => { + return import('./homeScreenSettings.template.html').then(({default: template}) => { for (let i = 1; i <= numConfigurableSections; i++) { template = template.replace(`{section${i}label}`, globalize.translate('LabelHomeScreenSectionValue', i)); } @@ -455,7 +456,7 @@ import 'emby-checkbox'; loading.show(); const userId = self.options.userId; - const apiClient = window.connectionManager.getApiClient(self.options.serverId); + const apiClient = ServerConnections.getApiClient(self.options.serverId); const userSettings = self.options.userSettings; apiClient.getUser(userId).then(user => { diff --git a/src/components/homesections/homesections.js b/src/components/homesections/homesections.js index 758773689b..6561b8a9e0 100644 --- a/src/components/homesections/homesections.js +++ b/src/components/homesections/homesections.js @@ -1,15 +1,17 @@ -import cardBuilder from 'cardBuilder'; -import dom from 'dom'; -import layoutManager from 'layoutManager'; -import imageLoader from 'imageLoader'; -import globalize from 'globalize'; -import appRouter from 'appRouter'; -import imageHelper from 'scripts/imagehelper'; -import 'paper-icon-button-light'; -import 'emby-itemscontainer'; -import 'emby-scroller'; -import 'emby-button'; -import 'css!./homesections'; +import cardBuilder from '../cardbuilder/cardBuilder'; +import dom from '../../scripts/dom'; +import layoutManager from '../layoutManager'; +import imageLoader from '../images/imageLoader'; +import globalize from '../../scripts/globalize'; +import { appRouter } from '../appRouter'; +import imageHelper from '../../scripts/imagehelper'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-itemscontainer/emby-itemscontainer'; +import '../../elements/emby-scroller/emby-scroller'; +import '../../elements/emby-button/emby-button'; +import './homesections.css'; +import Dashboard from '../../scripts/clientUtils'; +import ServerConnections from '../ServerConnections'; /* eslint-disable indent */ @@ -211,7 +213,7 @@ import 'css!./homesections'; function getFetchLatestItemsFn(serverId, parentId, collectionType) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); let limit = 16; if (enableScrollX()) { @@ -367,7 +369,7 @@ import 'css!./homesections'; function getContinueWatchingFetchFn(serverId) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); const screenWidth = dom.getWindowSize().innerWidth; let limit; @@ -440,7 +442,7 @@ import 'css!./homesections'; function getContinueListeningFetchFn(serverId) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); const screenWidth = dom.getWindowSize().innerWidth; let limit; @@ -513,7 +515,7 @@ import 'css!./homesections'; function getOnNowFetchFn(serverId) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.getLiveTvRecommendedPrograms({ userId: apiClient.getCurrentUserId(), IsAiring: true, @@ -656,7 +658,7 @@ import 'css!./homesections'; function getNextUpFetchFn(serverId) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.getNextUpEpisodes({ Limit: enableScrollX() ? 24 : 15, Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo,Path', @@ -727,7 +729,7 @@ import 'css!./homesections'; function getLatestRecordingsFetchFn(serverId, activeRecordingsOnly) { return function () { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.getLiveTvRecordings({ userId: apiClient.getCurrentUserId(), Limit: enableScrollX() ? 12 : 5, diff --git a/src/components/htmlMediaHelper.js b/src/components/htmlMediaHelper.js index be506b449d..4f803aa4b6 100644 --- a/src/components/htmlMediaHelper.js +++ b/src/components/htmlMediaHelper.js @@ -1,8 +1,9 @@ + /* eslint-disable indent */ -import appSettings from 'appSettings' ; -import browser from 'browser'; -import events from 'events'; +import appSettings from '../scripts/settings/appSettings' ; +import browser from '../scripts/browser'; +import { Events } from 'jellyfin-apiclient'; export function getSavedVolume() { return appSettings.get('volume') || 1; @@ -114,7 +115,7 @@ import events from 'events'; instance.destroyCustomTrack(instance._mediaElement); } - events.trigger(instance, 'error', [ + Events.trigger(instance, 'error', [ { type: type } @@ -359,7 +360,7 @@ import events from 'events'; src: instance._currentSrc }; - events.trigger(instance, 'stopped', [stopInfo]); + Events.trigger(instance, 'stopped', [stopInfo]); instance._currentTime = null; instance._currentSrc = null; diff --git a/src/components/imageDownloader/imageDownloader.js b/src/components/imageDownloader/imageDownloader.js index 1ec459ff83..18dd849aec 100644 --- a/src/components/imageDownloader/imageDownloader.js +++ b/src/components/imageDownloader/imageDownloader.js @@ -1,17 +1,18 @@ -import dom from 'dom'; -import loading from 'loading'; -import appHost from 'apphost'; -import dialogHelper from 'dialogHelper'; -import imageLoader from 'imageLoader'; -import browser from 'browser'; -import layoutManager from 'layoutManager'; -import scrollHelper from 'scrollHelper'; -import globalize from 'globalize'; -import 'emby-checkbox'; -import 'paper-icon-button-light'; -import 'emby-button'; -import 'formDialogStyle'; -import 'cardStyle'; +import dom from '../../scripts/dom'; +import loading from '../loading/loading'; +import { appHost } from '../apphost'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import imageLoader from '../images/imageLoader'; +import browser from '../../scripts/browser'; +import layoutManager from '../layoutManager'; +import scrollHelper from '../../scripts/scrollHelper'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../../elements/emby-button/emby-button'; +import '../formdialog.css'; +import '../cardbuilder/card.css'; +import ServerConnections from '../ServerConnections'; /* eslint-disable indent */ @@ -315,8 +316,8 @@ import 'cardStyle'; function showEditor(itemId, serverId, itemType) { loading.show(); - import('text!./imageDownloader.template.html').then(({default: template}) => { - const apiClient = window.connectionManager.getApiClient(serverId); + import('./imageDownloader.template.html').then(({default: template}) => { + const apiClient = ServerConnections.getApiClient(serverId); currentItemId = itemId; currentItemType = itemType; diff --git a/src/components/imageOptionsEditor/imageOptionsEditor.js b/src/components/imageOptionsEditor/imageOptionsEditor.js index d112dd65cc..a220a65c5d 100644 --- a/src/components/imageOptionsEditor/imageOptionsEditor.js +++ b/src/components/imageOptionsEditor/imageOptionsEditor.js @@ -5,12 +5,12 @@ * @module components/imageOptionsEditor/imageOptionsEditor */ -import globalize from 'globalize'; -import dom from 'dom'; -import dialogHelper from 'dialogHelper'; -import 'emby-checkbox'; -import 'emby-select'; -import 'emby-input'; +import globalize from '../../scripts/globalize'; +import dom from '../../scripts/dom'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-select/emby-select'; +import '../../elements/emby-input/emby-input'; function getDefaultImageConfig(itemType, type) { return { diff --git a/src/components/imageUploader/imageUploader.js b/src/components/imageUploader/imageUploader.js index e89cbda2ae..10beff5cab 100644 --- a/src/components/imageUploader/imageUploader.js +++ b/src/components/imageUploader/imageUploader.js @@ -5,16 +5,18 @@ * @module components/imageUploader/imageUploader */ -import dialogHelper from 'dialogHelper'; -import dom from 'dom'; -import loading from 'loading'; -import scrollHelper from 'scrollHelper'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import 'emby-button'; -import 'emby-select'; -import 'formDialogStyle'; -import 'css!./style'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import dom from '../../scripts/dom'; +import loading from '../loading/loading'; +import scrollHelper from '../../scripts/scrollHelper'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-select/emby-select'; +import '../formdialog.css'; +import './style.css'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; let currentItemId; let currentServerId; @@ -26,16 +28,12 @@ import 'css!./style'; switch (evt.target.error.code) { case evt.target.error.NOT_FOUND_ERR: - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageFileReadError')); - }); + toast(globalize.translate('MessageFileReadError')); break; case evt.target.error.ABORT_ERR: break; // noop default: - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageFileReadError')); - }); + toast(globalize.translate('MessageFileReadError')); break; } } @@ -87,9 +85,7 @@ import 'css!./style'; } if (!file.type.startsWith('image/')) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageImageFileTypeAllowed')); - }); + toast(globalize.translate('MessageImageFileTypeAllowed')); e.preventDefault(); return false; } @@ -100,14 +96,12 @@ import 'css!./style'; const imageType = dlg.querySelector('#selectImageType').value; if (imageType === 'None') { - import('toast').then(({default: toast}) => { - toast(globalize.translate('MessageImageTypeNotSelected')); - }); + toast(globalize.translate('MessageImageTypeNotSelected')); e.preventDefault(); return false; } - window.connectionManager.getApiClient(currentServerId).uploadItemImage(currentItemId, imageType, file).then(() => { + ServerConnections.getApiClient(currentServerId).uploadItemImage(currentItemId, imageType, file).then(() => { dlg.querySelector('#uploadImage').value = ''; loading.hide(); @@ -134,7 +128,7 @@ import 'css!./style'; function showEditor(options, resolve) { options = options || {}; - return import('text!./imageUploader.template.html').then(({default: template}) => { + return import('./imageUploader.template.html').then(({default: template}) => { currentItemId = options.itemId; currentServerId = options.serverId; diff --git a/src/components/imageeditor/imageeditor.js b/src/components/imageeditor/imageeditor.js index e5b59cfb22..69e9bcb8c7 100644 --- a/src/components/imageeditor/imageeditor.js +++ b/src/components/imageeditor/imageeditor.js @@ -1,18 +1,21 @@ -import dialogHelper from 'dialogHelper'; -import loading from 'loading'; -import dom from 'dom'; -import layoutManager from 'layoutManager'; -import focusManager from 'focusManager'; -import globalize from 'globalize'; -import scrollHelper from 'scrollHelper'; -import imageLoader from 'imageLoader'; -import browser from 'browser'; -import appHost from 'apphost'; -import 'cardStyle'; -import 'formDialogStyle'; -import 'emby-button'; -import 'paper-icon-button-light'; -import 'css!./imageeditor'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import loading from '../loading/loading'; +import dom from '../../scripts/dom'; +import layoutManager from '../layoutManager'; +import focusManager from '../focusManager'; +import globalize from '../../scripts/globalize'; +import scrollHelper from '../../scripts/scrollHelper'; +import imageLoader from '../images/imageLoader'; +import browser from '../../scripts/browser'; +import { appHost } from '../apphost'; +import '../cardbuilder/card.css'; +import '../formdialog.css'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import './imageeditor.css'; +import ServerConnections from '../ServerConnections'; +import alert from '../alert'; +import confirm from '../confirm/confirm'; /* eslint-disable indent */ @@ -35,10 +38,10 @@ import 'css!./imageeditor'; let apiClient; if (item) { - apiClient = window.connectionManager.getApiClient(item.ServerId); + apiClient = ServerConnections.getApiClient(item.ServerId); reloadItem(page, item, apiClient, focusContext); } else { - apiClient = window.connectionManager.getApiClient(currentItem.ServerId); + apiClient = ServerConnections.getApiClient(currentItem.ServerId); apiClient.getItem(apiClient.getCurrentUserId(), currentItem.Id).then(function (item) { reloadItem(page, item, apiClient, focusContext); }); @@ -199,15 +202,11 @@ import 'css!./imageeditor'; return; } - import('confirm').then(({default: confirm}) => { - confirm({ - - text: globalize.translate('ConfirmDeleteImage'), - confirmText: globalize.translate('Delete'), - primary: 'delete' - - }).then(afterConfirm); - }); + confirm({ + text: globalize.translate('ConfirmDeleteImage'), + confirmText: globalize.translate('Delete'), + primary: 'delete' + }).then(afterConfirm); } function moveImage(context, apiClient, itemId, type, index, newIndex, focusContext) { @@ -215,9 +214,7 @@ import 'css!./imageeditor'; hasChanges = true; reload(context, null, focusContext); }, function () { - import('alert').then(({default: alert}) => { - alert(globalize.translate('ErrorDefault')); - }); + alert(globalize.translate('ErrorDefault')); }); } @@ -281,7 +278,7 @@ import 'css!./imageeditor'; } function showImageDownloader(page, imageType) { - import('imageDownloader').then(({default: ImageDownloader}) => { + import('../imageDownloader/imageDownloader').then((ImageDownloader) => { ImageDownloader.show(currentItem.Id, currentItem.ServerId, currentItem.Type, imageType).then(function () { hasChanges = true; reload(page); @@ -292,14 +289,14 @@ import 'css!./imageeditor'; function showActionSheet(context, imageCard) { const itemId = imageCard.getAttribute('data-id'); const serverId = imageCard.getAttribute('data-serverid'); - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); const type = imageCard.getAttribute('data-imagetype'); const index = parseInt(imageCard.getAttribute('data-index')); const providerCount = parseInt(imageCard.getAttribute('data-providers')); const numImages = parseInt(imageCard.getAttribute('data-numimages')); - import('actionsheet').then(({default: actionSheet}) => { + import('../actionSheet/actionSheet').then(({default: actionSheet}) => { const commands = []; commands.push({ @@ -370,7 +367,7 @@ import 'css!./imageeditor'; addListeners(context, 'btnOpenUploadMenu', 'click', function () { const imageType = this.getAttribute('data-imagetype'); - import('imageUploader').then(({default: imageUploader}) => { + import('../imageUploader/imageUploader').then(({default: imageUploader}) => { imageUploader.show({ theme: options.theme, @@ -403,7 +400,7 @@ import 'css!./imageeditor'; const type = this.getAttribute('data-imagetype'); let index = this.getAttribute('data-index'); index = index === 'null' ? null : parseInt(index); - const apiClient = window.connectionManager.getApiClient(currentItem.ServerId); + const apiClient = ServerConnections.getApiClient(currentItem.ServerId); deleteImage(context, currentItem.Id, type, index, apiClient, true); }); @@ -411,7 +408,7 @@ import 'css!./imageeditor'; const type = this.getAttribute('data-imagetype'); const index = this.getAttribute('data-index'); const newIndex = this.getAttribute('data-newindex'); - const apiClient = window.connectionManager.getApiClient(currentItem.ServerId); + const apiClient = ServerConnections.getApiClient(currentItem.ServerId); moveImage(context, apiClient, currentItem.Id, type, index, newIndex, dom.parentWithClass(this, 'itemsContainer')); }); } @@ -422,8 +419,8 @@ import 'css!./imageeditor'; loading.show(); - import('text!./imageeditor.template.html').then(({default: template}) => { - const apiClient = window.connectionManager.getApiClient(serverId); + import('./imageeditor.template.html').then(({default: template}) => { + const apiClient = ServerConnections.getApiClient(serverId); apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) { const dialogOptions = { removeOnClose: true diff --git a/src/components/images/imageLoader.js b/src/components/images/imageLoader.js index 9dc708098d..1811b117e1 100644 --- a/src/components/images/imageLoader.js +++ b/src/components/images/imageLoader.js @@ -1,7 +1,7 @@ -import * as lazyLoader from 'lazyLoader'; -import * as userSettings from 'userSettings'; -import * as blurhash from 'blurhash'; -import 'css!./style'; +import * as lazyLoader from '../lazyLoader/lazyLoaderIntersectionObserver'; +import * as userSettings from '../../scripts/settings/userSettings'; +import { decode, isBlurhashValid } from 'blurhash'; +import './style.css'; /* eslint-disable indent */ export function lazyImage(elem, source = elem.getAttribute('data-src')) { @@ -13,7 +13,7 @@ import 'css!./style'; } function itemBlurhashing(target, blurhashstr) { - if (blurhash.isBlurhashValid(blurhashstr)) { + if (isBlurhashValid(blurhashstr)) { // Although the default values recommended by Blurhash developers is 32x32, a size of 18x18 seems to be the sweet spot for us, // improving the performance and reducing the memory usage, while retaining almost full blur quality. // Lower values had more visible pixelation @@ -21,7 +21,7 @@ import 'css!./style'; const height = 18; let pixels; try { - pixels = blurhash.decode(blurhashstr, width, height); + pixels = decode(blurhashstr, width, height); } catch (err) { console.error('Blurhash decode error: ', err); target.classList.add('non-blurhashable'); @@ -133,6 +133,7 @@ import 'css!./style'; } } } + lazyLoader.lazyChildren(elem, fillImage); } diff --git a/src/components/indicators/indicators.js b/src/components/indicators/indicators.js index bbd672ef72..5f8a8691d1 100644 --- a/src/components/indicators/indicators.js +++ b/src/components/indicators/indicators.js @@ -1,8 +1,8 @@ -import datetime from 'datetime'; -import itemHelper from 'itemHelper'; -import 'emby-progressbar'; -import 'css!./indicators.css'; -import 'material-icons'; +import datetime from '../../scripts/datetime'; +import itemHelper from '../itemHelper'; +import '../../elements/emby-progressbar/emby-progressbar'; +import './indicators.css'; +import 'material-design-icons-iconfont'; export function enableProgressIndicator(item) { if (item.MediaType === 'Video' && item.Type !== 'TvChannel') { diff --git a/src/components/itemContextMenu.js b/src/components/itemContextMenu.js index f36f623d9f..c8664512ef 100644 --- a/src/components/itemContextMenu.js +++ b/src/components/itemContextMenu.js @@ -1,10 +1,12 @@ -import appHost from 'apphost'; -import globalize from 'globalize'; -import itemHelper from 'itemHelper'; -import appRouter from 'appRouter'; -import playbackManager from 'playbackManager'; -import browser from 'browser'; -import actionsheet from 'actionsheet'; +import browser from '../scripts/browser'; +import globalize from '../scripts/globalize'; +import actionsheet from './actionSheet/actionSheet'; +import { appHost } from './apphost'; +import { appRouter } from './appRouter'; +import itemHelper from './itemHelper'; +import { playbackManager } from './playback/playbackmanager'; +import ServerConnections from './ServerConnections'; +import toast from './toast/toast'; /* eslint-disable indent */ export function getCommands(options) { @@ -329,12 +331,12 @@ import actionsheet from 'actionsheet'; function executeCommand(item, id, options) { const itemId = item.Id; const serverId = item.ServerId; - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return new Promise(function (resolve, reject) { switch (id) { case 'addtocollection': - import('collectionEditor').then(({default: collectionEditor}) => { + import('./collectionEditor/collectionEditor').then(({default: collectionEditor}) => { new collectionEditor({ items: [itemId], serverId: serverId @@ -342,7 +344,7 @@ import actionsheet from 'actionsheet'; }); break; case 'addtoplaylist': - import('playlistEditor').then(({default: playlistEditor}) => { + import('./playlisteditor/playlisteditor').then(({default: playlistEditor}) => { new playlistEditor({ items: [itemId], serverId: serverId @@ -350,7 +352,7 @@ import actionsheet from 'actionsheet'; }); break; case 'download': - import('fileDownloader').then((fileDownloader) => { + import('../scripts/fileDownloader').then((fileDownloader) => { const downloadHref = apiClient.getItemDownloadUrl(itemId); fileDownloader.download([{ url: downloadHref, @@ -372,9 +374,7 @@ import actionsheet from 'actionsheet'; textArea.select(); if (document.execCommand('copy')) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('CopyStreamURLSuccess')); - }); + toast(globalize.translate('CopyStreamURLSuccess')); } else { prompt(globalize.translate('CopyStreamURL'), downloadHref); } @@ -387,9 +387,7 @@ import actionsheet from 'actionsheet'; } else { /* eslint-disable-next-line compat/compat */ navigator.clipboard.writeText(downloadHref).then(function () { - import('toast').then(({default: toast}) => { - toast(globalize.translate('CopyStreamURLSuccess')); - }); + toast(globalize.translate('CopyStreamURLSuccess')); }).catch(function () { textAreaCopy(); }); @@ -398,7 +396,7 @@ import actionsheet from 'actionsheet'; break; } case 'editsubtitles': - import('subtitleEditor').then(({default: subtitleEditor}) => { + import('./subtitleeditor/subtitleeditor').then(({default: subtitleEditor}) => { subtitleEditor.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; @@ -406,7 +404,7 @@ import actionsheet from 'actionsheet'; editItem(apiClient, item).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); break; case 'editimages': - import('imageEditor').then(({default: imageEditor}) => { + import('./imageeditor/imageeditor').then((imageEditor) => { imageEditor.show({ itemId: itemId, serverId: serverId @@ -414,12 +412,12 @@ import actionsheet from 'actionsheet'; }); break; case 'identify': - import('itemIdentifier').then(({default: itemIdentifier}) => { + import('./itemidentifier/itemidentifier').then((itemIdentifier) => { itemIdentifier.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; case 'moremediainfo': - import('itemMediaInfo').then(({default: itemMediaInfo}) => { + import('./itemMediaInfo/itemMediaInfo').then((itemMediaInfo) => { itemMediaInfo.show(itemId, serverId).then(getResolveFunction(resolve, id), getResolveFunction(resolve, id)); }); break; @@ -454,7 +452,7 @@ import actionsheet from 'actionsheet'; playbackManager.clearQueue(); break; case 'record': - import('recordingCreator').then(({default: recordingCreator}) => { + import('./recordingcreator/recordingcreator').then(({default: recordingCreator}) => { recordingCreator.show(itemId, serverId).then(getResolveFunction(resolve, id, true), getResolveFunction(resolve, id)); }); break; @@ -525,7 +523,7 @@ import actionsheet from 'actionsheet'; } function deleteTimer(apiClient, item, resolve, command) { - import('recordingHelper').then(({default: recordingHelper}) => { + import('./recordingcreator/recordinghelper').then(({default: recordingHelper}) => { const timerId = item.TimerId || item.Id; recordingHelper.cancelTimerWithConfirmation(timerId, item.ServerId).then(function () { getResolveFunction(resolve, command, true)(); @@ -534,7 +532,7 @@ import actionsheet from 'actionsheet'; } function deleteSeriesTimer(apiClient, item, resolve, command) { - import('recordingHelper').then(({default: recordingHelper}) => { + import('./recordingcreator/recordinghelper').then(({default: recordingHelper}) => { recordingHelper.cancelSeriesTimerWithConfirmation(item.Id, item.ServerId).then(function () { getResolveFunction(resolve, command, true)(); }); @@ -568,15 +566,15 @@ import actionsheet from 'actionsheet'; const serverId = apiClient.serverInfo().Id; if (item.Type === 'Timer') { - import('recordingEditor').then(({default: recordingEditor}) => { + import('./recordingcreator/recordingeditor').then(({default: recordingEditor}) => { recordingEditor.show(item.Id, serverId).then(resolve, reject); }); } else if (item.Type === 'SeriesTimer') { - import('seriesRecordingEditor').then(({default: recordingEditor}) => { + import('./recordingcreator/seriesrecordingeditor').then(({default: recordingEditor}) => { recordingEditor.show(item.Id, serverId).then(resolve, reject); }); } else { - import('metadataEditor').then(({default: metadataEditor}) => { + import('./metadataEditor/metadataEditor').then(({default: metadataEditor}) => { metadataEditor.show(item.Id, serverId).then(resolve, reject); }); } @@ -585,7 +583,7 @@ import actionsheet from 'actionsheet'; function deleteItem(apiClient, item) { return new Promise(function (resolve, reject) { - import('deleteHelper').then(({default: deleteHelper}) => { + import('../scripts/deleteHelper').then((deleteHelper) => { deleteHelper.deleteItem({ item: item, navigate: false @@ -597,7 +595,7 @@ import actionsheet from 'actionsheet'; } function refresh(apiClient, item) { - import('refreshDialog').then(({default: refreshDialog}) => { + import('./refreshdialog/refreshdialog').then(({default: refreshDialog}) => { new refreshDialog({ itemIds: [item.Id], serverId: apiClient.serverInfo().Id, diff --git a/src/components/itemHelper.js b/src/components/itemHelper.js index 7d1ac0e110..de771546b0 100644 --- a/src/components/itemHelper.js +++ b/src/components/itemHelper.js @@ -1,5 +1,5 @@ -import appHost from 'apphost'; -import globalize from 'globalize'; +import { appHost } from './apphost'; +import globalize from '../scripts/globalize'; export function getDisplayName(item, options = {}) { if (!item) { diff --git a/src/components/itemMediaInfo/itemMediaInfo.js b/src/components/itemMediaInfo/itemMediaInfo.js index bd3a157dab..2094bcdacc 100644 --- a/src/components/itemMediaInfo/itemMediaInfo.js +++ b/src/components/itemMediaInfo/itemMediaInfo.js @@ -5,17 +5,18 @@ * @module components/itemMediaInfo/itemMediaInfo */ -import dialogHelper from 'dialogHelper'; -import layoutManager from 'layoutManager'; -import globalize from 'globalize'; -import loading from 'loading'; -import 'emby-select'; -import 'listViewStyle'; -import 'paper-icon-button-light'; -import 'css!./../formdialog'; -import 'material-icons'; -import 'emby-button'; -import 'flexStyles'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import layoutManager from '../layoutManager'; +import globalize from '../../scripts/globalize'; +import loading from '../loading/loading'; +import '../../elements/emby-select/emby-select'; +import '../listview/listview.css'; +import '../../elements/emby-button/emby-button'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../formdialog.css'; +import 'material-design-icons-iconfont'; +import '../../assets/css/flexstyles.scss'; +import ServerConnections from '../ServerConnections'; function setMediaInfo(user, page, item) { let html = item.MediaSources.map(version => { @@ -162,7 +163,7 @@ import 'flexStyles'; } function loadMediaInfo(itemId, serverId, template) { - const apiClient = window.connectionManager.getApiClient(serverId); + const apiClient = ServerConnections.getApiClient(serverId); return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(item => { const dialogOptions = { size: 'small', @@ -193,7 +194,7 @@ import 'flexStyles'; export function show(itemId, serverId) { loading.show(); - return import('text!./itemMediaInfo.template.html').then(({default: template}) => { + return import('./itemMediaInfo.template.html').then(({default: template}) => { return new Promise((resolve, reject) => { loadMediaInfo(itemId, serverId, template).then(resolve, reject); }); diff --git a/src/components/itemidentifier/itemidentifier.js b/src/components/itemidentifier/itemidentifier.js index 956cbb4f64..382226478f 100644 --- a/src/components/itemidentifier/itemidentifier.js +++ b/src/components/itemidentifier/itemidentifier.js @@ -5,19 +5,21 @@ * @module components/itemidentifier/itemidentifier */ -import dialogHelper from 'dialogHelper'; -import loading from 'loading'; -import globalize from 'globalize'; -import scrollHelper from 'scrollHelper'; -import layoutManager from 'layoutManager'; -import focusManager from 'focusManager'; -import browser from 'browser'; -import 'emby-input'; -import 'emby-checkbox'; -import 'paper-icon-button-light'; -import 'css!./../formdialog'; -import 'material-icons'; -import 'cardStyle'; +import dialogHelper from '../dialogHelper/dialogHelper'; +import loading from '../loading/loading'; +import globalize from '../../scripts/globalize'; +import scrollHelper from '../../scripts/scrollHelper'; +import layoutManager from '../layoutManager'; +import focusManager from '../focusManager'; +import browser from '../../scripts/browser'; +import '../../elements/emby-input/emby-input'; +import '../../elements/emby-checkbox/emby-checkbox'; +import '../../elements/emby-button/paper-icon-button-light'; +import '../formdialog.css'; +import 'material-design-icons-iconfont'; +import '../cardbuilder/card.css'; +import ServerConnections from '../ServerConnections'; +import toast from '../toast/toast'; const enableFocusTransform = !browser.slow && !browser.edge; @@ -30,7 +32,7 @@ import 'cardStyle'; let currentSearchResult; function getApiClient() { - return window.connectionManager.getApiClient(currentServerId); + return ServerConnections.getApiClient(currentServerId); } function searchForIdentificationResults(page) { @@ -67,9 +69,7 @@ import 'cardStyle'; } if (!hasId && !lookupInfo.Name) { - import('toast').then(({default: toast}) => { - toast(globalize.translate('PleaseEnterNameOrId')); - }); + toast(globalize.translate('PleaseEnterNameOrId')); return; } @@ -334,7 +334,7 @@ import 'cardStyle'; function showEditor(itemId) { loading.show(); - return import('text!./itemidentifier.template.html').then(({default: template}) => { + return import('./itemidentifier.template.html').then(({default: template}) => { const apiClient = getApiClient(); apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(item => { @@ -416,7 +416,7 @@ import 'cardStyle'; currentItem = null; currentItemType = itemType; - return import('text!./itemidentifier.template.html').then(({default: template}) => { + return import('./itemidentifier.template.html').then(({default: template}) => { const dialogOptions = { size: 'small', removeOnClose: true, diff --git a/src/components/itemsrefresher.js b/src/components/itemsrefresher.js index 3883e6e490..b5a73f5c45 100644 --- a/src/components/itemsrefresher.js +++ b/src/components/itemsrefresher.js @@ -1,6 +1,6 @@ -import playbackManager from 'playbackManager'; -import serverNotifications from 'serverNotifications'; -import events from 'events'; +import { playbackManager } from './playback/playbackmanager'; +import serverNotifications from '../scripts/serverNotifications'; +import { Events } from 'jellyfin-apiclient'; function onUserDataChanged(e, apiClient, userData) { const instance = this; @@ -110,7 +110,7 @@ function onPlaybackStopped(e, stopInfo) { function addNotificationEvent(instance, name, handler, owner) { const localHandler = handler.bind(instance); owner = owner || serverNotifications; - events.on(owner, name, localHandler); + Events.on(owner, name, localHandler); instance['event_' + name] = localHandler; } @@ -118,7 +118,7 @@ function removeNotificationEvent(instance, name, owner) { const handler = instance['event_' + name]; if (handler) { owner = owner || serverNotifications; - events.off(owner, name, handler); + Events.off(owner, name, handler); instance['event_' + name] = null; } } diff --git a/src/components/layoutManager.js b/src/components/layoutManager.js index 88a7265f8b..7268b3914b 100644 --- a/src/components/layoutManager.js +++ b/src/components/layoutManager.js @@ -1,6 +1,8 @@ -import browser from 'browser'; -import appSettings from 'appSettings'; -import events from 'events'; + +import { appHost } from './apphost'; +import browser from '../scripts/browser'; +import appSettings from '../scripts/settings/appSettings'; +import { Events } from 'jellyfin-apiclient'; function setLayout(instance, layout, selectedLayout) { if (layout === selectedLayout) { @@ -30,10 +32,10 @@ class LayoutManager { } } - events.trigger(this, 'modechange'); + Events.trigger(this, 'modechange'); } - getSavedLayout(layout) { + getSavedLayout() { return appSettings.get('layout'); } @@ -58,4 +60,12 @@ class LayoutManager { } } -export default new LayoutManager(); +const layoutManager = new LayoutManager(); + +if (appHost.getDefaultLayout) { + layoutManager.defaultLayout = appHost.getDefaultLayout(); +} + +layoutManager.init(); + +export default layoutManager; diff --git a/src/components/libraryoptionseditor/libraryoptionseditor.js b/src/components/libraryoptionseditor/libraryoptionseditor.js index 91dbe5ab9e..9b20cfff25 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 => { @@ -258,11 +258,9 @@ import 'emby-input'; elem.innerHTML = html; if (html) { elem.classList.remove('hide'); - page.querySelector('.chkDownloadImagesInAdvanceContainer').classList.remove('hide'); page.querySelector('.chkSaveLocalContainer').classList.remove('hide'); } else { elem.classList.add('hide'); - page.querySelector('.chkDownloadImagesInAdvanceContainer').classList.add('hide'); page.querySelector('.chkSaveLocalContainer').classList.add('hide'); } return true; @@ -306,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 = { @@ -364,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'))]; @@ -378,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'); @@ -507,7 +499,6 @@ import 'emby-input'; EnableRealtimeMonitor: parent.querySelector('.chkEnableRealtimeMonitor').checked, ExtractChapterImagesDuringLibraryScan: parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked, EnableChapterImageExtraction: parent.querySelector('.chkExtractChapterImages').checked, - DownloadImagesInAdvance: parent.querySelector('#chkDownloadImagesInAdvance').checked, EnableInternetProviders: true, SaveLocalMetadata: parent.querySelector('#chkSaveLocal').checked, EnableAutomaticSeriesGrouping: parent.querySelector('.chkAutomaticallyGroupSeries').checked, @@ -564,7 +555,6 @@ import 'emby-input'; parent.querySelector('.chkEnableRealtimeMonitor').checked = options.EnableRealtimeMonitor; parent.querySelector('.chkExtractChaptersDuringLibraryScan').checked = options.ExtractChapterImagesDuringLibraryScan; parent.querySelector('.chkExtractChapterImages').checked = options.EnableChapterImageExtraction; - parent.querySelector('#chkDownloadImagesInAdvance').checked = options.DownloadImagesInAdvance; parent.querySelector('#chkSaveLocal').checked = options.SaveLocalMetadata; parent.querySelector('.chkAutomaticallyGroupSeries').checked = options.EnableAutomaticSeriesGrouping; parent.querySelector('#chkEnableEmbeddedTitles').checked = options.EnableEmbeddedTitles; @@ -593,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 9f704e13e8..bd70bcb54e 100644 --- a/src/components/libraryoptionseditor/libraryoptionseditor.template.html +++ b/src/components/libraryoptionseditor/libraryoptionseditor.template.html @@ -1,8 +1,3 @@ -

${HeaderLibrarySettings}

@@ -69,14 +64,6 @@
${LabelSaveLocalMetadataHelp}
-
- -
${OptionDownloadImagesInAdvanceHelp}
-
-
'; @@ -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 17e28b9bdb..18404d605f 100644 --- a/src/controllers/dashboard/devices/device.js +++ b/src/controllers/dashboard/devices/device.js @@ -1,7 +1,8 @@ -import loading from 'loading'; -import dom from 'dom'; -import 'emby-input'; -import 'emby-button'; +import loading from '../../../components/loading/loading'; +import dom from '../../../scripts/dom'; +import '../../../elements/emby-input/emby-input'; +import '../../../elements/emby-button/emby-button'; +import Dashboard from '../../../scripts/clientUtils'; /* eslint-disable indent */ diff --git a/src/controllers/dashboard/devices/devices.js b/src/controllers/dashboard/devices/devices.js index c6e7281645..e1eb99e677 100644 --- a/src/controllers/dashboard/devices/devices.js +++ b/src/controllers/dashboard/devices/devices.js @@ -1,12 +1,14 @@ -import loading from 'loading'; -import dom from 'dom'; -import globalize from 'globalize'; -import imageHelper from 'scripts/imagehelper'; -import * as datefns from 'date-fns'; -import dfnshelper from 'dfnshelper'; -import 'emby-button'; -import 'emby-itemscontainer'; -import 'cardStyle'; +import loading from '../../../components/loading/loading'; +import dom from '../../../scripts/dom'; +import globalize from '../../../scripts/globalize'; +import imageHelper from '../../../scripts/imagehelper'; +import { formatDistanceToNow } from 'date-fns'; +import { localeWithSuffix } from '../../../scripts/dfnshelper'; +import '../../../elements/emby-button/emby-button'; +import '../../../elements/emby-itemscontainer/emby-itemscontainer'; +import '../../../components/cardbuilder/card.css'; +import Dashboard from '../../../scripts/clientUtils'; +import confirm from '../../../components/confirm/confirm'; /* eslint-disable indent */ @@ -20,14 +22,12 @@ import 'cardStyle'; function deleteAllDevices(page) { const msg = globalize.translate('DeleteDevicesConfirmation'); - require(['confirm'], async function (confirm) { - await confirm({ - text: msg, - title: globalize.translate('HeaderDeleteDevices'), - confirmText: globalize.translate('ButtonDelete'), - primary: 'delete' - }); - + confirm({ + text: msg, + title: globalize.translate('HeaderDeleteDevices'), + confirmText: globalize.translate('ButtonDelete'), + primary: 'delete' + }).then(async () => { loading.show(); await Promise.all( deviceIds.filter(canDelete).map((id) => ApiClient.deleteDevice(id)) @@ -39,17 +39,15 @@ import 'cardStyle'; function deleteDevice(page, id) { const msg = globalize.translate('DeleteDeviceConfirmation'); - import('confirm').then(({default: confirm}) => { - confirm({ - text: msg, - title: globalize.translate('HeaderDeleteDevice'), - confirmText: globalize.translate('Delete'), - primary: 'delete' - }).then(async () => { - loading.show(); - await ApiClient.deleteDevice(id); - loadData(page); - }); + confirm({ + text: msg, + title: globalize.translate('HeaderDeleteDevice'), + confirmText: globalize.translate('Delete'), + primary: 'delete' + }).then(async () => { + loading.show(); + await ApiClient.deleteDevice(id); + loadData(page); }); } @@ -72,7 +70,7 @@ import 'cardStyle'; }); } - import('actionsheet').then(({default: actionsheet}) => { + import('../../../components/actionSheet/actionSheet').then(({default: actionsheet}) => { actionsheet.show({ items: menuItems, positionTo: btn, @@ -128,7 +126,7 @@ import 'cardStyle'; if (device.LastUserName) { deviceHtml += device.LastUserName; - deviceHtml += ', ' + datefns.formatDistanceToNow(Date.parse(device.DateLastActivity), dfnshelper.localeWithSuffix); + deviceHtml += ', ' + formatDistanceToNow(Date.parse(device.DateLastActivity), localeWithSuffix); } deviceHtml += ' '; diff --git a/src/controllers/dashboard/dlna/profile.js b/src/controllers/dashboard/dlna/profile.js index 478b5ca878..74b4f2a25c 100644 --- a/src/controllers/dashboard/dlna/profile.js +++ b/src/controllers/dashboard/dlna/profile.js @@ -1,11 +1,13 @@ -import $ from 'jQuery'; -import loading from 'loading'; -import globalize from 'globalize'; -import 'emby-select'; -import 'emby-button'; -import 'emby-input'; -import 'emby-checkbox'; -import 'listViewStyle'; +import 'jquery'; +import loading from '../../../components/loading/loading'; +import globalize from '../../../scripts/globalize'; +import '../../../elements/emby-select/emby-select'; +import '../../../elements/emby-button/emby-button'; +import '../../../elements/emby-input/emby-input'; +import '../../../elements/emby-checkbox/emby-checkbox'; +import '../../../components/listview/listview.css'; +import Dashboard from '../../../scripts/clientUtils'; +import toast from '../../../components/toast/toast'; /* eslint-disable indent */ @@ -633,9 +635,7 @@ import 'listViewStyle'; data: JSON.stringify(profile), contentType: 'application/json' }).then(function () { - import('toast').then(({default: toast}) => { - toast('Settings saved.'); - }); + toast('Settings saved.'); }, Dashboard.processErrorResponse); } else { ApiClient.ajax({ diff --git a/src/controllers/dashboard/dlna/profiles.js b/src/controllers/dashboard/dlna/profiles.js index 4eb830df6f..e507fc4e7c 100644 --- a/src/controllers/dashboard/dlna/profiles.js +++ b/src/controllers/dashboard/dlna/profiles.js @@ -1,9 +1,10 @@ -import $ from 'jQuery'; -import globalize from 'globalize'; -import loading from 'loading'; -import libraryMenu from 'libraryMenu'; -import 'listViewStyle'; -import 'emby-button'; +import 'jquery'; +import globalize from '../../../scripts/globalize'; +import loading from '../../../components/loading/loading'; +import libraryMenu from '../../../scripts/libraryMenu'; +import '../../../components/listview/listview.css'; +import '../../../elements/emby-button/emby-button'; +import confirm from '../../../components/confirm/confirm'; /* eslint-disable indent */ @@ -64,16 +65,14 @@ import 'emby-button'; } function deleteProfile(page, id) { - import('confirm').then(({default: confirm}) => { - confirm(globalize.translate('MessageConfirmProfileDeletion'), globalize.translate('HeaderConfirmProfileDeletion')).then(function () { - loading.show(); - ApiClient.ajax({ - type: 'DELETE', - url: ApiClient.getUrl('Dlna/Profiles/' + id) - }).then(function () { - loading.hide(); - loadProfiles(page); - }); + confirm(globalize.translate('MessageConfirmProfileDeletion'), globalize.translate('HeaderConfirmProfileDeletion')).then(function () { + loading.show(); + ApiClient.ajax({ + type: 'DELETE', + url: ApiClient.getUrl('Dlna/Profiles/' + id) + }).then(function () { + loading.hide(); + loadProfiles(page); }); }); } diff --git a/src/controllers/dashboard/dlna/settings.js b/src/controllers/dashboard/dlna/settings.js index fb93441a55..33c35b9644 100644 --- a/src/controllers/dashboard/dlna/settings.js +++ b/src/controllers/dashboard/dlna/settings.js @@ -1,7 +1,8 @@ -import $ from 'jQuery'; -import loading from 'loading'; -import libraryMenu from 'libraryMenu'; -import globalize from 'globalize'; +import 'jquery'; +import loading from '../../../components/loading/loading'; +import libraryMenu from '../../../scripts/libraryMenu'; +import globalize from '../../../scripts/globalize'; +import Dashboard from '../../../scripts/clientUtils'; /* eslint-disable indent */ diff --git a/src/controllers/dashboard/encodingsettings.html b/src/controllers/dashboard/encodingsettings.html index 5afbdad24f..4c7b8c3a57 100644 --- a/src/controllers/dashboard/encodingsettings.html +++ b/src/controllers/dashboard/encodingsettings.html @@ -94,6 +94,15 @@ +
+
+ +
+
+
${LabelTranscodingTempPathHelp}
+
+
+
+ +
+ +
+
${LabelFallbackFontPathHelp}
+
+
+ +
${EnableFallbackFontHelp}
+
${LabelDownMixAudioScaleHelp}
@@ -202,11 +227,13 @@
${EncoderPresetHelp}
+
+ +
${H264CrfHelp}
-
-
+
@@ -46,8 +46,6 @@
- -
diff --git a/src/controllers/playback/video/index.js b/src/controllers/playback/video/index.js index 492f4aeee9..0cc37fa985 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 ffb7fbac0b..b96c6f4b10 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 472d1274a9..aabfdbcca8 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 5b92e255da..203605b8bb 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 1592e6b112..35191a1954 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 d4f7df5bbd..cc84d76fae 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 6a590fc318..32674a8062 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 6dd633d7b0..3dd08e3f76 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 3a17fd7997..b29898730b 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 */ @@ -17,7 +17,7 @@ import 'emby-button'; if (!pageData) { pageData = data[key] = { query: { - SortBy: 'SortName', + SortBy: 'Random', SortOrder: 'Ascending', IncludeItemTypes: 'Series', Recursive: true, diff --git a/src/controllers/shows/tvrecommended.js b/src/controllers/shows/tvrecommended.js index db7bef2d50..c028a118d8 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 949c994606..7ef60e21db 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 4be717fb7f..5a0276e5f4 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 f9df3df343..897c02b5a8 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 54f71ad571..322da73772 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 539365ff97..a1bb503adb 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 88cf28a216..cfc5b5f4a5 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 34a5ae0b1d..a44e96ec10 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 631253d019..4398de36e2 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 1600765ed5..162f3d9783 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 efa2f1bead..deb86535df 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 5451d6665c..446d01e61c 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 b967d668ad..c5689e73bb 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 6e3a82cd9b..4a88e861f0 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 3cd53b4ceb..3f524a870a 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 ec587fec8e..03566ac7ee 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 213bbc8e7f..3d911c6e37 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 f6c754fedb..ff817a31b4 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 d44c58ed48..0af079ec06 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 c87e73d48f..ca34a48554 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 3a71e29a6f..60f459f32b 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 51f3fc5be9..c3a15ce12e 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 7d8f941603..3d84ea6bed 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', + self.sortable = new Sortable(self, { + draggable: '.listItem', + handle: '.listViewDragHandle', - // dragging ended - onEnd: function (evt) { - return onDrop(evt, self); - } - }); + // 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 8d17ddf9ff..d177ed0df6 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 63b9f73f10..af463ebe4e 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 7c468a84a6..46cf4989d8 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 865d918b45..18ab1aa9a1 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 f7665c0618..639cb627cd 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 d7133e317a..396f1284e8 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 0629a74e52..4d336a63a9 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 01221b6cae..f7503d4fd5 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 555394af0d..828237a63d 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 7e16e31dd4..ebe46f9165 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 c14724346a..18158d8703 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 5e78b38dd3..7539ae398d 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 6dcf6783d1..214d86a02c 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 0000000000..b11bbddb5e --- /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 68a7dda73b..bdb049a23c 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 de6469c743..f2baf51572 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 917d8f48a3..7db319a34e 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 c56777f378..7d7a8b89be 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 165c1fa9ac..db498c10fd 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 e92fa4471b..0000000000 --- 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 f61a0055af..41341066b3 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 8cdf3db5e6..89e110cec3 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 bc301f01af..cae6e8b67c 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 6f413fac50..975e6f8a62 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 32f96c8e2e..1b1ec39d18 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'); } /** @@ -1030,15 +1024,21 @@ function tryRemoveElement(elem) { * @private */ renderSsaAss(videoElement, track, item) { + const avaliableFonts = []; const attachments = this._currentPlayOptions.mediaSource.MediaAttachments || []; - const apiClient = window.connectionManager.getApiClient(item); + attachments.map(function (i) { + // embedded font url + return avaliableFonts.push(i.DeliveryUrl); + }); + const apiClient = ServerConnections.getApiClient(item); + const fallbackFontList = apiClient.getUrl('/FallbackFont/Fonts', { + api_key: apiClient.accessToken() + }); const htmlVideoPlayer = this; const options = { video: videoElement, subUrl: getTextTrackUrl(track, item), - fonts: attachments.map(function (i) { - return apiClient.getUrl(i.DeliveryUrl); - }), + fonts: avaliableFonts, workerUrl: `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker.js`, legacyWorkerUrl: `${appRouter.baseUrl()}/libraries/subtitles-octopus-worker-legacy.js`, onError() { @@ -1058,8 +1058,22 @@ function tryRemoveElement(elem) { resizeVariation: 0.2, renderAhead: 90 }; - import('JavascriptSubtitlesOctopus').then(({default: SubtitlesOctopus}) => { - this.#currentSubtitlesOctopus = new SubtitlesOctopus(options); + import('libass-wasm').then(({default: SubtitlesOctopus}) => { + apiClient.getNamedConfiguration('encoding').then(config => { + if (config.EnableFallbackFont) { + apiClient.getJSON(fallbackFontList).then((fontFiles = []) => { + fontFiles.forEach(font => { + const fontUrl = apiClient.getUrl(`/FallbackFont/Fonts/${font.Name}`, { + api_key: apiClient.accessToken() + }); + avaliableFonts.push(fontUrl); + }); + this.#currentSubtitlesOctopus = new SubtitlesOctopus(options); + }); + } else { + this.#currentSubtitlesOctopus = new SubtitlesOctopus(options); + } + }); }); } @@ -1114,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 @@ -1135,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}`); @@ -1190,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`); @@ -1282,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'); @@ -1328,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 @@ -1341,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')); @@ -1519,7 +1535,7 @@ function tryRemoveElement(elem) { return false; } - static isAirPlayEnabled() { + isAirPlayEnabled() { if (document.AirPlayEnabled) { return !!document.AirplayElement; } @@ -1561,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'); } } @@ -1721,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 61b8f8a6d6..5067546c7f 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); - }; + 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(); - } + 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 480eb80546..5bface5911 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); + const downloadHref = apiClient.getItemDownloadUrl(item.Id); - this.bindEvents(); - pdfjs.GlobalWorkerOptions.workerSrc = appRouter.baseUrl() + '/libraries/pdf.worker.js'; + this.bindEvents(); + 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 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); - } + const percentageTicks = options.startPositionTicks / 10000; + if (percentageTicks !== 0) { + this.loadPage(percentageTicks); + this.progress = percentageTicks; + } else { + this.loadPage(1); + } - return resolve(); - }); + 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 c40477778e..2324ef8932 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 c64b63332b..6ed64342a6 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 619266b01f..b68154b19c 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 eed75a8116..6362cb3cbb 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