/***************************************************************************
* Copyright ( C ) 2005 by Joris Guisson *
* joris . guisson @ gmail . com *
* ivasic @ gmail . com *
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , write to the *
* Free Software Foundation , Inc . , *
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "queuemanager.h"
# include <tqstring.h>
# include <kmessagebox.h>
# include <klocale.h>
# include <util/log.h>
# include <util/error.h>
# include <util/sha1hash.h>
# include <util/waitjob.h>
# include <util/fileops.h>
# include <torrent/globals.h>
# include <torrent/torrent.h>
# include <torrent/torrentcontrol.h>
# include <interfaces/torrentinterface.h>
# include <interfaces/trackerslist.h>
# include <settings.h>
using namespace kt ;
namespace bt
{
QueueManager : : QueueManager ( ) : TQObject ( ) , exiting ( false )
{
downloads . setAutoDelete ( true ) ;
max_downloads = 0 ;
max_seeds = 0 ; //for testing. Needs to be added to Settings::
keep_seeding = true ; //test. Will be passed from Core
paused_state = false ;
}
QueueManager : : ~ QueueManager ( )
{ }
void QueueManager : : append ( kt : : TorrentInterface * tc )
{
downloads . append ( tc ) ;
downloads . sort ( ) ;
connect ( tc , TQT_SIGNAL ( diskSpaceLow ( kt : : TorrentInterface * , bool ) ) , this , TQT_SLOT ( onLowDiskSpace ( kt : : TorrentInterface * , bool ) ) ) ;
connect ( tc , TQT_SIGNAL ( torrentStopped ( kt : : TorrentInterface * ) ) , this , TQT_SLOT ( torrentStopped ( kt : : TorrentInterface * ) ) ) ;
}
void QueueManager : : remove ( kt : : TorrentInterface * tc )
{
paused_torrents . erase ( tc ) ;
int index = downloads . findRef ( tc ) ;
if ( index ! = - 1 )
downloads . remove ( index ) ;
else
Out ( SYS_GEN | LOG_IMPORTANT ) < < " Could not delete removed torrent control. " < < endl ;
}
void QueueManager : : clear ( )
{
Uint32 nd = downloads . count ( ) ;
paused_torrents . clear ( ) ;
downloads . clear ( ) ;
// wait for a second to allow all http jobs to send the stopped event
if ( nd > 0 )
SynchronousWait ( 1000 ) ;
}
kt : : TorrentStartResponse QueueManager : : start ( kt : : TorrentInterface * tc , bool user )
{
const TorrentStats & s = tc - > getStats ( ) ;
bool start_tc = user ;
bool check_done = false ;
if ( tc - > isCheckingData ( check_done ) & & ! check_done )
return kt : : BUSY_WITH_DATA_CHECK ;
if ( ! user )
{
if ( s . completed )
start_tc = ( max_seeds = = 0 | | getNumRunning ( false , true ) < max_seeds ) ;
else
start_tc = ( max_downloads = = 0 | | getNumRunning ( true ) < max_downloads ) ;
}
else
{
//User started this torrent so make it user controlled
tc - > setPriority ( 0 ) ;
}
if ( start_tc )
{
if ( ! s . completed ) //no need to check diskspace for seeding torrents
{
//check diskspace
bool shortDiskSpace = ! tc - > checkDiskSpace ( false ) ;
if ( shortDiskSpace )
{
//we're short!
switch ( Settings : : startDownloadsOnLowDiskSpace ( ) )
{
case 0 : //don't start!
tc - > setPriority ( 0 ) ;
return kt : : NOT_ENOUGH_DISKSPACE ;
case 1 : //ask user
if ( KMessageBox : : questionYesNo ( 0 , i18n ( " You don't have enough disk space to download this torrent. Are you sure you want to continue? " ) , i18n ( " Insufficient disk space for %1 " ) . arg ( s . torrent_name ) ) = = KMessageBox : : No )
{
tc - > setPriority ( 0 ) ;
return kt : : USER_CANCELED ;
}
else
break ;
case 2 : //force start
break ;
}
}
}
Out ( SYS_GEN | LOG_NOTICE ) < < " Starting download " < < endl ;
float ratio = kt : : ShareRatio ( s ) ;
float max_ratio = tc - > getMaxShareRatio ( ) ;
if ( s . completed & & max_ratio > 0 & & ratio > = max_ratio )
{
if ( KMessageBox : : questionYesNo ( 0 , i18n ( " Torrent \" %1 \" has reached its maximum share ratio. Ignore the limit and start seeding anyway? " ) . arg ( s . torrent_name ) , i18n ( " Maximum share ratio limit reached. " ) ) = = KMessageBox : : Yes )
{
tc - > setMaxShareRatio ( 0.00f ) ;
startSafely ( tc ) ;
}
else
return kt : : USER_CANCELED ;
}
else
startSafely ( tc ) ;
}
else
{
return kt : : TQM_LIMITS_REACHED ;
}
return kt : : START_OK ;
}
void QueueManager : : stop ( kt : : TorrentInterface * tc , bool user )
{
bool check_done = false ;
if ( tc - > isCheckingData ( check_done ) & & ! check_done )
return ;
const TorrentStats & s = tc - > getStats ( ) ;
if ( s . running )
{
stopSafely ( tc , user ) ;
}
if ( user ) //dequeue it
tc - > setPriority ( 0 ) ;
}
void QueueManager : : startall ( int type )
{
TQPtrList < kt : : TorrentInterface > : : iterator i = downloads . begin ( ) ;
while ( i ! = downloads . end ( ) )
{
kt : : TorrentInterface * tc = * i ;
if ( type > = 3 )
start ( tc , true ) ;
else
{
if ( ( tc - > getStats ( ) . completed & & type = = 2 ) | | ( ! tc - > getStats ( ) . completed & & type = = 1 ) | | ( type = = 3 ) )
start ( tc , true ) ;
}
i + + ;
}
}
void QueueManager : : stopall ( int type )
{
TQPtrList < kt : : TorrentInterface > : : iterator i = downloads . begin ( ) ;
while ( i ! = downloads . end ( ) )
{
kt : : TorrentInterface * tc = * i ;
const TorrentStats & s = tc - > getStats ( ) ;
if ( tc - > getStats ( ) . running )
{
try
{
if ( type > = 3 )
stopSafely ( tc , true ) ;
else if ( ( s . completed & & type = = 2 ) | | ( ! s . completed & & type = = 1 ) )
stopSafely ( tc , true ) ;
}
catch ( bt : : Error & err )
{
TQString msg =
i18n ( " Error stopping torrent %1 : %2 " )
. arg ( s . torrent_name ) . arg ( err . toString ( ) ) ;
KMessageBox : : error ( 0 , msg , i18n ( " Error " ) ) ;
}
}
else //if torrent is not running but it is queued we need to make it user controlled
if ( ( s . completed & & type = = 2 ) | | ( ! s . completed & & type = = 1 ) | | ( type = = 3 ) )
tc - > setPriority ( 0 ) ;
i + + ;
}
}
void QueueManager : : onExit ( WaitJob * wjob )
{
exiting = true ;
TQPtrList < kt : : TorrentInterface > : : iterator i = downloads . begin ( ) ;
while ( i ! = downloads . end ( ) )
{
kt : : TorrentInterface * tc = * i ;
if ( tc - > getStats ( ) . running )
{
stopSafely ( tc , false , wjob ) ;
}
i + + ;
}
}
void QueueManager : : startNext ( )
{
orderQueue ( ) ;
}
int QueueManager : : countDownloads ( )
{
int nr = 0 ;
TQPtrList < TorrentInterface > : : const_iterator i = downloads . begin ( ) ;
while ( i ! = downloads . end ( ) )
{
if ( ! ( * i ) - > getStats ( ) . completed )
+ + nr ;
+ + i ;
}
return nr ;
}
int QueueManager : : countSeeds ( )
{
int nr = 0 ;
TQPtrList < TorrentInterface > : : const_iterator i = downloads . begin ( ) ;
while ( i ! = downloads . end ( ) )
{
if ( ( * i ) - > getStats ( ) . completed )
+ + nr ;
+ + i ;
}
return nr ;
}
int QueueManager : : getNumRunning ( bool onlyDownload , bool onlySeed )
{
int nr = 0 ;
// int test = 1;
TQPtrList < TorrentInterface > : : const_iterator i = downloads . begin ( ) ;
while ( i ! = downloads . end ( ) )
{
const TorrentInterface * tc = * i ;
const TorrentStats & s = tc - > getStats ( ) ;
//Out() << "Torrent " << test++ << s.torrent_name << " priority: " << tc->getPriority() << endl;
if ( s . running )
{
if ( onlyDownload )
{
if ( ! s . completed ) nr + + ;
}
else
{
if ( onlySeed )
{
if ( s . completed ) nr + + ;
}
else
nr + + ;
}
}
i + + ;
}
// Out() << endl;
return nr ;
}
int QueueManager : : getNumRunning ( bool userControlled , bool onlyDownloads , bool onlySeeds )
{
int nr = 0 ;
// int test = 1;
TQPtrList < TorrentInterface > : : const_iterator i = downloads . begin ( ) ;
while ( i ! = downloads . end ( ) )
{
const TorrentInterface * tc = * i ;
const TorrentStats & s = tc - > getStats ( ) ;
//Out() << "Torrent " << test++ << s.torrent_name << " priority: " << tc->getPriority() << endl;
if ( s . running )
{
if ( onlyDownloads )
{
if ( ! s . completed & & ( userControlled & & s . user_controlled ) ) nr + + ;
}
else
{
if ( onlySeeds )
{
if ( s . completed & & ( userControlled & & s . user_controlled ) ) nr + + ;
}
else
if ( userControlled & & s . user_controlled ) nr + + ;
}
}
i + + ;
}
// Out() << endl;
return nr ;
}
TQPtrList < kt : : TorrentInterface > : : iterator QueueManager : : begin ( )
{
return downloads . begin ( ) ;
}
TQPtrList < kt : : TorrentInterface > : : iterator QueueManager : : end ( )
{
return downloads . end ( ) ;
}
void QueueManager : : setMaxDownloads ( int m )
{
max_downloads = m ;
}
void QueueManager : : setMaxSeeds ( int m )
{
max_seeds = m ;
}
void QueueManager : : setKeepSeeding ( bool ks )
{
keep_seeding = ks ;
}
bool QueueManager : : allreadyLoaded ( const SHA1Hash & ih ) const
{
TQPtrList < kt : : TorrentInterface > : : const_iterator itr = downloads . begin ( ) ;
while ( itr ! = downloads . end ( ) )
{
const TorrentControl * tor = ( const TorrentControl * ) ( * itr ) ;
if ( tor - > getTorrent ( ) . getInfoHash ( ) = = ih )
return true ;
itr + + ;
}
return false ;
}
void QueueManager : : mergeAnnounceList ( const SHA1Hash & ih , const TrackerTier * trk )
{
TQPtrList < kt : : TorrentInterface > : : iterator itr = downloads . begin ( ) ;
while ( itr ! = downloads . end ( ) )
{
TorrentControl * tor = ( TorrentControl * ) ( * itr ) ;
if ( tor - > getTorrent ( ) . getInfoHash ( ) = = ih )
{
TrackersList * ta = tor - > getTrackersList ( ) ;
ta - > merge ( trk ) ;
return ;
}
itr + + ;
}
}
void QueueManager : : orderQueue ( )
{
if ( ! downloads . count ( ) )
return ;
if ( paused_state | | exiting )
return ;
downloads . sort ( ) ;
TQPtrList < TorrentInterface > : : const_iterator it = downloads . begin ( ) ;
TQPtrList < TorrentInterface > : : const_iterator its = downloads . end ( ) ;
if ( max_downloads ! = 0 | | max_seeds ! = 0 )
{
bt : : QueuePtrList download_queue ;
bt : : QueuePtrList seed_queue ;
int user_downloading = 0 ;
int user_seeding = 0 ;
for ( ; it ! = downloads . end ( ) ; + + it )
{
TorrentInterface * tc = * it ;
const TorrentStats & s = tc - > getStats ( ) ;
if ( s . running & & s . user_controlled )
{
if ( ! s . completed )
+ + user_downloading ;
else
+ + user_seeding ;
}
if ( ! s . user_controlled & & ! tc - > isMovingFiles ( ) & & ! s . stopped_by_error )
{
if ( s . completed )
seed_queue . append ( tc ) ;
else
download_queue . append ( tc ) ;
}
}
int max_qm_downloads = max_downloads - user_downloading ;
int max_qm_seeds = max_seeds - user_seeding ;
//stop all QM started torrents
for ( Uint32 i = max_qm_downloads ; i < download_queue . count ( ) & & max_downloads ; + + i )
{
TorrentInterface * tc = download_queue . at ( i ) ;
const TorrentStats & s = tc - > getStats ( ) ;
if ( s . running & & ! s . user_controlled & & ! s . completed )
{
Out ( SYS_GEN | LOG_DEBUG ) < < " QM Stopping: " < < s . torrent_name < < endl ;
stop ( tc ) ;
}
}
//stop all QM started torrents
for ( Uint32 i = max_qm_seeds ; i < seed_queue . count ( ) & & max_seeds ; + + i )
{
TorrentInterface * tc = seed_queue . at ( i ) ;
const TorrentStats & s = tc - > getStats ( ) ;
if ( s . running & & ! s . user_controlled & & s . completed )
{
Out ( SYS_GEN | LOG_NOTICE ) < < " QM Stopping: " < < s . torrent_name < < endl ;
stop ( tc ) ;
}
}
//Now start all needed torrents
if ( max_downloads = = 0 )
max_qm_downloads = download_queue . count ( ) ;
if ( max_seeds = = 0 )
max_qm_seeds = seed_queue . count ( ) ;
int counter = 0 ;
for ( Uint32 i = 0 ; counter < max_qm_downloads & & i < download_queue . count ( ) ; + + i )
{
TorrentInterface * tc = download_queue . at ( i ) ;
const TorrentStats & s = tc - > getStats ( ) ;
if ( ! s . running & & ! s . completed & & ! s . user_controlled )
{
start ( tc , false ) ;
if ( tc - > getStats ( ) . stopped_by_error )
{
tc - > setPriority ( 0 ) ;
continue ;
}
}
+ + counter ;
}
counter = 0 ;
for ( Uint32 i = 0 ; counter < max_qm_seeds & & i < seed_queue . count ( ) ; + + i )
{
TorrentInterface * tc = seed_queue . at ( i ) ;
const TorrentStats & s = tc - > getStats ( ) ;
if ( ! s . running & & s . completed & & ! s . user_controlled )
{
start ( tc , false ) ;
if ( tc - > getStats ( ) . stopped_by_error )
{
tc - > setPriority ( 0 ) ;
continue ;
}
}
+ + counter ;
}
}
else
{
//no limits at all
for ( it = downloads . begin ( ) ; it ! = downloads . end ( ) ; + + it )
{
TorrentInterface * tc = * it ;
const TorrentStats & s = tc - > getStats ( ) ;
if ( ! s . running & & ! s . user_controlled & & ! s . stopped_by_error & & ! tc - > isMovingFiles ( ) )
{
start ( tc , false ) ;
if ( tc - > getStats ( ) . stopped_by_error )
tc - > setPriority ( 0 ) ;
}
}
}
}
void QueueManager : : torrentFinished ( kt : : TorrentInterface * tc )
{
//dequeue this tc
tc - > setPriority ( 0 ) ;
//make sure the max_seeds is not reached
// if(max_seeds !=0 && max_seeds < getNumRunning(false,true))
// tc->stop(true);
if ( keep_seeding )
torrentAdded ( tc , false , false ) ;
else
stop ( tc , true ) ;
orderQueue ( ) ;
}
void QueueManager : : torrentAdded ( kt : : TorrentInterface * tc , bool user , bool start_torrent )
{
if ( ! user )
{
TQPtrList < TorrentInterface > : : const_iterator it = downloads . begin ( ) ;
while ( it ! = downloads . end ( ) )
{
TorrentInterface * _tc = * it ;
int p = _tc - > getPriority ( ) ;
if ( p = = 0 )
break ;
else
_tc - > setPriority ( + + p ) ;
+ + it ;
}
tc - > setPriority ( 1 ) ;
}
else
{
tc - > setPriority ( 0 ) ;
if ( start_torrent )
start ( tc , true ) ;
}
orderQueue ( ) ;
}
void QueueManager : : torrentRemoved ( kt : : TorrentInterface * tc )
{
remove ( tc ) ;
orderQueue ( ) ;
}
void QueueManager : : setPausedState ( bool pause )
{
paused_state = pause ;
if ( ! pause )
{
std : : set < kt : : TorrentInterface * > : : iterator it = paused_torrents . begin ( ) ;
while ( it ! = paused_torrents . end ( ) )
{
TorrentInterface * tc = * it ;
startSafely ( tc ) ;
it + + ;
}
paused_torrents . clear ( ) ;
orderQueue ( ) ;
}
else
{
TQPtrList < TorrentInterface > : : const_iterator it = downloads . begin ( ) ;
for ( ; it ! = downloads . end ( ) ; it + + )
{
TorrentInterface * tc = * it ;
const TorrentStats & s = tc - > getStats ( ) ;
if ( s . running )
{
paused_torrents . insert ( tc ) ;
stopSafely ( tc , false ) ;
}
}
}
}
void QueueManager : : enqueue ( kt : : TorrentInterface * tc )
{
//if a seeding torrent reached its maximum share ratio or maximum seed time don't enqueue it...
if ( tc - > getStats ( ) . completed & & ( tc - > overMaxRatio ( ) | | tc - > overMaxSeedTime ( ) ) )
{
Out ( SYS_GEN | LOG_IMPORTANT ) < < " Torrent has reached max share ratio or max seed time and cannot be started automatically. " < < endl ;
emit queuingNotPossible ( tc ) ;
return ;
}
torrentAdded ( tc , false , false ) ;
}
void QueueManager : : dequeue ( kt : : TorrentInterface * tc )
{
int tp = tc - > getPriority ( ) ;
bool completed = tc - > getStats ( ) . completed ;
TQPtrList < TorrentInterface > : : const_iterator it = downloads . begin ( ) ;
while ( it ! = downloads . end ( ) )
{
TorrentInterface * _tc = * it ;
bool _completed = _tc - > getStats ( ) . completed ;
if ( tc = = _tc | | ( _completed ! = completed ) )
{
+ + it ;
continue ;
}
int p = _tc - > getPriority ( ) ;
if ( p < tp )
break ;
else
_tc - > setPriority ( - - p ) ;
+ + it ;
}
tc - > setPriority ( 0 ) ;
orderQueue ( ) ;
}
void QueueManager : : queue ( kt : : TorrentInterface * tc )
{
if ( tc - > getPriority ( ) = = 0 )
enqueue ( tc ) ;
else
dequeue ( tc ) ;
}
void QueueManager : : startSafely ( kt : : TorrentInterface * tc )
{
try
{
tc - > start ( ) ;
}
catch ( bt : : Error & err )
{
const TorrentStats & s = tc - > getStats ( ) ;
TQString msg =
i18n ( " Error starting torrent %1 : %2 " )
. arg ( s . torrent_name ) . arg ( err . toString ( ) ) ;
KMessageBox : : error ( 0 , msg , i18n ( " Error " ) ) ;
}
}
void QueueManager : : stopSafely ( kt : : TorrentInterface * tc , bool user , WaitJob * wjob )
{
try
{
tc - > stop ( user , wjob ) ;
}
catch ( bt : : Error & err )
{
const TorrentStats & s = tc - > getStats ( ) ;
TQString msg =
i18n ( " Error stopping torrent %1 : %2 " )
. arg ( s . torrent_name ) . arg ( err . toString ( ) ) ;
KMessageBox : : error ( 0 , msg , i18n ( " Error " ) ) ;
}
}
void QueueManager : : onLowDiskSpace ( kt : : TorrentInterface * tc , bool toStop )
{
if ( toStop )
{
stop ( tc , false ) ;
}
//then emit the signal to inform trayicon to show passive popup
emit lowDiskSpace ( tc , toStop ) ;
}
void QueueManager : : torrentStopped ( kt : : TorrentInterface * tc )
{
orderQueue ( ) ;
}
/////////////////////////////////////////////////////////////////////////////////////////////
QueuePtrList : : QueuePtrList ( ) : TQPtrList < kt : : TorrentInterface > ( )
{ }
QueuePtrList : : ~ QueuePtrList ( )
{ }
int QueuePtrList : : compareItems ( TQPtrCollection : : Item item1 , TQPtrCollection : : Item item2 )
{
kt : : TorrentInterface * tc1 = ( kt : : TorrentInterface * ) item1 ;
kt : : TorrentInterface * tc2 = ( kt : : TorrentInterface * ) item2 ;
if ( tc1 - > getPriority ( ) = = tc2 - > getPriority ( ) )
return 0 ;
if ( tc1 - > getPriority ( ) = = 0 & & tc2 - > getPriority ( ) ! = 0 )
return 1 ;
else if ( tc1 - > getPriority ( ) ! = 0 & & tc2 - > getPriority ( ) = = 0 )
return - 1 ;
return tc1 - > getPriority ( ) > tc2 - > getPriority ( ) ? - 1 : 1 ;
return 0 ;
}
}
# include "queuemanager.moc"