for ( DownloadManager download: initial_downloads ){
if ( download.isForceStart()){
TOTorrent torrent = download.getTorrent();
if ( torrent == null ){
continue;
}
try{
byte[] hash = torrent.getHash();
String hash_str = ByteFormatter.encodeString( hash );
Map m = xfer_cache.get( hash_str );
if ( m != null ){
if ( m.containsKey( "f" )){
log( download, "Resetting force-start" );
download.setForceStart( false );
}
}
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
}
}
gm.addListener(
new GlobalManagerAdapter()
{
public void
downloadManagerAdded(
DownloadManager dm )
{
freq_lim_updater.dispatch();
}
public void
downloadManagerRemoved(
DownloadManager dm )
{
freq_lim_updater.dispatch();
}
},
false );
}
DeviceManager manager = getManager();
DeviceOfflineDownloaderManager dodm = manager.getOfflineDownlaoderManager();
List<DownloadManager> downloads;
if ( dodm.isOfflineDownloadingEnabled() && isEnabled()){
List<DownloadManager> initial_downloads = gm.getDownloadManagers();
List<DownloadManager> relevant_downloads = new ArrayList<DownloadManager>( initial_downloads.size());
// remove uninteresting ones
for ( DownloadManager download: initial_downloads ){
int state = download.getState();
if ( state == DownloadManager.STATE_SEEDING ){
// state == DownloadManager.STATE_ERROR ){ removed - might be out of disk space and fixable
continue;
}
// don't include 'stopping' here as we go through stopping on way to queued
if ( state == DownloadManager.STATE_STOPPED ){
// don't remove from downloader if simply paused
if ( !download.isPaused()){
continue;
}
}
// if it is complete then of no interest
if ( download.isDownloadComplete( false )){
continue;
}
relevant_downloads.add( download );
}
downloads = new ArrayList<DownloadManager>( relevant_downloads.size());
if ( dodm.getOfflineDownloadingIsAuto()){
boolean include_private = dodm.getOfflineDownloadingIncludePrivate();
if ( include_private ){
downloads.addAll( relevant_downloads );
}else{
for ( DownloadManager download: relevant_downloads ){
TOTorrent torrent = download.getTorrent();
if ( !TorrentUtils.isReallyPrivate( torrent )){
downloads.add( download );
}
}
}
}else{
// manual, just use the tagged downloads
for ( DownloadManager download: relevant_downloads ){
if ( dodm.isManualDownload( PluginCoreUtils.wrap( download ))){
downloads.add( download );
}
}
}
}else{
downloads = new ArrayList<DownloadManager>();
}
Map<DownloadManager,byte[]> download_map = new HashMap<DownloadManager, byte[]>();
for ( DownloadManager download: downloads ){
TOTorrent torrent = download.getTorrent();
if ( torrent == null ){
continue;
}
try{
byte[] hash = torrent.getHash();
String hash_str = ByteFormatter.encodeString( hash );
DiskManager disk = download.getDiskManager();
if ( disk == null ){
byte[] existing = old_cache.get( hash_str );
if ( existing != null ){
new_cache.put( hash_str, existing );
download_map.put( download, existing );
}else{
// assume not yet started and just use the non-skipped files
DiskManagerFileInfo[] files = download.getDiskManagerFileInfo();
byte[] needed = new byte[( torrent.getNumberOfPieces() + 7 ) / 8];
int hits = 0;
for ( DiskManagerFileInfo file: files ){
if ( file.isSkipped()){
continue;
}
int first_piece = file.getFirstPieceNumber();
int last_piece = first_piece + file.getNbPieces() - 1;
int needed_pos = first_piece/8;
int current_byte = 0;
for ( int pos=first_piece;pos<=last_piece;pos++ ){
current_byte = current_byte << 1;
current_byte += 1;
hits++;
if (( pos %8 ) == 7 ){
needed[needed_pos++] |= (byte)current_byte;
current_byte = 0;
}
}
if ( current_byte != 0 ){
needed[needed_pos++] |= (byte)(current_byte << (8 - (last_piece % 8)));
}
}
if ( hits > 0 ){
new_cache.put( hash_str, needed );
download_map.put( download, needed );
}
}
}else{
DiskManagerPiece[] pieces = disk.getPieces();
byte[] needed = new byte[( pieces.length + 7 ) / 8];
int needed_pos = 0;
int current_byte = 0;
int pos = 0;
int hits = 0;
for ( DiskManagerPiece piece: pieces ){
current_byte = current_byte << 1;
if ( piece.isNeeded() && !piece.isDone()){
current_byte += 1;
hits++;
}
if (( pos %8 ) == 7 ){
needed[needed_pos++] = (byte)current_byte;
current_byte = 0;
}
pos++;
}
if (( pos % 8 ) != 0 ){
needed[needed_pos++] = (byte)(current_byte << (8 - (pos % 8)));
}
if ( hits > 0 ){
new_cache.put( hash_str, needed );
download_map.put( download, needed );
}
}
}catch( Throwable e ){
Debug.out( e );
}
}
// store this so we have consistent record for downloads that queue/pause etc and therefore lose accessible piece details
setPersistentMapProperty( PP_OD_STATE_CACHE, new_cache );
// sort by download priority
List<Map.Entry<DownloadManager, byte[]>> entries = new ArrayList<Map.Entry<DownloadManager,byte[]>>( download_map.entrySet());
Collections.sort(
entries,
new Comparator<Map.Entry<DownloadManager, byte[]>>()
{
public int
compare(
Map.Entry<DownloadManager, byte[]> o1,
Map.Entry<DownloadManager, byte[]> o2)
{
return( o1.getKey().getPosition() - o2.getKey().getPosition());
}
});
String download_hashes = "";
Iterator<Map.Entry<DownloadManager, byte[]>> it = entries.iterator();
while( it.hasNext()){
Map.Entry<DownloadManager, byte[]> entry = it.next();
DownloadManager download = entry.getKey();
try{
String hash = ByteFormatter.encodeString( download.getTorrent().getHash());
download_hashes += ( download_hashes.length()==0?"":"," ) + hash;
new_offline_downloads.put( hash, download );
}catch( Throwable e ){
log( download, "Failed to get download hash", e );
it.remove();
}
}
try{
String[] set_dl_results = service.setDownloads( client_id, download_hashes );
String set_dl_result = set_dl_results[0].trim();
String set_dl_status = set_dl_results[1];
if ( !set_dl_status.equals( "OK" )){
error_status = MessageText.getString( "device.od.error.opfailstatus", new String[]{ "SetDownloads", set_dl_status });
throw( new Exception( "Failing result returned: " + set_dl_status ));
}
String[] bits = Constants.PAT_SPLIT_COMMA.split(set_dl_result);
int num_bits = set_dl_result.length()==0?0:bits.length;
if ( num_bits != entries.size()){
log( "SetDownloads returned an invalid number of results (hashes=" + new_offline_downloads.size() + ",result=" + set_dl_result + ")");
}else{
it = entries.iterator();
int pos = 0;
while( it.hasNext()){
Map.Entry<DownloadManager, byte[]> entry = it.next();
DownloadManager download = entry.getKey();
try{
TOTorrent torrent = download.getTorrent();
String hash_str = ByteFormatter.encodeString( torrent.getHash());
int status = Integer.parseInt( bits[ pos++ ]);
boolean do_update = false;
if ( status == 0 ){
do_update = true;
}else if ( status == 1 ){
// need to add the torrent
try{
// for vuze content add in the azid
if ( PlatformTorrentUtils.isContent( torrent, true )){
String ext = DownloadUtils.getTrackerExtensions( PluginCoreUtils.wrap( download ));
if ( ext != null && ext.length() > 0 ){
try{
if ( ext.startsWith( "&" )){
ext = ext.substring(1);
}
torrent = TOTorrentFactory.deserialiseFromMap( torrent.serialiseToMap());
torrent.setAnnounceURL( appendToURL( torrent.getAnnounceURL(), ext ));
TOTorrentAnnounceURLSet[] sets = torrent.getAnnounceURLGroup().getAnnounceURLSets();
for ( TOTorrentAnnounceURLSet set: sets ){
URL[] urls = set.getAnnounceURLs();
for (int i=0;i<urls.length;i++){
urls[i] = appendToURL( urls[i], ext );
}
}
torrent.getAnnounceURLGroup().setAnnounceURLSets( sets );
}catch( Throwable e ){
log( "Torrent modification failed", e );
}
}
}
String add_result =
addTorrent(
hash_str,
ByteFormatter.encodeStringFully( BEncoder.encode( torrent.serialiseToMap())));
log( download, "AddDownload succeeded" );
if ( add_result.equals( "OK" )){
do_update = true;
}else{
error_status = MessageText.getString( "device.od.error.opfailstatus", new String[]{ "AddDownload", add_result });
throw( new Exception( "Failed to add download: " + add_result ));
}
}catch( Throwable e ){
// TODO: prevent continual attempts to add same torrent?
error_status = MessageText.getString( "device.od.error.opfailexcep", new String[]{ "AddDownload", Debug.getNestedExceptionMessage( e )});
log( download, "Failed to add download", e );
}
}else{
error_status = MessageText.getString( "device.od.error.opfailstatus", new String[]{ "SetDownloads", String.valueOf( status )});
log( download, "SetDownloads: error status returned - " + status );
}
if ( do_update ){
try{
byte[] required_map = entry.getValue();
String required_bitfield = ByteFormatter.encodeStringFully( required_map );
String[] update_results =
service.updateDownload(
client_id,
hash_str,
required_bitfield );
String have_bitfield = update_results[0];
String update_status = update_results[1];
if ( !update_status.equals( "OK" )){
error_status = MessageText.getString( "device.od.error.opfailstatus", new String[]{ "UpdateDownload", update_status });
throw( new Exception( "UpdateDownload: Failing result returned: " + update_status ));
}
int useful_piece_count = 0;
if ( have_bitfield.length() > 0 ){
byte[] have_map = ByteFormatter.decodeString( have_bitfield );
if ( have_map.length != required_map.length ){
throw( new Exception( "UpdateDownload: Returned bitmap length invalid" ));
}
for ( int i=0;i<required_map.length;i++){
int x = ( required_map[i] & have_map[i] )&0xff;
if ( x != 0 ){
for (int j=0;j<8;j++){
if ((x&0x01) != 0 ){
useful_piece_count++;
}
x >>= 1;
}
}
}
if ( useful_piece_count > 0 ) {
long piece_size = torrent.getPieceLength();
new_transferables.put( hash_str, new TransferableDownload( download, hash_str, have_map, useful_piece_count * piece_size ));
}
}