mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #1263 from itegulov/books
Implement minimalistic EPUB reader
This commit is contained in:
commit
9cae07205c
9 changed files with 524 additions and 5 deletions
|
@ -59,6 +59,7 @@
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"date-fns": "^2.14.0",
|
"date-fns": "^2.14.0",
|
||||||
"document-register-element": "^1.14.3",
|
"document-register-element": "^1.14.3",
|
||||||
|
"epubjs": "^0.3.85",
|
||||||
"fast-text-encoding": "^1.0.1",
|
"fast-text-encoding": "^1.0.1",
|
||||||
"flv.js": "^1.5.0",
|
"flv.js": "^1.5.0",
|
||||||
"headroom.js": "^0.11.0",
|
"headroom.js": "^0.11.0",
|
||||||
|
@ -97,6 +98,8 @@
|
||||||
"src/components/playback/mediasession.js",
|
"src/components/playback/mediasession.js",
|
||||||
"src/components/sanatizefilename.js",
|
"src/components/sanatizefilename.js",
|
||||||
"src/components/scrollManager.js",
|
"src/components/scrollManager.js",
|
||||||
|
"src/components/bookPlayer/plugin.js",
|
||||||
|
"src/components/bookPlayer/tableOfContent.js",
|
||||||
"src/components/syncplay/playbackPermissionManager.js",
|
"src/components/syncplay/playbackPermissionManager.js",
|
||||||
"src/components/syncplay/groupSelectionMenu.js",
|
"src/components/syncplay/groupSelectionMenu.js",
|
||||||
"src/components/syncplay/timeSyncManager.js",
|
"src/components/syncplay/timeSyncManager.js",
|
||||||
|
|
|
@ -102,6 +102,11 @@ _define('jellyfin-noto', function () {
|
||||||
return noto;
|
return noto;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var epubjs = require('epubjs');
|
||||||
|
_define('epubjs', function () {
|
||||||
|
return epubjs;
|
||||||
|
});
|
||||||
|
|
||||||
// page.js
|
// page.js
|
||||||
var page = require('page');
|
var page = require('page');
|
||||||
_define('page', function() {
|
_define('page', function() {
|
||||||
|
|
288
src/components/bookPlayer/plugin.js
Normal file
288
src/components/bookPlayer/plugin.js
Normal file
|
@ -0,0 +1,288 @@
|
||||||
|
import connectionManager from 'connectionManager';
|
||||||
|
import loading from 'loading';
|
||||||
|
import keyboardnavigation from 'keyboardnavigation';
|
||||||
|
import dialogHelper from 'dialogHelper';
|
||||||
|
import events from 'events';
|
||||||
|
import 'css!./style';
|
||||||
|
import 'material-icons';
|
||||||
|
import 'paper-icon-button-light';
|
||||||
|
|
||||||
|
import TableOfContent from './tableOfContent';
|
||||||
|
|
||||||
|
export class BookPlayer {
|
||||||
|
constructor() {
|
||||||
|
this.name = 'Book Player';
|
||||||
|
this.type = 'mediaplayer';
|
||||||
|
this.id = 'bookplayer';
|
||||||
|
this.priority = 1;
|
||||||
|
|
||||||
|
this.onDialogClosed = this.onDialogClosed.bind(this);
|
||||||
|
this.openTableOfContents = this.openTableOfContents.bind(this);
|
||||||
|
this.onWindowKeyUp = this.onWindowKeyUp.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
play(options) {
|
||||||
|
this._progress = 0;
|
||||||
|
this._loaded = false;
|
||||||
|
|
||||||
|
loading.show();
|
||||||
|
let elem = this.createMediaElement();
|
||||||
|
return this.setCurrentSrc(elem, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.unbindEvents();
|
||||||
|
|
||||||
|
let elem = this._mediaElement;
|
||||||
|
let tocElement = this._tocElement;
|
||||||
|
let rendition = this._rendition;
|
||||||
|
|
||||||
|
if (elem) {
|
||||||
|
dialogHelper.close(elem);
|
||||||
|
this._mediaElement = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tocElement) {
|
||||||
|
tocElement.destroy();
|
||||||
|
this._tocElement = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendition) {
|
||||||
|
rendition.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide loader in case player was not fully loaded yet
|
||||||
|
loading.hide();
|
||||||
|
this._cancellationToken.shouldCancel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentItem() {
|
||||||
|
return this._currentItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTime() {
|
||||||
|
return this._progress * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
duration() {
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBufferedRanges() {
|
||||||
|
return [{
|
||||||
|
start: 0,
|
||||||
|
end: 10000000
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
volume() {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
isMuted() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
paused() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
seekable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
onWindowKeyUp(e) {
|
||||||
|
let key = keyboardnavigation.getKeyName(e);
|
||||||
|
let rendition = this._rendition;
|
||||||
|
let book = rendition.book;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 'l':
|
||||||
|
case 'ArrowRight':
|
||||||
|
case 'Right':
|
||||||
|
if (this._loaded) {
|
||||||
|
book.package.metadata.direction === 'rtl' ? rendition.prev() : rendition.next();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
case 'ArrowLeft':
|
||||||
|
case 'Left':
|
||||||
|
if (this._loaded) {
|
||||||
|
book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Escape':
|
||||||
|
if (this._tocElement) {
|
||||||
|
// Close table of contents on ESC if it is open
|
||||||
|
this._tocElement.destroy();
|
||||||
|
} else {
|
||||||
|
// Otherwise stop the entire book player
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDialogClosed() {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
bindMediaElementEvents() {
|
||||||
|
let elem = this._mediaElement;
|
||||||
|
|
||||||
|
elem.addEventListener('close', this.onDialogClosed, {once: true});
|
||||||
|
elem.querySelector('.btnBookplayerExit').addEventListener('click', this.onDialogClosed, {once: true});
|
||||||
|
elem.querySelector('.btnBookplayerToc').addEventListener('click', this.openTableOfContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
this.bindMediaElementEvents();
|
||||||
|
|
||||||
|
document.addEventListener('keyup', this.onWindowKeyUp);
|
||||||
|
// FIXME: I don't really get why document keyup event is not triggered when epub is in focus
|
||||||
|
this._rendition.on('keyup', this.onWindowKeyUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbindMediaElementEvents() {
|
||||||
|
let elem = this._mediaElement;
|
||||||
|
|
||||||
|
elem.removeEventListener('close', this.onDialogClosed);
|
||||||
|
elem.querySelector('.btnBookplayerExit').removeEventListener('click', this.onDialogClosed);
|
||||||
|
elem.querySelector('.btnBookplayerToc').removeEventListener('click', this.openTableOfContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
unbindEvents() {
|
||||||
|
if (this._mediaElement) {
|
||||||
|
this.unbindMediaElementEvents();
|
||||||
|
}
|
||||||
|
document.removeEventListener('keyup', this.onWindowKeyUp);
|
||||||
|
if (this._rendition) {
|
||||||
|
this._rendition.off('keyup', this.onWindowKeyUp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openTableOfContents() {
|
||||||
|
if (this._loaded) {
|
||||||
|
this._tocElement = new TableOfContent(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createMediaElement() {
|
||||||
|
let elem = this._mediaElement;
|
||||||
|
|
||||||
|
if (elem) {
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem = document.getElementById('bookPlayer');
|
||||||
|
|
||||||
|
if (!elem) {
|
||||||
|
elem = dialogHelper.createDialog({
|
||||||
|
exitAnimationDuration: 400,
|
||||||
|
size: 'fullscreen',
|
||||||
|
autoFocus: false,
|
||||||
|
scrollY: false,
|
||||||
|
exitAnimation: 'fadeout',
|
||||||
|
removeOnClose: true
|
||||||
|
});
|
||||||
|
elem.id = 'bookPlayer';
|
||||||
|
|
||||||
|
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>';
|
||||||
|
|
||||||
|
elem.innerHTML = html;
|
||||||
|
|
||||||
|
dialogHelper.open(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._mediaElement = elem;
|
||||||
|
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentSrc(elem, options) {
|
||||||
|
let item = options.items[0];
|
||||||
|
this._currentItem = item;
|
||||||
|
this.streamInfo = {
|
||||||
|
started: true,
|
||||||
|
ended: false,
|
||||||
|
mediaSource: {
|
||||||
|
Id: item.Id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
let apiClient = connectionManager.getApiClient(serverId);
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
require(['epubjs'], (epubjs) => {
|
||||||
|
let downloadHref = apiClient.getItemDownloadUrl(item.Id);
|
||||||
|
let book = epubjs.default(downloadHref, {openAs: 'epub'});
|
||||||
|
let rendition = book.renderTo(elem, {width: '100%', height: '97%'});
|
||||||
|
|
||||||
|
this._currentSrc = downloadHref;
|
||||||
|
this._rendition = rendition;
|
||||||
|
let cancellationToken = {
|
||||||
|
shouldCancel: false
|
||||||
|
};
|
||||||
|
this._cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
return rendition.display().then(() => {
|
||||||
|
let epubElem = document.querySelector('.epub-container');
|
||||||
|
epubElem.style.display = 'none';
|
||||||
|
|
||||||
|
this.bindEvents();
|
||||||
|
|
||||||
|
return this._rendition.book.locations.generate(1024).then(() => {
|
||||||
|
if (cancellationToken.shouldCancel) {
|
||||||
|
return reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._loaded = true;
|
||||||
|
epubElem.style.display = 'block';
|
||||||
|
rendition.on('relocated', (locations) => {
|
||||||
|
this._progress = book.locations.percentageFromCfi(locations.start.cfi);
|
||||||
|
|
||||||
|
events.trigger(this, 'timeupdate');
|
||||||
|
});
|
||||||
|
|
||||||
|
loading.hide();
|
||||||
|
|
||||||
|
return resolve();
|
||||||
|
});
|
||||||
|
}, () => {
|
||||||
|
console.error('Failed to display epub');
|
||||||
|
return reject();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
canPlayMediaType(mediaType) {
|
||||||
|
return (mediaType || '').toLowerCase() === 'book';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BookPlayer;
|
39
src/components/bookPlayer/style.css
Normal file
39
src/components/bookPlayer/style.css
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#bookPlayer {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
z-index: 100;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topRightActionButtons {
|
||||||
|
right: 0.5vh;
|
||||||
|
top: 0.5vh;
|
||||||
|
z-index: 1002;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topLeftActionButtons {
|
||||||
|
left: 0.5vh;
|
||||||
|
top: 0.5vh;
|
||||||
|
z-index: 1002;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookplayerButtonIcon {
|
||||||
|
color: black;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dialogToc {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc li {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookplayerErrorMsg {
|
||||||
|
text-align: center;
|
||||||
|
}
|
90
src/components/bookPlayer/tableOfContent.js
Normal file
90
src/components/bookPlayer/tableOfContent.js
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import dialogHelper from 'dialogHelper';
|
||||||
|
|
||||||
|
export default class TableOfContent {
|
||||||
|
constructor(bookPlayer) {
|
||||||
|
this._bookPlayer = bookPlayer;
|
||||||
|
this._rendition = bookPlayer._rendition;
|
||||||
|
|
||||||
|
this.onDialogClosed = this.onDialogClosed.bind(this);
|
||||||
|
|
||||||
|
this.createMediaElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
let elem = this._elem;
|
||||||
|
if (elem) {
|
||||||
|
this.unbindEvents();
|
||||||
|
dialogHelper.close(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._bookPlayer._tocElement = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bindEvents() {
|
||||||
|
let elem = this._elem;
|
||||||
|
|
||||||
|
elem.addEventListener('close', this.onDialogClosed, {once: true});
|
||||||
|
elem.querySelector('.btnBookplayerTocClose').addEventListener('click', this.onDialogClosed, {once: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
unbindEvents() {
|
||||||
|
let elem = this._elem;
|
||||||
|
|
||||||
|
elem.removeEventListener('close', this.onDialogClosed);
|
||||||
|
elem.querySelector('.btnBookplayerTocClose').removeEventListener('click', this.onDialogClosed);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDialogClosed() {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceLinks(contents, f) {
|
||||||
|
let links = contents.querySelectorAll('a[href]');
|
||||||
|
|
||||||
|
links.forEach((link) => {
|
||||||
|
let href = link.getAttribute('href');
|
||||||
|
|
||||||
|
link.onclick = () => {
|
||||||
|
f(href);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
createMediaElement() {
|
||||||
|
let rendition = this._rendition;
|
||||||
|
|
||||||
|
let elem = dialogHelper.createDialog({
|
||||||
|
size: 'small',
|
||||||
|
autoFocus: false,
|
||||||
|
removeOnClose: true
|
||||||
|
});
|
||||||
|
elem.id = 'dialogToc';
|
||||||
|
|
||||||
|
let tocHtml = '<div class="topRightActionButtons">';
|
||||||
|
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>';
|
||||||
|
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>';
|
||||||
|
elem.innerHTML = tocHtml;
|
||||||
|
|
||||||
|
this.replaceLinks(elem, (href) => {
|
||||||
|
let relative = rendition.book.path.relative(href);
|
||||||
|
rendition.display(relative);
|
||||||
|
this.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
this._elem = elem;
|
||||||
|
|
||||||
|
this.bindEvents();
|
||||||
|
|
||||||
|
dialogHelper.open(elem);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2187,7 +2187,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
|
||||||
// Only used internally
|
// Only used internally
|
||||||
self.getCurrentTicks = getCurrentTicks;
|
self.getCurrentTicks = getCurrentTicks;
|
||||||
|
|
||||||
function playPhotos(items, options, user) {
|
function playOther(items, options, user) {
|
||||||
|
|
||||||
var playStartIndex = options.startIndex || 0;
|
var playStartIndex = options.startIndex || 0;
|
||||||
var player = getPlayer(items[playStartIndex], options);
|
var player = getPlayer(items[playStartIndex], options);
|
||||||
|
@ -2216,9 +2216,9 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstItem.MediaType === 'Photo') {
|
if (firstItem.MediaType === 'Photo' || firstItem.MediaType === 'Book') {
|
||||||
|
|
||||||
return playPhotos(items, options, user);
|
return playOther(items, options, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
var apiClient = connectionManager.getApiClient(firstItem.ServerId);
|
var apiClient = connectionManager.getApiClient(firstItem.ServerId);
|
||||||
|
|
|
@ -58,7 +58,7 @@ define(['events', 'globalize'], function (events, globalize) {
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
require([pluginSpec], (pluginFactory) => {
|
require([pluginSpec], (pluginFactory) => {
|
||||||
var plugin = new pluginFactory();
|
var plugin = pluginFactory.default ? new pluginFactory.default() : new pluginFactory();
|
||||||
|
|
||||||
// See if it's already installed
|
// See if it's already installed
|
||||||
var existing = instance.pluginsList.filter(function (p) {
|
var existing = instance.pluginsList.filter(function (p) {
|
||||||
|
|
|
@ -491,6 +491,7 @@ var AppInfo = {};
|
||||||
'components/htmlAudioPlayer/plugin',
|
'components/htmlAudioPlayer/plugin',
|
||||||
'components/htmlVideoPlayer/plugin',
|
'components/htmlVideoPlayer/plugin',
|
||||||
'components/photoPlayer/plugin',
|
'components/photoPlayer/plugin',
|
||||||
|
'components/bookPlayer/plugin',
|
||||||
'components/youtubeplayer/plugin',
|
'components/youtubeplayer/plugin',
|
||||||
'components/backdropScreensaver/plugin',
|
'components/backdropScreensaver/plugin',
|
||||||
'components/logoScreensaver/plugin'
|
'components/logoScreensaver/plugin'
|
||||||
|
@ -678,6 +679,7 @@ var AppInfo = {};
|
||||||
'fetch',
|
'fetch',
|
||||||
'flvjs',
|
'flvjs',
|
||||||
'jstree',
|
'jstree',
|
||||||
|
'epubjs',
|
||||||
'jQuery',
|
'jQuery',
|
||||||
'hlsjs',
|
'hlsjs',
|
||||||
'howler',
|
'howler',
|
||||||
|
|
94
yarn.lock
94
yarn.lock
|
@ -940,6 +940,20 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880"
|
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880"
|
||||||
integrity sha512-iYCgjm1dGPRuo12+BStjd1HiVQqhlRhWDOQigNxn023HcjnhsiFz9pc6CzJj4HwDCSQca9bxTL4PxJDbkdm3PA==
|
integrity sha512-iYCgjm1dGPRuo12+BStjd1HiVQqhlRhWDOQigNxn023HcjnhsiFz9pc6CzJj4HwDCSQca9bxTL4PxJDbkdm3PA==
|
||||||
|
|
||||||
|
"@types/jszip@^3.4.1":
|
||||||
|
version "3.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/jszip/-/jszip-3.4.1.tgz#e7a4059486e494c949ef750933d009684227846f"
|
||||||
|
integrity sha512-TezXjmf3lj+zQ651r6hPqvSScqBLvyPI9FxdXBqpEwBijNGQ2NXpaFW/7joGzveYkKQUil7iiDHLo6LV71Pc0A==
|
||||||
|
dependencies:
|
||||||
|
jszip "*"
|
||||||
|
|
||||||
|
"@types/localforage@0.0.34":
|
||||||
|
version "0.0.34"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/localforage/-/localforage-0.0.34.tgz#5e31c32dd8791ec4b9ff3ef47c9cb55b2d0d9438"
|
||||||
|
integrity sha1-XjHDLdh5HsS5/z70fJy1Wy0NlDg=
|
||||||
|
dependencies:
|
||||||
|
localforage "*"
|
||||||
|
|
||||||
"@types/minimatch@*":
|
"@types/minimatch@*":
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
||||||
|
@ -3919,6 +3933,23 @@ entities@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4"
|
||||||
integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
|
integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==
|
||||||
|
|
||||||
|
epubjs@^0.3.85:
|
||||||
|
version "0.3.87"
|
||||||
|
resolved "https://registry.yarnpkg.com/epubjs/-/epubjs-0.3.87.tgz#0a2a94e59777e04548deff49a1c713ccbf3378fc"
|
||||||
|
integrity sha512-UlzXj04JQaUJ4p6ux/glQcVC4ayBtnpHT7niw4ozGy8EOQTAr8+/z7UZEHUmqQj4yHIoPYC4qGXtmzNqImWx1A==
|
||||||
|
dependencies:
|
||||||
|
"@types/jszip" "^3.4.1"
|
||||||
|
"@types/localforage" "0.0.34"
|
||||||
|
event-emitter "^0.3.5"
|
||||||
|
jszip "^3.4.0"
|
||||||
|
localforage "^1.7.3"
|
||||||
|
lodash "^4.17.15"
|
||||||
|
marks-pane "^1.0.9"
|
||||||
|
path-webpack "0.0.3"
|
||||||
|
stream-browserify "^2.0.1"
|
||||||
|
url-polyfill "^1.1.9"
|
||||||
|
xmldom "^0.1.27"
|
||||||
|
|
||||||
errno@^0.1.3, errno@~0.1.7:
|
errno@^0.1.3, errno@~0.1.7:
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
|
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
|
||||||
|
@ -5953,6 +5984,11 @@ imagemin@^7.0.0:
|
||||||
p-pipe "^3.0.0"
|
p-pipe "^3.0.0"
|
||||||
replace-ext "^1.0.0"
|
replace-ext "^1.0.0"
|
||||||
|
|
||||||
|
immediate@~3.0.5:
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||||
|
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
|
||||||
|
|
||||||
immutable@^3:
|
immutable@^3:
|
||||||
version "3.8.2"
|
version "3.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
|
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
|
||||||
|
@ -6751,6 +6787,16 @@ jstree@^3.3.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
jquery ">=1.9.1"
|
jquery ">=1.9.1"
|
||||||
|
|
||||||
|
jszip@*, jszip@^3.4.0:
|
||||||
|
version "3.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.4.0.tgz#1a69421fa5f0bb9bc222a46bca88182fba075350"
|
||||||
|
integrity sha512-gZAOYuPl4EhPTXT0GjhI3o+ZAz3su6EhLrKUoAivcKqyqC7laS5JEv4XWZND9BgcDcF83vI85yGbDmDR6UhrIg==
|
||||||
|
dependencies:
|
||||||
|
lie "~3.3.0"
|
||||||
|
pako "~1.0.2"
|
||||||
|
readable-stream "~2.3.6"
|
||||||
|
set-immediate-shim "~1.0.1"
|
||||||
|
|
||||||
junk@^3.1.0:
|
junk@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1"
|
resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1"
|
||||||
|
@ -6879,6 +6925,20 @@ levn@^0.3.0, levn@~0.3.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
resolved "https://github.com/jellyfin/JavascriptSubtitlesOctopus#58e9a3f1a7f7883556ee002545f445a430120639"
|
resolved "https://github.com/jellyfin/JavascriptSubtitlesOctopus#58e9a3f1a7f7883556ee002545f445a430120639"
|
||||||
|
|
||||||
|
lie@3.1.1:
|
||||||
|
version "3.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
|
||||||
|
integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
|
||||||
|
dependencies:
|
||||||
|
immediate "~3.0.5"
|
||||||
|
|
||||||
|
lie@~3.3.0:
|
||||||
|
version "3.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
|
||||||
|
integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
|
||||||
|
dependencies:
|
||||||
|
immediate "~3.0.5"
|
||||||
|
|
||||||
liftoff@^3.1.0:
|
liftoff@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3"
|
resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3"
|
||||||
|
@ -6971,6 +7031,13 @@ loader-utils@^2.0.0:
|
||||||
emojis-list "^3.0.0"
|
emojis-list "^3.0.0"
|
||||||
json5 "^2.1.2"
|
json5 "^2.1.2"
|
||||||
|
|
||||||
|
localforage@*, localforage@^1.7.3:
|
||||||
|
version "1.7.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.7.3.tgz#0082b3ca9734679e1bd534995bdd3b24cf10f204"
|
||||||
|
integrity sha512-1TulyYfc4udS7ECSBT2vwJksWbkwwTX8BzeUIiq8Y07Riy7bDAAnxDaPU/tWyOVmQAcWJIEIFP9lPfBGqVoPgQ==
|
||||||
|
dependencies:
|
||||||
|
lie "3.1.1"
|
||||||
|
|
||||||
localtunnel@1.9.2:
|
localtunnel@1.9.2:
|
||||||
version "1.9.2"
|
version "1.9.2"
|
||||||
resolved "https://registry.yarnpkg.com/localtunnel/-/localtunnel-1.9.2.tgz#0012fcabc29cf964c130a01858768aa2bb65b5af"
|
resolved "https://registry.yarnpkg.com/localtunnel/-/localtunnel-1.9.2.tgz#0012fcabc29cf964c130a01858768aa2bb65b5af"
|
||||||
|
@ -7341,6 +7408,11 @@ markdown-table@^2.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
repeat-string "^1.0.0"
|
repeat-string "^1.0.0"
|
||||||
|
|
||||||
|
marks-pane@^1.0.9:
|
||||||
|
version "1.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/marks-pane/-/marks-pane-1.0.9.tgz#c0b5ab813384d8cd81faaeb3bbf3397dc809c1b3"
|
||||||
|
integrity sha512-Ahs4oeG90tbdPWwAJkAAoHg2lRR8lAs9mZXETNPO9hYg3AkjUJBKi1NQ4aaIQZVGrig7c/3NUV1jANl8rFTeMg==
|
||||||
|
|
||||||
matchdep@^2.0.0:
|
matchdep@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e"
|
resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e"
|
||||||
|
@ -8411,7 +8483,7 @@ page@^1.11.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-to-regexp "~1.2.1"
|
path-to-regexp "~1.2.1"
|
||||||
|
|
||||||
pako@~1.0.5:
|
pako@~1.0.2, pako@~1.0.5:
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||||
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||||
|
@ -8658,6 +8730,11 @@ path-type@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||||
|
|
||||||
|
path-webpack@0.0.3:
|
||||||
|
version "0.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-webpack/-/path-webpack-0.0.3.tgz#ff6dec749eec5a94605c04d5f63fc55607a03a16"
|
||||||
|
integrity sha1-/23sdJ7sWpRgXATV9j/FVgegOhY=
|
||||||
|
|
||||||
pbkdf2@^3.0.3:
|
pbkdf2@^3.0.3:
|
||||||
version "3.0.17"
|
version "3.0.17"
|
||||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
|
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
|
||||||
|
@ -10614,6 +10691,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||||
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
||||||
|
|
||||||
|
set-immediate-shim@~1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
|
||||||
|
integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=
|
||||||
|
|
||||||
set-value@^2.0.0, set-value@^2.0.1:
|
set-value@^2.0.0, set-value@^2.0.1:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
|
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
|
||||||
|
@ -12292,6 +12374,11 @@ url-parse@^1.4.3:
|
||||||
querystringify "^2.1.1"
|
querystringify "^2.1.1"
|
||||||
requires-port "^1.0.0"
|
requires-port "^1.0.0"
|
||||||
|
|
||||||
|
url-polyfill@^1.1.9:
|
||||||
|
version "1.1.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/url-polyfill/-/url-polyfill-1.1.9.tgz#2c8d4224889a5c942800f708f5585368085603d9"
|
||||||
|
integrity sha512-q/R5sowGuRfKHm497swkV+s9cPYtZRkHxzpDjRhqLO58FwdWTIkt6Y/fJlznUD/exaKx/XnDzCYXz0V16ND7ow==
|
||||||
|
|
||||||
url-to-options@^1.0.1:
|
url-to-options@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
|
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
|
||||||
|
@ -12849,6 +12936,11 @@ x-is-string@^0.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82"
|
resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82"
|
||||||
integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=
|
integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=
|
||||||
|
|
||||||
|
xmldom@^0.1.27:
|
||||||
|
version "0.1.31"
|
||||||
|
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff"
|
||||||
|
integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==
|
||||||
|
|
||||||
xmlhttprequest-ssl@~1.5.4:
|
xmlhttprequest-ssl@~1.5.4:
|
||||||
version "1.5.5"
|
version "1.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
|
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue