diff --git a/.editorconfig b/.editorconfig index 92cf9dc590..84ba694073 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,5 +8,5 @@ trim_trailing_whitespace = true insert_final_newline = true end_of_line = lf -[json] +[*.json] indent_size = 2 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 73f40aaca1..c51cd6b31f 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -36,6 +36,7 @@ - [MrTimscampi](https://github.com/MrTimscampi) - [Sarab Singh](https://github.com/sarab97) - [Andrei Oanca](https://github.com/OancaAndrei) + - [Cromefire_](https://github.com/cromefire) # Emby Contributors diff --git a/package.json b/package.json index 60a0ef92d4..3b966e3cdd 100644 --- a/package.json +++ b/package.json @@ -5,18 +5,18 @@ "repository": "https://github.com/jellyfin/jellyfin-web", "license": "GPL-2.0-or-later", "devDependencies": { - "@babel/core": "^7.10.5", + "@babel/core": "^7.11.0", "@babel/plugin-proposal-class-properties": "^7.10.1", "@babel/plugin-proposal-private-methods": "^7.10.1", "@babel/plugin-transform-modules-amd": "^7.10.5", "@babel/polyfill": "^7.8.7", - "@babel/preset-env": "^7.10.3", - "autoprefixer": "^9.8.5", + "@babel/preset-env": "^7.11.0", + "autoprefixer": "^9.8.6", "babel-eslint": "^11.0.0-beta.2", "babel-loader": "^8.0.6", "browser-sync": "^2.26.12", "copy-webpack-plugin": "^5.1.1", - "css-loader": "^4.0.0", + "css-loader": "^4.1.1", "cssnano": "^4.1.10", "del": "^5.1.0", "eslint": "^6.8.0", @@ -48,7 +48,7 @@ "stylelint-config-rational-order": "^0.1.2", "stylelint-no-browser-hacks": "^1.2.1", "stylelint-order": "^4.1.0", - "webpack": "^4.44.0", + "webpack": "^4.44.1", "webpack-merge": "^4.2.2", "webpack-stream": "^5.2.1" }, @@ -62,7 +62,7 @@ "fast-text-encoding": "^1.0.3", "flv.js": "^1.5.0", "headroom.js": "^0.11.0", - "hls.js": "^0.14.6", + "hls.js": "^0.14.7", "howler": "^2.2.0", "intersection-observer": "^0.11.0", "jellyfin-apiclient": "^1.4.1", @@ -141,6 +141,7 @@ "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", @@ -148,8 +149,10 @@ "src/components/playlisteditor/playlisteditor.js", "src/components/playmenu.js", "src/components/prompt/prompt.js", + "src/components/refreshdialog/refreshdialog.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", + "src/plugins/htmlVideoPlayer/plugin.js", "src/components/search/searchfields.js", "src/components/search/searchresults.js", "src/components/settingshelper.js", @@ -176,6 +179,12 @@ "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/mediaLibrary.js", "src/controllers/dashboard/metadataImages.js", "src/controllers/dashboard/metadatanfo.js", @@ -195,6 +204,7 @@ "src/controllers/playback/queue/index.js", "src/controllers/playback/video/index.js", "src/controllers/searchpage.js", + "src/controllers/livetvtuner.js", "src/controllers/shows/episodes.js", "src/controllers/shows/tvgenres.js", "src/controllers/shows/tvlatest.js", @@ -208,7 +218,6 @@ "src/controllers/user/playback/index.js", "src/controllers/user/profile/index.js", "src/controllers/user/subtitles/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", diff --git a/src/assets/css/librarybrowser.css b/src/assets/css/librarybrowser.css index 61815a590f..047ae0a1c6 100644 --- a/src/assets/css/librarybrowser.css +++ b/src/assets/css/librarybrowser.css @@ -646,7 +646,7 @@ .layout-desktop .detailRibbon, .layout-tv .detailRibbon { margin-top: -7.2em; - height: 7.18em; + height: 7.2em; } .layout-desktop .noBackdrop .detailRibbon, diff --git a/src/components/appRouter.js b/src/components/appRouter.js index e4c53fbbb8..7f4976424c 100644 --- a/src/components/appRouter.js +++ b/src/components/appRouter.js @@ -231,8 +231,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro max /= 8; max *= 1000000; max *= 0.7; - max = parseInt(max); - return max; + return parseInt(max, 10); } } /* eslint-enable compat/compat */ diff --git a/src/components/cardbuilder/chaptercardbuilder.js b/src/components/cardbuilder/chaptercardbuilder.js index 0215f8d8e5..1521650ed0 100644 --- a/src/components/cardbuilder/chaptercardbuilder.js +++ b/src/components/cardbuilder/chaptercardbuilder.js @@ -104,9 +104,7 @@ import browser from 'browser'; const cardBoxCssClass = 'cardBox'; const cardScalableClass = 'cardScalable'; - const html = ``; - - return html; + return ``; } export function buildChapterCards(item, chapters, options) { diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js index 85d5954d18..3c6394db79 100644 --- a/src/components/playback/playbackmanager.js +++ b/src/components/playback/playbackmanager.js @@ -2,6 +2,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla 'use strict'; loading = loading.default || loading; + PlayQueueManager = PlayQueueManager.default || PlayQueueManager; function enableLocalPlaylistManagement(player) { if (player.getPlaylist) { @@ -20,6 +21,11 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla screenfull.on('change', function () { events.trigger(player, 'fullscreenchange'); }); + } else { + // iOS Safari + document.addEventListener('webkitfullscreenchange', function () { + events.trigger(player, 'fullscreenchange'); + }, false); } } @@ -884,9 +890,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla } } - targets = targets.sort(sortPlayerTargets); - - return targets; + return targets.sort(sortPlayerTargets); }); }); }; @@ -1401,6 +1405,11 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla return player.isFullscreen(); } + if (!screenfull.isEnabled) { + // iOS Safari + return document.webkitIsFullScreen; + } + return screenfull.isFullscreen; }; @@ -1412,6 +1421,16 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla if (screenfull.isEnabled) { screenfull.toggle(); + } else { + // iOS Safari + if (document.webkitIsFullScreen && document.webkitCancelFullscreen) { + document.webkitCancelFullscreen(); + } else { + const elem = document.querySelector('video'); + if (elem && elem.webkitEnterFullscreen) { + elem.webkitEnterFullscreen(); + } + } } }; @@ -3372,8 +3391,8 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla PlaybackManager.prototype.getSubtitleUrl = function (textStream, serverId) { var apiClient = connectionManager.getApiClient(serverId); - var textStreamUrl = !textStream.IsExternalUrl ? apiClient.getUrl(textStream.DeliveryUrl) : textStream.DeliveryUrl; - return textStreamUrl; + + return !textStream.IsExternalUrl ? apiClient.getUrl(textStream.DeliveryUrl) : textStream.DeliveryUrl; }; PlaybackManager.prototype.stop = function (player) { diff --git a/src/components/playback/playqueuemanager.js b/src/components/playback/playqueuemanager.js index 696075b344..fbe1ceabfe 100644 --- a/src/components/playback/playqueuemanager.js +++ b/src/components/playback/playqueuemanager.js @@ -1,56 +1,56 @@ -define([], function () { - 'use strict'; +/*eslint prefer-const: "error"*/ - var currentId = 0; - function addUniquePlaylistItemId(item) { - if (!item.PlaylistItemId) { - item.PlaylistItemId = 'playlistItem' + currentId; - currentId++; +let currentId = 0; +function addUniquePlaylistItemId(item) { + if (!item.PlaylistItemId) { + item.PlaylistItemId = 'playlistItem' + currentId; + currentId++; + } +} + +function findPlaylistIndex(playlistItemId, list) { + for (let i = 0, length = list.length; i < length; i++) { + if (list[i].PlaylistItemId === playlistItemId) { + return i; } } - function findPlaylistIndex(playlistItemId, list) { - for (var i = 0, length = list.length; i < length; i++) { - if (list[i].PlaylistItemId === playlistItemId) { - return i; - } - } + return -1; +} - return -1; - } - - function PlayQueueManager() { +class PlayQueueManager { + constructor() { this._sortedPlaylist = []; this._playlist = []; this._repeatMode = 'RepeatNone'; this._shuffleMode = 'Sorted'; } - PlayQueueManager.prototype.getPlaylist = function () { + getPlaylist() { return this._playlist.slice(0); - }; + } - PlayQueueManager.prototype.setPlaylist = function (items) { + setPlaylist(items) { items = items.slice(0); - for (var i = 0, length = items.length; i < length; i++) { + for (let i = 0, length = items.length; i < length; i++) { addUniquePlaylistItemId(items[i]); } this._currentPlaylistItemId = null; this._playlist = items; this._repeatMode = 'RepeatNone'; - }; + } - PlayQueueManager.prototype.queue = function (items) { - for (var i = 0, length = items.length; i < length; i++) { + queue(items) { + for (let i = 0, length = items.length; i < length; i++) { addUniquePlaylistItemId(items[i]); this._playlist.push(items[i]); } - }; + } - PlayQueueManager.prototype.shufflePlaylist = function () { + shufflePlaylist() { this._sortedPlaylist = []; for (const item of this._playlist) { this._sortedPlaylist.push(item); @@ -65,42 +65,31 @@ define([], function () { } this._playlist.unshift(currentPlaylistItem); this._shuffleMode = 'Shuffle'; - }; + } - PlayQueueManager.prototype.sortShuffledPlaylist = function () { + sortShuffledPlaylist() { this._playlist = []; - for (let item of this._sortedPlaylist) { + for (const item of this._sortedPlaylist) { this._playlist.push(item); } this._sortedPlaylist = []; this._shuffleMode = 'Sorted'; - }; + } - PlayQueueManager.prototype.clearPlaylist = function (clearCurrentItem = false) { + clearPlaylist(clearCurrentItem = false) { const currentPlaylistItem = this._playlist.splice(this.getCurrentPlaylistIndex(), 1)[0]; this._playlist = []; if (!clearCurrentItem) { this._playlist.push(currentPlaylistItem); } - }; - - function arrayInsertAt(destArray, pos, arrayToInsert) { - var args = []; - args.push(pos); // where to insert - args.push(0); // nothing to remove - args = args.concat(arrayToInsert); // add on array to insert - destArray.splice.apply(destArray, args); // splice it in } - PlayQueueManager.prototype.queueNext = function (items) { - var i; - var length; - - for (i = 0, length = items.length; i < length; i++) { + queueNext(items) { + for (let i = 0, length = items.length; i < length; i++) { addUniquePlaylistItemId(items[i]); } - var currentIndex = this.getCurrentPlaylistIndex(); + let currentIndex = this.getCurrentPlaylistIndex(); if (currentIndex === -1) { currentIndex = this._playlist.length; @@ -109,43 +98,43 @@ define([], function () { } arrayInsertAt(this._playlist, currentIndex, items); - }; + } - PlayQueueManager.prototype.getCurrentPlaylistIndex = function () { + getCurrentPlaylistIndex() { return findPlaylistIndex(this.getCurrentPlaylistItemId(), this._playlist); - }; + } - PlayQueueManager.prototype.getCurrentItem = function () { - var index = findPlaylistIndex(this.getCurrentPlaylistItemId(), this._playlist); + getCurrentItem() { + const index = findPlaylistIndex(this.getCurrentPlaylistItemId(), this._playlist); return index === -1 ? null : this._playlist[index]; - }; + } - PlayQueueManager.prototype.getCurrentPlaylistItemId = function () { + getCurrentPlaylistItemId() { return this._currentPlaylistItemId; - }; + } - PlayQueueManager.prototype.setPlaylistState = function (playlistItemId, playlistIndex) { + setPlaylistState(playlistItemId, playlistIndex) { this._currentPlaylistItemId = playlistItemId; - }; + } - PlayQueueManager.prototype.setPlaylistIndex = function (playlistIndex) { + setPlaylistIndex(playlistIndex) { if (playlistIndex < 0) { this.setPlaylistState(null); } else { this.setPlaylistState(this._playlist[playlistIndex].PlaylistItemId); } - }; + } - PlayQueueManager.prototype.removeFromPlaylist = function (playlistItemIds) { + removeFromPlaylist(playlistItemIds) { if (this._playlist.length <= playlistItemIds.length) { return { result: 'empty' }; } - var currentPlaylistItemId = this.getCurrentPlaylistItemId(); - var isCurrentIndex = playlistItemIds.indexOf(currentPlaylistItemId) !== -1; + const currentPlaylistItemId = this.getCurrentPlaylistItemId(); + const isCurrentIndex = playlistItemIds.indexOf(currentPlaylistItemId) !== -1; this._sortedPlaylist = this._sortedPlaylist.filter(function (item) { return !playlistItemIds.includes(item.PlaylistItemId); @@ -159,17 +148,13 @@ define([], function () { result: 'removed', isCurrentIndex: isCurrentIndex }; - }; - - function moveInArray(array, from, to) { - array.splice(to, 0, array.splice(from, 1)[0]); } - PlayQueueManager.prototype.movePlaylistItem = function (playlistItemId, newIndex) { - var playlist = this.getPlaylist(); + movePlaylistItem(playlistItemId, newIndex) { + const playlist = this.getPlaylist(); - var oldIndex; - for (var i = 0, length = playlist.length; i < length; i++) { + let oldIndex; + for (let i = 0, length = playlist.length; i < length; i++) { if (playlist[i].PlaylistItemId === playlistItemId) { oldIndex = i; break; @@ -195,30 +180,30 @@ define([], function () { playlistItemId: playlistItemId, newIndex: newIndex }; - }; + } - PlayQueueManager.prototype.reset = function () { + reset() { this._sortedPlaylist = []; this._playlist = []; this._currentPlaylistItemId = null; this._repeatMode = 'RepeatNone'; this._shuffleMode = 'Sorted'; - }; + } - PlayQueueManager.prototype.setRepeatMode = function (value) { + setRepeatMode(value) { const repeatModes = ['RepeatOne', 'RepeatAll', 'RepeatNone']; if (repeatModes.includes(value)) { this._repeatMode = value; } else { throw new TypeError('invalid value provided for setRepeatMode'); } - }; + } - PlayQueueManager.prototype.getRepeatMode = function () { + getRepeatMode() { return this._repeatMode; - }; + } - PlayQueueManager.prototype.setShuffleMode = function (value) { + setShuffleMode(value) { switch (value) { case 'Shuffle': this.shufflePlaylist(); @@ -229,9 +214,9 @@ define([], function () { default: throw new TypeError('invalid value provided to setShuffleMode'); } - }; + } - PlayQueueManager.prototype.toggleShuffleMode = function () { + toggleShuffleMode() { switch (this._shuffleMode) { case 'Shuffle': this.setShuffleMode('Sorted'); @@ -242,16 +227,16 @@ define([], function () { default: throw new TypeError('current value for shufflequeue is invalid'); } - }; + } - PlayQueueManager.prototype.getShuffleMode = function () { + getShuffleMode() { return this._shuffleMode; - }; + } - PlayQueueManager.prototype.getNextItemInfo = function () { - var newIndex; - var playlist = this.getPlaylist(); - var playlistLength = playlist.length; + getNextItemInfo() { + let newIndex; + const playlist = this.getPlaylist(); + const playlistLength = playlist.length; switch (this.getRepeatMode()) { case 'RepeatOne': @@ -272,7 +257,7 @@ define([], function () { return null; } - var item = playlist[newIndex]; + const item = playlist[newIndex]; if (!item) { return null; @@ -282,7 +267,19 @@ define([], function () { item: item, index: newIndex }; - }; + } +} - return PlayQueueManager; -}); +function arrayInsertAt(destArray, pos, arrayToInsert) { + let args = []; + args.push(pos); // where to insert + args.push(0); // nothing to remove + args = args.concat(arrayToInsert); // add on array to insert + destArray.splice.apply(destArray, args); // splice it in +} + +function moveInArray(array, from, to) { + array.splice(to, 0, array.splice(from, 1)[0]); +} + +export default PlayQueueManager; diff --git a/src/components/refreshdialog/refreshdialog.js b/src/components/refreshdialog/refreshdialog.js index 57b2ee3189..1ec0517448 100644 --- a/src/components/refreshdialog/refreshdialog.js +++ b/src/components/refreshdialog/refreshdialog.js @@ -1,96 +1,108 @@ -define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'emby-input', 'emby-checkbox', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (dom, shell, dialogHelper, loading, layoutManager, connectionManager, appRouter, globalize) { - 'use strict'; +import dom from 'dom'; +import dialogHelper from 'dialogHelper'; +import loading from 'loading'; +import layoutManager from 'layoutManager'; +import connectionManager from 'connectionManager'; +import globalize from 'globalize'; +import 'emby-input'; +import 'emby-checkbox'; +import 'paper-icon-button-light'; +import 'emby-select'; +import 'material-icons'; +import 'css!./../formdialog'; +import 'emby-button'; - loading = loading.default || loading; +/*eslint prefer-const: "error"*/ - function getEditorHtml() { - var html = ''; +function getEditorHtml() { + let html = ''; - html += '