From da60aeb71bbf692ae30a69e091c3409ad09b1638 Mon Sep 17 00:00:00 2001 From: Akinwale Ariwodola Date: Fri, 17 Aug 2018 19:11:15 +0100 Subject: [PATCH] delete individual blob files for completed file downloads to save space (#234) --- app/src/component/AppNavigator.js | 7 +++++ app/src/constants.js | 9 +++--- app/src/page/splash/index.js | 2 ++ app/src/page/splash/view.js | 3 ++ app/src/redux/actions/file.js | 50 +++++++++++++++++++++++-------- 5 files changed, 55 insertions(+), 16 deletions(-) diff --git a/app/src/component/AppNavigator.js b/app/src/component/AppNavigator.js index 477ead4f..caf0c36f 100644 --- a/app/src/component/AppNavigator.js +++ b/app/src/component/AppNavigator.js @@ -27,6 +27,7 @@ import { TextInput, ToastAndroid } from 'react-native'; +import { doDeleteCompleteBlobs } from '../redux/actions/file'; import { SETTINGS, doHideNotification, doNotify, selectNotification } from 'lbry-redux'; import { doUserEmailVerify, @@ -227,6 +228,7 @@ class AppWithNavigationState extends React.Component { } _handleAppStateChange = (nextAppState) => { + const { dispatch } = this.props; // Check if the app was suspended if (AppState.currentState && AppState.currentState.match(/inactive|background/)) { AsyncStorage.getItem('firstLaunchTime').then(start => { @@ -237,6 +239,11 @@ class AppWithNavigationState extends React.Component { } }); } + + if (AppState.currentState && AppState.currentState.match(/active/)) { + // Cleanup blobs for completed files upon app resume to save space + dispatch(doDeleteCompleteBlobs()); + } } _handleUrl = (evt) => { diff --git a/app/src/constants.js b/app/src/constants.js index c58f0a85..003ec2d0 100644 --- a/app/src/constants.js +++ b/app/src/constants.js @@ -1,10 +1,11 @@ const Constants = { - KEY_FIRST_RUN_EMAIL: "firstRunEmail", - KEY_SHOULD_VERIFY_EMAIL: "shouldVerifyEmail", + KEY_FIRST_RUN_EMAIL: "firstRunEmail", + KEY_SHOULD_VERIFY_EMAIL: "shouldVerifyEmail", - SETTING_ALPHA_UNDERSTANDS_RISKS: "alphaUnderstandRisks", + SETTING_ALPHA_UNDERSTANDS_RISKS: "alphaUnderstandRisks", - ACTION_FIRST_RUN_PAGE_CHANGED: "FIRST_RUN_PAGE_CHANGED", + ACTION_DELETE_COMPLETED_BLOBS: "DELETE_COMPLETED_BLOBS", + ACTION_FIRST_RUN_PAGE_CHANGED: "FIRST_RUN_PAGE_CHANGED", }; export default Constants; diff --git a/app/src/page/splash/index.js b/app/src/page/splash/index.js index a8eb503b..6b832de6 100644 --- a/app/src/page/splash/index.js +++ b/app/src/page/splash/index.js @@ -8,6 +8,7 @@ import { selectUser, selectEmailToVerify } from 'lbryinc'; +import { doDeleteCompleteBlobs } from '../../redux/actions/file'; import SplashScreen from './view'; const select = state => ({ @@ -17,6 +18,7 @@ const select = state => ({ const perform = dispatch => ({ authenticate: (appVersion, deviceId) => dispatch(doAuthenticate(appVersion, deviceId)), + deleteCompleteBlobs: () => dispatch(doDeleteCompleteBlobs()), balanceSubscribe: () => dispatch(doBalanceSubscribe()), notify: data => dispatch(doNotify(data)), setEmailToVerify: email => dispatch(doUserEmailToVerify(email)), diff --git a/app/src/page/splash/view.js b/app/src/page/splash/view.js index 56214aac..1efbd63d 100644 --- a/app/src/page/splash/view.js +++ b/app/src/page/splash/view.js @@ -117,10 +117,13 @@ class SplashScreen extends React.PureComponent { } _updateStatusCallback(status) { + const { deleteCompleteBlobs } = this.props; const startupStatus = status.startup_status; // At the minimum, wallet should be started and blocks_behind equal to 0 before calling resolve const hasStarted = startupStatus.wallet && status.wallet.blocks_behind <= 0; if (hasStarted) { + deleteCompleteBlobs(); + // Wait until we are able to resolve a name before declaring // that we are done. // TODO: This is a hack, and the logic should live in the daemon diff --git a/app/src/redux/actions/file.js b/app/src/redux/actions/file.js index 871f1ee5..4b34fbd3 100644 --- a/app/src/redux/actions/file.js +++ b/app/src/redux/actions/file.js @@ -10,6 +10,7 @@ import { selectDownloadingByOutpoint, } from 'lbry-redux'; import { Alert, NativeModules } from 'react-native'; +import Constants from '../../constants'; const DOWNLOAD_POLL_INTERVAL = 250; @@ -40,14 +41,18 @@ export function doUpdateLoadStatus(uri, outpoint) { if (NativeModules.LbryDownloadManager) { NativeModules.LbryDownloadManager.updateDownload(uri, fileInfo.file_name, 100, writtenBytes, totalBytes); } - + + // Once a download has been completed, delete the individual blob files to save space + Lbry.blob_list({ sd_hash: fileInfo.sd_hash }).then(hashes => { + hashes.forEach(hash => { + Lbry.blob_delete({ blob_hash: hash }); + }); + }); + /*const notif = new window.Notification('LBRY Download Complete', { body: fileInfo.metadata.stream.metadata.title, silent: false, - }); - notif.onclick = () => { - ipcRenderer.send('focusWindow', 'main'); - };*/ + });*/ } else { // ready to play const { total_bytes: totalBytes, written_bytes: writtenBytes } = fileInfo; @@ -66,7 +71,7 @@ export function doUpdateLoadStatus(uri, outpoint) { if (NativeModules.LbryDownloadManager) { NativeModules.LbryDownloadManager.updateDownload(uri, fileInfo.file_name, progress, writtenBytes, totalBytes); } - + setTimeout(() => { dispatch(doUpdateLoadStatus(uri, outpoint)); }, DOWNLOAD_POLL_INTERVAL); @@ -96,7 +101,7 @@ export function doStartDownload(uri, outpoint) { fileInfo, }, }); - + if (NativeModules.LbryDownloadManager) { NativeModules.LbryDownloadManager.startDownload(uri, fileInfo.file_name); } @@ -110,7 +115,7 @@ export function doStopDownloadingFile(uri, fileInfo) { return dispatch => { let params = { status: 'stop' }; if (fileInfo.sd_hash) { - params.sd_hash = fileInfo.sd_hash; + params.sd_hash = fileInfo.sd_hash; } if (fileInfo.stream_hash) { params.stream_hash = fileInfo.stream_hash; @@ -122,11 +127,11 @@ export function doStopDownloadingFile(uri, fileInfo) { data: {} }); }); - + if (NativeModules.LbryDownloadManager) { NativeModules.LbryDownloadManager.stopDownload(uri, fileInfo.file_name); } - + // Should also delete the file after the user stops downloading dispatch(doDeleteFile(fileInfo.outpoint, uri)); }; @@ -159,7 +164,7 @@ export function doLoadVideo(uri) { uri, }, }); - + Lbry.get({ uri }) .then(streamInfo => { const timeout = @@ -186,7 +191,7 @@ export function doLoadVideo(uri) { type: ACTIONS.LOADING_VIDEO_FAILED, data: { uri }, }); - + dispatch(doNotify({ message: `Failed to download ${uri}, please try again. If this problem persists, visit https://lbry.io/faq/support for support.`, displayType: ['toast'] @@ -301,3 +306,24 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) { //setProgressBar(totalProgress); }; } + +export function doDeleteCompleteBlobs() { + return dispatch => { + dispatch({ + type: Constants.ACTION_DELETE_COMPLETED_BLOBS, + data: {}, + }); + + Lbry.file_list().then(files => { + files.forEach(fileInfo => { + if (fileInfo.completed) { + Lbry.blob_list({ sd_hash: fileInfo.sd_hash }).then(hashes => { + hashes.forEach(hash => { + Lbry.blob_delete({ blob_hash: hash }); + }); + }); + } + }); + }); + }; +}