Skip to content

Commit

Permalink
Improved downloading. Limit concurrent downloads and queue download j…
Browse files Browse the repository at this point in the history
…ob (#5)
  • Loading branch information
erikssource committed Dec 25, 2018
1 parent 32fe60b commit 9057309
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 2 deletions.
44 changes: 44 additions & 0 deletions src/renderer/common/downloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,28 @@ import path from 'path';

import config from '../store/modules/config';
import utils from './utils';
import { Task } from './task';
import { ProcessManager } from './process';

let downloadProcessor = new ProcessManager(config.state.concurrentDownloads);

let doDownload = function(payload) {
return new Promise((resolve, reject) => {
let {fileName, episode, progressCallback} = payload;

progress(request(episode.url))
.on('progress', (state) => {
progressCallback(Math.round(100 * state.percent));
})
.on('error', (err) => {
reject(err);
})
.on('end', () => {
resolve(fileName);
})
.pipe(fs.createWriteStream(fileName));
});
};

export default {
downloadEpisode(feed, episode, progressCallback, completeCallback) {
Expand All @@ -14,6 +36,28 @@ export default {
name: filenamify(episode.title),
ext: utils.mimeToExt(episode.mimetype)
});
let task = new Task(
doDownload,
{
fileName: fileName,
episode: episode,
progressCallback: progressCallback
},
(err) => console.error("Download Error: ", err),
(result) => {
console.log("Download Finished: ", result);
completeCallback(result);
},
() => console.log("Download Started: ", episode.title)
);
downloadProcessor.addTask(task);
},
oldDownloadEpisode(feed, episode, progressCallback, completeCallback) {
let fileName = path.format({
dir: this.getFeedPath(feed),
name: filenamify(episode.title),
ext: utils.mimeToExt(episode.mimetype)
});

progress(request(episode.url))
.on('progress', (state) => {
Expand Down
154 changes: 154 additions & 0 deletions src/renderer/common/linkedlist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@


class Node {
constructor(payload) {
this._next = null;
this._previous = null;
this._payload = payload;
}

setPrevious(previous) {
this._previous = previous;
}

setNext(next) {
this._next = next;
}

getNext() {
return this._next;
}

getPrevious() {
return this._previous;
}

getPayload() {
return this._payload;
}
}

export class LinkedList {

constructor() {
this._head = null;
this._tail = null;
this._size = 0;
}

add(payload) {
let node = new Node(payload);
if (this._head === null) {
this._head = node;
}
if (this._tail === null) {
this._tail = node;
}
else {
node.setPrevious(this._tail);
this._tail.setNext(node);
this._tail = node;
}
this._size++;
return payload;
}

addFirst(payload) {
let node = new Node(payload);
if (this._tail === null) {
this._tail = node;
}
if (this._head === null) {
this._head = node;
}
else {
node.setNext(this._tail);
this._head.setPrevious(node);
this._head = node;
}
this._size++;
return payload;
}

size() {
return this._size;
}

peekFirst() {
return this._head === null ? undefined : this._head.getPayload();
}

peekLast() {
return this._tail === null ? undefined : this._tail.getPayload();
}

poll() {
return this.remove();
}

forEach(cb) {
let node = this._head;
while (node !== null) {
cb(node.getPayload());
node = node.getNext();
}
}

forEachReverse(cb) {
let node = this._tail;
while (node !== null) {
cb(node.getPayload());
node = node.getPrevious();
}
}

toArray() {
let items = [];
this.forEach((payload) => items.push(payload));
return items;
}

remove() {
if (this._head === null) {
return undefined;
}
let payload = this._head.getPayload();
this._head = this._head.getNext();
if (this._head === null) {
this._tail = null;
}
else {
this._head.setPrevious(null);
}
this._size--;
return payload;
}

removeLast() {
if (this._tail === null) {
return undefined;
}
let payload = this._tail.getPayload();
this._tail = this._tail.getPrevious();
if (this._tail === null) {
this._tail = null;
}
else {
this._tail.setNext(null);
}
this._size--;
return payload;
}

push(payload) {
return this.add(payload);
}

pop() {
return this.removeLast();
}

peek() {
return this.peekLast();
}
}
30 changes: 30 additions & 0 deletions src/renderer/common/process.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { LinkedList } from './linkedlist';

export class ProcessManager {

constructor(concurrency) {
this._queue = new LinkedList();
this._concurrency = concurrency;
this._task_count = 0;
}

addTask(task) {
task._setHook(this._completeHook.bind(this));
this._queue.add(task);
this._executeTasks();
}

_completeHook() {
this._task_count--;
this._executeTasks();
}

_executeTasks() {
while (this._queue.size() > 0 && (this._task_count < this._concurrency)) {
let task = this._queue.poll();
this._task_count++;
task.start();
}
}

}
34 changes: 34 additions & 0 deletions src/renderer/common/task.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

export class Task {
constructor(task, payload, err, complete, start) {
this._task = task;
this._payload = payload;
this._err = err;
this._complete = complete;
this._start = start;
this._hook = null;
}

_setHook(hook) {
this._hook = hook;
}

start() {
if (this._start !== null) {
this._start();
}
this._task(this._payload).then((result) => {
if (this._hook !== null) {
this._hook();
}
this._complete(result);
},
(err) => {
if (this._hook !== null) {
this._hook();
}
this._err(err);
});
}
}

15 changes: 14 additions & 1 deletion src/renderer/components/EpisodeList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@
<span class="text-primary" v-if="data.item.bookmark > 0"><i class="fas fa-book"></i></span>
<span class="text-primary" v-if="data.item.filename"><i class="fas fa-arrow-alt-circle-down"></i></span>
</div>
<div v-if="isDownloading(data.item)"><b-progress :value="downloadProgress(data.item)" :max="100"></b-progress></div>
<div v-if="isDownloading(data.item)">
<b-progress :max="100">
<b-progress-bar :value="downloadProgress(data.item)">
<strong>{{
(downloadProgress(data.item) === 0) ? "Waiting..." : (downloadProgress(data.item).toString() + '%')
}}</strong>
</b-progress-bar>
</b-progress>
</div>
</template>
<template slot="published" slot-scope="data">
{{ formatDate(data.item.published) }}
Expand All @@ -26,6 +34,7 @@
<template slot="action" slot-scope="data">
<button type="button" v-b-tooltip.hover title="Play Podcast" class="btn btn-success btn-sm" v-on:click="playepisode(data.item)"><i class="fas fa-play"></i></button>
<button v-if="data.item.filename" v-b-tooltip.hover title="Delete Download" type="button" class="btn btn-danger btn-sm" @click="deleteDownload(data.item)"><i class="fas fa-file-excel"></i></button>
<button v-else-if="isDownloading(data.item)" class="btn btn-secondary btn-sm" disabled><i class="fas fa-download"></i></button>
<button v-else type="button" v-b-tooltip.hover title="Download" class="btn btn-primary btn-sm" v-on:click="download(data.item)"><i class="fas fa-download"></i></button>
</template>

Expand Down Expand Up @@ -95,4 +104,8 @@
overflow-x: hidden;
overflow-y: auto;
}
.progress {
background-color: #b9bcbf;
}
</style>
3 changes: 2 additions & 1 deletion src/renderer/store/modules/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { remote } from 'electron';
import path from 'path';

const state = {
poddir: path.join(remote.app.getPath('home'), 'podpup')
poddir: path.join(remote.app.getPath('home'), 'podpup'),
concurrentDownloads: 3
};

export default {
Expand Down

0 comments on commit 9057309

Please sign in to comment.