debug
This commit is contained in:
parent
dbd82645da
commit
357ff5ffef
@ -123,6 +123,11 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
}, {once: true}); */
|
}, {once: true}); */
|
||||||
|
|
||||||
await promise;
|
await promise;
|
||||||
|
} else if(doc.supportsStreaming) {
|
||||||
|
preloader = new ProgressivePreloader(container, false);
|
||||||
|
video.addEventListener('canplay', () => {
|
||||||
|
preloader.detach();
|
||||||
|
}, {once: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(middleware && !middleware()) {
|
if(middleware && !middleware()) {
|
||||||
|
@ -16,6 +16,8 @@ export function logger(prefix: string, level = LogLevels.log | LogLevels.warn |
|
|||||||
level = LogLevels.error;
|
level = LogLevels.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
level = LogLevels.log | LogLevels.warn | LogLevels.error | LogLevels.debug
|
||||||
|
|
||||||
function Log(...args: any[]) {
|
function Log(...args: any[]) {
|
||||||
return level & LogLevels.log && console.log(dT(), '[' + prefix + ']:', ...args);
|
return level & LogLevels.log && console.log(dT(), '[' + prefix + ']:', ...args);
|
||||||
}
|
}
|
||||||
@ -37,8 +39,12 @@ export function logger(prefix: string, level = LogLevels.log | LogLevels.warn |
|
|||||||
};
|
};
|
||||||
|
|
||||||
Log.debug = function(...args: any[]) {
|
Log.debug = function(...args: any[]) {
|
||||||
return level & LogLevels.debug && console.debug(dT(), '[' + prefix + ']:', ...args);
|
return level & LogLevels.debug && console.log(dT(), '[' + prefix + ']:', ...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Log.debug = function(...args: any[]) {
|
||||||
|
return level & LogLevels.debug && console.debug(dT(), '[' + prefix + ']:', ...args);
|
||||||
|
}; */
|
||||||
|
|
||||||
return Log;
|
return Log;
|
||||||
};
|
};
|
@ -1,46 +1,132 @@
|
|||||||
export class MediaProgressLine {
|
export class ProgressLine {
|
||||||
public container: HTMLDivElement;
|
public container: HTMLDivElement;
|
||||||
private filled: HTMLDivElement;
|
protected filled: HTMLDivElement;
|
||||||
private filledLoad: HTMLDivElement;
|
protected seek: HTMLInputElement;
|
||||||
private seek: HTMLInputElement;
|
|
||||||
|
|
||||||
private duration = 0;
|
protected duration = 100;
|
||||||
private mousedown = false;
|
protected mousedown = false;
|
||||||
private stopAndScrubTimeout = 0;
|
|
||||||
private progressRAF = 0;
|
|
||||||
|
|
||||||
public onSeek: (time: number) => void;
|
private events: Partial<{
|
||||||
|
//onMouseMove: ProgressLine['onMouseMove'],
|
||||||
|
onMouseDown: ProgressLine['onMouseDown'],
|
||||||
|
onMouseUp: ProgressLine['onMouseUp'],
|
||||||
|
onScrub: (scrubTime: number) => void
|
||||||
|
}> = {};
|
||||||
|
|
||||||
constructor(private media: HTMLAudioElement | HTMLVideoElement, private streamable = false) {
|
constructor() {
|
||||||
this.container = document.createElement('div');
|
this.container = document.createElement('div');
|
||||||
this.container.classList.add('media-progress');
|
this.container.classList.add('media-progress');
|
||||||
|
|
||||||
this.filled = document.createElement('div');
|
this.filled = document.createElement('div');
|
||||||
this.filled.classList.add('media-progress__filled');
|
this.filled.classList.add('media-progress__filled');
|
||||||
|
|
||||||
if(streamable) {
|
const seek = this.seek = document.createElement('input');
|
||||||
this.filledLoad = document.createElement('div');
|
|
||||||
this.filledLoad.classList.add('media-progress__filled', 'media-progress__loaded');
|
|
||||||
this.container.append(this.filledLoad);
|
|
||||||
//this.setLoadProgress();
|
|
||||||
}
|
|
||||||
|
|
||||||
let seek = this.seek = document.createElement('input');
|
|
||||||
seek.classList.add('media-progress__seek');
|
seek.classList.add('media-progress__seek');
|
||||||
seek.value = '0';
|
seek.value = '0';
|
||||||
seek.setAttribute('min', '0');
|
seek.setAttribute('min', '0');
|
||||||
seek.setAttribute('max', '0');
|
seek.setAttribute('max', '0');
|
||||||
seek.type = 'range';
|
seek.type = 'range';
|
||||||
seek.step = '0.1';
|
seek.step = '0.1';
|
||||||
|
seek.max = '' + (this.duration * 1000);
|
||||||
|
|
||||||
this.setSeekMax();
|
//this.setListeners();
|
||||||
this.setListeners();
|
|
||||||
|
this.container.append(this.filled, seek);
|
||||||
|
}
|
||||||
|
|
||||||
|
public setHandlers(events: ProgressLine['events']) {
|
||||||
|
this.events = events;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseMove = (e: MouseEvent) => {
|
||||||
|
this.mousedown && this.scrub(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMouseDown = (e: MouseEvent) => {
|
||||||
|
this.scrub(e);
|
||||||
|
this.mousedown = true;
|
||||||
|
this.events?.onMouseDown(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMouseUp = (e: MouseEvent) => {
|
||||||
|
this.mousedown = false;
|
||||||
|
this.events?.onMouseUp(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
protected setListeners() {
|
||||||
|
this.container.addEventListener('mousemove', this.onMouseMove);
|
||||||
|
this.container.addEventListener('mousedown', this.onMouseDown);
|
||||||
|
this.container.addEventListener('mouseup', this.onMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected scrub(e: MouseEvent) {
|
||||||
|
const scrubTime = e.offsetX / this.container.offsetWidth * this.duration;
|
||||||
|
|
||||||
|
let scaleX = scrubTime / this.duration;
|
||||||
|
scaleX = Math.max(0, Math.min(1, scaleX));
|
||||||
|
this.filled.style.transform = 'scaleX(' + scaleX + ')';
|
||||||
|
|
||||||
|
//this.events?.onScrub(scrubTime);
|
||||||
|
return scrubTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeListeners() {
|
||||||
|
this.container.removeEventListener('mousemove', this.onMouseMove);
|
||||||
|
this.container.removeEventListener('mousedown', this.onMouseDown);
|
||||||
|
this.container.removeEventListener('mouseup', this.onMouseUp);
|
||||||
|
|
||||||
|
this.events = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MediaProgressLine extends ProgressLine {
|
||||||
|
private filledLoad: HTMLDivElement;
|
||||||
|
|
||||||
|
private stopAndScrubTimeout = 0;
|
||||||
|
private progressRAF = 0;
|
||||||
|
|
||||||
|
constructor(private media: HTMLAudioElement | HTMLVideoElement, private streamable = false) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
if(streamable) {
|
||||||
|
this.filledLoad = document.createElement('div');
|
||||||
|
this.filledLoad.classList.add('media-progress__filled', 'media-progress__loaded');
|
||||||
|
this.container.prepend(this.filledLoad);
|
||||||
|
//this.setLoadProgress();
|
||||||
|
}
|
||||||
|
|
||||||
if(!media.paused || media.currentTime > 0) {
|
if(!media.paused || media.currentTime > 0) {
|
||||||
this.onPlay();
|
this.onPlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.container.append(this.filled, seek);
|
this.setSeekMax();
|
||||||
|
this.setListeners();
|
||||||
|
this.setHandlers({
|
||||||
|
onMouseDown: (e: MouseEvent) => {
|
||||||
|
//super.onMouseDown(e);
|
||||||
|
|
||||||
|
//Таймер для того, чтобы стопать видео, если зажал мышку и не отпустил клик
|
||||||
|
if(this.stopAndScrubTimeout) { // возможно лишнее
|
||||||
|
clearTimeout(this.stopAndScrubTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.stopAndScrubTimeout = setTimeout(() => {
|
||||||
|
!this.media.paused && this.media.pause();
|
||||||
|
this.stopAndScrubTimeout = 0;
|
||||||
|
}, 150);
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseUp: (e: MouseEvent) => {
|
||||||
|
//super.onMouseUp(e);
|
||||||
|
|
||||||
|
if(this.stopAndScrubTimeout) {
|
||||||
|
clearTimeout(this.stopAndScrubTimeout);
|
||||||
|
this.stopAndScrubTimeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.media.paused && this.media.play();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoadedData = () => {
|
onLoadedData = () => {
|
||||||
@ -70,42 +156,17 @@ export class MediaProgressLine {
|
|||||||
this.progressRAF = window.requestAnimationFrame(r);
|
this.progressRAF = window.requestAnimationFrame(r);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMouseMove = (e: MouseEvent) => {
|
|
||||||
this.mousedown && this.scrub(e);
|
|
||||||
};
|
|
||||||
|
|
||||||
onMouseDown = (e: MouseEvent) => {
|
|
||||||
this.media.pause();
|
|
||||||
this.scrub(e);
|
|
||||||
|
|
||||||
//Таймер для того, чтобы стопать видео, если зажал мышку и не отпустил клик
|
|
||||||
if(this.stopAndScrubTimeout) { // возможно лишнее
|
|
||||||
clearTimeout(this.stopAndScrubTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.stopAndScrubTimeout = setTimeout(() => {
|
|
||||||
!this.media.paused && this.media.pause();
|
|
||||||
this.stopAndScrubTimeout = 0;
|
|
||||||
}, 150);
|
|
||||||
|
|
||||||
this.mousedown = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
onMouseUp = (e: MouseEvent) => {
|
|
||||||
if(this.stopAndScrubTimeout) {
|
|
||||||
clearTimeout(this.stopAndScrubTimeout);
|
|
||||||
this.stopAndScrubTimeout = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.media.paused && this.media.play();
|
|
||||||
this.mousedown = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
onProgress = (e: Event) => {
|
onProgress = (e: Event) => {
|
||||||
this.setLoadProgress();
|
this.setLoadProgress();
|
||||||
};
|
};
|
||||||
|
|
||||||
private setLoadProgress() {
|
protected scrub(e: MouseEvent) {
|
||||||
|
const scrubTime = super.scrub(e);
|
||||||
|
this.media.currentTime = scrubTime;
|
||||||
|
return scrubTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected setLoadProgress() {
|
||||||
const buf = this.media.buffered;
|
const buf = this.media.buffered;
|
||||||
const numRanges = buf.length;
|
const numRanges = buf.length;
|
||||||
|
|
||||||
@ -127,7 +188,7 @@ export class MediaProgressLine {
|
|||||||
this.filledLoad.style.transform = 'scaleX(' + percents + ')';
|
this.filledLoad.style.transform = 'scaleX(' + percents + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
private setSeekMax() {
|
protected setSeekMax() {
|
||||||
this.duration = this.media.duration;
|
this.duration = this.media.duration;
|
||||||
if(this.duration > 0) {
|
if(this.duration > 0) {
|
||||||
this.onLoadedData();
|
this.onLoadedData();
|
||||||
@ -136,7 +197,7 @@ export class MediaProgressLine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private setProgress() {
|
protected setProgress() {
|
||||||
const currentTime = this.media.currentTime;
|
const currentTime = this.media.currentTime;
|
||||||
|
|
||||||
const scaleX = (currentTime / this.duration);
|
const scaleX = (currentTime / this.duration);
|
||||||
@ -144,41 +205,21 @@ export class MediaProgressLine {
|
|||||||
this.seek.value = '' + currentTime * 1000;
|
this.seek.value = '' + currentTime * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setListeners() {
|
protected setListeners() {
|
||||||
|
super.setListeners();
|
||||||
this.media.addEventListener('ended', this.onEnded);
|
this.media.addEventListener('ended', this.onEnded);
|
||||||
this.media.addEventListener('play', this.onPlay);
|
this.media.addEventListener('play', this.onPlay);
|
||||||
this.streamable && this.media.addEventListener('progress', this.onProgress);
|
this.streamable && this.media.addEventListener('progress', this.onProgress);
|
||||||
|
|
||||||
this.container.addEventListener('mousemove', this.onMouseMove);
|
|
||||||
this.container.addEventListener('mousedown', this.onMouseDown);
|
|
||||||
this.container.addEventListener('mouseup', this.onMouseUp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private scrub(e: MouseEvent) {
|
|
||||||
const scrubTime = e.offsetX / this.container.offsetWidth * this.duration;
|
|
||||||
this.media.currentTime = scrubTime;
|
|
||||||
|
|
||||||
if(this.onSeek) {
|
|
||||||
this.onSeek(scrubTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
let scaleX = scrubTime / this.duration;
|
|
||||||
scaleX = Math.max(0, Math.min(1, scaleX));
|
|
||||||
this.filled.style.transform = 'scaleX(' + scaleX + ')';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeListeners() {
|
public removeListeners() {
|
||||||
|
super.removeListeners();
|
||||||
|
|
||||||
this.media.removeEventListener('loadeddata', this.onLoadedData);
|
this.media.removeEventListener('loadeddata', this.onLoadedData);
|
||||||
this.media.removeEventListener('ended', this.onEnded);
|
this.media.removeEventListener('ended', this.onEnded);
|
||||||
this.media.removeEventListener('play', this.onPlay);
|
this.media.removeEventListener('play', this.onPlay);
|
||||||
this.streamable && this.media.removeEventListener('progress', this.onProgress);
|
this.streamable && this.media.removeEventListener('progress', this.onProgress);
|
||||||
|
|
||||||
this.container.removeEventListener('mousemove', this.onMouseMove);
|
|
||||||
this.container.removeEventListener('mousedown', this.onMouseDown);
|
|
||||||
this.container.removeEventListener('mouseup', this.onMouseUp);
|
|
||||||
|
|
||||||
this.onSeek = null;
|
|
||||||
|
|
||||||
if(this.stopAndScrubTimeout) {
|
if(this.stopAndScrubTimeout) {
|
||||||
clearTimeout(this.stopAndScrubTimeout);
|
clearTimeout(this.stopAndScrubTimeout);
|
||||||
}
|
}
|
||||||
@ -217,21 +258,19 @@ export default class VideoPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private stylePlayer() {
|
private stylePlayer() {
|
||||||
let player = this.wrapper;
|
const {wrapper: player, video, skin} = this;
|
||||||
let video = this.video;
|
|
||||||
|
|
||||||
let skin = this.skin;
|
|
||||||
player.classList.add(skin);
|
player.classList.add(skin);
|
||||||
|
|
||||||
let html = this.buildControls();
|
const html = this.buildControls();
|
||||||
player.insertAdjacentHTML('beforeend', html);
|
player.insertAdjacentHTML('beforeend', html);
|
||||||
let updateInterval = 0;
|
let updateInterval = 0;
|
||||||
let elapsed = 0;
|
let elapsed = 0;
|
||||||
let prevTime = 0;
|
let prevTime = 0;
|
||||||
|
|
||||||
if(skin === 'default') {
|
if(skin === 'default') {
|
||||||
var toggle = player.querySelectorAll('.toggle') as NodeListOf<HTMLElement>;
|
const toggle = player.querySelectorAll('.toggle') as NodeListOf<HTMLElement>;
|
||||||
var fullScreenButton = player.querySelector('.fullscreen') as HTMLElement;
|
const fullScreenButton = player.querySelector('.fullscreen') as HTMLElement;
|
||||||
var timeElapsed = player.querySelector('#time-elapsed');
|
var timeElapsed = player.querySelector('#time-elapsed');
|
||||||
var timeDuration = player.querySelector('#time-duration') as HTMLElement;
|
var timeDuration = player.querySelector('#time-duration') as HTMLElement;
|
||||||
timeDuration.innerHTML = String(video.duration | 0).toHHMMSS();
|
timeDuration.innerHTML = String(video.duration | 0).toHHMMSS();
|
||||||
@ -263,23 +302,20 @@ export default class VideoPlayer {
|
|||||||
return this.toggleFullScreen(fullScreenButton);
|
return this.toggleFullScreen(fullScreenButton);
|
||||||
});
|
});
|
||||||
|
|
||||||
let b = () => this.onFullScreen();
|
|
||||||
'webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange'.split(' ').forEach(eventName => {
|
'webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange'.split(' ').forEach(eventName => {
|
||||||
player.addEventListener(eventName, b, false);
|
player.addEventListener(eventName, this.onFullScreen, false);
|
||||||
});
|
});
|
||||||
}
|
} else if(skin === 'circle') {
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
if(skin === 'circle') {
|
|
||||||
let wrapper = document.createElement('div');
|
|
||||||
wrapper.classList.add('circle-time-left');
|
wrapper.classList.add('circle-time-left');
|
||||||
video.parentNode.insertBefore(wrapper, video);
|
video.parentNode.insertBefore(wrapper, video);
|
||||||
wrapper.innerHTML = '<div class="circle-time"></div><div class="iconVolume tgico-nosound"></div>';
|
wrapper.innerHTML = '<div class="circle-time"></div><div class="iconVolume tgico-nosound"></div>';
|
||||||
|
|
||||||
var circle = player.querySelector('.progress-ring__circle') as SVGCircleElement;
|
var circle = player.querySelector('.progress-ring__circle') as SVGCircleElement;
|
||||||
var radius = circle.r.baseVal.value;
|
const radius = circle.r.baseVal.value;
|
||||||
var circumference = 2 * Math.PI * radius;
|
var circumference = 2 * Math.PI * radius;
|
||||||
var timeDuration = player.querySelector('.circle-time') as HTMLElement;
|
var timeDuration = player.querySelector('.circle-time') as HTMLElement;
|
||||||
var iconVolume = player.querySelector('.iconVolume') as HTMLDivElement;
|
const iconVolume = player.querySelector('.iconVolume') as HTMLDivElement;
|
||||||
circle.style.strokeDasharray = circumference + ' ' + circumference;
|
circle.style.strokeDasharray = circumference + ' ' + circumference;
|
||||||
circle.style.strokeDashoffset = '' + circumference;
|
circle.style.strokeDashoffset = '' + circumference;
|
||||||
circle.addEventListener('click', () => {
|
circle.addEventListener('click', () => {
|
||||||
@ -295,7 +331,7 @@ export default class VideoPlayer {
|
|||||||
prevTime = video.currentTime;
|
prevTime = video.currentTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
let offset = circumference - elapsed / video.duration * circumference;
|
const offset = circumference - elapsed / video.duration * circumference;
|
||||||
circle.style.strokeDashoffset = '' + offset;
|
circle.style.strokeDashoffset = '' + offset;
|
||||||
if(video.paused) clearInterval(updateInterval);
|
if(video.paused) clearInterval(updateInterval);
|
||||||
}, 20);
|
}, 20);
|
||||||
@ -339,8 +375,7 @@ export default class VideoPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private handleProgress(timeDuration: HTMLElement, circumference: number, circle: SVGCircleElement, updateInterval: number) {
|
private handleProgress(timeDuration: HTMLElement, circumference: number, circle: SVGCircleElement, updateInterval: number) {
|
||||||
let video = this.video;
|
const {video, skin} = this;
|
||||||
let skin = this.skin;
|
|
||||||
|
|
||||||
clearInterval(updateInterval);
|
clearInterval(updateInterval);
|
||||||
let elapsed = 0;
|
let elapsed = 0;
|
||||||
@ -352,12 +387,13 @@ export default class VideoPlayer {
|
|||||||
elapsed = video.currentTime; // Update if getCurrentTime was changed
|
elapsed = video.currentTime; // Update if getCurrentTime was changed
|
||||||
prevTime = video.currentTime;
|
prevTime = video.currentTime;
|
||||||
}
|
}
|
||||||
let offset = circumference - elapsed / video.duration * circumference;
|
|
||||||
|
const offset = circumference - elapsed / video.duration * circumference;
|
||||||
circle.style.strokeDashoffset = '' + offset;
|
circle.style.strokeDashoffset = '' + offset;
|
||||||
if(video.paused) clearInterval(updateInterval);
|
if(video.paused) clearInterval(updateInterval);
|
||||||
}, 20);
|
}, 20);
|
||||||
|
|
||||||
let timeLeft = String((video.duration - video.currentTime) | 0).toHHMMSS();
|
const timeLeft = String((video.duration - video.currentTime) | 0).toHHMMSS();
|
||||||
if(timeLeft != '0') timeDuration.innerHTML = timeLeft;
|
if(timeLeft != '0') timeDuration.innerHTML = timeLeft;
|
||||||
|
|
||||||
return updateInterval;
|
return updateInterval;
|
||||||
@ -365,22 +401,27 @@ export default class VideoPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private buildControls() {
|
private buildControls() {
|
||||||
let skin = this.skin;
|
const skin = this.skin;
|
||||||
let html = [];
|
const html: string[] = [];
|
||||||
if(skin === 'default') {
|
if(skin === 'default') {
|
||||||
html.push('<button class="' + skin + '__button--big toggle tgico-largeplay" title="Toggle Play"></button>');
|
html.push(`
|
||||||
html.push('<div class="' + skin + '__gradient-bottom ckin__controls"></div>');
|
<button class="${skin}__button--big toggle tgico-largeplay" title="Toggle Play"></button>
|
||||||
html.push('<div class="' + skin + '__controls ckin__controls">');
|
<div class="${skin}__gradient-bottom ckin__controls"></div>
|
||||||
html.push('<div class="bottom-controls">',
|
<div class="${skin}__controls ckin__controls">
|
||||||
'<div class="left-controls"><button class="' + skin + '__button toggle tgico-play" title="Toggle Video"></button>',
|
<div class="bottom-controls">
|
||||||
'<div class="time">',
|
<div class="left-controls">
|
||||||
'<time id="time-elapsed">0:00</time>',
|
<button class="${skin}__button toggle tgico-play" title="Toggle Video"></button>
|
||||||
'<span> / </span>',
|
<div class="time">
|
||||||
'<time id="time-duration">0:00</time>',
|
<time id="time-elapsed">0:00</time>
|
||||||
'</div>',
|
<span> / </span>
|
||||||
'</div>',
|
<time id="time-duration">0:00</time>
|
||||||
'<div class="right-controls"><button class="' + skin + '__button fullscreen tgico-fullscreen" title="Full Screen"></button></div></div>');
|
</div>
|
||||||
html.push('</div>');
|
</div>
|
||||||
|
<div class="right-controls">
|
||||||
|
<button class="${skin}__button fullscreen tgico-fullscreen" title="Full Screen"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`);
|
||||||
} else if(skin === 'circle') {
|
} else if(skin === 'circle') {
|
||||||
html.push('<svg class="progress-ring" width="200px" height="200px">',
|
html.push('<svg class="progress-ring" width="200px" height="200px">',
|
||||||
'<circle class="progress-ring__circle" stroke="white" stroke-opacity="0.3" stroke-width="3.5" cx="100" cy="100" r="93" fill="transparent" transform="rotate(-90, 100, 100)"/>',
|
'<circle class="progress-ring__circle" stroke="white" stroke-opacity="0.3" stroke-width="3.5" cx="100" cy="100" r="93" fill="transparent" transform="rotate(-90, 100, 100)"/>',
|
||||||
@ -391,7 +432,7 @@ export default class VideoPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public updateButton(toggle: NodeListOf<HTMLElement>) {
|
public updateButton(toggle: NodeListOf<HTMLElement>) {
|
||||||
let icon = this.video.paused ? 'tgico-play' : 'tgico-pause';
|
const icon = this.video.paused ? 'tgico-play' : 'tgico-pause';
|
||||||
Array.from(toggle).forEach((button) => {
|
Array.from(toggle).forEach((button) => {
|
||||||
button.classList.remove('tgico-play', 'tgico-pause');
|
button.classList.remove('tgico-play', 'tgico-pause');
|
||||||
button.classList.add(icon);
|
button.classList.add(icon);
|
||||||
@ -452,12 +493,12 @@ export default class VideoPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public onFullScreen() {
|
onFullScreen = () => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let isFullscreenNow = document.webkitFullscreenElement !== null;
|
const isFullscreenNow = document.webkitFullscreenElement !== null;
|
||||||
if(!isFullscreenNow) {
|
if(!isFullscreenNow) {
|
||||||
this.wrapper.classList.remove('ckin__fullscreen');
|
this.wrapper.classList.remove('ckin__fullscreen');
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,9 @@ const ctx = self as any as ServiceWorkerGlobalScope;
|
|||||||
|
|
||||||
//console.error('INCLUDE !!!', new Error().stack);
|
//console.error('INCLUDE !!!', new Error().stack);
|
||||||
|
|
||||||
function isObject(object: any) {
|
/* function isObject(object: any) {
|
||||||
return typeof(object) === 'object' && object !== null;
|
return typeof(object) === 'object' && object !== null;
|
||||||
}
|
} */
|
||||||
|
|
||||||
/* function fillTransfer(transfer: any, obj: any) {
|
/* function fillTransfer(transfer: any, obj: any) {
|
||||||
if(!obj) return;
|
if(!obj) return;
|
||||||
@ -79,70 +79,72 @@ networkerFactory.setUpdatesProcessor((obj, bool) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const onMessage = async(e: ExtendableMessageEvent) => {
|
const onMessage = async(e: ExtendableMessageEvent) => {
|
||||||
const taskID = e.data.taskID;
|
try {
|
||||||
|
const taskID = e.data.taskID;
|
||||||
|
|
||||||
log.debug('got message:', taskID, e, e.data);
|
log.debug('got message:', taskID, e, e.data);
|
||||||
|
|
||||||
if(e.data.useLs) {
|
if(e.data.useLs) {
|
||||||
AppStorage.finishTask(e.data.taskID, e.data.args);
|
AppStorage.finishTask(e.data.taskID, e.data.args);
|
||||||
return;
|
return;
|
||||||
} else if(e.data.type == 'convertWebp') {
|
} else if(e.data.type == 'convertWebp') {
|
||||||
const {fileName, bytes} = e.data.payload;
|
const {fileName, bytes} = e.data.payload;
|
||||||
const deferred = apiFileManager.webpConvertPromises[fileName];
|
const deferred = apiFileManager.webpConvertPromises[fileName];
|
||||||
if(deferred) {
|
if(deferred) {
|
||||||
deferred.resolve(bytes);
|
deferred.resolve(bytes);
|
||||||
delete apiFileManager.webpConvertPromises[fileName];
|
delete apiFileManager.webpConvertPromises[fileName];
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(e.data.task) {
|
|
||||||
case 'computeSRP':
|
|
||||||
case 'gzipUncompress':
|
|
||||||
// @ts-ignore
|
|
||||||
return cryptoWorker[e.data.task].apply(cryptoWorker, e.data.args).then(result => {
|
|
||||||
respond(e.source, {taskID: taskID, result: result});
|
|
||||||
});
|
|
||||||
|
|
||||||
case 'cancelDownload':
|
|
||||||
case 'downloadFile': {
|
|
||||||
/* // @ts-ignore
|
|
||||||
return apiFileManager.downloadFile(...e.data.args); */
|
|
||||||
|
|
||||||
try {
|
|
||||||
// @ts-ignore
|
|
||||||
let result = apiFileManager[e.data.task].apply(apiFileManager, e.data.args);
|
|
||||||
|
|
||||||
if(result instanceof Promise) {
|
|
||||||
result = await result;
|
|
||||||
}
|
|
||||||
|
|
||||||
respond(e.source, {taskID: taskID, result: result});
|
|
||||||
} catch(err) {
|
|
||||||
respond(e.source, {taskID: taskID, error: err});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
switch(e.data.task) {
|
||||||
try {
|
case 'computeSRP':
|
||||||
|
case 'gzipUncompress':
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let result = apiManager[e.data.task].apply(apiManager, e.data.args);
|
return cryptoWorker[e.data.task].apply(cryptoWorker, e.data.args).then(result => {
|
||||||
|
respond(e.source, {taskID: taskID, result: result});
|
||||||
if(result instanceof Promise) {
|
});
|
||||||
result = await result;
|
|
||||||
|
case 'cancelDownload':
|
||||||
|
case 'downloadFile': {
|
||||||
|
/* // @ts-ignore
|
||||||
|
return apiFileManager.downloadFile(...e.data.args); */
|
||||||
|
|
||||||
|
try {
|
||||||
|
// @ts-ignore
|
||||||
|
let result = apiFileManager[e.data.task].apply(apiFileManager, e.data.args);
|
||||||
|
|
||||||
|
if(result instanceof Promise) {
|
||||||
|
result = await result;
|
||||||
|
}
|
||||||
|
|
||||||
|
respond(e.source, {taskID: taskID, result: result});
|
||||||
|
} catch(err) {
|
||||||
|
respond(e.source, {taskID: taskID, error: err});
|
||||||
}
|
}
|
||||||
|
|
||||||
respond(e.source, {taskID: taskID, result: result});
|
|
||||||
} catch(err) {
|
|
||||||
respond(e.source, {taskID: taskID, error: err});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//throw new Error('Unknown task: ' + e.data.task);
|
default: {
|
||||||
|
try {
|
||||||
|
// @ts-ignore
|
||||||
|
let result = apiManager[e.data.task].apply(apiManager, e.data.args);
|
||||||
|
|
||||||
|
if(result instanceof Promise) {
|
||||||
|
result = await result;
|
||||||
|
}
|
||||||
|
|
||||||
|
respond(e.source, {taskID: taskID, result: result});
|
||||||
|
} catch(err) {
|
||||||
|
respond(e.source, {taskID: taskID, error: err});
|
||||||
|
}
|
||||||
|
|
||||||
|
//throw new Error('Unknown task: ' + e.data.task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch(err) {
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.onmessage = onMessage;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service Worker Installation
|
* Service Worker Installation
|
||||||
*/
|
*/
|
||||||
@ -201,257 +203,274 @@ ctx.onerror = (error) => {
|
|||||||
log.error('error:', error);
|
log.error('error:', error);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ctx.onunhandledrejection = (error) => {
|
||||||
|
log.error('onunhandledrejection:', error);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeState = () => {
|
||||||
|
ctx.onmessage = onMessage;
|
||||||
|
ctx.onfetch = onFetch;
|
||||||
|
};
|
||||||
|
|
||||||
|
onChangeState();
|
||||||
|
|
||||||
|
ctx.onoffline = ctx.ononline = onChangeState;
|
||||||
|
|
||||||
const onFetch = (event: FetchEvent): void => {
|
const onFetch = (event: FetchEvent): void => {
|
||||||
const [, url, scope, params] = /http[:s]+\/\/.*?(\/(.*?)(?:$|\/(.*)$))/.exec(event.request.url) || [];
|
try {
|
||||||
|
const [, url, scope, params] = /http[:s]+\/\/.*?(\/(.*?)(?:$|\/(.*)$))/.exec(event.request.url) || [];
|
||||||
|
|
||||||
log.debug('[fetch]:', event);
|
log.debug('[fetch]:', event);
|
||||||
|
|
||||||
switch(scope) {
|
switch(scope) {
|
||||||
case 'download':
|
case 'download':
|
||||||
case 'thumb':
|
case 'thumb':
|
||||||
case 'document':
|
case 'document':
|
||||||
case 'photo': {
|
case 'photo': {
|
||||||
const info: DownloadOptions = JSON.parse(decodeURIComponent(params));
|
const info: DownloadOptions = JSON.parse(decodeURIComponent(params));
|
||||||
|
|
||||||
const rangeHeader = event.request.headers.get('Range');
|
const rangeHeader = event.request.headers.get('Range');
|
||||||
if(rangeHeader && info.mimeType && info.size) { // maybe safari
|
if(rangeHeader && info.mimeType && info.size) { // maybe safari
|
||||||
const range = parseRange(event.request.headers.get('Range'));
|
const range = parseRange(event.request.headers.get('Range'));
|
||||||
const possibleResponse = responseForSafariFirstRange(range, info.mimeType, info.size);
|
|
||||||
if(possibleResponse) {
|
|
||||||
return event.respondWith(possibleResponse);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileName = getFileNameByLocation(info.location, {fileName: info.fileName});
|
|
||||||
|
|
||||||
/* event.request.signal.addEventListener('abort', (e) => {
|
|
||||||
console.log('[SW] user aborted request:', fileName);
|
|
||||||
cancellablePromise.cancel();
|
|
||||||
});
|
|
||||||
|
|
||||||
event.request.signal.onabort = (e) => {
|
|
||||||
console.log('[SW] user aborted request:', fileName);
|
|
||||||
cancellablePromise.cancel();
|
|
||||||
};
|
|
||||||
|
|
||||||
if(fileName == '5452060085729624717') {
|
|
||||||
setInterval(() => {
|
|
||||||
console.log('[SW] request status:', fileName, event.request.signal.aborted);
|
|
||||||
}, 1000);
|
|
||||||
} */
|
|
||||||
|
|
||||||
const cancellablePromise = apiFileManager.downloadFile(info);
|
|
||||||
cancellablePromise.notify = (progress: {done: number, total: number, offset: number}) => {
|
|
||||||
notify({progress: {fileName, ...progress}});
|
|
||||||
};
|
|
||||||
|
|
||||||
log.debug('[fetch] file:', /* info, */fileName);
|
|
||||||
|
|
||||||
const promise = cancellablePromise.then(b => {
|
|
||||||
const responseInit: ResponseInit = {};
|
|
||||||
|
|
||||||
if(rangeHeader) {
|
|
||||||
responseInit.headers = {
|
|
||||||
'Accept-Ranges': 'bytes',
|
|
||||||
'Content-Range': `bytes 0-${info.size - 1}/${info.size || '*'}`,
|
|
||||||
'Content-Length': `${info.size}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response(b, responseInit);
|
|
||||||
});
|
|
||||||
|
|
||||||
event.respondWith(Promise.race([
|
|
||||||
timeout(45 * 1000),
|
|
||||||
promise
|
|
||||||
]));
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'stream': {
|
|
||||||
const range = parseRange(event.request.headers.get('Range'));
|
|
||||||
const [offset, end] = range;
|
|
||||||
|
|
||||||
const info: DownloadOptions = JSON.parse(decodeURIComponent(params));
|
|
||||||
//const fileName = getFileNameByLocation(info.location);
|
|
||||||
|
|
||||||
log.debug('[stream]', url, offset, end);
|
|
||||||
|
|
||||||
event.respondWith(Promise.race([
|
|
||||||
timeout(45 * 1000),
|
|
||||||
new Promise<Response>((resolve, reject) => {
|
|
||||||
// safari workaround
|
|
||||||
const possibleResponse = responseForSafariFirstRange(range, info.mimeType, info.size);
|
const possibleResponse = responseForSafariFirstRange(range, info.mimeType, info.size);
|
||||||
if(possibleResponse) {
|
if(possibleResponse) {
|
||||||
return resolve(possibleResponse);
|
return event.respondWith(possibleResponse);
|
||||||
}
|
|
||||||
|
|
||||||
const limit = end && end < STREAM_CHUNK_UPPER_LIMIT ? alignLimit(end - offset + 1) : STREAM_CHUNK_UPPER_LIMIT;
|
|
||||||
const alignedOffset = alignOffset(offset, limit);
|
|
||||||
|
|
||||||
//log.debug('[stream] requestFilePart:', info.dcID, info.location, alignedOffset, limit);
|
|
||||||
|
|
||||||
apiFileManager.requestFilePart(info.dcID, info.location, alignedOffset, limit).then(result => {
|
|
||||||
let ab = result.bytes;
|
|
||||||
|
|
||||||
//log.debug('[stream] requestFilePart result:', result);
|
|
||||||
|
|
||||||
const headers: Record<string, string> = {
|
|
||||||
'Accept-Ranges': 'bytes',
|
|
||||||
'Content-Range': `bytes ${alignedOffset}-${alignedOffset + ab.byteLength - 1}/${info.size || '*'}`,
|
|
||||||
'Content-Length': `${ab.byteLength}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(info.mimeType) headers['Content-Type'] = info.mimeType;
|
|
||||||
|
|
||||||
if(isSafari) {
|
|
||||||
ab = ab.slice(offset - alignedOffset, end - alignedOffset + 1);
|
|
||||||
headers['Content-Range'] = `bytes ${offset}-${offset + ab.byteLength - 1}/${info.size || '*'}`;
|
|
||||||
headers['Content-Length'] = `${ab.byteLength}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(new Response(ab, {
|
|
||||||
status: 206,
|
|
||||||
statusText: 'Partial Content',
|
|
||||||
headers,
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
})
|
|
||||||
]));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* case 'download': {
|
|
||||||
const info: DownloadOptions = JSON.parse(decodeURIComponent(params));
|
|
||||||
|
|
||||||
const promise = new Promise<Response>((resolve) => {
|
|
||||||
const headers: Record<string, string> = {
|
|
||||||
'Content-Disposition': `attachment; filename="${info.fileName}"`,
|
|
||||||
};
|
|
||||||
|
|
||||||
if(info.size) headers['Content-Length'] = info.size.toString();
|
|
||||||
if(info.mimeType) headers['Content-Type'] = info.mimeType;
|
|
||||||
|
|
||||||
log('[download] file:', info);
|
|
||||||
|
|
||||||
const stream = new ReadableStream({
|
|
||||||
start(controller: ReadableStreamDefaultController) {
|
|
||||||
const limitPart = DOWNLOAD_CHUNK_LIMIT;
|
|
||||||
|
|
||||||
apiFileManager.downloadFile({
|
|
||||||
...info,
|
|
||||||
limitPart,
|
|
||||||
processPart: (bytes, offset) => {
|
|
||||||
log('[download] file processPart:', bytes, offset);
|
|
||||||
|
|
||||||
controller.enqueue(new Uint8Array(bytes));
|
|
||||||
|
|
||||||
const isFinal = offset + limitPart >= info.size;
|
|
||||||
if(isFinal) {
|
|
||||||
controller.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
}).catch(err => {
|
|
||||||
log.error('[download] error:', err);
|
|
||||||
controller.error(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
log.error('[download] file canceled:', info);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileName = getFileNameByLocation(info.location, {fileName: info.fileName});
|
||||||
|
|
||||||
|
/* event.request.signal.addEventListener('abort', (e) => {
|
||||||
|
console.log('[SW] user aborted request:', fileName);
|
||||||
|
cancellablePromise.cancel();
|
||||||
});
|
});
|
||||||
|
|
||||||
resolve(new Response(stream, {headers}));
|
event.request.signal.onabort = (e) => {
|
||||||
});
|
console.log('[SW] user aborted request:', fileName);
|
||||||
|
cancellablePromise.cancel();
|
||||||
event.respondWith(promise);
|
};
|
||||||
|
|
||||||
break;
|
if(fileName == '5452060085729624717') {
|
||||||
} */
|
setInterval(() => {
|
||||||
|
console.log('[SW] request status:', fileName, event.request.signal.aborted);
|
||||||
case 'upload': {
|
}, 1000);
|
||||||
if(event.request.method == 'POST') {
|
} */
|
||||||
event.respondWith(event.request.blob().then(blob => {
|
|
||||||
return apiFileManager.uploadFile(blob).then(v => new Response(JSON.stringify(v), {headers: {'Content-Type': 'application/json'}}));
|
const cancellablePromise = apiFileManager.downloadFile(info);
|
||||||
}));
|
cancellablePromise.notify = (progress: {done: number, total: number, offset: number}) => {
|
||||||
|
notify({progress: {fileName, ...progress}});
|
||||||
|
};
|
||||||
|
|
||||||
|
log.debug('[fetch] file:', /* info, */fileName);
|
||||||
|
|
||||||
|
const promise = cancellablePromise.then(b => {
|
||||||
|
const responseInit: ResponseInit = {};
|
||||||
|
|
||||||
|
if(rangeHeader) {
|
||||||
|
responseInit.headers = {
|
||||||
|
'Accept-Ranges': 'bytes',
|
||||||
|
'Content-Range': `bytes 0-${info.size - 1}/${info.size || '*'}`,
|
||||||
|
'Content-Length': `${info.size}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(b, responseInit);
|
||||||
|
});
|
||||||
|
|
||||||
|
event.respondWith(Promise.race([
|
||||||
|
timeout(45 * 1000),
|
||||||
|
promise
|
||||||
|
]));
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
case 'stream': {
|
||||||
}
|
const range = parseRange(event.request.headers.get('Range'));
|
||||||
|
const [offset, end] = range;
|
||||||
/* default: {
|
|
||||||
|
const info: DownloadOptions = JSON.parse(decodeURIComponent(params));
|
||||||
break;
|
//const fileName = getFileNameByLocation(info.location);
|
||||||
}
|
|
||||||
case 'documents':
|
log.debug('[stream]', url, offset, end);
|
||||||
case 'photos':
|
|
||||||
case 'profiles':
|
event.respondWith(Promise.race([
|
||||||
// direct download
|
timeout(45 * 1000),
|
||||||
if (event.request.method === 'POST') {
|
new Promise<Response>((resolve, reject) => {
|
||||||
event.respondWith(// download(url, 'unknown file.txt', getFilePartRequest));
|
// safari workaround
|
||||||
event.request.text()
|
const possibleResponse = responseForSafariFirstRange(range, info.mimeType, info.size);
|
||||||
.then((text) => {
|
if(possibleResponse) {
|
||||||
const [, filename] = text.split('=');
|
return resolve(possibleResponse);
|
||||||
return download(url, filename ? filename.toString() : 'unknown file', getFilePartRequest);
|
}
|
||||||
}),
|
|
||||||
);
|
const limit = end && end < STREAM_CHUNK_UPPER_LIMIT ? alignLimit(end - offset + 1) : STREAM_CHUNK_UPPER_LIMIT;
|
||||||
|
const alignedOffset = alignOffset(offset, limit);
|
||||||
// inline
|
|
||||||
} else {
|
//log.debug('[stream] requestFilePart:', info.dcID, info.location, alignedOffset, limit);
|
||||||
event.respondWith(
|
|
||||||
ctx.cache.match(url).then((cached) => {
|
apiFileManager.requestFilePart(info.dcID, info.location, alignedOffset, limit).then(result => {
|
||||||
if (cached) return cached;
|
let ab = result.bytes;
|
||||||
|
|
||||||
return Promise.race([
|
//log.debug('[stream] requestFilePart result:', result);
|
||||||
timeout(45 * 1000), // safari fix
|
|
||||||
new Promise<Response>((resolve) => {
|
const headers: Record<string, string> = {
|
||||||
fetchRequest(url, resolve, getFilePartRequest, ctx.cache, fileProgress);
|
'Accept-Ranges': 'bytes',
|
||||||
|
'Content-Range': `bytes ${alignedOffset}-${alignedOffset + ab.byteLength - 1}/${info.size || '*'}`,
|
||||||
|
'Content-Length': `${ab.byteLength}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(info.mimeType) headers['Content-Type'] = info.mimeType;
|
||||||
|
|
||||||
|
if(isSafari) {
|
||||||
|
ab = ab.slice(offset - alignedOffset, end - alignedOffset + 1);
|
||||||
|
headers['Content-Range'] = `bytes ${offset}-${offset + ab.byteLength - 1}/${info.size || '*'}`;
|
||||||
|
headers['Content-Length'] = `${ab.byteLength}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(new Response(ab, {
|
||||||
|
status: 206,
|
||||||
|
statusText: 'Partial Content',
|
||||||
|
headers,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
]));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* case 'download': {
|
||||||
|
const info: DownloadOptions = JSON.parse(decodeURIComponent(params));
|
||||||
|
|
||||||
|
const promise = new Promise<Response>((resolve) => {
|
||||||
|
const headers: Record<string, string> = {
|
||||||
|
'Content-Disposition': `attachment; filename="${info.fileName}"`,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(info.size) headers['Content-Length'] = info.size.toString();
|
||||||
|
if(info.mimeType) headers['Content-Type'] = info.mimeType;
|
||||||
|
|
||||||
|
log('[download] file:', info);
|
||||||
|
|
||||||
|
const stream = new ReadableStream({
|
||||||
|
start(controller: ReadableStreamDefaultController) {
|
||||||
|
const limitPart = DOWNLOAD_CHUNK_LIMIT;
|
||||||
|
|
||||||
|
apiFileManager.downloadFile({
|
||||||
|
...info,
|
||||||
|
limitPart,
|
||||||
|
processPart: (bytes, offset) => {
|
||||||
|
log('[download] file processPart:', bytes, offset);
|
||||||
|
|
||||||
|
controller.enqueue(new Uint8Array(bytes));
|
||||||
|
|
||||||
|
const isFinal = offset + limitPart >= info.size;
|
||||||
|
if(isFinal) {
|
||||||
|
controller.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
log.error('[download] error:', err);
|
||||||
|
controller.error(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
log.error('[download] file canceled:', info);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(new Response(stream, {headers}));
|
||||||
|
});
|
||||||
|
|
||||||
|
event.respondWith(promise);
|
||||||
|
|
||||||
|
break;
|
||||||
|
} */
|
||||||
|
|
||||||
|
case 'upload': {
|
||||||
|
if(event.request.method == 'POST') {
|
||||||
|
event.respondWith(event.request.blob().then(blob => {
|
||||||
|
return apiFileManager.uploadFile(blob).then(v => new Response(JSON.stringify(v), {headers: {'Content-Type': 'application/json'}}));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* default: {
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'documents':
|
||||||
|
case 'photos':
|
||||||
|
case 'profiles':
|
||||||
|
// direct download
|
||||||
|
if (event.request.method === 'POST') {
|
||||||
|
event.respondWith(// download(url, 'unknown file.txt', getFilePartRequest));
|
||||||
|
event.request.text()
|
||||||
|
.then((text) => {
|
||||||
|
const [, filename] = text.split('=');
|
||||||
|
return download(url, filename ? filename.toString() : 'unknown file', getFilePartRequest);
|
||||||
}),
|
}),
|
||||||
]);
|
);
|
||||||
}),
|
|
||||||
);
|
// inline
|
||||||
|
} else {
|
||||||
|
event.respondWith(
|
||||||
|
ctx.cache.match(url).then((cached) => {
|
||||||
|
if (cached) return cached;
|
||||||
|
|
||||||
|
return Promise.race([
|
||||||
|
timeout(45 * 1000), // safari fix
|
||||||
|
new Promise<Response>((resolve) => {
|
||||||
|
fetchRequest(url, resolve, getFilePartRequest, ctx.cache, fileProgress);
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'stream': {
|
||||||
|
const [offset, end] = parseRange(event.request.headers.get('Range') || '');
|
||||||
|
|
||||||
|
log('stream', url, offset, end);
|
||||||
|
|
||||||
|
event.respondWith(new Promise((resolve) => {
|
||||||
|
fetchStreamRequest(url, offset, end, resolve, getFilePartRequest);
|
||||||
|
}));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
case 'stripped':
|
||||||
case 'stream': {
|
case 'cached': {
|
||||||
const [offset, end] = parseRange(event.request.headers.get('Range') || '');
|
const bytes = getThumb(url) || null;
|
||||||
|
event.respondWith(new Response(bytes, { headers: { 'Content-Type': 'image/jpg' } }));
|
||||||
log('stream', url, offset, end);
|
break;
|
||||||
|
}
|
||||||
event.respondWith(new Promise((resolve) => {
|
|
||||||
fetchStreamRequest(url, offset, end, resolve, getFilePartRequest);
|
default:
|
||||||
}));
|
if (url && url.endsWith('.tgs')) event.respondWith(fetchTGS(url));
|
||||||
break;
|
else event.respondWith(fetch(event.request.url)); */
|
||||||
}
|
}
|
||||||
|
} catch(err) {
|
||||||
case 'stripped':
|
event.respondWith(new Response('', {
|
||||||
case 'cached': {
|
status: 500,
|
||||||
const bytes = getThumb(url) || null;
|
statusText: 'Internal Server Error',
|
||||||
event.respondWith(new Response(bytes, { headers: { 'Content-Type': 'image/jpg' } }));
|
}));
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (url && url.endsWith('.tgs')) event.respondWith(fetchTGS(url));
|
|
||||||
else event.respondWith(fetch(event.request.url)); */
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch requests
|
|
||||||
*/
|
|
||||||
//ctx.addEventListener('fetch', );
|
|
||||||
ctx.onfetch = onFetch;
|
|
||||||
|
|
||||||
const DOWNLOAD_CHUNK_LIMIT = 512 * 1024;
|
const DOWNLOAD_CHUNK_LIMIT = 512 * 1024;
|
||||||
//const STREAM_CHUNK_UPPER_LIMIT = 256 * 1024;
|
|
||||||
//const SMALLEST_CHUNK_LIMIT = 256 * 4;
|
/* const STREAM_CHUNK_UPPER_LIMIT = 256 * 1024;
|
||||||
const STREAM_CHUNK_UPPER_LIMIT = 1024 * 1024;
|
const SMALLEST_CHUNK_LIMIT = 256 * 4; */
|
||||||
const SMALLEST_CHUNK_LIMIT = 1024 * 4;
|
/* const STREAM_CHUNK_UPPER_LIMIT = 1024 * 1024;
|
||||||
|
const SMALLEST_CHUNK_LIMIT = 1024 * 4; */
|
||||||
|
const STREAM_CHUNK_UPPER_LIMIT = 512 * 1024;
|
||||||
|
const SMALLEST_CHUNK_LIMIT = 512 * 4;
|
||||||
|
|
||||||
function parseRange(header: string): [number, number] {
|
function parseRange(header: string): [number, number] {
|
||||||
if(!header) return [0, 0];
|
if(!header) return [0, 0];
|
||||||
|
@ -81,10 +81,14 @@ class ApiManagerProxy extends CryptoWorkerMethods {
|
|||||||
this.finalizeTask(e.data.taskID, e.data.result, e.data.error);
|
this.finalizeTask(e.data.taskID, e.data.result, e.data.error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
navigator.serviceWorker.addEventListener('messageerror', (e) => {
|
||||||
|
this.log.error('SW messageerror:', e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private finalizeTask(taskID: number, result: any, error: any) {
|
private finalizeTask(taskID: number, result: any, error: any) {
|
||||||
let deferred = this.awaiting[taskID];
|
const deferred = this.awaiting[taskID];
|
||||||
if(deferred !== undefined) {
|
if(deferred !== undefined) {
|
||||||
this.log.debug('done', deferred.taskName, result, error);
|
this.log.debug('done', deferred.taskName, result, error);
|
||||||
result === undefined ? deferred.reject(error) : deferred.resolve(result);
|
result === undefined ? deferred.reject(error) : deferred.resolve(result);
|
||||||
@ -113,10 +117,12 @@ class ApiManagerProxy extends CryptoWorkerMethods {
|
|||||||
|
|
||||||
private releasePending() {
|
private releasePending() {
|
||||||
if(navigator.serviceWorker.controller) {
|
if(navigator.serviceWorker.controller) {
|
||||||
|
this.log.debug('releasing tasks, length:', this.pending.length);
|
||||||
this.pending.forEach(pending => {
|
this.pending.forEach(pending => {
|
||||||
navigator.serviceWorker.controller.postMessage(pending);
|
navigator.serviceWorker.controller.postMessage(pending);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.log.debug('released tasks');
|
||||||
this.pending.length = 0;
|
this.pending.length = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user