=== added file 'src/utils/feed_utils.py' --- src/utils/feed_utils.py 1970-01-01 00:00:00 +0000 +++ src/utils/feed_utils.py 2008-01-15 20:28:56 +0000 @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +# feed_utils - utilies for managing feeds in Entertainer +# Copyright (C) 2008 Joshua Scotton +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from xml.dom import minidom + +class OPMLParser: + """Enables parsing of opml files""" + + #Filename of opml file + opml_filename = "" + + + def __init__(self, filename): + """sets up parser""" + #all we need for this parser is the file to parse! + self.opml_filename = filename + + def get_rss_feeds(self): + """returns list of rss feeds in the opml file""" + list = [] + #this loads the xml from the opml file into xmldoc + xmldoc = minidom.parse(self.opml_filename) + #the rss feeds are in the xmlUrl attributes of the outline tags + #we loop through all the outline nodes in the opml file and add + #the contents of any xmlUrl attributes to the list variable + for node in xmldoc.getElementsByTagName('outline'): + url = node.getAttribute('xmlUrl') + if url.strip() <> "": + list.append(url) + #returns the list of rss feeds from the opml file + return list === modified file 'src/backend/components/feeds/feed_manager.py' --- src/backend/components/feeds/feed_manager.py 2008-01-12 23:12:19 +0000 +++ src/backend/components/feeds/feed_manager.py 2008-01-15 20:24:23 +0000 @@ -83,6 +83,16 @@ db_conn.commit() db_conn.close() + def wipeFeedCacheDatabase(self): + """Wipes the feed cache database file and tables.""" + # all we need to do to wipe the cache database is to drop the two tables containing the data: feed and entry + db_conn = sqlite.connect(self.__class__.FEED_DB) + db_cursor = db_conn.cursor() + db_cursor.execute("""DROP TABLE feed IF EXISTS""") + db_cursor.execute("""DROP TABLE entry IF EXISTS""") + db_conn.commit() + db_conn.close() + # Implementes MessageHandler interface def handleMessage(self, message): """ @@ -91,6 +101,20 @@ """ if message.get_type() == MessageType.UPDATE_FEEDS: self.updateFeedCache() + elif message.get_type() == MessageType.REBUILD_FEED_CACHE: + #rebuild feed cache + self.rebuildFeedCacheDatabase() + + def rebuildFeedCacheDatabase(self): + """Destroy all current data and index everything from the scratch.""" + self.logger.info("Feed cache rebuilding requested") + #we remove the tables from the database the create them again, this wipes the tables + self.wipeFeedCacheDatabase() + self.createFeedCacheDatabase() + #there will be no data in the tables so we need to populate the feed cache + self.updateFeedCache() + self.logger.info("Feed cache rebuilt") + def getName(self): """Get MessageHandler name.""" === modified file 'src/backend/core/client_connection.py' --- src/backend/core/client_connection.py 2008-01-12 23:12:19 +0000 +++ src/backend/core/client_connection.py 2008-01-14 22:27:02 +0000 @@ -72,7 +72,7 @@ dictionary = cPickle.loads(dict_str[:-1]) except: self.logger.error("Client sent malformed message dictionary to the backend!") - + # This client to the message bus if desired if dictionary != None and len(dictionary) > 0: self.message_bus.registerMessageHandler(self, dictionary) === modified file 'src/backend/core/message_type_priority.py' --- src/backend/core/message_type_priority.py 2008-01-12 23:12:19 +0000 +++ src/backend/core/message_type_priority.py 2008-01-15 19:55:20 +0000 @@ -85,5 +85,8 @@ # Require to rebuild video cache REBUILD_VIDEO_CACHE = 15 + # Require to rebuild feed cache + REBUILD_FEED_CACHE = 16 + # Weather information has been updated - WEATHER_INFO_UPDATED = 16 \ No newline at end of file + WEATHER_INFO_UPDATED = 17 === modified file 'src/entertainer-messagebus-notifier.py' --- src/entertainer-messagebus-notifier.py 2008-01-12 23:12:19 +0000 +++ src/entertainer-messagebus-notifier.py 2008-01-18 21:12:26 +0000 @@ -37,6 +37,7 @@ "PREFERENCES_CONF_UPDATED" : MessageType.PREFERENCES_CONF_UPDATED, "REBUILD_IMAGE_CACHE": MessageType.REBUILD_IMAGE_CACHE, "REBUILD_MUSIC_CACHE": MessageType.REBUILD_MUSIC_CACHE, + "REBUILD_FEED_CACHE": MessageType.REBUILD_FEED_CACHE, "REBUILD_VIDEO_CACHE": MessageType.REBUILD_VIDEO_CACHE } try: argument = (sys.argv[1])[10:] @@ -55,6 +56,7 @@ - REBUILD_IMAGE_CACHE - REBUILD_MUSIC_CACHE - REBUILD_VIDEO_CACHE + - REBUILD_FEED_CACHE """ sys.exit(1) try: === modified file 'src/frontend/medialibrary/feeds.py' --- src/frontend/medialibrary/feeds.py 2008-01-13 15:56:50 +0000 +++ src/frontend/medialibrary/feeds.py 2008-01-14 22:47:36 +0000 @@ -169,7 +169,7 @@ cursor = connection.cursor() cursor.execute("""SELECT title, subtitle, description, time, date FROM feed - WHERE feed_url=:url""", + WHERE url=:url""", { "url" : self.url }) result = cursor.fetchall() self.title = result[0][0] @@ -316,4 +316,4 @@ @return: String """ return self.date - \ No newline at end of file + === modified file 'src/utils/content_management_dialog.py' --- src/utils/content_management_dialog.py 2008-01-13 17:16:40 +0000 +++ src/utils/content_management_dialog.py 2008-01-18 21:39:12 +0000 @@ -2,6 +2,7 @@ # content_management_dialog - Content management GUI for Entertainer. # Copyright (C) 2007 Lauri Taimila +# + additions by blackcougar (C) 2008 Joshua Scotton # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,6 +27,7 @@ from backend.core.message import Message from backend.core.message_bus_proxy import MessageBusProxy from backend.core.message_type_priority import MessageType +from utils.feed_utils import OPMLParser try: import pygtk @@ -74,7 +76,8 @@ # Get content management dialog and bind signal callbacks self.dialog = self.widgets.get_widget("ContentManagementDialog") if (self.dialog): - callback_dic = { "on_close_button_clicked" : self.on_close_button_clicked, + callback_dic = { "on_button_open_list_clicked" : self.on_button_open_list_clicked, + "on_close_button_clicked" : self.on_close_button_clicked, "on_button_remove_videos_clicked" : self.on_button_remove_videos_clicked, "on_button_add_videos_clicked" : self.on_button_add_videos_clicked, "on_button_edit_videos_clicked" : self.on_button_edit_videos_clicked, @@ -405,6 +408,53 @@ # No need for actions. Item wasn't selected. pass + def on_button_open_list_clicked(self, widget): + """Opens add file dialog and then adds all feeds in the opml file selected """ + #get the widget containing the feedlist + widget = self.widgets.get_widget("treeview_feeds") + model = widget.get_model() + # open select file dialog + dialog = gtk.FileChooserDialog("Select OPML file",None,gtk.FILE_CHOOSER_ACTION_OPEN,(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK)) + #filter for only opml files + filter = gtk.FileFilter() + filter.set_name("OPML files") + filter.add_pattern("*.opml") + dialog.add_filter(filter) + #allow multiple selections + dialog.set_select_multiple(True) + status = dialog.run() + # if file was selected, get list of feeds from it and add to + #model and update config file + if(status == gtk.RESPONSE_OK): + try: + rss_list = [] + #we are allowing multiple selection so we need to loop through each file selected + selected_files = dialog.get_filenames() + for opml_file in selected_files: + #get the list of feeds from each file + opml_feeds = OPMLParser(opml_file).get_rss_feeds() + #add the feeds to the rss_list + for feed in opml_feeds: + rss_list.append(feed) + #now we need append the feeds to self.feeds and content manager + for feed in rss_list: + #this is adding the feed to the content manager widget + model.append([feed]) + #this adds the feed to the self.feeds ready to add to the content.conf file + self.feeds.append(feed) + #now we can parse the new feeds and add to content.conf + str_folders = string.join(self.feeds, ';') + self.config.set("RSS","feeds", str_folders) + cfg_file = file(os.path.expanduser('~/.entertainer/content.conf'), 'w') + self.config.write(cfg_file) + + except IOError: + #Catching IOError + error = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "IOError: There seems to be a problem with the file that you selected.") + error.run() + error.destroy() + dialog.destroy() + def on_fetch_interval_spinbutton_value_changed(self, widget): self.config.set("RSS","fetch_interval", widget.get_value_as_int()) cfg_file = file(os.path.expanduser('~/.entertainer/content.conf'),'w') @@ -454,14 +504,10 @@ proxy.sendMessage(Message(MessageType.REBUILD_VIDEO_CACHE)) proxy.disconnectFromMessageBus() except: - #error = gtk.MessageDialog(None, - # (gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT), - # gtk.MESSAGE_ERROR, - # gtk.BUTTONS_OK, - # "Entertainer server is not running!\nCan't rebuild media cache.") - #error.set_title("Error") - #error.run() - pass + # This means that backend is not running. Doesn't matter + error = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Entertainer backend is not running. Cache cannot be rebuilt.") + error.run() + error.destroy() def on_button_music_rebuild_clicked(self, widget): """Rebuild music cache requested""" @@ -471,7 +517,10 @@ proxy.sendMessage(Message(MessageType.REBUILD_MUSIC_CACHE)) proxy.disconnectFromMessageBus() except: - pass # This means that backend is not running. Doesn't matter + # This means that backend is not running. Doesn't matter + error = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Entertainer backend is not running. Cache cannot be rebuilt.") + error.run() + error.destroy() def on_button_image_rebuild_clicked(self, widget): """Rebuild image cache requested""" @@ -481,12 +530,30 @@ proxy.sendMessage(Message(MessageType.REBUILD_IMAGE_CACHE)) proxy.disconnectFromMessageBus() except: - pass # This means that backend is not running. Doesn't matter + # This means that backend is not running. Doesn't matter + error = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Entertainer backend is not running. Cache cannot be rebuilt.") + error.run() + error.destroy() def on_button_feed_rebuild_clicked(self, widget): """Rebuild feed cache requested""" - print "Feed cache udpate is not implemented yet." - pass + #We need the user to confirm the rebuild feed cache request + dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK_CANCEL, "This will completely remove any feed entries in the cache!") + status = dialog.run() + #If user has ok'd the request send the message to the message bus + if(status == gtk.RESPONSE_OK): + try: + proxy = MessageBusProxy(client_name = "Content Management GUI") + proxy.connectToMessageBus() + proxy.sendMessage(Message(MessageType.REBUILD_FEED_CACHE)) + proxy.disconnectFromMessageBus() + except: + # This means that backend is not running. Doesn't matter + error = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, "Entertainer backend is not running. Cache cannot be rebuilt.") + error.run() + error.destroy() + dialog.destroy() + # Signal handlers end === modified file 'src/utils/glade/entertainer-content-management.glade' --- src/utils/glade/entertainer-content-management.glade 2008-01-12 23:12:19 +0000 +++ src/utils/glade/entertainer-content-management.glade 2008-01-14 22:58:01 +0000 @@ -1,6 +1,6 @@ - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK @@ -716,6 +716,7 @@ True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Add RSS Feed gtk-add True 0 @@ -728,6 +729,7 @@ True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Remove RSS Feed gtk-remove True 0 @@ -743,6 +745,7 @@ True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Edit current item gtk-edit True 0 @@ -752,6 +755,22 @@ 2 + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Add feeds from a datasource + gtk-open + True + 0 + + + + 3 + + False @@ -955,7 +974,6 @@ True - False True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK