Common subdirectories: ./BitTorrent and ../BitTorrent-3.2.1b-experimental/BitTorrent diff -u ./btcompletedir.py ../BitTorrent-3.2.1b-experimental/btcompletedir.py --- ./btcompletedir.py 2003-03-17 11:26:39.000000000 -0800 +++ ../BitTorrent-3.2.1b-experimental/btcompletedir.py 2003-04-11 11:33:36.000000000 -0700 @@ -13,7 +13,7 @@ def dummy(x): pass -def completedir(dir, url, flag = Event(), vc = dummy, fc = dummy, piece_length = None): +def completedir(dir, url, flag = Event(), vc = dummy, fc = dummy, piece_len_pow2 = None): files = listdir(dir) files.sort() ext = '.torrent' @@ -36,7 +36,7 @@ try: t = split(i)[-1] if t not in ignore and t[0] != '.': - make_meta_file(i, url, flag = flag, progress = callback, progress_percent=0, piece_length = piece_length) + make_meta_file(i, url, flag = flag, progress = callback, progress_percent=0, piece_len_exp = piece_len_pow2) except ValueError: print_exc() diff -u ./btdownloadcurses.py ../BitTorrent-3.2.1b-experimental/btdownloadcurses.py --- ./btdownloadcurses.py 2003-03-27 21:14:08.000000000 -0800 +++ ../BitTorrent-3.2.1b-experimental/btdownloadcurses.py 2003-04-11 18:06:48.000000000 -0700 @@ -68,6 +68,9 @@ self.downloadTo = '' self.downRate = '---' self.upRate = '---' + self.shareRating = '' + self.seedStatus = '' + self.peerStatus = '' self.errors = [] self.globalerrlist = mainerrlist @@ -89,7 +92,8 @@ self.display() def display(self, fractionDone = None, timeEst = None, - downRate = None, upRate = None, activity = None): + downRate = None, upRate = None, activity = None, + statistics = None, **kws): if activity is not None and not self.done: self.activity = activity elif timeEst is not None: @@ -104,6 +108,16 @@ self.downRate = '%.1f KB/s' % (float(downRate) / (1 << 10)) if upRate is not None: self.upRate = '%.1f KB/s' % (float(upRate) / (1 << 10)) + if statistics is not None: + if (statistics.shareRating < 0) or (statistics.shareRating > 100): + self.shareRating = 'oo (%.1f MB up / %.1f MB down)' % (float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20)) + else: + self.shareRating = '%.3f (%.1f MB up / %.1f MB down)' % (statistics.shareRating, float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20)) + if not self.done: + self.seedStatus = '%d seen now, plus %.3f distributed copies' % (statistics.numSeeds,0.001*int(1000*statistics.numCopies)) + else: + self.seedStatus = '%d seen recently, plus %.3f distributed copies' % (statistics.numOldSeeds,0.001*int(1000*statistics.numCopies)) + self.peerStatus = '%d seen now, %.1f%% done at %.1f kB/s' % (statistics.numPeers,statistics.percentDone,float(statistics.torrentRate) / (1 << 10)) fieldwin.erase() fieldwin.addnstr(0, 0, self.file, fieldw, curses.A_BOLD) @@ -114,12 +128,15 @@ fieldwin.addnstr(4, 0, self.status, fieldw) fieldwin.addnstr(5, 0, self.downRate, fieldw) fieldwin.addnstr(6, 0, self.upRate, fieldw) + fieldwin.addnstr(7, 0, self.shareRating, fieldw) + fieldwin.addnstr(8, 0, self.seedStatus, fieldw) + fieldwin.addnstr(9, 0, self.peerStatus, fieldw) if self.errors: for i in range(len(self.errors)): - fieldwin.addnstr(7 + i, 0, self.errors[i], fieldw, curses.A_BOLD) + fieldwin.addnstr(10 + i, 0, self.errors[i], fieldw, curses.A_BOLD) else: - fieldwin.move(7, 0) + fieldwin.move(10, 0) curses.panel.update_panels() curses.doupdate() @@ -151,7 +168,10 @@ labelwin.addstr(4, 0, 'status:') labelwin.addstr(5, 0, 'dl speed:') labelwin.addstr(6, 0, 'ul speed:') - labelwin.addstr(7, 0, 'error(s):') + labelwin.addstr(7, 0, 'sharing:') + labelwin.addstr(8, 0, 'seeds:') + labelwin.addstr(9, 0, 'peers:') + labelwin.addstr(10, 0, 'error(s):') curses.panel.update_panels() curses.doupdate() @@ -197,4 +217,3 @@ print "These errors occurred during execution:" for error in mainerrlist: print error - diff -u ./btdownloadgui.py ../BitTorrent-3.2.1b-experimental/btdownloadgui.py --- ./btdownloadgui.py 2003-03-27 20:35:58.000000000 -0800 +++ ../BitTorrent-3.2.1b-experimental/btdownloadgui.py 2003-04-11 20:22:08.000000000 -0700 @@ -1,19 +1,25 @@ #!/usr/bin/env python # Written by Bram Cohen and Myers Carpenter +# Modifications by various people # see LICENSE.txt for license information from sys import argv, version assert version >= '2', "Install Python 2.0 or greater" from BitTorrent import version -from BitTorrent.download import download +from BitTorrent.download import * +from BitTorrent.ConnChoice import * from threading import Event, Thread -from os.path import join +from os.path import * from os import getcwd from wxPython.wx import * from time import strftime from webbrowser import open_new +import re +import sys +true = 1 +false = 0 def hours(n): if n == -1: @@ -43,74 +49,521 @@ self.args = args self.kwargs = kwargs +def pr(event): + print 'augh!' + class DownloadInfoFrame: def __init__(self, flag): - frame = wxFrame(None, -1, 'BitTorrent ' + version + ' download', size = wxSize(400, 250)) - self.frame = frame + frame = wxFrame(None, -1, 'BitTorrent ' + version + ' download') self.flag = flag self.fin = false + self.aboutBox = None + self.detailBox = None + self.creditsBox = None + self.spinlock = 0 + if (sys.platform == 'win32'): + self.icon = wxIcon('icon_bt.ico', wxBITMAP_TYPE_ICO) + self.starttime = time () + + self.frame = frame + if (sys.platform == 'win32'): + self.frame.SetIcon(self.icon) panel = wxPanel(frame, -1) colSizer = wxFlexGridSizer(cols = 1, vgap = 3) + colSizer.AddGrowableCol (0) + colSizer.AddGrowableRow (6) + border = wxBoxSizer(wxHORIZONTAL) + border.Add(colSizer, 1, wxEXPAND | wxALL, 4) + panel.SetSizer(border) + panel.SetAutoLayout(true) fnsizer = wxBoxSizer(wxHORIZONTAL) - - self.fileNameText = wxStaticText(panel, -1, '', style = wxALIGN_LEFT) - fnsizer.Add(self.fileNameText, 1, wxALIGN_BOTTOM) - self.aboutText = wxStaticText(panel, -1, 'about', style = wxALIGN_RIGHT) - self.aboutText.SetForegroundColour('Blue') - self.aboutText.SetFont(wxFont(14, wxNORMAL, wxNORMAL, wxNORMAL, true)) - fnsizer.Add(self.aboutText, 0, wxEXPAND) + fileNameText = wxStaticText(panel, -1, '', style = wxALIGN_LEFT) + fileNameText.SetFont(wxFont(12, wxNORMAL, wxNORMAL, wxNORMAL, false)) + fnsizer.Add(fileNameText, 1, wxALIGN_BOTTOM|wxEXPAND) + + fileDetails = wxStaticText(panel, -1, 'Details', style = wxALIGN_RIGHT) + fileDetails.SetFont(wxFont(12, wxNORMAL, wxNORMAL, wxNORMAL, true)) + fileDetails.SetForegroundColour('Blue') + + fnsizer.Add(fileDetails, 0, wxALIGN_BOTTOM) + + fnsizer.Add(wxStaticText(panel, -1, ' ', style = wxALIGN_RIGHT)) + + aboutText = wxStaticText(panel, -1, 'About', style = wxALIGN_RIGHT) + aboutText.SetForegroundColour('Blue') + aboutText.SetFont(wxFont(12, wxNORMAL, wxNORMAL, wxNORMAL, true)) + fnsizer.Add(aboutText, 0, wxEXPAND) + self.fileNameText = fileNameText + self.fnsizer = fnsizer colSizer.Add(fnsizer, 0, wxEXPAND) self.gauge = wxGauge(panel, -1, range = 1000, style = wxGA_SMOOTH) colSizer.Add(self.gauge, 0, wxEXPAND) - gridSizer = wxFlexGridSizer(cols = 2, vgap = 3, hgap = 8) - - gridSizer.Add(wxStaticText(panel, -1, 'Estimated time left:')) - self.timeEstText = wxStaticText(panel, -1, '') - gridSizer.Add(self.timeEstText, 0, wxEXPAND) + timeSizer = wxFlexGridSizer(cols = 2) + timeSizer.Add(wxStaticText(panel, -1, 'Time elapsed / estimated : ')) + self.timeText = wxStaticText(panel, -1, ' ') + timeSizer.Add(self.timeText) + timeSizer.AddGrowableCol(1) + colSizer.Add(timeSizer) - gridSizer.Add(wxStaticText(panel, -1, 'Download to:')) + destSizer = wxFlexGridSizer(cols = 2, hgap = 8) + destSizer.Add(wxStaticText(panel, -1, 'Download to:')) self.fileDestText = wxStaticText(panel, -1, '') - gridSizer.Add(self.fileDestText, 0, wxEXPAND) - - gridSizer.Add(wxStaticText(panel, -1, 'Download rate:')) - self.downRateText = wxStaticText(panel, -1, '') - gridSizer.Add(self.downRateText, 0, wxEXPAND) - - gridSizer.Add(wxStaticText(panel, -1, 'Upload rate:')) - self.upRateText = wxStaticText(panel, -1, '') - gridSizer.Add(self.upRateText, 0, wxEXPAND) - gridSizer.AddGrowableCol(1) - - colSizer.Add(gridSizer, 0, wxEXPAND) + destSizer.Add(self.fileDestText, flag = wxEXPAND) + destSizer.AddGrowableCol(1) + colSizer.Add(destSizer, flag = wxEXPAND) + + statSizer = wxFlexGridSizer(cols = 3, hgap = 8) + + self.ratesSizer = wxFlexGridSizer(cols = 2) + self.infoSizer = wxFlexGridSizer(cols = 2) + + self.ratesSizer.Add(wxStaticText(panel, -1, 'Download rate: ')) + self.downRateText = wxStaticText(panel, -1, ' 0 kB/s') + self.ratesSizer.Add(self.downRateText, flag = wxEXPAND) + + self.infoSizer.Add(wxStaticText(panel, -1, 'Downloaded: ')) + self.downText = wxStaticText(panel, -1, ' 0.00 MiB') + self.infoSizer.Add(self.downText, flag = wxEXPAND) + + self.ratesSizer.Add(wxStaticText(panel, -1, 'Upload rate: ')) + self.upRateText = wxStaticText(panel, -1, ' 0 kB/s') + self.ratesSizer.Add(self.upRateText, flag = wxEXPAND) + + self.infoSizer.Add(wxStaticText(panel, -1, 'Uploaded: ')) + self.upText = wxStaticText(panel, -1, ' 0.00 MiB') + self.infoSizer.Add(self.upText, flag = wxEXPAND) + + shareSizer = wxFlexGridSizer(cols = 2, hgap = 8) + shareSizer.Add(wxStaticText(panel, -1, 'Share rating:')) + self.shareRatingText = wxStaticText(panel, -1, '') + shareSizer.AddGrowableCol(1) + shareSizer.Add(self.shareRatingText, flag = wxEXPAND) + + statSizer.Add(self.ratesSizer) + statSizer.Add(self.infoSizer) + statSizer.Add(shareSizer, flag = wxALIGN_CENTER_VERTICAL) + colSizer.Add (statSizer) + + torrentSizer = wxFlexGridSizer(cols = 1) + self.peerStatusText = wxStaticText(panel, -1, '') + torrentSizer.Add(self.peerStatusText, 0, wxEXPAND) + self.seedStatusText = wxStaticText(panel, -1, '') + torrentSizer.Add(self.seedStatusText, 0, wxEXPAND) + torrentSizer.AddGrowableCol(0) + colSizer.Add(torrentSizer, 0, wxEXPAND) self.errorText = wxStaticText(panel, -1, '', style = wxALIGN_LEFT) self.errorText.SetForegroundColour('Red') colSizer.Add(self.errorText, 0, wxEXPAND) self.cancelButton = wxButton(panel, -1, 'Cancel') colSizer.Add(self.cancelButton, 0, wxALIGN_CENTER) + + # Setting options + + slideSizer = wxFlexGridSizer(cols = 7, hgap = 0, vgap = 0) + + # dropdown + + slideSizer.Add (wxStaticText(panel, -1, 'Settings for '), 0, wxALIGN_LEFT) + self.connChoice = wxChoice (panel, -1, (-1, -1), (90, -1), choices = connChoiceList) + self.connChoice.SetSelection(0) + slideSizer.Add (self.connChoice, 0, wxALIGN_CENTER) + slideSizer.Add (wxStaticText(panel, -1, ' Upload rate (kB/s) '), 0, wxALIGN_RIGHT) + + # max upload rate + + self.rateSpinner = wxSpinCtrl (panel, -1, "", (-1,-1), (50, -1)) + self.rateSpinner.SetRange(0,5000) + self.rateSpinner.SetValue(0) + slideSizer.Add (self.rateSpinner, 0, wxALIGN_CENTER) + + self.rateLowerText = wxStaticText(panel, -1, ' %5d' % (0)) + self.rateUpperText = wxStaticText(panel, -1, '%5d' % (5000)) + self.rateslider = wxSlider(panel, -1, 0, 0, 100, (-1, -1), (80, -1)) + + slideSizer.Add(self.rateLowerText, 0, wxALIGN_RIGHT) + slideSizer.Add(self.rateslider, 0, wxALIGN_CENTER) + slideSizer.Add(self.rateUpperText, 0, wxALIGN_LEFT) + + # Placeholders in Layout + + slideSizer.Add(wxStaticText(panel, -1, ''), 0, wxALIGN_LEFT) + slideSizer.Add(wxStaticText(panel, -1, ''), 0, wxALIGN_LEFT) + + # max uploads + + slideSizer.Add(wxStaticText(panel, -1, ' Max uploads '), 0, wxALIGN_RIGHT) + self.connSpinner = wxSpinCtrl (panel, -1, "", (-1,-1), (50, -1)) + self.connSpinner.SetRange(4,100) + self.connSpinner.SetValue(4) + slideSizer.Add (self.connSpinner, 0, wxALIGN_CENTER) + + self.connLowerText = wxStaticText(panel, -1, ' %5d' % (4)) + self.connUpperText = wxStaticText(panel, -1, '%5d' % (100)) + self.connslider = wxSlider(panel, -1, 4, 4, 100, (-1, -1), (80, -1)) + + slideSizer.Add(self.connLowerText, 0, wxALIGN_RIGHT) + slideSizer.Add(self.connslider, 0, wxALIGN_CENTER) + slideSizer.Add(self.connUpperText, 0, wxALIGN_LEFT) + + colSizer.Add(slideSizer, 1, wxALL|wxALIGN_CENTER|wxEXPAND, 0) + + colSizer.Add(wxStaticText(panel, -1, '0 kB/s means unlimited. Tip: your download rate is proportional to your upload rate'), 0, wxALIGN_CENTER) + + EVT_LEFT_DOWN(aboutText, self.about) + EVT_LEFT_DOWN(fileDetails, self.details) + EVT_CLOSE(frame, self.done) + EVT_BUTTON(frame, self.cancelButton.GetId(), self.done) + EVT_INVOKE(frame, self.onInvoke) + EVT_SCROLL(self.rateslider, self.onRateScroll) + EVT_SCROLL(self.connslider, self.onConnScroll) + EVT_CHOICE(self.connChoice, -1, self.onConnChoice) + EVT_SPINCTRL(self.connSpinner, -1, self.onConnSpinner) + EVT_SPINCTRL(self.rateSpinner, -1, self.onRateSpinner) + if (sys.platform == 'win32'): + EVT_ICONIZE(self.frame,self.onIconify) + + self.frame.Show() + border.Fit(panel) + self.frame.Fit() + + + def onIconify(self, evt): + if not hasattr(self.frame, "tbicon"): + self.frame.tbicon = wxTaskBarIcon() + self.frame.tbicon.SetIcon(self.icon, "BitTorrent") + # setup a taskbar icon, and catch some events from it + EVT_TASKBAR_LEFT_DCLICK(self.frame.tbicon, self.onTaskBarActivate) + EVT_TASKBAR_RIGHT_UP(self.frame.tbicon, self.onTaskBarMenu) + EVT_MENU(self.frame.tbicon, self.TBMENU_RESTORE, self.onTaskBarActivate) + EVT_MENU(self.frame.tbicon, self.TBMENU_CLOSE, self.done) + self.frame.Hide() + + def onTaskBarActivate(self, evt): + if self.frame.IsIconized(): + self.frame.Iconize(false) + if not self.frame.IsShown(): + self.frame.Show(true) + self.frame.Raise() + if hasattr(self.frame, "tbicon"): + del self.frame.tbicon + return + + TBMENU_RESTORE = 1000 + TBMENU_CLOSE = 1001 + + def onTaskBarMenu(self, evt): + menu = wxMenu() + menu.Append(self.TBMENU_RESTORE, "Restore BitTorrent") + menu.Append(self.TBMENU_CLOSE, "Close") + self.frame.tbicon.PopupMenu(menu) + menu.Destroy() + + def onRateScroll(self, event): + newValue = self.rateslider.GetValue() + if self.connChoice.GetSelection() == 0: + newValue = self.rateslider.GetValue() * 50 + self.rateSpinner.SetValue (newValue) + self.dow.setUploadRate (newValue) + + def onConnScroll(self, event): + self.connSpinner.SetValue (self.connslider.GetValue ()) + self.dow.setConns (self.connslider.GetValue ()) + + def onRateSpinner(self, event): + if (self.spinlock == 0): + self.spinlock = 1 + if self.connChoice.GetSelection() == 0: + newValue = self.rateSpinner.GetValue () + if newValue != 0: + newValue /= 50 + if self.rateSpinner.GetValue () % 10 != 9: + newValue += 1 + self.rateslider.SetValue (newValue) + newValue *= 50 + if (self.rateSpinner.GetValue () != newValue): + self.rateSpinner.SetValue (newValue) + self.dow.setUploadRate (self.rateSpinner.GetValue ()) + else: + self.dow.setUploadRate (self.rateSpinner.GetValue ()) + self.rateslider.SetValue (self.rateSpinner.GetValue ()) + self.spinlock = 0 + + def onConnSpinner(self, event): + self.connslider.SetValue (self.connSpinner.GetValue()) + self.dow.setConns (self.connSpinner.GetValue ()) + + def onConnChoice(self, event): + num = self.connChoice.GetSelection() + self.rateSpinner.SetValue (connChoices[num]['rate']['def']) + self.rateSpinner.SetRange (connChoices[num]['rate']['min'], + connChoices[num]['rate']['max']) + self.rateslider.SetRange ( + connChoices[num]['rate']['min']/connChoices[num]['rate'].get('div',1), + connChoices[num]['rate']['max']/connChoices[num]['rate'].get('div',1)) + self.rateslider.SetValue ( + connChoices[num]['rate']['def']/connChoices[num]['rate'].get('div',1)) + self.rateLowerText.SetLabel (' %d' % (connChoices[num]['rate']['min'])) + self.rateUpperText.SetLabel ('%d' % (connChoices[num]['rate']['max'])) + self.connSpinner.SetValue (connChoices[num]['conn']['def']) + self.connSpinner.SetRange (connChoices[num]['conn']['min'], + connChoices[num]['conn']['max']) + self.connslider.SetRange (connChoices[num]['conn']['min'], + connChoices[num]['conn']['max']) + self.connslider.SetValue (connChoices[num]['conn']['def']) + self.connLowerText.SetLabel (' %d' % (connChoices[num]['conn']['min'])) + self.connUpperText.SetLabel ('%d' % (connChoices[num]['conn']['max'])) + self.onConnScroll (0) + self.onRateScroll (0) + self.dow.setInitiate (connChoices[num].get('initiate', 40)) + + def about(self, event): + if (self.aboutBox is not None): + try: + self.aboutBox.Close () + except wxPyDeadObjectError, e: + self.aboutBox = None + + self.aboutBox = wxFrame(None, -1, 'About BitTorrent', size = (1,1)) + + panel = wxPanel(self.aboutBox, -1) + colSizer = wxFlexGridSizer(cols = 1, vgap = 3) + + titleSizer = wxBoxSizer(wxHORIZONTAL) + aboutTitle = wxStaticText(panel, -1, 'BitTorrent ' + version) + aboutTitle.SetFont(wxFont(14, wxNORMAL, wxNORMAL, wxNORMAL, false)) + titleSizer.Add (aboutTitle) + linkDonate = wxStaticText(panel, -1, 'Donate to Bram\n(link)', style = wxALIGN_RIGHT) + linkDonate.SetForegroundColour('Blue'); + linkDonate.SetFont(wxFont(-1, wxNORMAL, wxNORMAL, wxNORMAL, true)) + titleSizer.Add (linkDonate, 1, wxALIGN_BOTTOM&wxEXPAND) + colSizer.Add(titleSizer, 0, wxEXPAND) + + colSizer.Add(wxStaticText(panel, -1, 'created by Bram Cohen, Copyright 2001-2003')) + credits = wxStaticText(panel, -1, 'full credits\n') + credits.SetForegroundColour('Blue'); + credits.SetFont(wxFont(-1, wxNORMAL, wxNORMAL, wxNORMAL, true)) + colSizer.Add(credits); + + systemInformation = wxStaticText(panel, -1, + 'exact Version String: ' + version + '\n'+ + 'Python version: ' + sys.version + '\n') + colSizer.Add(systemInformation) + + babble1 = wxStaticText(panel, -1, + 'This is an experimental, unofficial build of BitTorrent.\n' + + 'It is Free Software under an MIT-Style license.') + babble2 = wxStaticText(panel, -1,'BitTorrent Homepage (link)') + babble2.SetForegroundColour('Blue'); + babble2.SetFont(wxFont(-1, wxNORMAL, wxNORMAL, wxNORMAL, true)) + babble4 = wxStaticText(panel, -1,'Experimental Client Homepage (link)') + babble4.SetForegroundColour('Blue'); + babble4.SetFont(wxFont(-1, wxNORMAL, wxNORMAL, wxNORMAL, true)) + babble6 = wxStaticText(panel, -1, 'License Terms (link)') + babble6.SetForegroundColour('Blue'); + babble6.SetFont(wxFont(-1, wxNORMAL, wxNORMAL, wxNORMAL, true)) + colSizer.Add (babble1) + colSizer.Add (babble2) + colSizer.Add (babble4) + colSizer.Add (babble6) + + okButton = wxButton(panel, -1, 'Ok') + colSizer.Add(okButton, 0, wxALIGN_RIGHT) + colSizer.AddGrowableCol(0) + + border = wxBoxSizer(wxHORIZONTAL) + border.Add(colSizer, 1, wxEXPAND | wxALL, 4) + panel.SetSizer(border) + panel.SetAutoLayout(true) + + EVT_LEFT_DOWN(linkDonate, self.donatelink) + EVT_LEFT_DOWN(babble2, self.aboutlink) + EVT_LEFT_DOWN(babble4, self.explink) + EVT_LEFT_DOWN(babble6, self.licenselink) + EVT_LEFT_DOWN(credits, self.credits) + + EVT_BUTTON(self.aboutBox, okButton.GetId(), self.closeAbout) + + self.aboutBox.Show () + border.Fit(panel) + self.aboutBox.Fit() + + def details(self, event): + metainfo = self.dow.getResponse() + announce = metainfo['announce'] + info = metainfo['info'] + info_hash = sha(bencode(info)) + piece_length = info['piece length'] + + if (self.detailBox is not None): + try: + self.detailBox.Close () + except wxPyDeadObjectError, e: + self.detailBox = None + + self.detailBox = wxFrame(None, -1, 'Torrent Details ', size = wxSize(405,230)) + + panel = wxPanel(self.detailBox, -1, size = wxSize (400,220)) + colSizer = wxFlexGridSizer(cols = 1, vgap = 3) + + titleSizer = wxBoxSizer(wxHORIZONTAL) + aboutTitle = wxStaticText(panel, -1, 'Details about ' + self.filename) + aboutTitle.SetFont(wxFont(14, wxNORMAL, wxNORMAL, wxNORMAL, false)) + titleSizer.Add (aboutTitle) + colSizer.Add (titleSizer) + + detailSizer = wxFlexGridSizer(cols = 2, vgap = 3) + detailSizer.AddGrowableCol(1) + colSizer.Add (detailSizer) + + if info.has_key('length'): + detailSizer.Add(wxStaticText(panel, -1, 'file name :')) + detailSizer.Add(wxStaticText(panel, -1, info['name'])) + file_length = info['length'] + name = "file size"; + else: + detailSizer.Add(wxStaticText(panel, -1, 'directory name :')) + detailSizer.Add(wxStaticText(panel, -1, info['name'])) + detailSizer.Add(wxStaticText(panel, -1, 'files :')) + file_length = 0; + for file in info['files']: + path = '' + for item in file['path']: + if (path != ''): + path = path + "/" + path = path + item + detailSizer.Add(wxStaticText(panel, -1, '%s (%d)' % (path, file['length']))) + detailSizer.Add(wxStaticText(panel, -1, '')) + file_length += file['length'] + name = 'archive size' + detailSizer.Add(wxStaticText(panel, -1, '')) + + detailSizer.Add(wxStaticText(panel, -1, 'info_hash :')) + detailSizer.Add(wxTextCtrl(panel, -1, info_hash.hexdigest(), size = (300, -1), style = wxTE_READONLY)) + piece_number, last_piece_length = divmod(file_length, piece_length) + detailSizer.Add(wxStaticText(panel, -1, name + ' : ')) + detailSizer.Add(wxStaticText(panel, -1, '%i (%i * %i + %i)' % (file_length, piece_number, piece_length, last_piece_length))) + detailSizer.Add(wxStaticText(panel, -1, 'announce url : ')) + detailSizer.Add(wxTextCtrl(panel, -1, announce, size = (300, -1), style = wxTE_READONLY)) + detailSizer.Add(wxStaticText(panel, -1, 'likely tracker :')) + p = re.compile( '(.*/)[^/]+') + turl = p.sub (r'\1', announce) + trackerUrl = wxStaticText(panel, -1, turl) + trackerUrl.SetForegroundColour('Blue'); + trackerUrl.SetFont(wxFont(-1, wxNORMAL, wxNORMAL, wxNORMAL, true)) + detailSizer.Add(trackerUrl) + + okButton = wxButton(panel, -1, 'Ok') + colSizer.Add(okButton, 0, wxALIGN_RIGHT) + colSizer.AddGrowableCol(0) + + border = wxBoxSizer(wxHORIZONTAL) + border.Add(colSizer, 1, wxEXPAND | wxALL, 4) + panel.SetSizer(border) + panel.SetAutoLayout(true) + + EVT_BUTTON(self.detailBox, okButton.GetId(), self.closeDetail) + + def trackerurl(self): + Thread(target = open_new(turl)).start() + + EVT_LEFT_DOWN(trackerUrl, trackerurl) + + self.detailBox.Show () + border.Fit(panel) + self.detailBox.Fit() + + def credits(self, event): + if (self.creditsBox is not None): + try: + self.creditsBox.Close () + except wxPyDeadObjectError, e: + self.creditsBox = None + + self.creditsBox = wxFrame(None, -1, 'Credits', size = (1,1)) + + panel = wxPanel(self.creditsBox, -1) + colSizer = wxFlexGridSizer(cols = 1, vgap = 3) + + titleSizer = wxBoxSizer(wxHORIZONTAL) + aboutTitle = wxStaticText(panel, -1, 'Credits') + aboutTitle.SetFont(wxFont(14, wxNORMAL, wxNORMAL, wxNORMAL, false)) + titleSizer.Add (aboutTitle) + colSizer.Add (titleSizer) + colSizer.Add (wxStaticText(panel, -1, + 'The following people have all helped with this\n' + + 'version of BitTorrent in some way (in no particular order) -\n')); + creditSizer = wxFlexGridSizer(cols = 3) + creditSizer.Add(wxStaticText(panel, -1, + 'Bill Bumgarner\n' + + 'David Creswick\n' + + 'Andrew Loewenstern\n' + + 'Ross Cohen\n' + + 'Jeremy Avnet\n' + + 'Greg Broiles\n' + + 'Barry Cohen\n' + + 'Bram Cohen\n' + + 'sayke\n' + + 'Steve Jenson\n' + + 'Myers Carpenter\n' + + 'Francis Crick\n' + + 'Petru Paler\n' + + 'Jeff Darcy\n' + + 'John Gilmore\n')) + creditSizer.Add(wxStaticText(panel, -1, ' ')) + creditSizer.Add(wxStaticText(panel, -1, + 'Yann Vernier\n' + + 'Pat Mahoney\n' + + 'Boris Zbarsky\n' + + 'Eric Tiedemann\n' + + 'Henry "Pi" James\n' + + 'Loring Holden\n' + + 'Robert Stone\n' + + 'Michael Janssen\n' + + 'Eike Frost\n' + + 'Andrew Todd\n' + + 'otaku\n' + + 'Edward Keyes\n')) + colSizer.Add (creditSizer, flag = wxALIGN_CENTER_HORIZONTAL) + okButton = wxButton(panel, -1, 'Ok') + colSizer.Add(okButton, 0, wxALIGN_RIGHT) colSizer.AddGrowableCol(0) - colSizer.AddGrowableRow(3) border = wxBoxSizer(wxHORIZONTAL) border.Add(colSizer, 1, wxEXPAND | wxALL, 4) panel.SetSizer(border) panel.SetAutoLayout(true) + + EVT_BUTTON(self.creditsBox, okButton.GetId(), self.closeCredits) + + self.creditsBox.Show () + border.Fit(panel) + self.creditsBox.Fit() + + def closeAbout(self, event): + self.aboutBox.Close () + + def closeCredits(self, event): + self.creditsBox.Close () - EVT_LEFT_DOWN(self.aboutText, self.donate) - EVT_CLOSE(frame, self.done) - EVT_BUTTON(frame, self.cancelButton.GetId(), self.done) - EVT_INVOKE(frame, self.onInvoke) - self.frame.Show() + def closeDetail(self, event): + self.detailBox.Close () + + def donatelink(self, event): + Thread(target = open_new('https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=bram@bitconjurer.org&item_name=BitTorrent&amount=5.00&submit=donate')).start() - def donate(self, event): - Thread(target = self.donate2).start() + def aboutlink(self, event): + Thread(target = open_new('http://bitconjurer.org/BitTorrent/')).start() - def donate2(self): - open_new('http://bitconjurer.org/BitTorrent/donate.html') + def explink(self, event): + Thread(target = open_new('http://ei.kefro.st/projects/btclient/')).start() + + def licenselink(self, event): + Thread(target = open_new('http://ei.kefro.st/projects/btclient/LICENSE.TXT')).start() def onInvoke(self, event): if not self.flag.isSet(): @@ -122,21 +575,44 @@ def updateStatus(self, fractionDone = None, timeEst = None, downRate = None, upRate = None, - activity=None): - self.invokeLater(self.onUpdateStatus, [fractionDone, timeEst, downRate, upRate, activity]) + activity=None, up = None, down = None, statistics = None): + self.invokeLater(self.onUpdateStatus, [fractionDone, timeEst, downRate, upRate, activity, up, down, statistics]) - def onUpdateStatus(self, fractionDone, timeEst, downRate, upRate, activity): + def onUpdateStatus(self, fractionDone, timeEst, downRate, upRate, activity, up, down, statistics): if fractionDone is not None and not self.fin: self.gauge.SetValue(int(fractionDone * 1000)) self.frame.SetTitle('%d%% %s - BitTorrent %s' % (int(fractionDone*100), self.filename, version)) if timeEst is not None: - self.timeEstText.SetLabel(hours(timeEst)) + self.timeText.SetLabel(hours(time () - self.starttime) + ' / ' + hours(timeEst)) if activity is not None and not self.fin: - self.timeEstText.SetLabel(activity) + self.timeText.SetLabel(hours(time () - self.starttime) + ' / ' + activity) if downRate is not None: - self.downRateText.SetLabel('%.0f kB/s' % (float(downRate) / (1 << 10))) + self.downRateText.SetLabel('%.0f kB/s' % (float(downRate) / (1000))) if upRate is not None: - self.upRateText.SetLabel('%.0f kB/s' % (float(upRate) / (1 << 10))) + self.upRateText.SetLabel('%.0f kB/s' % (float(upRate) / (1000))) + if down is not None: + self.downText.SetLabel('%.2f MiB' % (float(down) / (1 << 20))) + if up is not None: + self.upText.SetLabel('%.2f MiB' % (float(up) / (1 << 20))) + if hasattr(self.frame, "tbicon"): + icontext='BitTorrent %s' % self.filename + if fractionDone is not None and not self.fin: + icontext=icontext+' %d%%' % (int(fractionDone*100)) + if upRate is not None: + icontext=icontext+' u:%.0f kB/s' %(float(upRate) / (1000)) + if downRate is not None: + icontext=icontext+' u:%.0f kB/s' %(float(downRate) / (1000)) + self.frame.tbicon.SetIcon(self.icon,icontext) + if statistics is not None: + if (statistics.shareRating < 0) or (statistics.shareRating > 1000): + self.shareRatingText.SetLabel('oo') + else: + self.shareRatingText.SetLabel('%.3f' % (statistics.shareRating)) + if not self.fin: + self.seedStatusText.SetLabel('connected to %d seeds; also seeing %.3f distributed copies' % (statistics.numSeeds,0.001*int(1000*statistics.numCopies))) + else: + self.seedStatusText.SetLabel('%d seeds seen recently; also seeing %.3f distributed copies' % (statistics.numOldSeeds,0.001*int(1000*statistics.numCopies))) + self.peerStatusText.SetLabel('connected to %d peers with an average of %.1f%% completed (total speed %.1f kB/s)' % (statistics.numPeers,statistics.percentDone,float(statistics.torrentRate) / (1000))) def finished(self): self.fin = true @@ -150,14 +626,19 @@ self.invokeLater(self.onErrorEvent, [errormsg]) def onFinishEvent(self): - self.timeEstText.SetLabel('Download Succeeded!') + self.timeText.SetLabel(hours(time () - self.starttime) + ' / ' +'Download Succeeded!') self.cancelButton.SetLabel('Finish') self.gauge.SetValue(1000) self.frame.SetTitle('%s - Upload - BitTorrent %s' % (self.filename, version)) + if (sys.platform == 'win32'): + self.icon = wxIcon('icon_done.ico', wxBITMAP_TYPE_ICO) + self.frame.SetIcon(self.icon) + if hasattr(self.frame, "tbicon"): + self.frame.tbicon.SetIcon(self.icon, "BitTorrent - Finished") self.downRateText.SetLabel('') def onFailEvent(self): - self.timeEstText.SetLabel('Failed!') + self.timeText.SetLabel(hours(time () - self.starttime) + ' / ' +'Failed!') self.cancelButton.SetLabel('Close') self.gauge.SetValue(0) self.downRateText.SetLabel('') @@ -182,9 +663,12 @@ self.done(None) else: bucket[0] = dl.GetPath() - self.fileNameText.SetLabel('%s (%.1f MB)' % (default, float(size) / (1 << 20))) - self.timeEstText.SetLabel('Starting up...') + self.fileNameText.SetLabel('%s (%.1f MiB)' % (default, float(size) / (1 << 20))) + self.timeText.SetLabel(hours(time () - self.starttime) + ' / ' +'Starting up...') self.fileDestText.SetLabel(dl.GetPath()) + # Kludge to make details and about catch the event + self.frame.SetSize ((self.frame.GetSizeTuple()[0]+1, self.frame.GetSizeTuple()[1]+1)) + self.frame.SetSize ((self.frame.GetSizeTuple()[0]-1, self.frame.GetSizeTuple()[1]-1)) self.filename = default self.frame.SetTitle(default + '- BitTorrent ' + version) f.set() @@ -193,9 +677,28 @@ self.fileDestText.SetLabel(path) def done(self, event): + if hasattr(self.frame, "tbicon"): + self.frame.tbicon.Destroy() + del self.frame.tbicon self.flag.set() + if (self.detailBox is not None): + try: + self.detailBox.Close () + except wxPyDeadObjectError, e: + self.detailBox = None + if (self.aboutBox is not None): + try: + self.aboutBox.Close () + except wxPyDeadObjectError, e: + self.aboutBox = None + if (self.creditsBox is not None): + try: + self.creditsBox.Close () + except wxPyDeadObjectError, e: + self.creditsBox = None self.frame.Destroy() + class btWxApp(wxApp): def __init__(self, x, params): self.params = params @@ -215,7 +718,9 @@ app.MainLoop() def next(params, d, doneflag): - download(params, d.chooseFile, d.updateStatus, d.finished, d.error, doneflag, 100, d.newpath) + dow = Download () + d.dow = dow + dow.download(params, d.chooseFile, d.updateStatus, d.finished, d.error, doneflag, 100, d.newpath) if not d.fin: d.failed() diff -u ./btdownloadheadless.py ../BitTorrent-3.2.1b-experimental/btdownloadheadless.py --- ./btdownloadheadless.py 2003-03-17 11:26:39.000000000 -0800 +++ ../BitTorrent-3.2.1b-experimental/btdownloadheadless.py 2003-04-11 18:06:58.000000000 -0700 @@ -35,6 +35,9 @@ self.downloadTo = '' self.downRate = '' self.upRate = '' + self.shareRating = '' + self.seedStatus = '' + self.peerStatus = '' self.errors = [] def finished(self): @@ -56,7 +59,8 @@ self.display() def display(self, fractionDone = None, timeEst = None, - downRate = None, upRate = None, activity = None): + downRate = None, upRate = None, activity = None, + statistics = None, **kws): if fractionDone is not None: self.percentDone = str(float(int(fractionDone * 1000)) / 10) if timeEst is not None: @@ -64,9 +68,19 @@ if activity is not None and not self.done: self.timeEst = activity if downRate is not None: - self.downRate = '%.0f kB/s' % (float(downRate) / (1 << 10)) + self.downRate = '%.1f kB/s' % (float(downRate) / (1 << 10)) if upRate is not None: - self.upRate = '%.0f kB/s' % (float(upRate) / (1 << 10)) + self.upRate = '%.1f kB/s' % (float(upRate) / (1 << 10)) + if statistics is not None: + if (statistics.shareRating < 0) or (statistics.shareRating > 100): + self.shareRating = 'oo (%.1f MB up / %.1f MB down)' % (float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20)) + else: + self.shareRating = '%.3f (%.1f MB up / %.1f MB down)' % (statistics.shareRating, float(statistics.upTotal) / (1<<20), float(statistics.downTotal) / (1<<20)) + if not self.done: + self.seedStatus = '%d seen now, plus %.3f distributed copies' % (statistics.numSeeds,0.001*int(1000*statistics.numCopies)) + else: + self.seedStatus = '%d seen recently, plus %.3f distributed copies' % (statistics.numOldSeeds,0.001*int(1000*statistics.numCopies)) + self.peerStatus = '%d seen now, %.1f%% done at %.1f kB/s' % (statistics.numPeers,statistics.percentDone,float(statistics.torrentRate) / (1 << 10)) print '\n\n\n\n' for err in self.errors: print 'ERROR:\n' + err + '\n' @@ -76,6 +90,9 @@ print 'download to: ', self.downloadTo print 'download rate: ', self.downRate print 'upload rate: ', self.upRate + print 'share rating: ', self.shareRating + print 'seed status: ', self.seedStatus + print 'peer status: ', self.peerStatus stdout.flush() def chooseFile(self, default, size, saveas, dir): diff -u ./btlaunchmanycurses.py ../BitTorrent-3.2.1b-experimental/btlaunchmanycurses.py --- ./btlaunchmanycurses.py 2003-03-27 21:14:08.000000000 -0800 +++ ../BitTorrent-3.2.1b-experimental/btlaunchmanycurses.py 2003-04-11 18:07:14.000000000 -0700 @@ -1,37 +1,37 @@ #!/usr/bin/env python # Written by Michael Janssen (jamuraa at base0 dot net) -# heavily borrowed code from btlaunchmany.py written by Bram Cohen +# originally heavily borrowed code from btlaunchmany.py by Bram Cohen # and btdownloadcurses.py written by Henry 'Pi' James -# fmttime and fmtsize mercilessly stolen from btdownloadcurses. 0% of them are mine. +# now not so much. +# fmttime and fmtsize stolen from btdownloadcurses. # see LICENSE.txt for license information from BitTorrent.download import download -from threading import Thread, Event +from threading import Thread, Event, RLock from os import listdir from os.path import abspath, join, exists from sys import argv, version, stdout, exit from time import sleep -from signal import signal, SIGWINCH +from signal import signal, SIGWINCH import traceback assert version >= '2', "Install Python 2.0 or greater" def fmttime(n): if n == -1: - return 'download not progressing (file not being uploaded by others?)' + return 'download not progressing (no seeds?)' if n == 0: - return 'download complete!' + return 'download complete!' n = int(n) m, s = divmod(n, 60) h, m = divmod(m, 60) if h > 1000000: - return 'n/a' + return 'n/a' return 'finishing in %d:%02d:%02d' % (h, m, s) - def fmtsize(n): - unit = [' B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + unit = [' B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] i = 0 if (n > 999): i = 1 @@ -48,222 +48,219 @@ def dummy(*args, **kwargs): pass -def winch_handler(signum, stackframe): - global scrwin, mainwin, mainwinw, headerwin, totalwin, statuswin - global scrpan, mainpan, headerpan, totalpan, statuspan - # SIGWINCH. Remake the frames! - ## Curses Trickery - curses.endwin() - # delete scrwin somehow? - scrwin.refresh() - scrwin = curses.newwin(0, 0, 0, 0) - scrh, scrw = scrwin.getmaxyx() - scrpan = curses.panel.new_panel(scrwin) - ### Curses Setup - scrh, scrw = scrwin.getmaxyx() - scrpan = curses.panel.new_panel(scrwin) - mainwinh = scrh - 5 # - 2 (bars) - 1 (debugwin) - 1 (borderwin) - 1 (totalwin) - mainwinw = scrw - 4 # - 2 (bars) - 2 (spaces) - mainwiny = 2 # + 1 (bar) + 1 (titles) - mainwinx = 2 # + 1 (bar) + 1 (space) - # + 1 to all windows so we can write at mainwinw - mainwin = curses.newwin(mainwinh, mainwinw+1, mainwiny, mainwinx) - mainpan = curses.panel.new_panel(mainwin) - - headerwin = curses.newwin(1, mainwinw+1, 1, mainwinx) - headerpan = curses.panel.new_panel(headerwin) - - totalwin = curses.newwin(1, mainwinw+1, scrh-3, mainwinx) - totalpan = curses.panel.new_panel(totalwin) - - statuswin = curses.newwin(1, mainwinw+1, scrh-2, mainwinx) - statuspan = curses.panel.new_panel(statuswin) - mainwin.scrollok(0) - headerwin.scrollok(0) - totalwin.scrollok(0) - statuswin.addstr(0, 0, 'window resize: %s x %s' % (scrw, scrh)) - statuswin.scrollok(0) - prepare_display() - +threads = {} ext = '.torrent' -wininfo = {} +status = 'btlaunchmany starting..' +filecheck = RLock() -def runmany(d, params): - threads = [] +def dropdir_mainloop(d, params): deadfiles = [] - try: - while 1: - files = listdir(d) - # new files - for file in files: - if file[-len(ext):] == ext: - if file not in [x.getName() for x in threads] + deadfiles: - wininfo[file] = {'basex': 2 * len(threads), 'killflag': Event()} - statuswin.erase() - statuswin.addnstr(0, 0,'new torrent detected: %s' % file, mainwinw) - threads.append(Thread(target = SingleCursesDisplayer(join(d, file), params, file).download, name = file)) - threads[-1].start() - # gone files - for i in range(len(threads)): - try: - threadname = threads[i].getName() - except IndexError: - # raised when we delete a thread from earlier, so the last ones fall out of range - continue - if not threads[i].isAlive(): - # died without "permission" - deadfiles.append(threadname) - statuswin.erase() - statuswin.addnstr(0, 0,'torrent died: %s' % threadname, mainwinw) - - # rearrange remaining windows - mainwin.addnstr(wininfo[threadname]['basex'], 0, ' ' * mainwinw, mainwinw) - mainwin.addnstr(wininfo[threadname]['basex']+1, 0, ' ' * mainwinw, mainwinw) - for _, win in wininfo.items(): - if win['basex'] > wininfo[threadname]['basex']: - win['basex'] = win['basex'] - 2 - del wininfo[threadname] - del threads[i] - elif threadname not in files: - wininfo[threadname]['killflag'].set() - # rearrange remaining windows - mainwin.addnstr(wininfo[threadname]['basex'], 0, ' ' * mainwinw, mainwinw) - mainwin.addnstr(wininfo[threadname]['basex']+1, 0, ' ' * mainwinw, mainwinw) - for _, win in wininfo.items(): - if win['basex'] > wininfo[threadname]['basex']: - win['basex'] = win['basex'] - 2 - threads[i].join() - del wininfo[threadname] - del threads[i] - # update the totals - totalup = 0 - totaldown = 0 - for info in wininfo.values(): - totalup += info.get('uprate', 0) - totaldown += info.get('downrate', 0) - stringup = '%s/s' % fmtsize(totalup) - stringdown = '%s/s' % fmtsize(totaldown) - - totalwin.addnstr(0, mainwinw-20, ' ' * 20, 20) - totalwin.addnstr(0, mainwinw-20 + (10 - len(stringdown)), stringdown, 10) - totalwin.addnstr(0, mainwinw-10 + (10 - len(stringup)), stringup, 10) - - sleep(1) - except KeyboardInterrupt: - statuswin.erase() - statuswin.addnstr(0, 0,'^C caught.. cleaning up.. ', mainwinw) + global threads, status + while 1: + files = listdir(d) + # new files + for file in files: + if file[-len(ext):] == ext: + if file not in threads.keys() + deadfiles: + threads[file] = {'kill': Event(), 'try': 1} + status = 'New torrent: %s' % file + threads[file]['thread'] = Thread(target = StatusUpdater(join(d, file), params, file).download, name = file) + threads[file]['thread'].start() + # files with multiple tries + for file, threadinfo in threads.items(): + if threadinfo.get('timeout') == 0: + # Zero seconds left, try and start the thing again. + threadinfo['try'] = threadinfo['try'] + 1 + threadinfo['thread'] = Thread(target = StatusUpdater(join(d, file), params, file).download, name = file) + threadinfo['thread'].start() + threadinfo['timeout'] = -1 + elif threadinfo.get('timeout') > 0: + # Decrement our counter by 1 + threadinfo['timeout'] = threadinfo['timeout'] - 1 + elif not threadinfo['thread'].isAlive(): + # died without permission + if threadinfo.get('try') == 6: + # Died on the sixth try? You're dead. + deadfiles.append(file) + status = '%s died 6 times, added to dead list' % file + del threads[file] + else: + del threadinfo['thread'] + threadinfo['timeout'] = 10 + # dealing with files that dissapear + if file not in files: + status = 'Gone torrent: %s' % file + threadinfo['kill'].set() + threadinfo['thread'].join() + del threads[file] + for file in deadfiles: + # if the file dissapears, remove it from our dead list + if file not in files: + deadfiles.remove(file) + sleep(1) + +def display_thread(displaykiller): + interval = 0.1 + global threads, status + while 1: + # display file info + if (displaykiller.isSet()): + break + mainwin.erase() + winpos = 0 + totalup = 0 + totaldown = 0 + for file, threadinfo in threads.items(): + uprate = threadinfo.get('uprate', 0) + downrate = threadinfo.get('downrate', 0) + uptxt = '%s/s' % fmtsize(uprate) + downtxt = '%s/s' % fmtsize(downrate) + filesize = threadinfo.get('filesize', 'N/A') + mainwin.addnstr(winpos, 0, threadinfo.get('savefile', file), mainwinw - 28, curses.A_BOLD) + mainwin.addnstr(winpos, mainwinw - 28 + (8 - len(filesize)), filesize, 8) + mainwin.addnstr(winpos, mainwinw - 20 + (10 - len(downtxt)), downtxt, 10) + mainwin.addnstr(winpos, mainwinw - 10 + (10 - len(uptxt)), uptxt, 10) + winpos = winpos + 1 + mainwin.addnstr(winpos, 0, '^--- ', 5) + if threadinfo.get('timeout', 0) > 0: + mainwin.addnstr(winpos, 6, 'Try %d: died, retrying in %d' % (threadinfo.get('try', 1), threadinfo.get('timeout')), mainwinw - 5) + else: + mainwin.addnstr(winpos, 6, threadinfo.get('status',''), mainwinw - 5) + winpos = winpos + 1 + totalup += uprate + totaldown += downrate + # display statusline + statuswin.erase() + statuswin.addnstr(0, 0, status, mainwinw) + # display totals line + totaluptxt = '%s/s' % fmtsize(totaldown) + totaldowntxt = '%s/s' % fmtsize(totalup) + + totalwin.erase() + totalwin.addnstr(0, mainwinw - 27, 'Totals:', 7); + totalwin.addnstr(0, mainwinw - 20 + (10 - len(totaluptxt)), totaluptxt, 10) + totalwin.addnstr(0, mainwinw - 10 + (10 - len(totaldowntxt)), totaldowntxt, 10) curses.panel.update_panels() curses.doupdate() - for thread in threads: - threadname = thread.getName() - statuswin.erase() - statuswin.addnstr(0, 0,'killing torrent %s' % threadname, mainwinw) - curses.panel.update_panels() - curses.doupdate() - wininfo[threadname]['killflag'].set() - thread.join() - statuswin.erase() - statuswin.addnstr(0, 0,'Bye Bye!', mainwinw) - curses.panel.update_panels() - curses.doupdate() - + sleep(interval) -class SingleCursesDisplayer: +class StatusUpdater: def __init__(self, file, params, name): self.file = file self.params = params - self.status = 'starting...' - self.doingdown = '' - self.doingup = '' + self.name = name + self.myinfo = threads[name] self.done = 0 - self.downfile = '' - self.localfile = '' - self.fileSize = '' - self.activity = '' - self.myname = name - self.basex = wininfo[self.myname]['basex'] + self.checking = 0 + self.activity = 'starting up...' self.display() + self.myinfo['errors'] = [] - def download(self): - download(self.params + ['--responsefile', self.file], self.choose, self.display, self.finished, self.err, wininfo[self.myname]['killflag'], mainwinw) - statuswin.erase(); - statuswin.addnstr(0, 0, '%s: torrent stopped' % self.localfile, mainwinw) - curses.panel.update_panels() - curses.doupdate() + def download(self): + download(self.params + ['--responsefile', self.file], self.choose, self.display, self.finished, self.err, self.myinfo['kill'], 80) + status = 'Torrent %s stopped' % self.file - def finished(self): + def finished(self): self.done = 1 - self.doingdown = '--- KB/s' + self.myinfo['done'] = 1 self.activity = 'download succeeded!' self.display(fractionDone = 1) - + def err(self, msg): - self.status = msg + self.myinfo['errors'].append(msg) self.display() - def failed(self): - self.activity = 'download failed!' - self.display() - - def choose(self, default, size, saveas, dir): - self.downfile = default - self.fileSize = fmtsize(size) - if saveas == '': + def failed(self): + self.activity = 'download failed!' + self.display() + + def choose(self, default, size, saveas, dir): + global filecheck + self.myinfo['downfile'] = default + self.myinfo['filesize'] = fmtsize(size) + if saveas == '': saveas = default - self.localfile = abspath(saveas) + # it asks me where I want to save it before checking the file.. + if (exists(saveas)): + # file will get checked + while (not filecheck.acquire(blocking = 0) and not self.myinfo['kill'].isSet()): + self.myinfo['status'] = 'Waiting for disk check...' + sleep(0.1) + self.checking = 1 + self.myinfo['savefile'] = saveas return saveas - def display(self, fractionDone = None, timeEst = None, downRate = None, upRate = None, activity = None): - if self.basex != wininfo[self.myname]['basex']: - # leave nothing but blank space - mainwin.addnstr(self.basex, 0, ' ' * 1000, mainwinw) - mainwin.addnstr(self.basex+1, 0, ' ' * 1000, mainwinw) - self.basex = wininfo[self.myname]['basex'] - if activity is not None and not self.done: + def display(self, fractionDone = None, timeEst = None, downRate = None, upRate = None, activity = None, statistics = None, **kws): + global filecheck, status + if activity is not None and not self.done: self.activity = activity - elif timeEst is not None: + elif timeEst is not None: self.activity = fmttime(timeEst) - if fractionDone is not None: - self.status = '%s (%.1f%%)' % (self.activity, fractionDone * 100) + if fractionDone is not None: + self.myinfo['status'] = '%s (%.1f%%)' % (self.activity, fractionDone * 100) + if fractionDone == 1 and self.checking: + # we finished checking our files. + filecheck.release() + self.checking = 0 else: - self.status = self.activity + self.myinfo['status'] = self.activity if downRate is None: downRate = 0 if upRate is None: upRate = 0 - wininfo[self.myname]['downrate'] = int(downRate) - wininfo[self.myname]['uprate'] = int(upRate) - self.doingdown = '%s/s' % fmtsize(int(downRate)) - self.doingup = '%s/s' % fmtsize(int(upRate)) - - # clear the stats section - mainwin.addnstr(self.basex, 0, ' ' * mainwinw, mainwinw) - mainwin.addnstr(self.basex, 0, self.downfile, mainwinw - 28, curses.A_BOLD) - mainwin.addnstr(self.basex, mainwinw - 28 + (8 - len(self.fileSize)), self.fileSize, 8) - mainwin.addnstr(self.basex, mainwinw - 20 + (10 - len(self.doingdown)), self.doingdown, 10) - mainwin.addnstr(self.basex, mainwinw - 10 + (10 - len(self.doingup)), self.doingup, 10) - # clear the status bar first - mainwin.addnstr(self.basex+1, 0, ' ' * mainwinw, mainwinw) - mainwin.addnstr(self.basex+1, 0, '^--- ', 5) - mainwin.addnstr(self.basex+1, 6, self.status, (mainwinw-1) - 5) - curses.panel.update_panels() - curses.doupdate() + self.myinfo['uprate'] = int(upRate) + self.myinfo['downrate'] = int(downRate) -def prepare_display(): +def prepare_display(): global mainwinw, scrwin, headerwin, totalwin scrwin.border(ord('|'),ord('|'),ord('-'),ord('-'),ord(' '),ord(' '),ord(' '),ord(' ')) headerwin.addnstr(0, 0, 'Filename', mainwinw - 25, curses.A_BOLD) headerwin.addnstr(0, mainwinw - 24, 'Size', 4); headerwin.addnstr(0, mainwinw - 18, 'Download', 8); headerwin.addnstr(0, mainwinw - 6, 'Upload', 6); - totalwin.addnstr(0, mainwinw - 27, 'Totals:', 7); - curses.panel.update_panels() curses.doupdate() +def winch_handler(signum, stackframe): + global scrwin, mainwin, mainwinw, headerwin, totalwin, statuswin + global scrpan, mainpan, headerpan, totalpan, statuspan + # SIGWINCH. Remake the frames! + ## Curses Trickery + curses.endwin() + # delete scrwin somehow? + scrwin.refresh() + scrwin = curses.newwin(0, 0, 0, 0) + scrh, scrw = scrwin.getmaxyx() + scrpan = curses.panel.new_panel(scrwin) + ### Curses Setup + scrh, scrw = scrwin.getmaxyx() + scrpan = curses.panel.new_panel(scrwin) + mainwinh = scrh - 5 # - 2 (bars) - 1 (debugwin) - 1 (borderwin) - 1 (totalwin) + mainwinw = scrw - 4 # - 2 (bars) - 2 (spaces) + mainwiny = 2 # + 1 (bar) + 1 (titles) + mainwinx = 2 # + 1 (bar) + 1 (space) + # + 1 to all windows so we can write at mainwinw + mainwin = curses.newwin(mainwinh, mainwinw+1, mainwiny, mainwinx) + mainpan = curses.panel.new_panel(mainwin) + + headerwin = curses.newwin(1, mainwinw+1, 1, mainwinx) + headerpan = curses.panel.new_panel(headerwin) + + totalwin = curses.newwin(1, mainwinw+1, scrh-3, mainwinx) + totalpan = curses.panel.new_panel(totalwin) + + statuswin = curses.newwin(1, mainwinw+1, scrh-2, mainwinx) + statuspan = curses.panel.new_panel(statuswin) + mainwin.scrollok(0) + headerwin.scrollok(0) + totalwin.scrollok(0) + statuswin.addstr(0, 0, 'window resize: %s x %s' % (scrw, scrh)) + statuswin.scrollok(0) + prepare_display() + if __name__ == '__main__': - if (len(argv) < 2): + if (len(argv) < 2): print """Usage: btlaunchmanycurses.py - directory to look for .torrent files (non-recursive) - options to be applied to all torrents (see btdownloadheadless.py) @@ -272,7 +269,6 @@ try: import curses import curses.panel - scrwin = curses.initscr() curses.noecho() curses.cbreak() @@ -280,39 +276,51 @@ print 'Textmode GUI initialization failed, cannot proceed.' exit(-1) try: - try: - signal(SIGWINCH, winch_handler) - ### Curses Setup - scrh, scrw = scrwin.getmaxyx() - scrpan = curses.panel.new_panel(scrwin) - mainwinh = scrh - 5 # - 2 (bars) - 1 (debugwin) - 1 (borderwin) - 1 (totalwin) - mainwinw = scrw - 4 # - 2 (bars) - 2 (spaces) - mainwiny = 2 # + 1 (bar) + 1 (titles) - mainwinx = 2 # + 1 (bar) + 1 (space) - # + 1 to all windows so we can write at mainwinw - mainwin = curses.newwin(mainwinh, mainwinw+1, mainwiny, mainwinx) - mainpan = curses.panel.new_panel(mainwin) - - headerwin = curses.newwin(1, mainwinw+1, 1, mainwinx) - headerpan = curses.panel.new_panel(headerwin) - - totalwin = curses.newwin(1, mainwinw+1, scrh-3, mainwinx) - totalpan = curses.panel.new_panel(totalwin) - - statuswin = curses.newwin(1, mainwinw+1, scrh-2, mainwinx) - statuspan = curses.panel.new_panel(statuswin) - mainwin.scrollok(0) - headerwin.scrollok(0) - totalwin.scrollok(0) - statuswin.addstr(0, 0, 'btlaunchmany started') - statuswin.scrollok(0) - prepare_display() - curses.panel.update_panels() - curses.doupdate() - runmany(argv[1], argv[2:]) - finally: - curses.nocbreak() - curses.echo() - curses.endwin() + signal(SIGWINCH, winch_handler) + ### Curses Setup + scrh, scrw = scrwin.getmaxyx() + scrpan = curses.panel.new_panel(scrwin) + mainwinh = scrh - 5 # - 2 (bars) - 1 (debugwin) - 1 (borderwin) - 1 (totalwin) + mainwinw = scrw - 4 # - 2 (bars) - 2 (spaces) + mainwiny = 2 # + 1 (bar) + 1 (titles) + mainwinx = 2 # + 1 (bar) + 1 (space) + # + 1 to all windows so we can write at mainwinw + mainwin = curses.newwin(mainwinh, mainwinw+1, mainwiny, mainwinx) + mainpan = curses.panel.new_panel(mainwin) + + headerwin = curses.newwin(1, mainwinw+1, 1, mainwinx) + headerpan = curses.panel.new_panel(headerwin) + + totalwin = curses.newwin(1, mainwinw+1, scrh-3, mainwinx) + totalpan = curses.panel.new_panel(totalwin) + + statuswin = curses.newwin(1, mainwinw+1, scrh-2, mainwinx) + statuspan = curses.panel.new_panel(statuswin) + mainwin.scrollok(0) + headerwin.scrollok(0) + totalwin.scrollok(0) + statuswin.addstr(0, 0, 'btlaunchmany started') + statuswin.scrollok(0) + prepare_display() + displaykiller = Event() + displaythread = Thread(target = display_thread, name = 'display', args = [displaykiller]) + displaythread.setDaemon(1) + displaythread.start() + dropdir_mainloop(argv[1], argv[2:]) + except KeyboardInterrupt: + status = '^C caught! Killing torrents..' + for file, threadinfo in threads.items(): + status = 'Killing torrent %s' % file + threadinfo['kill'].set() + threadinfo['thread'].join() + del threads[file] + displaykiller.set() + displaythread.join() + curses.nocbreak() + curses.echo() + curses.endwin() except: + curses.nocbreak() + curses.echo() + curses.endwin() traceback.print_exc() diff -u ./btlaunchmany.py ../BitTorrent-3.2.1b-experimental/btlaunchmany.py --- ./btlaunchmany.py 2003-03-27 17:05:57.000000000 -0800 +++ ../BitTorrent-3.2.1b-experimental/btlaunchmany.py 2003-04-11 18:08:51.000000000 -0700 @@ -92,7 +92,7 @@ def status(self, fractionDone = None, timeEst = None, downRate = None, upRate = None, - activity = None): + activity = None, statistics = None, **kws): if fractionDone is not None: newpercent = int(fractionDone*100) if newpercent != self.percentDone: diff -u ./btmakemetafile.py ../BitTorrent-3.2.1b-experimental/btmakemetafile.py --- ./btmakemetafile.py 2003-03-29 16:57:03.000000000 -0800 +++ ../BitTorrent-3.2.1b-experimental/btmakemetafile.py 2003-04-11 11:35:08.000000000 -0700 @@ -14,10 +14,13 @@ from BitTorrent.btformats import check_info from BitTorrent.parseargs import parseargs, formatDefinitions from threading import Event +from time import asctime defaults = [ ('piece_size_pow2', None, 18, "which power of 2 to set the piece size to"), + ('comment', None, '', + "optional human-readable comment to put in .torrent"), ] ignore = ['core', 'CVS'] # ignoring these files could be trouble @@ -26,7 +29,7 @@ pass def make_meta_file(file, url, piece_len_exp = 18, - flag = Event(), progress = dummy, progress_percent=1): + flag = Event(), progress = dummy, progress_percent=1, comment = None): piece_length = 2 ** piece_len_exp a, b = split(file) if b == '': @@ -38,7 +41,10 @@ return check_info(info) h = open(f, 'wb') - h.write(bencode({'info': info, 'announce': strip(url)})) + data = {'info': info, 'announce': strip(url), 'creation date': asctime()} + if comment: + data['comment'] = comment + h.write(bencode(data)) h.close() def calcsize(file): @@ -138,7 +144,8 @@ else: try: config, args = parseargs(argv[3:], defaults, 0, 0) - make_meta_file(argv[1], argv[2], config['piece_size_pow2'], progress = prog) + make_meta_file(argv[1], argv[2], config['piece_size_pow2'], progress = prog, + comment = config['comment']) except ValueError, e: print 'error: ' + str(e) print 'run with no args for parameter explanations' Only in ../BitTorrent-3.2.1b-experimental/: build diff -u ./credits.txt ../BitTorrent-3.2.1b-experimental/credits.txt --- ./credits.txt 2003-03-27 19:05:46.000000000 -0800 +++ ../BitTorrent-3.2.1b-experimental/credits.txt 2003-04-11 11:53:25.000000000 -0700 @@ -23,3 +23,5 @@ Loring Holden Robert Stone Michael Janssen +Eike Frost +Andrew Todd Only in ../BitTorrent-3.2.1b-experimental/: CVS Only in ../BitTorrent-3.2.1b-experimental/: docs-rescue.torrent Only in .: FAQ.txt Only in ../BitTorrent-3.2.1b-experimental/: icon_bt.ico Only in ../BitTorrent-3.2.1b-experimental/: icon_done.ico diff -u ./MANIFEST.in ../BitTorrent-3.2.1b-experimental/MANIFEST.in --- ./MANIFEST.in 2002-07-14 11:50:52.000000000 -0700 +++ ../BitTorrent-3.2.1b-experimental/MANIFEST.in 2003-04-11 11:33:36.000000000 -0700 @@ -5,7 +5,6 @@ include btdownloadlibrary.py include btdownloadheadless.py include btdownloadgui.py -include btdownloadprefetched.py include btmakemetafile.py include bttrack.py include bttest.py Common subdirectories: ./osx and ../BitTorrent-3.2.1b-experimental/osx Only in .: todo.txt