Compare commits
23 Commits
file-monit
...
muwire-0.2
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2825a8d9a4 | ||
![]() |
8dcce9bda6 | ||
![]() |
d8d3e2cd58 | ||
![]() |
51d5dbe47e | ||
![]() |
84cee0aa43 | ||
![]() |
162844787f | ||
![]() |
d8a2b59055 | ||
![]() |
67a0939de4 | ||
![]() |
37ca922a2c | ||
![]() |
1d6781819b | ||
![]() |
64d45da94a | ||
![]() |
59c84d8a5e | ||
![]() |
8b55021a4b | ||
![]() |
8bd3ebfaf5 | ||
![]() |
526ec45da3 | ||
![]() |
deb7c0b4b0 | ||
![]() |
e85a0c7b2c | ||
![]() |
7b021a47eb | ||
![]() |
0c21d4d6c1 | ||
![]() |
8e9f79d404 | ||
![]() |
bf33a6ff61 | ||
![]() |
19c8d84afd | ||
![]() |
0c1008d6b3 |
@@ -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.
|
||||
|
||||
The current stable release - 0.1.5 is avaiable for download at http://muwire.com. You can find technical documentation in the "doc" folder.
|
||||
The current stable release - 0.2.5 is avaiable for download at http://muwire.com. You can find technical documentation in the "doc" folder.
|
||||
|
||||
### Building
|
||||
|
||||
@@ -23,7 +23,7 @@ Some of the UI tests will fail because they haven't been written yet :-/
|
||||
|
||||
### Running
|
||||
|
||||
You need to have an I2P router up and running on the same machine. After you build the application, look inside "gui/build/distributions". Untar/unzip one of the "shadow" files and then run the jar contained inside by typing "java -jar MuWire-x.y.z.jar" in a terminal or command prompt. If you use a custom I2CP host and port, create a file $HOME/.MuWire/i2p.properties and put "i2cp.tcp.host=<host>" and "i2cp.tcp.post=<port>" in there.
|
||||
You need to have an I2P router up and running on the same machine. After you build the application, look inside "gui/build/distributions". Untar/unzip one of the "shadow" files and then run the jar contained inside by typing "java -jar MuWire-x.y.z.jar" in a terminal or command prompt. If you use a custom I2CP host and port, create a file $HOME/.MuWire/i2p.properties and put "i2cp.tcp.host=<host>" and "i2cp.tcp.port=<port>" in there.
|
||||
|
||||
The first time you run MuWire it will ask you to select a nickname. This nickname will be displayed with search results, so that others can verify the file was shared by you. It is best to leave MuWire running all the time, just like I2P.
|
||||
|
||||
|
5
TODO.md
5
TODO.md
@@ -32,6 +32,10 @@ For ease of deployment for new users, and so that users do not need to run a sep
|
||||
|
||||
Basically any non-gui non-cli user interface
|
||||
|
||||
##### Metadata editing and search
|
||||
|
||||
To enable parsing of metadata from known file types and the user editing it or adding manual metadata
|
||||
|
||||
### Small Items
|
||||
|
||||
* Detect if router is dead and show warning or exit
|
||||
@@ -39,4 +43,3 @@ Basically any non-gui non-cli user interface
|
||||
* Download file sequentially
|
||||
* Unsharing of files
|
||||
* Multiple-selection download, Ctrl-A
|
||||
* Automatic sharing of new files in shared directories (more like medium item)
|
||||
|
@@ -35,7 +35,7 @@ class Cli {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.2.5")
|
||||
core = new Core(props, home, "0.2.10")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
@@ -73,7 +73,7 @@ class Cli {
|
||||
|
||||
Timer timer = new Timer("status-printer", true)
|
||||
timer.schedule({
|
||||
println "Connections $connectionsListener.connections Uploads $uploadsListener.uploads Shared $sharedListener.shared"
|
||||
println String.valueOf(new Date()) + " Connections $connectionsListener.connections Uploads $uploadsListener.uploads Shared $sharedListener.shared"
|
||||
} as TimerTask, 60000, 60000)
|
||||
|
||||
def latch = new CountDownLatch(1)
|
||||
@@ -119,11 +119,11 @@ class Cli {
|
||||
volatile int uploads
|
||||
public void onUploadEvent(UploadEvent e) {
|
||||
uploads++
|
||||
println "Starting upload of ${e.uploader.file.getName()} to ${e.uploader.request.downloader.getHumanReadableName()}"
|
||||
println String.valueOf(new Date()) + " Starting upload of ${e.uploader.file.getName()} to ${e.uploader.request.downloader.getHumanReadableName()}"
|
||||
}
|
||||
public void onUploadFinishedEvent(UploadFinishedEvent e) {
|
||||
uploads--
|
||||
println "Finished upload of ${e.uploader.file.getName()} to ${e.uploader.request.downloader.getHumanReadableName()}"
|
||||
println String.valueOf(new Date()) + " Finished upload of ${e.uploader.file.getName()} to ${e.uploader.request.downloader.getHumanReadableName()}"
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -53,7 +53,7 @@ class CliDownloader {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.2.5")
|
||||
core = new Core(props, home, "0.2.10")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
|
23
cli/src/main/groovy/com/muwire/cli/FileList.groovy
Normal file
23
cli/src/main/groovy/com/muwire/cli/FileList.groovy
Normal file
@@ -0,0 +1,23 @@
|
||||
package com.muwire.cli
|
||||
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import net.i2p.data.Base64
|
||||
|
||||
class FileList {
|
||||
public static void main(String [] args) {
|
||||
if (args.length < 1) {
|
||||
println "pass files.json as argument"
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
def slurper = new JsonSlurper()
|
||||
File filesJson = new File(args[0])
|
||||
filesJson.eachLine {
|
||||
def json = slurper.parseText(it)
|
||||
String name = DataUtil.readi18nString(Base64.decode(json.file))
|
||||
println "$name,$json.length,$json.pieceSize,$json.infoHash"
|
||||
}
|
||||
}
|
||||
}
|
@@ -72,6 +72,7 @@ public class Core {
|
||||
private final HasherService hasherService
|
||||
private final DownloadManager downloadManager
|
||||
private final DirectoryWatcher directoryWatcher
|
||||
final FileManager fileManager
|
||||
|
||||
public Core(MuWireSettings props, File home, String myVersion) {
|
||||
this.home = home
|
||||
@@ -155,7 +156,7 @@ public class Core {
|
||||
|
||||
|
||||
log.info "initializing file manager"
|
||||
FileManager fileManager = new FileManager(eventBus, props)
|
||||
fileManager = new FileManager(eventBus, props)
|
||||
eventBus.register(FileHashedEvent.class, fileManager)
|
||||
eventBus.register(FileLoadedEvent.class, fileManager)
|
||||
eventBus.register(FileDownloadedEvent.class, fileManager)
|
||||
@@ -276,7 +277,7 @@ public class Core {
|
||||
}
|
||||
}
|
||||
|
||||
Core core = new Core(props, home, "0.2.5")
|
||||
Core core = new Core(props, home, "0.2.10")
|
||||
core.startServices()
|
||||
|
||||
// ... at the end, sleep or execute script
|
||||
|
@@ -30,7 +30,7 @@ class MuWireSettings {
|
||||
nickname = props.getProperty("nickname","MuWireUser")
|
||||
downloadLocation = new File((String)props.getProperty("downloadLocation",
|
||||
System.getProperty("user.home")))
|
||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","15"))
|
||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","5"))
|
||||
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","36"))
|
||||
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
|
||||
|
||||
|
@@ -58,6 +58,8 @@ public class DownloadManager {
|
||||
e.result.each {
|
||||
destinations.add(it.sender.destination)
|
||||
}
|
||||
destinations.addAll(e.sources)
|
||||
destinations.remove(me.destination)
|
||||
|
||||
def downloader = new Downloader(eventBus, this, me, e.target, size,
|
||||
infohash, pieceSize, connector, destinations,
|
||||
|
@@ -85,7 +85,7 @@ class DownloadSession {
|
||||
if (code.startsWith("404 ")) {
|
||||
log.warning("file not found")
|
||||
endpoint.close()
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
if (code.startsWith("416 ")) {
|
||||
|
@@ -21,6 +21,7 @@ import com.muwire.core.files.FileDownloadedEvent
|
||||
|
||||
import groovy.util.logging.Log
|
||||
import net.i2p.data.Destination
|
||||
import net.i2p.util.ConcurrentHashSet
|
||||
|
||||
@Log
|
||||
public class Downloader {
|
||||
@@ -49,6 +50,7 @@ public class Downloader {
|
||||
private final File incompleteFile
|
||||
final int pieceSizePow2
|
||||
private final Map<Destination, DownloadWorker> activeWorkers = new ConcurrentHashMap<>()
|
||||
private final Set<Destination> successfulDestinations = new ConcurrentHashSet<>()
|
||||
|
||||
|
||||
private volatile boolean cancelled
|
||||
@@ -249,6 +251,7 @@ public class Downloader {
|
||||
requestPerformed = currentSession.request()
|
||||
if (!requestPerformed)
|
||||
break
|
||||
successfulDestinations.add(endpoint.destination)
|
||||
writePieces()
|
||||
}
|
||||
} catch (Exception bad) {
|
||||
@@ -268,7 +271,7 @@ public class Downloader {
|
||||
}
|
||||
eventBus.publish(
|
||||
new FileDownloadedEvent(
|
||||
downloadedFile : new DownloadedFile(file, getInfoHash(), pieceSizePow2, Collections.emptySet()),
|
||||
downloadedFile : new DownloadedFile(file, getInfoHash(), pieceSizePow2, successfulDestinations),
|
||||
downloader : Downloader.this))
|
||||
|
||||
}
|
||||
|
@@ -3,8 +3,11 @@ package com.muwire.core.download
|
||||
import com.muwire.core.Event
|
||||
import com.muwire.core.search.UIResultEvent
|
||||
|
||||
import net.i2p.data.Destination
|
||||
|
||||
class UIDownloadEvent extends Event {
|
||||
|
||||
UIResultEvent[] result
|
||||
Set<Destination> sources
|
||||
File target
|
||||
}
|
||||
|
@@ -5,6 +5,8 @@ import java.nio.file.FileSystems
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import static java.nio.file.StandardWatchEventKinds.*
|
||||
|
||||
import java.nio.file.ClosedWatchServiceException
|
||||
import java.nio.file.WatchEvent
|
||||
import java.nio.file.WatchKey
|
||||
import java.nio.file.WatchService
|
||||
@@ -79,7 +81,7 @@ class DirectoryWatcher {
|
||||
}
|
||||
key.reset()
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InterruptedException|ClosedWatchServiceException e) {
|
||||
if (!shutdown)
|
||||
throw e
|
||||
}
|
||||
@@ -91,6 +93,8 @@ class DirectoryWatcher {
|
||||
log.fine("created entry $f")
|
||||
if (f.isDirectory())
|
||||
f.toPath().register(watchService, kinds)
|
||||
else
|
||||
waitingFiles.put(f, System.currentTimeMillis())
|
||||
}
|
||||
|
||||
private void processModified(Path parent, Path path) {
|
||||
|
@@ -5,9 +5,11 @@ import net.i2p.data.Destination
|
||||
class Host {
|
||||
|
||||
private static final int MAX_FAILURES = 3
|
||||
private static final int CLEAR_INTERVAL = 60 * 60 * 1000
|
||||
|
||||
final Destination destination
|
||||
int failures,successes
|
||||
long lastAttempt
|
||||
|
||||
public Host(Destination destination) {
|
||||
this.destination = destination
|
||||
@@ -16,11 +18,13 @@ class Host {
|
||||
synchronized void onConnect() {
|
||||
failures = 0
|
||||
successes++
|
||||
lastAttempt = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
synchronized void onFailure() {
|
||||
failures++
|
||||
successes = 0
|
||||
lastAttempt = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
synchronized boolean isFailed() {
|
||||
@@ -34,4 +38,8 @@ class Host {
|
||||
synchronized void clearFailures() {
|
||||
failures = 0
|
||||
}
|
||||
|
||||
synchronized void canTryAgain() {
|
||||
System.currentTimeMillis() - lastAttempt > CLEAR_INTERVAL
|
||||
}
|
||||
}
|
||||
|
@@ -109,6 +109,8 @@ class HostCache extends Service {
|
||||
Host host = new Host(dest)
|
||||
host.failures = Integer.valueOf(String.valueOf(entry.failures))
|
||||
host.successes = Integer.valueOf(String.valueOf(entry.successes))
|
||||
if (entry.lastAttempt != null)
|
||||
host.lastAttempt = entry.lastAttempt
|
||||
if (allowHost(host))
|
||||
hosts.put(dest, host)
|
||||
}
|
||||
@@ -118,7 +120,7 @@ class HostCache extends Service {
|
||||
}
|
||||
|
||||
private boolean allowHost(Host host) {
|
||||
if (host.isFailed())
|
||||
if (host.isFailed() && !host.canTryAgain())
|
||||
return false
|
||||
if (host.destination == myself)
|
||||
return false
|
||||
@@ -143,6 +145,7 @@ class HostCache extends Service {
|
||||
map.destination = dest.toBase64()
|
||||
map.failures = host.failures
|
||||
map.successes = host.successes
|
||||
map.lastAttempt = host.lastAttempt
|
||||
def json = JsonOutput.toJson(map)
|
||||
writer.println json
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package com.muwire.core.search
|
||||
|
||||
import java.util.stream.Collectors
|
||||
|
||||
import javax.naming.directory.InvalidSearchControlsException
|
||||
|
||||
import com.muwire.core.InfoHash
|
||||
@@ -7,6 +9,7 @@ import com.muwire.core.Persona
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import net.i2p.data.Base64
|
||||
import net.i2p.data.Destination
|
||||
|
||||
class ResultsParser {
|
||||
public static UIResultEvent parse(Persona p, UUID uuid, def json) throws InvalidSearchResultException {
|
||||
@@ -58,6 +61,7 @@ class ResultsParser {
|
||||
size : size,
|
||||
infohash : parsedIH,
|
||||
pieceSize : pieceSize,
|
||||
sources : Collections.emptySet(),
|
||||
uuid : uuid)
|
||||
} catch (Exception e) {
|
||||
throw new InvalidSearchResultException("parsing search result failed",e)
|
||||
@@ -82,11 +86,17 @@ class ResultsParser {
|
||||
if (infoHash.length != InfoHash.SIZE)
|
||||
throw new InvalidSearchResultException("invalid infohash size $infoHash.length")
|
||||
int pieceSize = json.pieceSize
|
||||
|
||||
Set<Destination> sources = Collections.emptySet()
|
||||
if (json.sources != null)
|
||||
sources = json.sources.stream().map({new Destination(it)}).collect(Collectors.toSet())
|
||||
|
||||
return new UIResultEvent( sender : p,
|
||||
name : name,
|
||||
size : size,
|
||||
infohash : new InfoHash(infoHash),
|
||||
pieceSize : pieceSize,
|
||||
sources : sources,
|
||||
uuid: uuid)
|
||||
} catch (Exception e) {
|
||||
throw new InvalidSearchResultException("parsing search result failed",e)
|
||||
|
@@ -11,7 +11,9 @@ import java.util.concurrent.Executor
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ThreadFactory
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.stream.Collectors
|
||||
|
||||
import com.muwire.core.DownloadedFile
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.InfoHash
|
||||
|
||||
@@ -54,12 +56,16 @@ class ResultsSender {
|
||||
int pieceSize = it.getPieceSize()
|
||||
if (pieceSize == 0)
|
||||
pieceSize = FileHasher.getPieceSize(length)
|
||||
Set<Destination> suggested = Collections.emptySet()
|
||||
if (it instanceof DownloadedFile)
|
||||
suggested = it.sources
|
||||
def uiResultEvent = new UIResultEvent( sender : me,
|
||||
name : it.getFile().getName(),
|
||||
size : length,
|
||||
infohash : it.getInfoHash(),
|
||||
pieceSize : pieceSize,
|
||||
uuid : uuid
|
||||
uuid : uuid,
|
||||
sources : suggested
|
||||
)
|
||||
eventBus.publish(uiResultEvent)
|
||||
}
|
||||
@@ -110,6 +116,10 @@ class ResultsSender {
|
||||
}
|
||||
obj.hashList = hashListB64
|
||||
}
|
||||
|
||||
if (it instanceof DownloadedFile)
|
||||
obj.sources = it.sources.stream().map({dest -> dest.toBase64()}).collect(Collectors.toSet())
|
||||
|
||||
def json = jsonOutput.toJson(obj)
|
||||
os.writeShort((short)json.length())
|
||||
os.write(json.getBytes(StandardCharsets.US_ASCII))
|
||||
|
@@ -4,8 +4,11 @@ import com.muwire.core.Event
|
||||
import com.muwire.core.InfoHash
|
||||
import com.muwire.core.Persona
|
||||
|
||||
import net.i2p.data.Destination
|
||||
|
||||
class UIResultEvent extends Event {
|
||||
Persona sender
|
||||
Set<Destination> sources
|
||||
UUID uuid
|
||||
String name
|
||||
long size
|
||||
|
@@ -15,7 +15,7 @@ class DownloadSessionTest {
|
||||
private File source, target
|
||||
private InfoHash infoHash
|
||||
private Endpoint endpoint
|
||||
private Pieces pieces, claimed
|
||||
private Pieces pieces
|
||||
private String rootBase64
|
||||
|
||||
private DownloadSession session
|
||||
@@ -48,8 +48,7 @@ class DownloadSessionTest {
|
||||
else
|
||||
nPieces = size / pieceSize + 1
|
||||
pieces = new Pieces(nPieces)
|
||||
claimed = new Pieces(nPieces)
|
||||
claimedPieces.each {claimed.markDownloaded(it)}
|
||||
claimedPieces.each {pieces.claimed.set(it)}
|
||||
|
||||
fromDownloader = new PipedInputStream()
|
||||
fromUploader = new PipedInputStream()
|
||||
@@ -57,7 +56,7 @@ class DownloadSessionTest {
|
||||
toUploader = new PipedOutputStream(fromDownloader)
|
||||
endpoint = new Endpoint(null, fromUploader, toUploader, null)
|
||||
|
||||
session = new DownloadSession("",pieces, claimed, infoHash, endpoint, target, pieceSize, size)
|
||||
session = new DownloadSession("",pieces, infoHash, endpoint, target, pieceSize, size)
|
||||
downloadThread = new Thread( { session.request() } as Runnable)
|
||||
downloadThread.setDaemon(true)
|
||||
downloadThread.start()
|
||||
@@ -154,7 +153,7 @@ class DownloadSessionTest {
|
||||
int pieceSize = FileHasher.getPieceSize(1)
|
||||
int size = (1 << pieceSize) * 10
|
||||
initSession(size, [1,2,3,4,5,6,7,8,9])
|
||||
assert !claimed.isMarked(0)
|
||||
assert !pieces.claimed.get(0)
|
||||
|
||||
assert "GET $rootBase64" == readTillRN(fromDownloader)
|
||||
String range = readTillRN(fromDownloader)
|
||||
@@ -162,7 +161,7 @@ class DownloadSessionTest {
|
||||
int start = Integer.parseInt(matcher[0][1])
|
||||
int end = Integer.parseInt(matcher[0][2])
|
||||
|
||||
assert claimed.isMarked(0)
|
||||
assert pieces.claimed.get(0)
|
||||
assert start == 0 && end == (1 << pieceSize) - 1
|
||||
}
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ class PiecesTest {
|
||||
public void testSinglePiece() {
|
||||
pieces = new Pieces(1)
|
||||
assert !pieces.isComplete()
|
||||
assert pieces.getRandomPiece() == 0
|
||||
assert pieces.claim() == 0
|
||||
pieces.markDownloaded(0)
|
||||
assert pieces.isComplete()
|
||||
}
|
||||
@@ -25,11 +25,11 @@ class PiecesTest {
|
||||
public void testTwoPieces() {
|
||||
pieces = new Pieces(2)
|
||||
assert !pieces.isComplete()
|
||||
int piece = pieces.getRandomPiece()
|
||||
int piece = pieces.claim()
|
||||
assert piece == 0 || piece == 1
|
||||
pieces.markDownloaded(piece)
|
||||
assert !pieces.isComplete()
|
||||
int piece2 = pieces.getRandomPiece()
|
||||
int piece2 = pieces.claim()
|
||||
assert piece != piece2
|
||||
pieces.markDownloaded(piece2)
|
||||
assert pieces.isComplete()
|
||||
|
@@ -26,7 +26,7 @@ class FileHasherTest extends GroovyTestCase {
|
||||
void testPieceSize() {
|
||||
assert 17 == FileHasher.getPieceSize(1000000)
|
||||
assert 17 == FileHasher.getPieceSize(100000000)
|
||||
assert 27 == FileHasher.getPieceSize(FileHasher.MAX_SIZE)
|
||||
assert 24 == FileHasher.getPieceSize(FileHasher.MAX_SIZE)
|
||||
shouldFail IllegalArgumentException, {
|
||||
FileHasher.getPieceSize(Long.MAX_VALUE)
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ class HasherServiceTest {
|
||||
hasher = new FileHasher()
|
||||
service = new HasherService(hasher, eventBus, new FileManager(eventBus, new MuWireSettings()))
|
||||
eventBus.register(FileHashedEvent.class, listener)
|
||||
eventBus.register(FileSharedEvent.class, service)
|
||||
service.start()
|
||||
}
|
||||
|
||||
|
@@ -78,7 +78,7 @@ class PersisterServiceLoadingTest {
|
||||
persisted.write json
|
||||
|
||||
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(2000)
|
||||
|
||||
assert listener.publishedFiles.size() == 1
|
||||
@@ -121,7 +121,7 @@ class PersisterServiceLoadingTest {
|
||||
persisted.write json
|
||||
|
||||
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(2000)
|
||||
|
||||
assert listener.publishedFiles.size() == 1
|
||||
@@ -163,7 +163,7 @@ class PersisterServiceLoadingTest {
|
||||
persisted.append "$json2\n"
|
||||
|
||||
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(2000)
|
||||
|
||||
assert listener.publishedFiles.size() == 2
|
||||
@@ -195,7 +195,7 @@ class PersisterServiceLoadingTest {
|
||||
persisted.write json1
|
||||
|
||||
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(2000)
|
||||
|
||||
assert listener.publishedFiles.size() == 1
|
||||
|
@@ -58,7 +58,7 @@ class PersisterServiceSavingTest {
|
||||
sf = new SharedFile(f, ih, 0)
|
||||
|
||||
ps = new PersisterService(persisted, eventBus, 100, fileSource)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(1500)
|
||||
|
||||
JsonSlurper jsonSlurper = new JsonSlurper()
|
||||
@@ -77,7 +77,7 @@ class PersisterServiceSavingTest {
|
||||
sf = new DownloadedFile(f, ih, 0, new HashSet([dests.dest1, dests.dest2]))
|
||||
|
||||
ps = new PersisterService(persisted, eventBus, 100, fileSource)
|
||||
ps.start()
|
||||
ps.onUILoadedEvent(null)
|
||||
Thread.sleep(1500)
|
||||
|
||||
JsonSlurper jsonSlurper = new JsonSlurper()
|
||||
|
@@ -9,18 +9,18 @@ import com.muwire.core.InfoHash
|
||||
|
||||
class RequestParsingTest {
|
||||
|
||||
Request request
|
||||
ContentRequest request
|
||||
|
||||
private void fromString(String requestString) {
|
||||
def is = new ByteArrayInputStream(requestString.getBytes(StandardCharsets.US_ASCII))
|
||||
request = Request.parse(new InfoHash(new byte[InfoHash.SIZE]), is)
|
||||
request = Request.parseContentRequest(new InfoHash(new byte[InfoHash.SIZE]), is)
|
||||
}
|
||||
|
||||
|
||||
private static void failed(String requestString) {
|
||||
try {
|
||||
def is = new ByteArrayInputStream(requestString.getBytes(StandardCharsets.US_ASCII))
|
||||
Request.parse(new InfoHash(new byte[InfoHash.SIZE]), is)
|
||||
Request.parseContentRequest(new InfoHash(new byte[InfoHash.SIZE]), is)
|
||||
assert false
|
||||
} catch (IOException expected) {}
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ class UploaderTest {
|
||||
InputStream is
|
||||
OutputStream os
|
||||
|
||||
Request request
|
||||
ContentRequest request
|
||||
Uploader uploader
|
||||
|
||||
byte[] inFile
|
||||
@@ -52,7 +52,7 @@ class UploaderTest {
|
||||
}
|
||||
|
||||
private void startUpload() {
|
||||
uploader = new Uploader(file, request, endpoint)
|
||||
uploader = new ContentUploader(file, request, endpoint)
|
||||
uploadThread = new Thread(uploader.respond() as Runnable)
|
||||
uploadThread.setDaemon(true)
|
||||
uploadThread.start()
|
||||
@@ -77,7 +77,7 @@ class UploaderTest {
|
||||
@Test
|
||||
public void testSmallFile() {
|
||||
fillFile(20)
|
||||
request = new Request(range : new Range(0,19))
|
||||
request = new ContentRequest(range : new Range(0,19))
|
||||
startUpload()
|
||||
assert "200 OK" == readUntilRN()
|
||||
assert "Content-Range: 0-19" == readUntilRN()
|
||||
@@ -92,7 +92,7 @@ class UploaderTest {
|
||||
@Test
|
||||
public void testRequestMiddle() {
|
||||
fillFile(20)
|
||||
request = new Request(range : new Range(5,15))
|
||||
request = new ContentRequest(range : new Range(5,15))
|
||||
startUpload()
|
||||
assert "200 OK" == readUntilRN()
|
||||
assert "Content-Range: 5-15" == readUntilRN()
|
||||
@@ -108,7 +108,7 @@ class UploaderTest {
|
||||
@Test
|
||||
public void testOutOfRange() {
|
||||
fillFile(20)
|
||||
request = new Request(range : new Range(0,20))
|
||||
request = new ContentRequest(range : new Range(0,20))
|
||||
startUpload()
|
||||
assert "416 Range Not Satisfiable" == readUntilRN()
|
||||
assert "" == readUntilRN()
|
||||
@@ -118,7 +118,7 @@ class UploaderTest {
|
||||
public void testLargeFile() {
|
||||
final int length = 0x1 << 14
|
||||
fillFile(length)
|
||||
request = new Request(range : new Range(0, length - 1))
|
||||
request = new ContentRequest(range : new Range(0, length - 1))
|
||||
startUpload()
|
||||
readUntilRN()
|
||||
readUntilRN()
|
||||
|
@@ -1,5 +1,8 @@
|
||||
group = com.muwire
|
||||
version = 0.2.5
|
||||
version = 0.2.10
|
||||
groovyVersion = 2.4.15
|
||||
slf4jVersion = 1.7.25
|
||||
spockVersion = 1.1-groovy-2.4
|
||||
|
||||
sourceCompatibility=1.8
|
||||
targetCompatibility=1.8
|
||||
|
@@ -129,8 +129,9 @@ class MainFrameController {
|
||||
def group = selected.getClientProperty("mvc-group")
|
||||
|
||||
def resultsBucket = group.model.hashBucket[result.infohash]
|
||||
def sources = group.model.sourcesBucket[result.infohash]
|
||||
|
||||
core.eventBus.publish(new UIDownloadEvent(result : resultsBucket, target : file))
|
||||
core.eventBus.publish(new UIDownloadEvent(result : resultsBucket, sources: sources, target : file))
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
|
@@ -282,8 +282,10 @@ class MainFrameModel {
|
||||
updateTablePreservingSelection("trusted-table")
|
||||
updateTablePreservingSelection("distrusted-table")
|
||||
|
||||
results.values().each {
|
||||
it.view.pane.getClientProperty("results-table")?.model.fireTableDataChanged()
|
||||
results.values().each { MVCGroup group ->
|
||||
if (group.alive) {
|
||||
group.view.pane.getClientProperty("results-table")?.model.fireTableDataChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ class SearchTabModel {
|
||||
String uuid
|
||||
def results = []
|
||||
def hashBucket = [:]
|
||||
def sourcesBucket = [:]
|
||||
|
||||
|
||||
void mvcGroupInit(Map<String, String> args) {
|
||||
@@ -37,7 +38,7 @@ class SearchTabModel {
|
||||
|
||||
void handleResult(UIResultEvent e) {
|
||||
if (uiSettings.excludeLocalResult &&
|
||||
e.sender == core.me)
|
||||
core.fileManager.rootToFiles.containsKey(e.infohash))
|
||||
return
|
||||
runInsideUIAsync {
|
||||
def bucket = hashBucket.get(e.infohash)
|
||||
@@ -46,6 +47,13 @@ class SearchTabModel {
|
||||
hashBucket[e.infohash] = bucket
|
||||
}
|
||||
bucket << e
|
||||
|
||||
Set sourceBucket = sourcesBucket.get(e.infohash)
|
||||
if (sourceBucket == null) {
|
||||
sourceBucket = new HashSet()
|
||||
sourcesBucket.put(e.infohash, sourceBucket)
|
||||
}
|
||||
sourceBucket.addAll(e.sources)
|
||||
|
||||
results << e
|
||||
JTable table = builder.getVariable("results-table")
|
||||
@@ -56,13 +64,22 @@ class SearchTabModel {
|
||||
void handleResultBatch(UIResultEvent[] batch) {
|
||||
runInsideUIAsync {
|
||||
batch.each {
|
||||
if (uiSettings.excludeLocalResult && it.sender == core.me)
|
||||
if (uiSettings.excludeLocalResult &&
|
||||
core.fileManager.rootToFiles.containsKey(it.infohash))
|
||||
return
|
||||
def bucket = hashBucket.get(it.infohash)
|
||||
if (bucket == null) {
|
||||
bucket = []
|
||||
hashBucket[it.infohash] = bucket
|
||||
}
|
||||
|
||||
Set sourceBucket = sourcesBucket.get(it.infohash)
|
||||
if (sourceBucket == null) {
|
||||
sourceBucket = new HashSet()
|
||||
sourcesBucket.put(it.infohash, sourceBucket)
|
||||
}
|
||||
sourceBucket.addAll(it.sources)
|
||||
|
||||
bucket << it
|
||||
results << it
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package com.muwire.gui
|
||||
|
||||
import griffon.core.artifact.GriffonView
|
||||
import griffon.core.env.Metadata
|
||||
import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
import net.i2p.data.Base64
|
||||
@@ -37,6 +38,7 @@ import java.awt.event.MouseEvent
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
import javax.annotation.Nonnull
|
||||
import javax.inject.Inject
|
||||
|
||||
@ArtifactProviderFor(GriffonView)
|
||||
class MainFrameView {
|
||||
@@ -45,6 +47,8 @@ class MainFrameView {
|
||||
@MVCMember @Nonnull
|
||||
MainFrameModel model
|
||||
|
||||
@Inject Metadata metadata
|
||||
|
||||
def downloadsTable
|
||||
def lastDownloadSortEvent
|
||||
def lastSharedSortEvent
|
||||
@@ -54,7 +58,7 @@ class MainFrameView {
|
||||
builder.with {
|
||||
application(size : [1024,768], id: 'main-frame',
|
||||
locationRelativeTo : null,
|
||||
title: application.configuration['application.title'],
|
||||
title: application.configuration['application.title'] + " " + metadata["application.version"],
|
||||
iconImage: imageIcon('/griffon-icon-48x48.png').image,
|
||||
iconImages: [imageIcon('/griffon-icon-48x48.png').image,
|
||||
imageIcon('/griffon-icon-32x32.png').image,
|
||||
|
@@ -47,8 +47,9 @@ class SearchTabView {
|
||||
resultsTable = table(id : "results-table", autoCreateRowSorter : true) {
|
||||
tableModel(list: model.results) {
|
||||
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.name.replace('<','_')})
|
||||
closureColumn(header: "Size", preferredWidth: 50, type: Long, read : {row -> row.size})
|
||||
closureColumn(header: "Sources", preferredWidth: 10, type : Integer, read : { row -> model.hashBucket[row.infohash].size()})
|
||||
closureColumn(header: "Size", preferredWidth: 20, type: Long, read : {row -> row.size})
|
||||
closureColumn(header: "Direct Sources", preferredWidth: 50, type : Integer, read : { row -> model.hashBucket[row.infohash].size()})
|
||||
closureColumn(header: "Possible Sources", preferredWidth : 50, type : Integer, read : {row -> model.sourcesBucket[row.infohash].size()})
|
||||
closureColumn(header: "Sender", preferredWidth: 170, type: String, read : {row -> row.sender.getHumanReadableName()})
|
||||
closureColumn(header: "Trust", preferredWidth: 50, type: String, read : {row ->
|
||||
model.core.trustService.getLevel(row.sender.destination).toString()
|
||||
|
Reference in New Issue
Block a user