Author: dmeyer
Date: Sat Sep 15 17:18:32 2007
New Revision: 2813

Log:
Move the feed code to kaa.beacon and integrate it into the
server. This is still experimental. One bug showed up when
a client locked the db and quit before unlocking it. This
should be fixed now but something is still not right.


Added:
   trunk/beacon/src/server/channel/
   trunk/beacon/src/server/channel/__init__.py
      - copied, changed from r2798, /trunk/WIP/netsearch/src/feed/__init__.py
   trunk/beacon/src/server/channel/channel.py
      - copied, changed from r2809, /trunk/WIP/netsearch/src/feed/channel.py
   trunk/beacon/src/server/channel/feedparser.py
      - copied unchanged from r2794, 
/trunk/WIP/netsearch/src/feed/lib/feedparser.py
   trunk/beacon/src/server/channel/manager.py
      - copied, changed from r2809, /trunk/WIP/netsearch/src/feed/manager.py
   trunk/beacon/src/server/channel/rss.py
      - copied, changed from r2809, /trunk/WIP/netsearch/src/feed/plugins/rss.py
   trunk/beacon/test/feeds.py
Removed:
   trunk/WIP/netsearch/src/feed/__init__.py
   trunk/WIP/netsearch/src/feed/channel.py
   trunk/WIP/netsearch/src/feed/lib/feedparser.py
   trunk/WIP/netsearch/src/feed/manager.py
   trunk/WIP/netsearch/src/feed/plugins/rss.py
Modified:
   trunk/beacon/src/__init__.py
   trunk/beacon/src/client.py
   trunk/beacon/src/server/db.py
   trunk/beacon/src/server/server.py

Modified: trunk/beacon/src/__init__.py
==============================================================================
--- trunk/beacon/src/__init__.py        (original)
+++ trunk/beacon/src/__init__.py        Sat Sep 15 17:18:32 2007
@@ -194,3 +194,39 @@
     while not _client.is_connected():
         kaa.notifier.step()
     return _client.delete_media(id)
+
+
+class Feed(dict):
+    def update(self):
+        return _client.feed_func('channels.update', self.get('id'))
+
+    def remove(self):
+        return _client.feed_func('channels.remove', self.get('id'))
+
[EMAIL PROTECTED]()
+def list_feeds():
+    """
+    List all feeds
+    """
+    if not _client:
+        connect()
+    
+    async = _client.feed_func('channels.list')
+    yield async
+    feeds = []
+    for f in async.get_result():
+        feeds.append(Feed(f))
+    yield feeds
+    
+
[EMAIL PROTECTED]()
+def add_feed(url, destdir, download=True, num=0, keep=True):
+    """
+    Add a feed
+    """
+    if not _client:
+        connect()
+    async = _client.feed_func('channels.add', url, destdir, download, num, 
keep)
+    yield async
+    yield Feed(async.get_result())
+    

Modified: trunk/beacon/src/client.py
==============================================================================
--- trunk/beacon/src/client.py  (original)
+++ trunk/beacon/src/client.py  Sat Sep 15 17:18:32 2007
@@ -188,6 +188,14 @@
             self.rpc('db.media.delete', id)
 
 
+    def feed_func(self, func, *args, **kwargs):
+        """
+        Call feed manager function in the server
+        """
+        if self.status != DISCONNECTED:
+            return self.rpc(func, *args, **kwargs)
+
+
     # -------------------------------------------------------------------------
     # Server connect / disconnect / reconnect handling
     # -------------------------------------------------------------------------

Copied: trunk/beacon/src/server/channel/__init__.py (from r2798, 
/trunk/WIP/netsearch/src/feed/__init__.py)
==============================================================================
--- /trunk/WIP/netsearch/src/feed/__init__.py   (original)
+++ trunk/beacon/src/server/channel/__init__.py Sat Sep 15 17:18:32 2007
@@ -19,11 +19,40 @@
 #
 # ##################################################################
 
+import kaa.rpc
 
 import manager
-import plugins
+import channel
+import rss
 
-add_channel = manager.add_channel
-list_channels = manager.list_channels
-remove_channel = manager.remove_channel
-update = manager.update
[EMAIL PROTECTED]('channels.update')
+def update(id=None):
+    if id == None:
+        return manager.update()
+    for c in manager.list_channels():
+        if id == c.id:
+            return c.update()
+    return False
+    
[EMAIL PROTECTED]('channels.list')
+def list_channels():
+    channels = []
+    for c in manager.list_channels():
+        channels.append(c.get_config())
+    return channels
+
[EMAIL PROTECTED]('channels.add')
+def add_channel(url, destdir, download=True, num=0, keep=True):
+    return manager.add_channel(url, destdir, download, num, keep).get_config()
+
[EMAIL PROTECTED]('channels.remove')
+def remove_channel(id):
+    for c in manager.list_channels():
+        if id == c.id:
+            manager.remove_channel(c)
+            return True
+    return False
+
+def set_database(database):
+    channel.Channel._db = database
+    manager.init()

Copied: trunk/beacon/src/server/channel/channel.py (from r2809, 
/trunk/WIP/netsearch/src/feed/channel.py)
==============================================================================
--- /trunk/WIP/netsearch/src/feed/channel.py    (original)
+++ trunk/beacon/src/server/channel/channel.py  Sat Sep 15 17:18:32 2007
@@ -10,7 +10,6 @@
 # kaa imports
 import kaa.notifier
 import kaa.notifier.url
-import kaa.beacon
 from kaa.strutils import str_to_unicode, unicode_to_str
 
 # get manager module
@@ -28,26 +27,29 @@
 if not os.path.isdir(IMAGEDIR):
     os.makedirs(IMAGEDIR)
 
-class Channel(object):
+class Entry(dict):
 
-    class Entry(dict):
+    def __getattr__(self, attr):
+        if attr == 'basename' and not 'basename' in self.keys():
+            basename = os.path.basename(self['url'])
+            if self.url.endswith('/'):
+                ext = os.path.splitext(self['url'])[1]
+                basename = self['title'].replace('/', '') + ext
+            self['basename'] = unicode_to_str(basename)
+        return self.get(attr)
+
+    def fetch(self, filename):
+        log.info('%s -> %s' % (self.url, filename))
+        tmpname = os.path.join(os.path.dirname(filename),
+                               '.' + os.path.basename(filename))
+        return kaa.notifier.url.fetch(self.url, filename, tmpname)
 
-        def __getattr__(self, attr):
-            if attr == 'basename' and not 'basename' in self.keys():
-                basename = os.path.basename(self['url'])
-                if self.url.endswith('/'):
-                    ext = os.path.splitext(self['url'])[1]
-                    basename = self['title'].replace('/', '') + ext
-                self['basename'] = unicode_to_str(basename)
-            return self.get(attr)
-
-        def fetch(self, filename):
-            log.info('%s -> %s' % (self.url, filename))
-            tmpname = os.path.join(os.path.dirname(filename),
-                                   '.' + os.path.basename(filename))
-            return kaa.notifier.url.fetch(self.url, filename, tmpname)
 
+class Channel(object):
 
+    _db = None
+    NEXT_ID = 0
+    
     def __init__(self, url, destdir):
         self.url = url
         self.dirname = destdir
@@ -58,7 +60,9 @@
         self._keep = True
         if not os.path.isdir(destdir):
             os.makedirs(destdir)
-
+        self.id = Channel.NEXT_ID
+        Channel.NEXT_ID += 1
+        
 
     def configure(self, download=True, num=0, keep=True):
         """
@@ -73,6 +77,17 @@
         manager.save()
 
 
+    def get_config(self):
+        """
+        Get channel configuration.
+        """
+        return dict(id = self.id,
+                    url = self.url,
+                    directory = self.dirname,
+                    download = self._download,
+                    num = self._num,
+                    keep = self._keep)
+    
     def _readxml(self, node):
         """
         Read XML node with channel configuration and cache.
@@ -106,7 +121,7 @@
         d.appendChild(doc.createTextNode(self.dirname))
         node.appendChild(d)
         for url, fname in self._entries:
-            e = node.createElement('entry')
+            e = doc.createElement('entry')
             e.setAttribute('url', url)
             if fname:
                 e.setAttribute('filename', str_to_unicode(fname))
@@ -137,9 +152,15 @@
             sys.stdout.write("%s\r" % s.get_progressbar())
             sys.stdout.flush()
 
+        log.info('update channel %s', self.url)
+
         # get directory information
-        beacondir = kaa.beacon.get(self.dirname)
-        allurls = [ f.url for f in beacondir.list() ]
+        beacondir = self._db.query(filename=self.dirname)
+        listing = beacondir.list()
+        if isinstance(listing, kaa.notifier.InProgress):
+            yield listing
+            listing = listing.get_result()
+        allurls = [ f.url for f in listing ]
 
         num = self._num
         allfiles = [ e[1] for e in self._entries ]
@@ -152,6 +173,7 @@
                 yield entry
                 continue
 
+            log.info('process %s', entry.url)
             filename = None
 
             if not self._download and entry.url in allurls:
@@ -159,7 +181,11 @@
                 pass
             elif not self._download:
                 # add to beacon
-                i = kaa.beacon.add_item(parent=beacondir, **entry)
+                while self._db.read_lock.is_locked():
+                    yield self._db.read_lock.yield_unlock()
+                # use url as name
+                entry['name'] = unicode_to_str(entry.url)
+                i = self._db.add_object(parent=beacondir, **entry)
             else:
                 # download
                 filename = os.path.join(self.dirname, entry.basename)
@@ -181,21 +207,21 @@
                         continue
                     
                 if os.path.isfile(filename):
-                    item = kaa.beacon.get(filename)
+                    item = self._db.query(filename=filename)
                     if not item.scanned():
-                        # BEACON_FIXME
-                        item._beacon_request()
-                        while not item.scanned():
-                            yield kaa.notifier.YieldContinue
+                        async = item.scan()
+                        if isinstance(async, kaa.notifier.InProgress):
+                            yield async
                     for key, value in entry.items():
                         if not key in ('type', 'url', 'basename'):
                             item[key] = value
-                        
+
             self._entries.append((entry['url'], filename))
             num -= 1
             if num == 0:
                 break
 
+        log.info('*** finished with %s ***', self.url)
         manager.save()
 
         # delete old files or remove old entries from beacon
@@ -210,3 +236,26 @@
             elif os.path.isfile(filename):
                 # delete file on disc
                 os.unlink(filename)
+
+
+    @kaa.notifier.yield_execution()
+    def remove(self):
+        """
+        Remove entries from this channel.
+        """
+        log.info('remove %s', self.url)
+        if self._keep or self._download:
+            # only delete links in the filesystem
+            return
+            
+        # get directory information
+        beacondir = self._db.query(filename=self.dirname)
+        allurls = [ e[0] for e in self._entries ]
+        listing = beacondir.list()
+        if isinstance(listing, kaa.notifier.InProgress):
+            yield listing
+            listing = listing.get_result()
+        for entry in listing:
+            if entry.url in allurls:
+                log.info('delete %s', entry.url)
+                entry.delete()

Copied: trunk/beacon/src/server/channel/manager.py (from r2809, 
/trunk/WIP/netsearch/src/feed/manager.py)
==============================================================================
--- /trunk/WIP/netsearch/src/feed/manager.py    (original)
+++ trunk/beacon/src/server/channel/manager.py  Sat Sep 15 17:18:32 2007
@@ -5,13 +5,14 @@
 import kaa.notifier
 from kaa.strutils import unicode_to_str
 
+# fallback RSS parser
+import rss
+
 # get logging object
 log = logging.getLogger('beacon.channel')
 
 CACHE = os.path.expanduser("~/.beacon/channels.xml")
 
-_initialized = False
-
 # list of all channel objects
 _channels = []
 
@@ -32,29 +33,26 @@
     for regexp, generator in _generators:
         if regexp.match(url):
             return generator(url, destdir)
-    raise RuntimeError
+    return rss.Channel(url, destdir)
 
 
 def add_channel(url, destdir, download=True, num=0, keep=True):
     """
     Add a new channel.
     """
-    if not _initialized:
-        _init()
     for c in _channels:
         if c.dirname == destdir and c.url == url:
             raise RuntimeError('channel already exists')
     channel = _get_channel(url, destdir)
     _channels.append(channel)
     channel.configure(download, num, keep)
+    return channel
 
 
 def list_channels():
     """
     Return a list of all channels.
     """
-    if not _initialized:
-        _init()
     return _channels
 
 
@@ -63,6 +61,7 @@
     Remove a channel.
     """
     _channels.remove(channel)
+    channel.remove()
     save()
     
 
@@ -70,8 +69,6 @@
     """
     Save all channel information
     """
-    if not _initialized:
-        _init()
     doc = minidom.getDOMImplementation().createDocument(None, "channels", None)
     top = doc.documentElement
     for c in _channels:
@@ -83,7 +80,7 @@
     f.close()
 
     
-def _init():
+def init():
     """
     Load cached channels from disc.
     """
@@ -99,8 +96,6 @@
             _channels.append(channel)
             return
         
-    global _initialized
-    _initialized = True
     if not os.path.isfile(CACHE):
         return
 
@@ -130,11 +125,14 @@
     """
     global _updating
     if _updating:
+        log.error('update already in progress')
         yield False
-    if not _initialized:
-        _init()
+    log.info('start channel update')
     _updating = True
     for channel in _channels:
-        yield channel.update(verbose=verbose)
+        x = channel.update(verbose=verbose)
+        yield x
+        log.info('XXXXXXXXX')
+        x()
     _updating = False
     yield True

Copied: trunk/beacon/src/server/channel/rss.py (from r2809, 
/trunk/WIP/netsearch/src/feed/plugins/rss.py)
==============================================================================
--- /trunk/WIP/netsearch/src/feed/plugins/rss.py        (original)
+++ trunk/beacon/src/server/channel/rss.py      Sat Sep 15 17:18:32 2007
@@ -1,18 +1,22 @@
 import re
 import time
 import logging
+import urllib2
 
 import kaa.notifier
 
-from kaa.netsearch.feed.lib import feedparser
-from kaa.netsearch.feed.channel import Channel
-from kaa.netsearch.feed.manager import register
+import feedparser as _feedparser
+import channel
 
 # get logging object
 log = logging.getLogger('beacon.feed')
 isotime = '%a, %d %b %Y %H:%M:%S'
 
-class RSS(Channel):
[EMAIL PROTECTED]()
+def feedparser(url):
+    return _feedparser.parse(urllib2.urlopen(url))
+
+class Channel(channel.Channel):
 
     def __iter__(self):
         # get feed in a thread
@@ -85,7 +89,5 @@
             # getting a good basename for urls ending with /
             # based on type.
             # create entry
-            entry = Channel.Entry(**metadata)
+            entry = channel.Entry(**metadata)
             yield entry
-
-register(re.compile('^(http|https|file)://.*'), RSS)

Modified: trunk/beacon/src/server/db.py
==============================================================================
--- trunk/beacon/src/server/db.py       (original)
+++ trunk/beacon/src/server/db.py       Sat Sep 15 17:18:32 2007
@@ -56,29 +56,34 @@
     Read lock for the database.
     """
     def __init__(self):
-        self._clients = 0
+        self._clients = []
         self.signals = kaa.notifier.Signals('lock', 'unlock')
 
-    def lock(self):
+
+    def lock(self, client):
         """
         Lock the database for reading.
         """
         log.debug('lock++')
-        self._clients += 1
-        if self._clients == 1:
+        self._clients.append(client)
+        if len(self._clients) == 1:
             self.signals['lock'].emit()
             log.debug('locked')
 
 
-    def unlock(self):
+    def unlock(self, client, all=True):
         """
         Unlock the database. If more than one lock was made
         this will only decrease the lock variable but not
         unlock the database.
         """
         log.debug('lock--')
-        self._clients -= 1
-        if self._clients == 0:
+        if client in self._clients:
+            self._clients.remove(client)
+            if all:
+                # remove all locks from client
+                return self.unlock(client, all)
+        if len(self._clients) == 0:
             self.signals['unlock'].emit()
             log.debug('unlocked')
 

Modified: trunk/beacon/src/server/server.py
==============================================================================
--- trunk/beacon/src/server/server.py   (original)
+++ trunk/beacon/src/server/server.py   Sat Sep 15 17:18:32 2007
@@ -50,6 +50,7 @@
 from monitor import Monitor
 from crawl import Crawler
 from config import config
+import channel
 
 # get logging object
 log = logging.getLogger('beacon.server')
@@ -162,7 +163,9 @@
         for dir in config.monitors:
             self.monitor_dir(os.path.expandvars(os.path.expanduser(dir)))
 
-
+        channel.set_database(self._db)
+        self.ipc.connect(channel)
+        
 
     # -------------------------------------------------------------
     # client handling
@@ -191,6 +194,8 @@
                 for m in client_info[2]:
                     m.stop()
                 self._clients.remove(client_info)
+        self._db.read_lock.unlock(client_info[1], True)
+
 
     def has_clients(self):
         """
@@ -258,21 +263,20 @@
         self._db.delete_media(id)
 
 
-    @kaa.rpc.expose('db.lock')
-    def db_lock(self):
+    @kaa.rpc.expose('db.lock', add_client=True)
+    def db_lock(self, client_id):
         """
         Lock the database so clients can read
-        FIXME: if a client locks and dies before unlock the server is dead
         """
-        self._db.read_lock.lock()
+        self._db.read_lock.lock(client_id)
 
 
-    @kaa.rpc.expose('db.unlock')
-    def db_unlock(self):
+    @kaa.rpc.expose('db.unlock', add_client=True)
+    def db_unlock(self, client_id):
         """
         Unlock the database again
         """
-        self._db.read_lock.unlock()
+        self._db.read_lock.unlock(client_id)
 
 
     @kaa.rpc.expose('monitor.directory')

Added: trunk/beacon/test/feeds.py
==============================================================================
--- (empty file)
+++ trunk/beacon/test/feeds.py  Sat Sep 15 17:18:32 2007
@@ -0,0 +1,59 @@
+import sys
+import kaa.notifier
+import kaa.beacon
+
+kaa.beacon.connect()
+
+if len(sys.argv) > 1:
+    if sys.argv[1] in ('--update', '-u'):
+        if len(sys.argv) > 2:
+            def update(feeds, id):
+                for f in feeds:
+                    if f.get('id') == id:
+                        f.update().connect(sys.exit)
+            kaa.beacon.list_feeds().connect(update, int(sys.argv[2]))
+        else:
+            kaa.beacon.update_feeds().connect(sys.exit)
+
+    elif sys.argv[1] in ('--list', '-l'):
+        def show(feeds):
+            for f in feeds:
+                print f
+            sys.exit(0)
+        kaa.beacon.list_feeds().connect(show)
+
+    elif sys.argv[1] in ('--add', '-a') and len(sys.argv) > 3:
+        url = sys.argv[2]
+        destdir = sys.argv[3]
+        if len(sys.argv) > 4:
+            if sys.argv[4].lower() in ('true', 'yes'):
+                download = True
+            elif sys.argv[4].lower() in ('false', 'no'):
+                download = False
+            num = int(sys.argv[5])
+            if sys.argv[6].lower() in ('true', 'yes'):
+                keep = True
+            elif sys.argv[6].lower() in ('false', 'no'):
+                keep = False
+            kaa.beacon.add_feed(url, destdir, download, num, 
keep).connect(sys.exit)
+        else:
+            kaa.beacon.add_feed(url, destdir).connect(sys.exit)
+
+    elif sys.argv[1] in ('--remove', '-r') and len(sys.argv) > 2:
+        def remove(feeds, id):
+            for f in feeds:
+                if f.get('id') == id:
+                    f.remove().connect(sys.exit)
+        kaa.beacon.list_feeds().connect(remove, int(sys.argv[2]))
+        
+    else:
+        print 'help'
+        sys.exit(0)
+        
+    kaa.notifier.loop()
+    sys.exit(0)
+
+print 'help'
+# Add example
+# python test/feeds.py -a http://www.radiobremen.de/podcast/bestof/ \
+#      /local/podcast False 2 False

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog

Reply via email to