-
Notifications
You must be signed in to change notification settings - Fork 389
Description
Let's assume we have some torrent containing N pieces and we want to download only first 5 of them using the customized PieceSelector and the following code:
int[] piecesToDownload = new int[]{0,1,2,3,4};
final PieceSelector pieceSelector = new PieceSelector() {
@Override
public IntStream getNextPieces(BitSet bitSet, PieceStatistics pieceStatistics) {
return IntStream.of(piecesToDownload);
}
};
Torrent torrent;
final int selectedFileIndex = 0;
final FilePrioritySkipSelector filePrioritySkipSelector =
torrentFile -> {
if (torrent != null) {
return torrent.getFiles().indexOf(torrentFile) == selectedFileIndex
? FilePriority.HIGH_PRIORITY
: FilePriority.SKIP;
} else return FilePriority.SKIP;
};
BtRuntime createRuntime() {
try {
Config config = new Config() {
@Override
public EncryptionPolicy getEncryptionPolicy() {
return EncryptionPolicy.PREFER_ENCRYPTED;
}
@Override
public int getNumOfHashingThreads() {
return Runtime.getRuntime().availableProcessors();
}
@Override
public int getAcceptorPort() {
return 6891;
}
};
Module dhtModule = new DHTModule(new DHTConfig() {
@Override
public boolean shouldUseRouterBootstrap() {
return true;
}
});
return BtRuntime.builder(config)
.autoLoadModules()
.module(dhtModule)
.module(new UpnpPortMapperModule())
.module(new PeerExchangeModule())
.module(new HttpTrackerModule())
.build();
} catch (Exception e) {
System.out.println("Error creating BT runtime "+e);
return null;
}
}
BtClient createClient(URI uri, BtRuntime runtime, Storage storage,
FilePrioritySkipSelector filePrioritySkipSelector,
PieceSelector pieceSelector) {
try {
return Bt.client(runtime)
.storage(storage)
.torrent(uri.toURL())
.afterTorrentFetched(t->{torrent = t;})
.fileSelector(filePrioritySkipSelector)
.selector(pieceSelector)
.stopWhenDownloaded()
.initEagerly()
.build();
} catch (Exception e) {
System.out.println("Error creating BT client "+ e);
return null;
}
}
Storage storage = new FileSystemStorage(Paths.get(context.getFilesDir().getAbsolutePath()+"/tordl"));
void startDownload(URI uri) {
System.out.println("Creating BT runtime...");
BtRuntime runtime = createRuntime();
System.out.println("Creating client...");
BtClient client = createClient(uri, runtime, storage, filePrioritySkipSelector, pieceSelector);
System.out.println("Starting download...");
client.startAsync(state -> {
final int activePeers = state.getConnectedPeers().size();
final int piecesComplete = state.getPiecesComplete();
System.out.println("Peers: " + activePeers
+ "; Downloaded: " + piecesComplete + " pieces, current range: "
+ Arrays.toString(piecesToDownload));
}, 5000);
System.out.println("Download started");
}
On Android device (tested Android 9 and 11) we will not get 5 pieces, just 3 pieces (0,1,2) will be downloaded which is equivalent to default Config.maxSimultaneouslyAssignedPieces value.
If we increase maxSimultaneouslyAssignedPieces to 5 or more, all 5 pieces will be downloaded but only from single peer.
Here comes the next trouble - any IntStream of pieces provided by custom PieceSelector in Android will not be distributed between peers, in the best case (range size<=maxSimultaneouslyAssignedPieces) all pieces will be assigned to one peer and downloaded, in worst case (range size>maxSimultaneouslyAssignedPieces) only maxSimultaneouslyAssignedPieces pieces from desired range will be assigned to one peer and downloaded.
To reproduce try to download using any torrent link (for example http://d.rutor.info/download/934413):
startDownload(URI.create("http://d.rutor.info/download/934413"));
If we try the same code on desktop (Windows), everything works as expected - any piece range supplied by custom PieceSelector will be distributed between peers and downloaded assigning up to maxSimultaneouslyAssignedPieces pieces to each peer.
If we use another PieceSelector (for example SequentialSelector) - whole torrent will be downloaded and piece distribution between peers seems to work as expected even on Android device.
What may be the problem with Android?