/LMG/Tools/search.py
Python | 674 lines | 623 code | 46 blank | 5 comment | 31 complexity | 444c94f8602105889e6f0a0403fce426 MD5 | raw file
- import sys
- import os
- import re
- import ConfigParser
- import wx
- import wx.lib.flatnotebook as NC
- from threading import Thread
- from xml.sax import parseString, handler, saxutils, SAXParseException
- from wx.lib.mixins.listctrl import ColumnSorterMixin, ListCtrlAutoWidthMixin
- from BitTornado.zurllib import urlopen
- from LMG.GUI.Base.menu import Menu
- from LMG.GUI.Base.ArtManager import ArtManager
- from LMG.Utility.helpers import existsAndIsReadable
-
- regex = re.compile("(?P<name>.*?)\[(?P<seeds>[0-9]+)/(?P<peers>[0-9]+)\]")
-
- class SearchPanel(wx.Panel):
- def __init__(self, parent):
- wx.Panel.__init__(self, parent, -1, style = wx.STATIC_BORDER)
-
- self.searchHandler = SearchHandler()
-
- sizer = wx.BoxSizer(wx.HORIZONTAL)
-
- # Splitter
- self.splitter = wx.SplitterWindow(self, -1, style = wx.SP_LIVE_UPDATE)
- self.splitter.SetMinimumPaneSize(25)
- self.splitter.SetSashGravity(0.0)
- self.enginesPanel = SearchEnginesPanel(self)
- self.resultPanel = SearchListPanel(self)
- self.splitter.SplitVertically(self.enginesPanel, self.resultPanel, 150)
-
- sizer.Add(self.splitter, 1, wx.EXPAND|wx.TOP, 5)
- self.SetAutoLayout(True)
- self.SetSizerAndFit(sizer)
-
-
- class SearchResultsList(wx.ListCtrl, ColumnSorterMixin, ListCtrlAutoWidthMixin):
- def __init__(self, parent):
- wx.ListCtrl.__init__(self, parent, -1, style=wx.LC_REPORT | wx.LC_VRULES)
- ListCtrlAutoWidthMixin.__init__(self)
- ColumnSorterMixin.__init__(self, 6)
-
- self.parent = parent
-
- self.itemDataMap = {}
- self._col = self.parent.def_col
-
- self.InsertColumn(0, _("Name"))
- self.InsertColumn(1, _("File Size"))
- self.InsertColumn(2, _("Seeds"))
- self.InsertColumn(3, _("Peers"))
- self.InsertColumn(4, _("Category"))
- self.InsertColumn(5, _("Engine"))
-
- self.setResizeColumn(1)
-
- self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated)
-
-
-
- def OnItemActivated(self, event):
- selected = event.m_itemIndex
- data = self.itemDataMap[self.GetItemData(selected)]
- url = data[5]
- onBrowser = data[6]
- self.SetItemTextColour(selected, wx.BLUE)
- if onBrowser:
- wx.LaunchDefaultBrowser(url)
- else:
- utility.queue.addtorrents.ScanUrl(url, single = True, allowBrowser = True)
-
- def clearList(self):
- self.DeleteAllItems()
- self.itemDataMap = {}
-
- def feedList(self, data, engine):
- for item in data:
-
- # data
- link = item.get("Link", "---")
-
- name = item.get("Name")
- if not name:
- if '/' in link:
- name = link.split('/')[-1]
- else:
- name = link
-
- category = item.get("Category", "---")
-
- seeders = item.get("Seeders", "---")
- leechers = item.get("Leechers", "---")
-
- size = item.get("Size")
- if size is not None:
- try:
- if "G" in size:
- size = float(size.split("G")[0].strip()) * 1024**3
- elif "M" in size:
- size = float(size.split("M")[0].strip()) * 1024**2
- elif "K" in size:
- size = float(size.split("K")[0].strip()) * 1024
- elif "B" in size and size.find(" ") > 0:
- size = float(size.split(" ")[0])
- else:
- size = float(size)
- except:
- size = -1
-
- if size is not None and size > -1:
- strsize = utility.size_format(size)
- else:
- strsize = "---"
-
- # insert item
- listIndex = self.InsertStringItem(sys.maxint, name)
- self.SetStringItem(listIndex, 1, strsize)
- self.SetStringItem(listIndex, 2, seeders)
- self.SetStringItem(listIndex, 3, leechers)
- self.SetStringItem(listIndex, 4, category)
- self.SetStringItem(listIndex, 5, engine.viewName)
-
- self.SetItemData(listIndex, listIndex)
-
- # column sorter
- try:
- seeders = int(seeders)
- except:
- seeders = -1
- try:
- leechers = int(leechers)
- except:
- leechers = -1
-
- if type(seeders) == type(leechers) == int:
- if seeders == 0:
- item = self.GetItem(listIndex)
- item.SetTextColour(wx.RED)
- self.SetItem(item)
- elif seeders > 5 and (leechers == 0 or seeders/float(leechers) > 0.5):
- item = self.GetItem(listIndex)
- item.SetTextColour(wx.Colour(0, 150, 0))
- self.SetItem(item)
-
- self.itemDataMap[listIndex] = (name, size, seeders, leechers, category, link, engine.onbrowser)
- self.SortItems(self.GetColumnSorter())
-
- def SortItems(self, *args, **kwargs):
- super(SearchResultsList, self).SortItems(*args, **kwargs)
- ArtManager.Get().MakeAlternateList(self)
-
- def GetListCtrl(self):
- return self
-
- def OnSortOrderChanged(self):
- self.parent.def_col = self._col
-
- class SearchListPanel(wx.Panel):
- def __init__(self, parent):
- wx.Panel.__init__(self, parent.splitter, -1)
- self.parent = parent
-
- self.searches = {}
- self.recent = []
-
- self.def_col = -1
-
- sizer = wx.BoxSizer(wx.VERTICAL)
-
- self.searchTerm = wx.SearchCtrl(self, size=(-1,-1), style=wx.TE_PROCESS_ENTER)
- self.searchTerm.ShowCancelButton(True)
- self.searchTerm.SetDescriptiveText(_("Enter Search Term..."))
- sizer.Add(self.searchTerm, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 0)
-
- nbstyle = NC.FNB_X_ON_TAB | NC.FNB_FF2 | NC.FNB_DROPDOWN_TABS_LIST | NC.FNB_NO_NAV_BUTTONS
- self.notebook = NC.FlatNotebook(self, wx.ID_ANY, style=nbstyle)
- sizer.Add(self.notebook, 1, wx.EXPAND|wx.ALL, 0)
-
- self.notebook.Bind(NC.EVT_FLATNOTEBOOK_PAGE_CLOSING, self.OnPageClosed)
- self.notebook._pages.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
- self.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN, self.OnClean, self.searchTerm)
- self.Bind(wx.EVT_TEXT_ENTER, self.onSearch, self.searchTerm)
- self.Bind(wx.EVT_MENU, self.onRecentSearch)
-
- self.SetSizerAndFit(sizer)
-
- def onRecentSearch(self, event):
- try:
- text = self.recent[event.GetId()-888]
- self.searchTerm.SetValue(text)
- self.searchTerm.SendTextUpdatedEvent()
- except:
- del self.recent[:]
- wx.CallAfter(self.searchTerm.SetMenu, None)
-
- def onSearch(self, event = None):
- term = self.searchTerm.GetValue()
- if not term:
- return
-
- if term in self.recent:
- self.recent.remove(term)
- self.recent.append(term)
- if len(self.recent) > 5:
- del self.recent[0]
- self.MakeMenu()
-
- resultList = self.newSearch(term)
- resultList.clearList()
- #self.searchTerm.Clear()
- self.parent.searchHandler.search(term, self.feedList)
-
- def MakeMenu(self):
- menu = Menu()
- item = menu.Append(-1, _("Recent Searches"))
- item.Enable(False)
- menu.AppendSeparator()
- for idx, txt in enumerate(self.recent):
- menu.Append(888+idx, txt)
- menu.AppendSeparator()
- menu.Append(888+len(self.recent), _("Clean"))
- self.searchTerm.SetMenu(menu)
-
- def OnClean(self, event):
- keyword = self.searchTerm.GetValue()
- if self.searches.has_key(keyword):
- del self.searches[keyword]
- self.searchTerm.SetValue("")
- self.searchTerm.SendTextUpdatedEvent()
-
- def OnLeftDClick(self, event):
- where, tabIdx = self.notebook._pages.HitTest(event.GetPosition())
- if where == NC.FNB_TAB and tabIdx >= 0:
- self.searchTerm.SetValue(self.notebook.GetPageText(tabIdx))
- self.searchTerm.SendTextUpdatedEvent()
- event.Skip()
-
- def newSearch(self, keyword):
- if keyword in self.searches:
- page = self.searches[keyword]
- self.notebook.SetSelection(self.notebook.GetPageIndex(page))
- else:
- page = SearchResultsList(self)
- self.notebook.AddPage(page, keyword)
- self.searches[keyword] = page
- return page
-
- def OnPageClosed(self, event):
- keyword = self.notebook.GetPageText(event.GetSelection())
- if keyword in self.searches:
- del self.searches[keyword]
- event.Skip()
-
- def feedList(self, data, keyword, engine):
- keyword = keyword.replace("%20", " ")
- if keyword in self.searches and data:
- wx.CallAfter(self.searches[keyword].feedList, data, engine)
-
- class SearchEnginesPanel(wx.Panel):
- def __init__(self, parent):
- wx.Panel.__init__(self, parent.splitter, -1)
- self.parent = parent
-
- sizer = wx.BoxSizer(wx.VERTICAL)
-
- # Make and layout the controls
- t = wx.StaticText(self, -1, _("Search Engines"))
- t.SetFont(wx.Font(self.GetFont().GetPointSize(), wx.SWISS, wx.NORMAL, wx.BOLD))
- sizer.Add(t, 0, wx.CENTER|wx.ALL, 5)
-
-
- self.enginesList = wx.CheckListBox(self, -1)
- sizer.Add(self.enginesList, 1, wx.EXPAND|wx.ALL, 0)
-
- btnsizer = wx.BoxSizer(wx.HORIZONTAL)
-
- AddButton = utility.makeBitmapButton(self, wx.ArtProvider.GetBitmap(wx.ART_ADD_BOOKMARK, wx.ART_TOOLBAR), _('Add'), self.onAdd)
- btnsizer.Add(AddButton, 1, wx.EXPAND|wx.ALL, 0)
-
- RemButton = utility.makeBitmapButton(self, wx.ArtProvider.GetBitmap(wx.ART_DEL_BOOKMARK, wx.ART_TOOLBAR), _('Remove'), self.onRemove)
- btnsizer.Add(RemButton, 1, wx.EXPAND|wx.ALL, 0)
-
- EditButton = utility.makeBitmapButton(self, wx.ArtProvider.GetBitmap(wx.ART_HELP_SETTINGS, wx.ART_TOOLBAR), _('Edit'), self.onEdit)
- btnsizer.Add(EditButton, 1, wx.EXPAND|wx.ALL, 0)
-
- sizer.Add(btnsizer, 0, wx.EXPAND|wx.ALL, 0)
-
- self.SetAutoLayout(True)
- self.SetSizerAndFit(sizer)
-
- self.Bind(wx.EVT_LISTBOX_DCLICK, self.onEdit, self.enginesList)
- self.Bind(wx.EVT_CHECKLISTBOX, self.OnUpdateCheck, self.enginesList)
- self.load()
-
- def load(self):
- engines = self.parent.searchHandler.engines
- engines.sort(key = lambda x: x.viewName.lower())
- self.enginesList.Set([engine.viewName for engine in engines])
- for index in range(len(engines)):
- self.enginesList.Check(index, engines[index].active)
-
- def OnUpdateCheck(self, event):
- index = event.GetSelection()
- engines = self.parent.searchHandler.engines
- if 0 <= index < len(engines):
- engines[index].active = self.enginesList.IsChecked(index)
- self.parent.searchHandler.save()
-
- event.Skip()
-
- def onAdd(self, event):
- dlg = AddEngineDialog(self, _("Add Engine"))
- result = dlg.ShowModal()
- dlg.Destroy()
- if result == wx.ID_OK:
- self.parent.searchHandler.addEngine(dlg.engine)
- self.parent.searchHandler.save()
- self.load()
-
- def onRemove(self, event):
- index = self.enginesList.GetSelection()
- if index < 0:
- return
- del self.parent.searchHandler.engines[index]
- self.parent.searchHandler.save()
- self.load()
-
- def onEdit(self, event):
- index = self.enginesList.GetSelection()
- if index < 0:
- return
- engine = self.parent.searchHandler.engines[index]
- dlg = AddEngineDialog(self, _("Edit Engine"), engine.url, engine.viewName, engine.onbrowser, *engine.headers)
- result = dlg.ShowModal()
- dlg.Destroy()
- if result == wx.ID_OK:
- dlg.engine.active = engine.active
- self.parent.searchHandler.engines[index] = dlg.engine
- self.parent.searchHandler.save()
- self.load()
-
- class AddEngineDialog(wx.Dialog):
- def __init__(self, parent, title, url = "", viewName = "", onbrowser = True,
- GroupTag = "item", linkTag = "link", nameTag = "title",
- sizeTag = "size", seedersTag = "seeders", leechersTag = "leechers",
- categoryTag = "category", descriptionTag = "description"):
- wx.Dialog.__init__(self, parent, -1, title)
-
- sizer = wx.BoxSizer(wx.VERTICAL)
-
- engineBox = wx.StaticBoxSizer(wx.StaticBox(self, -1, _('Engine')), wx.VERTICAL)
- engineSizer = wx.FlexGridSizer(0, 2, 5, 5)
- self.viewNameCtrl = wx.TextCtrl(self, -1, viewName, size = (300, -1))
- self.urlCtrl = wx.TextCtrl(self, -1, url, size = (300, -1))
- engineSizer.Add(wx.StaticText(self, -1, _("Name:")), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
- engineSizer.Add(self.viewNameCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 0)
- engineSizer.Add(wx.StaticText(self, -1, _("URL:")), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
- engineSizer.Add(self.urlCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 0)
- engineBox.Add(engineSizer, 0, wx.EXPAND|wx.ALL, 5)
- sizer.Add(engineBox, 0, wx.EXPAND|wx.ALL, 3)
-
- xmltagsBox = wx.StaticBoxSizer(wx.StaticBox(self, -1, _('XML Tags')), wx.VERTICAL)
- xmltagsSizer = wx.FlexGridSizer(0, 2, 5, 5)
- self.GroupTagCtrl = wx.TextCtrl(self, -1, GroupTag)
- self.linkTagCtrl = wx.TextCtrl(self, -1, linkTag)
- self.nameTagCtrl = wx.TextCtrl(self, -1, nameTag)
- self.sizeTagCtrl = wx.TextCtrl(self, -1, sizeTag)
- self.seedersTagCtrl = wx.TextCtrl(self, -1, seedersTag)
- self.leechersTagCtrl = wx.TextCtrl(self, -1, leechersTag)
- self.categoryTagCtrl = wx.TextCtrl(self, -1, categoryTag)
- self.descriptionTagCtrl = wx.TextCtrl(self, -1, descriptionTag)
- xmltagsSizer.Add(wx.StaticText(self, -1, _("New Group:")), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
- xmltagsSizer.Add(self.GroupTagCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 0)
- xmltagsSizer.Add(wx.StaticText(self, -1, _("Link:")), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
- xmltagsSizer.Add(self.linkTagCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 0)
- xmltagsSizer.Add(wx.StaticText(self, -1, _("Name:")), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
- xmltagsSizer.Add(self.nameTagCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 0)
- xmltagsSizer.Add(wx.StaticText(self, -1, _("Size:")), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
- xmltagsSizer.Add(self.sizeTagCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 0)
- xmltagsSizer.Add(wx.StaticText(self, -1, _("Seeders:")), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
- xmltagsSizer.Add(self.seedersTagCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 0)
- xmltagsSizer.Add(wx.StaticText(self, -1, _("Leechers:")), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
- xmltagsSizer.Add(self.leechersTagCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 0)
- xmltagsSizer.Add(wx.StaticText(self, -1, _("Category:")), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
- xmltagsSizer.Add(self.categoryTagCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 0)
- xmltagsSizer.Add(wx.StaticText(self, -1, _("*Extended*:")), 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 3)
- xmltagsSizer.Add(self.descriptionTagCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 0)
- xmltagsBox.Add(xmltagsSizer, 0, wx.EXPAND|wx.ALL, 5)
- sizer.Add(xmltagsBox, 0, wx.EXPAND|wx.ALL, 3)
-
- optionsBox = wx.StaticBoxSizer(wx.StaticBox(self, -1, _('Options')), wx.VERTICAL)
- self.onbrowser = wx.CheckBox(self, -1, _("Open results in default browser"))
- self.onbrowser.SetValue(onbrowser)
- optionsBox.Add(self.onbrowser, 0, wx.EXPAND|wx.ALL, 5)
- sizer.Add(optionsBox, 0, wx.EXPAND|wx.ALL, 3)
-
- sizer.Add(wx.StaticLine(self), 0, wx.GROW, 0)
-
- buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
- okButton = wx.Button(self, wx.ID_OK, _("OK"))
- cancelButton = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
- buttonSizer.Add(okButton, 0, wx.EXPAND|wx.ALL, 5)
- buttonSizer.Add(cancelButton, 0, wx.EXPAND|wx.ALL, 5)
- sizer.Add(buttonSizer, 0, wx.CENTER, 0)
-
- self.SetSizerAndFit(sizer)
-
- self.Bind(wx.EVT_BUTTON, self.onOK, id = wx.ID_OK)
- self.Bind(wx.EVT_BUTTON, self.onCancel, id = wx.ID_CANCEL)
-
- def onOK(self, event):
- if not self.urlCtrl.GetValue():
- dlg = wx.MessageDialog(self, _("You must enter a value for the URL field"), _("Input Error"), wx.OK)
- dlg.ShowModal()
- dlg.Destroy()
- self.urlCtrl.SetFocus()
- return
- headers = (self.GroupTagCtrl.GetValue(), self.linkTagCtrl.GetValue(), self.nameTagCtrl.GetValue(),
- self.sizeTagCtrl.GetValue(), self.seedersTagCtrl.GetValue(), self.leechersTagCtrl.GetValue(),
- self.categoryTagCtrl.GetValue(), self.descriptionTagCtrl.GetValue())
- self.engine = SearchEngine(self.urlCtrl.GetValue(), self.viewNameCtrl.GetValue(),
- headers, onbrowser = self.onbrowser.GetValue())
- self.EndModal(wx.ID_OK)
-
- def onCancel(self, event):
- self.EndModal(wx.ID_CANCEL)
-
- class SearchEngine(object):
- def __init__(self, url = "", viewName = None,
- headers = ("item", "link", "title", "size", "seeders", "leechers", "category", "description"),
- active = True, onbrowser = True):
- self.url = url
- self.viewName = viewName or url
- self.headers = headers
- self.active = active
- self.onbrowser = onbrowser
-
- def toFile(self):
- return [self.url, self.viewName, self.headers, self.active, self.onbrowser]
-
- def fromFile(self, data):
- self.url = data[0]
- self.viewName = data[1]
- self.headers = data[2]
- self.active = data[3]
- self.onbrowser = data[4]
- return self
-
-
- class SearchHandler(object):
- def __init__(self):
-
- # Setup config file
- self.config_file = os.path.join(utility.getConfigPath(), "search.conf")
-
- # Load Engines
- self.engines = []
- self.load()
-
- self.current = {}
-
- def addEngine(self, engine):
- self.engines.append(engine)
-
- def search(self, keyword, callback):
- keyword = keyword.replace(" ", "%20")
- engines = [engine for engine in self.engines if engine.active]
- for engine in engines:
- if "%@" in engine.url:
- url = engine.url.replace("%@", keyword)
- else:
- url = engine.url + keyword
- searchThread = Thread(target=self.__perform_search, args = [url, engine, callback, keyword])
- searchThread.setDaemon(True)
- searchThread.start()
-
- def __perform_search(self, url, engine, callback, keyword):
- try:
- answer = urlopen(url, encoding = None)
- if answer:
- xmlHandler = XMLHandler(engine)
- parseString(answer.read(), xmlHandler)
- callback(xmlHandler.data, keyword, engine)
- except (IOError, ValueError, SAXParseException), e:
- print url, e
- wx.LogDebug(str(e))
-
- def save(self):
- """
- Save to config file
- """
- try:
- cp = ConfigParser.ConfigParser()
- cp.add_section('SEARCH')
- i = 0
- for item in self.engines:
- cp.set('SEARCH', str(i), item.toFile())
- i = i+1
- file = open(self.config_file, 'w')
- cp.write(file)
- file.close()
- except:
- pass
-
- def load(self):
- """
- Load from config file
- """
- if not existsAndIsReadable(self.config_file):
- return
-
- try:
- cp = ConfigParser.ConfigParser()
- file = open(self.config_file, 'r')
- cp.readfp(file)
- items = cp.items('SEARCH')
- for key, param in items:
- param = eval(param)
- self.addEngine(SearchEngine().fromFile(param))
- file.close()
- except:
- wx.LogError("couldn't read from search file")
-
- class XMLHandler(handler.ContentHandler):
- TORRENT = 0
- LINK = 1
- NAME = 2
- SIZE = 3
- SEEDERS = 4
- LEECHERS = 5
- CATEGORY = 6
- DESCRIPTION = 7
- def __init__(self, engine):
- handler.ContentHandler.__init__(self)
- self.headers = ("Torrent", "Link", "Name", "Size", "Seeders", "Leechers", "Category", "description")
- self.engine = engine
- self.currentHeader = None
- self.data = []
-
- def startElement(self, name, attrs):
- if name != self.engine.headers[self.TORRENT] and not self.data:
- return
-
- if name == self.engine.headers[self.TORRENT]:
- assert self.currentHeader == None
- self.currentHeader = self.TORRENT
- self.data.append({})
-
- elif name == self.engine.headers[self.LINK]:
- self.currentHeader = self.LINK
- obj = self.data[-1]
- if attrs.get("href"):
- obj[self.headers[self.LINK]] = attrs["href"]
- elif attrs.get("url"):
- obj[self.headers[self.LINK]] = attrs["url"]
- if attrs.get("length") and not obj.has_key(self.headers[self.SIZE]):
- size = int(attrs["length"]) / (1024*1024)
- obj[self.headers[self.SIZE]] = str(size) + " MB"
-
- elif name == self.engine.headers[self.NAME]:
- self.currentHeader = self.NAME
-
- elif name == self.engine.headers[self.SIZE]:
- self.currentHeader = self.SIZE
-
- elif name == self.engine.headers[self.SEEDERS]:
- self.currentHeader = self.SEEDERS
-
- elif name == self.engine.headers[self.LEECHERS]:
- self.currentHeader = self.LEECHERS
-
- elif name == self.engine.headers[self.CATEGORY]:
- self.currentHeader = self.CATEGORY
-
- elif name == self.engine.headers[self.DESCRIPTION]:
- self.currentHeader = self.DESCRIPTION
- else:
- self.currentHeader = None
-
- def endElement(self, name):
- # Group ended
- if name == self.engine.headers[self.TORRENT]:
- self.currentHeader = None
-
- def characters(self, content):
- try:
- self._characters(content)
- except:
- pass
-
- def _characters(self, content):
- def isnumber(s):
- try:
- float(s)
- return True
- except:
- return False
-
- content = content.strip()
-
- # Bad state
- if self.currentHeader == None or not content or not self.data:
- return
-
- obj = self.data[-1]
-
- # Already have this data
- if obj.has_key(self.headers[self.currentHeader]) \
- and self.currentHeader != self.DESCRIPTION:
- return
-
- # fix content
- content = saxutils.unescape(content)
-
- # Name
- if self.currentHeader == self.NAME:
- if not self.engine.headers[self.SEEDERS] and not self.engine.headers[self.LEECHERS]:
- match = regex.match(content)
- if match:
- obj[self.headers[self.NAME]] = match.group(1)
- obj[self.headers[self.SEEDERS]] = match.group(2)
- obj[self.headers[self.LEECHERS]] = match.group(3)
- else:
- obj[self.headers[self.NAME]] = content
- else:
- obj[self.headers[self.NAME]] = content
-
- # Description
- elif self.currentHeader == self.DESCRIPTION:
- sizekeys = ("size:", "filesize:", "file size:")
- seedkeys = ("seeders:", "seeds:", "seed:")
- peerkeys = ("leechers:", "peers:", "leech:", "downloaders:")
- content = content.replace(" :", ":").lower()
- content = content.split(" ")
-
- for index in xrange(len(content)):
- item = content[index]
- if not item.strip():
- continue
-
- data = ""
- for key in sizekeys:
- if item == key:
- data = content[index+1].upper()
- if isnumber(data):
- data += " " + content[index+2].upper()
- elif item.startswith(key):
- data = item.split(key)[1].upper()
- if isnumber(data):
- data += " " + content[index+1].upper()
- if data:
- obj[self.headers[self.SIZE]] = data
-
- data = ""
- for key in seedkeys:
- if item == key:
- data = content[index+1]
- elif item.startswith(key):
- data = item.split(key)[1]
- if data:
- obj[self.headers[self.SEEDERS]] = data
-
- data = ""
- for key in peerkeys:
- if item == key:
- data = content[index+1]
- elif item.startswith(key):
- data = item.split(key)[1]
- if data:
- obj[self.headers[self.LEECHERS]] = data
- # Other
- else:
- obj[self.headers[self.currentHeader]] = content