mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge branch 'master' into hadicharara/added-support-for-rtl-layouts
This commit is contained in:
commit
32f103b852
178 changed files with 25310 additions and 7347 deletions
|
@ -9,6 +9,7 @@ import Screenfull from 'screenfull';
|
|||
import TableOfContents from './tableOfContents';
|
||||
import dom from '../../scripts/dom';
|
||||
import { translateHtml } from '../../scripts/globalize';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
|
||||
|
@ -294,6 +295,12 @@ export class BookPlayer {
|
|||
this.currentSrc = downloadHref;
|
||||
this.rendition = rendition;
|
||||
|
||||
rendition.themes.register('dark', { 'body': { 'color': '#fff' } });
|
||||
|
||||
if (userSettings.theme(undefined) === 'dark' || userSettings.theme(undefined) === null) {
|
||||
rendition.themes.select('dark');
|
||||
}
|
||||
|
||||
return rendition.display().then(() => {
|
||||
const epubElem = document.querySelector('.epub-container');
|
||||
epubElem.style.opacity = '0';
|
||||
|
|
|
@ -42,6 +42,12 @@
|
|||
|
||||
#dialogToc {
|
||||
background-color: white;
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
max-height: 80%;
|
||||
max-width: 60%;
|
||||
padding-right: 50px;
|
||||
padding-bottom: 15px;
|
||||
|
||||
.bookplayerButtonIcon {
|
||||
color: black;
|
||||
|
@ -49,5 +55,19 @@
|
|||
|
||||
.toc li {
|
||||
margin-bottom: 5px;
|
||||
|
||||
list-style-type: none;
|
||||
font-size: 120%;
|
||||
|
||||
a:link {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
color: #00a4dc;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,7 @@ import dialogHelper from '../../components/dialogHelper/dialogHelper';
|
|||
import keyboardnavigation from '../../scripts/keyboardNavigation';
|
||||
import { appRouter } from '../../components/appRouter';
|
||||
import ServerConnections from '../../components/ServerConnections';
|
||||
// eslint-disable-next-line import/named, import/namespace
|
||||
import { Swiper } from 'swiper/swiper-bundle.esm';
|
||||
import 'swiper/swiper-bundle.css';
|
||||
import * as userSettings from '../../scripts/settings/userSettings';
|
||||
|
||||
import './style.scss';
|
||||
|
||||
|
@ -27,6 +25,9 @@ export class ComicsPlayer {
|
|||
this.currentPage = 0;
|
||||
this.pageCount = 0;
|
||||
|
||||
const mediaSourceId = options.items[0].Id;
|
||||
this.comicsPlayerSettings = userSettings.getComicsPlayerSettings(mediaSourceId);
|
||||
|
||||
const elem = this.createMediaElement();
|
||||
return this.setCurrentSrc(elem, options);
|
||||
}
|
||||
|
@ -40,6 +41,9 @@ export class ComicsPlayer {
|
|||
|
||||
Events.trigger(this, 'stopped', [stopInfo]);
|
||||
|
||||
const mediaSourceId = this.item.Id;
|
||||
userSettings.setComicsPlayerSettings(this.comicsPlayerSettings, mediaSourceId);
|
||||
|
||||
this.archiveSource?.release();
|
||||
|
||||
const elem = this.mediaElement;
|
||||
|
@ -87,6 +91,85 @@ export class ComicsPlayer {
|
|||
this.stop();
|
||||
}
|
||||
|
||||
onDirChanged = () => {
|
||||
let langDir = this.comicsPlayerSettings.langDir;
|
||||
|
||||
if (!langDir || langDir === 'ltr')
|
||||
langDir = 'rtl';
|
||||
else
|
||||
langDir = 'ltr';
|
||||
|
||||
this.changeLanguageDirection(langDir);
|
||||
|
||||
this.comicsPlayerSettings.langDir = langDir;
|
||||
};
|
||||
|
||||
changeLanguageDirection(langDir) {
|
||||
const currentPage = this.currentPage;
|
||||
|
||||
this.swiperInstance.changeLanguageDirection(langDir);
|
||||
|
||||
const prevIcon = langDir === 'ltr' ? 'arrow_circle_left' : 'arrow_circle_right';
|
||||
this.mediaElement.querySelector('.btnToggleLangDir > span').classList.remove(prevIcon);
|
||||
|
||||
const newIcon = langDir === 'ltr' ? 'arrow_circle_right' : 'arrow_circle_left';
|
||||
this.mediaElement.querySelector('.btnToggleLangDir > span').classList.add(newIcon);
|
||||
|
||||
const dirTitle = langDir === 'ltr' ? 'Right To Left' : 'Left To Right';
|
||||
this.mediaElement.querySelector('.btnToggleLangDir').title = dirTitle;
|
||||
|
||||
this.reload(currentPage);
|
||||
}
|
||||
|
||||
onViewChanged = () => {
|
||||
let view = this.comicsPlayerSettings.pagesPerView;
|
||||
|
||||
if (!view || view === 1)
|
||||
view = 2;
|
||||
else
|
||||
view = 1;
|
||||
|
||||
this.changeView(view);
|
||||
|
||||
this.comicsPlayerSettings.pagesPerView = view;
|
||||
};
|
||||
|
||||
changeView(view) {
|
||||
const currentPage = this.currentPage;
|
||||
|
||||
this.swiperInstance.params.slidesPerView = view;
|
||||
this.swiperInstance.params.slidesPerGroup = view;
|
||||
|
||||
const prevIcon = view === 1 ? 'devices_fold' : 'import_contacts';
|
||||
this.mediaElement.querySelector('.btnToggleView > span').classList.remove(prevIcon);
|
||||
|
||||
const newIcon = view === 1 ? 'import_contacts' : 'devices_fold';
|
||||
this.mediaElement.querySelector('.btnToggleView > span').classList.add(newIcon);
|
||||
|
||||
const viewTitle = view === 1 ? 'Double Page View' : 'Single Page View';
|
||||
this.mediaElement.querySelector('.btnToggleView').title = viewTitle;
|
||||
|
||||
this.reload(currentPage);
|
||||
}
|
||||
|
||||
reload(currentPage) {
|
||||
const effect = this.swiperInstance.params.effect;
|
||||
|
||||
this.swiperInstance.params.effect = 'none';
|
||||
this.swiperInstance.update();
|
||||
|
||||
this.swiperInstance.slideNext();
|
||||
this.swiperInstance.slidePrev();
|
||||
|
||||
if (this.currentPage != currentPage) {
|
||||
this.swiperInstance.slideTo(currentPage);
|
||||
this.swiperInstance.update();
|
||||
}
|
||||
|
||||
this.swiperInstance.params.effect = effect;
|
||||
this.swiperInstance.update();
|
||||
}
|
||||
|
||||
onWindowKeyUp(e) {
|
||||
const key = keyboardnavigation.getKeyName(e);
|
||||
switch (key) {
|
||||
|
@ -101,6 +184,8 @@ export class ComicsPlayer {
|
|||
|
||||
elem?.addEventListener('close', this.onDialogClosed, { once: true });
|
||||
elem?.querySelector('.btnExit').addEventListener('click', this.onDialogClosed, { once: true });
|
||||
elem?.querySelector('.btnToggleLangDir').addEventListener('click', this.onDirChanged);
|
||||
elem?.querySelector('.btnToggleView').addEventListener('click', this.onViewChanged);
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
|
@ -114,6 +199,8 @@ export class ComicsPlayer {
|
|||
|
||||
elem?.removeEventListener('close', this.onDialogClosed);
|
||||
elem?.querySelector('.btnExit').removeEventListener('click', this.onDialogClosed);
|
||||
elem?.querySelector('.btnToggleLangDir').removeEventListener('click', this.onDirChanged);
|
||||
elem?.querySelector('.btnToggleView').removeEventListener('click', this.onViewChanged);
|
||||
}
|
||||
|
||||
unbindEvents() {
|
||||
|
@ -139,18 +226,40 @@ export class ComicsPlayer {
|
|||
removeOnClose: true
|
||||
});
|
||||
|
||||
const viewIcon = this.comicsPlayerSettings.pagesPerView === 1 ? 'import_contacts' : 'devices_fold';
|
||||
const dirIcon = this.comicsPlayerSettings.langDir === 'ltr' ? 'arrow_circle_right' : 'arrow_circle_left';
|
||||
|
||||
elem.id = 'comicsPlayer';
|
||||
elem.classList.add('slideshowDialog');
|
||||
|
||||
elem.innerHTML = `<div class="slideshowSwiperContainer"><div class="swiper-wrapper"></div></div>
|
||||
<div class="actionButtons">
|
||||
<button is="paper-icon-button-light" class="autoSize btnExit" tabindex="-1"><span class="material-icons actionButtonIcon close" aria-hidden="true"></span></button>
|
||||
</div>`;
|
||||
elem.innerHTML = `<div dir=${this.comicsPlayerSettings.langDir} class="slideshowSwiperContainer">
|
||||
<div class="swiper-wrapper"></div>
|
||||
<div class="swiper-button-next actionButtonIcon"></div>
|
||||
<div class="swiper-button-prev actionButtonIcon"></div>
|
||||
<div class="swiper-pagination"></div>
|
||||
</div>
|
||||
<div class="actionButtons">
|
||||
<button is="paper-icon-button-light" class="autoSize btnToggleLangDir" tabindex="-1">
|
||||
<span class="material-icons actionButtonIcon ${dirIcon}" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button is="paper-icon-button-light" class="autoSize btnToggleView" tabindex="-1">
|
||||
<span class="material-icons actionButtonIcon ${viewIcon}" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button is="paper-icon-button-light" class="autoSize btnExit" tabindex="-1">
|
||||
<span class="material-icons actionButtonIcon close" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>`;
|
||||
|
||||
dialogHelper.open(elem);
|
||||
}
|
||||
|
||||
this.mediaElement = elem;
|
||||
|
||||
const dirTitle = this.comicsPlayerSettings.langDir === 'ltr' ? 'Right To Left' : 'Left To Right';
|
||||
this.mediaElement.querySelector('.btnToggleLangDir').title = dirTitle;
|
||||
|
||||
const viewTitle = this.comicsPlayerSettings.pagesPerView === 1 ? 'Double Page View' : 'Single Page View';
|
||||
this.mediaElement.querySelector('.btnToggleView').title = viewTitle;
|
||||
|
||||
this.bindEvents();
|
||||
return elem;
|
||||
}
|
||||
|
@ -179,45 +288,61 @@ export class ComicsPlayer {
|
|||
const downloadUrl = apiClient.getItemDownloadUrl(item.Id);
|
||||
this.archiveSource = new ArchiveSource(downloadUrl);
|
||||
|
||||
return this.archiveSource.load().then(() => {
|
||||
loading.hide();
|
||||
//eslint-disable-next-line import/no-unresolved
|
||||
import('swiper/css/bundle');
|
||||
|
||||
this.pageCount = this.archiveSource.urls.length;
|
||||
this.currentPage = options.startPositionTicks / 10000 || 0;
|
||||
return this.archiveSource.load()
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
.then(() => import('swiper/bundle'))
|
||||
.then(({ Swiper }) => {
|
||||
loading.hide();
|
||||
|
||||
this.swiperInstance = new Swiper(elem.querySelector('.slideshowSwiperContainer'), {
|
||||
direction: 'horizontal',
|
||||
// loop is disabled due to the lack of Swiper support in virtual slides
|
||||
loop: false,
|
||||
zoom: {
|
||||
minRatio: 1,
|
||||
toggle: true,
|
||||
containerClass: 'slider-zoom-container'
|
||||
},
|
||||
autoplay: false,
|
||||
keyboard: {
|
||||
enabled: true
|
||||
},
|
||||
preloadImages: true,
|
||||
slidesPerView: 1,
|
||||
slidesPerColumn: 1,
|
||||
initialSlide: this.currentPage,
|
||||
// reduces memory consumption for large libraries while allowing preloading of images
|
||||
virtual: {
|
||||
slides: this.archiveSource.urls,
|
||||
cache: true,
|
||||
renderSlide: this.getImgFromUrl,
|
||||
addSlidesBefore: 1,
|
||||
addSlidesAfter: 1
|
||||
}
|
||||
this.pageCount = this.archiveSource.urls.length;
|
||||
this.currentPage = options.startPositionTicks / 10000 || 0;
|
||||
|
||||
this.swiperInstance = new Swiper(elem.querySelector('.slideshowSwiperContainer'), {
|
||||
direction: 'horizontal',
|
||||
// loop is disabled due to the lack of Swiper support in virtual slides
|
||||
loop: false,
|
||||
zoom: {
|
||||
minRatio: 1,
|
||||
toggle: true,
|
||||
containerClass: 'slider-zoom-container'
|
||||
},
|
||||
autoplay: false,
|
||||
keyboard: {
|
||||
enabled: true
|
||||
},
|
||||
preloadImages: true,
|
||||
slidesPerView: this.comicsPlayerSettings.pagesPerView,
|
||||
slidesPerGroup: this.comicsPlayerSettings.pagesPerView,
|
||||
slidesPerColumn: 1,
|
||||
initialSlide: this.currentPage,
|
||||
navigation: {
|
||||
nextEl: '.swiper-button-next',
|
||||
prevEl: '.swiper-button-prev'
|
||||
},
|
||||
pagination: {
|
||||
el: '.swiper-pagination',
|
||||
clickable: true,
|
||||
type: 'fraction'
|
||||
},
|
||||
// reduces memory consumption for large libraries while allowing preloading of images
|
||||
virtual: {
|
||||
slides: this.archiveSource.urls,
|
||||
cache: true,
|
||||
renderSlide: this.getImgFromUrl,
|
||||
addSlidesBefore: 1,
|
||||
addSlidesAfter: 1
|
||||
}
|
||||
});
|
||||
|
||||
// save current page ( a page is an image file inside the archive )
|
||||
this.swiperInstance.on('slideChange', () => {
|
||||
this.currentPage = this.swiperInstance.activeIndex;
|
||||
Events.trigger(this, 'pause');
|
||||
});
|
||||
});
|
||||
|
||||
// save current page ( a page is an image file inside the archive )
|
||||
this.swiperInstance.on('slideChange', () => {
|
||||
this.currentPage = this.swiperInstance.activeIndex;
|
||||
Events.trigger(this, 'pause');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getImgFromUrl(url) {
|
||||
|
|
|
@ -12,5 +12,31 @@
|
|||
|
||||
.swiper-slide-img {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.swiper-pagination {
|
||||
width: max-content;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
padding: 2px 5px 2px 5px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0%);
|
||||
text-shadow: 0 0 20px #fff;
|
||||
}
|
||||
|
||||
.actionButtons {
|
||||
right: 0.5vh;
|
||||
top: 0.5vh;
|
||||
z-index: 1002;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.actionButtonIcon {
|
||||
color: #000;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1150,8 +1150,7 @@ function tryRemoveElement(elem) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// This is unfortunate, but we're unable to remove the textTrack that gets added via addTextTrack
|
||||
if (browser.firefox || browser.web0s) {
|
||||
if (browser.web0s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
background: #000 !important;
|
||||
padding-left: env(safe-area-inset-left);
|
||||
padding-right: env(safe-area-inset-right);
|
||||
padding-top: env(safe-area-inset-top);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.videoPlayerContainer-onTop {
|
||||
|
@ -58,6 +62,9 @@ video[controls]::-webkit-media-controls {
|
|||
right: 0;
|
||||
color: #fff;
|
||||
font-size: 170%;
|
||||
padding-left: env(safe-area-inset-left);
|
||||
padding-right: env(safe-area-inset-right);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.videoSubtitlesInner {
|
||||
|
|
|
@ -7,7 +7,6 @@ import { appRouter } from '../../components/appRouter';
|
|||
import './style.scss';
|
||||
import '../../elements/emby-button/paper-icon-button-light';
|
||||
import { Events } from 'jellyfin-apiclient';
|
||||
import { GlobalWorkerOptions, getDocument } from 'pdfjs-dist';
|
||||
|
||||
export class PdfPlayer {
|
||||
constructor() {
|
||||
|
@ -200,14 +199,14 @@ export class PdfPlayer {
|
|||
const serverId = item.ServerId;
|
||||
const apiClient = ServerConnections.getApiClient(serverId);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
return import('pdfjs-dist').then(({ GlobalWorkerOptions, getDocument }) => {
|
||||
const downloadHref = apiClient.getItemDownloadUrl(item.Id);
|
||||
|
||||
this.bindEvents();
|
||||
GlobalWorkerOptions.workerSrc = appRouter.baseUrl() + '/libraries/pdf.worker.js';
|
||||
|
||||
const downloadTask = getDocument(downloadHref);
|
||||
downloadTask.promise.then(book => {
|
||||
return downloadTask.promise.then(book => {
|
||||
if (this.cancellationToken) return;
|
||||
this.book = book;
|
||||
this.loaded = true;
|
||||
|
@ -219,8 +218,6 @@ export class PdfPlayer {
|
|||
} else {
|
||||
this.loadPage(1);
|
||||
}
|
||||
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,21 +5,21 @@
|
|||
overflow: none;
|
||||
z-index: 100;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
#canvas {
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.actionButtons {
|
||||
right: 0.5vh;
|
||||
top: 0.5vh;
|
||||
z-index: 1002;
|
||||
position: absolute;
|
||||
}
|
||||
.actionButtons {
|
||||
right: 0.5vh;
|
||||
top: 0.5vh;
|
||||
z-index: 1002;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.actionButtonIcon {
|
||||
color: black;
|
||||
opacity: 0.7;
|
||||
.actionButtonIcon {
|
||||
color: #000;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: env(safe-area-inset-left);
|
||||
padding-right: env(safe-area-inset-right);
|
||||
padding-top: env(safe-area-inset-top);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.youtubePlayerContainer.onTop {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue