2020-05-22 10:39:21 +09:00
|
|
|
import connectionManager from 'connectionManager';
|
|
|
|
import dom from 'dom';
|
|
|
|
import loading from 'loading';
|
|
|
|
import playbackManager from 'playbackManager';
|
|
|
|
import keyboardnavigation from 'keyboardnavigation';
|
|
|
|
import dialogHelper from 'dialogHelper';
|
|
|
|
import appHost from 'apphost';
|
|
|
|
import 'css!./style';
|
|
|
|
import 'material-icons';
|
|
|
|
import 'paper-icon-button-light';
|
|
|
|
|
|
|
|
export class BookPlayer {
|
2020-05-22 10:42:58 +09:00
|
|
|
constructor() {
|
|
|
|
this.name = 'Book Player';
|
|
|
|
this.type = 'mediaplayer';
|
|
|
|
this.id = 'bookplayer';
|
|
|
|
this.priority = 1;
|
|
|
|
}
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
play(options) {
|
|
|
|
loading.show();
|
|
|
|
let elem = this.createMediaElement();
|
|
|
|
return this.setCurrentSrc(elem, options);
|
|
|
|
}
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
stop() {
|
|
|
|
let elem = this._mediaElement;
|
2020-05-28 18:38:13 +10:00
|
|
|
let tocElement = this._tocElement;
|
2020-05-22 10:42:58 +09:00
|
|
|
let rendition = this._rendition;
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-28 18:38:13 +10:00
|
|
|
if (elem) {
|
|
|
|
dialogHelper.close(elem);
|
2020-05-22 10:42:58 +09:00
|
|
|
this._mediaElement = null;
|
2020-05-22 10:39:21 +09:00
|
|
|
}
|
2020-05-28 18:38:13 +10:00
|
|
|
|
|
|
|
if (tocElement) {
|
|
|
|
dialogHelper.close(tocElement);
|
|
|
|
this._tocElement = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rendition) {
|
|
|
|
rendition.destroy();
|
|
|
|
}
|
2020-05-22 10:42:58 +09:00
|
|
|
}
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
onWindowKeyUp(e) {
|
|
|
|
let key = keyboardnavigation.getKeyName(e);
|
|
|
|
let rendition = this._rendition;
|
|
|
|
let book = rendition.book;
|
|
|
|
|
|
|
|
switch (key) {
|
|
|
|
case 'l':
|
|
|
|
case 'ArrowRight':
|
|
|
|
case 'Right':
|
|
|
|
book.package.metadata.direction === 'rtl' ? rendition.prev() : rendition.next();
|
|
|
|
break;
|
|
|
|
case 'j':
|
|
|
|
case 'ArrowLeft':
|
|
|
|
case 'Left':
|
|
|
|
book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev();
|
|
|
|
break;
|
|
|
|
case 'Escape':
|
|
|
|
dialogHelper.close(this._mediaElement);
|
|
|
|
if (this._tocElement) {
|
|
|
|
dialogHelper.close(this._tocElement);
|
|
|
|
}
|
|
|
|
break;
|
2020-05-16 22:02:52 +10:00
|
|
|
}
|
2020-05-22 10:42:58 +09:00
|
|
|
}
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
onDialogClosed() {
|
|
|
|
this.stop();
|
|
|
|
}
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
replaceLinks(contents, f) {
|
|
|
|
let links = contents.querySelectorAll('a[href]');
|
2020-05-19 16:05:44 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
links.forEach((link) => {
|
|
|
|
let href = link.getAttribute('href');
|
2020-05-19 16:05:44 +10:00
|
|
|
|
2020-05-28 18:45:28 +10:00
|
|
|
link.onclick = () => {
|
2020-05-22 10:42:58 +09:00
|
|
|
f(href);
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
createMediaElement() {
|
|
|
|
let elem = this._mediaElement;
|
|
|
|
|
|
|
|
if (elem) {
|
|
|
|
return elem;
|
2020-05-19 16:05:44 +10:00
|
|
|
}
|
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
elem = document.getElementById('bookPlayer');
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
if (!elem) {
|
|
|
|
elem = dialogHelper.createDialog({
|
|
|
|
exitAnimationDuration: 400,
|
|
|
|
size: 'fullscreen',
|
|
|
|
autoFocus: false,
|
|
|
|
scrollY: false,
|
|
|
|
exitAnimation: 'fadeout',
|
|
|
|
removeOnClose: true
|
|
|
|
});
|
|
|
|
elem.id = 'bookPlayer';
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
let html = '';
|
|
|
|
html += '<div class="topRightActionButtons">';
|
|
|
|
html += '<button is="paper-icon-button-light" class="autoSize bookplayerButton btnBookplayerExit hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon close"></i></button>';
|
|
|
|
html += '</div>';
|
|
|
|
html += '<div class="topLeftActionButtons">';
|
|
|
|
html += '<button is="paper-icon-button-light" class="autoSize bookplayerButton btnBookplayerToc hide-mouse-idle-tv" tabindex="-1"><i class="material-icons bookplayerButtonIcon toc"></i></button>';
|
|
|
|
html += '</div>';
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
elem.innerHTML = html;
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-28 18:45:28 +10:00
|
|
|
elem.querySelector('.btnBookplayerExit').addEventListener('click', () => {
|
2020-05-22 10:42:58 +09:00
|
|
|
dialogHelper.close(elem);
|
|
|
|
});
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-28 18:45:28 +10:00
|
|
|
elem.querySelector('.btnBookplayerToc').addEventListener('click', () => {
|
2020-05-22 10:42:58 +09:00
|
|
|
let rendition = this._rendition;
|
|
|
|
if (rendition) {
|
|
|
|
let tocElement = dialogHelper.createDialog({
|
|
|
|
size: 'small',
|
|
|
|
autoFocus: false,
|
|
|
|
removeOnClose: true
|
|
|
|
});
|
|
|
|
tocElement.id = 'dialogToc';
|
|
|
|
|
|
|
|
let tocHtml = '<div class="topRightActionButtons">';
|
2020-05-27 21:12:33 +09:00
|
|
|
tocHtml += '<button is="paper-icon-button-light" class="autoSize bookplayerButton btnBookplayerTocClose hide-mouse-idle-tv" tabindex="-1"><span class="material-icons bookplayerButtonIcon close"></span></button>';
|
2020-05-22 10:42:58 +09:00
|
|
|
tocHtml += '</div>';
|
|
|
|
tocHtml += '<ul class="toc">';
|
|
|
|
rendition.book.navigation.forEach((chapter) => {
|
|
|
|
tocHtml += '<li>';
|
|
|
|
// Remove '../' from href
|
|
|
|
let link = chapter.href.startsWith('../') ? chapter.href.substr(3) : chapter.href;
|
|
|
|
tocHtml += `<a href="${rendition.book.path.directory + link}">${chapter.label}</a>`;
|
|
|
|
tocHtml += '</li>';
|
|
|
|
});
|
|
|
|
tocHtml += '</ul>';
|
|
|
|
tocElement.innerHTML = tocHtml;
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-28 18:45:28 +10:00
|
|
|
tocElement.querySelector('.btnBookplayerTocClose').addEventListener('click', () => {
|
2020-05-22 10:42:58 +09:00
|
|
|
dialogHelper.close(tocElement);
|
|
|
|
});
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
this.replaceLinks(tocElement, (href) => {
|
|
|
|
let relative = rendition.book.path.relative(href);
|
|
|
|
rendition.display(relative);
|
|
|
|
dialogHelper.close(tocElement);
|
|
|
|
});
|
2020-05-19 16:05:44 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
this._tocElement = tocElement;
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
dialogHelper.open(tocElement);
|
|
|
|
}
|
2020-05-28 18:45:28 +10:00
|
|
|
});
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
dialogHelper.open(elem);
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
elem.addEventListener('close', this.onDialogClosed.bind(this));
|
2020-05-16 22:02:52 +10:00
|
|
|
}
|
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
this._mediaElement = elem;
|
|
|
|
|
|
|
|
return elem;
|
|
|
|
}
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
setCurrentSrc(elem, options) {
|
2020-05-28 18:39:49 +10:00
|
|
|
let item = options.items[0];
|
|
|
|
if (!item.Path.endsWith('.epub')) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
let errorDialog = dialogHelper.createDialog({
|
|
|
|
size: 'small',
|
|
|
|
autoFocus: false,
|
|
|
|
removeOnClose: true
|
|
|
|
});
|
|
|
|
|
|
|
|
errorDialog.innerHTML = '<h1 class="bookplayerErrorMsg">This book type is not supported yet</h1>';
|
|
|
|
|
|
|
|
this.stop();
|
|
|
|
|
|
|
|
dialogHelper.open(errorDialog);
|
|
|
|
loading.hide();
|
|
|
|
|
|
|
|
return resolve();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
let serverId = item.ServerId;
|
2020-05-22 10:42:58 +09:00
|
|
|
let apiClient = connectionManager.getApiClient(serverId);
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-28 18:45:28 +10:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
require(['epubjs'], (epubjs) => {
|
2020-05-28 18:39:49 +10:00
|
|
|
let downloadHref = apiClient.getItemDownloadUrl(item.Id);
|
2020-05-22 10:42:58 +09:00
|
|
|
let book = epubjs.default(downloadHref, {openAs: 'epub'});
|
|
|
|
let rendition = book.renderTo(elem, {width: '100%', height: '97%'});
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-28 18:45:28 +10:00
|
|
|
this._currentSrc = downloadHref;
|
|
|
|
this._rendition = rendition;
|
|
|
|
return rendition.display().then(() => {
|
|
|
|
document.addEventListener('keyup', this.onWindowKeyUp.bind(this));
|
2020-05-22 10:55:34 +09:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
// FIXME: I don't really get why document keyup event is not triggered when epub is in focus
|
2020-05-28 18:45:28 +10:00
|
|
|
this._rendition.on('keyup', this.onWindowKeyUp.bind(this));
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:42:58 +09:00
|
|
|
loading.hide();
|
|
|
|
return resolve();
|
2020-05-28 18:45:28 +10:00
|
|
|
}, () => {
|
2020-05-22 10:42:58 +09:00
|
|
|
console.error('Failed to display epub');
|
|
|
|
return reject();
|
2020-05-16 22:02:52 +10:00
|
|
|
});
|
|
|
|
});
|
2020-05-22 10:42:58 +09:00
|
|
|
});
|
|
|
|
}
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:39:21 +09:00
|
|
|
canPlayMediaType(mediaType) {
|
2020-05-16 22:02:52 +10:00
|
|
|
return (mediaType || '').toLowerCase() === 'book';
|
2020-05-22 10:39:21 +09:00
|
|
|
}
|
|
|
|
}
|
2020-05-16 22:02:52 +10:00
|
|
|
|
2020-05-22 10:39:21 +09:00
|
|
|
export default BookPlayer;
|