Compare commits

..

7 Commits

Author SHA1 Message Date
Zlatin Balevsky
bd21cf65ea Release 0.1.5 2019-06-09 20:37:39 +01:00
Zlatin Balevsky
dea592eb27 do not resume cancelled downloads on restart 2019-06-09 20:36:14 +01:00
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
13 changed files with 117 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
* 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
try {
core = new Core(props, home, "0.1.3")
core = new Core(props, home, "0.1.5")
} catch (Exception bad) {
bad.printStackTrace(System.out)
println "Failed to initialize core, exiting"

View File

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

View File

@@ -12,6 +12,7 @@ import com.muwire.core.connection.I2PConnector
import com.muwire.core.connection.LeafConnectionManager
import com.muwire.core.connection.UltrapeerConnectionManager
import com.muwire.core.download.DownloadManager
import com.muwire.core.download.UIDownloadCancelledEvent
import com.muwire.core.download.UIDownloadEvent
import com.muwire.core.files.FileDownloadedEvent
import com.muwire.core.files.FileHashedEvent
@@ -191,8 +192,11 @@ 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)
eventBus.register(UIDownloadCancelledEvent.class, downloadManager)
log.info("initializing upload manager")
UploadManager uploadManager = new UploadManager(eventBus, fileManager)
@@ -253,7 +257,7 @@ public class Core {
}
}
Core core = new Core(props, home, "0.1.3")
Core core = new Core(props, home, "0.1.5")
core.startServices()
// ... 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
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
import java.util.concurrent.Executor
import java.util.concurrent.Executors
@@ -16,13 +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
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.connector = connector
this.incompletes = incompletes
this.incompletes = new File(home,"incompletes")
this.home = home
this.me = me
incompletes.mkdir()
@@ -50,11 +62,67 @@ public class DownloadManager {
def downloader = new Downloader(eventBus, this, me, e.target, size,
infohash, pieceSize, connector, destinations,
incompletes)
downloaders.add(downloader)
persistDownloaders()
executor.execute({downloader.download()} as Runnable)
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
}
public void onUIDownloadCancelledEvent(UIDownloadCancelledEvent e) {
downloaders.remove(e.downloader)
persistDownloaders()
}
void resume(Downloader downloader) {
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 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)
}
}
}
@@ -205,7 +214,8 @@ public class Downloader {
if (downloaded.isComplete() && !eventFired) {
piecesFile.delete()
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()
}

View File

@@ -0,0 +1,7 @@
package com.muwire.core.download
import com.muwire.core.Event
class UIDownloadCancelledEvent extends Event {
Downloader downloader
}

View File

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

View File

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

View File

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

View File

@@ -15,6 +15,7 @@ import javax.inject.Inject
import com.muwire.core.Constants
import com.muwire.core.Core
import com.muwire.core.download.DownloadStartedEvent
import com.muwire.core.download.UIDownloadCancelledEvent
import com.muwire.core.download.UIDownloadEvent
import com.muwire.core.search.QueryEvent
import com.muwire.core.search.SearchEvent
@@ -133,6 +134,7 @@ class MainFrameController {
void cancel() {
def downloader = model.downloads[selectedDownload()].downloader
downloader.cancel()
core.eventBus.publish(new UIDownloadCancelledEvent(downloader : downloader))
}
@ControllerAction

View File

@@ -7,6 +7,7 @@ import org.codehaus.griffon.runtime.core.AbstractLifecycleHandler
import com.muwire.core.Core
import com.muwire.core.MuWireSettings
import com.muwire.core.UILoadedEvent
import com.muwire.core.files.FileSharedEvent
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 UILoadedEvent())
}
private static String selectHome() {