# Written by Jie Yang, Arno Bakker # see LICENSE.txt for license information import wx from wx.lib import masked import os from base64 import encodestring from Swapper.CacheDB.CacheDBHandler import TorrentDBHandler, MyPreferenceDBHandler from Swapper.utilities import friendly_time, sort_dictlist from Swapper.unicode import str2unicode, dunno2unicode from common import CommonSwapperList from Utility.constants import * #IGNORE:W0611 DEBUG = False SHOW_TORRENT_NAME = True relevance_display_factor = 1000.0 def showInfoHash(infohash): if infohash.startswith('torrent'): # for testing return infohash try: n = int(infohash) return str(n) except: pass return encodestring(infohash).replace("\n","") # try: # return encodestring(infohash) # except: # return infohash class MyPreferenceList(CommonSwapperList): def __init__(self, parent): self.parent = parent self.utility = parent.utility self.mypref_db = parent.mypref_db self.min_rank = -1 self.max_rank = 5 self.reversesort = 0 self.lastcolumnsorted = -1 style = wx.LC_REPORT|wx.LC_HRULES|wx.LC_VRULES prefix = 'mypref' minid = 0 maxid = 5 rightalign = [] centeralign = [ MYPREF_TORRENTNAME, MYPREF_CONTENTNAME, MYPREF_RANK, MYPREF_SIZE, MYPREF_LASTSEEN, ] exclude = [] self.keys = ['torrent_name', 'content_name', 'rank', 'length', 'last_seen'] CommonSwapperList.__init__(self, parent, style, prefix, minid, maxid, exclude, rightalign, centeralign) # change display format for item data def getText(self, data, row, col): key = self.keys[col] original_data = data[row][key] if DEBUG: print "mypref frame: getText",key, `original_data` if key == 'length': length = original_data/1024/1024.0 return '%.2f MB'%(length) if key == 'last_seen': if original_data == 0: return '?' return friendly_time(original_data) ret = str2unicode(original_data) return ret def reloadData(self): myprefs = self.mypref_db.getPrefList() keys = ['infohash', 'torrent_name', 'info', 'content_name', 'rank', 'last_seen'] self.data = self.mypref_db.getPrefs(myprefs, keys) for i in xrange(len(self.data)): info = self.data[i]['info'] self.data[i]['length'] = info.get('length', 0) if self.data[i]['torrent_name'] == '': self.data[i]['torrent_name'] = '?' if self.data[i]['content_name'] == '': self.data[i]['content_name'] = '?' def OnActivated(self, event): self.curr_idx = event.m_itemIndex def getMenuItems(self, min_rank, max_rank): menu_items = {} for i in range(min_rank, max_rank+1): id = wx.NewId() func = 'OnRank' + str(i - min_rank) func = getattr(self, func) if i == -1: label = "Fake File" elif i == 0: label = "No Rate" else: label = "*" * i menu_items[i] = {'id':id, 'func':func, 'label':label} return menu_items def OnRightClick(self, event=None): curr_idx = self.getSelectedItems() if not hasattr(self, "adjustRankID"): self.adjustRankID = wx.NewId() self.menu_items = self.getMenuItems(self.min_rank, self.max_rank) for i in self.menu_items: self.Bind(wx.EVT_MENU, self.menu_items[i]['func'], id=self.menu_items[i]['id']) if not hasattr(self, "deletePrefID"): self.deletePrefID = wx.NewId() self.Bind(wx.EVT_MENU, self.OnDeletePref, id=self.deletePrefID) # menu for change torrent's rank sm = wx.Menu() curr_rank = self.data[curr_idx[0]]['rank'] for i in curr_idx[1:]: if self.data[i]['rank'] != curr_rank: curr_rank = None submenu = wx.Menu() idx = self.menu_items.keys() idx.sort() idx.reverse() for i in idx: # 5..-1 if i == curr_rank: label = '> '+self.menu_items[i]['label'] else: label = ' '+self.menu_items[i]['label'] submenu.Append(self.menu_items[i]['id'], label) sm.AppendMenu(self.adjustRankID, "Rank items", submenu) sm.Append(self.deletePrefID, 'Delete') self.PopupMenu(sm, event.GetPosition()) sm.Destroy() def changeRank(self, curr_idx, rank): torrent = self.data[curr_idx] torrent['rank'] = rank self.mypref_db.updateRank(torrent['infohash'], rank) self.loadList(False, False) #self.SetStringItem(curr_idx, 2, str(rank)) #print "Set torrent", showInfoHash(torrent['infohash']), "rank", rank def OnRank0(self, event=None): selected = self.getSelectedItems() for i in selected: self.changeRank(i, 0+self.min_rank) def OnRank1(self, event=None): selected = self.getSelectedItems() for i in selected: self.changeRank(i, 1+self.min_rank) def OnRank2(self, event=None): selected = self.getSelectedItems() for i in selected: self.changeRank(i, 2+self.min_rank) def OnRank3(self, event=None): selected = self.getSelectedItems() for i in selected: self.changeRank(i, 3+self.min_rank) def OnRank4(self, event=None): selected = self.getSelectedItems() for i in selected: self.changeRank(i, 4+self.min_rank) def OnRank5(self, event=None): selected = self.getSelectedItems() for i in selected: self.changeRank(i, 5+self.min_rank) def OnRank6(self, event=None): selected = self.getSelectedItems() for i in selected: self.changeRank(i, 6+self.min_rank) def OnDeletePref(self, event=None): selected = self.getSelectedItems() j = 0 for i in selected: infohash = self.data[i-j]['infohash'] self.mypref_db.deletePreference(infohash) self.DeleteItem(i-j) self.data.pop(i-j) j += 1 self.mypref_db.sync() class MyPreferencePanel(wx.Panel): def __init__(self, frame, parent): self.parent = parent self.utility = frame.utility self.mypref_db = frame.mypref_db self.torrent_db = frame.torrent_db wx.Panel.__init__(self, parent, -1) mainbox = wx.BoxSizer(wx.VERTICAL) self.list=MyPreferenceList(self) mainbox.Add(self.list, 1, wx.EXPAND|wx.ALL, 5) label = wx.StaticText(self, -1, "Right click on a torrent to assign a 1--5 star rating") mainbox.Add(label, 0, wx.ALIGN_CENTER_VERTICAL) self.SetSizer(mainbox) self.SetAutoLayout(True) self.Show(True) self.list.loadList() def updateColumns(self, force=False): self.list.loadList(False, False) class FileList(CommonSwapperList): def __init__(self, parent): self.parent = parent self.utility = parent.utility self.torrent_db = parent.torrent_db self.min_rank = -1 self.max_rank = 5 self.reversesort = 0 self.lastcolumnsorted = -1 self.loadRelevanceThreshold() style = wx.LC_REPORT|wx.LC_HRULES|wx.LC_VRULES prefix = 'torrent' minid = 0 maxid = 10 rightalign = [] centeralign = [ TORRENT_TORRENTNAME, TORRENT_CONTENTNAME, TORRENT_RECOMMENDATION, TORRENT_SOURCES, TORRENT_SIZE, TORRENT_NFILES, TORRENT_INJECTED, TORRENT_TRACKER, TORRENT_NLEECHERS, TORRENT_NSEEDERS, ] exclude = [] self.keys = ['torrent_name', 'content_name', 'relevance', 'num_owners', 'length', 'num_files', 'date', 'tracker', 'leecher', 'seeder' ] CommonSwapperList.__init__(self, parent, style, prefix, minid, maxid, exclude, rightalign, centeralign) # change display format for item data def getText(self, data, row, col): key = self.keys[col] original_data = data[row][key] if key == 'relevance': # should this change, also update return '%.2f'%(original_data/relevance_display_factor) if key == 'infohash': return showInfoHash(original_data) if key == 'length': length = original_data/1024/1024.0 return '%.2f MB'%(length) if key == 'date': if original_data == 0: return '?' return friendly_time(original_data) # if key == 'seeder' or key == 'leecher': # if original_data < 0: # return '?' return str2unicode(original_data) def reloadData(self): def showFile(data): if data['relevance'] < self.relevance_threshold or \ not data['torrent_name'] or not data['info']: return False src = os.path.join(data['torrent_dir'], data['torrent_name']) return os.path.isfile(src) key = ['infohash', 'torrent_name', 'torrent_dir', 'relevance', 'info', 'num_owners', 'leecher', 'seeder'] self.data = self.torrent_db.getRecommendedTorrents(key) self.data = filter(showFile, self.data) for i in xrange(len(self.data)): info = self.data[i]['info'] self.data[i]['length'] = info.get('length', 0) self.data[i]['content_name'] = dunno2unicode(info.get('name', '?')) if self.data[i]['torrent_name'] == '': self.data[i]['torrent_name'] = '?' # self.data[i]['seeder'] = -1 # self.data[i]['leecher'] = -1 self.data[i]['num_files'] = int(info.get('num_files', 0)) self.data[i]['date'] = info.get('creation date', 0) self.data[i]['tracker'] = info.get('announce', '') self.data[i]['leecher'] = self.data[i].get('leecher', 0) self.data[i]['seeder'] = self.data[i].get('seeder', 0) def OnDeleteTorrent(self, event=None): selected = self.getSelectedItems() j = 0 for i in selected: infohash = self.data[i-j]['infohash'] self.torrent_db.deleteTorrent(infohash, True) self.DeleteItem(i-j) self.data.pop(i-j) j += 1 self.torrent_db.sync() def OnRightClick(self, event=None): if not hasattr(self, "deleteTorrentID"): self.deleteTorrentID = wx.NewId() self.Bind(wx.EVT_MENU, self.OnDeleteTorrent, id=self.deleteTorrentID) if not hasattr(self, "downloadTorrentID"): self.downloadTorrentID = wx.NewId() self.Bind(wx.EVT_MENU, self.OnDownload, id=self.downloadTorrentID) # menu for change torrent's rank sm = wx.Menu() sm.Append(self.deleteTorrentID, self.utility.lang.get('delete')) sm.Append(self.downloadTorrentID, self.utility.lang.get('download')) self.PopupMenu(sm, event.GetPosition()) sm.Destroy() def OnActivated(self, event): self.curr_idx = event.m_itemIndex self.download(self.curr_idx) def OnDownload(self, event): first_idx = self.GetFirstSelected() if first_idx < 0: return self.download(first_idx) while 1: idx = self.GetNextSelected(first_idx) if idx < 0: break self.download(idx) def download(self, idx): src = os.path.join(self.data[idx]['torrent_dir'], self.data[idx]['torrent_name']) if os.path.isfile(src): if self.data[idx]['content_name']: name = self.data[idx]['content_name'] else: name = showInfoHash(self.data[idx]['infohash']) #start_download = self.utility.lang.get('start_downloading') #str = name + "?" str = self.utility.lang.get('download_start') + u' ' + name + u'?' dlg = wx.MessageDialog(self, str, self.utility.lang.get('click_and_download'), wx.YES_NO|wx.NO_DEFAULT|wx.ICON_INFORMATION) result = dlg.ShowModal() dlg.Destroy() if result == wx.ID_YES: src = os.path.join(self.data[idx]['torrent_dir'], self.data[idx]['torrent_name']) if os.path.isfile(src): self.parent.clickAndDownload(src) self.DeleteItem(idx) del self.data[idx] self.parent.frame.updateMyPref() def setRelevanceThreshold(self,value): self.relevance_threshold = value def getRelevanceThreshold(self): return self.relevance_threshold def loadRelevanceThreshold(self): self.relevance_threshold = self.parent.utility.config.Read("rec_relevance_threshold", "int" ) def saveRelevanceThreshold(self): self.parent.utility.config.Write( "rec_relevance_threshold", self.relevance_threshold) self.parent.utility.config.Flush() class FilePanel(wx.Panel): def __init__(self, frame, parent): self.parent = parent self.frame = frame self.utility = frame.utility self.mypref_db = frame.mypref_db self.torrent_db = frame.torrent_db wx.Panel.__init__(self, parent, -1) mainbox = wx.BoxSizer(wx.VERTICAL) # Arno: Somehow the list gets painted over the other controls below it in # the window if we specifiy a size of the list, so don't. self.list=FileList(self) mainbox.Add(self.list, 1, wx.EXPAND|wx.ALL, 5) botbox = self.createBotUtility() mainbox.Add(botbox, 0, wx.EXPAND|wx.ALL, 5) self.SetSizer(mainbox) self.SetAutoLayout(True) self.Show(True) self.Bind(masked.EVT_NUM, self.OnSetRelevanceThreshold, self.relev_ctl) def createBotUtility(self): botbox = wx.BoxSizer(wx.VERTICAL) label = wx.StaticText(self, -1, self.utility.lang.get('recommendinstructions')) botbox.Add(label, 0, wx.EXPAND|wx.ALL, 5) relev_box = wx.BoxSizer(wx.HORIZONTAL) relev_box.Add(wx.StaticText(self, -1, self.utility.lang.get('recommendfilter')), 0, wx.ALIGN_CENTER_VERTICAL) self.relev_ctl = self.utility.makeNumCtrl(self, self.list.getRelevanceThreshold()/relevance_display_factor, min = 0.0, max = 65536.0, fractionWidth = 2) relev_box.Add(self.relev_ctl, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) relev_box.Add(wx.StaticText(self, -1, self.utility.lang.get('recommendfilterall')), 0, wx.ALIGN_CENTER_VERTICAL) botbox.Add(relev_box, 1, wx.EXPAND|wx.ALL, 5) return botbox def updateFileList(self,relevance_threshold=0): self.list.setRelevanceThreshold(relevance_threshold) self.list.loadList() def clickAndDownload(self, src): self.utility.queue.addtorrents.AddTorrentFromFile(src, forceasklocation = False) def OnSetRelevanceThreshold(self,event=None): value = self.relev_ctl.GetValue() value = int(value * relevance_display_factor) self.updateFileList(value) def updateColumns(self, force=False): self.list.loadList(False, False) class ABCFileFrame(wx.Frame): def __init__(self, parent): self.utility = parent.utility wx.Frame.__init__(self, None, -1, self.utility.lang.get('tb_file_short'), size=self.utility.frame.fileFrame_size, pos=self.utility.frame.fileFrame_pos) self.main_panel = self.createMainPanel() self.count = 0 self.loadFileList = False self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) self.Bind(wx.EVT_IDLE, self.updateFileList) self.Show() def createMainPanel(self): main_panel = wx.Panel(self) self.createNoteBook(main_panel) bot_box = self.createBottomBoxer(main_panel) mainbox = wx.BoxSizer(wx.VERTICAL) mainbox.Add(self.notebook, 1, wx.EXPAND|wx.ALL, 5) mainbox.Add(bot_box, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5) main_panel.SetSizer(mainbox) return main_panel def loadDatabase(self): self.mypref_db = self.utility.mypref_db self.torrent_db = self.utility.torrent_db def createNoteBook(self, main_panel): self.loadDatabase() self.notebook = wx.Notebook(main_panel, -1) self.filePanel = FilePanel(self, self.notebook) self.myPreferencePanel = MyPreferencePanel(self, self.notebook) self.notebook.AddPage(self.filePanel, self.utility.lang.get('file_list_title')) self.notebook.AddPage(self.myPreferencePanel, self.utility.lang.get('mypref_list_title')) def createBottomBoxer(self, main_panel): bot_box = wx.BoxSizer(wx.HORIZONTAL) button = wx.Button(main_panel, -1, self.utility.lang.get('close'), style = wx.BU_EXACTFIT) self.Bind(wx.EVT_BUTTON, self.OnCloseWindow, button) bot_box.Add(button, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 3) return bot_box def updateMyPref(self): # used by File List self.myPreferencePanel.list.loadList() def updateFileList(self, event=None): # Arno: on Linux, the list does not get painted properly before this # idle handler is called, which is weird. Hence, I wait for the next # idle event and load the filelist there. self.count += 1 if not self.loadFileList and self.count >= 2: self.filePanel.list.loadList() self.Unbind(wx.EVT_IDLE) self.count = 0 def OnCloseWindow(self, event = None): self.filePanel.list.saveRelevanceThreshold() self.utility.frame.fileFrame_size = self.GetSize() self.utility.frame.fileFrame_pos = self.GetPosition() self.utility.frame.fileFrame = None self.utility.abcfileframe = None self.Destroy()