]>
git.ipfire.org Git - people/shoehn/ipfire.org.git/blob - www/pages/torrent/client/download_bt1.py
1 # Written by Bram Cohen
2 # see LICENSE.txt for license information
4 from zurllib
import urlopen
5 from urlparse
import urlparse
6 from BT1
.btformats
import check_message
7 from BT1
.Choker
import Choker
8 from BT1
.Storage
import Storage
9 from BT1
.StorageWrapper
import StorageWrapper
10 from BT1
.FileSelector
import FileSelector
11 from BT1
.Uploader
import Upload
12 from BT1
.Downloader
import Downloader
13 from BT1
.HTTPDownloader
import HTTPDownloader
14 from BT1
.Connecter
import Connecter
15 from RateLimiter
import RateLimiter
16 from BT1
.Encrypter
import Encoder
17 from RawServer
import RawServer
, autodetect_ipv6
, autodetect_socket_style
18 from BT1
.Rerequester
import Rerequester
19 from BT1
.DownloaderFeedback
import DownloaderFeedback
20 from RateMeasure
import RateMeasure
21 from CurrentRateMeasure
import Measure
22 from BT1
.PiecePicker
import PiecePicker
23 from BT1
.Statistics
import Statistics
24 from ConfigDir
import ConfigDir
25 from bencode
import bencode
, bdecode
26 from natpunch
import UPnP_test
28 from os
import path
, makedirs
, listdir
29 from parseargs
import parseargs
, formatDefinitions
, defaultargs
30 from socket
import error
as socketerror
31 from random
import seed
32 from threading
import Thread
, Event
33 from clock
import clock
34 from __init__
import createPeerID
44 "the maximum number of uploads to allow at once."),
45 ('keepalive_interval', 120.0,
46 'number of seconds to pause between sending keepalives'),
47 ('download_slice_size', 2 ** 14,
48 "How many bytes to query for per request."),
49 ('upload_unit_size', 1460,
50 "when limiting upload rate, how many bytes to send at a time"),
51 ('request_backlog', 10,
52 "maximum number of requests to keep in a single pipe at once."),
53 ('max_message_length', 2 ** 23,
54 "maximum length prefix encoding you'll accept over the wire - larger values get the connection dropped."),
56 "ip to report you have to the tracker."),
57 ('minport', 10000, 'minimum port to listen on, counts up if unavailable'),
58 ('maxport', 60000, 'maximum port to listen on'),
59 ('random_port', 1, 'whether to choose randomly inside the port range ' +
60 'instead of counting up linearly'),
62 'file the server response was stored in, alternative to url'),
64 'url to get file from, alternative to responsefile'),
65 ('selector_enabled', 1,
66 'whether to enable the file selector and fast resume function'),
67 ('expire_cache_data', 10,
68 'the number of days after which you wish to expire old cache data ' +
71 'a list of file priorities separated by commas, must be one per file, ' +
72 '0 = highest, 1 = normal, 2 = lowest, -1 = download disabled'),
74 'local file name to save the file as, null indicates query user'),
76 'time to wait between closing sockets which nothing has been received on'),
77 ('timeout_check_interval', 60.0,
78 'time to wait between checking if any connections have timed out'),
79 ('max_slice_length', 2 ** 17,
80 "maximum length slice to send to peers, larger requests are ignored"),
81 ('max_rate_period', 20.0,
82 "maximum amount of time to guess the current rate estimate represents"),
84 'comma-separated list of ips/hostnames to bind to locally'),
85 # ('ipv6_enabled', autodetect_ipv6(),
87 'allow the client to connect to peers via IPv6'),
88 ('ipv6_binds_v4', autodetect_socket_style(),
89 "set if an IPv6 server socket won't also field IPv4 connections"),
90 ('upnp_nat_access', 1,
91 'attempt to autoconfigure a UPnP router to forward a server port ' +
92 '(0 = disabled, 1 = mode 1 [fast], 2 = mode 2 [slow])'),
93 ('upload_rate_fudge', 5.0,
94 'time equivalent of writing to kernel-level TCP buffer, for rate adjustment'),
95 ('tcp_ack_fudge', 0.03,
96 'how much TCP ACK download overhead to add to upload rate calculations ' +
98 ('display_interval', .5,
99 'time between updates of displayed information'),
100 ('rerequest_interval', 5 * 60,
101 'time to wait between requesting more peers'),
103 'minimum number of peers to not do rerequesting'),
105 'number of seconds to wait before assuming that an http connection has timed out'),
107 'number of peers at which to stop initiating new connections'),
109 'whether to check hashes on disk'),
110 ('max_upload_rate', 0,
111 'maximum kB/s to upload at (0 = no limit, -1 = automatic)'),
112 ('max_download_rate', 0,
113 'maximum kB/s to download at (0 = no limit)'),
114 ('alloc_type', 'normal',
115 'allocation type (may be normal, background, pre-allocate or sparse)'),
117 'rate (in MiB/s) to allocate space at using background allocation'),
119 'whether to buffer disk reads'),
120 ('write_buffer_size', 4,
121 'the maximum amount of space to use for buffering disk writes ' +
122 '(in megabytes, 0 = disabled)'),
123 ('breakup_seed_bitfield', 1,
124 'sends an incomplete bitfield and then fills with have messages, '
125 'in order to get around stupid ISP manipulation'),
127 "seconds to wait for data to come in over a connection before assuming it's semi-permanently choked"),
129 "whether to display diagnostic info to stdout"),
130 ('rarest_first_cutoff', 2,
131 "number of downloads at which to switch from random to rarest first"),
132 ('rarest_first_priority_cutoff', 5,
133 'the number of peers which need to have a piece before other partials take priority over rarest first'),
135 "the number of uploads to fill out to with extra optimistic unchokes"),
136 ('max_files_open', 50,
137 'the maximum number of files to keep open at a time, 0 means no limit'),
138 ('round_robin_period', 30,
139 "the number of seconds between the client's switching upload targets"),
141 "whether to use special upload-efficiency-maximizing routines (only for dedicated seeds)"),
143 "whether to enable extra security features intended to prevent abuse"),
144 ('max_connections', 0,
145 "the absolute maximum number of peers to connect with (0 = no limit)"),
147 "whether to allow the client to automatically kick/ban peers that send bad data"),
149 "whether to double-check data being written to the disk for errors (may increase CPU load)"),
151 "whether to thoroughly check data being written to the disk (may slow disk access)"),
153 "whether to lock files the client is working with"),
154 ('lock_while_reading', 0,
155 "whether to lock access to files being read"),
157 "minutes between automatic flushes to disk (0 = disabled)"),
158 ('dedicated_seed_id', '',
159 "code to send to tracker identifying as a dedicated seed"),
162 argslistheader
= 'Arguments are:\n\n'
168 # old-style downloader
169 def download(params
, filefunc
, statusfunc
, finfunc
, errorfunc
, doneflag
, cols
,
170 pathFunc
= None, presets
= {}, exchandler
= None,
171 failed
= _failfunc
, paramfunc
= None):
174 config
= parse_params(params
, presets
)
175 except ValueError, e
:
176 failed('error: ' + str(e
) + '\nrun with no args for parameter explanations')
179 errorfunc(get_usage())
182 myid
= createPeerID()
185 rawserver
= RawServer(doneflag
, config
['timeout_check_interval'],
186 config
['timeout'], ipv6_enable
= config
['ipv6_enabled'],
187 failfunc
= failed
, errorfunc
= exchandler
)
189 upnp_type
= UPnP_test(config
['upnp_nat_access'])
191 listen_port
= rawserver
.find_and_bind(config
['minport'], config
['maxport'],
192 config
['bind'], ipv6_socket_style
= config
['ipv6_binds_v4'],
193 upnp
= upnp_type
, randomizer
= config
['random_port'])
194 except socketerror
, e
:
195 failed("Couldn't listen - " + str(e
))
198 response
= get_response(config
['responsefile'], config
['url'], failed
)
202 infohash
= sha(bencode(response
['info'])).digest()
204 d
= BT1Download(statusfunc
, finfunc
, errorfunc
, exchandler
, doneflag
,
205 config
, response
, infohash
, myid
, rawserver
, listen_port
)
207 if not d
.saveAs(filefunc
):
211 pathFunc(d
.getFilename())
213 hashcheck
= d
.initFiles(old_style
= True)
218 if not d
.startEngine():
223 statusfunc(activity
= 'connecting to peers')
226 paramfunc({ 'max_upload_rate' : d
.setUploadRate
, # change_max_upload_rate(<int KiB/sec>)
227 'max_uploads': d
.setConns
, # change_max_uploads(<int max uploads>)
228 'listen_port' : listen_port
, # int
229 'peer_id' : myid
, # string
230 'info_hash' : infohash
, # string
231 'start_connection' : d
._startConnection
, # start_connection((<string ip>, <int port>), <peer id>)
234 rawserver
.listen_forever(d
.getPortHandler())
239 def parse_params(params
, presets
= {}):
242 config
, args
= parseargs(params
, defaults
, 0, 1, presets
= presets
)
244 if config
['responsefile'] or config
['url']:
245 raise ValueError,'must have responsefile or url as arg or parameter, not both'
246 if path
.isfile(args
[0]):
247 config
['responsefile'] = args
[0]
252 raise ValueError, 'bad filename or url'
253 config
['url'] = args
[0]
254 elif (config
['responsefile'] == '') == (config
['url'] == ''):
255 raise ValueError, 'need responsefile or url, must have one, cannot have both'
259 def get_usage(defaults
= defaults
, cols
= 100, presets
= {}):
260 return (argslistheader
+ formatDefinitions(defaults
, cols
, presets
))
263 def get_response(file, url
, errorfunc
):
268 line
= h
.read(10) # quick test to see if responsefile contains a dict
269 front
,garbage
= line
.split(':',1)
270 assert front
[0] == 'd'
273 errorfunc(file+' is not a valid responsefile')
287 errorfunc(url
+' bad url')
292 errorfunc('problem getting response info - ' + str(e
))
300 response
= bdecode(response
)
302 errorfunc("warning: bad data in responsefile")
303 response
= bdecode(response
, sloppy
=1)
304 check_message(response
)
305 except ValueError, e
:
306 errorfunc("got bad file info - " + str(e
))
313 def __init__(self
, statusfunc
, finfunc
, errorfunc
, excfunc
, doneflag
,
314 config
, response
, infohash
, id, rawserver
, port
,
316 self
.statusfunc
= statusfunc
317 self
.finfunc
= finfunc
318 self
.errorfunc
= errorfunc
319 self
.excfunc
= excfunc
320 self
.doneflag
= doneflag
322 self
.response
= response
323 self
.infohash
= infohash
325 self
.rawserver
= rawserver
328 self
.info
= self
.response
['info']
329 self
.pieces
= [self
.info
['pieces'][x
:x
+20]
330 for x
in xrange(0, len(self
.info
['pieces']), 20)]
331 self
.len_pieces
= len(self
.pieces
)
332 self
.argslistheader
= argslistheader
333 self
.unpauseflag
= Event()
334 self
.unpauseflag
.set()
335 self
.downloader
= None
336 self
.storagewrapper
= None
337 self
.fileselector
= None
338 self
.super_seeding_active
= False
339 self
.filedatflag
= Event()
340 self
.spewflag
= Event()
341 self
.superseedflag
= Event()
342 self
.whenpaused
= None
343 self
.finflag
= Event()
344 self
.rerequest
= None
345 self
.tcp_ack_fudge
= config
['tcp_ack_fudge']
347 self
.selector_enabled
= config
['selector_enabled']
349 self
.appdataobj
= appdataobj
350 elif self
.selector_enabled
:
351 self
.appdataobj
= ConfigDir()
352 self
.appdataobj
.deleteOldCacheData( config
['expire_cache_data'],
355 self
.excflag
= self
.rawserver
.get_exception_flag()
357 self
.checking
= False
360 self
.picker
= PiecePicker(self
.len_pieces
, config
['rarest_first_cutoff'],
361 config
['rarest_first_priority_cutoff'])
362 self
.choker
= Choker(config
, rawserver
.add_task
,
363 self
.picker
, self
.finflag
.isSet
)
366 def checkSaveLocation(self
, loc
):
367 if self
.info
.has_key('length'):
368 return path
.exists(loc
)
369 for x
in self
.info
['files']:
370 if path
.exists(path
.join(loc
, x
['path'][0])):
375 def saveAs(self
, filefunc
, pathfunc
= None):
377 def make(f
, forcedir
= False):
380 if f
!= '' and not path
.exists(f
):
383 if self
.info
.has_key('length'):
384 file_length
= self
.info
['length']
385 file = filefunc(self
.info
['name'], file_length
,
386 self
.config
['saveas'], False)
390 files
= [(file, file_length
)]
393 for x
in self
.info
['files']:
394 file_length
+= x
['length']
395 file = filefunc(self
.info
['name'], file_length
,
396 self
.config
['saveas'], True)
400 # if this path exists, and no files from the info dict exist, we assume it's a new download and
401 # the user wants to create a new directory with the default name
403 if path
.exists(file):
404 if not path
.isdir(file):
405 self
.errorfunc(file + 'is not a dir')
407 if len(listdir(file)) > 0: # if it's not empty
408 for x
in self
.info
['files']:
409 if path
.exists(path
.join(file, x
['path'][0])):
412 file = path
.join(file, self
.info
['name'])
413 if path
.exists(file) and not path
.isdir(file):
414 if file[-8:] == '.torrent':
416 if path
.exists(file) and not path
.isdir(file):
417 self
.errorfunc("Can't create dir - " + self
.info
['name'])
421 # alert the UI to any possible change in path
426 for x
in self
.info
['files']:
430 files
.append((n
, x
['length']))
433 self
.errorfunc("Couldn't allocate dir - " + str(e
))
438 self
.datalength
= file_length
443 def getFilename(self
):
450 self
.storage
.set_readonly()
451 except (IOError, OSError), e
:
452 self
.errorfunc('trouble setting readonly at end - ' + str(e
))
453 if self
.superseedflag
.isSet():
454 self
._set
_super
_seed
()
455 self
.choker
.set_round_robin_period(
456 max( self
.config
['round_robin_period'],
457 self
.config
['round_robin_period'] *
458 self
.info
['piece length'] / 200000 ) )
459 self
.rerequest_complete()
462 def _data_flunked(self
, amount
, index
):
463 self
.ratemeasure_datarejected(amount
)
464 if not self
.doneflag
.isSet():
465 self
.errorfunc('piece %d failed hash check, re-downloading it' % index
)
467 def _failed(self
, reason
):
470 if reason
is not None:
471 self
.errorfunc(reason
)
474 def initFiles(self
, old_style
= False, statusfunc
= None):
475 if self
.doneflag
.isSet():
478 statusfunc
= self
.statusfunc
480 disabled_files
= None
481 if self
.selector_enabled
:
482 self
.priority
= self
.config
['priority']
485 self
.priority
= self
.priority
.split(',')
486 assert len(self
.priority
) == len(self
.files
)
487 self
.priority
= [int(p
) for p
in self
.priority
]
488 for p
in self
.priority
:
492 self
.errorfunc('bad priority list given, ignored')
495 data
= self
.appdataobj
.getTorrentData(self
.infohash
)
497 d
= data
['resume data']['priority']
498 assert len(d
) == len(self
.files
)
499 disabled_files
= [x
== -1 for x
in d
]
502 disabled_files
= [x
== -1 for x
in self
.priority
]
508 self
.storage
= Storage(self
.files
, self
.info
['piece length'],
509 self
.doneflag
, self
.config
, disabled_files
)
511 self
.errorfunc('trouble accessing files - ' + str(e
))
513 if self
.doneflag
.isSet():
516 self
.storagewrapper
= StorageWrapper(self
.storage
, self
.config
['download_slice_size'],
517 self
.pieces
, self
.info
['piece length'], self
._finished
, self
._failed
,
518 statusfunc
, self
.doneflag
, self
.config
['check_hashes'],
519 self
._data
_flunked
, self
.rawserver
.add_task
,
520 self
.config
, self
.unpauseflag
)
522 except ValueError, e
:
523 self
._failed
('bad data - ' + str(e
))
525 self
._failed
('IOError - ' + str(e
))
526 if self
.doneflag
.isSet():
529 if self
.selector_enabled
:
530 self
.fileselector
= FileSelector(self
.files
, self
.info
['piece length'],
531 self
.appdataobj
.getPieceDir(self
.infohash
),
532 self
.storage
, self
.storagewrapper
,
533 self
.rawserver
.add_task
,
536 data
= data
.get('resume data')
538 self
.fileselector
.unpickle(data
)
542 return self
.storagewrapper
.old_style_init()
543 return self
.storagewrapper
.initialize
546 def getCachedTorrentData(self
):
547 return self
.appdataobj
.getTorrentData(self
.infohash
)
550 def _make_upload(self
, connection
, ratelimiter
, totalup
):
551 return Upload(connection
, ratelimiter
, totalup
,
552 self
.choker
, self
.storagewrapper
, self
.picker
,
555 def _kick_peer(self
, connection
):
556 def k(connection
= connection
):
558 self
.rawserver
.add_task(k
,0)
560 def _ban_peer(self
, ip
):
563 def _received_raw_data(self
, x
):
564 if self
.tcp_ack_fudge
:
565 x
= int(x
*self
.tcp_ack_fudge
)
566 self
.ratelimiter
.adjust_sent(x
)
567 # self.upmeasure.update_rate(x)
569 def _received_data(self
, x
):
570 self
.downmeasure
.update_rate(x
)
571 self
.ratemeasure
.data_came_in(x
)
573 def _received_http_data(self
, x
):
574 self
.downmeasure
.update_rate(x
)
575 self
.ratemeasure
.data_came_in(x
)
576 self
.downloader
.external_data_received(x
)
578 def _cancelfunc(self
, pieces
):
579 self
.downloader
.cancel_piece_download(pieces
)
580 self
.httpdownloader
.cancel_piece_download(pieces
)
581 def _reqmorefunc(self
, pieces
):
582 self
.downloader
.requeue_piece_download(pieces
)
584 def startEngine(self
, ratelimiter
= None, statusfunc
= None):
585 if self
.doneflag
.isSet():
588 statusfunc
= self
.statusfunc
590 self
.checking
= False
592 for i
in xrange(self
.len_pieces
):
593 if self
.storagewrapper
.do_I_have(i
):
594 self
.picker
.complete(i
)
595 self
.upmeasure
= Measure(self
.config
['max_rate_period'],
596 self
.config
['upload_rate_fudge'])
597 self
.downmeasure
= Measure(self
.config
['max_rate_period'])
600 self
.ratelimiter
= ratelimiter
602 self
.ratelimiter
= RateLimiter(self
.rawserver
.add_task
,
603 self
.config
['upload_unit_size'],
605 self
.ratelimiter
.set_upload_rate(self
.config
['max_upload_rate'])
607 self
.ratemeasure
= RateMeasure()
608 self
.ratemeasure_datarejected
= self
.ratemeasure
.data_rejected
610 self
.downloader
= Downloader(self
.storagewrapper
, self
.picker
,
611 self
.config
['request_backlog'], self
.config
['max_rate_period'],
612 self
.len_pieces
, self
.config
['download_slice_size'],
613 self
._received
_data
, self
.config
['snub_time'], self
.config
['auto_kick'],
614 self
._kick
_peer
, self
._ban
_peer
)
615 self
.downloader
.set_download_rate(self
.config
['max_download_rate'])
616 self
.connecter
= Connecter(self
._make
_upload
, self
.downloader
, self
.choker
,
617 self
.len_pieces
, self
.upmeasure
, self
.config
,
618 self
.ratelimiter
, self
.rawserver
.add_task
)
619 self
.encoder
= Encoder(self
.connecter
, self
.rawserver
,
620 self
.myid
, self
.config
['max_message_length'], self
.rawserver
.add_task
,
621 self
.config
['keepalive_interval'], self
.infohash
,
622 self
._received
_raw
_data
, self
.config
)
623 self
.encoder_ban
= self
.encoder
.ban
625 self
.httpdownloader
= HTTPDownloader(self
.storagewrapper
, self
.picker
,
626 self
.rawserver
, self
.finflag
, self
.errorfunc
, self
.downloader
,
627 self
.config
['max_rate_period'], self
.infohash
, self
._received
_http
_data
,
628 self
.connecter
.got_piece
)
629 if self
.response
.has_key('httpseeds') and not self
.finflag
.isSet():
630 for u
in self
.response
['httpseeds']:
631 self
.httpdownloader
.make_download(u
)
633 if self
.selector_enabled
:
634 self
.fileselector
.tie_in(self
.picker
, self
._cancelfunc
,
635 self
._reqmorefunc
, self
.rerequest_ondownloadmore
)
637 self
.fileselector
.set_priorities_now(self
.priority
)
638 self
.appdataobj
.deleteTorrentData(self
.infohash
)
639 # erase old data once you've started modifying it
641 if self
.config
['super_seeder']:
642 self
.set_super_seed()
648 def rerequest_complete(self
):
650 self
.rerequest
.announce(1)
652 def rerequest_stopped(self
):
654 self
.rerequest
.announce(2)
656 def rerequest_lastfailed(self
):
658 return self
.rerequest
.last_failed
661 def rerequest_ondownloadmore(self
):
665 def startRerequester(self
, seededfunc
= None, force_rapid_update
= False):
666 if self
.response
.has_key('announce-list'):
667 trackerlist
= self
.response
['announce-list']
669 trackerlist
= [[self
.response
['announce']]]
671 self
.rerequest
= Rerequester(trackerlist
, self
.config
['rerequest_interval'],
672 self
.rawserver
.add_task
, self
.connecter
.how_many_connections
,
673 self
.config
['min_peers'], self
.encoder
.start_connections
,
674 self
.rawserver
.add_task
, self
.storagewrapper
.get_amount_left
,
675 self
.upmeasure
.get_total
, self
.downmeasure
.get_total
, self
.port
, self
.config
['ip'],
676 self
.myid
, self
.infohash
, self
.config
['http_timeout'],
677 self
.errorfunc
, self
.excfunc
, self
.config
['max_initiate'],
678 self
.doneflag
, self
.upmeasure
.get_rate
, self
.downmeasure
.get_rate
,
679 self
.unpauseflag
, self
.config
['dedicated_seed_id'],
680 seededfunc
, force_rapid_update
)
682 self
.rerequest
.start()
685 def _init_stats(self
):
686 self
.statistics
= Statistics(self
.upmeasure
, self
.downmeasure
,
687 self
.connecter
, self
.httpdownloader
, self
.ratelimiter
,
688 self
.rerequest_lastfailed
, self
.filedatflag
)
689 if self
.info
.has_key('files'):
690 self
.statistics
.set_dirstats(self
.files
, self
.info
['piece length'])
691 if self
.config
['spew']:
694 def autoStats(self
, displayfunc
= None):
696 displayfunc
= self
.statusfunc
699 DownloaderFeedback(self
.choker
, self
.httpdownloader
, self
.rawserver
.add_task
,
700 self
.upmeasure
.get_rate
, self
.downmeasure
.get_rate
,
701 self
.ratemeasure
, self
.storagewrapper
.get_stats
,
702 self
.datalength
, self
.finflag
, self
.spewflag
, self
.statistics
,
703 displayfunc
, self
.config
['display_interval'])
705 def startStats(self
):
707 d
= DownloaderFeedback(self
.choker
, self
.httpdownloader
, self
.rawserver
.add_task
,
708 self
.upmeasure
.get_rate
, self
.downmeasure
.get_rate
,
709 self
.ratemeasure
, self
.storagewrapper
.get_stats
,
710 self
.datalength
, self
.finflag
, self
.spewflag
, self
.statistics
)
714 def getPortHandler(self
):
718 def shutdown(self
, torrentdata
= {}):
719 if self
.checking
or self
.started
:
720 self
.storagewrapper
.sync()
722 self
.rerequest_stopped()
723 if self
.fileselector
and self
.started
:
725 self
.fileselector
.finish()
726 torrentdata
['resume data'] = self
.fileselector
.pickle()
728 self
.appdataobj
.writeTorrentData(self
.infohash
,torrentdata
)
730 self
.appdataobj
.deleteTorrentData(self
.infohash
) # clear it
731 return not self
.failed
and not self
.excflag
.isSet()
732 # if returns false, you may wish to auto-restart the torrent
735 def setUploadRate(self
, rate
):
737 def s(self
= self
, rate
= rate
):
738 self
.config
['max_upload_rate'] = rate
739 self
.ratelimiter
.set_upload_rate(rate
)
740 self
.rawserver
.add_task(s
)
741 except AttributeError:
744 def setConns(self
, conns
, conns2
= None):
748 def s(self
= self
, conns
= conns
, conns2
= conns2
):
749 self
.config
['min_uploads'] = conns
750 self
.config
['max_uploads'] = conns2
752 self
.config
['max_initiate'] = conns
+ 10
753 self
.rawserver
.add_task(s
)
754 except AttributeError:
757 def setDownloadRate(self
, rate
):
759 def s(self
= self
, rate
= rate
):
760 self
.config
['max_download_rate'] = rate
761 self
.downloader
.set_download_rate(rate
)
762 self
.rawserver
.add_task(s
)
763 except AttributeError:
766 def startConnection(self
, ip
, port
, id):
767 self
.encoder
._start
_connection
((ip
, port
), id)
769 def _startConnection(self
, ipandport
, id):
770 self
.encoder
._start
_connection
(ipandport
, id)
772 def setInitiate(self
, initiate
):
774 def s(self
= self
, initiate
= initiate
):
775 self
.config
['max_initiate'] = initiate
776 self
.rawserver
.add_task(s
)
777 except AttributeError:
783 def getDefaults(self
):
784 return defaultargs(defaults
)
786 def getUsageText(self
):
787 return self
.argslistheader
789 def reannounce(self
, special
= None):
791 def r(self
= self
, special
= special
):
793 self
.rerequest
.announce()
795 self
.rerequest
.announce(specialurl
= special
)
796 self
.rawserver
.add_task(r
)
797 except AttributeError:
800 def getResponse(self
):
808 # if self.storagewrapper:
809 # self.rawserver.add_task(self._pausemaker, 0)
812 # self.unpauseflag.clear()
815 # def _pausemaker(self):
816 # self.whenpaused = clock()
817 # self.unpauseflag.wait() # sticks a monkey wrench in the main thread
820 # self.unpauseflag.set()
821 # if self.whenpaused and clock()-self.whenpaused > 60:
822 # def r(self = self):
823 # self.rerequest.announce(3) # rerequest automatically if paused for >60 seconds
824 # self.rawserver.add_task(r)
827 if not self
.storagewrapper
:
829 self
.unpauseflag
.clear()
830 self
.rawserver
.add_task(self
.onPause
)
834 self
.whenpaused
= clock()
835 if not self
.downloader
:
837 self
.downloader
.pause(True)
838 self
.encoder
.pause(True)
839 self
.choker
.pause(True)
842 self
.unpauseflag
.set()
843 self
.rawserver
.add_task(self
.onUnpause
)
846 if not self
.downloader
:
848 self
.downloader
.pause(False)
849 self
.encoder
.pause(False)
850 self
.choker
.pause(False)
851 if self
.rerequest
and self
.whenpaused
and clock()-self
.whenpaused
> 60:
852 self
.rerequest
.announce(3) # rerequest automatically if paused for >60 seconds
854 def set_super_seed(self
):
856 self
.superseedflag
.set()
858 if self
.finflag
.isSet():
859 self
._set
_super
_seed
()
860 self
.rawserver
.add_task(s
)
861 except AttributeError:
864 def _set_super_seed(self
):
865 if not self
.super_seeding_active
:
866 self
.super_seeding_active
= True
867 self
.errorfunc(' ** SUPER-SEED OPERATION ACTIVE **\n' +
868 ' please set Max uploads so each peer gets 6-8 kB/s')
870 self
.downloader
.set_super_seed()
871 self
.choker
.set_super_seed()
872 self
.rawserver
.add_task(s
)
873 if self
.finflag
.isSet(): # mode started when already finished
875 self
.rerequest
.announce(3) # so after kicking everyone off, reannounce
876 self
.rawserver
.add_task(r
)
878 def am_I_finished(self
):
879 return self
.finflag
.isSet()
881 def get_transfer_stats(self
):
882 return self
.upmeasure
.get_total(), self
.downmeasure
.get_total()