Compare commits
31 Commits
muwire-0.3
...
fix-piece-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e829e09b13 | ||
![]() |
120980100b | ||
![]() |
e64ed2c89d | ||
![]() |
81436aaa66 | ||
![]() |
3bb530e34f | ||
![]() |
51425bbfd9 | ||
![]() |
6a4879bc0b | ||
![]() |
e7fe56439b | ||
![]() |
2886feab4a | ||
![]() |
fb91194026 | ||
![]() |
4527478b0d | ||
![]() |
b0062f146e | ||
![]() |
bf16561170 | ||
![]() |
3b23dc29c4 | ||
![]() |
c0645b670e | ||
![]() |
30613fe530 | ||
![]() |
e7822f6edc | ||
![]() |
7e5c9ba115 | ||
![]() |
647fa3a481 | ||
![]() |
538eca9297 | ||
![]() |
e73a23d4a4 | ||
![]() |
76e41a0383 | ||
![]() |
7045927666 | ||
![]() |
5fb3086b42 | ||
![]() |
2de18227c1 | ||
![]() |
bd12a1de3d | ||
![]() |
a3a91050c8 | ||
![]() |
6c1cc28e49 | ||
![]() |
b6e5b54f05 | ||
![]() |
a6e559ec67 | ||
![]() |
f11badb824 |
13
README.md
13
README.md
@@ -37,8 +37,8 @@ The first time you run MuWire it will ask you to select a nickname. This nickna
|
||||
* why is MuWire slow ?
|
||||
|
||||
- too few sources you're downloading from
|
||||
- you can increse the number of tunnels by using more tunnels via Options->I2P Inbound/Outbound Quantity
|
||||
the default is 4 and you could raise it and even can go up as high as 16 ( Caution !!!!)
|
||||
- you can increase the number of tunnels by using more tunnels via Options->I2P Inbound/Outbound Quantity
|
||||
the default is 4 and you could raise up to as high as 16 ( Caution !!!!)
|
||||
|
||||
* my search is not returning (enough) results !
|
||||
|
||||
@@ -46,4 +46,11 @@ The first time you run MuWire it will ask you to select a nickname. This nickna
|
||||
- keywords and hash(es) are NOT regexed or wildcarded so they have to be complete
|
||||
so searching for 'musi' will not return results with 'music' - you have to search for 'music'
|
||||
- ALL keywords have to match
|
||||
- only use <SPACE> for keyword separation
|
||||
- only use space for keyword separation
|
||||
- if you already have the file in question it is not displayed ( can be changed via Options )
|
||||
|
||||
* what's this right click -> 'Copy hash to clipboard' for ?
|
||||
|
||||
- if you have a specific file you wish to share or download you can use the hash as a unique identifier
|
||||
to make sure you have exactly the right file.
|
||||
- you can share this hash with others to ensure they are getting the right file
|
||||
|
5
TODO.md
5
TODO.md
@@ -4,10 +4,6 @@ Not in any particular order yet
|
||||
|
||||
### Big Items
|
||||
|
||||
##### Alternate Locations
|
||||
|
||||
This helps peers discover new sources for a file while the download is in progress. Also makes sharing of partial files possible.
|
||||
|
||||
##### Bloom Filters
|
||||
|
||||
This reduces query traffic by not sending last hop queries to peers that definitely do not have the file
|
||||
@@ -38,7 +34,6 @@ To enable parsing of metadata from known file types and the user editing it or a
|
||||
|
||||
### Small Items
|
||||
|
||||
* Detect if router is dead and show warning or exit
|
||||
* Wrapper of some kind for in-place upgrades
|
||||
* Download file sequentially
|
||||
* Unsharing of files
|
||||
|
@@ -35,7 +35,7 @@ class Cli {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.3.5")
|
||||
core = new Core(props, home, "0.3.8")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
|
@@ -53,7 +53,7 @@ class CliDownloader {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.3.5")
|
||||
core = new Core(props, home, "0.3.8")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
|
@@ -16,6 +16,8 @@ import com.muwire.core.download.DownloadManager
|
||||
import com.muwire.core.download.SourceDiscoveredEvent
|
||||
import com.muwire.core.download.UIDownloadCancelledEvent
|
||||
import com.muwire.core.download.UIDownloadEvent
|
||||
import com.muwire.core.download.UIDownloadPausedEvent
|
||||
import com.muwire.core.download.UIDownloadResumedEvent
|
||||
import com.muwire.core.files.FileDownloadedEvent
|
||||
import com.muwire.core.files.FileHashedEvent
|
||||
import com.muwire.core.files.FileHasher
|
||||
@@ -48,6 +50,7 @@ import net.i2p.client.I2PSession
|
||||
import net.i2p.client.streaming.I2PSocketManager
|
||||
import net.i2p.client.streaming.I2PSocketManagerFactory
|
||||
import net.i2p.client.streaming.I2PSocketOptions
|
||||
import net.i2p.client.streaming.I2PSocketManager.DisconnectListener
|
||||
import net.i2p.crypto.DSAEngine
|
||||
import net.i2p.crypto.SigType
|
||||
import net.i2p.data.Destination
|
||||
@@ -124,6 +127,7 @@ public class Core {
|
||||
}
|
||||
socketManager.getDefaultOptions().setReadTimeout(60000)
|
||||
socketManager.getDefaultOptions().setConnectTimeout(30000)
|
||||
socketManager.addDisconnectListener({eventBus.publish(new RouterDisconnectedEvent())} as DisconnectListener)
|
||||
i2pSession = socketManager.getSession()
|
||||
|
||||
def destination = new Destination()
|
||||
@@ -169,7 +173,7 @@ public class Core {
|
||||
eventBus.register(SearchEvent.class, fileManager)
|
||||
|
||||
log.info("initializing mesh manager")
|
||||
MeshManager meshManager = new MeshManager(fileManager)
|
||||
MeshManager meshManager = new MeshManager(fileManager, home)
|
||||
eventBus.register(SourceDiscoveredEvent.class, meshManager)
|
||||
|
||||
log.info "initializing persistence service"
|
||||
@@ -215,6 +219,8 @@ public class Core {
|
||||
eventBus.register(FileDownloadedEvent.class, downloadManager)
|
||||
eventBus.register(UIDownloadCancelledEvent.class, downloadManager)
|
||||
eventBus.register(SourceDiscoveredEvent.class, downloadManager)
|
||||
eventBus.register(UIDownloadPausedEvent.class, downloadManager)
|
||||
eventBus.register(UIDownloadResumedEvent.class, downloadManager)
|
||||
|
||||
log.info("initializing upload manager")
|
||||
UploadManager uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager)
|
||||
@@ -291,7 +297,7 @@ public class Core {
|
||||
}
|
||||
}
|
||||
|
||||
Core core = new Core(props, home, "0.3.5")
|
||||
Core core = new Core(props, home, "0.3.8")
|
||||
core.startServices()
|
||||
|
||||
// ... at the end, sleep or execute script
|
||||
|
@@ -30,8 +30,8 @@ class MuWireSettings {
|
||||
nickname = props.getProperty("nickname","MuWireUser")
|
||||
downloadLocation = new File((String)props.getProperty("downloadLocation",
|
||||
System.getProperty("user.home")))
|
||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","5"))
|
||||
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","36"))
|
||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","1"))
|
||||
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","24"))
|
||||
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
|
||||
|
||||
watchedDirectories = new HashSet<>()
|
||||
|
@@ -0,0 +1,4 @@
|
||||
package com.muwire.core
|
||||
|
||||
class RouterDisconnectedEvent extends Event {
|
||||
}
|
@@ -90,6 +90,14 @@ public class DownloadManager {
|
||||
persistDownloaders()
|
||||
}
|
||||
|
||||
public void onUIDownloadPausedEvent(UIDownloadPausedEvent e) {
|
||||
persistDownloaders()
|
||||
}
|
||||
|
||||
public void onUIDownloadResumedEvent(UIDownloadResumedEvent e) {
|
||||
persistDownloaders()
|
||||
}
|
||||
|
||||
void resume(Downloader downloader) {
|
||||
executor.execute({downloader.download() as Runnable})
|
||||
}
|
||||
@@ -119,7 +127,11 @@ public class DownloadManager {
|
||||
|
||||
def downloader = new Downloader(eventBus, this, me, file, (long)json.length,
|
||||
infoHash, json.pieceSizePow2, connector, destinations, incompletes, pieces)
|
||||
if (json.paused != null)
|
||||
downloader.paused = json.paused
|
||||
downloaders.put(infoHash, downloader)
|
||||
downloader.readPieces()
|
||||
if (!downloader.paused)
|
||||
downloader.download()
|
||||
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
|
||||
}
|
||||
@@ -174,6 +186,8 @@ public class DownloadManager {
|
||||
json.hashList = Base64.encode(infoHash.hashList)
|
||||
else
|
||||
json.hashRoot = Base64.encode(infoHash.getRoot())
|
||||
|
||||
json.paused = downloader.paused
|
||||
writer.println(JsonOutput.toJson(json))
|
||||
}
|
||||
}
|
||||
|
@@ -75,7 +75,7 @@ class DownloadSession {
|
||||
if (available.isEmpty())
|
||||
piece = pieces.claim()
|
||||
else
|
||||
piece = pieces.claim(available)
|
||||
piece = pieces.claim(new HashSet<>(available))
|
||||
if (piece == -1)
|
||||
return false
|
||||
boolean unclaim = true
|
||||
|
@@ -18,6 +18,7 @@ import com.muwire.core.DownloadedFile
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.connection.I2PConnector
|
||||
import com.muwire.core.files.FileDownloadedEvent
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import groovy.util.logging.Log
|
||||
import net.i2p.data.Destination
|
||||
@@ -25,7 +26,7 @@ import net.i2p.util.ConcurrentHashSet
|
||||
|
||||
@Log
|
||||
public class Downloader {
|
||||
public enum DownloadState { CONNECTING, HASHLIST, DOWNLOADING, FAILED, CANCELLED, FINISHED }
|
||||
public enum DownloadState { CONNECTING, HASHLIST, DOWNLOADING, FAILED, CANCELLED, PAUSED, FINISHED }
|
||||
private enum WorkerState { CONNECTING, HASHLIST, DOWNLOADING, FINISHED}
|
||||
|
||||
private static final ExecutorService executorService = Executors.newCachedThreadPool({r ->
|
||||
@@ -53,7 +54,7 @@ public class Downloader {
|
||||
private final Set<Destination> successfulDestinations = new ConcurrentHashSet<>()
|
||||
|
||||
|
||||
private volatile boolean cancelled
|
||||
private volatile boolean cancelled, paused
|
||||
private final AtomicBoolean eventFired = new AtomicBoolean()
|
||||
private boolean piecesFileClosed
|
||||
|
||||
@@ -136,6 +137,9 @@ public class Downloader {
|
||||
public DownloadState getCurrentState() {
|
||||
if (cancelled)
|
||||
return DownloadState.CANCELLED
|
||||
if (paused)
|
||||
return DownloadState.PAUSED
|
||||
|
||||
boolean allFinished = true
|
||||
activeWorkers.values().each {
|
||||
allFinished &= it.currentState == WorkerState.FINISHED
|
||||
@@ -183,6 +187,11 @@ public class Downloader {
|
||||
pieces.clearAll()
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
paused = true
|
||||
stop()
|
||||
}
|
||||
|
||||
void stop() {
|
||||
activeWorkers.values().each {
|
||||
it.cancel()
|
||||
@@ -199,6 +208,8 @@ public class Downloader {
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
paused = false
|
||||
readPieces()
|
||||
destinations.each { destination ->
|
||||
def worker = activeWorkers.get(destination)
|
||||
if (worker != null) {
|
||||
@@ -259,7 +270,7 @@ public class Downloader {
|
||||
writePieces()
|
||||
}
|
||||
} catch (Exception bad) {
|
||||
log.log(Level.WARNING,"Exception while downloading",bad)
|
||||
log.log(Level.WARNING,"Exception while downloading",DataUtil.findRoot(bad))
|
||||
} finally {
|
||||
currentState = WorkerState.FINISHED
|
||||
if (pieces.isComplete() && eventFired.compareAndSet(false, true)) {
|
||||
|
@@ -0,0 +1,6 @@
|
||||
package com.muwire.core.download
|
||||
|
||||
import com.muwire.core.Event
|
||||
|
||||
class UIDownloadPausedEvent extends Event {
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
package com.muwire.core.download
|
||||
|
||||
import com.muwire.core.Event
|
||||
|
||||
class UIDownloadResumedEvent extends Event {
|
||||
}
|
@@ -1,18 +1,34 @@
|
||||
package com.muwire.core.mesh
|
||||
|
||||
import java.util.logging.Level
|
||||
import java.util.stream.Collectors
|
||||
|
||||
import com.muwire.core.Constants
|
||||
import com.muwire.core.InfoHash
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.download.Pieces
|
||||
import com.muwire.core.download.SourceDiscoveredEvent
|
||||
import com.muwire.core.files.FileManager
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.util.logging.Log
|
||||
import net.i2p.data.Base64
|
||||
|
||||
@Log
|
||||
class MeshManager {
|
||||
|
||||
private static final int EXPIRATION = 60 * 60 * 1000
|
||||
|
||||
private final Map<InfoHash, Mesh> meshes = Collections.synchronizedMap(new HashMap<>())
|
||||
private final FileManager fileManager
|
||||
private final File home
|
||||
|
||||
MeshManager(FileManager fileManager) {
|
||||
MeshManager(FileManager fileManager, File home) {
|
||||
this.fileManager = fileManager
|
||||
this.home = home
|
||||
load()
|
||||
}
|
||||
|
||||
Mesh get(InfoHash infoHash) {
|
||||
@@ -23,6 +39,7 @@ class MeshManager {
|
||||
synchronized(meshes) {
|
||||
if (meshes.containsKey(infoHash))
|
||||
return meshes.get(infoHash)
|
||||
log.log(Level.INFO,"creating mesh with pieces:$nPieces", new Exception())
|
||||
Pieces pieces = new Pieces(nPieces, Constants.DOWNLOAD_SEQUENTIAL_RATIO)
|
||||
if (fileManager.rootToFiles.containsKey(infoHash)) {
|
||||
for (int i = 0; i < nPieces; i++)
|
||||
@@ -39,5 +56,50 @@ class MeshManager {
|
||||
if (mesh == null)
|
||||
return
|
||||
mesh.sources.add(e.source)
|
||||
save()
|
||||
}
|
||||
|
||||
private void save() {
|
||||
File meshFile = new File(home, "mesh.json")
|
||||
synchronized(meshes) {
|
||||
meshFile.withPrintWriter { writer ->
|
||||
meshes.values().each { mesh ->
|
||||
def json = [:]
|
||||
json.timestamp = System.currentTimeMillis()
|
||||
json.infoHash = Base64.encode(mesh.infoHash.getRoot())
|
||||
json.sources = mesh.sources.stream().map({it.toBase64()}).collect(Collectors.toList())
|
||||
json.nPieces = mesh.pieces.nPieces
|
||||
json.xHave = DataUtil.encodeXHave(mesh.pieces.downloaded, mesh.pieces.nPieces)
|
||||
writer.println(JsonOutput.toJson(json))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void load() {
|
||||
File meshFile = new File(home, "mesh.json")
|
||||
if (!meshFile.exists())
|
||||
return
|
||||
long now = System.currentTimeMillis()
|
||||
JsonSlurper slurper = new JsonSlurper()
|
||||
meshFile.eachLine {
|
||||
def json = slurper.parseText(it)
|
||||
if (now - json.timestamp > EXPIRATION)
|
||||
return
|
||||
InfoHash infoHash = new InfoHash(Base64.decode(json.infoHash))
|
||||
Pieces pieces = new Pieces(json.nPieces, Constants.DOWNLOAD_SEQUENTIAL_RATIO)
|
||||
|
||||
Mesh mesh = new Mesh(infoHash, pieces)
|
||||
json.sources.each { source ->
|
||||
Persona persona = new Persona(new ByteArrayInputStream(Base64.decode(source)))
|
||||
mesh.sources.add(persona)
|
||||
}
|
||||
|
||||
if (json.xHave != null)
|
||||
DataUtil.decodeXHave(json.xHave).each { pieces.markDownloaded(it) }
|
||||
|
||||
if (!mesh.sources.isEmpty())
|
||||
meshes.put(infoHash, mesh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -12,8 +12,10 @@ import com.muwire.core.connection.Endpoint
|
||||
import com.muwire.core.mesh.Mesh
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import groovy.util.logging.Log
|
||||
import net.i2p.data.Destination
|
||||
|
||||
@Log
|
||||
class ContentUploader extends Uploader {
|
||||
|
||||
private final File file
|
||||
@@ -42,8 +44,10 @@ class ContentUploader extends Uploader {
|
||||
int endPiece = range.end / (0x1 << pieceSize)
|
||||
for (int i = startPiece; i <= endPiece; i++)
|
||||
satisfiable &= mesh.pieces.isDownloaded(i)
|
||||
log.info("requested range $range.start-$range.end startPiece:$startPiece endPiece:$endPiece satisfiable:$satisfiable my pieces: ${mesh.pieces.getDownloaded()}")
|
||||
}
|
||||
if (!satisfiable) {
|
||||
log.info("416 range not satisfiable")
|
||||
os.write("416 Range Not Satisfiable\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
writeMesh(request.downloader)
|
||||
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
@@ -51,6 +55,8 @@ class ContentUploader extends Uploader {
|
||||
return
|
||||
}
|
||||
|
||||
log.info("200 ok")
|
||||
|
||||
os.write("200 OK\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
os.write("Content-Range: $range.start-$range.end\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||
writeMesh(request.downloader)
|
||||
|
@@ -109,4 +109,10 @@ class DataUtil {
|
||||
}
|
||||
available
|
||||
}
|
||||
|
||||
public static Exception findRoot(Exception e) {
|
||||
while(e.getCause() != null)
|
||||
e = e.getCause()
|
||||
e
|
||||
}
|
||||
}
|
||||
|
@@ -30,7 +30,7 @@ public class SharedFile {
|
||||
long length = file.length();
|
||||
int rawPieceSize = 0x1 << pieceSize;
|
||||
int rv = (int) (length / rawPieceSize);
|
||||
if (length % pieceSize != 0)
|
||||
if (length % rawPieceSize != 0)
|
||||
rv++;
|
||||
return rv;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
group = com.muwire
|
||||
version = 0.3.5
|
||||
version = 0.3.8
|
||||
groovyVersion = 2.4.15
|
||||
slf4jVersion = 1.7.25
|
||||
spockVersion = 1.1-groovy-2.4
|
||||
|
@@ -17,6 +17,8 @@ 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.download.UIDownloadPausedEvent
|
||||
import com.muwire.core.download.UIDownloadResumedEvent
|
||||
import com.muwire.core.search.QueryEvent
|
||||
import com.muwire.core.search.SearchEvent
|
||||
import com.muwire.core.trust.TrustEvent
|
||||
@@ -164,6 +166,14 @@ class MainFrameController {
|
||||
void resume() {
|
||||
def downloader = model.downloads[selectedDownload()].downloader
|
||||
downloader.resume()
|
||||
core.eventBus.publish(new UIDownloadResumedEvent())
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void pause() {
|
||||
def downloader = model.downloads[selectedDownload()].downloader
|
||||
downloader.pause()
|
||||
core.eventBus.publish(new UIDownloadPausedEvent())
|
||||
}
|
||||
|
||||
private void markTrust(String tableName, TrustLevel level, def list) {
|
||||
|
@@ -77,9 +77,9 @@ class OptionsController {
|
||||
model.font = text
|
||||
uiSettings.font = text
|
||||
|
||||
boolean showMonitor = view.monitorCheckbox.model.isSelected()
|
||||
model.showMonitor = showMonitor
|
||||
uiSettings.showMonitor = showMonitor
|
||||
// boolean showMonitor = view.monitorCheckbox.model.isSelected()
|
||||
// model.showMonitor = showMonitor
|
||||
// uiSettings.showMonitor = showMonitor
|
||||
|
||||
boolean clearCancelledDownloads = view.clearCancelledDownloadsCheckbox.model.isSelected()
|
||||
model.clearCancelledDownloads = clearCancelledDownloads
|
||||
@@ -93,9 +93,9 @@ class OptionsController {
|
||||
model.excludeLocalResult = excludeLocalResult
|
||||
uiSettings.excludeLocalResult = excludeLocalResult
|
||||
|
||||
boolean showSearchHashes = view.showSearchHashesCheckbox.model.isSelected()
|
||||
model.showSearchHashes = showSearchHashes
|
||||
uiSettings.showSearchHashes = showSearchHashes
|
||||
// boolean showSearchHashes = view.showSearchHashesCheckbox.model.isSelected()
|
||||
// model.showSearchHashes = showSearchHashes
|
||||
// uiSettings.showSearchHashes = showSearchHashes
|
||||
|
||||
File uiSettingsFile = new File(core.home, "gui.properties")
|
||||
uiSettingsFile.withOutputStream {
|
||||
|
@@ -11,6 +11,7 @@ import com.muwire.core.Core
|
||||
import com.muwire.core.InfoHash
|
||||
import com.muwire.core.MuWireSettings
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.RouterDisconnectedEvent
|
||||
import com.muwire.core.connection.ConnectionAttemptStatus
|
||||
import com.muwire.core.connection.ConnectionEvent
|
||||
import com.muwire.core.connection.DisconnectionEvent
|
||||
@@ -67,6 +68,8 @@ class MainFrameModel {
|
||||
@Observable boolean trustButtonsEnabled
|
||||
@Observable boolean cancelButtonEnabled
|
||||
@Observable boolean retryButtonEnabled
|
||||
@Observable boolean pauseButtonEnabled
|
||||
@Observable String resumeButtonText
|
||||
|
||||
private final Set<InfoHash> infoHashes = new HashSet<>()
|
||||
|
||||
@@ -135,6 +138,7 @@ class MainFrameModel {
|
||||
core.eventBus.register(UpdateAvailableEvent.class, this)
|
||||
core.eventBus.register(FileDownloadedEvent.class, this)
|
||||
core.eventBus.register(FileUnsharedEvent.class, this)
|
||||
core.eventBus.register(RouterDisconnectedEvent.class, this)
|
||||
|
||||
timer.schedule({
|
||||
if (core.shutdown.get())
|
||||
@@ -166,6 +170,8 @@ class MainFrameModel {
|
||||
watched.addAll(core.muOptions.watchedDirectories)
|
||||
builder.getVariable("watched-directories-table").model.fireTableDataChanged()
|
||||
watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) }
|
||||
|
||||
resumeButtonText = "Retry"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -339,6 +345,14 @@ class MainFrameModel {
|
||||
}
|
||||
}
|
||||
|
||||
void onRouterDisconnectedEvent(RouterDisconnectedEvent e) {
|
||||
runInsideUIAsync {
|
||||
JOptionPane.showMessageDialog(null, "MuWire lost connection to the I2P router and will now exit.",
|
||||
"Connection to I2P router lost", JOptionPane.WARNING_MESSAGE)
|
||||
System.exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
void onFileDownloadedEvent(FileDownloadedEvent e) {
|
||||
if (!core.muOptions.shareDownloadedFiles)
|
||||
return
|
||||
|
@@ -135,8 +135,9 @@ class MainFrameView {
|
||||
}
|
||||
}
|
||||
panel (constraints : BorderLayout.SOUTH) {
|
||||
button(text: "Pause", enabled : bind {model.pauseButtonEnabled}, pauseAction)
|
||||
button(text: "Cancel", enabled : bind {model.cancelButtonEnabled }, cancelAction )
|
||||
button(text: "Retry", enabled : bind {model.retryButtonEnabled}, resumeAction)
|
||||
button(text: bind { model.resumeButtonText }, enabled : bind {model.retryButtonEnabled}, resumeAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,6 +296,7 @@ class MainFrameView {
|
||||
if (selectedRow < 0) {
|
||||
model.cancelButtonEnabled = false
|
||||
model.retryButtonEnabled = false
|
||||
model.pauseButtonEnabled = false
|
||||
return
|
||||
}
|
||||
def downloader = model.downloads[selectedRow]?.downloader
|
||||
@@ -305,15 +307,25 @@ class MainFrameView {
|
||||
case Downloader.DownloadState.DOWNLOADING :
|
||||
case Downloader.DownloadState.HASHLIST:
|
||||
model.cancelButtonEnabled = true
|
||||
model.pauseButtonEnabled = true
|
||||
model.retryButtonEnabled = false
|
||||
break
|
||||
case Downloader.DownloadState.FAILED:
|
||||
model.cancelButtonEnabled = true
|
||||
model.retryButtonEnabled = true
|
||||
model.resumeButtonText = "Retry"
|
||||
model.pauseButtonEnabled = false
|
||||
break
|
||||
case Downloader.DownloadState.PAUSED:
|
||||
model.cancelButtonEnabled = true
|
||||
model.retryButtonEnabled = true
|
||||
model.resumeButtonText = "Resume"
|
||||
model.pauseButtonEnabled = false
|
||||
break
|
||||
default:
|
||||
model.cancelButtonEnabled = false
|
||||
model.retryButtonEnabled = false
|
||||
model.pauseButtonEnabled = false
|
||||
}
|
||||
})
|
||||
|
||||
@@ -421,21 +433,32 @@ class MainFrameView {
|
||||
int selected = selectedDownloaderRow()
|
||||
if (selected < 0)
|
||||
return
|
||||
boolean pauseEnabled = false
|
||||
boolean cancelEnabled = false
|
||||
boolean retryEnabled = false
|
||||
String resumeText = "Retry"
|
||||
Downloader downloader = model.downloads[selected].downloader
|
||||
switch(downloader.currentState) {
|
||||
case Downloader.DownloadState.DOWNLOADING:
|
||||
case Downloader.DownloadState.HASHLIST:
|
||||
case Downloader.DownloadState.CONNECTING:
|
||||
pauseEnabled = true
|
||||
cancelEnabled = true
|
||||
retryEnabled = false
|
||||
break
|
||||
case Downloader.DownloadState.FAILED:
|
||||
pauseEnabled = false
|
||||
cancelEnabled = true
|
||||
retryEnabled = true
|
||||
break
|
||||
case Downloader.DownloadState.PAUSED:
|
||||
pauseEnabled = false
|
||||
cancelEnabled = true
|
||||
retryEnabled = true
|
||||
resumeText = "Resume"
|
||||
break
|
||||
default :
|
||||
pauseEnabled = false
|
||||
cancelEnabled = false
|
||||
retryEnabled = false
|
||||
}
|
||||
@@ -450,6 +473,12 @@ class MainFrameView {
|
||||
})
|
||||
menu.add(copyHashToClipboard)
|
||||
|
||||
if (pauseEnabled) {
|
||||
JMenuItem pause = new JMenuItem("Pause")
|
||||
pause.addActionListener({mvcGroup.controller.pause()})
|
||||
menu.add(pause)
|
||||
}
|
||||
|
||||
if (cancelEnabled) {
|
||||
JMenuItem cancel = new JMenuItem("Cancel")
|
||||
cancel.addActionListener({mvcGroup.controller.cancel()})
|
||||
@@ -457,7 +486,7 @@ class MainFrameView {
|
||||
}
|
||||
|
||||
if (retryEnabled) {
|
||||
JMenuItem retry = new JMenuItem("Retry")
|
||||
JMenuItem retry = new JMenuItem(resumeText)
|
||||
retry.addActionListener({mvcGroup.controller.resume()})
|
||||
menu.add(retry)
|
||||
}
|
||||
|
@@ -89,16 +89,16 @@ class OptionsView {
|
||||
lnfField = textField(text : bind {model.lnf}, columns : 4, constraints : gbc(gridx : 1, gridy : 1))
|
||||
label(text : "Font", constraints : gbc(gridx: 0, gridy : 2))
|
||||
fontField = textField(text : bind {model.font}, columns : 4, constraints : gbc(gridx : 1, gridy:2))
|
||||
label(text : "Show Monitor", constraints : gbc(gridx :0, gridy: 3))
|
||||
monitorCheckbox = checkBox(selected : bind {model.showMonitor}, constraints : gbc(gridx : 1, gridy: 3))
|
||||
// label(text : "Show Monitor", constraints : gbc(gridx :0, gridy: 3))
|
||||
// monitorCheckbox = checkBox(selected : bind {model.showMonitor}, constraints : gbc(gridx : 1, gridy: 3))
|
||||
label(text : "Clear Cancelled Downloads", constraints: gbc(gridx: 0, gridy:4))
|
||||
clearCancelledDownloadsCheckbox = checkBox(selected : bind {model.clearCancelledDownloads}, constraints : gbc(gridx : 1, gridy:4))
|
||||
label(text : "Clear Finished Downloads", constraints: gbc(gridx: 0, gridy:5))
|
||||
clearFinishedDownloadsCheckbox = checkBox(selected : bind {model.clearFinishedDownloads}, constraints : gbc(gridx : 1, gridy:5))
|
||||
label(text : "Exclude Local Files From Results", constraints: gbc(gridx:0, gridy:6))
|
||||
excludeLocalResultCheckbox = checkBox(selected : bind {model.excludeLocalResult}, constraints : gbc(gridx: 1, gridy : 6))
|
||||
label(text : "Show Hash Searches In Monitor", constraints: gbc(gridx:0, gridy:7))
|
||||
showSearchHashesCheckbox = checkBox(selected : bind {model.showSearchHashes}, constraints : gbc(gridx: 1, gridy: 7))
|
||||
// label(text : "Show Hash Searches In Monitor", constraints: gbc(gridx:0, gridy:7))
|
||||
// showSearchHashesCheckbox = checkBox(selected : bind {model.showSearchHashes}, constraints : gbc(gridx: 1, gridy: 7))
|
||||
}
|
||||
buttonsPanel = builder.panel {
|
||||
gridBagLayout()
|
||||
|
@@ -12,12 +12,12 @@ class UISettings {
|
||||
|
||||
UISettings(Properties props) {
|
||||
lnf = props.getProperty("lnf", "system")
|
||||
showMonitor = Boolean.parseBoolean(props.getProperty("showMonitor", "true"))
|
||||
showMonitor = Boolean.parseBoolean(props.getProperty("showMonitor", "false"))
|
||||
font = props.getProperty("font",null)
|
||||
clearCancelledDownloads = Boolean.parseBoolean(props.getProperty("clearCancelledDownloads","false"))
|
||||
clearCancelledDownloads = Boolean.parseBoolean(props.getProperty("clearCancelledDownloads","true"))
|
||||
clearFinishedDownloads = Boolean.parseBoolean(props.getProperty("clearFinishedDownloads","false"))
|
||||
excludeLocalResult = Boolean.parseBoolean(props.getProperty("excludeLocalResult","true"))
|
||||
showSearchHashes = Boolean.parseBoolean(props.getProperty("showSearchHashes","false"))
|
||||
showSearchHashes = Boolean.parseBoolean(props.getProperty("showSearchHashes","true"))
|
||||
}
|
||||
|
||||
void write(OutputStream out) throws IOException {
|
||||
|
@@ -35,7 +35,7 @@ handlers= java.util.logging.FileHandler
|
||||
|
||||
# default file output is in user's home directory.
|
||||
java.util.logging.FileHandler.pattern = MuWire.log
|
||||
java.util.logging.FileHandler.limit = 50000000
|
||||
java.util.logging.FileHandler.limit = 150000000
|
||||
java.util.logging.FileHandler.count = 1
|
||||
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
|
||||
|
||||
@@ -60,3 +60,5 @@ java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$
|
||||
# messages:
|
||||
com.xyz.foo.level = SEVERE
|
||||
com.muwire.core.level = FINE
|
||||
net.i2p.client.streaming.impl.level = FINE
|
||||
net.i2p.client.impl.level = FINE
|
||||
|
Reference in New Issue
Block a user