diff --git a/.ci/azure-pipelines-build.yml b/.ci/azure-pipelines-build.yml index 026afe76a6..128fe54605 100644 --- a/.ci/azure-pipelines-build.yml +++ b/.ci/azure-pipelines-build.yml @@ -8,6 +8,8 @@ jobs: BuildConfiguration: development Production: BuildConfiguration: production + Standalone: + BuildConfiguration: standalone pool: vmImage: 'ubuntu-latest' @@ -37,6 +39,10 @@ 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/deployment/build.portable b/deployment/build.portable index 7161ae6c2d..873c145fab 100755 --- a/deployment/build.portable +++ b/deployment/build.portable @@ -15,8 +15,8 @@ fi # build archives npx yarn install -mv dist jellyfin-web-${version} -tar -czf jellyfin-web-${version}-portable.tar.gz jellyfin-web-${version} +mv dist jellyfin-web_${version} +tar -czf jellyfin-web_${version}_portable.tar.gz jellyfin-web_${version} rm -rf dist # move the artifacts diff --git a/gulpfile.js b/gulpfile.js index 03826e8b6e..84f4558e6a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -16,6 +16,7 @@ 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'); @@ -67,7 +68,7 @@ function serve() { } }); - watch(options.apploader.query, apploader()); + watch(options.apploader.query, apploader(true)); watch('src/bundle.js', webpack); @@ -130,12 +131,18 @@ function javascript(query) { .pipe(browserSync.stream()); } -function apploader() { - return src(options.apploader.query, { base: './src/' }) - .pipe(concat('scripts/apploader.js')) - .pipe(pipelineJavascript()) - .pipe(dest('dist/')) - .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() { @@ -183,5 +190,10 @@ function injectBundle() { .pipe(browserSync.stream()); } -exports.default = series(clean, parallel(javascript, apploader, webpack, css, html, images, copy), injectBundle); -exports.serve = series(exports.default, serve); +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); diff --git a/package.json b/package.json index a1f2fe9a0a..846983de2d 100644 --- a/package.json +++ b/package.json @@ -5,13 +5,13 @@ "repository": "https://github.com/jellyfin/jellyfin-web", "license": "GPL-2.0-or-later", "devDependencies": { - "@babel/core": "^7.10.2", + "@babel/core": "^7.10.3", "@babel/plugin-proposal-class-properties": "^7.10.1", "@babel/plugin-proposal-private-methods": "^7.10.1", "@babel/plugin-transform-modules-amd": "^7.9.6", "@babel/polyfill": "^7.8.7", - "@babel/preset-env": "^7.10.2", - "autoprefixer": "^9.8.0", + "@babel/preset-env": "^7.10.3", + "autoprefixer": "^9.8.2", "babel-eslint": "^11.0.0-beta.2", "babel-loader": "^8.0.6", "browser-sync": "^2.26.7", @@ -44,7 +44,7 @@ "postcss-loader": "^3.0.0", "postcss-preset-env": "^6.7.0", "style-loader": "^1.1.3", - "stylelint": "^13.6.0", + "stylelint": "^13.6.1", "stylelint-config-rational-order": "^0.1.2", "stylelint-no-browser-hacks": "^1.2.1", "stylelint-order": "^4.1.0", @@ -60,7 +60,7 @@ "date-fns": "^2.14.0", "document-register-element": "^1.14.3", "epubjs": "^0.3.85", - "fast-text-encoding": "^1.0.1", + "fast-text-encoding": "^1.0.3", "flv.js": "^1.5.0", "headroom.js": "^0.11.0", "hls.js": "^0.13.1", @@ -69,7 +69,7 @@ "jellyfin-apiclient": "^1.2.2", "jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto", "jquery": "^3.5.1", - "jstree": "^3.3.7", + "jstree": "^3.3.10", "libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-smarttv", "material-design-icons-iconfont": "^5.0.1", "native-promise-only": "^0.8.0-a", @@ -77,9 +77,9 @@ "query-string": "^6.13.1", "resize-observer-polyfill": "^1.5.1", "screenfull": "^5.0.2", - "shaka-player": "^2.5.13", + "shaka-player": "^3.0.1", "sortablejs": "^1.10.2", - "swiper": "^5.4.3", + "swiper": "^5.4.5", "webcomponents.js": "^0.7.24", "whatwg-fetch": "^3.0.0" }, @@ -98,9 +98,11 @@ "src/components/cardbuilder/peoplecardbuilder.js", "src/components/dialog/dialog.js", "src/components/dialogHelper/dialogHelper.js", + "src/components/channelMapper/channelMapper.js", "src/components/images/imageLoader.js", "src/components/indicators/indicators.js", "src/components/lazyLoader/lazyLoaderIntersectionObserver.js", + "src/components/listview/listview.js", "src/components/playback/brightnessosd.js", "src/components/playback/mediasession.js", "src/components/playback/nowplayinghelper.js", @@ -113,11 +115,13 @@ "src/components/playmenu.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", + "src/components/shortcuts.js", "src/components/syncPlay/groupSelectionMenu.js", "src/components/syncPlay/playbackPermissionManager.js", "src/components/syncPlay/syncPlayManager.js", "src/components/syncPlay/timeSyncManager.js", "src/controllers/dashboard/logs.js", + "src/controllers/dashboard/plugins/repositories.js", "src/plugins/bookPlayer/plugin.js", "src/plugins/bookPlayer/tableOfContents.js", "src/plugins/photoPlayer/plugin.js", @@ -164,6 +168,7 @@ "prepare": "gulp --production", "build:development": "gulp --development", "build:production": "gulp --production", + "build:standalone": "gulp standalone --development", "lint": "eslint \".\"", "stylelint": "stylelint \"src/**/*.css\"" } diff --git a/src/assets/css/site.css b/src/assets/css/site.css index d489f77f01..9fbd8a4fca 100644 --- a/src/assets/css/site.css +++ b/src/assets/css/site.css @@ -121,6 +121,11 @@ div[data-role=page] { transform: translateY(-100%); } +.drawerContent { + /* make sure the bottom of the drawer is visible when music is playing */ + padding-bottom: 4em; +} + .force-scroll { overflow-y: scroll; } diff --git a/src/components/cardbuilder/card.css b/src/components/cardbuilder/card.css index c24fcf6ba6..d77fe5660c 100644 --- a/src/components/cardbuilder/card.css +++ b/src/components/cardbuilder/card.css @@ -192,9 +192,14 @@ button::-moz-focus-inner { /* Needed in case this is a button */ display: block; - - /* Needed in case this is a button */ margin: 0 !important; + border: 0 !important; + padding: 0 !important; + cursor: pointer; + color: inherit; + width: 100%; + font-family: inherit; + font-size: inherit; /* Needed in safari */ height: 100%; @@ -203,19 +208,12 @@ button::-moz-focus-inner { contain: strict; } -.cardContent-button { - border: 0 !important; - padding: 0 !important; - cursor: pointer; - color: inherit; - width: 100%; - vertical-align: middle; - font-family: inherit; - font-size: inherit; +.cardContent:not(.defaultCardBackground) { + background-color: transparent; } -.cardContent-button:not(.defaultCardBackground) { - background-color: transparent; +.cardBox:not(.visualCardBox) .cardPadder { + background-color: #242424; } .visualCardBox .cardContent { @@ -223,7 +221,8 @@ button::-moz-focus-inner { border-bottom-right-radius: 0; } -.cardContent-shadow { +.cardContent-shadow, +.cardBox:not(.visualCardBox) .cardPadder { box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37); } diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index e540a40211..52d5ed202a 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -368,9 +368,7 @@ import 'programStyles'; let apiClient; let lastServerId; - for (let i = 0; i < items.length; i++) { - - let item = items[i]; + for (const [i, item] of items.entries()) { let serverId = item.ServerId || options.serverId; if (serverId !== lastServerId) { @@ -541,7 +539,7 @@ import 'programStyles'; imgType = 'Backdrop'; imgTag = item.ParentBackdropImageTags[0]; itemId = item.ParentBackdropItemId; - } else if (item.ImageTags && item.ImageTags.Primary) { + } else if (item.ImageTags && item.ImageTags.Primary && (item.Type !== 'Episode' || item.ChildCount !== 0)) { imgType = 'Primary'; imgTag = item.ImageTags.Primary; height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; @@ -556,7 +554,10 @@ import 'programStyles'; coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2; } } - + } else if (item.SeriesPrimaryImageTag) { + imgType = 'Primary'; + imgTag = item.SeriesPrimaryImageTag; + itemId = item.SeriesId; } else if (item.PrimaryImageTag) { imgType = 'Primary'; imgTag = item.PrimaryImageTag; @@ -577,10 +578,6 @@ import 'programStyles'; imgType = 'Primary'; imgTag = item.ParentPrimaryImageTag; itemId = item.ParentPrimaryImageItemId; - } else if (item.SeriesPrimaryImageTag) { - imgType = 'Primary'; - imgTag = item.SeriesPrimaryImageTag; - itemId = item.SeriesId; } else if (item.AlbumId && item.AlbumPrimaryImageTag) { imgType = 'Primary'; imgTag = item.AlbumPrimaryImageTag; @@ -1370,9 +1367,6 @@ import 'programStyles'; let cardScalableClose = ''; let cardContentClass = 'cardContent'; - if (!options.cardLayout) { - cardContentClass += ' cardContent-shadow'; - } let blurhashAttrib = ''; if (blurhash && blurhash.length > 0) { @@ -1380,21 +1374,20 @@ import 'programStyles'; } if (layoutManager.tv) { - // Don't use the IMG tag with safari because it puts a white border around it cardImageContainerOpen = imgUrl ? ('