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?
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:
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):
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?