Skip to content

Commit

Permalink
Handle downloads via ipc, report on progress
Browse files Browse the repository at this point in the history
  • Loading branch information
nukeop committed Apr 14, 2019
1 parent be578f3 commit d7f0277
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 44 deletions.
48 changes: 31 additions & 17 deletions app/actions/downloads.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
import _ from 'lodash';
export const ADD_TO_DOWNLOADS = 'ADD_TO_DOWNLOADS';
export const DOWNLOAD_STARTED = 'DOWNLOAD_STARTED';
export const DOWNLOAD_PROGRESS = 'DOWNLOAD_PROGRESS';
export const DOWNLOAD_FINISHED = 'DOWNLOAD_FINISHED';
export const DOWNLOAD_ERROR = 'DOWNLOAD_ERROR';

function addTrackToDownloads(track) {
export function addToDownloads(musicSources, track) {
return {
type: ADD_TO_DOWNLOADS,
payload: { item: {
status: 'Started',
status: 'Waiting',
completion: 0,
track
} }
};
}

export function addToDownloads(musicSources, track) {
return dispatch => {
Promise.all(_.map(musicSources, m => m.search({ artist: track.artist, track: track.name })))
.then(results => Promise.all(results))
.then(streams => {
dispatch(
addTrackToDownloads(
Object.assign(
{},
track,
{ streams }
)
)
);
});
export function onDownloadStarted(uuid) {
return {
type: DOWNLOAD_STARTED,
payload: {
uuid
}
};
}

export function onDownloadProgress(uuid, progress) {
return {
type: DOWNLOAD_PROGRESS,
payload: {
uuid,
progress
}
};
}

export function onDownloadFinished(uuid) {
return {
type: DOWNLOAD_FINISHED,
payload: {
uuid
}
};
}
1 change: 0 additions & 1 deletion app/containers/DownloadsContainer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { bindActionCreators } from 'redux';
import Downloads from '../../components/Downloads';

const DownloadsContainer = props => {
console.log(props);
return (
<Downloads
downloads={ props.downloads }
Expand Down
17 changes: 16 additions & 1 deletion app/containers/IpcContainer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as QueueActions from '../../actions/queue';
import * as SettingsActions from '../../actions/settings';
import * as PlaylistActions from '../../actions/playlists';
import * as EqualizerActions from '../../actions/equalizer';
import * as DownloadsActions from '../../actions/downloads';

import {
onNext,
Expand Down Expand Up @@ -50,6 +51,19 @@ class IpcContainer extends React.Component {
ipcRenderer.on('refresh-playlists', (event) => onRefreshPlaylists(event, this.props.actions));
ipcRenderer.on('update-equalizer', (event, data) => onUpdateEqualizer(event, this.props.actions, data));
ipcRenderer.on('set-equalizer', (event, data) => onSetEqualizer(event, this.props.actions, data));

ipcRenderer.on('download-started', (event, data) => {
this.props.actions.onDownloadStarted(data);
});
ipcRenderer.on('download-progress', (event, data) => {
this.props.actions.onDownloadProgress(data.uuid, data.progress);
});
ipcRenderer.on('download-finished', (event, data) => {
this.props.actions.onDownloadFinished(data);
});
ipcRenderer.on('download-error', (event, data) => {
console.error(data);
});
}

componentWillReceiveProps(nextProps){
Expand Down Expand Up @@ -80,7 +94,8 @@ function mapDispatchToProps(dispatch) {
QueueActions,
SettingsActions,
PlaylistActions,
EqualizerActions
EqualizerActions,
DownloadsActions
),
dispatch
)
Expand Down
29 changes: 28 additions & 1 deletion app/reducers/downloads.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,45 @@
import _ from 'lodash';
import {
ADD_TO_DOWNLOADS
ADD_TO_DOWNLOADS,
DOWNLOAD_STARTED,
DOWNLOAD_PROGRESS,
DOWNLOAD_FINISHED,
DOWNLOAD_ERROR
} from '../actions/downloads';

const initialState = {
downloads: []
};

let newDownloads, changedItem;

export default function DownloadsReducer(state=initialState, action) {
switch (action.type) {
case ADD_TO_DOWNLOADS:
return Object.assign({}, state, {
downloads: _.concat(state.downloads, action.payload.item)
});
case DOWNLOAD_STARTED:
newDownloads = _.cloneDeep(state.downloads);
changedItem = _.find(newDownloads, item => item.track.uuid === action.payload.uuid);
_.set(changedItem, 'status', 'Started');
return Object.assign({}, state, {
downloads: newDownloads
});
case DOWNLOAD_PROGRESS:
newDownloads = _.cloneDeep(state.downloads);
changedItem = _.find(newDownloads, item => item.track.uuid === action.payload.uuid);
_.set(changedItem, 'completion', action.payload.progress);
return Object.assign({}, state, {
downloads: newDownloads
});
case DOWNLOAD_FINISHED:
newDownloads = _.cloneDeep(state.downloads);
changedItem = _.find(newDownloads, item => item.track.uuid === action.payload.uuid);
_.set(changedItem, 'status', 'Finished');
return Object.assign({}, state, {
downloads: newDownloads
});
default:
return state;
}
Expand Down
12 changes: 3 additions & 9 deletions app/rest/Youtube.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,14 @@ import globals from '../globals';
import ytlist from 'youtube-playlist';
import getArtistTitle from 'get-artist-title';

import { trackSearch } from './youtube-search';

const lastfm = new core.LastFmApi(
globals.lastfmApiKey,
globals.lastfmApiSecret
);

function prepareUrl (url) {
return `${url}&key=${globals.ytApiKey}`;
}

export function trackSearch (track) {
return fetch(prepareUrl('https://www.googleapis.com/youtube/v3/search?part=id,snippet&type=video&maxResults=50&q=' + encodeURIComponent(track)));
}
export { trackSearch };

function isValidURL (str) {
let pattern = new RegExp('^(https?:\\/\\/)' + // protocol
Expand Down Expand Up @@ -106,5 +102,3 @@ export function urlSearch (url) {
});
}
}


9 changes: 9 additions & 0 deletions app/rest/youtube-search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import globals from '../globals';

export function prepareUrl (url) {
return `${url}&key=${globals.ytApiKey}`;
}

export function trackSearch (track) {
return fetch(prepareUrl('https://www.googleapis.com/youtube/v3/search?part=id,snippet&type=video&maxResults=50&q=' + encodeURIComponent(track)));
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"babel-preset-stage-0": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"babel-register": "^6.26.0",
"bufferutil": "^4.0.1",
"chai": "^4.1.2",
"classnames": "^2.2.5",
"css-loader": "^1.0.0",
Expand Down Expand Up @@ -137,6 +138,7 @@
"style-loader": "^0.23.0",
"svg-inline-loader": "^0.8.0",
"url-loader": "^1.0.1",
"utf-8-validate": "^5.0.2",
"webpack": "^4.12.1",
"webpack-dev-server": "^3.1.4"
},
Expand Down
58 changes: 47 additions & 11 deletions server/downloads.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,55 @@
import electronDl from 'electron-dl';
import 'isomorphic-fetch';
import electronDl, { download } from 'electron-dl';
import logger from 'electron-timber';
import ytdl from 'ytdl-core';
import _ from 'lodash';
import { BrowserWindow, ipcMain } from 'electron';
import { ipcMain } from 'electron';

import { trackSearch } from '../app/rest/youtube-search';

export const registerDownloadsEvents = () => {
let rendererWindow = null;
electronDl();

export const registerDownloadsEvents = window => {
ipcMain.once('started', event => {
rendererWindow = event.sender;
});

ipcMain.on('start-download', (event, data) => {
const streamUrl = _.get(data, 'streams[0].stream');
electronDl();
logger.log(`Downloading from ${streamUrl}`);
const win = BrowserWindow.getFocusedWindow();
electronDl.download(win, streamUrl)
.then(result => {
logger.log(result);
const query = `${_.get(data, 'artist.name')} ${_.get(data, 'name')}`;
const filename = `${_.get(data, 'artist.name')} - ${_.get(data, 'name')}`;

// In order to get a valid stream URL, we need to generate it right before downloading
trackSearch(query)
.then(results => results.json())
.then(data => {
const trackId = _.get(_.head(data.items), 'id.videoId');
return ytdl.getInfo(`http://www.youtube.com/watch?v=${trackId}`);
})
.then(videoInfo => {
const formatInfo = _.head(videoInfo.formats.filter(e => e.itag === '140'));
const streamUrl = formatInfo.url;

return download(window, streamUrl, {
filename: filename,
onStarted: () => {
rendererWindow.send('download-started', data.uuid);
},
onProgress: progress => {
rendererWindow.send('download-progress', {
uuid: data.uuid,
progress
});
}
});
})
.then(() => {
rendererWindow.send('download-finished', data.uuid);
})
.catch(logger.error);
.catch(error => {
logger.error(error);
rendererWindow.send('download-error', { uui: data.uuid, error });
});

});
};
5 changes: 2 additions & 3 deletions server/main.dev.linux.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'babel-polyfill';
import logger from 'electron-timber';
import electronDl from 'electron-dl';
import platform from 'electron-platform';
import path from 'path';
import url from 'url';
Expand All @@ -17,7 +17,6 @@ const getOption = require('./store').getOption;
if (!platform.isDarwin && !platform.isWin32) {
// Player = require('mpris-service');
}
electronDl();

let win;
let httpServer;
Expand Down Expand Up @@ -93,7 +92,7 @@ function createWindow() {
tray.setToolTip('nuclear music player');
tray.setContextMenu(trayMenu);

registerDownloadsEvents();
registerDownloadsEvents(win);

ipcMain.on('close', () => {
logger.log('Received a close message from ipc, quitting');
Expand Down
2 changes: 1 addition & 1 deletion server/mpris.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ let rendererWindow = null;

// const events = ['raise', 'quit', 'next', 'previous', 'pause', 'playpause', 'stop', 'play', 'seek', 'position', 'open', 'volume', 'settings'];

ipcMain.on('started', event => {
ipcMain.once('started', event => {
logger.log('Renderer process started and registered.');
rendererWindow = event.sender;
});
Expand Down

0 comments on commit d7f0277

Please sign in to comment.