implement resume across restart
This commit is contained in:
@ -31,5 +31,4 @@ The first time you run MuWire it will ask you to select a nickname. This nickna
|
||||
### Known bugs and limitations
|
||||
|
||||
* Many UI features you would expect are not there yet
|
||||
* Downloads in progress do not get remembered between restarts
|
||||
|
||||
|
@ -191,7 +191,7 @@ public class Core {
|
||||
eventBus.register(ResultsEvent.class, searchManager)
|
||||
|
||||
log.info("initializing download manager")
|
||||
DownloadManager downloadManager = new DownloadManager(eventBus, i2pConnector, new File(home, "incompletes"), me)
|
||||
DownloadManager downloadManager = new DownloadManager(eventBus, i2pConnector, home, me)
|
||||
eventBus.register(UIDownloadEvent.class, downloadManager)
|
||||
eventBus.register(UILoadedEvent.class, downloadManager)
|
||||
eventBus.register(FileDownloadedEvent.class, downloadManager)
|
||||
|
@ -2,12 +2,18 @@ package com.muwire.core.download
|
||||
|
||||
import com.muwire.core.connection.I2PConnector
|
||||
import com.muwire.core.files.FileDownloadedEvent
|
||||
import com.muwire.core.files.FileHasher
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import groovy.json.JsonBuilder
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
import net.i2p.data.Base64
|
||||
import net.i2p.data.Destination
|
||||
import net.i2p.util.ConcurrentHashSet
|
||||
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.InfoHash
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.UILoadedEvent
|
||||
|
||||
@ -19,15 +25,16 @@ public class DownloadManager {
|
||||
private final EventBus eventBus
|
||||
private final I2PConnector connector
|
||||
private final Executor executor
|
||||
private final File incompletes
|
||||
private final File incompletes, home
|
||||
private final Persona me
|
||||
|
||||
private final Set<Downloader> downloaders = new ConcurrentHashSet<>()
|
||||
|
||||
public DownloadManager(EventBus eventBus, I2PConnector connector, File incompletes, Persona me) {
|
||||
public DownloadManager(EventBus eventBus, I2PConnector connector, File home, Persona me) {
|
||||
this.eventBus = eventBus
|
||||
this.connector = connector
|
||||
this.incompletes = incompletes
|
||||
this.incompletes = new File(home,"incompletes")
|
||||
this.home = home
|
||||
this.me = me
|
||||
|
||||
incompletes.mkdir()
|
||||
@ -66,14 +73,51 @@ public class DownloadManager {
|
||||
}
|
||||
|
||||
void onUILoadedEvent(UILoadedEvent e) {
|
||||
// TODO: load downloads here
|
||||
File downloadsFile = new File(home, "downloads.json")
|
||||
if (!downloadsFile.exists())
|
||||
return
|
||||
def slurper = new JsonSlurper()
|
||||
downloadsFile.eachLine {
|
||||
def json = slurper.parseText(it)
|
||||
File file = new File(DataUtil.readi18nString(Base64.decode(json.file)))
|
||||
def destinations = new HashSet<>()
|
||||
json.destinations.each { destination ->
|
||||
destinations.add new Destination(destination)
|
||||
}
|
||||
byte[] hashList = Base64.decode(json.hashList)
|
||||
InfoHash infoHash = InfoHash.fromHashList(hashList)
|
||||
def downloader = new Downloader(eventBus, this, me, file, (long)json.length,
|
||||
infoHash, json.pieceSizePow2, connector, destinations, incompletes)
|
||||
downloader.download()
|
||||
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
|
||||
}
|
||||
}
|
||||
|
||||
void onFileDownloadedEvent(FileDownloadedEvent e) {
|
||||
downloaders.remove(e.downloader)
|
||||
persistDownloaders()
|
||||
}
|
||||
|
||||
private void persistDownloaders() {
|
||||
|
||||
File downloadsFile = new File(home,"downloads.json")
|
||||
downloadsFile.withPrintWriter { writer ->
|
||||
downloaders.each { downloader ->
|
||||
if (!downloader.cancelled) {
|
||||
def json = [:]
|
||||
json.file = Base64.encode(DataUtil.encodei18nString(downloader.file.getAbsolutePath()))
|
||||
json.length = downloader.length
|
||||
json.pieceSizePow2 = downloader.pieceSizePow2
|
||||
def destinations = []
|
||||
downloader.destinations.each {
|
||||
destinations << it.toBase64()
|
||||
}
|
||||
json.destinations = destinations
|
||||
|
||||
json.hashList = Base64.encode(downloader.infoHash.hashList)
|
||||
|
||||
writer.println(JsonOutput.toJson(json))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ public class Downloader {
|
||||
private final Set<Destination> destinations
|
||||
private final int nPieces
|
||||
private final File piecesFile
|
||||
final int pieceSizePow2
|
||||
private final Map<Destination, DownloadWorker> activeWorkers = new ConcurrentHashMap<>()
|
||||
|
||||
|
||||
@ -61,6 +62,7 @@ public class Downloader {
|
||||
this.connector = connector
|
||||
this.destinations = destinations
|
||||
this.piecesFile = new File(incompletes, file.getName()+".pieces")
|
||||
this.pieceSizePow2 = pieceSizePow2
|
||||
this.pieceSize = 1 << pieceSizePow2
|
||||
|
||||
int nPieces
|
||||
@ -88,8 +90,8 @@ public class Downloader {
|
||||
void readPieces() {
|
||||
if (!piecesFile.exists())
|
||||
return
|
||||
piecesFile.withReader {
|
||||
int piece = Integer.parseInt(it.readLine())
|
||||
piecesFile.eachLine {
|
||||
int piece = Integer.parseInt(it)
|
||||
downloaded.markDownloaded(piece)
|
||||
}
|
||||
}
|
||||
@ -163,11 +165,18 @@ public class Downloader {
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
activeWorkers.each { destination, worker ->
|
||||
if (worker.currentState == WorkerState.FINISHED) {
|
||||
def newWorker = new DownloadWorker(destination)
|
||||
activeWorkers.put(destination, newWorker)
|
||||
executorService.submit(newWorker)
|
||||
destinations.each { destination ->
|
||||
def worker = activeWorkers.get(destination)
|
||||
if (worker != null) {
|
||||
if (worker.currentState == WorkerState.FINISHED) {
|
||||
def newWorker = new DownloadWorker(destination)
|
||||
activeWorkers.put(destination, newWorker)
|
||||
executorService.submit(newWorker)
|
||||
}
|
||||
} else {
|
||||
worker = new DownloadWorker(destination)
|
||||
activeWorkers.put(destination, worker)
|
||||
executorService.submit(worker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user