Compare commits

..

5 Commits

Author SHA1 Message Date
Zlatin Balevsky
c81f963e0a Release 0.1.4 2019-06-09 17:37:10 +01:00
Zlatin Balevsky
dc6b1199f3 implement resume across restart 2019-06-09 17:35:32 +01:00
Zlatin Balevsky
42621a2dfb wip on persisting downloads between restarts 2019-06-09 16:26:00 +01:00
Zlatin Balevsky
a7125963a7 DownloadManager listens to events, not FileManager 2019-06-09 16:19:35 +01:00
Zlatin Balevsky
f39d7f4fa8 emit an event when the UI loads 2019-06-09 15:44:06 +01:00
11 changed files with 101 additions and 18 deletions

View File

@@ -31,5 +31,4 @@ The first time you run MuWire it will ask you to select a nickname. This nickna
### Known bugs and limitations ### Known bugs and limitations
* Many UI features you would expect are not there yet * Many UI features you would expect are not there yet
* Downloads in progress do not get remembered between restarts

View File

@@ -34,7 +34,7 @@ class Cli {
Core core Core core
try { try {
core = new Core(props, home, "0.1.3") core = new Core(props, home, "0.1.4")
} catch (Exception bad) { } catch (Exception bad) {
bad.printStackTrace(System.out) bad.printStackTrace(System.out)
println "Failed to initialize core, exiting" println "Failed to initialize core, exiting"

View File

@@ -53,7 +53,7 @@ class CliDownloader {
Core core Core core
try { try {
core = new Core(props, home, "0.1.3") core = new Core(props, home, "0.1.4")
} catch (Exception bad) { } catch (Exception bad) {
bad.printStackTrace(System.out) bad.printStackTrace(System.out)
println "Failed to initialize core, exiting" println "Failed to initialize core, exiting"

View File

@@ -191,8 +191,10 @@ public class Core {
eventBus.register(ResultsEvent.class, searchManager) eventBus.register(ResultsEvent.class, searchManager)
log.info("initializing download manager") 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(UIDownloadEvent.class, downloadManager)
eventBus.register(UILoadedEvent.class, downloadManager)
eventBus.register(FileDownloadedEvent.class, downloadManager)
log.info("initializing upload manager") log.info("initializing upload manager")
UploadManager uploadManager = new UploadManager(eventBus, fileManager) UploadManager uploadManager = new UploadManager(eventBus, fileManager)
@@ -253,7 +255,7 @@ public class Core {
} }
} }
Core core = new Core(props, home, "0.1.3") Core core = new Core(props, home, "0.1.4")
core.startServices() core.startServices()
// ... at the end, sleep or execute script // ... at the end, sleep or execute script

View File

@@ -0,0 +1,4 @@
package com.muwire.core
class UILoadedEvent extends Event {
}

View File

@@ -1,12 +1,21 @@
package com.muwire.core.download package com.muwire.core.download
import com.muwire.core.connection.I2PConnector 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.Base64
import net.i2p.data.Destination import net.i2p.data.Destination
import net.i2p.util.ConcurrentHashSet
import com.muwire.core.EventBus import com.muwire.core.EventBus
import com.muwire.core.InfoHash
import com.muwire.core.Persona import com.muwire.core.Persona
import com.muwire.core.UILoadedEvent
import java.util.concurrent.Executor import java.util.concurrent.Executor
import java.util.concurrent.Executors import java.util.concurrent.Executors
@@ -16,13 +25,16 @@ public class DownloadManager {
private final EventBus eventBus private final EventBus eventBus
private final I2PConnector connector private final I2PConnector connector
private final Executor executor private final Executor executor
private final File incompletes private final File incompletes, home
private final Persona me private final Persona me
public DownloadManager(EventBus eventBus, I2PConnector connector, File incompletes, Persona me) { private final Set<Downloader> downloaders = new ConcurrentHashSet<>()
public DownloadManager(EventBus eventBus, I2PConnector connector, File home, Persona me) {
this.eventBus = eventBus this.eventBus = eventBus
this.connector = connector this.connector = connector
this.incompletes = incompletes this.incompletes = new File(home,"incompletes")
this.home = home
this.me = me this.me = me
incompletes.mkdir() incompletes.mkdir()
@@ -50,6 +62,8 @@ public class DownloadManager {
def downloader = new Downloader(eventBus, this, me, e.target, size, def downloader = new Downloader(eventBus, this, me, e.target, size,
infohash, pieceSize, connector, destinations, infohash, pieceSize, connector, destinations,
incompletes) incompletes)
downloaders.add(downloader)
persistDownloaders()
executor.execute({downloader.download()} as Runnable) executor.execute({downloader.download()} as Runnable)
eventBus.publish(new DownloadStartedEvent(downloader : downloader)) eventBus.publish(new DownloadStartedEvent(downloader : downloader))
} }
@@ -57,4 +71,53 @@ public class DownloadManager {
void resume(Downloader downloader) { void resume(Downloader downloader) {
executor.execute({downloader.download() as Runnable}) executor.execute({downloader.download() as Runnable})
} }
void onUILoadedEvent(UILoadedEvent e) {
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))
}
}
}
}
} }

View File

@@ -42,6 +42,7 @@ public class Downloader {
private final Set<Destination> destinations private final Set<Destination> destinations
private final int nPieces private final int nPieces
private final File piecesFile private final File piecesFile
final int pieceSizePow2
private final Map<Destination, DownloadWorker> activeWorkers = new ConcurrentHashMap<>() private final Map<Destination, DownloadWorker> activeWorkers = new ConcurrentHashMap<>()
@@ -61,6 +62,7 @@ public class Downloader {
this.connector = connector this.connector = connector
this.destinations = destinations this.destinations = destinations
this.piecesFile = new File(incompletes, file.getName()+".pieces") this.piecesFile = new File(incompletes, file.getName()+".pieces")
this.pieceSizePow2 = pieceSizePow2
this.pieceSize = 1 << pieceSizePow2 this.pieceSize = 1 << pieceSizePow2
int nPieces int nPieces
@@ -88,8 +90,8 @@ public class Downloader {
void readPieces() { void readPieces() {
if (!piecesFile.exists()) if (!piecesFile.exists())
return return
piecesFile.withReader { piecesFile.eachLine {
int piece = Integer.parseInt(it.readLine()) int piece = Integer.parseInt(it)
downloaded.markDownloaded(piece) downloaded.markDownloaded(piece)
} }
} }
@@ -163,11 +165,18 @@ public class Downloader {
} }
public void resume() { public void resume() {
activeWorkers.each { destination, worker -> destinations.each { destination ->
if (worker.currentState == WorkerState.FINISHED) { def worker = activeWorkers.get(destination)
def newWorker = new DownloadWorker(destination) if (worker != null) {
activeWorkers.put(destination, newWorker) if (worker.currentState == WorkerState.FINISHED) {
executorService.submit(newWorker) def newWorker = new DownloadWorker(destination)
activeWorkers.put(destination, newWorker)
executorService.submit(newWorker)
}
} else {
worker = new DownloadWorker(destination)
activeWorkers.put(destination, worker)
executorService.submit(worker)
} }
} }
} }
@@ -205,7 +214,8 @@ public class Downloader {
if (downloaded.isComplete() && !eventFired) { if (downloaded.isComplete() && !eventFired) {
piecesFile.delete() piecesFile.delete()
eventFired = true eventFired = true
eventBus.publish(new FileDownloadedEvent(downloadedFile : new DownloadedFile(file, infoHash, pieceSize, Collections.emptySet()))) eventBus.publish(new FileDownloadedEvent(downloadedFile : new DownloadedFile(file, infoHash, pieceSize, Collections.emptySet())),
downloader : Downloader.this)
} }
endpoint?.close() endpoint?.close()
} }

View File

@@ -2,10 +2,11 @@ package com.muwire.core.files
import com.muwire.core.DownloadedFile import com.muwire.core.DownloadedFile
import com.muwire.core.Event import com.muwire.core.Event
import com.muwire.core.download.Downloader
import net.i2p.data.Destination import net.i2p.data.Destination
class FileDownloadedEvent extends Event { class FileDownloadedEvent extends Event {
Downloader downloader
DownloadedFile downloadedFile DownloadedFile downloadedFile
} }

View File

@@ -4,6 +4,7 @@ import com.muwire.core.EventBus
import com.muwire.core.InfoHash import com.muwire.core.InfoHash
import com.muwire.core.MuWireSettings import com.muwire.core.MuWireSettings
import com.muwire.core.SharedFile import com.muwire.core.SharedFile
import com.muwire.core.UILoadedEvent
import com.muwire.core.search.ResultsEvent import com.muwire.core.search.ResultsEvent
import com.muwire.core.search.SearchEvent import com.muwire.core.search.SearchEvent
import com.muwire.core.search.SearchIndex import com.muwire.core.search.SearchIndex

View File

@@ -1,5 +1,5 @@
group = com.muwire group = com.muwire
version = 0.1.3 version = 0.1.4
groovyVersion = 2.4.15 groovyVersion = 2.4.15
slf4jVersion = 1.7.25 slf4jVersion = 1.7.25
spockVersion = 1.1-groovy-2.4 spockVersion = 1.1-groovy-2.4

View File

@@ -7,6 +7,7 @@ import org.codehaus.griffon.runtime.core.AbstractLifecycleHandler
import com.muwire.core.Core import com.muwire.core.Core
import com.muwire.core.MuWireSettings import com.muwire.core.MuWireSettings
import com.muwire.core.UILoadedEvent
import com.muwire.core.files.FileSharedEvent import com.muwire.core.files.FileSharedEvent
import javax.annotation.Nonnull import javax.annotation.Nonnull
@@ -117,6 +118,8 @@ class Ready extends AbstractLifecycleHandler {
core.eventBus.publish(new FileSharedEvent(file : new File(it))) core.eventBus.publish(new FileSharedEvent(file : new File(it)))
} }
} }
core.eventBus.publish(new UILoadedEvent())
} }
private static String selectHome() { private static String selectHome() {