Skip to content

Commit

Permalink
Added background changing and improved downloading.
Browse files Browse the repository at this point in the history
  • Loading branch information
ikogan committed Jun 3, 2016
1 parent b20537e commit 8159c83
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 87 deletions.
211 changes: 161 additions & 50 deletions main.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
'use strict';

const debug = require('debug')('farnsworth:main');
const electron = require('electron');
const request = require('request');
const fs = require('fs');
const path = require('path');
const fsExtra = require('fs-extra');
const download = require('download');
const sha1 = require('sha1');
const _ = require('lodash');

// Module to control application life.
Expand Down Expand Up @@ -68,20 +70,21 @@ function createWindow () {
});
}

function cleanBackgrounds() {
function cleanBackgrounds(backgrounds) {
console.log('Cleaning leftover backgrounds...');

// Clean up any backgrounds we don't have in the list anymore.
fs.readDir(BACKGROUND_SAVE_PATH, function(error, files) {
if(!error) {
_.each(files, function(f) {
var index = path.basename(f, path.extname(f));
var filename = path.basename(f, path.extname(f));

try {
if(parseInt(index) >= backgrounds.length) {
fs.unlink(f);
}
} catch(e) {}
if(!_.find(backgrounds, function(entry) {
return path.basename(f) === entry.filename;
})) {
console.log(`Deleting ${f}`);
fs.unlink(f);
}
});
}
});
Expand All @@ -93,67 +96,175 @@ function downloadBackgrounds() {
ipc.on('background-service-ready', function(ev) {
var bgService = ev.sender;

function doDownload(backgrounds, index) {
function doDownload(backgrounds, index, bgAvailable) {
debug(`Checking for background at index ${index}`);

return new Promise(function(reject, resolve) {
if(index < backgrounds.length) {
var filename = path.join(BACKGROUNDS_DIR, `${index}${path.extname(backgrounds[0].url)}`);

console.log(`Downloading ${backgrounds[index].url} to ${filename}...`);
download(backgrounds[index].url)
.pipe(fs.createWriteStream(filename))
.on('close', function() {
if(index === 0) {
bgService.send('background-available', index, backgrounds[index]);
}
if(backgrounds[index].downloaded) {
debug(' - Already downloaded, skipping.');
doDownload(backgrounds, index + 1).then(resolve, reject);
} else {
// Since the URLs contain a variety of filenames, for easiest
// portability, we're just going to generate a new filename
// based on the sha1 hash of the URL. Overkill? Maybe.
// Problematic for other reasons? Also possible.
//
// As one of the dads would say: "Good enough for who it's for."
debug(' - Downloading...');
backgrounds[index].filename = `${sha1(backgrounds[index].url)}${path.extname(backgrounds[index].url)}`;
var filename = path.join(BACKGROUNDS_DIR, backgrounds[index].filename);

return doDownload(backgrounds, index + 1);
}).on('error', function(error) {
console.log(error);
});
console.log(`Downloading ${backgrounds[index].url} to ${filename}...`);
download(backgrounds[index].url)
.pipe(fs.createWriteStream(filename))
.on('close', function() {
debug(' - Download complete.');

// Set the probably unnecessary downloaded flag to true.
backgrounds[index].downloaded = true;

// If we haven't told the renderer that a bg is available, do
// so now.
if(!bgAvailable) {
bgAvailable = true;
bgService.send('background-available', index, backgrounds[index]);
}

bgService.send('background-data-available', backgrounds);

// Save the backgrounds file. Possibly a minor performance impact
// serializing JSON and writing to disk every time we download an
// image but...see above dad quote.
fs.writeFile(BACKGROUNDS_SAVE_PATH, JSON.stringify(backgrounds), function(error) {
if(error) {
bgWindow.send('backgrounds-error', 'Could not save backgrounds list', error);
}

doDownload(backgrounds, index + 1).then(resolve, reject);
});
}).on('error', function(error) {
console.log(`Error downloading ${backgrounds[index].url}:`, error);
bgWindow.send('backgrounds-error', `Error downloading ${backgrounds[index].url}`, error);
doDownload(backgrounds, index + 1).then(resolve, reject);
});
}
} else {
// Resolve only when we've processed all images.
debug('Completed all files, resolving.');
resolve();
}
});
}

console.log('Loading background images...');

// Load the JSON file describing the backgrounds.
request(BACKGROUND_URL, function(error, response, body) {
if(error || response.statusCode !== 200) {
console.error('Error loading backgrounds from ' + BACKGROUND_URL, error);
bgService.send('backgrounds-error', 'Error loading backgrounds from ' + BACKGROUND_URL, error);
} else {
var backgrounds = JSON.parse(body);
// Try to be as quick as possible finding the first background.
// First, see if we already have background data.
fs.readFile(BACKGROUNDS_SAVE_PATH, function(error, data) {
var backgrounds = null;
var bgAvailable = false;

if(!error) {
debug('Found existing backgrounds file, loading...');

// We do, try and load it and, if successful, tell the
// renderer that we have data and look for an actual
// downloaded image file. If we have at least one, let
// the renderer know we're ready to go.
try {
backgrounds = JSON.parse(data);
bgService.send('background-data-available', backgrounds);

bgService.send('background-data-available', backgrounds);
if(_.find(backgrounds, function(entry) {
if(!entry.downloaded || !entry.filename) {
return false;
}

// Store the file locally.
fs.writeFile(BACKGROUNDS_SAVE_PATH, body, function(error) {
if(error) {
bgWindow.send('Could not save backgrounds list: ' + error);
try {
fs.accessSync(entry.filename, fs.R_OK);
return true;
} catch(e) {
return false;
}
})) {
debug('Found at least one available background image, notifying renderer.');
bgAvailable = true;
bgService.send('background-available', backgrounds[index]);
}
});

// Create the backgrounds directory
fsExtra.mkdirs(BACKGROUNDS_DIR, function(error) {
// TODO: Handle already exists
if(error) {
console.error('Could not create directory for backgrounds: ', error);
bgService.send('backgrounds-error', 'Could not create directory for backgrounds', error);
} else {
doDownload(backgrounds, 0).then(function() {
bgService.send('backgrounds-downloaded');
} catch(e) {
backgrounds = null;
}
}

// Now, load up the background file from the URL and, if successful,
// save it or update ours by adding new backgrounds and removing those
// that don't exist.
request(BACKGROUND_URL, function(error, response, body) {
if(error || response.statusCode !== 200) {
console.error('Error loading backgrounds from ' + BACKGROUND_URL, error);
bgService.send('backgrounds-error', 'Error loading backgrounds from ' + BACKGROUND_URL, error);
} else {
var newBgData = JSON.parse(body);

cleanBackgrounds();
}, function() {
bgService.send('backgrounds-downloaded');
// If we already have data, go through it and update it with
// data from the server.
if(backgrounds !== null) {
debug(`Updating data with new entries from remote. Existing count: ${backgrounds.length}, remote length: ${newBgData.length}`);
_.each(newBgData, function(entry) {
if(!_.find(backgrounds, function(existing) {
return entry.url === existing.url;
})) {
backgrounds.push(entry);
}
});

cleanBackgrounds();
debug(`New count: ${backgrounds.length}, removing backgrounds that have been removed from remote.`);
_.remove(backgrounds, function(entry) {
return !_.find(newBgData, function(newEntry) {
return newEntry.url === entry.url;
});
});

debug(`New count: ${backgrounds.length}.`);
} else {
backgrounds = newBgData;
}
});
}

debug(`Total backgrounds: ${backgrounds.length}`);
// Notify the renderer that new data is available. Even
// though we've already notified them once, give them
// a chance to deal with new data being available.
bgService.send('background-data-available', backgrounds);

// Save the background file locally.
fs.writeFile(BACKGROUNDS_SAVE_PATH, JSON.stringify(backgrounds, null, 4), function(error) {
if(error) {
bgWindow.send('backgrounds-error', 'Could not save backgrounds list', error);
}
});

// Create the backgrounds directory
fsExtra.mkdirs(BACKGROUNDS_DIR, function(error) {
if(error) {
console.error('Could not create directory for backgrounds: ', error);
bgService.send('backgrounds-error', 'Could not create directory for backgrounds', error);
} else {
// Download all of the background images, succeeding whether or not
// the promise resolves or rejects.
doDownload(backgrounds, 0, bgAvailable).then(function() {
debug(`Background downloading succeeded.`);
bgService.send('backgrounds-downloaded');

cleanBackgrounds(backgrounds);
}, function(error) {
bgService.send('backgrounds-error', `Unhandled error downloading backgrounds: ${error}`);
throw error;
});
}
});
}
});
});
});
}
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"angular-route": "^1.5.5",
"angular-sanitize": "^1.5.5",
"angular-scroll": "^1.0.0",
"debug": "^2.2.0",
"download": "^5.0.0",
"fs-extra": "^0.30.0",
"lodash": "^4.11.1",
Expand All @@ -45,6 +46,7 @@
"mousetrap": "^1.5.3",
"request": "^2.72.0",
"roboto-fontface": "^0.4.5",
"sha1": "^1.1.1",
"slash": "^1.0.0"
}
}
48 changes: 31 additions & 17 deletions ui/scripts/controllers/home.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
*/
angular.module('farnsworth')
.controller('HomeController', function($location, $mdToast, $mdDialog, $timeout, $q,
$scope, $route, $document, hotkeys, duScrollDuration, duScrollEasing,
SettingsService, BackgroundsService, HotkeyDialog) {
$scope, $route, $document, hotkeys, duScrollDuration,
duScrollEasing, SettingsService, BackgroundsService, HotkeyDialog) {
var slash = require('slash'); // Convert Windows paths to something we can use in `file:///` urls.
var app = require('electron').remote.app; // Needed to close the application.

var self = this;
var constants = {
holdTime: 1000, // Time in MS we have to hold enter before we popup the edit dialog
systemBackgroundColor: '#4B585C', // Background color of the hardcoded "System" category tiles.
systemTextColor: '#FFFFFF' // Text color of the hardcoded "System" category tiles.
systemTextColor: '#FFFFFF', // Text color of the hardcoded "System" category tiles.
backgroundRefreshTime: 1800 // Time in seconds for background cycling.
};

self.loading = true; // Used to track when to show the loading spinner.
Expand All @@ -36,7 +37,9 @@ angular.module('farnsworth')

SettingsService.get().then(function(settings) {
self.settings = settings;


self.setRandomBackground();

if(_.has(settings, 'categories') && _.size(settings.categories) > 0) {
self.categories = settings.categories;

Expand All @@ -46,29 +49,40 @@ angular.module('farnsworth')
self.initEmpty();
}

self.loading = false;
BackgroundsService.waitForBackground.finally(function() {
self.loading = false;
});
}).catch(function(error) {
self.setRandomBackground();

$mdToast.show(
$mdToast.simple()
.textContent(`Error loading application settings: ${error}`)
.hideDelay(3000));

self.initEmpty();
self.loading = false;
});

BackgroundsService.getRandomBackground().then(function(background) {
self.background = background;
self.backgroundStyle = {
'background-image': `url("file:///${slash(background.filename)}")`
};
}).catch(function(error) {
$mdToast.show(
$mdToast.simple()
.textContent(`Error loading application background images: ${error}`)
.hideDelay(3000));
BackgroundsService.waitForBackground.finally(function() {
self.loading = false;
});
});

self.setRandomBackground = function() {
BackgroundsService.getRandomBackground().then(function(background) {
self.background = background;
self.backgroundStyle = {
'background-image': `url("file:///${slash(background.filename)}")`
};

$timeout(self.setRandomBackground, constants.backgroundRefreshTime * 1000);
}).catch(function(error) {
$mdToast.show(
$mdToast.simple()
.textContent(`Error loading application background images: ${error}`)
.hideDelay(3000));
});
};

/**
* Initialize an empty homescreen. Really just bind the "Enter" button
* to trigger adding our first tile.
Expand Down
Loading

0 comments on commit 8159c83

Please sign in to comment.