mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-14 15:56:38 +03:00
f1768a5f84
We don't need this anymore with `exemptEmptyConstructors` config added in #8901
386 lines
11 KiB
JavaScript
386 lines
11 KiB
JavaScript
/*! @license
|
|
* Shaka Player
|
|
* Copyright 2016 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
goog.provide('shaka.offline.StorageCellHandle');
|
|
goog.provide('shaka.offline.StorageCellPath');
|
|
goog.provide('shaka.offline.StorageMuxer');
|
|
|
|
goog.require('shaka.log');
|
|
goog.require('shaka.util.Error');
|
|
goog.require('shaka.util.IDestroyable');
|
|
|
|
/**
|
|
* @typedef {{
|
|
* mechanism: string,
|
|
* cell: string,
|
|
* }}
|
|
*
|
|
* @property {string} mechanism
|
|
* The name of the mechanism that holds the cell.
|
|
* @property {string} cell
|
|
* The name of the cell in the mechanism.
|
|
*/
|
|
shaka.offline.StorageCellPath;
|
|
|
|
|
|
/**
|
|
* @typedef {{
|
|
* path: shaka.offline.StorageCellPath,
|
|
* cell: !shaka.extern.StorageCell,
|
|
* }}
|
|
*
|
|
* @property {shaka.offline.StorageCellPath} path
|
|
* The path that maps to the cell.
|
|
* @property {shaka.extern.StorageCell} cell
|
|
* The storage cell that the path points to within the storage muxer.
|
|
*/
|
|
shaka.offline.StorageCellHandle;
|
|
|
|
|
|
// TODO: revisit this when Closure Compiler supports partially-exported classes.
|
|
/**
|
|
* StorageMuxer is responsible for managing StorageMechanisms and addressing
|
|
* cells. The primary purpose of the muxer is to give the caller the correct
|
|
* cell for the operations they want to perform.
|
|
*
|
|
* |findActive| will be used when the caller wants a cell that supports
|
|
* add-operations. This will be used when saving new content to storage.
|
|
*
|
|
* |findAll| will be used when the caller want to look at all the content
|
|
* in storage.
|
|
*
|
|
* |resolvePath| will be used to convert a path (from |findActive| and
|
|
* |findAll|) into a cell, which it then returns.
|
|
*
|
|
* @implements {shaka.util.IDestroyable}
|
|
* @export
|
|
*/
|
|
shaka.offline.StorageMuxer = class {
|
|
constructor() {
|
|
/**
|
|
* A key in this map is the name given when registering a StorageMechanism.
|
|
*
|
|
* @private {!Map<string, !shaka.extern.StorageMechanism>}
|
|
*/
|
|
this.mechanisms_ = new Map();
|
|
}
|
|
|
|
// TODO: revisit this when the compiler supports partially-exported classes.
|
|
/**
|
|
* Free all resources used by the muxer, mechanisms, and cells. This should
|
|
* not affect the stored content.
|
|
*
|
|
* @override
|
|
* @export
|
|
*/
|
|
destroy() {
|
|
/** @type {!Array<!Promise>} */
|
|
const destroys = [];
|
|
for (const mechanism of this.mechanisms_.values()) {
|
|
destroys.push(mechanism.destroy());
|
|
}
|
|
|
|
// Empty the map so that subsequent calls will be no-ops.
|
|
this.mechanisms_.clear();
|
|
|
|
return Promise.all(destroys);
|
|
}
|
|
|
|
/**
|
|
* Initialize the storage muxer. This must be called before any other calls.
|
|
* This will initialize the muxer to use all mechanisms that have been
|
|
* registered with |StorageMuxer.register|.
|
|
*
|
|
* @return {!Promise}
|
|
*/
|
|
init() {
|
|
// Add the new instance of each mechanism to the muxer.
|
|
const registry = shaka.offline.StorageMuxer.getRegistry_();
|
|
registry.forEach((factory, name) => {
|
|
const mech = factory();
|
|
if (mech) {
|
|
this.mechanisms_.set(name, mech);
|
|
} else {
|
|
shaka.log.info(
|
|
'Skipping ' + name + ' as it is not supported on this platform');
|
|
}
|
|
});
|
|
|
|
/** @type {!Array<!Promise>} */
|
|
const initPromises = [];
|
|
for (const mechanism of this.mechanisms_.values()) {
|
|
initPromises.push(mechanism.init());
|
|
}
|
|
|
|
return Promise.all(initPromises);
|
|
}
|
|
|
|
/**
|
|
* Get a promise that will resolve with a storage cell that supports
|
|
* add-operations. If no cell can be found, the promise will be rejected.
|
|
*
|
|
* @return {shaka.offline.StorageCellHandle}
|
|
*/
|
|
getActive() {
|
|
/** @type {?shaka.offline.StorageCellHandle} */
|
|
let handle = null;
|
|
|
|
this.mechanisms_.forEach((mechanism, mechanismName) => {
|
|
mechanism.getCells().forEach((cell, cellName) => {
|
|
// If this cell is not useful to us or we already have a handle, then
|
|
// we don't need to make a new handle.
|
|
if (cell.hasFixedKeySpace() || handle) {
|
|
return;
|
|
}
|
|
|
|
const path = {
|
|
mechanism: mechanismName,
|
|
cell: cellName,
|
|
};
|
|
|
|
handle = {
|
|
path: path,
|
|
cell: cell,
|
|
};
|
|
});
|
|
});
|
|
|
|
if (handle) {
|
|
return /** @type {shaka.offline.StorageCellHandle} */(handle);
|
|
}
|
|
|
|
throw new shaka.util.Error(
|
|
shaka.util.Error.Severity.CRITICAL,
|
|
shaka.util.Error.Category.STORAGE,
|
|
shaka.util.Error.Code.MISSING_STORAGE_CELL,
|
|
'Could not find a cell that supports add-operations');
|
|
}
|
|
|
|
/**
|
|
* @param {function(!shaka.offline.StorageCellPath,
|
|
* !shaka.extern.StorageCell)} callback
|
|
*/
|
|
forEachCell(callback) {
|
|
this.mechanisms_.forEach((mechanism, mechanismName) => {
|
|
mechanism.getCells().forEach((cell, cellName) => {
|
|
const path = {
|
|
mechanism: mechanismName,
|
|
cell: cellName,
|
|
};
|
|
|
|
callback(path, cell);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get a specific storage cell. The promise will resolve with the storage
|
|
* cell if it is found. If the storage cell is not found, the promise will
|
|
* be rejected.
|
|
*
|
|
* @param {string} mechanismName
|
|
* @param {string} cellName
|
|
* @return {!shaka.extern.StorageCell}
|
|
*/
|
|
getCell(mechanismName, cellName) {
|
|
const mechanism = this.mechanisms_.get(mechanismName);
|
|
if (!mechanism) {
|
|
throw new shaka.util.Error(
|
|
shaka.util.Error.Severity.CRITICAL,
|
|
shaka.util.Error.Category.STORAGE,
|
|
shaka.util.Error.Code.MISSING_STORAGE_CELL,
|
|
'Could not find mechanism with name ' + mechanismName);
|
|
}
|
|
|
|
const cell = mechanism.getCells().get(cellName);
|
|
if (!cell) {
|
|
throw new shaka.util.Error(
|
|
shaka.util.Error.Severity.CRITICAL,
|
|
shaka.util.Error.Category.STORAGE,
|
|
shaka.util.Error.Code.MISSING_STORAGE_CELL,
|
|
'Could not find cell with name ' + cellName);
|
|
}
|
|
|
|
return cell;
|
|
}
|
|
|
|
/**
|
|
* @param {function(!shaka.extern.EmeSessionStorageCell)} callback
|
|
*/
|
|
forEachEmeSessionCell(callback) {
|
|
this.mechanisms_.forEach((mechanism, name) => {
|
|
callback(mechanism.getEmeSessionCell());
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Gets an arbitrary EME session cell that can be used for storing new session
|
|
* info.
|
|
*
|
|
* @return {!shaka.extern.EmeSessionStorageCell}
|
|
*/
|
|
getEmeSessionCell() {
|
|
const mechanisms = Array.from(this.mechanisms_.keys());
|
|
if (!mechanisms.length) {
|
|
throw new shaka.util.Error(
|
|
shaka.util.Error.Severity.CRITICAL,
|
|
shaka.util.Error.Category.STORAGE,
|
|
shaka.util.Error.Code.STORAGE_NOT_SUPPORTED,
|
|
'No supported storage mechanisms found');
|
|
}
|
|
|
|
return this.mechanisms_.get(mechanisms[0]).getEmeSessionCell();
|
|
}
|
|
|
|
/**
|
|
* Find the cell that the path points to. A path is made up of a mount point
|
|
* and a cell id. If a cell can be found, the cell will be returned. If no
|
|
* cell is found, null will be returned.
|
|
*
|
|
* @param {shaka.offline.StorageCellPath} path
|
|
* @return {shaka.extern.StorageCell}
|
|
*/
|
|
resolvePath(path) {
|
|
const mechanism = this.mechanisms_.get(path.mechanism);
|
|
|
|
if (!mechanism) {
|
|
return null;
|
|
}
|
|
|
|
return mechanism.getCells().get(path.cell);
|
|
}
|
|
|
|
/**
|
|
* This will erase all previous content from storage. Using paths obtained
|
|
* before calling |erase| is discouraged, as cells may have changed during a
|
|
* erase.
|
|
*
|
|
* @return {!Promise}
|
|
*/
|
|
async erase() {
|
|
// If we have initialized, we will use the existing mechanism instances.
|
|
/** @type {!Array<!shaka.extern.StorageMechanism>} */
|
|
const mechanisms = Array.from(this.mechanisms_.values());
|
|
const alreadyInitialized = mechanisms.length > 0;
|
|
|
|
// If we have not initialized, we should still be able to erase. This is
|
|
// critical to our ability to wipe the DB in case of a version mismatch.
|
|
// If there are no instances, create temporary ones and destroy them later.
|
|
if (!alreadyInitialized) {
|
|
const registry = shaka.offline.StorageMuxer.getRegistry_();
|
|
registry.forEach((factory, name) => {
|
|
const mech = factory();
|
|
if (mech) {
|
|
mechanisms.push(mech);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Erase all storage mechanisms.
|
|
await Promise.all(mechanisms.map((m) => m.erase()));
|
|
|
|
// If we were erasing temporary instances, destroy them, too.
|
|
if (!alreadyInitialized) {
|
|
await Promise.all(mechanisms.map((m) => m.destroy()));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register a storage mechanism for use with the default storage muxer. This
|
|
* will have no effect on any storage muxer already in main memory.
|
|
*
|
|
* @param {string} name
|
|
* @param {function():shaka.extern.StorageMechanism} factory
|
|
* @export
|
|
*/
|
|
static register(name, factory) {
|
|
shaka.offline.StorageMuxer.registry_.set(name, factory);
|
|
}
|
|
|
|
|
|
/**
|
|
* Unregister a storage mechanism for use with the default storage muxer. This
|
|
* will have no effect on any storage muxer already in main memory.
|
|
*
|
|
* @param {string} name The name that the storage mechanism was registered
|
|
* under.
|
|
* @export
|
|
*/
|
|
static unregister(name) {
|
|
shaka.offline.StorageMuxer.registry_.delete(name);
|
|
}
|
|
|
|
/**
|
|
* Check if there is support for storage on this platform. It is assumed that
|
|
* if there are any mechanisms registered, it means that storage is supported
|
|
* on this platform. We do not check if the mechanisms have any cells.
|
|
*
|
|
* @return {boolean}
|
|
*/
|
|
static support() {
|
|
const registry = shaka.offline.StorageMuxer.getRegistry_();
|
|
// Make sure that we will have SOME mechanisms created by creating a
|
|
// mechanism and immediately destroying it.
|
|
for (const create of registry.values()) {
|
|
const instance = create();
|
|
|
|
if (instance) {
|
|
instance.destroy();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Replace the mechanism map used by the muxer. This should only be used
|
|
* in testing.
|
|
*
|
|
* @param {Map<string, function(): shaka.extern.StorageMechanism>} map
|
|
*/
|
|
static overrideSupport(map) {
|
|
shaka.offline.StorageMuxer.override_ = map;
|
|
}
|
|
|
|
/**
|
|
* Undo a previous call to |overrideSupport|.
|
|
*/
|
|
static clearOverride() {
|
|
shaka.offline.StorageMuxer.override_ = null;
|
|
}
|
|
|
|
/**
|
|
* Get the registry. If the support has been disabled, this will always
|
|
* an empty registry. Reading should always be done via |getRegistry_|.
|
|
*
|
|
* @return {!Map<string, function(): shaka.extern.StorageMechanism>}
|
|
* @private
|
|
*/
|
|
static getRegistry_() {
|
|
const override = shaka.offline.StorageMuxer.override_;
|
|
const registry = shaka.offline.StorageMuxer.registry_;
|
|
|
|
if (COMPILED) {
|
|
return registry;
|
|
} else {
|
|
return override || registry;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* @private {Map<string, function(): shaka.extern.StorageMechanism>}
|
|
*/
|
|
shaka.offline.StorageMuxer.override_ = null;
|
|
|
|
|
|
/**
|
|
* @private {!Map<string, function(): shaka.extern.StorageMechanism>}
|
|
*/
|
|
shaka.offline.StorageMuxer.registry_ = new Map();
|