1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00
jellyfin-web/src/plugins/pdfPlayer/plugin.js

319 lines
8.6 KiB
JavaScript
Raw Normal View History

2020-11-19 16:42:21 -05:00
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';
2021-01-26 15:48:00 -05:00
import './style.scss';
import '../../elements/emby-button/paper-icon-button-light';
import { Events } from 'jellyfin-apiclient';
2020-11-20 23:28:21 -05:00
import { GlobalWorkerOptions, getDocument } from 'pdfjs-dist';
2020-09-01 22:55:08 +09:00
export class PdfPlayer {
constructor() {
this.name = 'PDF Player';
this.type = 'mediaplayer';
this.id = 'pdfplayer';
this.priority = 1;
this.onDialogClosed = this.onDialogClosed.bind(this);
this.onWindowKeyUp = this.onWindowKeyUp.bind(this);
this.onTouchStart = this.onTouchStart.bind(this);
}
play(options) {
this.progress = 0;
this.loaded = false;
this.cancellationToken = false;
this.pages = {};
loading.show();
2020-10-23 05:22:03 +09:00
const elem = this.createMediaElement();
2020-09-01 22:55:08 +09:00
return this.setCurrentSrc(elem, options);
}
stop() {
this.unbindEvents();
const stopInfo = {
src: this.item
};
Events.trigger(this, 'stopped', [stopInfo]);
2020-10-23 05:22:03 +09:00
const elem = this.mediaElement;
2020-09-01 22:55:08 +09:00
if (elem) {
dialogHelper.close(elem);
this.mediaElement = null;
}
// hide loading animation
loading.hide();
// cancel page render
this.cancellationToken = true;
}
destroy() {
// Nothing to do here
}
2020-09-01 22:55:08 +09:00
currentItem() {
return this.item;
}
currentTime() {
return this.progress;
}
duration() {
return this.book ? this.book.numPages : 0;
}
volume() {
return 100;
}
isMuted() {
return false;
}
paused() {
return false;
}
seekable() {
return true;
}
onWindowKeyUp(e) {
2020-10-23 05:22:03 +09:00
const key = keyboardnavigation.getKeyName(e);
2020-09-01 22:55:08 +09:00
if (!this.loaded) return;
switch (key) {
case 'l':
case 'ArrowRight':
case 'Right':
this.next();
break;
case 'j':
case 'ArrowLeft':
case 'Left':
this.previous();
break;
case 'Escape':
this.stop();
break;
}
}
onTouchStart(e) {
if (!this.loaded || !e.touches || e.touches.length === 0) return;
if (e.touches[0].clientX < dom.getWindowSize().innerWidth / 2) {
this.previous();
} else {
this.next();
}
}
onDialogClosed() {
this.stop();
}
bindMediaElementEvents() {
2020-10-23 05:22:03 +09:00
const elem = this.mediaElement;
2020-09-01 22:55:08 +09:00
elem.addEventListener('close', this.onDialogClosed, { once: true });
elem.querySelector('.btnExit').addEventListener('click', this.onDialogClosed, { once: true });
2020-09-01 22:55:08 +09:00
}
bindEvents() {
this.bindMediaElementEvents();
document.addEventListener('keyup', this.onWindowKeyUp);
document.addEventListener('touchstart', this.onTouchStart);
}
unbindMediaElementEvents() {
2020-10-23 05:22:03 +09:00
const elem = this.mediaElement;
2020-09-01 22:55:08 +09:00
elem.removeEventListener('close', this.onDialogClosed);
elem.querySelector('.btnExit').removeEventListener('click', this.onDialogClosed);
}
unbindEvents() {
if (this.mediaElement) {
this.unbindMediaElementEvents();
}
document.removeEventListener('keyup', this.onWindowKeyUp);
document.removeEventListener('touchstart', this.onTouchStart);
}
createMediaElement() {
let elem = this.mediaElement;
if (elem) {
return elem;
}
elem = document.getElementById('pdfPlayer');
if (!elem) {
elem = dialogHelper.createDialog({
exitAnimationDuration: 400,
size: 'fullscreen',
autoFocus: false,
scrollY: false,
exitAnimation: 'fadeout',
removeOnClose: true
});
let html = '';
html += '<canvas id="canvas"></canvas>';
html += '<div class="actionButtons">';
2022-02-24 20:15:24 +03:00
html += '<button is="paper-icon-button-light" class="autoSize btnExit" tabindex="-1"><span class="material-icons actionButtonIcon close" aria-hidden="true"></span></button>';
2020-09-01 22:55:08 +09:00
html += '</div>';
elem.id = 'pdfPlayer';
elem.innerHTML = html;
dialogHelper.open(elem);
}
this.mediaElement = elem;
return elem;
}
setCurrentSrc(elem, options) {
2020-10-23 05:22:03 +09:00
const item = options.items[0];
2020-09-01 22:55:08 +09:00
this.item = item;
this.streamInfo = {
started: true,
ended: false,
item: this.item,
2020-09-01 22:55:08 +09:00
mediaSource: {
Id: item.Id
}
};
2020-10-23 05:22:03 +09:00
const serverId = item.ServerId;
2020-11-19 14:08:14 -05:00
const apiClient = ServerConnections.getApiClient(serverId);
2020-09-01 22:55:08 +09:00
2021-01-26 22:20:12 -05:00
return new Promise((resolve) => {
2020-11-20 23:28:21 -05:00
const downloadHref = apiClient.getItemDownloadUrl(item.Id);
this.bindEvents();
GlobalWorkerOptions.workerSrc = appRouter.baseUrl() + '/libraries/pdf.worker.js';
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 + 1);
2020-11-20 23:28:21 -05:00
this.progress = percentageTicks;
} else {
this.loadPage(1);
}
return resolve();
2020-09-01 22:55:08 +09:00
});
});
}
next() {
if (this.progress === this.duration() - 1) return;
this.loadPage(this.progress + 2);
this.progress = this.progress + 1;
Events.trigger(this, 'pause');
2020-09-01 22:55:08 +09:00
}
previous() {
if (this.progress === 0) return;
this.loadPage(this.progress);
this.progress = this.progress - 1;
Events.trigger(this, 'pause');
2020-09-01 22:55:08 +09:00
}
replaceCanvas(canvas) {
const old = document.getElementById('canvas');
canvas.id = 'canvas';
old.parentNode.replaceChild(canvas, old);
}
loadPage(number) {
const prefix = 'page';
const pad = 2;
// generate list of cached pages by padding the requested page on both sides
2020-10-23 05:22:03 +09:00
const pages = [prefix + number];
2020-09-01 22:55:08 +09:00
for (let i = 1; i <= pad; i++) {
if (number - i > 0) pages.push(prefix + (number - i));
if (number + i < this.duration()) pages.push(prefix + (number + i));
}
// load any missing pages in the cache
2020-10-23 05:22:03 +09:00
for (const page of pages) {
2020-09-01 22:55:08 +09:00
if (!this.pages[page]) {
this.pages[page] = document.createElement('canvas');
this.renderPage(this.pages[page], parseInt(page.slice(4)));
2020-09-01 22:55:08 +09:00
}
}
// show the requested page
this.replaceCanvas(this.pages[prefix + number], number);
// delete all pages outside the cache area
2020-10-23 05:22:03 +09:00
for (const page in this.pages) {
2020-09-01 22:55:08 +09:00
if (!pages.includes(page)) {
delete this.pages[page];
}
}
}
renderPage(canvas, number) {
this.book.getPage(number).then(page => {
const original = page.getViewport({ scale: 1 });
const context = canvas.getContext('2d');
const widthRatio = dom.getWindowSize().innerWidth / original.width;
const heightRatio = dom.getWindowSize().innerHeight / original.height;
const scale = Math.min(heightRatio, widthRatio);
const viewport = page.getViewport({ scale: scale });
canvas.width = viewport.width;
canvas.height = viewport.height;
2020-10-23 05:22:03 +09:00
const renderContext = {
2020-09-01 22:55:08 +09:00
canvasContext: context,
viewport: viewport
};
2020-10-23 05:22:03 +09:00
const renderTask = page.render(renderContext);
2020-09-01 22:55:08 +09:00
renderTask.promise.then(() => {
loading.hide();
});
});
}
canPlayMediaType(mediaType) {
return (mediaType || '').toLowerCase() === 'book';
}
canPlayItem(item) {
if (item.Path && item.Path.endsWith('pdf')) {
return true;
}
return false;
}
}
export default PdfPlayer;