remotion
Version:
Make videos programmatically
206 lines (205 loc) • 7.89 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.prefetch = exports.usePreload = exports.getSrcWithoutHash = exports.removeAndGetHashFragment = void 0;
const react_1 = require("react");
const get_remotion_environment_js_1 = require("./get-remotion-environment.js");
const log_js_1 = require("./log.js");
const playback_logging_js_1 = require("./playback-logging.js");
const prefetch_state_js_1 = require("./prefetch-state.js");
const removeAndGetHashFragment = (src) => {
const hashIndex = src.indexOf('#');
if (hashIndex === -1) {
return null;
}
return hashIndex;
};
exports.removeAndGetHashFragment = removeAndGetHashFragment;
const getSrcWithoutHash = (src) => {
const hashIndex = (0, exports.removeAndGetHashFragment)(src);
if (hashIndex === null) {
return src;
}
return src.slice(0, hashIndex);
};
exports.getSrcWithoutHash = getSrcWithoutHash;
const usePreload = (src) => {
const preloads = (0, react_1.useContext)(prefetch_state_js_1.PreloadContext);
const hashFragmentIndex = (0, exports.removeAndGetHashFragment)(src);
const withoutHashFragment = (0, exports.getSrcWithoutHash)(src);
if (!preloads[withoutHashFragment]) {
return src;
}
if (hashFragmentIndex !== null) {
return preloads[withoutHashFragment] + src.slice(hashFragmentIndex);
}
return preloads[withoutHashFragment];
};
exports.usePreload = usePreload;
const blobToBase64 = function (blob) {
const reader = new FileReader();
return new Promise((resolve, reject) => {
reader.onload = function () {
const dataUrl = reader.result;
resolve(dataUrl);
};
reader.onerror = (err) => {
return reject(err);
};
reader.readAsDataURL(blob);
});
};
const getBlobFromReader = async ({ reader, contentType, contentLength, onProgress, }) => {
let receivedLength = 0;
const chunks = [];
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
chunks.push(value);
receivedLength += value.length;
if (onProgress) {
onProgress({ loadedBytes: receivedLength, totalBytes: contentLength });
}
}
const chunksAll = new Uint8Array(receivedLength);
let position = 0;
for (const chunk of chunks) {
chunksAll.set(chunk, position);
position += chunk.length;
}
return new Blob([chunksAll], {
type: contentType !== null && contentType !== void 0 ? contentType : undefined,
});
};
/*
* @description When you call the prefetch() function, an asset will be fetched and kept in memory so it is ready when you want to play it in a <Player>.
* @see [Documentation](https://www.remotion.dev/docs/prefetch)
*/
const prefetch = (src, options) => {
var _a, _b, _c;
const method = (_a = options === null || options === void 0 ? void 0 : options.method) !== null && _a !== void 0 ? _a : 'blob-url';
const logLevel = (_b = options === null || options === void 0 ? void 0 : options.logLevel) !== null && _b !== void 0 ? _b : 'info';
const srcWithoutHash = (0, exports.getSrcWithoutHash)(src);
if ((0, get_remotion_environment_js_1.getRemotionEnvironment)().isRendering) {
return {
free: () => undefined,
waitUntilDone: () => Promise.resolve(srcWithoutHash),
};
}
log_js_1.Log.verbose(logLevel, `[prefetch] Starting prefetch ${srcWithoutHash}`);
let canceled = false;
let objectUrl = null;
let resolve = () => undefined;
let reject = () => undefined;
const waitUntilDone = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
const controller = new AbortController();
let canBeAborted = true;
fetch(srcWithoutHash, {
signal: controller.signal,
credentials: (_c = options === null || options === void 0 ? void 0 : options.credentials) !== null && _c !== void 0 ? _c : undefined,
})
.then((res) => {
var _a, _b, _c;
canBeAborted = false;
if (canceled) {
return null;
}
if (!res.ok) {
throw new Error(`HTTP error, status = ${res.status}`);
}
const headerContentType = res.headers.get('Content-Type');
const contentType = (_a = options === null || options === void 0 ? void 0 : options.contentType) !== null && _a !== void 0 ? _a : headerContentType;
const hasProperContentType = contentType &&
(contentType.startsWith('video/') ||
contentType.startsWith('audio/') ||
contentType.startsWith('image/'));
if (!hasProperContentType) {
// eslint-disable-next-line no-console
console.warn(`Called prefetch() on ${srcWithoutHash} which returned a "Content-Type" of ${headerContentType}. Prefetched content should have a proper content type (video/... or audio/...) or a contentType passed the options of prefetch(). Otherwise, prefetching will not work properly in all browsers.`);
}
if (!res.body) {
throw new Error(`HTTP response of ${srcWithoutHash} has no body`);
}
const reader = res.body.getReader();
return getBlobFromReader({
reader,
contentType: (_c = (_b = options === null || options === void 0 ? void 0 : options.contentType) !== null && _b !== void 0 ? _b : headerContentType) !== null && _c !== void 0 ? _c : null,
contentLength: res.headers.get('Content-Length')
? parseInt(res.headers.get('Content-Length'), 10)
: null,
onProgress: options === null || options === void 0 ? void 0 : options.onProgress,
});
})
.then((buf) => {
if (!buf) {
return;
}
const actualBlob = (options === null || options === void 0 ? void 0 : options.contentType)
? new Blob([buf], { type: options.contentType })
: buf;
if (method === 'base64') {
return blobToBase64(actualBlob);
}
return URL.createObjectURL(actualBlob);
})
.then((url) => {
if (canceled) {
return;
}
(0, playback_logging_js_1.playbackLogging)({
logLevel,
tag: 'prefetch',
message: `Finished prefetch ${srcWithoutHash} with method ${method}`,
mountTime: null,
});
objectUrl = url;
(0, prefetch_state_js_1.setPreloads)((p) => ({
...p,
[srcWithoutHash]: objectUrl,
}));
resolve(objectUrl);
})
.catch((err) => {
if (err === null || err === void 0 ? void 0 : err.message.includes('free() called')) {
return;
}
reject(err);
});
return {
free: () => {
(0, playback_logging_js_1.playbackLogging)({
logLevel,
tag: 'prefetch',
message: `Freeing ${srcWithoutHash}`,
mountTime: null,
});
if (objectUrl) {
if (method === 'blob-url') {
URL.revokeObjectURL(objectUrl);
}
(0, prefetch_state_js_1.setPreloads)((p) => {
const copy = { ...p };
delete copy[srcWithoutHash];
return copy;
});
}
else {
canceled = true;
if (canBeAborted) {
try {
controller.abort(new Error('free() called'));
}
catch (_a) { }
}
}
},
waitUntilDone: () => {
return waitUntilDone;
},
};
};
exports.prefetch = prefetch;