#! /usr/bin/env python
# -*- coding: utf-8 -*-
#-----------------------------------------------------------------------------
# Name: g3rss.py
# Purpose:
#
# Author: d0c 54v4g3, Jeremy Arendt
#
# Created: 2004/23/02
# RCS-ID: $Id:
# Copyright: (c) 2002
# Licence: See G3.LICENCE.TXT
#-----------------------------------------------------------------------------
import wx
import wx.html as html
import webbrowser
import urlparse
import ConfigParser
from wx.lib.mixins.listctrl import ColumnSorterMixin,ListCtrlAutoWidthMixin
from btconfig import BTConfig
from images import Images
from g3listctrl import *
from feedparser import parse
from string import split
from threading import Thread
from os.path import join,exists
import os
import os.path
if sys.platform == "win32":
win32_flag = True
else:
win32_flag = False
class T_GetFeed(Thread):
def __init__(self, invokelater, callback, url):
Thread.__init__(self)
self.success = False
self.Callback = callback
self.InvokeLater = invokelater
self.url = url
def run(self):
data = None
try:
data = parse(self.url)
except:
self.success = False
return False
self.success = True
try:
self.InvokeLater(self.Callback, data)
except wx.PyDeadObjectError:
pass
return True
class RSSList(G3ListCtrl, ListCtrlAutoWidthMixin, ColumnSorterMixin):
def __init__(self, parent, btconfig, bmps=None, pos=wx.DefaultPosition, size=wx.DefaultSize,
style = wx.LC_REPORT | wx.LC_VRULES, onclickfunc=None, ondblclickfunc=None, onurlfunc=None, onbrowfunc=None, oncopyfunc=None):
G3ListCtrl.__init__(self, parent, btconfig, "RSSList", -1, pos, size, style)
self.CmdOnClick = onclickfunc
self.CmdDblClick = ondblclickfunc
self.CmdURL = onurlfunc
self.CmdBrowFunc = onbrowfunc
self.CmdCopyFunc = oncopyfunc
self.btconfig = btconfig
ListCtrlAutoWidthMixin.__init__(self)
ColumnSorterMixin.__init__(self, 2)
self.itemDataMap = {}
self.list_rows = {}
self.previtem = 0 # used to limit html refresh to once a selection
self.col2sort = 1
self.ascending = False
cols = [ [True, _("Title"), wx.LIST_FORMAT_LEFT, 300],
[True, _("URL"), wx.LIST_FORMAT_LEFT, 400],
]
self.InsertColumns(cols)
wx.EVT_LIST_ITEM_ACTIVATED(self, -1, self.OnDblClick)
wx.EVT_COMMAND_LEFT_CLICK(self, -1, self.OnClick) # wxMSW
wx.EVT_LEFT_UP(self, self.OnClick) # wxGTK
wx.EVT_COMMAND_RIGHT_CLICK(self, -1, self.OnListRightClick) # wxMSW
wx.EVT_RIGHT_UP(self, self.OnListRightClick) # wxGTK
def GetSelectedData(self):
if self.GetFirstSelected() != -1:
return self.GetItemData( self.GetFirstSelected() )
else:
return None
def OnDblClick(self, event):
if self.CmdDblClick:
key = self.GetSelectedData()
if self.itemDataMap.has_key(key):
self.CmdDblClick(key)
def OnClick(self, event):
if self.CmdOnClick:
key = self.GetSelectedData()
if self.itemDataMap.has_key(key) and key != self.previtem:
self.CmdOnClick(key)
self.previtem = key
def OnListRightClick(self, event):
if not hasattr(self, "_popup_id"):
self._popup_id = []
for i in range(0, 2):
self._popup_id.append(wx.NewId())
wx.EVT_MENU(self, self._popup_id[0], self.MenuOnCopy)
wx.EVT_MENU(self, self._popup_id[1], self.MenuOnBrowser)
listmenu = wx.Menu()
listmenu.Append(self._popup_id[0], _("Copy URL to clipboard"), _("Copy URL to clipboard"))
listmenu.Append(self._popup_id[1], _("Open URL in Default Browser"), _("Open URL in Default Browser"))
self.PopupMenu(listmenu, self.ScreenToClient(wx.GetMousePosition()))
listmenu.Destroy()
def MenuOnCopy(self, event):
if self.CmdCopyFunc:
key = self.GetSelectedData()
if self.itemDataMap.has_key(key):
self.CmdCopyFunc(key)
def MenuOnBrowser(self, event):
if self.CmdBrowFunc:
key = self.GetSelectedData()
if self.itemDataMap.has_key(key):
self.CmdBrowFunc(key)
def OnURL(self, event):
if self.CmdDblClick:
key = self.GetSelectedData()
if self.itemDataMap.has_key(key):
self.CmdDblClick(key)
def GetListCtrl(self):
return self
def Populate(self, rowdata):
self.DeleteAllItems()
for row in rowdata:
key = hash(row)
self.itemDataMap[key] = row
item_idx = self.InsertRow(key, row)
class HTMLDescrption(html.HtmlWindow):
def __init__(self, parent, btconfig, addfunc=None):
html.HtmlWindow.__init__(self, parent, -1, size=(-1,-1), style=wx.SUNKEN_BORDER)
self.SetBorders(4)
self.CmdAddFunc = addfunc
wx.EVT_COMMAND_RIGHT_CLICK(self, -1, self.OnRightClick) # wxMSW
wx.EVT_RIGHT_UP(self, self.OnRightClick) # wxGTK
def OnLinkClicked(self, link):
if ".torrent" in link.GetHref():
if self.CmdAddFunc:
self.CmdAddFunc(link.GetHref())
def OnRightClick(self, event):
if not hasattr(self, "htmlpopID1"):
self.htmlpopID1 = wx.NewId()
wx.EVT_MENU(self, self.htmlpopID1, self.OnViewSource)
htmlmenu = wx.Menu()
htmlmenu.Append(self.htmlpopID1, _("View Source"), _("View Source"))
self.PopupMenu(htmlmenu, self.ScreenToClient(wx.GetMousePosition()))
htmlmenu.Destroy()
def OnViewSource(self, event):
import wx.lib.dialogs
source = self.GetParser().GetSource()
dlg = wx.lib.dialogs.ScrolledMessageDialog(self, source, _("HTML Source"))
dlg.ShowModal()
dlg.Destroy()
class RSSPanel(wx.Panel):
def __init__(self, parent, btconfig, path="", pos=wx.DefaultPosition, size=(100,100),
bmps = None, addfunc=None):
wx.Panel.__init__(self, parent, -1)
if addfunc:
self.AddTorrent = addfunc
self.rowdata = []
self.feeds = []
self.descriptions = {}
self.rendering = False
if win32_flag:
self.rss_file = join(btconfig['path'], "rssfeeds.ini")
else:
self.rss_file = join(os.path.expanduser('~/.Rufus'), "rssfeeds.ini")
self.splitter = wx.SplitterWindow(self, -1, style=wx.SP_3D|wx.SP_BORDER)
self.splitter.SetMinimumPaneSize(40)
self.htmlpanel = wx.Panel(self.splitter, -1)
self.listpanel = wx.Panel(self.splitter, -1)
self.list = RSSList(self.listpanel, btconfig, onclickfunc=self.OnClick, ondblclickfunc=self.OnDblClick, onurlfunc=self.OnURL, onbrowfunc=self.OnBrowser, oncopyfunc=self.OnCopy)
self.html = HTMLDescrption(self.htmlpanel, btconfig, addfunc=self.AddTorrent)
self.LoadFeeds() # Get feeds from rss ini
self.editurl = wx.ComboBox(self, -1, "", size=(200,20), choices=self.feeds)
if self.feeds:
self.editurl.SetValue(self.feeds[0])
self.updatebutt = wx.Button(self, -1, _("Update"))
amendbutt = wx.Button(self, -1, _("Save/Del"))
amendbutt.SetToolTipString(_("Save/delete the RSS feed URL"))
colsizer = wx.FlexGridSizer(2, 1, 0, 0)
htmlsizer = wx.FlexGridSizer(1, 1, 0, 0)
listsizer = wx.FlexGridSizer(1, 1, 0, 0)
topsizer = wx.FlexGridSizer(1, 3, 0, 0)
topsizer.Add(amendbutt, 0, 0)
topsizer.Add(self.editurl, 0, wx.EXPAND, 0)
topsizer.Add(self.updatebutt, 0, 0)
topsizer.AddGrowableCol(1)
colsizer.Add(topsizer, 1, wx.EXPAND, 0)
listsizer.Add(self.list, 1, wx.EXPAND, 0)
self.listpanel.SetAutoLayout(True)
self.listpanel.SetSizer(listsizer)
listsizer.Fit(self.listpanel)
listsizer.SetSizeHints(self.listpanel)
listsizer.AddGrowableRow(0)
listsizer.AddGrowableCol(0)
htmlsizer.Add(self.html, 0, wx.EXPAND, 0)
self.htmlpanel.SetAutoLayout(True)
self.htmlpanel.SetSizer(htmlsizer)
htmlsizer.Fit(self.htmlpanel)
htmlsizer.SetSizeHints(self.htmlpanel)
htmlsizer.AddGrowableRow(0)
htmlsizer.AddGrowableCol(0)
self.splitter.SplitHorizontally(self.listpanel, self.htmlpanel, 153)
colsizer.Add(self.splitter, 1, wx.EXPAND, 1)
self.SetAutoLayout(True)
self.SetSizer(colsizer)
colsizer.Fit(self)
colsizer.SetSizeHints(self)
colsizer.AddGrowableRow(1)
colsizer.AddGrowableCol(0)
wx.EVT_BUTTON(self, self.updatebutt.GetId(), self.Populate)
wx.EVT_BUTTON(self, amendbutt.GetId(), self.Amend)
wx.EVT_SIZE(self, self.OnSize)
def AddTorrent(self, url):
print url
def OnSize(self, event):
self.Layout()
if self.splitter.GetSashPosition() > 0:
self.splitter.SetSashPosition(self.splitter.GetSashPosition())
def OnClick(self, key):
wx.CallAfter(self.RenderHtml, self.RenderHtml(key))
def OnDblClick(self, key):
desc = self.descriptions.get(key)
if desc:
self.AddTorrent(desc[5])
def OnBrowser(self, key):
desc = self.descriptions.get(key)
if desc:
Thread(target = self._OpenUrl, args = [desc[5]]).start()
def OnCopy(self, key):
desc = self.descriptions.get(key)
if desc:
clipdata = wx.TextDataObject()
clipdata.SetText(desc[5])
wx.TheClipboard.Open()
wx.TheClipboard.SetData(clipdata)
wx.TheClipboard.Close()
def OnURL(self, key, type=0):
wx.CallAfter(self.RenderHtml, self.RenderHtml(key))
def Amend(self, event):
item = self.editurl.GetValue()
key = self.editurl.GetSelection()
if item != "":
if item in self.feeds:
print "Deleting RSS entry %s" % item
self.feeds.remove(item)
else:
print "Adding RSS entry %s" % item
self.feeds.append(item)
self.SaveFeeds() ## Save Changes
self.editurl.Clear()
self.editurl.SetValue("")
if self.feeds: # repopulate combobox
for f in self.feeds:
self.editurl.Append(f)
def GetFeed(self, url):
#disable update button to stop hammering of RSS feed by user
self.updatebutt.Enable(False)
t = T_GetFeed(self.Getdata, "", url)
t.start()
def Getdata(self, params, result):
import time
time.sleep(0.01)
self.FeedResults(params, result)
def FeedResults(self, params, result):
from html2text import html2text
self.html.SetPage("")
self.rowdata = []
self.descriptions = {}
#re-enable update button
self.updatebutt.Enable(True)
if result.entries:
#feed title
feedtitle = result.feed.title
#base URL
feedlink = result.feed.link
feedbase = result.feed.title_detail.base
for entry in result.entries:
## print entry
url = "..."
try:
url = entry.enclosures[0].url
except:
url = entry.links[0].href
titlelink = entry.get('link', "")
entrytitle = entry.get('title', "")
entrysummary = entry.get('summary', "")
fixed_row = html2text(entrytitle)
row = (fixed_row[:len(fixed_row)-1], url) # removes CR from end of string (thanks to Matias for spotting this one)
key = hash(row)
self.rowdata.append(row)
self.descriptions[key] = feedbase, feedtitle, titlelink, entrytitle , entrysummary, url
self.list.Populate(self.rowdata)
else:
row = (_("No data in RSS feed"),"...")
key = hash(row)
self.rowdata.append(row)
self.list.Populate(self.rowdata)
def Populate(self, e=None):
url = self.editurl.GetValue()
i = 0
found = False
while i < self.editurl.GetCount():
if self.editurl.GetString(i) == url:
found = True
break
i += 1
if not found:
self.editurl.Insert(url, 0)
self.GetFeed(url)
def RenderHtml(self, key):
desc = self.descriptions.get(key)
if desc and not self.rendering:
self.rendering = True
page = "<html> \n" \
+"<head> \n" \
+" <base href=" + desc[0] + "> \n" \
+"</head> \n" \
+"<body> \n" \
+" <table width='100%' bgcolor='#cccccc' cellpadding='0' cellspacing='0' border='0'> \n" \
+" <tr><td> \n" \
+" <font face='Verdana,Sans-serif' size=10pt color='#000000'><a href='" + desc[0] + "'><b>Feed: </b>" + desc[1] + "</a><br> \n" \
+" <a href='" + desc[2] + "'><b>Title: </b>" + desc[3] + "</a><br></font> \n" \
+" </td><td align='right'> \n" \
+" </td></tr> \n" \
+" <tr bgcolor='#666666' height='1'><td colspan='2'></td></tr> \n" \
+" </table> \n" \
+" <table width='100%' cellpadding='2' cellspacing='2' border='0'><tr><td> \n" \
+" " + desc[4] +" \n" \
+" </td></tr> \n" \
+" </table> \n" \
+" </body> \n" \
+"</html> \n"
self.html.SetPage(page)
self.rendering = False
def _OpenUrl(self, url):
try:
webbrowser.open_new(url)
except:
pass
def OnUrlClick(self, event):
url = self.tlabels[1].GetLabel()
url = "http://%s/" % (urlparse.urlparse(url)[1])
Thread(target = self._OpenUrl, args = [url]).start()
def SaveFeeds(self):
print 'Saving RSS Feeds:', self.rss_file
try:
cp = ConfigParser.ConfigParser()
cp.add_section('RSS feeds')
i = 0
for f in self.feeds:
cp.set('RSS feeds', str(i), f)
i += 1
file = open(self.rss_file, 'w')
cp.write(file)
file.close()
except IOError, e:
print "ERROR: writing to RSS ini file -", str(e)
except:
print "ERROR: writing to RSS ini file"
def LoadFeeds(self):
if exists(self.rss_file):
print 'Loading RSS Feeds:', self.rss_file
try:
cp = ConfigParser.ConfigParser()
file = open(self.rss_file, 'r')
cp.readfp(file)
items = cp.items('RSS feeds')
for key, param in items:
self.feeds.append(param)
file.close()
except IOError, e:
print "ERROR: accessing rssfeeds.ini - ", str(e)
except (ConfigParser.MissingSectionHeaderError, ConfigParser.NoSectionError), e:
print "ERROR: processing rssfeeds.ini - ", str(e)
if __name__ == "__main__":
_ = lambda x: x # needed for gettext
app = wx.PySimpleApp()
frame = wx.Frame(None, -1, '', size=(800,500))
ppp = RSSPanel(frame, BTConfig())
ppp.Show(True)
frame.Show(True)
app.MainLoop()
|