mirror of
https://github.com/SrIzan10/vdo.ninja.git
synced 2026-05-01 11:05:24 +00:00
version 24.4 happy holidays
This commit is contained in:
348
thirdparty/CodecsHandler.js
vendored
348
thirdparty/CodecsHandler.js
vendored
@@ -26,221 +26,122 @@ Copyright (c) 2012-2020 [Muaz Khan](https://github.com/muaz-khan)
|
||||
// *FILE HAS BEEN HEAVILY MODIFIED BY STEVE SEGUIN. ALL RIGHTS RESERVED WHERE APPLICABLE *
|
||||
|
||||
var CodecsHandler = (function() {
|
||||
function preferCodec(sdp, codecName) {
|
||||
|
||||
if (codecName){
|
||||
codecName = codecName.toLowerCase();
|
||||
function preferCodec(sdp, codec, useRed=false, useUlpfec=false) {
|
||||
if (codec){
|
||||
codec = codec.toLowerCase();
|
||||
}
|
||||
|
||||
var info = splitLines(sdp);
|
||||
if (!info.videoCodecNumbers) {
|
||||
return sdp;
|
||||
} else if (codecName === 'vp8' && info.vp8LineNumber === info.videoCodecNumbers[0]) {
|
||||
return sdp;
|
||||
} else if (codecName === 'vp9' && info.vp9LineNumber === info.videoCodecNumbers[0]) {
|
||||
return sdp;
|
||||
} else if (codecName === 'h264' && info.h264LineNumber === info.videoCodecNumbers[0]) {
|
||||
return sdp;
|
||||
} else if (codecName === 'h265' && info.h265LineNumber === info.videoCodecNumbers[0]) {
|
||||
return sdp;
|
||||
} else if (codecName === 'av1' && info.av1LineNumber === info.videoCodecNumbers[0]) {
|
||||
return sdp;
|
||||
}
|
||||
//} else if (codecName === 'red' && info.redLineNumber === info.videoCodecNumbers[0]) {
|
||||
// return sdp;
|
||||
//} else if (codecName === 'fec' && info.fecLineNumber === info.videoCodecNumbers[0]) {
|
||||
// return sdp;
|
||||
//}
|
||||
|
||||
sdp = preferCodecHelper(sdp, codecName, info);
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
|
||||
function preferAudioCodec(sdp, codecName) {
|
||||
if (codecName){
|
||||
codecName = codecName.toLowerCase();
|
||||
}
|
||||
var info = splitAudioLines(sdp);
|
||||
if (!info.audioCodecNumbers) {
|
||||
return sdp;
|
||||
} else if (codecName === 'opus' && info.opusLineNumber === info.audioCodecNumbers[0]) {
|
||||
return sdp;
|
||||
} else if (codecName === 'isac' && info.isacLineNumber === info.audioCodecNumbers[0]) {
|
||||
return sdp;
|
||||
} else if (codecName === 'g722' && info.g722LineNumber === info.audioCodecNumbers[0]) {
|
||||
return sdp;
|
||||
} else if (codecName === 'pcmu' && info.pcmuLineNumber === info.audioCodecNumbers[0]) {
|
||||
return sdp;
|
||||
} else if (codecName === 'pcma' && info.pcmaLineNumber === info.audioCodecNumbers[0]) {
|
||||
return sdp;
|
||||
} else if (codecName === 'red' && info.redLineNumber === info.audioCodecNumbers[0]) {
|
||||
return sdp;
|
||||
}
|
||||
|
||||
sdp = preferAudioCodecHelper(sdp, codecName, info);
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function preferCodecHelper(sdp, codec, info) {
|
||||
var preferCodecNumber = '';
|
||||
|
||||
if (codec === 'vp8') {
|
||||
if (!info.vp8LineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.vp8LineNumber;
|
||||
|
||||
} else if (codec === 'vp9') {
|
||||
if (!info.vp9LineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.vp9LineNumber;
|
||||
|
||||
} else if (codec === 'h264') {
|
||||
if (!info.h264LineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.h264LineNumber;
|
||||
} else if (codec === 'h265') {
|
||||
if (!info.h265LineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.h265LineNumber;
|
||||
} else if (codec === 'av1') {
|
||||
if (!info.av1LineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.av1LineNumber;
|
||||
} else {
|
||||
var info = splitLines(sdp);
|
||||
if (!info.videoCodecNumbers) {
|
||||
return sdp;
|
||||
}
|
||||
//} else if (codec === 'red') {
|
||||
// if (!info.redLineNumber) {
|
||||
// return sdp;
|
||||
// }
|
||||
// preferCodecNumber = info.redLineNumber;
|
||||
|
||||
//} else if (codec === 'fec') {
|
||||
// if (!info.fecLineNumber) {
|
||||
// return sdp;
|
||||
// }
|
||||
// preferCodecNumber = info.fecLineNumber;
|
||||
// }
|
||||
|
||||
var newLine = info.videoCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ';
|
||||
|
||||
var newOrder = [preferCodecNumber];
|
||||
|
||||
info.videoCodecNumbers.forEach(function(codecNumber) {
|
||||
if (codecNumber === preferCodecNumber) return;
|
||||
newOrder.push(codecNumber);
|
||||
});
|
||||
|
||||
newLine += newOrder.join(' ');
|
||||
|
||||
sdp = sdp.replace(info.videoCodecNumbersOriginal, newLine);
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function preferAudioCodecHelper(sdp, codec, info) {
|
||||
var preferCodecNumber = '';
|
||||
|
||||
if (codec === 'opus') {
|
||||
if (!info.opusLineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.opusLineNumber;
|
||||
} else if (codec === 'isac') {
|
||||
if (!info.isacLineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.isacLineNumber;
|
||||
} else if (codec === 'g722') {
|
||||
if (!info.g722LineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.g722LineNumber;
|
||||
} else if (codec === 'pcmu') {
|
||||
if (!info.pcmuLineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.pcmuLineNumber;
|
||||
} else if (codec === 'pcma') {
|
||||
if (!info.pcmaLineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.pcmaLineNumber;
|
||||
} else if (codec === 'red') {
|
||||
if (!info.redLineNumber) {
|
||||
return sdp;
|
||||
}
|
||||
preferCodecNumber = info.redLineNumber;
|
||||
var preferCodecNumber = '';
|
||||
var preferErrorCorrectionNumbers = [];
|
||||
if (codec === 'vp8') {
|
||||
preferCodecNumber = info.vp8LineNumber || '';
|
||||
} else if (codec === 'vp9') {
|
||||
preferCodecNumber = info.vp9LineNumber || '';
|
||||
} else if (codec === 'h264') {
|
||||
preferCodecNumber = info.h264LineNumber || '';
|
||||
} else if (codec === 'h265') {
|
||||
preferCodecNumber = info.h265LineNumber || '';
|
||||
} else if (codec === 'av1') {
|
||||
preferCodecNumber = info.av1LineNumber || '';
|
||||
} else if (codec === 'red') { // you can treat red as a codec
|
||||
preferCodecNumber = info.redLineNumber || '';
|
||||
} else if (codec === 'fec') {
|
||||
preferCodecNumber = info.ulpfecLineNumber || '';
|
||||
}
|
||||
var newLine = info.audioCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ';
|
||||
|
||||
var newOrder = [preferCodecNumber];
|
||||
|
||||
info.audioCodecNumbers.forEach(function(codecNumber) {
|
||||
if (codecNumber === preferCodecNumber) return;
|
||||
newOrder.push(codecNumber);
|
||||
});
|
||||
|
||||
newLine += newOrder.join(' ');
|
||||
|
||||
sdp = sdp.replace(info.audioCodecNumbersOriginal, newLine);
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function splitLines(sdp) {
|
||||
var info = {};
|
||||
sdp.split('\n').forEach(function(line) {
|
||||
if (line.indexOf('m=video') === 0) {
|
||||
info.videoCodecNumbers = [];
|
||||
line.split('SAVPF')[1].split(' ').forEach(function(codecNumber) {
|
||||
codecNumber = codecNumber.trim();
|
||||
if (!codecNumber || !codecNumber.length) return;
|
||||
info.videoCodecNumbers.push(codecNumber);
|
||||
info.videoCodecNumbersOriginal = line;
|
||||
});
|
||||
}
|
||||
|
||||
if (useRed && info.redLineNumber) { // or as a setting
|
||||
preferErrorCorrectionNumbers.push(info.redLineNumber);
|
||||
}
|
||||
if (useUlpfec && info.ulpfecLineNumber) {
|
||||
preferErrorCorrectionNumbers.push(info.ulpfecLineNumber);
|
||||
}
|
||||
if (preferCodecNumber === '') {
|
||||
return sdp;
|
||||
}
|
||||
var newOrder = [preferCodecNumber].concat(preferErrorCorrectionNumbers);
|
||||
info.videoCodecNumbers.forEach(function(codecNumber) {
|
||||
if (!newOrder.includes(codecNumber)) {
|
||||
newOrder.push(codecNumber);
|
||||
}
|
||||
});
|
||||
var newLine = info.videoCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ' + newOrder.join(' ');
|
||||
sdp = sdp.replace(info.videoCodecNumbersOriginal, newLine);
|
||||
return sdp;
|
||||
}
|
||||
function splitLines(sdp) {
|
||||
var info = {};
|
||||
sdp.split('\n').forEach(function(line) {
|
||||
if (line.indexOf('m=video') === 0) {
|
||||
info.videoCodecNumbers = [];
|
||||
line.split('SAVPF')[1].split(' ').forEach(function(codecNumber) {
|
||||
codecNumber = codecNumber.trim();
|
||||
if (!codecNumber || !codecNumber.length) return;
|
||||
info.videoCodecNumbers.push(codecNumber);
|
||||
info.videoCodecNumbersOriginal = line;
|
||||
});
|
||||
}
|
||||
var LINE = line.toUpperCase();
|
||||
|
||||
if (LINE.indexOf('VP8/90000') !== -1 && !info.vp8LineNumber) {
|
||||
info.vp8LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (LINE.indexOf('VP9/90000') !== -1 && !info.vp9LineNumber) {
|
||||
info.vp9LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (LINE.indexOf('H264/90000') !== -1 && !info.h264LineNumber) {
|
||||
info.h264LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (LINE.indexOf('VP8/90000') !== -1 && !info.vp8LineNumber) {
|
||||
info.vp8LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
if (LINE.indexOf('VP9/90000') !== -1 && !info.vp9LineNumber) {
|
||||
info.vp9LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
if (LINE.indexOf('H264/90000') !== -1 && !info.h264LineNumber) {
|
||||
info.h264LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
if (LINE.indexOf('H265/90000') !== -1 && !info.h265LineNumber) {
|
||||
info.h265LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (LINE.indexOf('AV1X/90000') !== -1 && !info.av1LineNumber) {
|
||||
info.av1LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
} else if (LINE.indexOf('AV1/90000') !== -1 && !info.av1LineNumber) {
|
||||
info.av1LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
//if (LINE.indexOf('RED/90000') !== -1 && !info.redLineNumber) {
|
||||
// info.redLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
// }
|
||||
|
||||
// if (LINE.indexOf('FEC/90000') !== -1 && !info.fecLineNumber) {
|
||||
// info.fecLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
// }
|
||||
});
|
||||
|
||||
return info;
|
||||
}
|
||||
info.h265LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
if (LINE.indexOf('AV1X/90000') !== -1 && !info.av1LineNumber) {
|
||||
info.av1LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
} else if (LINE.indexOf('AV1/90000') !== -1 && !info.av1LineNumber) {
|
||||
info.av1LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
if (LINE.indexOf('RED/90000') !== -1 && !info.redLineNumber) {
|
||||
info.redLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
if (LINE.indexOf('ULPFEC/90000') !== -1 && !info.ulpfecLineNumber) {
|
||||
info.ulpfecLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
});
|
||||
return info;
|
||||
}
|
||||
|
||||
function preferAudioCodec(sdp, codec, useRed=false, useUlpfec=false) {
|
||||
if (codec) {
|
||||
codec = codec.toLowerCase();
|
||||
}
|
||||
var info = splitAudioLines(sdp);
|
||||
if (!info.audioCodecNumbers) {
|
||||
return sdp;
|
||||
}
|
||||
var preferCodecNumber = '';
|
||||
var errorCorrectionNumbers = [];
|
||||
if (codec && info[codec + 'LineNumber']) {
|
||||
preferCodecNumber = info[codec + 'LineNumber'];
|
||||
}
|
||||
if (useRed && info.redPcmLineNumber) {
|
||||
errorCorrectionNumbers.push(info.redPcmLineNumber);
|
||||
}
|
||||
if (useRed && info.redLineNumber && !info.redPcmLineNumber) {
|
||||
errorCorrectionNumbers.push(info.redLineNumber);
|
||||
}
|
||||
if (useUlpfec && info.ulpfecLineNumber) {
|
||||
errorCorrectionNumbers.push(info.ulpfecLineNumber);
|
||||
}
|
||||
var newOrder = [preferCodecNumber].concat(errorCorrectionNumbers);
|
||||
info.audioCodecNumbers.forEach(function(codecNumber) {
|
||||
if (!newOrder.includes(codecNumber)) {
|
||||
newOrder.push(codecNumber);
|
||||
}
|
||||
});
|
||||
var newLine = info.audioCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ' + newOrder.join(' ');
|
||||
sdp = sdp.replace(info.audioCodecNumbersOriginal, newLine);
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function splitAudioLines(sdp) {
|
||||
var info = {};
|
||||
@@ -254,37 +155,48 @@ var CodecsHandler = (function() {
|
||||
info.audioCodecNumbersOriginal = line;
|
||||
});
|
||||
}
|
||||
|
||||
var LINE = line.toLowerCase();
|
||||
|
||||
if (LINE.indexOf('opus/48000') !== -1 && !info.opusLineNumber) {
|
||||
info.opusLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (LINE.indexOf('isac/32000') !== -1 && !info.isacLineNumber) {
|
||||
info.isacLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (LINE.indexOf('g722/8000') !== -1 && !info.g722LineNumber) {
|
||||
info.g722LineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (LINE.indexOf('pcmu/8000') !== -1 && !info.pcmuLineNumber) {
|
||||
info.pcmuLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (LINE.indexOf('pcma/8000') !== -1 && !info.pcmaLineNumber) {
|
||||
info.pcmaLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (LINE.indexOf('red/48000') !== -1 && !info.redLineNumber) {
|
||||
info.redLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
|
||||
if (LINE.indexOf('ulpfec/48000') !== -1 && !info.ulpfecLineNumber) {
|
||||
info.ulpfecLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
if (line.indexOf('red/8000') !== -1 && !info.redPcmLineNumber) {
|
||||
info.redPcmLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
if (line.indexOf('ulpfec/8000') !== -1 && !info.ulpfecLineNumber) {
|
||||
info.ulpfecLineNumber = line.replace('a=rtpmap:', '').split(' ')[0];
|
||||
}
|
||||
});
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
function addRedForPcmToSdp(sdp, info, redPcmLine) {
|
||||
// Ensure RED for PCM is not already the first codec
|
||||
if (!info.audioCodecNumbers.includes(redPcmLine)) {
|
||||
var newOrder = info.audioCodecNumbers.filter(codecNumber => codecNumber !== redPcmLine);
|
||||
newOrder.unshift(redPcmLine); // Add RED for PCM at the start
|
||||
var newLine = info.audioCodecNumbersOriginal.split('SAVPF')[0] + 'SAVPF ' + newOrder.join(' ');
|
||||
sdp = sdp.replace(info.audioCodecNumbersOriginal, newLine);
|
||||
}
|
||||
return sdp;
|
||||
}
|
||||
|
||||
function extractSdp(sdpLine, pattern) {
|
||||
var result = sdpLine.match(pattern);
|
||||
|
||||
5
thirdparty/StreamSaver.js
vendored
5
thirdparty/StreamSaver.js
vendored
@@ -17,8 +17,7 @@ function streamSaverFunction(){
|
||||
//console.log(ponyfill);
|
||||
//console.log(isSecureContext);
|
||||
|
||||
// TODO: Must come up with a real detection test (#69)
|
||||
let useBlobFallback = /constructor/i.test(global.HTMLElement) || !!global.safari || !!global.WebKitPoint;
|
||||
let useBlobFallback = false; // we do not want to use blob recording because it can crash the browser.
|
||||
|
||||
//console.log(useBlobFallback);
|
||||
|
||||
@@ -214,7 +213,7 @@ function streamSaverFunction(){
|
||||
WritableStream: global.WritableStream || ponyfill.WritableStream,
|
||||
supported: true,
|
||||
version: { full: '2.0.7', major: 2, minor: 0, dot: 7 },
|
||||
mitm: './thirdparty/mitm.html?v=2'
|
||||
mitm: './thirdparty/mitm.html?v=3'
|
||||
}
|
||||
|
||||
//console.log(streamSaver);
|
||||
|
||||
317
thirdparty/StreamSaver_legacy.js
vendored
Normal file
317
thirdparty/StreamSaver_legacy.js
vendored
Normal file
@@ -0,0 +1,317 @@
|
||||
/*! streamsaver. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
||||
|
||||
/* global chrome location ReadableStream define MessageChannel TransformStream */
|
||||
|
||||
function streamSaverFunction(){
|
||||
'use strict'
|
||||
|
||||
const global = typeof window === 'object' ? window : this;
|
||||
if (!global.HTMLElement) console.warn('streamsaver is meant to run on browsers main thread')
|
||||
|
||||
let mitmTransporter = null;
|
||||
let supportsTransferable = false;
|
||||
const test = fn => { try { fn() } catch (e) {} };
|
||||
const ponyfill = global.WebStreamsPolyfill || {};
|
||||
const isSecureContext = global.isSecureContext;
|
||||
|
||||
//console.log(ponyfill);
|
||||
//console.log(isSecureContext);
|
||||
|
||||
// TODO: Must come up with a real detection test (#69)
|
||||
let useBlobFallback = /constructor/i.test(global.HTMLElement) || !!global.safari || !!global.WebKitPoint;
|
||||
|
||||
//console.log(useBlobFallback);
|
||||
|
||||
const downloadStrategy = isSecureContext || 'MozAppearance' in document.documentElement.style
|
||||
? 'iframe'
|
||||
: 'navigate';
|
||||
|
||||
//console.log(downloadStrategy);
|
||||
|
||||
function createWriteStream (filename, stopStream){
|
||||
//console.log("createWriteStream");
|
||||
let opts = {
|
||||
size: null,
|
||||
pathname: null,
|
||||
writableStrategy: undefined,
|
||||
readableStrategy: undefined
|
||||
}
|
||||
|
||||
let bytesWritten = 0 // by StreamSaver.js (not the service worker)
|
||||
let downloadUrl = null
|
||||
let channel = null
|
||||
let ts = null
|
||||
|
||||
if (!useBlobFallback) {
|
||||
loadTransporter()
|
||||
|
||||
channel = new MessageChannel()
|
||||
|
||||
// Make filename RFC5987 compatible
|
||||
filename = encodeURIComponent(filename.replace(/\//g, ':'))
|
||||
.replace(/['()]/g, escape)
|
||||
.replace(/\*/g, '%2A')
|
||||
|
||||
const response = {
|
||||
transferringReadable: supportsTransferable,
|
||||
pathname: opts.pathname || Math.random().toString().slice(-6) + '/' + filename,
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream; charset=utf-8',
|
||||
'Content-Disposition': "attachment; filename*=UTF-8''" + filename
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.size) {
|
||||
response.headers['Content-Length'] = opts.size
|
||||
}
|
||||
|
||||
const args = [ response, '*', [ channel.port2 ] ]
|
||||
|
||||
if (supportsTransferable) {
|
||||
const transformer = downloadStrategy === 'iframe' ? undefined : {
|
||||
// This transformer & flush method is only used by insecure context.
|
||||
transform (chunk, controller) {
|
||||
if (!(chunk instanceof Uint8Array)) {
|
||||
throw new TypeError('Can only write Uint8Arrays')
|
||||
}
|
||||
bytesWritten += chunk.length
|
||||
controller.enqueue(chunk)
|
||||
|
||||
if (downloadUrl) {
|
||||
location.href = downloadUrl
|
||||
downloadUrl = null
|
||||
}
|
||||
},
|
||||
flush () {
|
||||
if (downloadUrl) {
|
||||
location.href = downloadUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
ts = new streamSaver.TransformStream(
|
||||
transformer,
|
||||
opts.writableStrategy,
|
||||
opts.readableStrategy
|
||||
)
|
||||
const readableStream = ts.readable
|
||||
|
||||
channel.port1.postMessage({ readableStream }, [ readableStream ])
|
||||
}
|
||||
|
||||
channel.port1.onmessage = evt => {
|
||||
console.log(evt);
|
||||
// Service worker sent us a link that we should open.
|
||||
if (evt.data.download) {
|
||||
// Special treatment for popup...
|
||||
if (downloadStrategy === 'navigate') {
|
||||
mitmTransporter.remove()
|
||||
mitmTransporter = null
|
||||
if (bytesWritten) {
|
||||
location.href = evt.data.download
|
||||
} else {
|
||||
downloadUrl = evt.data.download
|
||||
}
|
||||
} else {
|
||||
if (mitmTransporter.isPopup) {
|
||||
mitmTransporter.remove()
|
||||
mitmTransporter = null
|
||||
// Special case for firefox, they can keep sw alive with fetch
|
||||
if (downloadStrategy === 'iframe') {
|
||||
makeIframe(streamSaver.mitm)
|
||||
}
|
||||
}
|
||||
|
||||
// We never remove this iframes b/c it can interrupt saving
|
||||
makeIframe(evt.data.download)
|
||||
}
|
||||
} else if (evt.data.abort) {
|
||||
stopStream(false, true);
|
||||
chunks = []
|
||||
channel.port1.postMessage('abort') //send back so controller is aborted
|
||||
channel.port1.onmessage = null
|
||||
|
||||
setTimeout(function(channel){
|
||||
channel.port1.close()
|
||||
channel.port2.close()
|
||||
channel = null
|
||||
},1300,channel);
|
||||
}
|
||||
}
|
||||
|
||||
if (mitmTransporter.loaded) {
|
||||
mitmTransporter.postMessage(...args)
|
||||
} else {
|
||||
mitmTransporter.addEventListener('load', () => {
|
||||
mitmTransporter.postMessage(...args)
|
||||
}, { once: true })
|
||||
}
|
||||
}
|
||||
|
||||
let chunks = []
|
||||
|
||||
return (!useBlobFallback && ts && ts.writable) || new streamSaver.WritableStream({
|
||||
write (chunk) {
|
||||
if (!(chunk instanceof Uint8Array)) {
|
||||
throw new TypeError('Can only write Uint8Arrays')
|
||||
}
|
||||
if (useBlobFallback) {
|
||||
// Safari... The new IE6
|
||||
// https://github.com/jimmywarting/StreamSaver.js/issues/69
|
||||
//
|
||||
// even though it has everything it fails to download anything
|
||||
// that comes from the service worker..!
|
||||
chunks.push(chunk)
|
||||
return
|
||||
}
|
||||
|
||||
// is called when a new chunk of data is ready to be written
|
||||
// to the underlying sink. It can return a promise to signal
|
||||
// success or failure of the write operation. The stream
|
||||
// implementation guarantees that this method will be called
|
||||
// only after previous writes have succeeded, and never after
|
||||
// close or abort is called.
|
||||
|
||||
// TODO: Kind of important that service worker respond back when
|
||||
// it has been written. Otherwise we can't handle backpressure
|
||||
// EDIT: Transferable streams solves this...
|
||||
try {
|
||||
channel.port1.postMessage(chunk)
|
||||
} catch(e){
|
||||
|
||||
};
|
||||
bytesWritten += chunk.length
|
||||
if (downloadUrl) {
|
||||
location.href = downloadUrl
|
||||
downloadUrl = null
|
||||
}
|
||||
},
|
||||
close () {
|
||||
if (useBlobFallback) {
|
||||
const blob = new Blob(chunks, { type: 'application/octet-stream; charset=utf-8' })
|
||||
const link = document.createElement('a')
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = filename
|
||||
link.click()
|
||||
} else {
|
||||
channel.port1.postMessage('end')
|
||||
}
|
||||
},
|
||||
abort () {
|
||||
chunks = []
|
||||
channel.port1.postMessage('abort')
|
||||
channel.port1.onmessage = null
|
||||
setTimeout(function(channel){
|
||||
channel.port1.close()
|
||||
channel.port2.close()
|
||||
channel = null
|
||||
},1300,channel);
|
||||
}
|
||||
}, opts.writableStrategy)
|
||||
}
|
||||
|
||||
const streamSaver = {
|
||||
createWriteStream,
|
||||
WritableStream: global.WritableStream || ponyfill.WritableStream,
|
||||
supported: true,
|
||||
version: { full: '2.0.7', major: 2, minor: 0, dot: 7 },
|
||||
mitm: './thirdparty/mitm.html?v=2'
|
||||
}
|
||||
|
||||
//console.log(streamSaver);
|
||||
|
||||
/**
|
||||
* create a hidden iframe and append it to the DOM (body)
|
||||
*
|
||||
* @param {string} src page to load
|
||||
* @return {HTMLIFrameElement} page to load
|
||||
*/
|
||||
function makeIframe (src) {
|
||||
if (!src) throw new Error('meh')
|
||||
const iframe = document.createElement('iframe')
|
||||
iframe.hidden = true
|
||||
iframe.src = src
|
||||
iframe.loaded = false
|
||||
iframe.name = 'iframe'
|
||||
iframe.isIframe = true
|
||||
iframe.postMessage = (...args) => iframe.contentWindow.postMessage(...args)
|
||||
iframe.addEventListener('load', () => {
|
||||
iframe.loaded = true
|
||||
}, { once: true })
|
||||
document.body.appendChild(iframe)
|
||||
return iframe
|
||||
}
|
||||
|
||||
/**
|
||||
* create a popup that simulates the basic things
|
||||
* of what a iframe can do
|
||||
*
|
||||
* @param {string} src page to load
|
||||
* @return {object} iframe like object
|
||||
*/
|
||||
function makePopup (src) {
|
||||
const options = 'width=200,height=100'
|
||||
const delegate = document.createDocumentFragment()
|
||||
const popup = {
|
||||
frame: global.open(src, 'popup', options),
|
||||
loaded: false,
|
||||
isIframe: false,
|
||||
isPopup: true,
|
||||
remove () { popup.frame.close() },
|
||||
addEventListener (...args) { delegate.addEventListener(...args) },
|
||||
dispatchEvent (...args) { delegate.dispatchEvent(...args) },
|
||||
removeEventListener (...args) { delegate.removeEventListener(...args) },
|
||||
postMessage (...args) { popup.frame.postMessage(...args) }
|
||||
}
|
||||
|
||||
const onReady = evt => {
|
||||
if (evt.source === popup.frame) {
|
||||
popup.loaded = true
|
||||
global.removeEventListener('message', onReady)
|
||||
popup.dispatchEvent(new Event('load'))
|
||||
}
|
||||
}
|
||||
|
||||
global.addEventListener('message', onReady)
|
||||
|
||||
return popup
|
||||
}
|
||||
|
||||
try {
|
||||
// We can't look for service worker since it may still work on http
|
||||
new Response(new ReadableStream())
|
||||
if (isSecureContext && !('serviceWorker' in navigator)) {
|
||||
useBlobFallback = true
|
||||
}
|
||||
} catch (err) {
|
||||
useBlobFallback = true
|
||||
}
|
||||
|
||||
//console.log("useBlobFallback: "+useBlobFallback);
|
||||
|
||||
test(() => {
|
||||
// Transferable stream was first enabled in chrome v73 behind a flag
|
||||
const { readable } = new TransformStream()
|
||||
const mc = new MessageChannel()
|
||||
mc.port1.postMessage(readable, [readable])
|
||||
mc.port1.close()
|
||||
mc.port2.close()
|
||||
supportsTransferable = true
|
||||
// Freeze TransformStream object (can only work with native)
|
||||
Object.defineProperty(streamSaver, 'TransformStream', {
|
||||
configurable: false,
|
||||
writable: false,
|
||||
value: TransformStream
|
||||
})
|
||||
})
|
||||
|
||||
function loadTransporter () {
|
||||
if (!mitmTransporter) {
|
||||
mitmTransporter = isSecureContext
|
||||
? makeIframe(streamSaver.mitm)
|
||||
: makePopup(streamSaver.mitm)
|
||||
}
|
||||
}
|
||||
|
||||
return streamSaver
|
||||
};
|
||||
var streamSaver = streamSaverFunction();
|
||||
Reference in New Issue
Block a user