From 593e4e4f449634403dc40d0560085e8b10950beb Mon Sep 17 00:00:00 2001 From: Razvan Deaconescu Date: Tue, 8 Dec 2009 19:14:20 +0200 Subject: [PATCH] added libtorrent parse scripts --- scripts/parse_log_files | 25 + scripts/parse_status_files | 18 + .../Tribler/Test/API/contentdir/file.avi | 1371 ----------------- tribler-mod/Tribler/Test/API/file.wmv | Bin 82948 -> 0 bytes tribler-mod/Tribler/Test/API/file2.wmv | Bin 377860 -> 0 bytes 5 files changed, 43 insertions(+), 1371 deletions(-) create mode 100755 scripts/parse_log_files create mode 100755 scripts/parse_status_files delete mode 100644 tribler-mod/Tribler/Test/API/contentdir/file.avi delete mode 100644 tribler-mod/Tribler/Test/API/file.wmv delete mode 100644 tribler-mod/Tribler/Test/API/file2.wmv diff --git a/scripts/parse_log_files b/scripts/parse_log_files new file mode 100755 index 0000000..b1b3daf --- /dev/null +++ b/scripts/parse_log_files @@ -0,0 +1,25 @@ +#!/bin/bash + +DATABASE_FILE=client-limitation-experiment.db +REPOSITORY_ROOT=../../../cs-p2p-next + +echo "Running libtorrent log file parser." +echo "Database file is $DATABASE_FILE" +echo -e "Repository root is $REPOSITORY_ROOT" +echo -e "Going hot ...\n" + +id=0 +start_from=21 +for folder in $(find .. -type d -name 'p2p-next-*' | sort); do + ((id++)) + if test $id -lt $start_from; then + continue + fi + + echo "Parsing status folder $folder for session id $id ... " + + for log_file in $(find $folder/log/libtorrent_logs5* -type f); do + yes y | PYTHONPATH=$REPOSITORY_ROOT/auto/db/ python $REPOSITORY_ROOT/log-parser/libtorrent/LogParser.py -i $id -f $log_file $DATABASE_FILE + done + +done diff --git a/scripts/parse_status_files b/scripts/parse_status_files new file mode 100755 index 0000000..7a14641 --- /dev/null +++ b/scripts/parse_status_files @@ -0,0 +1,18 @@ +#!/bin/bash + +DATABASE_FILE=client-test.db +REPOSITORY_ROOT=../../../cs-p2p-next + +echo "Running status file parser." +echo "Database file is $DATABASE_FILE" +echo -e "Repository root is $REPOSITORY_ROOT" +echo -e "Going hot ...\n" + +id=0 +for status_file in $(find .. -type f -name 'hrktorrent_file*.log' | sort); do + ((id++)) + echo -n "Parsing status file $status_file for session id $id ... " + + PYTHONPATH=$REPOSITORY_ROOT/auto/db/ python $REPOSITORY_ROOT/log-parser/libtorrent/StatusParser.py -i $id -f $status_file $DATABASE_FILE + echo "done" +done diff --git a/tribler-mod/Tribler/Test/API/contentdir/file.avi b/tribler-mod/Tribler/Test/API/contentdir/file.avi deleted file mode 100644 index 08a5020..0000000 --- a/tribler-mod/Tribler/Test/API/contentdir/file.avi +++ /dev/null @@ -1,1371 +0,0 @@ -#!/usr/bin/python - -######################################################################### -# -# Author : Choopan RATTANAPOKA, Jie Yang, Arno Bakker -# -# Description : Main ABC [Yet Another Bittorrent Client] python script. -# you can run from source code by using -# >python abc.py -# need Python, WxPython in order to run from source code. -######################################################################### - -# Arno: M2Crypto overrides the method for https:// in the -# standard Python libraries. This causes msnlib to fail and makes Tribler -# freakout when "http://www.tribler.org/version" is redirected to -# "https://www.tribler.org/version/" (which happened during our website -# changeover) Until M2Crypto 0.16 is patched I'll restore the method to the -# original, as follows. -# -# This must be done in the first python file that is started. -# - -import urllib -original_open_https = urllib.URLopener.open_https -import M2Crypto -urllib.URLopener.open_https = original_open_https - -import sys, locale -import os -import wx, commands -from wx import xrc -#import hotshot - -if sys.platform == "darwin": - # on Mac, we can only load VLC libraries - # relative to the location of tribler.py - os.chdir(os.path.abspath(os.path.dirname(sys.argv[0]))) - -from threading import Thread, Timer, Event,currentThread,enumerate -from time import time, ctime, sleep -from traceback import print_exc, print_stack -from cStringIO import StringIO -import urllib - -from interconn import ServerListener, ClientPassParam -from launchmanycore import ABCLaunchMany - -from ABC.Toolbars.toolbars import ABCBottomBar2, ABCStatusBar, ABCMenuBar, ABCToolBar -from ABC.GUI.menu import ABCMenu -from ABC.Scheduler.scheduler import ABCScheduler - -from webservice import WebListener - -if (sys.platform == 'win32'): - from Dialogs.regdialog import RegCheckDialog - -from ABC.GUI.list import ManagedList -from Utility.utility import Utility -from Utility.constants import * #IGNORE:W0611 - -from Tribler.__init__ import tribler_init, tribler_done -from BitTornado.__init__ import product_name -from safeguiupdate import DelayedInvocation,FlaglessDelayedInvocation -import webbrowser -from Tribler.Dialogs.MugshotManager import MugshotManager -from Tribler.vwxGUI.GuiUtility import GUIUtility -import Tribler.vwxGUI.updateXRC as updateXRC -from Tribler.Video.VideoPlayer import VideoPlayer,return_feasible_playback_modes,PLAYBACKMODE_INTERNAL -from Tribler.Video.VideoServer import VideoHTTPServer -from Tribler.Dialogs.GUIServer import GUIServer -from Tribler.vwxGUI.TasteHeart import set_tasteheart_bitmaps -from Tribler.vwxGUI.perfBar import set_perfBar_bitmaps -from Tribler.Dialogs.BandwidthSelector import BandwidthSelector -from Tribler.Subscriptions.rss_client import TorrentFeedThread -from Tribler.Dialogs.activities import * -from Tribler.DecentralizedTracking import mainlineDHT -from Tribler.DecentralizedTracking.rsconvert import RawServerConverter -from Tribler.DecentralizedTracking.mainlineDHTChecker import mainlineDHTChecker - -from Tribler.notification import init as notification_init -from Tribler.vwxGUI.font import * -from Tribler.Web2.util.update import Web2Updater - -from Tribler.CacheDB.CacheDBHandler import BarterCastDBHandler -from Tribler.Overlay.permid import permid_for_user -from BitTornado.download_bt1 import EVIL - -DEBUG = False -ALLOW_MULTIPLE = False -start_time = 0 -start_time2 = 0 - - -################################################################ -# -# Class: FileDropTarget -# -# To enable drag and drop for ABC list in main menu -# -################################################################ -class FileDropTarget(wx.FileDropTarget): - def __init__(self, utility): - # Initialize the wsFileDropTarget Object - wx.FileDropTarget.__init__(self) - # Store the Object Reference for dropped files - self.utility = utility - - def OnDropFiles(self, x, y, filenames): - for filename in filenames: - self.utility.queue.addtorrents.AddTorrentFromFile(filename) - return True - - -############################################################## -# -# Class : ABCList -# -# ABC List class that contains the torrent list -# -############################################################## -class ABCList(ManagedList): - def __init__(self, parent): - style = wx.LC_REPORT|wx.LC_VRULES|wx.CLIP_CHILDREN - - prefix = 'column' - minid = 4 - maxid = 26 - exclude = [] - rightalign = [COL_PROGRESS, - COL_SIZE, - COL_DLSPEED, - COL_ULSPEED, - COL_RATIO, - COL_PEERPROGRESS, - COL_DLSIZE, - COL_ULSIZE, - COL_TOTALSPEED] - - ManagedList.__init__(self, parent, style, prefix, minid, maxid, exclude, rightalign) - - dragdroplist = FileDropTarget(self.utility) - self.SetDropTarget(dragdroplist) - - self.lastcolumnsorted = -1 - self.reversesort = 0 - - self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) - self.Bind(wx.EVT_LIST_COL_CLICK, self.OnColLeftClick) - - self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnItemSelected) - - # Bring up advanced details on left double click - self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick) - - # Bring up local settings on middle double click - self.Bind(wx.EVT_MIDDLE_DCLICK, self.utility.actions[ACTION_LOCALUPLOAD].action) - - # Do thing when keys are pressed down - def OnKeyDown(self, event): - keycode = event.GetKeyCode() - if event.CmdDown(): - if keycode == ord('a') or keycode == ord('A'): - # Select all files (CTRL-A) - self.selectAll() - elif keycode == ord('x') or keycode == ord('X'): - # Invert file selection (CTRL-X) - self.invertSelection() - elif keycode == wx.WXK_RETURN or keycode == wx.WXK_NUMPAD_ENTER: - # Open advanced details (Enter) - self.utility.actions[ACTION_DETAILS].action() - elif keycode == wx.WXK_SPACE: - # Open local settings (Space) - self.utility.actions[ACTION_LOCALUPLOAD].action() - elif keycode == 399: - # Open right-click menu (windows menu key) - self.OnItemSelected() - - event.Skip() - - def OnColLeftClick(self, event): - rank = event.GetColumn() - colid = self.columns.getIDfromRank(rank) - if colid == self.lastcolumnsorted: - self.reversesort = 1 - self.reversesort - else: - self.reversesort = 0 - self.lastcolumnsorted = colid - self.utility.queue.sortList(colid, self.reversesort) - - def selectAll(self): - self.updateSelected(select = range(0, self.GetItemCount())) - - def updateSelected(self, unselect = None, select = None): - if unselect is not None: - for index in unselect: - self.SetItemState(index, 0, wx.LIST_STATE_SELECTED) - if select is not None: - for index in select: - self.Select(index) - self.SetFocus() - - def getTorrentSelected(self, firstitemonly = False, reverse = False): - queue = self.utility.queue - - torrentselected = [] - for index in self.getSelected(firstitemonly, reverse): - ABCTorrentTemp = queue.getABCTorrent(index = index) - if ABCTorrentTemp is not None: - torrentselected.append(ABCTorrentTemp) - return torrentselected - - def OnItemSelected(self, event = None): - selected = self.getTorrentSelected() - if not selected: - return - - popupmenu = ABCMenu(self.utility, 'menu_listrightclick') - - # Popup the menu. If an item is selected then its handler - # will be called before PopupMenu returns. - if event is None: - # use the position of the first selected item (key event) - ABCTorrentTemp = selected[0] - position = self.GetItemPosition(ABCTorrentTemp.listindex) - else: - # use the cursor position (mouse event) - position = event.GetPosition() - - self.PopupMenu(popupmenu, position) - - def OnLeftDClick(self, event): - event.Skip() - try: - self.utility.actions[ACTION_DETAILS].action() - except: - print_exc() - - -############################################################## -# -# Class : ABCPanel -# -# Main ABC Panel class -# -############################################################## -class ABCPanel(wx.Panel): - def __init__(self, parent): - style = wx.CLIP_CHILDREN - wx.Panel.__init__(self, parent, -1, style = style) - - #Debug Output. - sys.stdout.write('Preparing GUI.\n'); - - self.utility = parent.utility - self.utility.window = self - self.queue = self.utility.queue - - # List of deleting torrents events that occur when the RateManager is active - # Such events are processed after the RateManager finishes - # postponedevents is a list of tupples : each tupple contains the method of ABCPanel to be called to - # deal with the event and the event. - self.postponedevents = [] - - #Manual Bittorrent Adding UI - ############################## - colSizer = wx.BoxSizer(wx.VERTICAL) - - self.list = ABCList(self) - self.utility.list = self.list - colSizer.Add(self.list, 1, wx.ALL|wx.EXPAND, 3) - - """ - # Add status bar - statbarbox = wx.BoxSizer(wx.HORIZONTAL) - self.sb_buttons = ABCStatusButtons(self,self.utility) - statbarbox.Add(self.sb_buttons, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 0) - self.abc_sb = ABCStatusBar(self,self.utility) - statbarbox.Add(self.abc_sb, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 0) - colSizer.Add(statbarbox, 0, wx.ALL|wx.EXPAND, 0) - """ - - #colSizer.Add(self.contentPanel, 1, wx.ALL|wx.EXPAND, 3) - self.SetSizer(colSizer) - self.SetAutoLayout(True) - - self.list.SetFocus() - - - def getSelectedList(self, event = None): - return self.list - - ###################################### - # Update ABC on-the-fly - ###################################### - def updateColumns(self, force = False): - # Update display in column for inactive torrent - for ABCTorrentTemp in self.utility.torrents["all"]: - ABCTorrentTemp.updateColumns(force = force) - - -############################################################## -# -# Class : ABCTaskBarIcon -# -# Task Bar Icon -# -############################################################## -class ABCTaskBarIcon(wx.TaskBarIcon): - def __init__(self, parent): - wx.TaskBarIcon.__init__(self) - - self.utility = parent.utility - - self.TBMENU_RESTORE = wx.NewId() - - # setup a taskbar icon, and catch some events from it - self.Bind(wx.EVT_TASKBAR_LEFT_DCLICK, parent.onTaskBarActivate) - self.Bind(wx.EVT_MENU, parent.onTaskBarActivate, id = self.TBMENU_RESTORE) - - self.updateIcon(False) - - def updateIcon(self,iconifying = False): - remove = True - - mintray = self.utility.config.Read('mintray', "int") - if (mintray >= 2) or ((mintray >= 1) and iconifying): - remove = False - - if remove and self.IsIconInstalled(): - self.RemoveIcon() - elif not remove and not self.IsIconInstalled(): - self.SetIcon(self.utility.icon, product_name) - - def CreatePopupMenu(self): - menu = wx.Menu() - - self.utility.actions[ACTION_STOPALL].addToMenu(menu, bindto = self) - self.utility.actions[ACTION_UNSTOPALL].addToMenu(menu, bindto = self) - menu.AppendSeparator() - menu.Append(self.TBMENU_RESTORE, self.utility.lang.get('showabcwindow')) - self.utility.actions[ACTION_EXIT].addToMenu(menu, bindto = self) - return menu - - -############################################################## -# -# Class : ABColdFrame -# -# Main ABC Frame class that contains menu and menu bar management -# and contains ABCPanel -# -############################################################## -class ABCOldFrame(wx.Frame,FlaglessDelayedInvocation): - def __init__(self, ID, params, utility): - self.utility = utility - #self.utility.frame = self - - title = "Old Interface" - # Get window size and position from config file - size = (400,400) - style = wx.DEFAULT_FRAME_STYLE | wx.CLIP_CHILDREN - - wx.Frame.__init__(self, None, ID, title, size = size, style = style) - - FlaglessDelayedInvocation.__init__(self) - - self.GUIupdate = True - - self.window = ABCPanel(self) - self.Bind(wx.EVT_SET_FOCUS, self.onFocus) - self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) - - self.tb = ABCToolBar(self) # new Tribler gui has no toolbar - self.SetToolBar(self.tb) - - - def onFocus(self, event = None): - if event is not None: - event.Skip() - self.window.getSelectedList(event).SetFocus() - - def OnCloseWindow(self, event = None): - self.Hide() - -# Custom class loaded by XRC -class ABCFrame(wx.Frame, DelayedInvocation): - def __init__(self, *args): - if len(args) == 0: - pre = wx.PreFrame() - # the Create step is done by XRC. - self.PostCreate(pre) - self.Bind(wx.EVT_WINDOW_CREATE, self.OnCreate) - else: - wx.Frame.__init__(self, args[0], args[1], args[2], args[3]) - self._PostInit() - - def OnCreate(self, event): - self.Unbind(wx.EVT_WINDOW_CREATE) - wx.CallAfter(self._PostInit) - event.Skip() - return True - - def _PostInit(self): - # Do all init here - self.guiUtility = GUIUtility.getInstance() - self.utility = self.guiUtility.utility - self.params = self.guiUtility.params - self.utility.frame = self - - title = self.utility.lang.get('title') + \ - " " + \ - self.utility.lang.get('version') - - # Get window size and position from config file - size, position = self.getWindowSettings() - style = wx.DEFAULT_FRAME_STYLE | wx.CLIP_CHILDREN - - self.SetSize(size) - self.SetPosition(position) - self.SetTitle(title) - tt = self.GetToolTip() - if tt is not None: - tt.SetTip('') - - #wx.Frame.__init__(self, None, ID, title, position, size, style = style) - - self.doneflag = Event() - DelayedInvocation.__init__(self) - - dragdroplist = FileDropTarget(self.utility) - self.SetDropTarget(dragdroplist) - - self.tbicon = None - - # Arno: see ABCPanel - #self.abc_sb = ABCStatusBar(self,self.utility) - #self.SetStatusBar(self.abc_sb) - - """ - # Add status bar - statbarbox = wx.BoxSizer(wx.HORIZONTAL) - self.sb_buttons = ABCStatusButtons(self,self.utility) - statbarbox.Add(self.sb_buttons, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 0) - self.abc_sb = ABCStatusBar(self,self.utility) - statbarbox.Add(self.abc_sb, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 0) - #colSizer.Add(statbarbox, 0, wx.ALL|wx.EXPAND, 0) - self.SetStatusBar(statbarbox) - """ - - - try: - self.SetIcon(self.utility.icon) - except: - pass - - # Don't update GUI as often when iconized - self.GUIupdate = True - - # Start the scheduler before creating the ListCtrl - self.utility.queue = ABCScheduler(self.utility) - #self.window = ABCPanel(self) - #self.abc_sb = self.window.abc_sb - - - self.oldframe = ABCOldFrame(-1, self.params, self.utility) - self.oldframe.Refresh() - self.oldframe.Layout() - #self.oldframe.Show(True) - - self.window = self.GetChildren()[0] - self.window.utility = self.utility - - """ - self.list = ABCList(self.window) - self.list.Show(False) - self.utility.list = self.list - print self.window.GetName() - self.window.list = self.list - self.utility.window = self.window - """ - #self.window.sb_buttons = ABCStatusButtons(self,self.utility) - - self.utility.window.postponedevents = [] - - # Menu Options - ############################ - menuBar = ABCMenuBar(self) - if sys.platform == "darwin": - wx.App.SetMacExitMenuItemId(wx.ID_CLOSE) - self.SetMenuBar(menuBar) - - #self.tb = ABCToolBar(self) # new Tribler gui has no toolbar - #self.SetToolBar(self.tb) - - self.buddyFrame = None - self.fileFrame = None - self.buddyFrame_page = 0 - self.buddyFrame_size = (800, 500) - self.buddyFrame_pos = None - self.fileFrame_size = (800, 500) - self.fileFrame_pos = None - - # Menu Events - ############################ - - self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) -# self.Bind(wx.EVT_MENU, self.OnMenuExit, id = wx.ID_CLOSE) - - # leaving here for the time being: - # wxMSW apparently sends the event to the App object rather than - # the top-level Frame, but there seemed to be some possibility of - # change - self.Bind(wx.EVT_QUERY_END_SESSION, self.OnCloseWindow) - self.Bind(wx.EVT_END_SESSION, self.OnCloseWindow) - - try: - self.tbicon = ABCTaskBarIcon(self) - except: - pass - self.Bind(wx.EVT_ICONIZE, self.onIconify) - self.Bind(wx.EVT_SET_FOCUS, self.onFocus) - self.Bind(wx.EVT_SIZE, self.onSize) - self.Bind(wx.EVT_MAXIMIZE, self.onSize) - #self.Bind(wx.EVT_IDLE, self.onIdle) - - # Start up the controller - self.utility.controller = ABCLaunchMany(self.utility) - self.utility.controller.start() - - # Start up mainline DHT - # Arno: do this in a try block, as khashmir gives a very funky - # error when started from a .dmg (not from cmd line) on Mac. In particular - # it complains that it cannot find the 'hex' encoding method when - # hstr.encode('hex') is called, and hstr is a string?! - # - try: - rsconvert = RawServerConverter(self.utility.controller.get_rawserver()) - mainlineDHT.init('', self.utility.listen_port, self.utility.getConfigPath(),rawserver=rsconvert) - # Create torrent-liveliness checker based on DHT - c = mainlineDHTChecker.getInstance() - c.register(mainlineDHT.dht) - except: - print_exc() - - # Give GUI time to set up stuff - wx.Yield() - - #if server start with params run it - ##################################### - - if DEBUG: - print >>sys.stderr,"abc: wxFrame: params is",self.params - - if self.params[0] != "": - success, msg, ABCTorrentTemp = self.utility.queue.addtorrents.AddTorrentFromFile(self.params[0],caller=CALLER_ARGV) - - self.utility.queue.postInitTasks(self.params) - - if self.params[0] != "": - # Update torrent.list, but after having read the old list of torrents, otherwise we get interference - ABCTorrentTemp.torrentconfig.writeSrc(False) - self.utility.torrentconfig.Flush() - - self.videoFrame = None - feasible = return_feasible_playback_modes(self.utility.getPath()) - if PLAYBACKMODE_INTERNAL in feasible: - # This means vlc is available - from Tribler.Video.EmbeddedPlayer import VideoFrame - self.videoFrame = VideoFrame(self) - - #self.videores = xrc.XmlResource("Tribler/vwxGUI/MyPlayer.xrc") - #self.videoframe = self.videores.LoadFrame(None, "MyPlayer") - #self.videoframe.Show() - - videoplayer = VideoPlayer.getInstance() - videoplayer.set_parentwindow(self.videoFrame) - else: - videoplayer = VideoPlayer.getInstance() - videoplayer.set_parentwindow(self) - - sys.stdout.write('GUI Complete.\n') - - self.Show(True) - - - # Just for debugging: add test permids and display top 5 peers from which the most is downloaded in bartercastdb - bartercastdb = BarterCastDBHandler() - mypermid = bartercastdb.my_permid - - if DEBUG: - bartercastdb.incrementItem((mypermid, "testpermid_1"), 'uploaded', 1024) - bartercastdb.incrementItem((mypermid, "testpermid_1"), 'downloaded', 20000) - - bartercastdb.incrementItem((mypermid, "testpermid_2"), 'uploaded', 40000) - bartercastdb.incrementItem((mypermid, "testpermid_2"), 'downloaded', 60000) - - top = bartercastdb.getTopNPeers(5)['top'] - - print 'My Permid: ', permid_for_user(mypermid) - - print 'Top 5 BarterCast peers:' - print '=======================' - - i = 1 - for (permid, up, down) in top: - print '%2d: %15s - %10d up %10d down' % (i, bartercastdb.getName(permid), up, down) - i += 1 - - - # Check to see if ABC is associated with torrents - ####################################################### - if (sys.platform == 'win32'): - if self.utility.config.Read('associate', "boolean"): - if self.utility.regchecker and not self.utility.regchecker.testRegistry(): - dialog = RegCheckDialog(self) - dialog.ShowModal() - dialog.Destroy() - - self.checkVersion() - - - def checkVersion(self): - t = Timer(2.0, self._checkVersion) - t.start() - - def _checkVersion(self): - my_version = self.utility.getVersion() - try: - curr_status = urllib.urlopen('http://tribler.org/version').readlines() - line1 = curr_status[0] - if len(curr_status) > 1: - self.update_url = curr_status[1].strip() - else: - self.update_url = 'http://tribler.org' - _curr_status = line1.split() - self.curr_version = _curr_status[0] - if self.newversion(self.curr_version, my_version): - # Arno: we are a separate thread, delegate GUI updates to MainThread - self.upgradeCallback() - - # Also check new version of web2definitions for youtube etc. search - Web2Updater(self.utility).checkUpdate() - except Exception,e: - print >> sys.stderr, "Tribler: Version check failed", ctime(time()), str(e) - #print_exc() - - def newversion(self, curr_version, my_version): - curr = curr_version.split('.') - my = my_version.split('.') - if len(my) >= len(curr): - nversion = len(my) - else: - nversion = len(curr) - for i in range(nversion): - if i < len(my): - my_v = int(my[i]) - else: - my_v = 0 - if i < len(curr): - curr_v = int(curr[i]) - else: - curr_v = 0 - if curr_v > my_v: - return True - elif curr_v < my_v: - return False - return False - - def upgradeCallback(self): - self.invokeLater(self.OnUpgrade) - # TODO: warn multiple times? - - def OnUpgrade(self, event=None): - self.setActivity(ACT_NEW_VERSION) - - def onFocus(self, event = None): - if event is not None: - event.Skip() - #self.window.getSelectedList(event).SetFocus() - - def setGUIupdate(self, update): - oldval = self.GUIupdate - self.GUIupdate = update - - if self.GUIupdate and not oldval: - # Force an update of all torrents - for torrent in self.utility.torrents["all"]: - torrent.updateColumns() - torrent.updateColor() - - - def taskbarCallback(self): - self.invokeLater(self.onTaskBarActivate,[]) - - - ####################################### - # minimize to tray bar control - ####################################### - def onTaskBarActivate(self, event = None): - self.Iconize(False) - self.Show(True) - self.Raise() - - if self.tbicon is not None: - self.tbicon.updateIcon(False) - - #self.window.list.SetFocus() - - # Resume updating GUI - self.setGUIupdate(True) - - def onIconify(self, event = None): - # This event handler is called both when being minimalized - # and when being restored. - if DEBUG: - if event is not None: - print >> sys.stderr,"abc: onIconify(",event.Iconized() - else: - print >> sys.stderr,"abc: onIconify event None" - if event.Iconized(): - if (self.utility.config.Read('mintray', "int") > 0 - and self.tbicon is not None): - self.tbicon.updateIcon(True) - self.Show(False) - - # Don't update GUI while minimized - self.setGUIupdate(False) - else: - self.setGUIupdate(True) - if event is not None: - event.Skip() - - def onSize(self, event = None): - # Arno: On Windows when I enable the tray icon and then change - # virtual desktop (see MS DeskmanPowerToySetup.exe) - # I get a onIconify(event.Iconized()==True) event, but when - # I switch back, I don't get an event. As a result the GUIupdate - # remains turned off. The wxWidgets wiki on the TaskBarIcon suggests - # catching the onSize event. - - if DEBUG: - if event is not None: - print >> sys.stderr,"abc: onSize:",self.GetSize() - else: - print >> sys.stderr,"abc: onSize: None" - self.setGUIupdate(True) - if event is not None: - if event.GetEventType() == wx.EVT_MAXIMIZE: - self.window.SetClientSize(self.GetClientSize()) - event.Skip() - - - # Refresh subscreens - self.refreshNeeded = True - self.guiUtility.refreshOnResize() - - def onIdle(self, event = None): - """ - Only refresh screens (especially detailsPanel) when resizes are finished - This gives less flickering, but doesnt look pretty, so i commented it out - """ - if self.refreshNeeded: - self.guiUtility.refreshOnResize() - self.refreshNeeded = False - - def getWindowSettings(self): - width = self.utility.config.Read("window_width") - height = self.utility.config.Read("window_height") - try: - size = wx.Size(int(width), int(height)) - except: - size = wx.Size(710, 400) - - x = self.utility.config.Read("window_x") - y = self.utility.config.Read("window_y") - if (x == "" or y == ""): - position = wx.DefaultPosition - else: - position = wx.Point(int(x), int(y)) - - return size, position - - def saveWindowSettings(self): - width, height = self.GetSizeTuple() - x, y = self.GetPositionTuple() - self.utility.config.Write("window_width", width) - self.utility.config.Write("window_height", height) - self.utility.config.Write("window_x", x) - self.utility.config.Write("window_y", y) - - self.utility.config.Flush() - - ################################## - # Close Program - ################################## - - def OnCloseWindow(self, event = None): - if event != None: - nr = event.GetEventType() - lookup = { wx.EVT_CLOSE.evtType[0]: "EVT_CLOSE", wx.EVT_QUERY_END_SESSION.evtType[0]: "EVT_QUERY_END_SESSION", wx.EVT_END_SESSION.evtType[0]: "EVT_END_SESSION" } - if nr in lookup: nr = lookup[nr] - print "Closing due to event ",nr - print >>sys.stderr,"Closing due to event ",nr - else: - print "Closing untriggered by event" - - # Don't do anything if the event gets called twice for some reason - if self.utility.abcquitting: - return - - # Check to see if we can veto the shutdown - # (might not be able to in case of shutting down windows) - if event is not None: - try: - if event.CanVeto() and self.utility.config.Read('confirmonclose', "boolean") and not event.GetEventType() == wx.EVT_QUERY_END_SESSION.evtType[0]: - dialog = wx.MessageDialog(None, self.utility.lang.get('confirmmsg'), self.utility.lang.get('confirm'), wx.OK|wx.CANCEL) - result = dialog.ShowModal() - dialog.Destroy() - if result != wx.ID_OK: - event.Veto() - return - except: - data = StringIO() - print_exc(file = data) - sys.stderr.write(data.getvalue()) - pass - - self.utility.abcquitting = True - self.GUIupdate = False - - self.guiUtility.guiOpen.clear() - - # Close the Torrent Maker - self.utility.actions[ACTION_MAKETORRENT].closeWin() - - try: - self.utility.webserver.stop() - except: - data = StringIO() - print_exc(file = data) - sys.stderr.write(data.getvalue()) - pass - - try: - # tell scheduler to close all active thread - self.utility.queue.clearScheduler() - except: - data = StringIO() - print_exc(file = data) - sys.stderr.write(data.getvalue()) - pass - - try: - # Restore the window before saving size and position - # (Otherwise we'll get the size of the taskbar button and a negative position) - self.onTaskBarActivate() - self.saveWindowSettings() - except: - #print_exc(file=sys.stderr) - print_exc() - - try: - if self.buddyFrame is not None: - self.buddyFrame.Destroy() - if self.fileFrame is not None: - self.fileFrame.Destroy() - if self.videoFrame is not None: - self.videoFrame.Destroy() - except: - pass - - self.oldframe.Destroy() - - try: - if self.tbicon is not None: - self.tbicon.RemoveIcon() - self.tbicon.Destroy() - self.Destroy() - except: - data = StringIO() - print_exc(file = data) - sys.stderr.write(data.getvalue()) - pass - - # Arno: at the moment, Tribler gets a segmentation fault when the - # tray icon is always enabled. This SEGV occurs in the wx mainloop - # which is entered as soon as we leave this method. Hence I placed - # tribler_done() here, so the database are closed properly - # before the crash. - # - # Arno, 2007-02-28: Preferably this should be moved to the main - # run() method below, that waits a while to allow threads to finish. - # Ideally, the database should still be open while they finish up. - # Because of the crash problem with the icontray this is the safer - # place. - # - # Arno, 2007-08-10: When a torrentfile is passed on the command line, - # the client will crash just after this point due to unknown reasons - # (it even does it when we don't look at the cmd line args at all!) - # Hence, for safety, I close the DB here already. - #if sys.platform == 'linux2': - # - - #tribler_done(self.utility.getConfigPath()) - - if DEBUG: - print >>sys.stderr,"abc: OnCloseWindow END" - - if DEBUG: - ts = enumerate() - for t in ts: - print >>sys.stderr,"abc: Thread still running",t.getName(),"daemon",t.isDaemon() - - - - def onWarning(self,exc): - msg = self.utility.lang.get('tribler_startup_nonfatalerror') - msg += str(exc.__class__)+':'+str(exc) - dlg = wx.MessageDialog(None, msg, self.utility.lang.get('tribler_warning'), wx.OK|wx.ICON_WARNING) - result = dlg.ShowModal() - dlg.Destroy() - - def onUPnPError(self,upnp_type,listenport,error_type,exc=None,listenproto='TCP'): - - if error_type == 0: - errormsg = unicode(' UPnP mode '+str(upnp_type)+' ')+self.utility.lang.get('tribler_upnp_error1') - elif error_type == 1: - errormsg = unicode(' UPnP mode '+str(upnp_type)+' ')+self.utility.lang.get('tribler_upnp_error2')+unicode(str(exc))+self.utility.lang.get('tribler_upnp_error2_postfix') - elif error_type == 2: - errormsg = unicode(' UPnP mode '+str(upnp_type)+' ')+self.utility.lang.get('tribler_upnp_error3') - else: - errormsg = unicode(' UPnP mode '+str(upnp_type)+' Unknown error') - - msg = self.utility.lang.get('tribler_upnp_error_intro') - msg += listenproto+' ' - msg += str(listenport) - msg += self.utility.lang.get('tribler_upnp_error_intro_postfix') - msg += errormsg - msg += self.utility.lang.get('tribler_upnp_error_extro') - - dlg = wx.MessageDialog(None, msg, self.utility.lang.get('tribler_warning'), wx.OK|wx.ICON_WARNING) - result = dlg.ShowModal() - dlg.Destroy() - - def onReachable(self,event=None): - """ Called by GUI thread """ - if self.firewallStatus is not None: - self.firewallStatus.setToggled(True) - tt = self.firewallStatus.GetToolTip() - if tt is not None: - tt.SetTip(self.utility.lang.get('reachable_tooltip')) - - - def setActivity(self,type,msg=u''): - - if currentThread().getName() != "MainThread": - print >> sys.stderr,"abc: setActivity thread",currentThread().getName(),"is NOT MAIN THREAD" - print_stack() - - if type == ACT_NONE: - prefix = u'' - msg = u'' - elif type == ACT_UPNP: - prefix = self.utility.lang.get('act_upnp') - elif type == ACT_REACHABLE: - prefix = self.utility.lang.get('act_reachable') - elif type == ACT_GET_EXT_IP_FROM_PEERS: - prefix = self.utility.lang.get('act_get_ext_ip_from_peers') - elif type == ACT_MEET: - prefix = self.utility.lang.get('act_meet') - elif type == ACT_GOT_METADATA: - prefix = self.utility.lang.get('act_got_metadata') - elif type == ACT_RECOMMEND: - prefix = self.utility.lang.get('act_recommend') - elif type == ACT_DISK_FULL: - prefix = self.utility.lang.get('act_disk_full') - elif type == ACT_NEW_VERSION: - prefix = self.utility.lang.get('act_new_version') - if msg == u'': - text = prefix - else: - text = unicode( prefix+u' '+msg) - - if DEBUG: - print >> sys.stderr,"abc: Setting activity",`text`,"EOT" - self.messageField.SetLabel(text) - - -class TorThread(Thread): - - def __init__(self): - Thread.__init__(self) - self.setDaemon(True) - self.setName("TorThread"+self.getName()) - self.child_out = None - self.child_in = None - - def run(self): - try: - if DEBUG: - print >>sys.stderr,"TorThread starting",currentThread().getName() - if sys.platform == "win32": - # Not "Nul:" but "nul" is /dev/null on Win32 - cmd = 'tor.exe' - sink = 'nul' - elif sys.platform == "darwin": - cmd = 'tor.mac' - sink = '/dev/null' - else: - cmd = 'tor' - sink = '/dev/null' - - (self.child_out,self.child_in) = os.popen2( "%s --log err-err > %s 2>&1" % (cmd,sink), 'b' ) - while True: - if DEBUG: - print >>sys.stderr,"TorThread reading",currentThread().getName() - - msg = self.child_in.read() - if DEBUG: - print >>sys.stderr,"TorThread: tor said",msg - if len(msg) == 0: - break - sleep(1) - except: - print_exc() - - def shutdown(self): - if self.child_out is not None: - self.child_out.close() - if self.child_in is not None: - self.child_in.close() - - -############################################################## -# -# Class : ABCApp -# -# Main ABC application class that contains ABCFrame Object -# -############################################################## -class ABCApp(wx.App,FlaglessDelayedInvocation): - def __init__(self, x, params, single_instance_checker, abcpath): - global start_time, start_time2 - start_time2 = time() - #print "[StartUpDebug]----------- from ABCApp.__init__ ----------Tribler starts up at", ctime(start_time2), "after", start_time2 - start_time - self.params = params - self.single_instance_checker = single_instance_checker - self.abcpath = abcpath - self.error = None - self.torthread = None - wx.App.__init__(self, x) - - def OnInit(self): - try: - self.utility = Utility(self.abcpath) - # Set locale to determine localisation - locale.setlocale(locale.LC_ALL, '') - - sys.stdout.write('Client Starting Up.\n') - sys.stdout.write('Build: ' + self.utility.lang.get('build') + '\n') - - bm = wx.Bitmap(os.path.join(self.utility.getPath(),'icons','splash.jpg'),wx.BITMAP_TYPE_JPEG) - #s = wx.MINIMIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER | wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN - #s = wx.SIMPLE_BORDER|wx.FRAME_NO_TASKBAR|wx.FRAME_FLOAT_ON_PARENT - self.splash = wx.SplashScreen(bm, wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_TIMEOUT, 1000, None, -1) - - wx.CallAfter(self.PostInit) - return True - - except Exception,e: - print_exc() - self.error = e - self.onError() - return False - - - def PostInit(self): - try: - tribler_init(self.utility.getConfigPath(),self.utility.getPath(),self.db_exception_handler) - self.utility.setTriblerVariables() - self.utility.postAppInit() - - # Singleton for executing tasks that are too long for GUI thread and - # network thread - self.guiserver = GUIServer.getInstance() - self.guiserver.register() - - # Singleton for management of user's mugshots (i.e. icons/display pictures) - self.mm = MugshotManager.getInstance() - self.mm.register(self.utility.getConfigPath(),self.utility.getPath()) - - # H4x0r a bit - set_tasteheart_bitmaps(self.utility.getPath()) - set_perfBar_bitmaps(self.utility.getPath()) - - # Put it here so an error is shown in the startup-error popup - self.serverlistener = ServerListener(self.utility) - - # Check webservice for autostart webservice - ####################################################### - WebListener(self.utility) - if self.utility.webconfig.Read("webautostart", "boolean"): - self.utility.webserver.start() - - # Start single instance server listenner - ############################################ - self.serverthread = Thread(target = self.serverlistener.start) - self.serverthread.setDaemon(True) - self.serverthread.setName("SingleInstanceServer"+self.serverthread.getName()) - self.serverthread.start() - - self.videoplayer = VideoPlayer.getInstance() - self.videoplayer.register(self.utility) - self.videoserver = VideoHTTPServer.getInstance() - self.videoserver.background_serve() - - notification_init( self.utility ) - - # Change config when experiment ends, before ABCLaunchMany is created - global EVIL - if EVIL and time() > 1190099288.0: - EVIL = False - end = self.utility.config.Read('lure_ended', "boolean") - if not end: - self.utility.config.Write('lure_ended', 1, "boolean") - self.utility.config.Write('tor_enabled', 0, "boolean") - self.utility.config.Write('ut_pex_max_addrs_from_peer', 16) - - msg = "The Tribler download accelerator using the TOR network has been turned off. For more information visit http://TV.seas.Harvard.edu/" - dlg = wx.MessageDialog(None, msg, "Tribler Warning", wx.OK|wx.ICON_INFORMATION) - result = dlg.ShowModal() - dlg.Destroy() - - enabled = self.utility.config.Read('tor_enabled', "boolean") - if enabled: - self.torthread = TorThread() - self.torthread.start() - - # - # Read and create GUI from .xrc files - # - #self.frame = ABCFrame(-1, self.params, self.utility) - self.guiUtility = GUIUtility.getInstance(self.utility, self.params) - updateXRC.main([os.path.join(self.utility.getPath(),'Tribler','vwxGUI')]) - self.res = xrc.XmlResource(os.path.join(self.utility.getPath(),'Tribler','vwxGUI','MyFrame.xrc')) - self.guiUtility.xrcResource = self.res - self.frame = self.res.LoadFrame(None, "MyFrame") - self.guiUtility.frame = self.frame - self.guiUtility.scrollWindow = xrc.XRCCTRL(self.frame, "level0") - self.guiUtility.mainSizer = self.guiUtility.scrollWindow.GetSizer() - self.frame.topBackgroundRight = xrc.XRCCTRL(self.frame, "topBG3") - self.guiUtility.scrollWindow.SetScrollbars(1,1,1024,768) - self.guiUtility.scrollWindow.SetScrollRate(15,15) - self.frame.mainButtonPersons = xrc.XRCCTRL(self.frame, "mainButtonPersons") - - - self.frame.numberPersons = xrc.XRCCTRL(self.frame, "numberPersons") - numperslabel = xrc.XRCCTRL(self.frame, "persons") - self.frame.numberFiles = xrc.XRCCTRL(self.frame, "numberFiles") - numfileslabel = xrc.XRCCTRL(self.frame, "files") - self.frame.messageField = xrc.XRCCTRL(self.frame, "messageField") - self.frame.firewallStatus = xrc.XRCCTRL(self.frame, "firewallStatus") - tt = self.frame.firewallStatus.GetToolTip() - if tt is not None: - tt.SetTip(self.utility.lang.get('unknownreac_tooltip')) - - if sys.platform == "linux2": - self.frame.numberPersons.SetFont(wx.Font(9,FONTFAMILY,FONTWEIGHT,wx.NORMAL,False,FONTFACE)) - self.frame.numberFiles.SetFont(wx.Font(9,FONTFAMILY,FONTWEIGHT,wx.NORMAL,False,FONTFACE)) - self.frame.messageField.SetFont(wx.Font(9,FONTFAMILY,FONTWEIGHT,wx.NORMAL,False,FONTFACE)) - numperslabel.SetFont(wx.Font(9,FONTFAMILY,FONTWEIGHT,wx.NORMAL,False,FONTFACE)) - numfileslabel.SetFont(wx.Font(9,FONTFAMILY,FONTWEIGHT,wx.NORMAL,False,FONTFACE)) - """ - searchfilebut = xrc.XRCCTRL(self.frame, "bt257cC") - searchfilebut.Bind(wx.EVT_LEFT_UP, self.guiUtility.buttonClicked) - searchpersbut = xrc.XRCCTRL(self.frame, "bt258cC") - searchpersbut.Bind(wx.EVT_LEFT_UP, self.guiUtility.buttonClicked) - - self.frame.searchtxtctrl = xrc.XRCCTRL(self.frame, "tx220cCCC") - """ - - #self.frame.Refresh() - #self.frame.Layout() - self.frame.Show(True) -#=============================================================================== -# global start_time2 -# current_time = time() -# print "\n\n[StartUpDebug]-----------------------------------------" -# print "[StartUpDebug]" -# print "[StartUpDebug]----------- from ABCApp.OnInit ----------Tribler frame is shown after", current_time-start_time2 -# print "[StartUpDebug]" -# print "[StartUpDebug]-----------------------------------------\n\n" -#=============================================================================== - - # GUI start - # - load myFrame - # - load standardGrid - # - gui utility > button mainButtonFiles = clicked - - - self.Bind(wx.EVT_QUERY_END_SESSION, self.frame.OnCloseWindow) - self.Bind(wx.EVT_END_SESSION, self.frame.OnCloseWindow) - - - #asked = self.utility.config.Read('askeduploadbw', 'boolean') - asked = True - if not asked: - dlg = BandwidthSelector(self.frame,self.utility) - result = dlg.ShowModal() - if result == wx.ID_OK: - ulbw = dlg.getUploadBandwidth() - self.utility.config.Write('maxuploadrate',ulbw) - self.utility.config.Write('maxseeduploadrate',ulbw) - self.utility.config.Write('askeduploadbw','1') - dlg.Destroy() - - # Arno, 2007-05-03: wxWidgets 2.8.3.0 and earlier have the MIME-type for .bmp - # files set to 'image/x-bmp' whereas 'image/bmp' is the official one. - try: - bmphand = None - hands = wx.Image.GetHandlers() - for hand in hands: - #print "Handler",hand.GetExtension(),hand.GetType(),hand.GetMimeType() - if hand.GetMimeType() == 'image/x-bmp': - bmphand = hand - break - #wx.Image.AddHandler() - if bmphand is not None: - bmphand.SetMimeType('image/bmp') - except: - # wx < 2.7 don't like wx.Image.GetHandlers() - print_exc() - - # Must be after ABCLaunchMany is created - self.torrentfeed = TorrentFeedThread.getInstance() - self.torrentfeed.register(self.utility) - self.torrentfeed.start() - - #print "DIM",wx.GetDisplaySize() - #print "MM",wx.GetDisplaySizeMM() - - wx.CallAfter(self.startWithRightView) - - except Exception,e: - print_exc() - self.error = e - self.onError() - return False - - return True - - def onError(self,source=None): - # Don't use language independence stuff, self.utility may not be - # valid. - msg = "Unfortunately, Tribler ran into an internal error:\n\n" - if source is not None: - msg += source - msg += str(self.error.__class__)+':'+str(self.error) - msg += '\n' - msg += 'Please see the FAQ on www.tribler.org on how to act.' - dlg = wx.MessageDialog(None, msg, "Tribler Fatal Error", wx.OK|wx.ICON_ERROR) - result = dlg.ShowModal() - print_exc() - dlg.Destroy() - - def MacOpenFile(self,filename): - self.utility.queue.addtorrents.AddTorrentFromFile(filename) - - def OnExit(self): - - self.torrentfeed.shutdown() - if self.torthread is not None: - self.torthread.shutdown() - mainlineDHT.deinit() - - if not ALLOW_MULTIPLE: - del self.single_instance_checker - ClientPassParam("Close Connection") - return 0 - - def db_exception_handler(self,e): - if DEBUG: - print >> sys.stderr,"abc: Database Exception handler called",e,"value",e.args,"#" - try: - if e.args[1] == "DB object has been closed": - return # We caused this non-fatal error, don't show. - if self.error is not None and self.error.args[1] == e.args[1]: - return # don't repeat same error - except: - print >> sys.stderr, "abc: db_exception_handler error", e, type(e) - print_exc() - #print_stack() - self.error = e - self.invokeLater(self.onError,[],{'source':"The database layer reported: "}) - - def getConfigPath(self): - return self.utility.getConfigPath() - - def startWithRightView(self): - if self.params[0] != "": - self.guiUtility.standardLibraryOverview() - - -class DummySingleInstanceChecker: - - def __init__(self,basename): - pass - - def IsAnotherRunning(self): - "Uses pgrep to find other tribler.py processes" - # If no pgrep available, it will always start tribler - progressInfo = commands.getoutput('pgrep -fl tribler.py | grep -v pgrep') - numProcesses = len(progressInfo.split('\n')) - if DEBUG: - print 'ProgressInfo: %s, num: %d' % (progressInfo, numProcesses) - return numProcesses > 1 - - -############################################################## -# -# Main Program Start Here -# -############################################################## -def run(params = None): - global start_time - start_time = time() - if params is None: - params = [""] - - if len(sys.argv) > 1: - params = sys.argv[1:] - - # Create single instance semaphore - # Arno: On Linux and wxPython-2.8.1.1 the SingleInstanceChecker appears - # to mess up stderr, i.e., I get IOErrors when writing to it via print_exc() - # - # TEMPORARILY DISABLED on Linux - if sys.platform != 'linux2': - single_instance_checker = wx.SingleInstanceChecker("tribler-" + wx.GetUserId()) - else: - single_instance_checker = DummySingleInstanceChecker("tribler-") - - #print "[StartUpDebug]---------------- 1", time()-start_time - if not ALLOW_MULTIPLE and single_instance_checker.IsAnotherRunning(): - #Send torrent info to abc single instance - ClientPassParam(params[0]) - #print "[StartUpDebug]---------------- 2", time()-start_time - else: - abcpath = os.path.abspath(os.path.dirname(sys.argv[0])) - # Arno: don't chdir to allow testing as other user from other dir. - #os.chdir(abcpath) - - # Launch first abc single instance - app = ABCApp(0, params, single_instance_checker, abcpath) - configpath = app.getConfigPath() -# print "[StartUpDebug]---------------- 3", time()-start_time - app.MainLoop() - - print "Client shutting down. Sleeping for a few seconds to allow other threads to finish" - sleep(4) - - # This is the right place to close the database, unfortunately Linux has - # a problem, see ABCFrame.OnCloseWindow - # - #if sys.platform != 'linux2': - # tribler_done(configpath) - os._exit(0) - -if __name__ == '__main__': - run() - diff --git a/tribler-mod/Tribler/Test/API/file.wmv b/tribler-mod/Tribler/Test/API/file.wmv deleted file mode 100644 index ff5f772b6aaec2fdd4986a21d7034a6bd5b9bf0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82948 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