diff --git a/README.md b/README.md index d9d8e5c6..e0eb5745 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/core/src/main/groovy/com/muwire/core/Core.groovy b/core/src/main/groovy/com/muwire/core/Core.groovy index f61f4ef2..8f4297a1 100644 --- a/core/src/main/groovy/com/muwire/core/Core.groovy +++ b/core/src/main/groovy/com/muwire/core/Core.groovy @@ -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) diff --git a/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy b/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy index 3de8d17e..c91dcbe0 100644 --- a/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy +++ b/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy @@ -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 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)) + } + } + } } } diff --git a/core/src/main/groovy/com/muwire/core/download/Downloader.groovy b/core/src/main/groovy/com/muwire/core/download/Downloader.groovy index dcfc9283..a2fc8730 100644 --- a/core/src/main/groovy/com/muwire/core/download/Downloader.groovy +++ b/core/src/main/groovy/com/muwire/core/download/Downloader.groovy @@ -42,6 +42,7 @@ public class Downloader { private final Set destinations private final int nPieces private final File piecesFile + final int pieceSizePow2 private final Map 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) } } }