diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml index 1b16b94b62..7c7801b866 100644 --- a/.ci/azure-pipelines.yml +++ b/.ci/azure-pipelines.yml @@ -7,38 +7,70 @@ trigger: tags: include: - '*' +pr: + branches: + include: + - '*' jobs: - - job: main_build - displayName: 'Main Build' - - dependsOn: lint - condition: succeeded() + - job: build + displayName: 'Build' pool: vmImage: 'ubuntu-latest' + strategy: + matrix: + Development: + BuildConfiguration: development + Production: + BuildConfiguration: production + Standalone: + BuildConfiguration: standalone + maxParallel: 3 + steps: - task: NodeTool@0 displayName: 'Install Node' inputs: - versionSpec: '10.x' + versionSpec: '12.x' - - script: 'yarn install' + - task: Cache@2 + displayName: 'Check Cache' + 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') + + - script: 'yarn build:development' + displayName: 'Build Development' + condition: eq(variables['BuildConfiguration'], 'development') + + - script: 'yarn build:production' + displayName: 'Build Bundle' + condition: eq(variables['BuildConfiguration'], 'production') + + - script: 'yarn build:standalone' + displayName: 'Build Standalone' + condition: eq(variables['BuildConfiguration'], 'standalone') - script: 'test -d dist' displayName: 'Check Build' - - script: 'yarn pack --filename jellyfin-web.tgz' - displayName: 'Bundle Release' + - script: 'mv dist jellyfin-web' + displayName: 'Rename Directory' + condition: succeeded() - task: PublishPipelineArtifact@1 displayName: 'Publish Release' condition: succeeded() inputs: - targetPath: '$(Build.SourcesDirectory)/jellyfin-web.tgz' - artifactName: 'jellyfin-web' + targetPath: '$(Build.SourcesDirectory)/jellyfin-web' + artifactName: 'jellyfin-web-$(BuildConfiguration)' - job: lint displayName: 'Lint' @@ -50,14 +82,21 @@ jobs: - task: NodeTool@0 displayName: 'Install Node' inputs: - versionSpec: '10.x' + versionSpec: '12.x' - - script: 'yarn install' + - task: Cache@2 + displayName: 'Check Cache' + 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') - script: 'yarn run lint' displayName: 'Run ESLint' - - script: | - yarn run stylelint - displayName: 'Run stylelint' + - script: 'yarn run stylelint' + displayName: 'Run Stylelint' diff --git a/.eslintrc.yml b/.eslintrc.yml index b215e15800..377716d53c 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -3,25 +3,75 @@ env: browser: true amd: true +parserOptions: + ecmaVersion: 6 + sourceType: module + ecmaFeatures: + impliedStrict: true + +globals: + # New browser globals + DataView: readonly + MediaMetadata: readonly + Promise: readonly + # Deprecated browser globals + DocumentTouch: readonly + # Tizen globals + tizen: readonly + webapis: readonly + # WebOS globals + webOS: readonly + # Dependency globals + $: readonly + jQuery: readonly + queryString: readonly + requirejs: readonly + # Jellyfin globals + ApiClient: writable + AppInfo: writable + chrome: writable + ConnectionManager: writable + DlnaProfilePage: writable + Dashboard: writable + DashboardPage: writable + Emby: readonly + Events: writable + getParameterByName: writable + getWindowLocationSearch: writable + Globalize: writable + Hls: writable + humaneDate: writable + humaneElapsed: writable + LibraryMenu: writable + LinkParser: writable + LiveTvHelpers: writable + MetadataEditor: writable + pageClassOn: writable + pageIdOn: writable + PlaylistViewer: writable + UserParentalControlPage: writable + Windows: readonly + +extends: + - eslint:recommended + rules: block-spacing: ["error"] brace-style: ["error"] comma-dangle: ["error", "never"] comma-spacing: ["error"] - eol-last: ["off"] + eol-last: ["error"] indent: ["error", 4, { "SwitchCase": 1 }] keyword-spacing: ["error"] - line-comment-position: ["off"] max-statements-per-line: ["error"] - no-empty: ["error"] - no-extra-semi: ["error"] no-floating-decimal: ["error"] no-multi-spaces: ["error"] no-multiple-empty-lines: ["error", { "max": 1 }] no-trailing-spaces: ["error"] - no-void: ["off"] one-var: ["error", "never"] - padding-line-between-statements: ["off"] - semi: ["off"] + semi: ["warn"] space-before-blocks: ["error"] - yoda: ["off"] + # TODO: Fix warnings and remove these rules + no-redeclare: ["warn"] + no-unused-vars: ["warn"] + no-useless-escape: ["warn"] diff --git a/.stylelintrc b/.stylelintrc index 93e3592099..a13acf428d 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -140,4 +140,4 @@ "value-list-comma-space-before": "never", "value-list-max-empty-lines": 0, } -} \ No newline at end of file +} diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 5a43208068..aa3ec707e3 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -33,6 +33,7 @@ - [Daniel Hartung](https://github.com/dhartung) - [Ryan Hartzell](https://github.com/ryan-hartzell) - [Thibault Nocchi](https://github.com/ThibaultNocchi) + - [MrTimscampi](https://github.com/MrTimscampi) # Emby Contributors diff --git a/README.md b/README.md index 6a80b0b09c..e2aac6b155 100644 --- a/README.md +++ b/README.md @@ -45,20 +45,37 @@ Jellyfin Web is the frontend used for most of the clients available for end user ### Dependencies - Yarn +- Gulp-cli ### Getting Started 1. Clone or download this repository. + ```sh git clone https://github.com/jellyfin/jellyfin-web.git cd jellyfin-web ``` + 2. Install build dependencies in the project directory. + ```sh yarn install ``` 3. Run the web client with webpack for local development. + ```sh yarn serve ``` + +4. Build the client with sourcemaps. + + ```sh + yarn build:development + ``` + + You can build a nginx compatible version as well. + + ```sh + yarn build:standalone + ``` \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000000..0eb5593541 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,201 @@ +'use strict'; + +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'); +const sourcemaps = require('gulp-sourcemaps'); +const mode = require('gulp-mode')({ + modes: ["development", "production"], + default: "development", + verbose: false +}); +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'); + +let config; +if (mode.production()) { + config = require('./webpack.prod.js'); +} else { + config = require('./webpack.dev.js'); +} + +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'] + }, + css: { + query: ['src/**/*.css', 'src/**/*.scss'] + }, + html: { + query: ['src/**/*.html', '!src/index.html'] + }, + images: { + query: ['src/**/*.png', 'src/**/*.jpg', 'src/**/*.gif', 'src/**/*.svg'] + }, + copy: { + query: ['src/**/*.json', 'src/**/*.ico'] + }, + injectBundle: { + query: 'src/index.html' + } +}; + +function serve() { + browserSync.init({ + server: { + baseDir: "./dist" + }, + port: 8080 + }); + + let events = ['add', 'change']; + + watch(options.javascript.query).on('all', function (event, path) { + if (events.includes(event)) { + javascript(path); + } + }); + + watch(options.apploader.query, apploader(true)); + + watch('src/bundle.js', webpack); + + watch(options.css.query).on('all', function (event, path) { + if (events.includes(event)) { + css(path); + } + }); + + watch(options.html.query).on('all', function (event, path) { + if (events.includes(event)) { + html(path); + } + }); + + watch(options.images.query).on('all', function (event, path) { + if (events.includes(event)) { + images(path); + } + }); + + watch(options.copy.query).on('all', function (event, path) { + if (events.includes(event)) { + copy(path); + } + }); + + watch(options.injectBundle.query, injectBundle); +} + +function clean() { + return del(['dist/']); +} + +let pipelineJavascript = lazypipe() + .pipe(function () { + return mode.development(sourcemaps.init({ loadMaps: true })); + }) + .pipe(function () { + return babel({ + presets: [ + ['@babel/preset-env'] + ] + }); + }) + .pipe(function () { + return terser({ + keep_fnames: true, + mangle: false + }); + }) + .pipe(function () { + return mode.development(sourcemaps.write('.')); + }); + +function javascript(query) { + return src(typeof query !== 'function' ? query : options.javascript.query, { base: './src/' }) + .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() { + return stream(config) + .pipe(dest('dist/')) + .pipe(browserSync.stream()); +} + +function css(query) { + return src(typeof query !== 'function' ? query : options.css.query, { base: './src/' }) + .pipe(mode.development(sourcemaps.init({ loadMaps: true }))) + .pipe(sass().on('error', sass.logError)) + .pipe(postcss()) + .pipe(mode.development(sourcemaps.write('.'))) + .pipe(dest('dist/')) + .pipe(browserSync.stream()); +} + +function html(query) { + return src(typeof query !== 'function' ? query : options.html.query, { base: './src/' }) + .pipe(mode.production(htmlmin({ collapseWhitespace: true }))) + .pipe(dest('dist/')) + .pipe(browserSync.stream()); +} + +function images(query) { + return src(typeof query !== 'function' ? query : options.images.query, { base: './src/' }) + .pipe(mode.production(imagemin())) + .pipe(dest('dist/')) + .pipe(browserSync.stream()); +} + +function copy(query) { + return src(typeof query !== 'function' ? query : options.copy.query, { base: './src/' }) + .pipe(dest('dist/')) + .pipe(browserSync.stream()); +} + +function injectBundle() { + return src(options.injectBundle.query, { base: './src/' }) + .pipe(inject( + src(['src/scripts/apploader.js'], { read: false }, { base: './src/' }), { relative: true } + )) + .pipe(dest('dist/')) + .pipe(browserSync.stream()); +} + +function build(standalone) { + return series(clean, parallel(javascript, apploader(standalone), webpack, css, html, images, copy), injectBundle); +} + +exports.default = build(false); +exports.standalone = build(true); +exports.serve = series(exports.standalone, serve); diff --git a/package.json b/package.json index 87050382c5..090d98f4bb 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,38 @@ "repository": "https://github.com/jellyfin/jellyfin-web", "license": "GPL-2.0-or-later", "devDependencies": { + "@babel/core": "^7.8.6", + "@babel/plugin-transform-modules-amd": "^7.8.3", + "@babel/polyfill": "^7.8.7", + "@babel/preset-env": "^7.8.6", + "autoprefixer": "^9.7.4", + "babel-loader": "^8.0.6", + "browser-sync": "^2.26.7", "clean-webpack-plugin": "^3.0.0", "copy-webpack-plugin": "^5.1.1", "css-loader": "^3.4.2", + "cssnano": "^4.1.10", + "del": "^5.1.0", "eslint": "^6.8.0", - "file-loader": "^5.0.2", - "html-webpack-plugin": "^3.2.0", + "file-loader": "^6.0.0", + "gulp": "^4.0.2", + "gulp-babel": "^8.0.0", + "gulp-cli": "^2.2.0", + "gulp-concat": "^2.6.1", + "gulp-htmlmin": "^5.0.1", + "gulp-if": "^3.0.0", + "gulp-imagemin": "^7.1.0", + "gulp-inject": "^5.0.5", + "gulp-mode": "^1.0.2", + "gulp-postcss": "^8.0.0", + "gulp-sass": "^4.0.2", + "gulp-sourcemaps": "^2.6.5", + "gulp-terser": "^1.2.0", + "html-webpack-plugin": "^4.0.2", + "lazypipe": "^1.0.2", + "node-sass": "^4.13.1", + "postcss-loader": "^3.0.0", + "postcss-preset-env": "^6.7.0", "style-loader": "^1.1.3", "stylelint": "^13.1.0", "stylelint-config-rational-order": "^0.1.2", @@ -20,10 +46,12 @@ "webpack-cli": "^3.3.10", "webpack-concat-plugin": "^3.0.0", "webpack-dev-server": "^3.10.3", - "webpack-merge": "^4.2.2" + "webpack-merge": "^4.2.2", + "webpack-stream": "^5.2.1" }, "dependencies": { "alameda": "^1.4.0", + "core-js": "^3.6.4", "document-register-element": "^1.14.3", "flv.js": "^1.5.0", "hls.js": "^0.13.1", @@ -31,18 +59,37 @@ "jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto", "jquery": "^3.4.1", "jstree": "^3.3.7", - "libass-wasm": "^2.1.1", - "libjass": "^0.11.0", + "libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf", "material-design-icons-iconfont": "^5.0.1", "native-promise-only": "^0.8.0-a", - "requirejs": "^2.3.5", + "page": "^1.11.5", + "query-string": "^6.11.1", "resize-observer-polyfill": "^1.5.1", - "shaka-player": "^2.5.9", + "shaka-player": "^2.5.10", "sortablejs": "^1.10.2", "swiper": "^5.3.1", "webcomponents.js": "^0.7.24", "whatwg-fetch": "^3.0.0" }, + "babel": { + "presets": [ + "@babel/preset-env" + ], + "overrides": [ + { + "test": [ + "src/components/cardbuilder/cardBuilder.js", + "src/components/filedownloader.js", + "src/components/filesystem.js", + "src/components/input/keyboardnavigation.js", + "src/components/sanatizefilename.js" + ], + "plugins": [ + "@babel/plugin-transform-modules-amd" + ] + } + ] + }, "browserslist": [ "last 2 Firefox versions", "last 2 Chrome versions", @@ -50,6 +97,7 @@ "last 2 Safari versions", "last 2 iOS versions", "last 2 Edge versions", + "Chrome 27", "Chrome 38", "Chrome 47", "Chrome 53", @@ -58,10 +106,12 @@ "Firefox ESR" ], "scripts": { - "serve": "webpack-dev-server --config webpack.dev.js --open", - "build": "webpack --config webpack.prod.js", + "serve": "gulp serve", + "prepare": "gulp --production", + "build:development": "gulp --development", + "build:production": "gulp --production", + "build:standalone": "gulp standalone --development", "lint": "eslint \"src\"", - "stylelint": "stylelint \"src/**/*.css\"", - "prepare": "webpack --config webpack.prod.js" + "stylelint": "stylelint \"src/**/*.css\"" } } diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000000..23159fd295 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,11 @@ +const postcssPresetEnv = require('postcss-preset-env'); +const cssnano = require('cssnano'); + +const config = () => ({ + plugins: [ + postcssPresetEnv(), + cssnano() + ] +}); + +module.exports = config diff --git a/src/assets/css/dashboard.css b/src/assets/css/dashboard.css index 8c8a9ca7f1..894d7332f4 100644 --- a/src/assets/css/dashboard.css +++ b/src/assets/css/dashboard.css @@ -63,6 +63,10 @@ progress[aria-valuenow]::before { } .adminDrawerLogo { + display: none; +} + +.layout-mobile .adminDrawerLogo { padding: 1.5em 1em 1.2em; border-bottom: 1px solid #e0e0e0; margin-bottom: 1em; @@ -161,7 +165,7 @@ div[data-role=controlgroup] a.ui-btn-active { @media all and (min-width: 40em) { .content-primary { - padding-top: 7em; + padding-top: 4.6em; } .withTabs .content-primary { diff --git a/src/assets/css/fonts.css b/src/assets/css/fonts.css index f125bc6a03..cb0da0f80f 100644 --- a/src/assets/css/fonts.css +++ b/src/assets/css/fonts.css @@ -29,7 +29,7 @@ h3 { } .layout-tv { - font-size: 2.5vh; + font-size: 130%; } .layout-mobile { diff --git a/src/assets/css/librarybrowser.css b/src/assets/css/librarybrowser.css index 8010d05fa9..e4b5bcf8d6 100644 --- a/src/assets/css/librarybrowser.css +++ b/src/assets/css/librarybrowser.css @@ -21,7 +21,7 @@ } .libraryPage { - padding-top: 7em !important; + padding-top: 7em; } .itemDetailPage { @@ -115,7 +115,7 @@ display: -webkit-inline-box; display: -webkit-inline-flex; display: inline-flex; - margin: 0.3em 0 0 0.5em; + margin: 0 0 0 0.5em; height: 1.7em; -webkit-box-align: center; -webkit-align-items: center; @@ -128,6 +128,10 @@ margin-top: 0; } +.layout-mobile .pageTitleWithDefaultLogo { + background-image: url(../img/icon-transparent.png); +} + .headerLeft, .skinHeader { display: -webkit-box; @@ -227,6 +231,7 @@ .centerMessage { margin: auto; width: 30%; + padding: 5em 0; text-align: center; } @@ -241,7 +246,6 @@ } @media all and (min-width: 40em) { - .dashboardDocument .adminDrawerLogo, .dashboardDocument .mainDrawerButton { display: none !important; } @@ -267,12 +271,6 @@ } } -@media all and (max-width: 60em) { - .libraryDocument .mainDrawerButton { - display: none; - } -} - @media all and (max-width: 84em) { .withSectionTabs .headerTop { padding-bottom: 0.55em; @@ -315,7 +313,7 @@ } .dashboardDocument .mainDrawer-scrollContainer { - margin-top: 6em !important; + margin-top: 4.6em !important; } } @@ -438,10 +436,46 @@ background-size: cover; background-repeat: no-repeat; background-position: center; + background-attachment: fixed; height: 50vh; position: relative; } +.layout-mobile .itemBackdrop { + background-attachment: scroll; +} + +.layout-desktop .itemBackdrop::after, +.layout-tv .itemBackdrop::after { + content: ""; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.65); + display: block; +} + +.layout-desktop .noBackdrop .itemBackdrop, +.layout-tv .noBackdrop .itemBackdrop { + display: none; +} + +.detailPageContent { + display: flex; + flex-direction: column; + padding-left: 2%; + padding-right: 2%; +} + +.layout-desktop .noBackdrop .detailPageContent, +.layout-tv .noBackdrop .detailPageContent { + margin-top: 2.5em; +} + +.layout-desktop .noBackdrop .detailImageContainer img, +.layout-tv .noBackdrop .detailImageContainer img { + margin-top: 0; +} + .personBackdrop { background-size: contain; } @@ -502,14 +536,13 @@ display: flex; align-items: center; justify-content: center; + text-align: center; } .detailPagePrimaryContainer { display: flex; align-items: center; align-content: center; - position: sticky; - top: 3.85em; z-index: 2; } @@ -519,12 +552,21 @@ top: 0; } -.layout-tv .detailPagePrimaryContainer { +.layout-tv #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer, +.layout-desktop #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer { position: relative; + top: 0; + padding-left: 32.45vw; } -.detailSticky { - background-color: #101010; +.layout-desktop .detailSticky, +.layout-tv .detailSticky { + margin-top: -7.2em; +} + +.layout-desktop .noBackdrop .detailSticky, +.layout-tv .noBackdrop .detailSticky { + margin-top: 0; } .infoWrapper { @@ -546,23 +588,17 @@ margin: 1.25em 0; } -.detailPageContent { - display: flex; - flex-direction: column; - padding-left: 2%; - padding-right: 2%; -} - .detailImageContainer { - position: sticky; - top: 25%; + position: relative; + margin-top: -25vh; float: left; - width: 22.786458333333332vw; + width: 25vw; + z-index: 3; } -.layout-mobile .detailImageContainer, -.layout-tv .detailImageContainer { - position: relative; +.layout-desktop .noBackdrop .detailImageContainer, +.layout-tv .noBackdrop .detailImageContainer { + margin-top: 0; } .detailPagePrimaryContent { @@ -570,15 +606,19 @@ } .detailLogo { - width: 25em; - height: 9.375em; + width: 67.25vw; + height: 14.5vh; position: absolute; - top: 14.5%; - right: 10.5%; + top: 15vh; + right: 0; -webkit-background-size: contain; background-size: contain; } +.noBackdrop .detailLogo { + display: none; +} + @media all and (max-width: 87.5em) { .detailLogo { right: 5%; @@ -605,8 +645,27 @@ .itemDetailImage { width: 100% !important; - box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37); - -webkit-box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37); + -webkit-box-shadow: 0 0.1em 0.5em 0 rgba(0, 0, 0, 0.75); + box-shadow: 0 0.1em 0.5em 0 rgba(0, 0, 0, 0.75); +} + +div.itemDetailGalleryLink.defaultCardBackground { + text-align: center; +} + +.itemDetailGalleryLink.defaultCardBackground { + height: 23vw; /* Dirty hack to get it to look somewhat square. Less than ideal. */ +} + +.btnSyncComplete i { + -webkit-border-radius: 100em; + border-radius: 100em; +} + +.itemDetailGalleryLink.defaultCardBackground > i { + font-size: 15vw; + margin-top: 50%; + transform: translateY(-50%); } @media all and (max-width: 62.5em) { @@ -614,6 +673,16 @@ position: relative; } + .layout-desktop .detailPageWrapperContainer, + .layout-tv .detailPageWrapperContainer { + margin-top: 7.2em; + } + + .layout-tv #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer, + .layout-desktop #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer { + padding-left: 3.3%; + } + .btnPlaySimple { display: none !important; } @@ -629,11 +698,6 @@ background: #673ab7 !important; } -.btnSyncComplete i { - -webkit-border-radius: 100em; - border-radius: 100em; -} - .emby-button.detailFloatingButton { position: absolute; background-color: rgba(0, 0, 0, 0.5) !important; @@ -787,7 +851,7 @@ width: auto; align-items: center; justify-content: center; - margin-top: -4.3em; + margin-top: -4.2em; position: relative; } @@ -833,6 +897,11 @@ border-collapse: collapse; } +.layout-desktop .noBackdrop .detailPageWrapperContainer, +.layout-tv .noBackdrop .detailPageWrapperContainer { + margin-top: 3.8em; +} + .mediaInfoStream { margin: 0 3em 0 0; display: inline-block; @@ -882,12 +951,9 @@ } } -@media all and (max-width: 75em) { - .listViewUserDataButtons { - display: flex; - align-items: center; - font-size: 65%; - } +.listViewUserDataButtons { + display: flex; + align-items: center; } .bulletSeparator { @@ -1053,3 +1119,50 @@ div:not(.sectionTitleContainer-cards) > .sectionTitle-cards { .itemsViewSettingsContainer > .button-flat { margin: 0; } + +.layout-mobile #myPreferencesMenuPage { + padding-top: 3.75em; +} + +.itemDetailsGroup { + margin-bottom: 1.5em; +} + +.trackSelections { + max-width: 44em; +} + +.detailsGroupItem, +.trackSelections .selectContainer { + display: flex; + max-width: 44em; + margin: 0 0 0.5em !important; +} + +.trackSelections .selectContainer { + margin: 0 0 0.3em !important; +} + +.detailsGroupItem .label, +.trackSelections .selectContainer .selectLabel { + cursor: default; + flex-grow: 0; + flex-shrink: 0; + flex-basis: 6.25em; + margin: 0 0.6em 0 0; +} + +.trackSelections .selectContainer .selectLabel { + margin: 0 0.2em 0 0; +} + +.trackSelections .selectContainer .detailTrackSelect { + font-size: inherit; + padding: 0; + overflow: hidden; +} + +.trackSelections .selectContainer .selectArrowContainer .selectArrow { + margin-top: 0; + font-size: 1.4em; +} diff --git a/src/assets/css/site.css b/src/assets/css/site.css index 55ce4c8807..67416663e7 100644 --- a/src/assets/css/site.css +++ b/src/assets/css/site.css @@ -5,6 +5,11 @@ html { height: 100%; } +.material-icons { + /* Fix font ligatures on older WebOS versions */ + -webkit-font-feature-settings: "liga"; +} + .backgroundContainer { position: fixed; top: 0; diff --git a/src/assets/img/devices/android.svg b/src/assets/img/devices/android.svg index c0d377bb72..24edc8bbf9 100644 --- a/src/assets/img/devices/android.svg +++ b/src/assets/img/devices/android.svg @@ -1,4 +1,4 @@ diff --git a/src/assets/img/fresh.svg b/src/assets/img/fresh.svg new file mode 100644 index 0000000000..0602ab2481 --- /dev/null +++ b/src/assets/img/fresh.svg @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/src/assets/img/rotten.svg b/src/assets/img/rotten.svg new file mode 100644 index 0000000000..04bef05bc1 --- /dev/null +++ b/src/assets/img/rotten.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/availableplugins.html b/src/availableplugins.html index ea1147fbfd..6c9b89c26d 100644 --- a/src/availableplugins.html +++ b/src/availableplugins.html @@ -5,4 +5,4 @@
- \ No newline at end of file + diff --git a/src/bundle.js b/src/bundle.js index 308687513a..ba5f74b163 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -16,6 +16,12 @@ _define("fetch", function() { return fetch }); +// query-string +var query = require("query-string"); +_define("queryString", function() { + return query; +}); + // flvjs var flvjs = require("flv.js/dist/flv").default; _define("flvjs", function() { @@ -47,12 +53,6 @@ _define("howler", function() { return howler; }); -// native-promise-only -var nativePromise = require("native-promise-only"); -_define("native-promise-only", function() { - return nativePromise; -}); - // resize-observer-polyfill var resize = require("resize-observer-polyfill").default; _define("resize-observer-polyfill", function() { @@ -66,7 +66,7 @@ _define("shaka", function() { }); // swiper -var swiper = require("swiper"); +var swiper = require("swiper/js/swiper"); require("swiper/css/swiper.min.css"); _define("swiper", function() { return swiper; @@ -81,14 +81,7 @@ _define("sortable", function() { // webcomponents var webcomponents = require("webcomponents.js/webcomponents-lite"); _define("webcomponents", function() { - return webcomponents -}); - -// libjass -var libjass = require("libjass"); -require("libjass/libjass.css"); -_define("libjass", function() { - return libjass; + return webcomponents; }); // libass-wasm @@ -103,7 +96,19 @@ _define("material-icons", function() { return material_icons; }); -var jellyfin_noto = require("jellyfin-noto"); +// noto font +var noto = require("jellyfin-noto"); _define("jellyfin-noto", function () { - return jellyfin_noto; + return noto; +}); + +// page.js +var page = require("page"); +_define("page", function() { + return page; +}); + +var polyfill = require("@babel/polyfill/dist/polyfill"); +_define("polyfill", function () { + return polyfill; }); diff --git a/src/components/accessschedule/accessschedule.template.html b/src/components/accessschedule/accessschedule.template.html index a0d05537c4..02f11942ed 100644 --- a/src/components/accessschedule/accessschedule.template.html +++ b/src/components/accessschedule/accessschedule.template.html @@ -1,6 +1,6 @@' + noLibDescription + '
' + html += '