Compare commits
53 Commits
muwire-0.3
...
embedded-r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7aa3008c0 | ||
|
|
485325e824 | ||
|
|
0df2a0e039 | ||
|
|
fb7b4466c2 | ||
|
|
53105245f4 | ||
|
|
b68eab91e0 | ||
|
|
f72cf91462 | ||
|
|
a655c4ef50 | ||
|
|
5d46e9b796 | ||
|
|
642e6e67b3 | ||
|
|
2b6b86f903 | ||
|
|
f2706a4426 | ||
|
|
1af75413aa | ||
|
|
adc4077b1a | ||
|
|
01f4e2453b | ||
|
|
61267374dd | ||
|
|
970f814685 | ||
|
|
4fd9fc1991 | ||
|
|
26207ffd1b | ||
|
|
2614cfbe5f | ||
|
|
f11d461ec0 | ||
|
|
b2eb2d2755 | ||
|
|
ea46a54f19 | ||
|
|
627add45ad | ||
|
|
d364855459 | ||
|
|
14ee35e77a | ||
|
|
8773eb4ee0 | ||
|
|
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 |
15
README.md
@@ -4,7 +4,7 @@ MuWire is an easy to use file-sharing program which offers anonymity using [I2P
|
|||||||
|
|
||||||
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
|
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
|
||||||
|
|
||||||
The current stable release - 0.2.5 is avaiable for download at http://muwire.com. You can find technical documentation in the "doc" folder.
|
The current stable release - 0.4.0 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
@@ -37,8 +37,8 @@ The first time you run MuWire it will ask you to select a nickname. This nickna
|
|||||||
* why is MuWire slow ?
|
* why is MuWire slow ?
|
||||||
|
|
||||||
- too few sources you're downloading from
|
- too few sources you're downloading from
|
||||||
- you can increse the number of tunnels by using more tunnels via Options->I2P Inbound/Outbound Quantity
|
- 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 it and even can go up as high as 16 ( Caution !!!!)
|
the default is 4 and you could raise up to as high as 16 ( Caution !!!!)
|
||||||
|
|
||||||
* my search is not returning (enough) results !
|
* 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
|
- 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'
|
so searching for 'musi' will not return results with 'music' - you have to search for 'music'
|
||||||
- ALL keywords have to match
|
- 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
@@ -4,10 +4,6 @@ Not in any particular order yet
|
|||||||
|
|
||||||
### Big Items
|
### 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
|
##### Bloom Filters
|
||||||
|
|
||||||
This reduces query traffic by not sending last hop queries to peers that definitely do not have the file
|
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
|
### Small Items
|
||||||
|
|
||||||
* Detect if router is dead and show warning or exit
|
|
||||||
* Wrapper of some kind for in-place upgrades
|
* Wrapper of some kind for in-place upgrades
|
||||||
* Download file sequentially
|
* Download file sequentially
|
||||||
* Unsharing of files
|
* Unsharing of files
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class Cli {
|
|||||||
|
|
||||||
Core core
|
Core core
|
||||||
try {
|
try {
|
||||||
core = new Core(props, home, "0.3.5")
|
core = new Core(props, home, "0.4.0")
|
||||||
} 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"
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class CliDownloader {
|
|||||||
|
|
||||||
Core core
|
Core core
|
||||||
try {
|
try {
|
||||||
core = new Core(props, home, "0.3.5")
|
core = new Core(props, home, "0.4.0")
|
||||||
} 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"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ apply plugin : 'application'
|
|||||||
mainClassName = 'com.muwire.core.Core'
|
mainClassName = 'com.muwire.core.Core'
|
||||||
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
||||||
dependencies {
|
dependencies {
|
||||||
|
compile 'net.i2p:router:0.9.40'
|
||||||
compile 'net.i2p.client:mstreaming:0.9.40'
|
compile 'net.i2p.client:mstreaming:0.9.40'
|
||||||
compile 'net.i2p.client:streaming:0.9.40'
|
compile 'net.i2p.client:streaming:0.9.40'
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,5 @@ class Constants {
|
|||||||
public static final int MAX_HEADER_SIZE = 0x1 << 14
|
public static final int MAX_HEADER_SIZE = 0x1 << 14
|
||||||
public static final int MAX_HEADERS = 16
|
public static final int MAX_HEADERS = 16
|
||||||
|
|
||||||
public static final float DOWNLOAD_SEQUENTIAL_RATIO = 0.8f
|
public static final String SPLIT_PATTERN = "[\\*\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|\\[\\]\\{\\}]"
|
||||||
|
|
||||||
public static final String SPLIT_PATTERN = "[\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|]"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import com.muwire.core.download.DownloadManager
|
|||||||
import com.muwire.core.download.SourceDiscoveredEvent
|
import com.muwire.core.download.SourceDiscoveredEvent
|
||||||
import com.muwire.core.download.UIDownloadCancelledEvent
|
import com.muwire.core.download.UIDownloadCancelledEvent
|
||||||
import com.muwire.core.download.UIDownloadEvent
|
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.FileDownloadedEvent
|
||||||
import com.muwire.core.files.FileHashedEvent
|
import com.muwire.core.files.FileHashedEvent
|
||||||
import com.muwire.core.files.FileHasher
|
import com.muwire.core.files.FileHasher
|
||||||
@@ -25,6 +27,7 @@ import com.muwire.core.files.FileSharedEvent
|
|||||||
import com.muwire.core.files.FileUnsharedEvent
|
import com.muwire.core.files.FileUnsharedEvent
|
||||||
import com.muwire.core.files.HasherService
|
import com.muwire.core.files.HasherService
|
||||||
import com.muwire.core.files.PersisterService
|
import com.muwire.core.files.PersisterService
|
||||||
|
import com.muwire.core.files.AllFilesLoadedEvent
|
||||||
import com.muwire.core.files.DirectoryWatcher
|
import com.muwire.core.files.DirectoryWatcher
|
||||||
import com.muwire.core.hostcache.CacheClient
|
import com.muwire.core.hostcache.CacheClient
|
||||||
import com.muwire.core.hostcache.HostCache
|
import com.muwire.core.hostcache.HostCache
|
||||||
@@ -48,6 +51,7 @@ import net.i2p.client.I2PSession
|
|||||||
import net.i2p.client.streaming.I2PSocketManager
|
import net.i2p.client.streaming.I2PSocketManager
|
||||||
import net.i2p.client.streaming.I2PSocketManagerFactory
|
import net.i2p.client.streaming.I2PSocketManagerFactory
|
||||||
import net.i2p.client.streaming.I2PSocketOptions
|
import net.i2p.client.streaming.I2PSocketOptions
|
||||||
|
import net.i2p.client.streaming.I2PSocketManager.DisconnectListener
|
||||||
import net.i2p.crypto.DSAEngine
|
import net.i2p.crypto.DSAEngine
|
||||||
import net.i2p.crypto.SigType
|
import net.i2p.crypto.SigType
|
||||||
import net.i2p.data.Destination
|
import net.i2p.data.Destination
|
||||||
@@ -55,6 +59,9 @@ import net.i2p.data.PrivateKey
|
|||||||
import net.i2p.data.Signature
|
import net.i2p.data.Signature
|
||||||
import net.i2p.data.SigningPrivateKey
|
import net.i2p.data.SigningPrivateKey
|
||||||
|
|
||||||
|
import net.i2p.router.Router
|
||||||
|
import net.i2p.router.RouterContext
|
||||||
|
|
||||||
@Log
|
@Log
|
||||||
public class Core {
|
public class Core {
|
||||||
|
|
||||||
@@ -77,14 +84,31 @@ public class Core {
|
|||||||
private final DirectoryWatcher directoryWatcher
|
private final DirectoryWatcher directoryWatcher
|
||||||
final FileManager fileManager
|
final FileManager fileManager
|
||||||
|
|
||||||
|
private final Router router
|
||||||
|
|
||||||
final AtomicBoolean shutdown = new AtomicBoolean()
|
final AtomicBoolean shutdown = new AtomicBoolean()
|
||||||
|
|
||||||
public Core(MuWireSettings props, File home, String myVersion) {
|
public Core(MuWireSettings props, File home, String myVersion) {
|
||||||
this.home = home
|
this.home = home
|
||||||
this.muOptions = props
|
this.muOptions = props
|
||||||
|
|
||||||
|
if (!props.embeddedRouter) {
|
||||||
log.info "Initializing I2P context"
|
log.info "Initializing I2P context"
|
||||||
I2PAppContext.getGlobalContext().logManager()
|
I2PAppContext.getGlobalContext().logManager()
|
||||||
I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager()
|
I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager()
|
||||||
|
router = null
|
||||||
|
} else {
|
||||||
|
log.info("launching embedded router")
|
||||||
|
Properties routerProps = new Properties()
|
||||||
|
routerProps.setProperty("i2p.dir.config", home.getAbsolutePath())
|
||||||
|
routerProps.setProperty("i2np.inboundKBytesPerSecond", String.valueOf(props.inBw))
|
||||||
|
routerProps.setProperty("i2np.outboundKBytesPerSecond", String.valueOf(props.outBw))
|
||||||
|
router = new Router(routerProps)
|
||||||
|
I2PAppContext.getGlobalContext().metaClass = new RouterContextMetaClass()
|
||||||
|
router.runRouter()
|
||||||
|
while(!router.isRunning())
|
||||||
|
Thread.sleep(100)
|
||||||
|
}
|
||||||
|
|
||||||
log.info("initializing I2P socket manager")
|
log.info("initializing I2P socket manager")
|
||||||
def i2pClient = new I2PClientFactory().createClient()
|
def i2pClient = new I2PClientFactory().createClient()
|
||||||
@@ -124,6 +148,7 @@ public class Core {
|
|||||||
}
|
}
|
||||||
socketManager.getDefaultOptions().setReadTimeout(60000)
|
socketManager.getDefaultOptions().setReadTimeout(60000)
|
||||||
socketManager.getDefaultOptions().setConnectTimeout(30000)
|
socketManager.getDefaultOptions().setConnectTimeout(30000)
|
||||||
|
socketManager.addDisconnectListener({eventBus.publish(new RouterDisconnectedEvent())} as DisconnectListener)
|
||||||
i2pSession = socketManager.getSession()
|
i2pSession = socketManager.getSession()
|
||||||
|
|
||||||
def destination = new Destination()
|
def destination = new Destination()
|
||||||
@@ -169,7 +194,7 @@ public class Core {
|
|||||||
eventBus.register(SearchEvent.class, fileManager)
|
eventBus.register(SearchEvent.class, fileManager)
|
||||||
|
|
||||||
log.info("initializing mesh manager")
|
log.info("initializing mesh manager")
|
||||||
MeshManager meshManager = new MeshManager(fileManager)
|
MeshManager meshManager = new MeshManager(fileManager, home, props)
|
||||||
eventBus.register(SourceDiscoveredEvent.class, meshManager)
|
eventBus.register(SourceDiscoveredEvent.class, meshManager)
|
||||||
|
|
||||||
log.info "initializing persistence service"
|
log.info "initializing persistence service"
|
||||||
@@ -215,6 +240,8 @@ public class Core {
|
|||||||
eventBus.register(FileDownloadedEvent.class, downloadManager)
|
eventBus.register(FileDownloadedEvent.class, downloadManager)
|
||||||
eventBus.register(UIDownloadCancelledEvent.class, downloadManager)
|
eventBus.register(UIDownloadCancelledEvent.class, downloadManager)
|
||||||
eventBus.register(SourceDiscoveredEvent.class, downloadManager)
|
eventBus.register(SourceDiscoveredEvent.class, downloadManager)
|
||||||
|
eventBus.register(UIDownloadPausedEvent.class, downloadManager)
|
||||||
|
eventBus.register(UIDownloadResumedEvent.class, downloadManager)
|
||||||
|
|
||||||
log.info("initializing upload manager")
|
log.info("initializing upload manager")
|
||||||
UploadManager uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager)
|
UploadManager uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager)
|
||||||
@@ -230,6 +257,7 @@ public class Core {
|
|||||||
log.info("initializing directory watcher")
|
log.info("initializing directory watcher")
|
||||||
directoryWatcher = new DirectoryWatcher(eventBus, fileManager)
|
directoryWatcher = new DirectoryWatcher(eventBus, fileManager)
|
||||||
eventBus.register(FileSharedEvent.class, directoryWatcher)
|
eventBus.register(FileSharedEvent.class, directoryWatcher)
|
||||||
|
eventBus.register(AllFilesLoadedEvent.class, directoryWatcher)
|
||||||
|
|
||||||
log.info("initializing hasher service")
|
log.info("initializing hasher service")
|
||||||
hasherService = new HasherService(new FileHasher(), eventBus, fileManager)
|
hasherService = new HasherService(new FileHasher(), eventBus, fileManager)
|
||||||
@@ -238,7 +266,6 @@ public class Core {
|
|||||||
|
|
||||||
public void startServices() {
|
public void startServices() {
|
||||||
hasherService.start()
|
hasherService.start()
|
||||||
directoryWatcher.start()
|
|
||||||
trustService.start()
|
trustService.start()
|
||||||
trustService.waitForLoad()
|
trustService.waitForLoad()
|
||||||
hostCache.start()
|
hostCache.start()
|
||||||
@@ -265,6 +292,23 @@ public class Core {
|
|||||||
directoryWatcher.stop()
|
directoryWatcher.stop()
|
||||||
log.info("shutting down connection manager")
|
log.info("shutting down connection manager")
|
||||||
connectionManager.shutdown()
|
connectionManager.shutdown()
|
||||||
|
if (router != null) {
|
||||||
|
log.info("shutting down embedded router")
|
||||||
|
router.shutdown(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RouterContextMetaClass extends DelegatingMetaClass {
|
||||||
|
private final Object logManager = new MuWireLogManager()
|
||||||
|
RouterContextMetaClass() {
|
||||||
|
super(RouterContext.class)
|
||||||
|
}
|
||||||
|
|
||||||
|
Object invokeMethod(Object object, String name, Object[] args) {
|
||||||
|
if (name == "logManager")
|
||||||
|
return logManager
|
||||||
|
super.invokeMethod(object, name, args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static main(args) {
|
static main(args) {
|
||||||
@@ -291,7 +335,7 @@ public class Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core core = new Core(props, home, "0.3.5")
|
Core core = new Core(props, home, "0.4.0")
|
||||||
core.startServices()
|
core.startServices()
|
||||||
|
|
||||||
// ... at the end, sleep or execute script
|
// ... at the end, sleep or execute script
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ class MuWireSettings {
|
|||||||
CrawlerResponse crawlerResponse
|
CrawlerResponse crawlerResponse
|
||||||
boolean shareDownloadedFiles
|
boolean shareDownloadedFiles
|
||||||
Set<String> watchedDirectories
|
Set<String> watchedDirectories
|
||||||
|
float downloadSequentialRatio
|
||||||
|
int hostClearInterval
|
||||||
|
int meshExpiration
|
||||||
|
boolean embeddedRouter
|
||||||
|
int inBw, outBw
|
||||||
|
|
||||||
MuWireSettings() {
|
MuWireSettings() {
|
||||||
this(new Properties())
|
this(new Properties())
|
||||||
@@ -30,9 +35,15 @@ class MuWireSettings {
|
|||||||
nickname = props.getProperty("nickname","MuWireUser")
|
nickname = props.getProperty("nickname","MuWireUser")
|
||||||
downloadLocation = new File((String)props.getProperty("downloadLocation",
|
downloadLocation = new File((String)props.getProperty("downloadLocation",
|
||||||
System.getProperty("user.home")))
|
System.getProperty("user.home")))
|
||||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","5"))
|
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","1"))
|
||||||
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","36"))
|
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","24"))
|
||||||
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
|
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
|
||||||
|
downloadSequentialRatio = Float.valueOf(props.getProperty("downloadSequentialRatio","0.8"))
|
||||||
|
hostClearInterval = Integer.valueOf(props.getProperty("hostClearInterval","60"))
|
||||||
|
meshExpiration = Integer.valueOf(props.getProperty("meshExpiration","60"))
|
||||||
|
embeddedRouter = Boolean.valueOf(props.getProperty("embeddedRouter","false"))
|
||||||
|
inBw = Integer.valueOf(props.getProperty("inBw","256"))
|
||||||
|
outBw = Integer.valueOf(props.getProperty("outBw","128"))
|
||||||
|
|
||||||
watchedDirectories = new HashSet<>()
|
watchedDirectories = new HashSet<>()
|
||||||
if (props.containsKey("watchedDirectories")) {
|
if (props.containsKey("watchedDirectories")) {
|
||||||
@@ -52,6 +63,12 @@ class MuWireSettings {
|
|||||||
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
|
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
|
||||||
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
|
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
|
||||||
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
||||||
|
props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio))
|
||||||
|
props.setProperty("hostClearInterval", String.valueOf(hostClearInterval))
|
||||||
|
props.setProperty("meshExpiration", String.valueOf(meshExpiration))
|
||||||
|
props.setProperty("embeddedRouter", String.valueOf(embeddedRouter))
|
||||||
|
props.setProperty("inBw", String.valueOf(inBw))
|
||||||
|
props.setProperty("outBw", String.valueOf(outBw))
|
||||||
|
|
||||||
if (!watchedDirectories.isEmpty()) {
|
if (!watchedDirectories.isEmpty()) {
|
||||||
String encoded = watchedDirectories.stream().
|
String encoded = watchedDirectories.stream().
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.muwire.core
|
||||||
|
|
||||||
|
class RouterDisconnectedEvent extends Event {
|
||||||
|
}
|
||||||
@@ -90,6 +90,14 @@ public class DownloadManager {
|
|||||||
persistDownloaders()
|
persistDownloaders()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onUIDownloadPausedEvent(UIDownloadPausedEvent e) {
|
||||||
|
persistDownloaders()
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onUIDownloadResumedEvent(UIDownloadResumedEvent e) {
|
||||||
|
persistDownloaders()
|
||||||
|
}
|
||||||
|
|
||||||
void resume(Downloader downloader) {
|
void resume(Downloader downloader) {
|
||||||
executor.execute({downloader.download() as Runnable})
|
executor.execute({downloader.download() as Runnable})
|
||||||
}
|
}
|
||||||
@@ -119,7 +127,11 @@ public class DownloadManager {
|
|||||||
|
|
||||||
def downloader = new Downloader(eventBus, this, me, file, (long)json.length,
|
def downloader = new Downloader(eventBus, this, me, file, (long)json.length,
|
||||||
infoHash, json.pieceSizePow2, connector, destinations, incompletes, pieces)
|
infoHash, json.pieceSizePow2, connector, destinations, incompletes, pieces)
|
||||||
|
if (json.paused != null)
|
||||||
|
downloader.paused = json.paused
|
||||||
downloaders.put(infoHash, downloader)
|
downloaders.put(infoHash, downloader)
|
||||||
|
downloader.readPieces()
|
||||||
|
if (!downloader.paused)
|
||||||
downloader.download()
|
downloader.download()
|
||||||
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
|
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
|
||||||
}
|
}
|
||||||
@@ -174,6 +186,8 @@ public class DownloadManager {
|
|||||||
json.hashList = Base64.encode(infoHash.hashList)
|
json.hashList = Base64.encode(infoHash.hashList)
|
||||||
else
|
else
|
||||||
json.hashRoot = Base64.encode(infoHash.getRoot())
|
json.hashRoot = Base64.encode(infoHash.getRoot())
|
||||||
|
|
||||||
|
json.paused = downloader.paused
|
||||||
writer.println(JsonOutput.toJson(json))
|
writer.println(JsonOutput.toJson(json))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class DownloadSession {
|
|||||||
if (available.isEmpty())
|
if (available.isEmpty())
|
||||||
piece = pieces.claim()
|
piece = pieces.claim()
|
||||||
else
|
else
|
||||||
piece = pieces.claim(available)
|
piece = pieces.claim(new HashSet<>(available))
|
||||||
if (piece == -1)
|
if (piece == -1)
|
||||||
return false
|
return false
|
||||||
boolean unclaim = true
|
boolean unclaim = true
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import com.muwire.core.DownloadedFile
|
|||||||
import com.muwire.core.EventBus
|
import com.muwire.core.EventBus
|
||||||
import com.muwire.core.connection.I2PConnector
|
import com.muwire.core.connection.I2PConnector
|
||||||
import com.muwire.core.files.FileDownloadedEvent
|
import com.muwire.core.files.FileDownloadedEvent
|
||||||
|
import com.muwire.core.util.DataUtil
|
||||||
|
|
||||||
import groovy.util.logging.Log
|
import groovy.util.logging.Log
|
||||||
import net.i2p.data.Destination
|
import net.i2p.data.Destination
|
||||||
@@ -25,7 +26,7 @@ import net.i2p.util.ConcurrentHashSet
|
|||||||
|
|
||||||
@Log
|
@Log
|
||||||
public class Downloader {
|
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 enum WorkerState { CONNECTING, HASHLIST, DOWNLOADING, FINISHED}
|
||||||
|
|
||||||
private static final ExecutorService executorService = Executors.newCachedThreadPool({r ->
|
private static final ExecutorService executorService = Executors.newCachedThreadPool({r ->
|
||||||
@@ -53,7 +54,7 @@ public class Downloader {
|
|||||||
private final Set<Destination> successfulDestinations = new ConcurrentHashSet<>()
|
private final Set<Destination> successfulDestinations = new ConcurrentHashSet<>()
|
||||||
|
|
||||||
|
|
||||||
private volatile boolean cancelled
|
private volatile boolean cancelled, paused
|
||||||
private final AtomicBoolean eventFired = new AtomicBoolean()
|
private final AtomicBoolean eventFired = new AtomicBoolean()
|
||||||
private boolean piecesFileClosed
|
private boolean piecesFileClosed
|
||||||
|
|
||||||
@@ -136,6 +137,9 @@ public class Downloader {
|
|||||||
public DownloadState getCurrentState() {
|
public DownloadState getCurrentState() {
|
||||||
if (cancelled)
|
if (cancelled)
|
||||||
return DownloadState.CANCELLED
|
return DownloadState.CANCELLED
|
||||||
|
if (paused)
|
||||||
|
return DownloadState.PAUSED
|
||||||
|
|
||||||
boolean allFinished = true
|
boolean allFinished = true
|
||||||
activeWorkers.values().each {
|
activeWorkers.values().each {
|
||||||
allFinished &= it.currentState == WorkerState.FINISHED
|
allFinished &= it.currentState == WorkerState.FINISHED
|
||||||
@@ -183,6 +187,11 @@ public class Downloader {
|
|||||||
pieces.clearAll()
|
pieces.clearAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void pause() {
|
||||||
|
paused = true
|
||||||
|
stop()
|
||||||
|
}
|
||||||
|
|
||||||
void stop() {
|
void stop() {
|
||||||
activeWorkers.values().each {
|
activeWorkers.values().each {
|
||||||
it.cancel()
|
it.cancel()
|
||||||
@@ -199,6 +208,8 @@ public class Downloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void resume() {
|
public void resume() {
|
||||||
|
paused = false
|
||||||
|
readPieces()
|
||||||
destinations.each { destination ->
|
destinations.each { destination ->
|
||||||
def worker = activeWorkers.get(destination)
|
def worker = activeWorkers.get(destination)
|
||||||
if (worker != null) {
|
if (worker != null) {
|
||||||
@@ -259,7 +270,7 @@ public class Downloader {
|
|||||||
writePieces()
|
writePieces()
|
||||||
}
|
}
|
||||||
} catch (Exception bad) {
|
} catch (Exception bad) {
|
||||||
log.log(Level.WARNING,"Exception while downloading",bad)
|
log.log(Level.WARNING,"Exception while downloading",DataUtil.findRoot(bad))
|
||||||
} finally {
|
} finally {
|
||||||
currentState = WorkerState.FINISHED
|
currentState = WorkerState.FINISHED
|
||||||
if (pieces.isComplete() && eventFired.compareAndSet(false, true)) {
|
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 {
|
||||||
|
}
|
||||||
@@ -47,7 +47,7 @@ class DirectoryWatcher {
|
|||||||
publisherThread.setDaemon(true)
|
publisherThread.setDaemon(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
void start() {
|
void onAllFilesLoadedEvent(AllFilesLoadedEvent e) {
|
||||||
watchService = FileSystems.getDefault().newWatchService()
|
watchService = FileSystems.getDefault().newWatchService()
|
||||||
watcherThread.start()
|
watcherThread.start()
|
||||||
publisherThread.start()
|
publisherThread.start()
|
||||||
@@ -55,9 +55,9 @@ class DirectoryWatcher {
|
|||||||
|
|
||||||
void stop() {
|
void stop() {
|
||||||
shutdown = true
|
shutdown = true
|
||||||
watcherThread.interrupt()
|
watcherThread?.interrupt()
|
||||||
publisherThread.interrupt()
|
publisherThread?.interrupt()
|
||||||
watchService.close()
|
watchService?.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
void onFileSharedEvent(FileSharedEvent e) {
|
void onFileSharedEvent(FileSharedEvent e) {
|
||||||
|
|||||||
@@ -7,4 +7,10 @@ class FileHashedEvent extends Event {
|
|||||||
|
|
||||||
SharedFile sharedFile
|
SharedFile sharedFile
|
||||||
String error
|
String error
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
super.toString() + " sharedFile " + sharedFile?.file.getAbsolutePath() + " error: $error"
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,9 @@ import com.muwire.core.Event
|
|||||||
class FileSharedEvent extends Event {
|
class FileSharedEvent extends Event {
|
||||||
|
|
||||||
File file
|
File file
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + " file: "+file.getAbsolutePath()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ import net.i2p.data.Destination
|
|||||||
class Host {
|
class Host {
|
||||||
|
|
||||||
private static final int MAX_FAILURES = 3
|
private static final int MAX_FAILURES = 3
|
||||||
private static final int CLEAR_INTERVAL = 60 * 60 * 1000
|
|
||||||
|
|
||||||
final Destination destination
|
final Destination destination
|
||||||
|
private final int clearInterval
|
||||||
int failures,successes
|
int failures,successes
|
||||||
long lastAttempt
|
long lastAttempt
|
||||||
|
|
||||||
public Host(Destination destination) {
|
public Host(Destination destination, int clearInterval) {
|
||||||
this.destination = destination
|
this.destination = destination
|
||||||
|
this.clearInterval = clearInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void onConnect() {
|
synchronized void onConnect() {
|
||||||
@@ -40,6 +41,6 @@ class Host {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized void canTryAgain() {
|
synchronized void canTryAgain() {
|
||||||
System.currentTimeMillis() - lastAttempt > CLEAR_INTERVAL
|
System.currentTimeMillis() - lastAttempt > (clearInterval * 60 * 1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class HostCache extends Service {
|
|||||||
hosts.get(e.destination).clearFailures()
|
hosts.get(e.destination).clearFailures()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Host host = new Host(e.destination)
|
Host host = new Host(e.destination, settings.hostClearInterval)
|
||||||
if (allowHost(host)) {
|
if (allowHost(host)) {
|
||||||
hosts.put(e.destination, host)
|
hosts.put(e.destination, host)
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ class HostCache extends Service {
|
|||||||
Destination dest = e.endpoint.destination
|
Destination dest = e.endpoint.destination
|
||||||
Host host = hosts.get(dest)
|
Host host = hosts.get(dest)
|
||||||
if (host == null) {
|
if (host == null) {
|
||||||
host = new Host(dest)
|
host = new Host(dest, settings.hostClearInterval)
|
||||||
hosts.put(dest, host)
|
hosts.put(dest, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ class HostCache extends Service {
|
|||||||
storage.eachLine {
|
storage.eachLine {
|
||||||
def entry = slurper.parseText(it)
|
def entry = slurper.parseText(it)
|
||||||
Destination dest = new Destination(entry.destination)
|
Destination dest = new Destination(entry.destination)
|
||||||
Host host = new Host(dest)
|
Host host = new Host(dest, settings.hostClearInterval)
|
||||||
host.failures = Integer.valueOf(String.valueOf(entry.failures))
|
host.failures = Integer.valueOf(String.valueOf(entry.failures))
|
||||||
host.successes = Integer.valueOf(String.valueOf(entry.successes))
|
host.successes = Integer.valueOf(String.valueOf(entry.successes))
|
||||||
if (entry.lastAttempt != null)
|
if (entry.lastAttempt != null)
|
||||||
|
|||||||
@@ -1,18 +1,32 @@
|
|||||||
package com.muwire.core.mesh
|
package com.muwire.core.mesh
|
||||||
|
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
import com.muwire.core.Constants
|
import com.muwire.core.Constants
|
||||||
import com.muwire.core.InfoHash
|
import com.muwire.core.InfoHash
|
||||||
|
import com.muwire.core.MuWireSettings
|
||||||
|
import com.muwire.core.Persona
|
||||||
import com.muwire.core.download.Pieces
|
import com.muwire.core.download.Pieces
|
||||||
import com.muwire.core.download.SourceDiscoveredEvent
|
import com.muwire.core.download.SourceDiscoveredEvent
|
||||||
import com.muwire.core.files.FileManager
|
import com.muwire.core.files.FileManager
|
||||||
|
import com.muwire.core.util.DataUtil
|
||||||
|
|
||||||
|
import groovy.json.JsonOutput
|
||||||
|
import groovy.json.JsonSlurper
|
||||||
|
import net.i2p.data.Base64
|
||||||
|
|
||||||
class MeshManager {
|
class MeshManager {
|
||||||
|
|
||||||
private final Map<InfoHash, Mesh> meshes = Collections.synchronizedMap(new HashMap<>())
|
private final Map<InfoHash, Mesh> meshes = Collections.synchronizedMap(new HashMap<>())
|
||||||
private final FileManager fileManager
|
private final FileManager fileManager
|
||||||
|
private final File home
|
||||||
|
private final MuWireSettings settings
|
||||||
|
|
||||||
MeshManager(FileManager fileManager) {
|
MeshManager(FileManager fileManager, File home, MuWireSettings settings) {
|
||||||
this.fileManager = fileManager
|
this.fileManager = fileManager
|
||||||
|
this.home = home
|
||||||
|
this.settings = settings
|
||||||
|
load()
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh get(InfoHash infoHash) {
|
Mesh get(InfoHash infoHash) {
|
||||||
@@ -23,7 +37,7 @@ class MeshManager {
|
|||||||
synchronized(meshes) {
|
synchronized(meshes) {
|
||||||
if (meshes.containsKey(infoHash))
|
if (meshes.containsKey(infoHash))
|
||||||
return meshes.get(infoHash)
|
return meshes.get(infoHash)
|
||||||
Pieces pieces = new Pieces(nPieces, Constants.DOWNLOAD_SEQUENTIAL_RATIO)
|
Pieces pieces = new Pieces(nPieces, settings.downloadSequentialRatio)
|
||||||
if (fileManager.rootToFiles.containsKey(infoHash)) {
|
if (fileManager.rootToFiles.containsKey(infoHash)) {
|
||||||
for (int i = 0; i < nPieces; i++)
|
for (int i = 0; i < nPieces; i++)
|
||||||
pieces.markDownloaded(i)
|
pieces.markDownloaded(i)
|
||||||
@@ -39,5 +53,50 @@ class MeshManager {
|
|||||||
if (mesh == null)
|
if (mesh == null)
|
||||||
return
|
return
|
||||||
mesh.sources.add(e.source)
|
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 > settings.meshExpiration * 60 * 1000)
|
||||||
|
return
|
||||||
|
InfoHash infoHash = new InfoHash(Base64.decode(json.infoHash))
|
||||||
|
Pieces pieces = new Pieces(json.nPieces, settings.downloadSequentialRatio)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ package com.muwire.core.upload
|
|||||||
|
|
||||||
class ContentRequest extends Request {
|
class ContentRequest extends Request {
|
||||||
Range range
|
Range range
|
||||||
boolean have
|
int have
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,4 +105,14 @@ class ContentUploader extends Uploader {
|
|||||||
request.downloader.getHumanReadableName()
|
request.downloader.getHumanReadableName()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDonePieces() {
|
||||||
|
return request.have;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTotalPieces() {
|
||||||
|
return mesh.pieces.nPieces;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,5 +51,15 @@ class HashListUploader extends Uploader {
|
|||||||
request.downloader.getHumanReadableName()
|
request.downloader.getHumanReadableName()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDonePieces() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTotalPieces() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,10 +50,10 @@ class Request {
|
|||||||
downloader = new Persona(new ByteArrayInputStream(decoded))
|
downloader = new Persona(new ByteArrayInputStream(decoded))
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean have = false
|
int have = 0
|
||||||
if (headers.containsKey("X-Have")) {
|
if (headers.containsKey("X-Have")) {
|
||||||
def encoded = headers["X-Have"].trim()
|
def encoded = headers["X-Have"].trim()
|
||||||
have = DataUtil.decodeXHave(encoded).size() > 0
|
have = DataUtil.decodeXHave(encoded).size()
|
||||||
}
|
}
|
||||||
new ContentRequest( infoHash : infoHash, range : new Range(start, end),
|
new ContentRequest( infoHash : infoHash, range : new Range(start, end),
|
||||||
headers : headers, downloader : downloader, have : have)
|
headers : headers, downloader : downloader, have : have)
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ public class UploadManager {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.have)
|
if (request.have > 0)
|
||||||
eventBus.publish(new SourceDiscoveredEvent(infoHash : request.infoHash, source : request.downloader))
|
eventBus.publish(new SourceDiscoveredEvent(infoHash : request.infoHash, source : request.downloader))
|
||||||
|
|
||||||
Mesh mesh
|
Mesh mesh
|
||||||
@@ -205,7 +205,7 @@ public class UploadManager {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.have)
|
if (request.have > 0)
|
||||||
eventBus.publish(new SourceDiscoveredEvent(infoHash : request.infoHash, source : request.downloader))
|
eventBus.publish(new SourceDiscoveredEvent(infoHash : request.infoHash, source : request.downloader))
|
||||||
|
|
||||||
Mesh mesh
|
Mesh mesh
|
||||||
|
|||||||
@@ -32,4 +32,8 @@ abstract class Uploader {
|
|||||||
abstract int getProgress();
|
abstract int getProgress();
|
||||||
|
|
||||||
abstract String getDownloader();
|
abstract String getDownloader();
|
||||||
|
|
||||||
|
abstract int getDonePieces();
|
||||||
|
|
||||||
|
abstract int getTotalPieces()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,4 +109,10 @@ class DataUtil {
|
|||||||
}
|
}
|
||||||
available
|
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();
|
long length = file.length();
|
||||||
int rawPieceSize = 0x1 << pieceSize;
|
int rawPieceSize = 0x1 << pieceSize;
|
||||||
int rv = (int) (length / rawPieceSize);
|
int rv = (int) (length / rawPieceSize);
|
||||||
if (length % pieceSize != 0)
|
if (length % rawPieceSize != 0)
|
||||||
rv++;
|
rv++;
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
group = com.muwire
|
group = com.muwire
|
||||||
version = 0.3.5
|
version = 0.4.0
|
||||||
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
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import com.muwire.core.Core
|
|||||||
import com.muwire.core.download.DownloadStartedEvent
|
import com.muwire.core.download.DownloadStartedEvent
|
||||||
import com.muwire.core.download.UIDownloadCancelledEvent
|
import com.muwire.core.download.UIDownloadCancelledEvent
|
||||||
import com.muwire.core.download.UIDownloadEvent
|
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.QueryEvent
|
||||||
import com.muwire.core.search.SearchEvent
|
import com.muwire.core.search.SearchEvent
|
||||||
import com.muwire.core.trust.TrustEvent
|
import com.muwire.core.trust.TrustEvent
|
||||||
@@ -42,6 +44,8 @@ class MainFrameController {
|
|||||||
search = search.trim()
|
search = search.trim()
|
||||||
if (search.length() == 0)
|
if (search.length() == 0)
|
||||||
return
|
return
|
||||||
|
if (search.length() > 128)
|
||||||
|
search = search.substring(0,128)
|
||||||
def uuid = UUID.randomUUID()
|
def uuid = UUID.randomUUID()
|
||||||
Map<String, Object> params = new HashMap<>()
|
Map<String, Object> params = new HashMap<>()
|
||||||
params["search-terms"] = search
|
params["search-terms"] = search
|
||||||
@@ -164,6 +168,14 @@ class MainFrameController {
|
|||||||
void resume() {
|
void resume() {
|
||||||
def downloader = model.downloads[selectedDownload()].downloader
|
def downloader = model.downloads[selectedDownload()].downloader
|
||||||
downloader.resume()
|
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) {
|
private void markTrust(String tableName, TrustLevel level, def list) {
|
||||||
|
|||||||
@@ -4,9 +4,15 @@ import griffon.core.artifact.GriffonController
|
|||||||
import griffon.core.controller.ControllerAction
|
import griffon.core.controller.ControllerAction
|
||||||
import griffon.inject.MVCMember
|
import griffon.inject.MVCMember
|
||||||
import griffon.metadata.ArtifactProviderFor
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
import groovy.util.logging.Log
|
||||||
|
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
import javax.annotation.Nonnull
|
import javax.annotation.Nonnull
|
||||||
|
import javax.swing.JFileChooser
|
||||||
|
|
||||||
import com.muwire.core.Core
|
import com.muwire.core.Core
|
||||||
|
import com.muwire.core.MuWireSettings
|
||||||
|
|
||||||
@ArtifactProviderFor(GriffonController)
|
@ArtifactProviderFor(GriffonController)
|
||||||
class OptionsController {
|
class OptionsController {
|
||||||
@@ -46,7 +52,7 @@ class OptionsController {
|
|||||||
text = view.retryField.text
|
text = view.retryField.text
|
||||||
model.downloadRetryInterval = text
|
model.downloadRetryInterval = text
|
||||||
|
|
||||||
def settings = application.context.get("muwire-settings")
|
MuWireSettings settings = application.context.get("muwire-settings")
|
||||||
settings.downloadRetryInterval = Integer.valueOf(text)
|
settings.downloadRetryInterval = Integer.valueOf(text)
|
||||||
|
|
||||||
text = view.updateField.text
|
text = view.updateField.text
|
||||||
@@ -61,6 +67,18 @@ class OptionsController {
|
|||||||
model.shareDownloadedFiles = shareDownloaded
|
model.shareDownloadedFiles = shareDownloaded
|
||||||
settings.shareDownloadedFiles = shareDownloaded
|
settings.shareDownloadedFiles = shareDownloaded
|
||||||
|
|
||||||
|
String downloadLocation = model.downloadLocation
|
||||||
|
settings.downloadLocation = new File(downloadLocation)
|
||||||
|
|
||||||
|
if (settings.embeddedRouter) {
|
||||||
|
text = view.inBwField.text
|
||||||
|
model.inBw = text
|
||||||
|
settings.inBw = Integer.valueOf(text)
|
||||||
|
text = view.outBwField.text
|
||||||
|
model.outBw = text
|
||||||
|
settings.outBw = Integer.valueOf(text)
|
||||||
|
}
|
||||||
|
|
||||||
File settingsFile = new File(core.home, "MuWire.properties")
|
File settingsFile = new File(core.home, "MuWire.properties")
|
||||||
settingsFile.withOutputStream {
|
settingsFile.withOutputStream {
|
||||||
settings.write(it)
|
settings.write(it)
|
||||||
@@ -77,9 +95,9 @@ class OptionsController {
|
|||||||
model.font = text
|
model.font = text
|
||||||
uiSettings.font = text
|
uiSettings.font = text
|
||||||
|
|
||||||
boolean showMonitor = view.monitorCheckbox.model.isSelected()
|
// boolean showMonitor = view.monitorCheckbox.model.isSelected()
|
||||||
model.showMonitor = showMonitor
|
// model.showMonitor = showMonitor
|
||||||
uiSettings.showMonitor = showMonitor
|
// uiSettings.showMonitor = showMonitor
|
||||||
|
|
||||||
boolean clearCancelledDownloads = view.clearCancelledDownloadsCheckbox.model.isSelected()
|
boolean clearCancelledDownloads = view.clearCancelledDownloadsCheckbox.model.isSelected()
|
||||||
model.clearCancelledDownloads = clearCancelledDownloads
|
model.clearCancelledDownloads = clearCancelledDownloads
|
||||||
@@ -93,9 +111,9 @@ class OptionsController {
|
|||||||
model.excludeLocalResult = excludeLocalResult
|
model.excludeLocalResult = excludeLocalResult
|
||||||
uiSettings.excludeLocalResult = excludeLocalResult
|
uiSettings.excludeLocalResult = excludeLocalResult
|
||||||
|
|
||||||
boolean showSearchHashes = view.showSearchHashesCheckbox.model.isSelected()
|
// boolean showSearchHashes = view.showSearchHashesCheckbox.model.isSelected()
|
||||||
model.showSearchHashes = showSearchHashes
|
// model.showSearchHashes = showSearchHashes
|
||||||
uiSettings.showSearchHashes = showSearchHashes
|
// uiSettings.showSearchHashes = showSearchHashes
|
||||||
|
|
||||||
File uiSettingsFile = new File(core.home, "gui.properties")
|
File uiSettingsFile = new File(core.home, "gui.properties")
|
||||||
uiSettingsFile.withOutputStream {
|
uiSettingsFile.withOutputStream {
|
||||||
@@ -110,4 +128,15 @@ class OptionsController {
|
|||||||
view.d.setVisible(false)
|
view.d.setVisible(false)
|
||||||
mvcGroup.destroy()
|
mvcGroup.destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void downloadLocation() {
|
||||||
|
def chooser = new JFileChooser()
|
||||||
|
chooser.setFileHidingEnabled(false)
|
||||||
|
chooser.setDialogTitle("Select location for downloaded files")
|
||||||
|
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
|
||||||
|
int rv = chooser.showOpenDialog(null)
|
||||||
|
if (rv == JFileChooser.APPROVE_OPTION)
|
||||||
|
model.downloadLocation = chooser.getSelectedFile().getAbsolutePath()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -43,6 +43,8 @@ class Initialize extends AbstractLifecycleHandler {
|
|||||||
|
|
||||||
application.context.put("muwire-home", home.getAbsolutePath())
|
application.context.put("muwire-home", home.getAbsolutePath())
|
||||||
|
|
||||||
|
System.getProperties().setProperty("awt.useSystemAAFontSettings", "true")
|
||||||
|
|
||||||
def guiPropsFile = new File(home, "gui.properties")
|
def guiPropsFile = new File(home, "gui.properties")
|
||||||
UISettings uiSettings
|
UISettings uiSettings
|
||||||
if (guiPropsFile.exists()) {
|
if (guiPropsFile.exists()) {
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ class Ready extends AbstractLifecycleHandler {
|
|||||||
props.downloadLocation = new File(portableDownloads)
|
props.downloadLocation = new File(portableDownloads)
|
||||||
} else {
|
} else {
|
||||||
def chooser = new JFileChooser()
|
def chooser = new JFileChooser()
|
||||||
|
chooser.setFileHidingEnabled(false)
|
||||||
chooser.setDialogTitle("Select a directory where downloads will be saved")
|
chooser.setDialogTitle("Select a directory where downloads will be saved")
|
||||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
|
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
|
||||||
int rv = chooser.showOpenDialog(null)
|
int rv = chooser.showOpenDialog(null)
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import com.muwire.core.Core
|
|||||||
import com.muwire.core.InfoHash
|
import com.muwire.core.InfoHash
|
||||||
import com.muwire.core.MuWireSettings
|
import com.muwire.core.MuWireSettings
|
||||||
import com.muwire.core.Persona
|
import com.muwire.core.Persona
|
||||||
|
import com.muwire.core.RouterDisconnectedEvent
|
||||||
import com.muwire.core.connection.ConnectionAttemptStatus
|
import com.muwire.core.connection.ConnectionAttemptStatus
|
||||||
import com.muwire.core.connection.ConnectionEvent
|
import com.muwire.core.connection.ConnectionEvent
|
||||||
import com.muwire.core.connection.DisconnectionEvent
|
import com.muwire.core.connection.DisconnectionEvent
|
||||||
import com.muwire.core.download.DownloadStartedEvent
|
import com.muwire.core.download.DownloadStartedEvent
|
||||||
import com.muwire.core.download.Downloader
|
import com.muwire.core.download.Downloader
|
||||||
|
import com.muwire.core.files.AllFilesLoadedEvent
|
||||||
import com.muwire.core.files.FileDownloadedEvent
|
import com.muwire.core.files.FileDownloadedEvent
|
||||||
import com.muwire.core.files.FileHashedEvent
|
import com.muwire.core.files.FileHashedEvent
|
||||||
import com.muwire.core.files.FileLoadedEvent
|
import com.muwire.core.files.FileLoadedEvent
|
||||||
@@ -67,6 +69,8 @@ class MainFrameModel {
|
|||||||
@Observable boolean trustButtonsEnabled
|
@Observable boolean trustButtonsEnabled
|
||||||
@Observable boolean cancelButtonEnabled
|
@Observable boolean cancelButtonEnabled
|
||||||
@Observable boolean retryButtonEnabled
|
@Observable boolean retryButtonEnabled
|
||||||
|
@Observable boolean pauseButtonEnabled
|
||||||
|
@Observable String resumeButtonText
|
||||||
|
|
||||||
private final Set<InfoHash> infoHashes = new HashSet<>()
|
private final Set<InfoHash> infoHashes = new HashSet<>()
|
||||||
|
|
||||||
@@ -135,6 +139,8 @@ class MainFrameModel {
|
|||||||
core.eventBus.register(UpdateAvailableEvent.class, this)
|
core.eventBus.register(UpdateAvailableEvent.class, this)
|
||||||
core.eventBus.register(FileDownloadedEvent.class, this)
|
core.eventBus.register(FileDownloadedEvent.class, this)
|
||||||
core.eventBus.register(FileUnsharedEvent.class, this)
|
core.eventBus.register(FileUnsharedEvent.class, this)
|
||||||
|
core.eventBus.register(RouterDisconnectedEvent.class, this)
|
||||||
|
core.eventBus.register(AllFilesLoadedEvent.class, this)
|
||||||
|
|
||||||
timer.schedule({
|
timer.schedule({
|
||||||
if (core.shutdown.get())
|
if (core.shutdown.get())
|
||||||
@@ -163,14 +169,20 @@ class MainFrameModel {
|
|||||||
trusted.addAll(core.trustService.good.values())
|
trusted.addAll(core.trustService.good.values())
|
||||||
distrusted.addAll(core.trustService.bad.values())
|
distrusted.addAll(core.trustService.bad.values())
|
||||||
|
|
||||||
watched.addAll(core.muOptions.watchedDirectories)
|
|
||||||
builder.getVariable("watched-directories-table").model.fireTableDataChanged()
|
resumeButtonText = "Retry"
|
||||||
watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) }
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onAllFilesLoadedEvent(AllFilesLoadedEvent e) {
|
||||||
|
runInsideUIAsync {
|
||||||
|
watched.addAll(core.muOptions.watchedDirectories)
|
||||||
|
builder.getVariable("watched-directories-table").model.fireTableDataChanged()
|
||||||
|
watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) }
|
||||||
|
}
|
||||||
|
}
|
||||||
void onUIResultEvent(UIResultEvent e) {
|
void onUIResultEvent(UIResultEvent e) {
|
||||||
MVCGroup resultsGroup = results.get(e.uuid)
|
MVCGroup resultsGroup = results.get(e.uuid)
|
||||||
resultsGroup?.model.handleResult(e)
|
resultsGroup?.model.handleResult(e)
|
||||||
@@ -339,6 +351,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) {
|
void onFileDownloadedEvent(FileDownloadedEvent e) {
|
||||||
if (!core.muOptions.shareDownloadedFiles)
|
if (!core.muOptions.shareDownloadedFiles)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class OptionsModel {
|
|||||||
@Observable String updateCheckInterval
|
@Observable String updateCheckInterval
|
||||||
@Observable boolean onlyTrusted
|
@Observable boolean onlyTrusted
|
||||||
@Observable boolean shareDownloadedFiles
|
@Observable boolean shareDownloadedFiles
|
||||||
|
@Observable String downloadLocation
|
||||||
|
|
||||||
// i2p options
|
// i2p options
|
||||||
@Observable String inboundLength
|
@Observable String inboundLength
|
||||||
@@ -29,12 +30,17 @@ class OptionsModel {
|
|||||||
@Observable boolean excludeLocalResult
|
@Observable boolean excludeLocalResult
|
||||||
@Observable boolean showSearchHashes
|
@Observable boolean showSearchHashes
|
||||||
|
|
||||||
|
// bw options
|
||||||
|
@Observable String inBw
|
||||||
|
@Observable String outBw
|
||||||
|
|
||||||
void mvcGroupInit(Map<String, String> args) {
|
void mvcGroupInit(Map<String, String> args) {
|
||||||
MuWireSettings settings = application.context.get("muwire-settings")
|
MuWireSettings settings = application.context.get("muwire-settings")
|
||||||
downloadRetryInterval = settings.downloadRetryInterval
|
downloadRetryInterval = settings.downloadRetryInterval
|
||||||
updateCheckInterval = settings.updateCheckInterval
|
updateCheckInterval = settings.updateCheckInterval
|
||||||
onlyTrusted = !settings.allowUntrusted()
|
onlyTrusted = !settings.allowUntrusted()
|
||||||
shareDownloadedFiles = settings.shareDownloadedFiles
|
shareDownloadedFiles = settings.shareDownloadedFiles
|
||||||
|
downloadLocation = settings.downloadLocation.getAbsolutePath()
|
||||||
|
|
||||||
Core core = application.context.get("core")
|
Core core = application.context.get("core")
|
||||||
inboundLength = core.i2pOptions["inbound.length"]
|
inboundLength = core.i2pOptions["inbound.length"]
|
||||||
@@ -50,5 +56,10 @@ class OptionsModel {
|
|||||||
clearFinishedDownloads = uiSettings.clearFinishedDownloads
|
clearFinishedDownloads = uiSettings.clearFinishedDownloads
|
||||||
excludeLocalResult = uiSettings.excludeLocalResult
|
excludeLocalResult = uiSettings.excludeLocalResult
|
||||||
showSearchHashes = uiSettings.showSearchHashes
|
showSearchHashes = uiSettings.showSearchHashes
|
||||||
|
|
||||||
|
if (core.router != null) {
|
||||||
|
inBw = String.valueOf(settings.inBw)
|
||||||
|
outBw = String.valueOf(settings.outBw)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
gui/griffon-app/resources/MuWire-128x128.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
gui/griffon-app/resources/MuWire-16x16.png
Normal file
|
After Width: | Height: | Size: 344 B |
BIN
gui/griffon-app/resources/MuWire-32x32.png
Normal file
|
After Width: | Height: | Size: 459 B |
BIN
gui/griffon-app/resources/MuWire-48x48.png
Normal file
|
After Width: | Height: | Size: 1003 B |
BIN
gui/griffon-app/resources/MuWire-64x64.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
@@ -21,10 +21,10 @@ class EventListView {
|
|||||||
application(size: [320, 80], id: 'event-list',
|
application(size: [320, 80], id: 'event-list',
|
||||||
locationRelativeTo : null,
|
locationRelativeTo : null,
|
||||||
title: application.configuration['application.title'],
|
title: application.configuration['application.title'],
|
||||||
iconImage: imageIcon('/griffon-icon-48x48.png').image,
|
iconImage: imageIcon('/MuWire-48x48.png').image,
|
||||||
iconImages: [imageIcon('/griffon-icon-48x48.png').image,
|
iconImages: [imageIcon('/MuWire-48x48.png').image,
|
||||||
imageIcon('/griffon-icon-32x32.png').image,
|
imageIcon('/MuWire-32x32.png').image,
|
||||||
imageIcon('/griffon-icon-16x16.png').image],
|
imageIcon('/MuWire-16x16.png').image],
|
||||||
visible: bind { !model.coreInitialized} ) {
|
visible: bind { !model.coreInitialized} ) {
|
||||||
panel {
|
panel {
|
||||||
vbox {
|
vbox {
|
||||||
|
|||||||
@@ -60,10 +60,10 @@ class MainFrameView {
|
|||||||
locationRelativeTo : null,
|
locationRelativeTo : null,
|
||||||
title: application.configuration['application.title'] + " " +
|
title: application.configuration['application.title'] + " " +
|
||||||
metadata["application.version"] + " revision " + metadata["build.revision"],
|
metadata["application.version"] + " revision " + metadata["build.revision"],
|
||||||
iconImage: imageIcon('/griffon-icon-48x48.png').image,
|
iconImage: imageIcon('/MuWire-48x48.png').image,
|
||||||
iconImages: [imageIcon('/griffon-icon-48x48.png').image,
|
iconImages: [imageIcon('/MuWire-48x48.png').image,
|
||||||
imageIcon('/griffon-icon-32x32.png').image,
|
imageIcon('/MuWire-32x32.png').image,
|
||||||
imageIcon('/griffon-icon-16x16.png').image],
|
imageIcon('/MuWire-16x16.png').image],
|
||||||
pack : false,
|
pack : false,
|
||||||
visible : bind { model.coreInitialized }) {
|
visible : bind { model.coreInitialized }) {
|
||||||
menuBar {
|
menuBar {
|
||||||
@@ -135,8 +135,9 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
panel (constraints : BorderLayout.SOUTH) {
|
panel (constraints : BorderLayout.SOUTH) {
|
||||||
|
button(text: "Pause", enabled : bind {model.pauseButtonEnabled}, pauseAction)
|
||||||
button(text: "Cancel", enabled : bind {model.cancelButtonEnabled }, cancelAction )
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,11 +185,14 @@ class MainFrameView {
|
|||||||
closureColumn(header : "Name", type : String, read : {row -> row.getName() })
|
closureColumn(header : "Name", type : String, read : {row -> row.getName() })
|
||||||
closureColumn(header : "Progress", type : String, read : { row ->
|
closureColumn(header : "Progress", type : String, read : { row ->
|
||||||
int percent = row.getProgress()
|
int percent = row.getProgress()
|
||||||
"$percent%"
|
"$percent% of piece".toString()
|
||||||
})
|
})
|
||||||
closureColumn(header : "Downloader", type : String, read : { row ->
|
closureColumn(header : "Downloader", type : String, read : { row ->
|
||||||
row.getDownloader()
|
row.getDownloader()
|
||||||
})
|
})
|
||||||
|
closureColumn(header : "Remote Pieces", type : String, read : { row ->
|
||||||
|
"${row.getDonePieces()}/${row.getTotalPieces()}".toString()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,6 +299,7 @@ class MainFrameView {
|
|||||||
if (selectedRow < 0) {
|
if (selectedRow < 0) {
|
||||||
model.cancelButtonEnabled = false
|
model.cancelButtonEnabled = false
|
||||||
model.retryButtonEnabled = false
|
model.retryButtonEnabled = false
|
||||||
|
model.pauseButtonEnabled = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
def downloader = model.downloads[selectedRow]?.downloader
|
def downloader = model.downloads[selectedRow]?.downloader
|
||||||
@@ -305,15 +310,25 @@ class MainFrameView {
|
|||||||
case Downloader.DownloadState.DOWNLOADING :
|
case Downloader.DownloadState.DOWNLOADING :
|
||||||
case Downloader.DownloadState.HASHLIST:
|
case Downloader.DownloadState.HASHLIST:
|
||||||
model.cancelButtonEnabled = true
|
model.cancelButtonEnabled = true
|
||||||
|
model.pauseButtonEnabled = true
|
||||||
model.retryButtonEnabled = false
|
model.retryButtonEnabled = false
|
||||||
break
|
break
|
||||||
case Downloader.DownloadState.FAILED:
|
case Downloader.DownloadState.FAILED:
|
||||||
model.cancelButtonEnabled = true
|
model.cancelButtonEnabled = true
|
||||||
model.retryButtonEnabled = 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
|
break
|
||||||
default:
|
default:
|
||||||
model.cancelButtonEnabled = false
|
model.cancelButtonEnabled = false
|
||||||
model.retryButtonEnabled = false
|
model.retryButtonEnabled = false
|
||||||
|
model.pauseButtonEnabled = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -421,21 +436,32 @@ class MainFrameView {
|
|||||||
int selected = selectedDownloaderRow()
|
int selected = selectedDownloaderRow()
|
||||||
if (selected < 0)
|
if (selected < 0)
|
||||||
return
|
return
|
||||||
|
boolean pauseEnabled = false
|
||||||
boolean cancelEnabled = false
|
boolean cancelEnabled = false
|
||||||
boolean retryEnabled = false
|
boolean retryEnabled = false
|
||||||
|
String resumeText = "Retry"
|
||||||
Downloader downloader = model.downloads[selected].downloader
|
Downloader downloader = model.downloads[selected].downloader
|
||||||
switch(downloader.currentState) {
|
switch(downloader.currentState) {
|
||||||
case Downloader.DownloadState.DOWNLOADING:
|
case Downloader.DownloadState.DOWNLOADING:
|
||||||
case Downloader.DownloadState.HASHLIST:
|
case Downloader.DownloadState.HASHLIST:
|
||||||
case Downloader.DownloadState.CONNECTING:
|
case Downloader.DownloadState.CONNECTING:
|
||||||
|
pauseEnabled = true
|
||||||
cancelEnabled = true
|
cancelEnabled = true
|
||||||
retryEnabled = false
|
retryEnabled = false
|
||||||
break
|
break
|
||||||
case Downloader.DownloadState.FAILED:
|
case Downloader.DownloadState.FAILED:
|
||||||
|
pauseEnabled = false
|
||||||
cancelEnabled = true
|
cancelEnabled = true
|
||||||
retryEnabled = true
|
retryEnabled = true
|
||||||
break
|
break
|
||||||
|
case Downloader.DownloadState.PAUSED:
|
||||||
|
pauseEnabled = false
|
||||||
|
cancelEnabled = true
|
||||||
|
retryEnabled = true
|
||||||
|
resumeText = "Resume"
|
||||||
|
break
|
||||||
default :
|
default :
|
||||||
|
pauseEnabled = false
|
||||||
cancelEnabled = false
|
cancelEnabled = false
|
||||||
retryEnabled = false
|
retryEnabled = false
|
||||||
}
|
}
|
||||||
@@ -450,6 +476,12 @@ class MainFrameView {
|
|||||||
})
|
})
|
||||||
menu.add(copyHashToClipboard)
|
menu.add(copyHashToClipboard)
|
||||||
|
|
||||||
|
if (pauseEnabled) {
|
||||||
|
JMenuItem pause = new JMenuItem("Pause")
|
||||||
|
pause.addActionListener({mvcGroup.controller.pause()})
|
||||||
|
menu.add(pause)
|
||||||
|
}
|
||||||
|
|
||||||
if (cancelEnabled) {
|
if (cancelEnabled) {
|
||||||
JMenuItem cancel = new JMenuItem("Cancel")
|
JMenuItem cancel = new JMenuItem("Cancel")
|
||||||
cancel.addActionListener({mvcGroup.controller.cancel()})
|
cancel.addActionListener({mvcGroup.controller.cancel()})
|
||||||
@@ -457,7 +489,7 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (retryEnabled) {
|
if (retryEnabled) {
|
||||||
JMenuItem retry = new JMenuItem("Retry")
|
JMenuItem retry = new JMenuItem(resumeText)
|
||||||
retry.addActionListener({mvcGroup.controller.resume()})
|
retry.addActionListener({mvcGroup.controller.resume()})
|
||||||
menu.add(retry)
|
menu.add(retry)
|
||||||
}
|
}
|
||||||
@@ -487,6 +519,7 @@ class MainFrameView {
|
|||||||
|
|
||||||
def shareFiles = {
|
def shareFiles = {
|
||||||
def chooser = new JFileChooser()
|
def chooser = new JFileChooser()
|
||||||
|
chooser.setFileHidingEnabled(false)
|
||||||
chooser.setDialogTitle("Select file to share")
|
chooser.setDialogTitle("Select file to share")
|
||||||
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY)
|
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY)
|
||||||
int rv = chooser.showOpenDialog(null)
|
int rv = chooser.showOpenDialog(null)
|
||||||
@@ -497,6 +530,7 @@ class MainFrameView {
|
|||||||
|
|
||||||
def watchDirectories = {
|
def watchDirectories = {
|
||||||
def chooser = new JFileChooser()
|
def chooser = new JFileChooser()
|
||||||
|
chooser.setFileHidingEnabled(false)
|
||||||
chooser.setDialogTitle("Select directory to watch")
|
chooser.setDialogTitle("Select directory to watch")
|
||||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
|
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
|
||||||
int rv = chooser.showOpenDialog(null)
|
int rv = chooser.showOpenDialog(null)
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import javax.swing.JPanel
|
|||||||
import javax.swing.JTabbedPane
|
import javax.swing.JTabbedPane
|
||||||
import javax.swing.SwingConstants
|
import javax.swing.SwingConstants
|
||||||
|
|
||||||
|
import com.muwire.core.Core
|
||||||
|
|
||||||
import java.awt.BorderLayout
|
import java.awt.BorderLayout
|
||||||
import java.awt.event.WindowAdapter
|
import java.awt.event.WindowAdapter
|
||||||
import java.awt.event.WindowEvent
|
import java.awt.event.WindowEvent
|
||||||
@@ -26,6 +28,7 @@ class OptionsView {
|
|||||||
def p
|
def p
|
||||||
def i
|
def i
|
||||||
def u
|
def u
|
||||||
|
def bandwidth
|
||||||
|
|
||||||
def retryField
|
def retryField
|
||||||
def updateField
|
def updateField
|
||||||
@@ -45,6 +48,10 @@ class OptionsView {
|
|||||||
def excludeLocalResultCheckbox
|
def excludeLocalResultCheckbox
|
||||||
def showSearchHashesCheckbox
|
def showSearchHashesCheckbox
|
||||||
|
|
||||||
|
|
||||||
|
def inBwField
|
||||||
|
def outBwField
|
||||||
|
|
||||||
def buttonsPanel
|
def buttonsPanel
|
||||||
|
|
||||||
def mainFrame
|
def mainFrame
|
||||||
@@ -69,6 +76,10 @@ class OptionsView {
|
|||||||
label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3))
|
label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3))
|
||||||
shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, gridy:3))
|
shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, gridy:3))
|
||||||
|
|
||||||
|
label(text : "Save downloaded files to:", constraints: gbc(gridx:0, gridy:4))
|
||||||
|
button(text : "Choose", constraints : gbc(gridx : 1, gridy:4), downloadLocationAction)
|
||||||
|
label(text : bind {model.downloadLocation}, constraints: gbc(gridx:0, gridy:5, gridwidth:2))
|
||||||
|
|
||||||
}
|
}
|
||||||
i = builder.panel {
|
i = builder.panel {
|
||||||
gridBagLayout()
|
gridBagLayout()
|
||||||
@@ -89,17 +100,27 @@ class OptionsView {
|
|||||||
lnfField = textField(text : bind {model.lnf}, columns : 4, constraints : gbc(gridx : 1, gridy : 1))
|
lnfField = textField(text : bind {model.lnf}, columns : 4, constraints : gbc(gridx : 1, gridy : 1))
|
||||||
label(text : "Font", constraints : gbc(gridx: 0, gridy : 2))
|
label(text : "Font", constraints : gbc(gridx: 0, gridy : 2))
|
||||||
fontField = textField(text : bind {model.font}, columns : 4, constraints : gbc(gridx : 1, 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))
|
// label(text : "Show Monitor", constraints : gbc(gridx :0, gridy: 3))
|
||||||
monitorCheckbox = checkBox(selected : bind {model.showMonitor}, constraints : gbc(gridx : 1, 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))
|
label(text : "Clear Cancelled Downloads", constraints: gbc(gridx: 0, gridy:4))
|
||||||
clearCancelledDownloadsCheckbox = checkBox(selected : bind {model.clearCancelledDownloads}, constraints : gbc(gridx : 1, 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))
|
label(text : "Clear Finished Downloads", constraints: gbc(gridx: 0, gridy:5))
|
||||||
clearFinishedDownloadsCheckbox = checkBox(selected : bind {model.clearFinishedDownloads}, constraints : gbc(gridx : 1, 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))
|
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))
|
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))
|
// 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))
|
// showSearchHashesCheckbox = checkBox(selected : bind {model.showSearchHashes}, constraints : gbc(gridx: 1, gridy: 7))
|
||||||
}
|
}
|
||||||
|
bandwidth = builder.panel {
|
||||||
|
gridBagLayout()
|
||||||
|
label(text : "Changing these settings requires a restart", constraints : gbc(gridx : 0, gridy : 0, gridwidth: 2))
|
||||||
|
label(text : "Inbound bandwidth (KB)", constraints : gbc(gridx: 0, gridy : 1))
|
||||||
|
inBwField = textField(text : bind {model.inBw}, columns : 3, constraints : gbc(gridx : 1, gridy : 1))
|
||||||
|
label(text : "Outbound bandwidth (KB)", constraints : gbc(gridx: 0, gridy : 2))
|
||||||
|
outBwField = textField(text : bind {model.outBw}, columns : 3, constraints : gbc(gridx : 1, gridy : 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
buttonsPanel = builder.panel {
|
buttonsPanel = builder.panel {
|
||||||
gridBagLayout()
|
gridBagLayout()
|
||||||
button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
|
button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
|
||||||
@@ -112,6 +133,10 @@ class OptionsView {
|
|||||||
tabbedPane.addTab("MuWire", p)
|
tabbedPane.addTab("MuWire", p)
|
||||||
tabbedPane.addTab("I2P", i)
|
tabbedPane.addTab("I2P", i)
|
||||||
tabbedPane.addTab("GUI", u)
|
tabbedPane.addTab("GUI", u)
|
||||||
|
Core core = application.context.get("core")
|
||||||
|
if (core.router != null) {
|
||||||
|
tabbedPane.addTab("Bandwidth", bandwidth)
|
||||||
|
}
|
||||||
|
|
||||||
JPanel panel = new JPanel()
|
JPanel panel = new JPanel()
|
||||||
panel.setLayout(new BorderLayout())
|
panel.setLayout(new BorderLayout())
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ class UISettings {
|
|||||||
|
|
||||||
UISettings(Properties props) {
|
UISettings(Properties props) {
|
||||||
lnf = props.getProperty("lnf", "system")
|
lnf = props.getProperty("lnf", "system")
|
||||||
showMonitor = Boolean.parseBoolean(props.getProperty("showMonitor", "true"))
|
showMonitor = Boolean.parseBoolean(props.getProperty("showMonitor", "false"))
|
||||||
font = props.getProperty("font",null)
|
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"))
|
clearFinishedDownloads = Boolean.parseBoolean(props.getProperty("clearFinishedDownloads","false"))
|
||||||
excludeLocalResult = Boolean.parseBoolean(props.getProperty("excludeLocalResult","true"))
|
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 {
|
void write(OutputStream out) throws IOException {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ handlers= java.util.logging.FileHandler
|
|||||||
# can be overriden by a facility specific level
|
# can be overriden by a facility specific level
|
||||||
# Note that the ConsoleHandler also has a separate level
|
# Note that the ConsoleHandler also has a separate level
|
||||||
# setting to limit messages printed to the console.
|
# setting to limit messages printed to the console.
|
||||||
.level= SEVERE
|
.level= INFO
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
# Handler specific properties.
|
# Handler specific properties.
|
||||||
@@ -35,7 +35,7 @@ handlers= java.util.logging.FileHandler
|
|||||||
|
|
||||||
# default file output is in user's home directory.
|
# default file output is in user's home directory.
|
||||||
java.util.logging.FileHandler.pattern = MuWire.log
|
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.count = 1
|
||||||
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
|
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:
|
# messages:
|
||||||
com.xyz.foo.level = SEVERE
|
com.xyz.foo.level = SEVERE
|
||||||
com.muwire.core.level = FINE
|
com.muwire.core.level = FINE
|
||||||
|
net.i2p.client.streaming.impl.level = FINE
|
||||||
|
net.i2p.client.impl.level = FINE
|
||||||
|
|||||||