UNPKG

react-native-media-suite

Version:

A native media player with download capabilities. It was forked from react-native-media-kit.

328 lines (280 loc) 12.7 kB
import { NativeModules, NativeEventEmitter, Platform, AppState } from 'react-native'; import _ from 'lodash'; import storageService from 'rn-multi-tenant-async-storage'; import Download, { DOWNLOAD_STATES, EVENT_LISTENER_TYPES } from './download'; const tenantKey = 'RNMediaSuite_DownloadManager'; class DownloadManager { constructor() { if (!DownloadManager.downloader) { storageService.addStorageTenant(tenantKey); this.tenant = storageService.getStorageTenant(tenantKey); this.restoreMediaDownloader = this.restoreMediaDownloader.bind(this); this.setMaxSimultaneousDownloads = this.setMaxSimultaneousDownloads.bind(this); this.createNewDownload = this.createNewDownload.bind(this); this.deleteDownloaded = this.deleteDownloaded.bind(this); this.onDownloadStarted = this.onDownloadStarted.bind(this); this.onDownloadProgress = this.onDownloadProgress.bind(this); this.onDownloadFinished = this.onDownloadFinished.bind(this); this.onDownloadError = this.onDownloadError.bind(this); this.onDownloadCancelled = this.onDownloadCancelled.bind(this); this.onDownloadPaused = this.onDownloadPaused.bind(this); this.onDownloadResumed = this.onDownloadResumed.bind(this); this.addUpdateListener = this.addUpdateListener.bind(this); this.callUpdateListeners = this.callUpdateListeners.bind(this); this.removeUpdateListener = this.removeUpdateListener.bind(this); this.handleAppStateChange = this.handleAppStateChange.bind(this); this.getDownload = this.getDownload.bind(this); this.isDownloaded = this.isDownloaded.bind(this); this.checkIfStillDownloaded = this.checkIfStillDownloaded.bind(this); this.persistDownload = this.persistDownload.bind(this); this.downloads = []; this.updateListeners = []; this.nativeDownloader = NativeModules.MediaDownloader; const downloaderEvent = new NativeEventEmitter(NativeModules.MediaDownloader); downloaderEvent.addListener('onDownloadFinished', this.onDownloadFinished); downloaderEvent.addListener('onDownloadProgress', this.onDownloadProgress); downloaderEvent.addListener('onDownloadStarted', this.onDownloadStarted); downloaderEvent.addListener('onDownloadError', this.onDownloadError); downloaderEvent.addListener('onDownloadCancelled', this.onDownloadCancelled); AppState.addEventListener('change', this.handleAppStateChange); DownloadManager.downloader = this; } return DownloadManager.downloader; } restoreMediaDownloader() { return new Promise((resolve, reject) => { storageService.getAllKeyValuePairs(this.tenant).then( downloads => { let downloadIds = []; _.forEach(downloads, download => { const newDownload = new Download( download[1].downloadID, download[1].remoteURL, download[1].state, download[1].bitRate, download[1].title, download[1].assetArtworkURL, this.nativeDownloader, download[1] ); newDownload.addEventListener( EVENT_LISTENER_TYPES.deleted, this.deleteDownloaded ); newDownload.addEventListener( EVENT_LISTENER_TYPES.paused, this.onDownloadPaused ); newDownload.addEventListener( EVENT_LISTENER_TYPES.resumed, this.onDownloadResumed ); this.downloads.push(newDownload); downloadIds.push(download[1].downloadID); }); if (Platform.OS === 'ios') { this.nativeDownloader.restoreMediaDownloader().then(() => { this.checkIfStillDownloaded(); resolve(downloadIds); }); } else { this.checkIfStillDownloaded(); resolve(downloadIds); } }, error => { reject(error); } ); }); } setMaxSimultaneousDownloads(maxSimultaneousDownloads) { if (Platform.OS === 'ios') { if ( typeof maxSimultaneousDownloads !== 'number' || maxSimultaneousDownloads % 1 !== 0 ) { throw 'maxSimultaneousDownloads should be of type integer.'; } this.nativeDownloader.setMaxSimultaneousDownloads(maxSimultaneousDownloads); } } createNewDownload(url, downloadID, title, assetArtworkURL, bitRate = 0) { let download = this.downloads.find(download => download.downloadID === downloadID); if (download && !download.isFailed()) { throw `Download already exists with ID: ${downloadID}`; } download = new Download( downloadID, url, DOWNLOAD_STATES.initialized, bitRate, title, assetArtworkURL, this.nativeDownloader ); this.downloads.push(download); download.addEventListener(EVENT_LISTENER_TYPES.deleted, this.deleteDownloaded); download.addEventListener(EVENT_LISTENER_TYPES.paused, this.onDownloadPaused); download.addEventListener(EVENT_LISTENER_TYPES.resumed, this.onDownloadResumed); this.persistDownload(download); this.callUpdateListeners(downloadID); return download; } deleteDownloaded(downloadID) { this.callUpdateListeners(downloadID); _.remove(this.downloads, download => download.downloadID === downloadID); storageService.removeItem(this.tenant, downloadID); } onDownloadStarted(data) { let download = this.getDownload(data.downloadID); if (!download) return; download.onDownloadStarted(); this.persistDownload(download); this.callUpdateListeners(data.downloadID); } onDownloadProgress(data) { let download = this.getDownload(data.downloadID); if (!download) return; download.onDownloadProgress(data.percentComplete); this.persistDownload(download); this.callUpdateListeners(data.downloadID); } onDownloadFinished(data) { let download = this.getDownload(data.downloadID); if (!download) return; download.onDownloadFinished(data.downloadLocation, data.size); this.persistDownload(download); this.callUpdateListeners(data.downloadID); } onDownloadError(data) { let download = this.getDownload(data.downloadID); if (!download) return; if (data.errorType === 'UNEXPECTEDLY_CANCELLED') { download.retry(); return; } download.onDownloadError(data.errorType, data.error); console.warn(data.error); this.persistDownload(download); this.callUpdateListeners(data.downloadID); } onDownloadCancelled(data) { let download = this.getDownload(data.downloadID); if (!download) return; download.onDownloadCancelled(); _.remove(this.downloads, download => download.downloadID === data.downloadID); storageService.removeItem(this.tenant, data.downloadID); this.callUpdateListeners(data.downloadID); } onDownloadPaused(downloadID) { let download = this.getDownload(downloadID); if (!download) return; this.persistDownload(download); this.callUpdateListeners(downloadID); } onDownloadResumed(downloadID) { let download = this.getDownload(downloadID); if (!download) return; this.persistDownload(download); this.callUpdateListeners(downloadID); } addUpdateListener(listener, options) { if (!options.downloadIDs) { this.updateListeners.push({ downloadIDs: null, listener: listener }); listener(this.getDownload(_.map(this.downloads, 'downloadID'), true)); } else { this.updateListeners.push({ downloadIDs: options.downloadIDs, listener }); if (options.updateImmediately) this.callUpdateListeners(options.downloadIDs[0]); } } callUpdateListeners(downloadID) { _.forEach(this.updateListeners, listenerObject => { if (_.isArray(listenerObject.downloadIDs)) { if (_.includes(listenerObject.downloadIDs, downloadID)) { listenerObject.listener(this.getDownload(listenerObject.downloadIDs, true)); } } else if (listenerObject.downloadIDs) { if (listenerObject.downloadIDs === downloadID) listenerObject.listener(this.getDownload(downloadID)); } else { listenerObject.listener( this.getDownload(_.map(this.downloads, 'downloadID'), true) ); } }); } handleAppStateChange(nextAppState) { if (nextAppState === 'active') { this.checkIfStillDownloaded(); } } removeUpdateListener(listener) { _.remove(this.updateListeners, listenerObject => listenerObject.listener === listener); } getDownload(downloadIDs, returnWithLabels = false) { const matchedDownloads = _.filter(this.downloads, download => { if (download.state !== DOWNLOAD_STATES.deleted) { if (_.isArray(downloadIDs)) { return _.indexOf(downloadIDs, download.downloadID) !== -1; } return download.downloadID === downloadIDs; } return false; }); if (_.isEmpty(matchedDownloads)) { return null; } if (returnWithLabels) { const matchedDownloadsWithLabels = {}; _.forEach(matchedDownloads, matchedDownload => { matchedDownloadsWithLabels[matchedDownload.downloadID] = matchedDownload; }); return matchedDownloadsWithLabels; } if (_.size(matchedDownloads) === 1) { return matchedDownloads[0]; } return matchedDownloads; } isDownloaded(downloadID) { return !!this.downloads.find(download => download.downloadID === downloadID); } checkIfStillDownloaded() { let downloadIDs = _.map(this.downloads, download => download.downloadID); this.nativeDownloader.checkIfStillDownloaded(downloadIDs).then(downloadedDownloadIDs => { if (!_.isEmpty(downloadedDownloadIDs)) { let deletedDownloadIDs = _.difference(downloadIDs, downloadedDownloadIDs); _.forEach(deletedDownloadIDs, downloadedDownloadID => { const download = _.find( this.downloads, download => download.downloadID === downloadedDownloadID ); if (download && download.state !== DOWNLOAD_STATES.failed) download.delete(); }); } }); } persistDownload(download) { storageService.setItem(this.tenant, download.downloadID, { downloadID: download.downloadID, remoteURL: download.remoteURL, state: download.state, bitRate: download.bitRate, title: download.title, assetArtworkURL: download.assetArtworkURL, progress: download.progress, localURL: download.localURL, fileSize: download.fileSize, errorType: download.errorType, errorMessage: download.errorMessage, startedTimeStamp: download.startedTimeStamp, finishedTimeStamp: download.finishedTimeStamp, erroredTimeStamp: download.erroredTimeStamp, progressTimeStamp: download.progressTimeStamp }); } } const downloadManager = new DownloadManager(); Object.freeze(downloadManager); export default downloadManager;