Skip to content

Commit

Permalink
fix(FEC-13513): save playback speed to session storage (#672)
Browse files Browse the repository at this point in the history
### Description of the Changes

save playbackRate in session storage instead of local storage.

Solves FEC-13513
  • Loading branch information
lianbenjamin authored Dec 5, 2023
1 parent b990ab5 commit 88f1d03
Show file tree
Hide file tree
Showing 11 changed files with 472 additions and 243 deletions.
208 changes: 208 additions & 0 deletions src/common/storage/base-storage-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// @flow
import StorageWrapper from './storage-wrapper';
import {Error, Utils, getLogger} from '@playkit-js/playkit-js';

export class BaseStorageHelper {
static instance: BaseStorageHelper | null = null;
storageManagers: Array<BaseStorageManager>;

constructor() {
this.storageManagers = [];
}

addManager(manager: BaseStorageManager): void {
this.storageManagers.push(manager);
}

static getInstance(): BaseStorageHelper {
if (this.instance === null) {
this.instance = new BaseStorageHelper();
}
return this.instance;
}
}

export class BaseStorageManager {
static _logger: any;
static StorageKeys: {[key: string]: string};

/**
* Initializes class.
* @private
* @param {string} className - The manager's class name.
* @returns {void}
*/
static init(className: string): void {
this._logger = getLogger(className);
//$FlowFixMe
BaseStorageHelper.getInstance().addManager(this);
}

/**
* Applies cache support if it's supported by the environment.
* @private
* @param {KalturaPlayer} player - The Kaltura player.
* @returns {void}
*/
static attachAll(player: Player): void {
BaseStorageHelper.getInstance().storageManagers.forEach((manager: BaseStorageManager) => {
//$FlowFixMe
if (manager.isStorageAvailable()) {
//$FlowFixMe
manager.attach(player);
}
});
}

/**
* Sets the storage config on the player config if certain conditions are met.
* @private
* @param {KPOptionsObject} options - kaltura player options
* @returns {void}
*/
static setStorageConfig(options: KPOptionsObject): void {
BaseStorageHelper.getInstance().storageManagers.forEach(manager => {
//$FlowFixMe
if (manager.isStorageAvailable() && manager.hasStorage()) {
//$FlowFixMe
Utils.Object.mergeDeep(options, manager.getStorageConfig());
}
});
}

/**
* @static
* @private
* @returns {boolean} - Whether the storage is implemented in the current browser.
*/
static isStorageAvailable(): boolean {
return StorageWrapper.isStorageAvailable(this.getStorageObject());
}

/**
* Checks if we have previous storage.
* @private
* @static
* @return {boolean} - Whether we have previous storage.
*/
static hasStorage(): boolean {
const storageSize = this.getStorageSize();
const hasStorage = storageSize !== 0;
if (hasStorage) {
this._logger.debug('Storage found with size of ', storageSize);
} else {
this._logger.debug('No storage found');
}
return hasStorage;
}

/**
* Sets an item in the storage.
* @private
* @static
* @param {string} key - The key of the item
* @param {any} item - The value of the item
* @returns {void}
*/
static setItem(key: string, item: any): void {
StorageWrapper.setItem(key, item, this.getStorageObject());
}

/**
* Gets an item from the storage.
* @private
* @static
* @param {string} key - The item key
* @returns {any} - The item value
*/
static getItem(key: string): any {
StorageWrapper.getItem(key, this.getStorageObject());
}

/**
* Gets the storage size
* @static
* @private
* @return {number} - The number of keys in the local storage started with wanted prefix.
*/
static getStorageSize(): number {
return StorageWrapper.getStorageSize(this.getStorageObject());
}

/**
* Gets the storage in the structure of the player configuration.
* @private
* @static
* @return {Object} - Partial storageable player configuration.
*/
static getStorageConfig(): Object {
const values = this._getExistingValues();
const storageConfig = this._buildStorageConfig(values);
this._logger.debug('Gets storage config', storageConfig);
return storageConfig;
}

/**
* Gets the current existing values in the storage.
* @private
* @static
* @return {Object} - The values object from the storage.
*/
static _getExistingValues(): Object {
const obj = {};
Object.keys(this.StorageKeys).forEach(key => {
const value = this.StorageKeys[key];
const item = StorageWrapper.getItem(value, this.getStorageObject());
if (item != null) {
obj[value] = item;
}
});
return obj;
}

/**
* Builds the storage configuration object.
* @private
* @static
* @param {Object} values - The values to set to storage configuration
* @return {Object} - The configuration with values from the storage.
*/
static _buildStorageConfig(values: Object): Object {
const storageConfig = Utils.Object.mergeDeep({}, values);
delete storageConfig.textStyle;
return {
playback: storageConfig
};
}

/**
* Gets the storage object to access.
* i.e: sessionStorage, localStorage
* @private
* @static
* @return {Object} - The storage object.
*/
static getStorageObject(): Object {
throw new Error(Error.Severity.CRITICAL, Error.Category.PLAYER, Error.Code.RUNTIME_ERROR_METHOD_NOT_IMPLEMENTED, 'getStorageObject()');
}

/**
* Attaches listeners to the storage manager.
* @private
* @static
* @return {void}
*/
static attach(): void {
throw new Error(Error.Severity.CRITICAL, Error.Category.PLAYER, Error.Code.RUNTIME_ERROR_METHOD_NOT_IMPLEMENTED, 'attach()');
}

/**
* Initialize the storage manager.
* @private
* @static
* @return {void}
*/
static initialize(): void {
throw new Error(Error.Severity.CRITICAL, Error.Category.PLAYER, Error.Code.RUNTIME_ERROR_METHOD_NOT_IMPLEMENTED, 'initialize()');
}
}
102 changes: 102 additions & 0 deletions src/common/storage/local-storage-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// @flow
import {EventManager} from '@playkit-js/playkit-js';
import {BaseStorageManager} from './base-storage-manager';

export default class LocalStorageManager extends BaseStorageManager {
static StorageKeys: {[key: string]: string} = {
MUTED: 'muted',
VOLUME: 'volume',
AUDIO_LANG: 'audioLanguage',
TEXT_LANG: 'textLanguage',
CAPTIONS_DISPLAY: 'captionsDisplay',
TEXT_STYLE: 'textStyle'
};

static initialize() {
this.init(this.name);
}

static getStorageObject() {
return localStorage;
}

/**
* Attaches the player listeners to the local storage wrapper.
* @private
* @param {Player} player - The player reference.
* @static
* @returns {void}
*/
static attach(player: Player): void {
this._logger.debug('Attach local storage');
let eventManager = new EventManager();
eventManager.listen(player, player.Event.UI.USER_CLICKED_MUTE, () => {
if (!player.isCasting()) {
this.setItem(this.StorageKeys.MUTED, player.muted);
}
});
eventManager.listen(player, player.Event.UI.USER_CLICKED_UNMUTE, () => {
if (!player.isCasting()) {
this.setItem(this.StorageKeys.MUTED, player.muted);
}
});

eventManager.listen(player, player.Event.UI.USER_CHANGED_VOLUME, () => {
if (!player.isCasting()) {
this.setItem(this.StorageKeys.MUTED, !player.volume);
this.setItem(this.StorageKeys.VOLUME, player.volume);
}
});

eventManager.listen(player, player.Event.UI.USER_SELECTED_AUDIO_TRACK, event => {
const audioTrack = event.payload.audioTrack;
this.setItem(this.StorageKeys.AUDIO_LANG, audioTrack.language);
});

eventManager.listen(player, player.Event.UI.USER_SELECTED_CAPTION_TRACK, event => {
const textTrack = event.payload.captionTrack;
if (textTrack.language !== 'off') {
this.setItem(this.StorageKeys.TEXT_LANG, textTrack.language);
this.setItem(this.StorageKeys.CAPTIONS_DISPLAY, true);
} else {
this.setItem(this.StorageKeys.CAPTIONS_DISPLAY, false);
}
});

const onToggleCaptions = () => {
eventManager.listenOnce(player, player.Event.TEXT_TRACK_CHANGED, event => {
const {selectedTextTrack} = event.payload;
if (selectedTextTrack.language !== 'off') {
this.setItem(this.StorageKeys.TEXT_LANG, selectedTextTrack.language);
this.setItem(this.StorageKeys.CAPTIONS_DISPLAY, true);
} else {
this.setItem(this.StorageKeys.CAPTIONS_DISPLAY, false);
}
});
};

eventManager.listen(player, player.Event.UI.USER_SHOWED_CAPTIONS, onToggleCaptions);
eventManager.listen(player, player.Event.UI.USER_HID_CAPTIONS, onToggleCaptions);

eventManager.listen(player, player.Event.UI.USER_SELECTED_CAPTIONS_STYLE, event => {
try {
const textStyle = JSON.stringify(event.payload.captionsStyle);
this.setItem(this.StorageKeys.TEXT_STYLE, textStyle);
} catch (e) {
this._logger.error(e.message);
}
});

eventManager.listen(player, player.Event.PLAYER_DESTROY, () => eventManager.destroy());
}

/**
* Gets the player text style from storage.
* @private
* @static
* @returns {?Object} - The stored text style object
*/
static getPlayerTextStyle(): ?Object {
return this.getItem(this.StorageKeys.TEXT_STYLE);
}
}
34 changes: 34 additions & 0 deletions src/common/storage/session-storage-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// @flow
import {EventManager} from '@playkit-js/playkit-js';
import {BaseStorageManager} from './base-storage-manager';

export default class SessionStorageManager extends BaseStorageManager {
static StorageKeys: {[key: string]: string} = {
PLAYBACK_RATE: 'playbackRate'
};

static initialize() {
this.init(this.name);
}

static getStorageObject() {
return sessionStorage;
}

/**
* Attaches the player listeners to the local storage wrapper.
* @private
* @param {Player} player - The player reference.
* @static
* @returns {void}
*/
static attach(player: Player): void {
this._logger.debug('Attach session storage');
let eventManager = new EventManager();
eventManager.listen(player, player.Event.UI.USER_SELECTED_SPEED, () => {
if (!player.isCasting()) {
this.setItem(this.StorageKeys.PLAYBACK_RATE, player.playbackRate);
}
});
}
}
Loading

0 comments on commit 88f1d03

Please sign in to comment.