Byron Clark avatar Byron Clark committed caadbfe

Rearrange to prepare for distutils.

Comments (0)

Files changed (28)

+#!/usr/bin/python
+
+if __name__ == '__main__':
+    import datetime
+    import logging
+    import os
+    import string
+    import sys
+
+    # hook the logging up
+    console = logging.StreamHandler(sys.stderr)
+    formatter = logging.Formatter('%(name)s %(levelname)s %(message)s')
+    console.setFormatter(formatter)
+    logging.getLogger('').addHandler(console)
+    logging.getLogger('').setLevel(logging.INFO)
+
+    import comicbox.configuration
+    from comicbox.download import fetch_strips
+
+    logger = logging.getLogger('comicbox')
+    today = datetime.datetime.now()
+    # parse the configuration
+    try:
+        exepath = os.path.dirname(os.path.abspath(sys.argv[0]))
+        conf = comicbox.configuration.load_configuration(exepath)
+    except comicbox.configuration.ConfigurationError, err:
+        logger.fatal('unable to load configuration: %s' % (err,))
+        sys.exit(1)
+
+    backend_name, backend_args, strips = conf
+
+    # backend_name should look like 'file_archive', need to find the class
+    S = string.capwords(backend_name.replace('_', ' ')).replace(' ', '')
+    backend_class = S
+    backend_name = 'comicbox.backends.%s' % (backend_name,)
+
+    # attempt to load the backend class
+    try:
+        module = __import__(backend_name, globals(),
+                            locals(), [backend_class])
+        backend = getattr(module, backend_class)
+    except ImportError, err:
+        logger.fatal('unable to load backend: %s' % (err,))
+
+    b = backend(today, backend_args)
+    fetch_strips(strips, b, today)
+    b.finish()

comicbox.py

-#!/usr/bin/python
-
-if __name__ == '__main__':
-    import datetime
-    import logging
-    import os
-    import string
-    import sys
-
-    # hook the logging up
-    console = logging.StreamHandler(sys.stderr)
-    formatter = logging.Formatter('%(name)s %(levelname)s %(message)s')
-    console.setFormatter(formatter)
-    logging.getLogger('').addHandler(console)
-    logging.getLogger('').setLevel(logging.INFO)
-
-    # munge the include path
-    exepath = os.path.dirname(os.path.abspath(sys.argv[0]))
-    lib_path = os.path.join(exepath, 'lib')
-    sys.path.append(lib_path)
-
-    import configuration
-    from download import fetch_strips
-
-    logger = logging.getLogger('comicbox')
-    today = datetime.datetime.now()
-    # parse the configuration
-    try:
-        conf = configuration.load_configuration(exepath)
-    except configuration.ConfigurationError, err:
-        logger.fatal('unable to load configuration: %s' % (err,))
-        sys.exit(1)
-
-    backend_name, backend_args, strips = conf
-    
-    # backend_name should look like 'file_archive', need to find the class
-    S = string.capwords(backend_name.replace('_', ' ')).replace(' ', '')
-    backend_class = S
-    backend_name = 'backends.%s' % (backend_name,)
-
-    # attempt to load the backend class
-    try:
-        module = __import__(backend_name, globals(), 
-                            locals(), [backend_class])
-        backend = getattr(module, backend_class)
-    except ImportError, err:
-        logger.fatal('unable to load backend: %s' % (err,))
-
-    b = backend(today, backend_args)
-    fetch_strips(strips, b, today)
-    b.finish()

Empty file added.

Add a comment to this file

comicbox/backends/__init__.py

Empty file added.

comicbox/backends/file_archive.py

+import datetime
+import logging
+import os
+
+class FileArchive(object):
+    DEFAULT_DIRECTORY = 'storage'
+
+    def __init__(self, date, args):
+        """Initialize a file based storage archive.
+
+        The following parameters in args will be used:
+            - basedir: path where the archive will be stored.
+                       can be relative."""
+
+        if 'basedir' in args:
+            self.basedir = os.path.abspath(args['basedir'])
+        else:
+            self.basedir = os.path.abspath(self.DEFAULT_DIRECTORY)
+
+        self.date_string = date.strftime('%Y%m%d')
+        self.display_date_string = date.strftime('%A, %B %d, %Y')
+        self.archive = os.path.join(self.basedir, 
+                                    'index-%s.html' % (self.date_string))
+
+        self.logger = logging.getLogger('comicbox.backend.FileArchive')
+
+        if not os.path.exists(self.basedir):
+            self.logger.info('creating base directory: %s' % (self.basedir))
+            os.makedirs(self.basedir)
+
+        if os.path.exists(self.archive):
+            self.logger.info('overwriting exisiting archive file')
+
+        self.outfile = open(self.archive, 'wt')
+
+        self._write_header()
+
+    def add_strip(self, strip, strip_home, image, image_url, filename):
+        self.outfile.write('<a href="%s"><h3>%s</h3></a>\n' %
+                           (strip_home, strip))
+        if image and len(image) > 0:
+            if filename:
+                image_filename = filename
+            else:
+                image_filename = image_url[image_url.rfind('/') + 1:]
+            image_dir = os.path.join(self.basedir, strip)
+            image_path = os.path.join(strip, image_filename)
+            image_fullpath = os.path.join(self.basedir, image_path)
+
+            if not os.path.exists(image_dir):
+                self.logger.info('creating strip directory: %s' % 
+                                 (image_dir))
+                os.makedirs(image_dir)
+            image_file = open(image_fullpath, 'wb')
+            image_file.write(image)
+            image_file.close()
+
+            self.outfile.write('<img src="%s" />' % (image_path))
+        else:
+            self.outfile.write('<em>image not available</em>')
+        self.outfile.write('<br />')
+
+    def finish(self):
+        self._write_footer()
+        self.outfile.close()
+        self._update_index()
+
+    def _write_header(self):
+        self.outfile.write('<html><head>\n')
+        self.outfile.write('<title>comicbox - %s</title>\n' % 
+                           (self.display_date_string))
+        self.outfile.write('</head>\n')
+        self.outfile.write('<body>\n')
+        self.outfile.write('<h1>comicbox - %s</h1>\n' % 
+                           (self.display_date_string))
+
+    def _write_footer(self):
+        self.outfile.write('</body>\n</html>\n')
+
+    def _update_index(self):
+        outfile = open(os.path.join(self.basedir, 'archive.html'), 'wt')
+        outfile.write('<html><head>\n')
+        outfile.write('<title>comicbox - archive</title>\n')
+        outfile.write('</head>\n')
+        outfile.write('<body>\n')
+        outfile.write('<h1>comicbox - archive</h1>\n')
+
+        days = [d for d in os.listdir(self.basedir) if d.startswith('index-')]
+        days.sort()
+        days.reverse()
+        for d in days:
+            try:
+                date_str = d.split('-')[1].split('.')[0]
+                # datetime.datetime.strptime is only in python >= 2.5
+                year = int(date_str[0:4])
+                month = int(date_str[4:6])
+                day = int(date_str[6:8])
+                date = datetime.datetime(year, month, day)
+                date_out = date.strftime('%A, %B %d, %Y')
+                outfile.write('<a href="%s">%s</a><br />\n' % (d, date_out))
+            except ValueError:
+                self.logger.error('skipping invalid day file: %s' % d)
+
+        outfile.write('</body>\n</html>\n')
+        outfile.close()
+

comicbox/comics_com.py

+import datetime
+import logging
+import re
+import urllib2
+
+from request_builder import build_request
+
+class ComicsComStrip(object):
+    _BASE_URL = 'http://www.comics.com'
+    _HOME_PAGE = '/%s/'
+    _SEARCH_PAGE = '/%s/%s/'
+    _SEARCH_PATTERN = '<img .*src="(\S*/dyn/str_strip/\S*\.gif)".*>'
+
+    def __init__(self, strip, identifier, image_host=None):
+        self.logger = logging.getLogger('comicbox.comicscom.%s' % (strip))
+        self.strip = strip
+        self.identifier = identifier
+        self.image_host = image_host
+
+    def name(self):
+        return self.strip
+
+    def home(self):
+        home_page = self._HOME_PAGE % (self.identifier)
+        return self._BASE_URL + home_page
+
+    def strip_url(self, date):
+        search_page = self._SEARCH_PAGE % (self.identifier,
+                                           date.strftime('%Y-%m-%d'))
+        search_url = self._BASE_URL + search_page
+        search_pattern = self._SEARCH_PATTERN
+        search_regex = re.compile(search_pattern)
+
+        try:
+            self.logger.debug('requesting search page "%s"' % (search_url))
+            search_req = build_request(search_url)
+            page = urllib2.urlopen(search_req).read()
+            matches = search_regex.search(page)
+            if matches:
+                if self.image_host and matches.group(1).startswith('/'):
+                    return 'http://%s%s' % (self.image_host, matches.group(1))
+                else:
+                    return matches.group(1)
+            else:
+                self.logger.error('strip url not found')
+                return ''
+        except urllib2.URLError, e:
+            self.logger.error('failed to download search page: %s' % (e))
+            return ''

comicbox/configuration.py

+import ConfigParser
+import os
+import string
+
+import globals
+from strip_definitions import Strips
+
+class ConfigurationError(Exception):
+    pass
+
+
+def load_configuration(exepath):
+    """load comicbox configuration from a file.
+
+    Loads the comicbox configuration from comicboxrc in same dir as
+    sys.argv[0] and ~/.comicboxrc.  Settings in ~/.comicboxrc override settings
+    in sys.argv[0]/comicboxrc.
+
+    There must be at least a 'comicbox' section in the configuration file.
+    This section holds the backend and chosen strips.  If the backend takes
+    additional parameters they will be in a section with the same name as the
+    backend.  See doc/comicboxrc.example for an example."""
+
+    config_files = [os.path.join(exepath, 'comicboxrc'),
+                    os.path.expanduser('~/.comicboxrc')]
+
+    parser = ConfigParser.SafeConfigParser()
+    if len(parser.read(config_files)) == 0:
+        raise ConfigurationError('No configuration files found')
+
+    if 'comicbox' not in parser.sections():
+        raise ConfigurationError('Missing [comicbox] section')
+
+    backend = None
+    strips = None
+    for item in parser.items('comicbox'):
+        if item[0] == 'backend':
+            backend = item[1]
+        elif item[0] == 'strips':
+            # remove all whitespace
+            value = item[1]
+            for c in string.whitespace:
+                value = value.replace(c, '')
+            strips = value.split(',')
+
+    # set the default backend
+    if backend == None:
+        backend = globals.DEFAULT_BACKEND
+
+    # build the backend arguments
+    backend_args = {}
+    if backend in parser.sections():
+        args = parser.items(backend)
+        for arg in args:
+            backend_args[arg[0]] = arg[1]
+
+    if strips == None or len(strips) == 0:
+        raise ConfigurationError('No strips to download')
+
+    resolved_strips = []
+    for strip in strips:
+        try:
+            resolved_strips.append(Strips[strip])
+        except KeyError:
+            raise ConfigurationError('Invalid strip: %s' % (strip))
+
+    return (backend, backend_args, resolved_strips)

comicbox/download.py

+import logging
+import time
+import urllib2
+
+from request_builder import build_request
+
+def fetch_strips(strips, backend, date):
+    logger = logging.getLogger('comicbox.fetch_strips')
+    for s in strips:
+        time.sleep(0.01)
+        url = s.strip_url(date)
+        if len(url) > 0:
+            logger.debug('fetching "%s"' % (url,))
+            if 'filename' in dir(s):
+                filename = s.filename(date)
+            else:
+                filename = None
+            try:
+                if 'strip_referer' in dir(s):
+                    req = build_request(url, s.strip_referer())
+                else:
+                    req = build_request(url)
+                image = urllib2.urlopen(req).read()
+                backend.add_strip(s.name(), s.home(), image, url, filename)
+            except urllib2.URLError, e:
+                logger.error('failed to download %s: %s' % (s.name(), e))
+                backend.add_strip(s.name(), s.home(), None, None, None)
+        else:
+            backend.add_strip(s.name(), s.home(), None, None, None)

comicbox/generic_search.py

+import datetime
+import logging
+import re
+import urllib2
+
+from request_builder import build_request
+
+class GenericSearchStrip(object):
+    def __init__(self, strip, home_page, search_page, search_pattern):
+        self.strip = strip
+        self.home_page = home_page
+        self.search_page = search_page
+        self.search_pattern = search_pattern
+
+        l = logging.getLogger('comicbox.generic_search.%s' % (strip))
+        self.logger = l
+
+    def name(self):
+        return self.strip
+
+    def home(self):
+        return self.home_page
+
+    def strip_url(self, date):
+        # deal with any date escapes in search_{page,pattern}
+        search_url = date.strftime(self.search_page)
+        search_pattern = date.strftime(self.search_pattern)
+        search_regex = re.compile(search_pattern, re.IGNORECASE)
+
+        try:
+            self.logger.debug('requesting search page "%s"' % (search_url))
+            search_req = build_request(search_url)
+            page = urllib2.urlopen(search_req).read()
+            matches = search_regex.search(page)
+            if matches:
+                return matches.group(1)
+            else:
+                self.logger.error('strip url not found')
+                return ''
+        except urllib2.URLError, e:
+            self.logger.error('failed to download search page: %s' % (e))
+            return ''

comicbox/globals.py

+NAME = 'comicbox'
+VERSION = '0.1'
+
+DEFAULT_BACKEND = 'file_archive'

comicbox/king_sfgate.py

+import datetime
+import logging
+
+class KingFeaturesSFGate(object):
+    _HOME_PAGE = 'http://www.sfgate.com/cgi-bin/article.cgi?file=/comics/%s.dtl'
+    _STRIP_URL = 'http://pst.rbma.com/content/%s'
+
+    def __init__(self, strip, identifier):
+        self.logger = logging.getLogger('comicbox.king_sfgate.%s' % (strip))
+        self.strip = strip
+        self.identifier = identifier
+
+    def name(self):
+        return self.strip
+
+    def home(self):
+        return self._HOME_PAGE % (self.identifier)
+
+    def strip_url(self, date):
+        # these strips are only available for today
+        today = datetime.datetime.now()
+        current = datetime.datetime(today.year, today.month, today.day)
+        passed = datetime.datetime(date.year, date.month, date.day)
+        if current != passed:
+            self.logger.error('only available for current date')
+            return ''
+
+        return self._STRIP_URL % (self.identifier)
+
+    def strip_referer(self):
+        return self._HOME_PAGE % (self.identifier)
+
+    def filename(self, date):
+        return '%s-%s.gif' % (self.identifier, date.strftime('%Y%m%d'))

comicbox/request_builder.py

+import urllib2
+
+import globals
+
+def build_request(url, referer=None):
+    """Builds a urllib2.Request object.
+
+    Builds a urllib2.Request object for the given url and referer.  While
+    simple enough to build the request object without this function, this
+    allows for common headers, such as a user agent, to be added to all
+    requests."""
+
+    req = urllib2.Request(url)
+    if referer:
+        req.add_header('Referer', referer)
+
+    req.add_header('User-agent', '%s/%s' % (globals.NAME, globals.VERSION))
+    return req

comicbox/strip_definitions.py

+from generic_search import GenericSearchStrip
+from comics_com import ComicsComStrip
+from king_sfgate import KingFeaturesSFGate
+from ucomics import UcomicsStrip
+from united_media import UnitedMediaStrip
+
+# TODO: handle the following kinds of strips
+# ucomics: old comic reruns
+#   - Bloom County
+#   - Minimum Security
+#   - Waylay
+
+
+Strips = { # ucomics 
+           '9to5' :
+               UcomicsStrip('9 to 5', '9to5', 'tmntf'),
+           'adamathome' :
+               UcomicsStrip('Adam@Home', 'adamathome', 'ad'),
+           'agnes' :
+               UcomicsStrip('Agnes', 'agnes', 'cragn'),
+           'andycapp' :
+               UcomicsStrip('Andy Capp', 'andycapp', 'crcap'),
+           'animalcrackers' :
+               UcomicsStrip('Animal Crackers', 'animalcrackers', 'tmani'),
+           'annie' :
+               UcomicsStrip('Annie', 'annie', 'tmann'),
+           'theargylesweater' :
+               UcomicsStrip('The Argyle Sweater', 'theargylesweater', 'tas'),
+           'askshagg' :
+               UcomicsStrip('Ask Shagg', 'askshagg', 'crask'),
+           'bc' :
+               UcomicsStrip('B.C.', 'bc', 'crbc'),
+           'badreporter' :
+               UcomicsStrip('Bad Reporter', 'badreporter', 'bad'),
+           'baldo' :
+               UcomicsStrip('Baldo', 'baldo', 'ba'),
+           'ballardstreet' :
+               UcomicsStrip('Ballard Street', 'ballardstreet', 'crbal'),
+           'bigtop' :
+               UcomicsStrip('Big Top', 'bigtop', 'bt'),
+           'biographic' :
+               UcomicsStrip('Biographic', 'biographic', 'biov'),
+           'bonanas' :
+               UcomicsStrip('Bo Nanas', 'bonanas', 'bon'),
+           'bobthesquirrel' :
+               UcomicsStrip('Bob the Squirrel', 'bobthesquirrel', 'bob'),
+           'boondocks' :
+               UcomicsStrip('The Boondocks', 'boondocks', 'bo'),
+           'bottomliners' :
+               UcomicsStrip('Bottomliners', 'bottomliners', 'tmbot'),
+           'boundandgagged' :
+               UcomicsStrip('Bound and Gagged', 'boundandgagged', 'tmbou'),
+           'brainwaves' :
+               UcomicsStrip('Brainwaves', 'brainwaves', 'bwv'),
+           'brendastarr' :
+               UcomicsStrip('Brenda Starr', 'brendastarr', 'tmbre'),
+           'brewsterrockit' :
+               UcomicsStrip('Brewster Rockit', 'brewsterrockit', 'tmrkt'),
+           'broomhilda' :
+               UcomicsStrip('Broom Hilda', 'broomhilda', 'tmbro'),
+           'calvinandhobbes' :
+               UcomicsStrip('Calvin and Hobbes', 'calvinandhobbes', 'ch'),
+           'candorville' :
+               UcomicsStrip('Candorville', 'candorville', 'cand'),
+           'cathy' :
+               UcomicsStrip('Cathy', 'cathy', 'ca'),
+           'cestlavie' :
+               UcomicsStrip('C\'est la Vie', 'cestlavie', 'clv'),
+           'citizendog' :
+               UcomicsStrip('Citizen Dog', 'citizendog', 'cd'),
+           'thecity' :
+               UcomicsStrip('The City', 'thecity', 'derf'),
+           'clearbluewater' :
+               UcomicsStrip('Clear Blue Water', 'clearbluewater', 'cbw'),
+           'cleats' :
+               UcomicsStrip('Cleats', 'cleats', 'cle'),
+           'closetohome' :
+               UcomicsStrip('Close to Home', 'closetohome', 'cl'),
+           'compu-toon' :
+               UcomicsStrip('Compu-toon', 'compu-toon', 'tmcom'),
+           'cornered' :
+               UcomicsStrip('Cornered', 'cornered', 'co'),
+           'culdesac' :
+               UcomicsStrip('Cul de Sac', 'culdesac', 'cds'),
+           'deepcover' :
+               UcomicsStrip('Deep Cover', 'deepcover', 'deep'),
+           'dicktracy' :
+               UcomicsStrip('Dick Tracy', 'dicktracy', 'tmdic'),
+           'dinetteset' :
+               UcomicsStrip('The Dinette Set', 'dinetteset', 'crdin'),
+           'dogeatdoug' :
+               UcomicsStrip('Dog Eat Doug', 'dogeatdoug', 'crdog'),
+           'domesticabuse' :
+               UcomicsStrip('Domestic Abuse', 'domesticabuse', 'dom'),
+           'doodles' :
+               UcomicsStrip('Doodles', 'doodles', 'tmdoo'),
+           'doonesbury' :
+               UcomicsStrip('Doonesbury', 'doonesbury', 'db'),
+           'duplex' :
+               UcomicsStrip('The Duplex', 'duplex', 'dp'),
+           'theelderberries' :
+               UcomicsStrip('The Elderberries', 'theelderberries', 'eld'),
+           'thefifthwave' :
+               UcomicsStrip('Fifth Wave', 'thefifthwave', 'fw'),
+           'flightdeck' :
+               UcomicsStrip('Flight Deck', 'flightdeck', 'crfd'),
+           'floandfriends' :
+               UcomicsStrip('Flo and Friends', 'floandfriends', 'crflo'),
+           'theflyingmccoys' :
+               UcomicsStrip('The Flying McCoys', 'theflyingmccoys', 'fmc'),
+           'forbetterorforworse' :
+               UcomicsStrip('For Better or For Worse', 'forbetterorforworse', 'fb'),
+           'forheavenssake' :
+               UcomicsStrip('For Heaven\'s Sake', 'forheavenssake', 'crfhs'),
+           '44unionavenue' :
+               UcomicsStrip('44 Union Avenue', '44unionavenue', 'fua'),
+           'foxtrot' :
+               UcomicsStrip('FoxTrot', 'foxtrot', 'ft'),
+           'foxtrotclassics' :
+               UcomicsStrip('FoxTrot Classics', 'foxtrotclassics', 'ftcl'),
+           'fredbasset' :
+               UcomicsStrip('Fred Basset', 'fredbasset', 'tmfba'),
+           'frogapplause' :
+               UcomicsStrip('Frog Applause', 'frogapplause', 'frog'),
+           'thefuscobrothers' :
+               UcomicsStrip('The Fusco Brothers', 'thefuscobrothers', 'fu'),
+           'garfield' :
+               UcomicsStrip('Garfield', 'garfield', 'ga'),
+           'gasolinealley' :
+               UcomicsStrip('Gasoline Alley', 'gasolinealley', 'tmgas'),
+           'gilthorp' :
+               UcomicsStrip('Gil Thorp', 'gilthorp', 'tmgil'),
+           'gingermeggs' :
+               UcomicsStrip('Ginger Meggs', 'gingermeggs', 'gin'),
+           'girlsandsports' :
+               UcomicsStrip('Girls & Sports', 'girlsandsports', 'crgis'),
+           'heartofthecity' :
+               UcomicsStrip('Heart of the City', 'heartofthecity', 'hc'),
+           'heathcliff' :
+               UcomicsStrip('Heathcliff', 'heathcliff', 'crhea'),
+           'herbandjamaal' :
+               UcomicsStrip('Herb and Jamaal', 'herbandjamaal', 'crher'),
+           'housebroken' :
+               UcomicsStrip('Housebroken', 'housebroken', 'tmhou'),
+           'hubertandabby' :
+               UcomicsStrip('Hubert and Abby', 'hubertandabby', 'haa'),
+           'idiotbox' :
+               UcomicsStrip('Idiot Box', 'idiotbox', 'ibox'),
+           'inthebleachers' :
+               UcomicsStrip('In the Bleachers', 'inthebleachers', 'bl'),
+           'inkpen' :
+               UcomicsStrip('Ink Pen', 'inkpen', 'ink'),
+           'thekchronicles' :
+               UcomicsStrip('The K Chronicles', 'thekchronicles', 'kk'),
+           'kudzu' :
+               UcomicsStrip('Kudzu', 'kudzu', 'tmkud'),
+           'lacucaracha' :
+               UcomicsStrip('La Cucaracha', 'lacucaracha', 'lc'),
+           'libertymeadows' :
+               UcomicsStrip('Liberty Meadows', 'libertymeadows', 'crlib'),
+           'lio' :
+               UcomicsStrip('Lio', 'lio', 'lio'),
+           'looseparts' :
+               UcomicsStrip('Loose Parts', 'looseparts', 'tmloo'),
+           'luckycow' :
+               UcomicsStrip('Lucky Cow', 'luckycow', 'luc'),
+           'meaningoflila' :
+               UcomicsStrip('The Meaning of Lila', 'meaningoflila', 'crlil'),
+           'themiddletons' :
+               UcomicsStrip('The Middletons', 'themiddletons', 'tmmid'),
+           'momma' :
+               UcomicsStrip('Momma', 'momma', 'crmom'),
+           'muttandjeff' :
+               UcomicsStrip('Mutt & Jeff', 'muttandjeff', 'mutt'),
+           'naturalselection' :
+               UcomicsStrip('Natural Selection', 'naturalselection', 'crns'),
+           'nestheads' :
+               UcomicsStrip('Nest Heads', 'nestheads', 'cpnst'),
+           'neurotica' :
+               UcomicsStrip('NEUROTICA', 'neurotica', 'neu'),
+           'nonsequitur' :
+               UcomicsStrip('Non Sequitur', 'nonsequitur', 'nq'),
+           'thenorm' :
+               UcomicsStrip('The Norm', 'thenorm', 'kfnrm'),
+           'onaclaireday' :
+               UcomicsStrip('On A Claire Day', 'onaclaireday', 'crocd'),
+           'theothercoast' :
+               UcomicsStrip('The Other Coast', 'theothercoast', 'crtoc'),
+           'outofthegenepool' :
+               UcomicsStrip('Out of the Gene Pool', 'outofthegenepool', 'wpgen'),
+           'overboard' :
+               UcomicsStrip('Overboard', 'overboard', 'ob'),
+           'pickles' :
+               UcomicsStrip('Pickles', 'pickles', 'wppic'),
+           'thepinkpanther' :
+               UcomicsStrip('The Pink Panther', 'thepinkpanther', 'tmpnk'),
+           'pluggers' :
+               UcomicsStrip('Pluggers', 'pluggers', 'tmplu'),
+           # FIXME: add an encoding so that cafe can have an accent
+           'poochcafe' :
+               UcomicsStrip('Pooch Cafe', 'poochcafe', 'poc'),
+           'popculture' :
+               UcomicsStrip('Pop Culture', 'popculture', 'pop'),
+           'preteena' :
+               UcomicsStrip('PreTeena', 'preteena', 'pr'),
+           'pricklycity' :
+               UcomicsStrip('Prickly City', 'pricklycity', 'prc'),
+           'thequigmans' :
+               UcomicsStrip('The Quigmans', 'thequigmans', 'tmqui'),
+           'reallifeadventures' :
+               UcomicsStrip('Real Life Adventures', 'reallifeadventures', 'rl'),
+           'redandrover' :
+               UcomicsStrip('Red and Rover', 'redandrover', 'wpred'),
+           'redmeat' :
+               UcomicsStrip('Red Meat', 'redmeat', 'red'),
+           'ronaldinhogaucho' :
+               UcomicsStrip('Ronaldinho Gaucho', 'ronaldinhogaucho', 'ron'),
+           'rubes' :
+               UcomicsStrip('Rubes', 'rubes', 'crrub'),
+           'shoe' :
+               UcomicsStrip('Shoe', 'shoe', 'tmsho'),
+           'shoecabbage' :
+               UcomicsStrip('Shoecabbage', 'shoecabbage', 'shcab'),
+           'slowpoke' :
+               UcomicsStrip('Slowpoke', 'slowpoke', 'slow'),
+           'smallworld' :
+               UcomicsStrip('Small World', 'smallworld', 'small'),
+           'spaceistheplace' :
+               UcomicsStrip('Space is the Place', 'spaceistheplace', 'sitp'),
+           'speedbump' :
+               UcomicsStrip('Speed Bump', 'speedbump', 'crspe'),
+           'stateoftheunion' :
+               UcomicsStrip('State of the Union', 'stateoftheunion', 'crsou'),
+           'stonesoup' :
+               UcomicsStrip('Stone Soup', 'stonesoup', 'ss'),
+           'strangebrew' :
+               UcomicsStrip('Strange Brew', 'strangebrew', 'ctstr'),
+           'suttonimpact' :
+               UcomicsStrip('Sutton Impact', 'suttonimpact', 'stn'),
+           'sylvia' :
+               UcomicsStrip('Sylvia', 'sylvia', 'tmsyl'),
+           'tankmcnamara' :
+               UcomicsStrip('Tank McNamara', 'tankmcnamara', 'tm'),
+           'teenagemutantninjaturtles' :
+               UcomicsStrip('Teenage Mutant Ninja Turtles', 'teenagemutantninjaturtles', 'tmnt'),
+           'think' :
+               UcomicsStrip('(th)ink', 'think', 'think'),
+           'thickandthin' :
+               UcomicsStrip('Through Thick and Thin', 'thickandthin', 'cpthk'),
+           'tinysepuku' :
+               UcomicsStrip('Tiny Sepuku', 'tinysepuku', 'tiny'),
+           'tomthedancingbug' :
+               UcomicsStrip('Tom the Dancing Bug', 'tomthedancingbug', 'td'),
+           'toomuchcoffeeman' :
+               UcomicsStrip('Too Much Coffee Man', 'toomuchcoffeeman', 'tmcm'),
+           'watchyourhead' :
+               UcomicsStrip('Watch Your Head', 'watchyourhead', 'wpwyh'),
+           'weepals' :
+               UcomicsStrip('Wee Pals', 'weepals', 'crwee'),
+           'winniethepooh' :
+               UcomicsStrip('Winnie the Pooh', 'winniethepooh', 'crwin'),
+           'wizardofid' :
+               UcomicsStrip('Wizard of Id', 'wizardofid', 'crwiz'),
+           'workingitout' :
+               UcomicsStrip('Working It Out', 'workingitout', 'crwio'),
+           'yenny' :
+               UcomicsStrip('Yenny', 'yenny', 'yen'),
+           'zackhill' :
+               UcomicsStrip('Zack Hill', 'zackhill', 'crzhi'),
+           'ziggy' :
+               UcomicsStrip('Ziggy', 'ziggy', 'zi'),
+
+           # united media
+           'chickweed' :
+               UnitedMediaStrip('9 Chickweed Lane', 'chickweed'),
+           'alleyoop' :
+               UnitedMediaStrip('Alley Oop', 'alleyoop'),
+           'arlonjanis' :
+               ComicsComStrip('Arlo & Janis', 'arlo&janis'),
+           'ben' :
+               UnitedMediaStrip('Ben', 'ben'),
+           'betty' :
+               UnitedMediaStrip('Betty', 'betty'),
+           'bignate' :
+               UnitedMediaStrip('Big Nate', 'bignate'),
+           'bornloser' :
+               UnitedMediaStrip('The Born Loser', 'bornloser'),
+           'brevity' :
+               UnitedMediaStrip('Brevity', 'brevity'),
+           'buckets' :
+               UnitedMediaStrip('The Buckets', 'buckets'),
+           'captainmurphey' :
+               UnitedMediaStrip('Captain Murphey', 'captainmurphey'),
+           'acaseinpoint' :
+               UnitedMediaStrip('A Case in Point', 'acaseinpoint'),
+           'committed' :
+               ComicsComStrip('Committed', 'committed'),
+           'cowandboy' :
+               UnitedMediaStrip('Cow & Boy', 'cowandboy'),
+           'diesel' :
+               UnitedMediaStrip('Diesel Sweeties', 'diesel'),
+           'dilbert' :
+               ComicsComStrip('Dilbert.com', 'dilbert', 'dilbert.com'),
+           'drabble' :
+               UnitedMediaStrip('Drabble', 'drabble'),
+           'farcus' :
+               UnitedMediaStrip('Farcus', 'farcus'),
+           'fatcats' :
+               UnitedMediaStrip('Fat Cats Classics', 'fatcats'),
+           'ferdnand' :
+               UnitedMediaStrip('Ferd\'nand', 'ferdnand'),
+           'fminus' :
+               UnitedMediaStrip('F Minus', 'fminus'),
+           'franknernest' :
+               UnitedMediaStrip('Frank and Ernest', 'franknernest'),
+           'frazz' :
+               UnitedMediaStrip('Frazz', 'frazz'),
+           'geech' :
+               UnitedMediaStrip('Geech Classics', 'geech'),
+           'getfuzzy' :
+               ComicsComStrip('Get Fuzzy', 'get_fuzzy'),
+           'gofish' :
+               UnitedMediaStrip('Go Fish', 'gofish'),
+           'graffiti' :
+               UnitedMediaStrip('Graffiti', 'graffiti'),
+           'grandave' :
+               UnitedMediaStrip('Grand Avenue', 'grandave'),
+           'grizzwells' :
+               UnitedMediaStrip('The Grizzwells', 'grizzwells'),
+           'herman' :
+               UnitedMediaStrip('Herman', 'herman'),
+           'humblestumble' :
+               UnitedMediaStrip('The Humble Stumble', 'humblestumble'),
+           'janesworld' :
+               UnitedMediaStrip('Jane\'s World Classics', 'janesworld'),
+           'jumpstart' :
+               UnitedMediaStrip('Jump Start', 'jumpstart'),
+           'kitncarlyle' :
+               UnitedMediaStrip('Kit \'N\' Carlyle', 'kitncarlyle'),
+           'littledee' :
+               UnitedMediaStrip('Little Dee', 'littledee'),
+           'lilabner' :
+               UnitedMediaStrip('Li\'l Abner Classics', 'lilabner'),
+           'lola' :
+               UnitedMediaStrip('Lola', 'lola'),
+           'luann' :
+               UnitedMediaStrip('Luann', 'luann'),
+           'marmaduke' :
+               UnitedMediaStrip('Marmaduke', 'marmaduke'),
+           'meg' :
+               UnitedMediaStrip('Meg! Classics', 'meg'),
+           'moderatelyconfused' :
+               UnitedMediaStrip('Moderately Confused', 'moderatelyconfused'),
+           'monty' :
+               ComicsComStrip('Monty', 'monty'),
+           'motley' :
+               UnitedMediaStrip('Motley Classics', 'motley'),
+           'nancy' :
+               UnitedMediaStrip('Nancy', 'nancy'),
+           'naturalselection' :
+               ComicsComStrip('Natural Selection', 'natural_selection'),
+           'offthemark' :
+               UnitedMediaStrip('Off The Mark', 'offthemark'),
+           'onebighappy' :
+               ComicsComStrip('One Big Happy', 'one_big_happy_classics'),
+           'hedge' :
+               UnitedMediaStrip('Over the Hedge', 'hedge'),
+           'peanuts' :
+               UnitedMediaStrip('Peanuts', 'peanuts'),
+           'pearls' :
+               ComicsComStrip('Pearls Before Swine', 'pearls_before_swine'),
+           'pibgorn' :
+               UnitedMediaStrip('Pibgorn', 'pibgorn'),
+           'raisingduncan' :
+               UnitedMediaStrip('Raising Duncan', 'raisingduncan'),
+           'reality' :
+               ComicsComStrip('Reality Check', 'reality_check'),
+           'ripleys' :
+               UnitedMediaStrip('Ripley\'s Believe It or Not!', 'ripleys'),
+           'roseisrose' :
+               UnitedMediaStrip('Rose Is Rose', 'roseisrose'),
+           'rudypark' :
+               UnitedMediaStrip('Rudy Park', 'rudypark'),
+           'shirleynson' :
+               UnitedMediaStrip('Shirley And Son Classics', 'shirleynson'),
+           'peanuts' :
+               UnitedMediaStrip('Snoopy.com', 'peanuts'),
+           'soup2nutz' :
+               UnitedMediaStrip('Soup To Nutz', 'soup2nutz'),
+           'spotthefrog' :
+               UnitedMediaStrip('Spot The Frog', 'spotthefrog'),
+           'sunshineclub' :
+               UnitedMediaStrip('The Sunshine Club', 'sunshineclub'),
+           'tarzan' :
+               UnitedMediaStrip('Tarzan Classics', 'tarzan'),
+           'workingdaze' :
+               ComicsComStrip('Working Daze', 'working_daze'),
+
+           # king features via sfgate
+           'babyblues' :
+               KingFeaturesSFGate('Baby Blues', 'Baby_Blues'),
+           'beetlebailey' :
+               KingFeaturesSFGate('Beetle Bailey', 'Beetle_Bailey'),
+           'bizarro' :
+               KingFeaturesSFGate('Bizarro', 'Bizarro'),
+           'blondie' :
+               KingFeaturesSFGate('Blondie', 'Blondie'),
+           'crock' :
+               KingFeaturesSFGate('Crock', 'Crock'),
+           'curtis' :
+               KingFeaturesSFGate('Curtis', 'Curtis'),
+           'dennisthemenace' :
+               KingFeaturesSFGate('Dennis the Menace', 'Dennis_The_Menace'),
+           'edgecity' :
+               KingFeaturesSFGate('Edge City', 'Edge_City'),
+           'funkywinkerbean' :
+               KingFeaturesSFGate('Funky Winkerbean', 'Funky_Winkerbean'),
+           'mallardfillmore' :
+               KingFeaturesSFGate('Mallard Fillmore', 'Mallard_Fillmore'),
+           'mutts' :
+               KingFeaturesSFGate('Mutts', 'Mutts'),
+           'thephantom' :
+               KingFeaturesSFGate('The Phantom', 'Phantom'),
+           'piranhaclub' :
+               KingFeaturesSFGate('Piranha Club', 'Piranha'),
+           'popeye' :
+               KingFeaturesSFGate('Popeye', 'Popeye'),
+           'rhymeswithorange' :
+               KingFeaturesSFGate('Rhymes with Orange', 'Rhymes_with_Orange'),
+           'sallyforth' :
+               KingFeaturesSFGate('Sally Forth', 'Sally_Forth'),
+           'shermanslagoon' :
+               KingFeaturesSFGate('Sherman\'s Lagoon', 'Shermans_Lagoon'),
+           'sixchix' :
+               KingFeaturesSFGate('Six Chix', '6Chix'),
+           'zits' :
+               KingFeaturesSFGate('Zits', 'Zits'),
+
+           'userfriendly' :
+               GenericSearchStrip('User Friendly',
+                                  'http://www.userfriendly.org/',
+                                  'http://ars.userfriendly.org/cartoons/?id=%Y%m%d&mode=classic',
+                                  '<img .*src="(http://www.userfriendly.org/cartoons/archives/%y.+/x?uf.+\.gif)".*>'),
+         }

comicbox/ucomics.py

+import datetime
+import logging
+
+class UcomicsStrip(object):
+    _HOME_PAGE = 'http://www.ucomics.com/%s/'
+    _STRIP_URL = 'http://images.ucomics.com/comics/%s/%s/%s%s.gif'
+
+    def __init__(self, strip, identifier, code):
+        self.logger = logging.getLogger('comicbox.ucomics.%s' % (strip))
+        self.strip = strip
+        self.identifier = identifier
+        self.code = code
+
+    def name(self):
+        return self.strip
+
+    def home(self):
+        return self._HOME_PAGE % (self.identifier)
+
+    def strip_url(self, date):
+        return self._STRIP_URL % (self.code, date.strftime('%Y'), 
+                                  self.code, date.strftime('%y%m%d'))

comicbox/united_media.py

+import datetime
+import logging
+import re
+import urllib2
+
+from request_builder import build_request
+
+class UnitedMediaStrip(object):
+    _BASE_URL = 'http://www.unitedmedia.com'
+    _HOME_PAGE = '/comics/%s/'
+    _SEARCH_PAGE = '/comics/%s/archive/%s-%s.html'
+    _SEARCH_PATTERN = '<img .*src="(/comics/%s/archive/images/%s.+?)".*>'
+
+    def __init__(self, strip, identifier):
+        self.logger = logging.getLogger('comicbox.unitedmedia.%s' % (strip))
+        self.strip = strip
+        self.identifier = identifier
+
+    def name(self):
+        return self.strip
+
+    def home(self):
+        home_page = self._HOME_PAGE % (self.identifier)
+        return self._BASE_URL + home_page
+
+    def strip_url(self, date):
+        search_page = self._SEARCH_PAGE % (self.identifier,
+                                           self.identifier,
+                                           date.strftime('%Y%m%d'))
+        search_url = self._BASE_URL + search_page
+        search_pattern = self._SEARCH_PATTERN % (self.identifier,
+                                                 self.identifier)
+        search_regex = re.compile(search_pattern, re.IGNORECASE)
+
+        try:
+            self.logger.debug('requesting search page "%s"' % (search_url))
+            search_req = build_request(search_url)
+            page = urllib2.urlopen(search_req).read()
+            matches = search_regex.search(page)
+            if matches:
+                return self._BASE_URL + matches.group(1)
+            else:
+                self.logger.error('strip url not found')
+                return ''
+        except urllib2.URLError, e:
+            self.logger.error('failed to download search page: %s' % (e))
+            return ''
Add a comment to this file

lib/__init__.py

Empty file removed.

Add a comment to this file

lib/backends/__init__.py

Empty file removed.

lib/backends/file_archive.py

-import datetime
-import logging
-import os
-
-class FileArchive(object):
-    DEFAULT_DIRECTORY = 'storage'
-
-    def __init__(self, date, args):
-        """Initialize a file based storage archive.
-
-        The following parameters in args will be used:
-            - basedir: path where the archive will be stored.
-                       can be relative."""
-
-        if 'basedir' in args:
-            self.basedir = os.path.abspath(args['basedir'])
-        else:
-            self.basedir = os.path.abspath(self.DEFAULT_DIRECTORY)
-
-        self.date_string = date.strftime('%Y%m%d')
-        self.display_date_string = date.strftime('%A, %B %d, %Y')
-        self.archive = os.path.join(self.basedir, 
-                                    'index-%s.html' % (self.date_string))
-
-        self.logger = logging.getLogger('comicbox.backend.FileArchive')
-
-        if not os.path.exists(self.basedir):
-            self.logger.info('creating base directory: %s' % (self.basedir))
-            os.makedirs(self.basedir)
-
-        if os.path.exists(self.archive):
-            self.logger.info('overwriting exisiting archive file')
-
-        self.outfile = open(self.archive, 'wt')
-
-        self._write_header()
-
-    def add_strip(self, strip, strip_home, image, image_url, filename):
-        self.outfile.write('<a href="%s"><h3>%s</h3></a>\n' %
-                           (strip_home, strip))
-        if image and len(image) > 0:
-            if filename:
-                image_filename = filename
-            else:
-                image_filename = image_url[image_url.rfind('/') + 1:]
-            image_dir = os.path.join(self.basedir, strip)
-            image_path = os.path.join(strip, image_filename)
-            image_fullpath = os.path.join(self.basedir, image_path)
-
-            if not os.path.exists(image_dir):
-                self.logger.info('creating strip directory: %s' % 
-                                 (image_dir))
-                os.makedirs(image_dir)
-            image_file = open(image_fullpath, 'wb')
-            image_file.write(image)
-            image_file.close()
-
-            self.outfile.write('<img src="%s" />' % (image_path))
-        else:
-            self.outfile.write('<em>image not available</em>')
-        self.outfile.write('<br />')
-
-    def finish(self):
-        self._write_footer()
-        self.outfile.close()
-        self._update_index()
-
-    def _write_header(self):
-        self.outfile.write('<html><head>\n')
-        self.outfile.write('<title>comicbox - %s</title>\n' % 
-                           (self.display_date_string))
-        self.outfile.write('</head>\n')
-        self.outfile.write('<body>\n')
-        self.outfile.write('<h1>comicbox - %s</h1>\n' % 
-                           (self.display_date_string))
-
-    def _write_footer(self):
-        self.outfile.write('</body>\n</html>\n')
-
-    def _update_index(self):
-        outfile = open(os.path.join(self.basedir, 'archive.html'), 'wt')
-        outfile.write('<html><head>\n')
-        outfile.write('<title>comicbox - archive</title>\n')
-        outfile.write('</head>\n')
-        outfile.write('<body>\n')
-        outfile.write('<h1>comicbox - archive</h1>\n')
-
-        days = [d for d in os.listdir(self.basedir) if d.startswith('index-')]
-        days.sort()
-        days.reverse()
-        for d in days:
-            try:
-                date_str = d.split('-')[1].split('.')[0]
-                # datetime.datetime.strptime is only in python >= 2.5
-                year = int(date_str[0:4])
-                month = int(date_str[4:6])
-                day = int(date_str[6:8])
-                date = datetime.datetime(year, month, day)
-                date_out = date.strftime('%A, %B %d, %Y')
-                outfile.write('<a href="%s">%s</a><br />\n' % (d, date_out))
-            except ValueError:
-                self.logger.error('skipping invalid day file: %s' % d)
-
-        outfile.write('</body>\n</html>\n')
-        outfile.close()
-

lib/comics_com.py

-import datetime
-import logging
-import re
-import urllib2
-
-from request_builder import build_request
-
-class ComicsComStrip(object):
-    _BASE_URL = 'http://www.comics.com'
-    _HOME_PAGE = '/%s/'
-    _SEARCH_PAGE = '/%s/%s/'
-    _SEARCH_PATTERN = '<img .*src="(\S*/dyn/str_strip/\S*\.gif)".*>'
-
-    def __init__(self, strip, identifier, image_host=None):
-        self.logger = logging.getLogger('comicbox.comicscom.%s' % (strip))
-        self.strip = strip
-        self.identifier = identifier
-        self.image_host = image_host
-
-    def name(self):
-        return self.strip
-
-    def home(self):
-        home_page = self._HOME_PAGE % (self.identifier)
-        return self._BASE_URL + home_page
-
-    def strip_url(self, date):
-        search_page = self._SEARCH_PAGE % (self.identifier,
-                                           date.strftime('%Y-%m-%d'))
-        search_url = self._BASE_URL + search_page
-        search_pattern = self._SEARCH_PATTERN
-        search_regex = re.compile(search_pattern)
-
-        try:
-            self.logger.debug('requesting search page "%s"' % (search_url))
-            search_req = build_request(search_url)
-            page = urllib2.urlopen(search_req).read()
-            matches = search_regex.search(page)
-            if matches:
-                if self.image_host and matches.group(1).startswith('/'):
-                    return 'http://%s%s' % (self.image_host, matches.group(1))
-                else:
-                    return matches.group(1)
-            else:
-                self.logger.error('strip url not found')
-                return ''
-        except urllib2.URLError, e:
-            self.logger.error('failed to download search page: %s' % (e))
-            return ''

lib/configuration.py

-import ConfigParser
-import os
-import string
-
-import globals
-from strip_definitions import Strips
-
-class ConfigurationError(Exception):
-    pass
-
-
-def load_configuration(exepath):
-    """load comicbox configuration from a file.
-
-    Loads the comicbox configuration from comicboxrc in same dir as
-    sys.argv[0] and ~/.comicboxrc.  Settings in ~/.comicboxrc override settings
-    in comicbox.conf.
-
-    There must be at least a 'comicbox' section in the configuration file.
-    This section holds the backend and chosen strips.  If the backend takes
-    additional parameters they will be in a section with the same name as the
-    backend.  See doc/comicboxrc.example for an example."""
-
-    config_files = [os.path.join(exepath, 'comicboxrc'),
-                    os.path.expanduser('.comicboxrc')]
-
-    parser = ConfigParser.SafeConfigParser()
-    if len(parser.read(config_files)) == 0:
-        raise ConfigurationError('No configuration files found')
-
-    if 'comicbox' not in parser.sections():
-        raise ConfigurationError('Missing [comicbox] section')
-
-    backend = None
-    strips = None
-    for item in parser.items('comicbox'):
-        if item[0] == 'backend':
-            backend = item[1]
-        elif item[0] == 'strips':
-            # remove all whitespace
-            value = item[1]
-            for c in string.whitespace:
-                value = value.replace(c, '')
-            strips = value.split(',')
-
-    # set the default backend
-    if backend == None:
-        backend = globals.DEFAULT_BACKEND
-
-    # build the backend arguments
-    backend_args = {}
-    if backend in parser.sections():
-        args = parser.items(backend)
-        for arg in args:
-            backend_args[arg[0]] = arg[1]
-
-    if strips == None or len(strips) == 0:
-        raise ConfigurationError('No strips to download')
-
-    resolved_strips = []
-    for strip in strips:
-        try:
-            resolved_strips.append(Strips[strip])
-        except KeyError:
-            raise ConfigurationError('Invalid strip: %s' % (strip))
-
-    return (backend, backend_args, resolved_strips)

lib/download.py

-import logging
-import time
-import urllib2
-
-from request_builder import build_request
-
-def fetch_strips(strips, backend, date):
-    logger = logging.getLogger('comicbox.fetch_strips')
-    for s in strips:
-        time.sleep(0.01)
-        url = s.strip_url(date)
-        if len(url) > 0:
-            logger.debug('fetching "%s"' % (url,))
-            if 'filename' in dir(s):
-                filename = s.filename(date)
-            else:
-                filename = None
-            try:
-                if 'strip_referer' in dir(s):
-                    req = build_request(url, s.strip_referer())
-                else:
-                    req = build_request(url)
-                image = urllib2.urlopen(req).read()
-                backend.add_strip(s.name(), s.home(), image, url, filename)
-            except urllib2.URLError, e:
-                logger.error('failed to download %s: %s' % (s.name(), e))
-                backend.add_strip(s.name(), s.home(), None, None, None)
-        else:
-            backend.add_strip(s.name(), s.home(), None, None, None)

lib/generic_search.py

-import datetime
-import logging
-import re
-import urllib2
-
-from request_builder import build_request
-
-class GenericSearchStrip(object):
-    def __init__(self, strip, home_page, search_page, search_pattern):
-        self.strip = strip
-        self.home_page = home_page
-        self.search_page = search_page
-        self.search_pattern = search_pattern
-
-        l = logging.getLogger('comicbox.generic_search.%s' % (strip))
-        self.logger = l
-
-    def name(self):
-        return self.strip
-
-    def home(self):
-        return self.home_page
-
-    def strip_url(self, date):
-        # deal with any date escapes in search_{page,pattern}
-        search_url = date.strftime(self.search_page)
-        search_pattern = date.strftime(self.search_pattern)
-        search_regex = re.compile(search_pattern, re.IGNORECASE)
-
-        try:
-            self.logger.debug('requesting search page "%s"' % (search_url))
-            search_req = build_request(search_url)
-            page = urllib2.urlopen(search_req).read()
-            matches = search_regex.search(page)
-            if matches:
-                return matches.group(1)
-            else:
-                self.logger.error('strip url not found')
-                return ''
-        except urllib2.URLError, e:
-            self.logger.error('failed to download search page: %s' % (e))
-            return ''

lib/globals.py

-NAME = 'comicbox'
-VERSION = '0.1'
-
-DEFAULT_BACKEND = 'file_archive'

lib/king_sfgate.py

-import datetime
-import logging
-
-class KingFeaturesSFGate(object):
-    _HOME_PAGE = 'http://www.sfgate.com/cgi-bin/article.cgi?file=/comics/%s.dtl'
-    _STRIP_URL = 'http://pst.rbma.com/content/%s'
-
-    def __init__(self, strip, identifier):
-        self.logger = logging.getLogger('comicbox.king_sfgate.%s' % (strip))
-        self.strip = strip
-        self.identifier = identifier
-
-    def name(self):
-        return self.strip
-
-    def home(self):
-        return self._HOME_PAGE % (self.identifier)
-
-    def strip_url(self, date):
-        # these strips are only available for today
-        today = datetime.datetime.now()
-        current = datetime.datetime(today.year, today.month, today.day)
-        passed = datetime.datetime(date.year, date.month, date.day)
-        if current != passed:
-            self.logger.error('only available for current date')
-            return ''
-
-        return self._STRIP_URL % (self.identifier)
-
-    def strip_referer(self):
-        return self._HOME_PAGE % (self.identifier)
-
-    def filename(self, date):
-        return '%s-%s.gif' % (self.identifier, date.strftime('%Y%m%d'))

lib/request_builder.py

-import urllib2
-
-import globals
-
-def build_request(url, referer=None):
-    """Builds a urllib2.Request object.
-
-    Builds a urllib2.Request object for the given url and referer.  While
-    simple enough to build the request object without this function, this
-    allows for common headers, such as a user agent, to be added to all
-    requests."""
-
-    req = urllib2.Request(url)
-    if referer:
-        req.add_header('Referer', referer)
-
-    req.add_header('User-agent', '%s/%s' % (globals.NAME, globals.VERSION))
-    return req

lib/strip_definitions.py

-from generic_search import GenericSearchStrip
-from comics_com import ComicsComStrip
-from king_sfgate import KingFeaturesSFGate
-from ucomics import UcomicsStrip
-from united_media import UnitedMediaStrip
-
-# TODO: handle the following kinds of strips
-# ucomics: old comic reruns
-#   - Bloom County
-#   - Minimum Security
-#   - Waylay
-
-
-Strips = { # ucomics 
-           '9to5' :
-               UcomicsStrip('9 to 5', '9to5', 'tmntf'),
-           'adamathome' :
-               UcomicsStrip('Adam@Home', 'adamathome', 'ad'),
-           'agnes' :
-               UcomicsStrip('Agnes', 'agnes', 'cragn'),
-           'andycapp' :
-               UcomicsStrip('Andy Capp', 'andycapp', 'crcap'),
-           'animalcrackers' :
-               UcomicsStrip('Animal Crackers', 'animalcrackers', 'tmani'),
-           'annie' :
-               UcomicsStrip('Annie', 'annie', 'tmann'),
-           'theargylesweater' :
-               UcomicsStrip('The Argyle Sweater', 'theargylesweater', 'tas'),
-           'askshagg' :
-               UcomicsStrip('Ask Shagg', 'askshagg', 'crask'),
-           'bc' :
-               UcomicsStrip('B.C.', 'bc', 'crbc'),
-           'badreporter' :
-               UcomicsStrip('Bad Reporter', 'badreporter', 'bad'),
-           'baldo' :
-               UcomicsStrip('Baldo', 'baldo', 'ba'),
-           'ballardstreet' :
-               UcomicsStrip('Ballard Street', 'ballardstreet', 'crbal'),
-           'bigtop' :
-               UcomicsStrip('Big Top', 'bigtop', 'bt'),
-           'biographic' :
-               UcomicsStrip('Biographic', 'biographic', 'biov'),
-           'bonanas' :
-               UcomicsStrip('Bo Nanas', 'bonanas', 'bon'),
-           'bobthesquirrel' :
-               UcomicsStrip('Bob the Squirrel', 'bobthesquirrel', 'bob'),
-           'boondocks' :
-               UcomicsStrip('The Boondocks', 'boondocks', 'bo'),
-           'bottomliners' :
-               UcomicsStrip('Bottomliners', 'bottomliners', 'tmbot'),
-           'boundandgagged' :
-               UcomicsStrip('Bound and Gagged', 'boundandgagged', 'tmbou'),
-           'brainwaves' :
-               UcomicsStrip('Brainwaves', 'brainwaves', 'bwv'),
-           'brendastarr' :
-               UcomicsStrip('Brenda Starr', 'brendastarr', 'tmbre'),
-           'brewsterrockit' :
-               UcomicsStrip('Brewster Rockit', 'brewsterrockit', 'tmrkt'),
-           'broomhilda' :
-               UcomicsStrip('Broom Hilda', 'broomhilda', 'tmbro'),
-           'calvinandhobbes' :
-               UcomicsStrip('Calvin and Hobbes', 'calvinandhobbes', 'ch'),
-           'candorville' :
-               UcomicsStrip('Candorville', 'candorville', 'cand'),
-           'cathy' :
-               UcomicsStrip('Cathy', 'cathy', 'ca'),
-           'cestlavie' :
-               UcomicsStrip('C\'est la Vie', 'cestlavie', 'clv'),
-           'citizendog' :
-               UcomicsStrip('Citizen Dog', 'citizendog', 'cd'),
-           'thecity' :
-               UcomicsStrip('The City', 'thecity', 'derf'),
-           'clearbluewater' :
-               UcomicsStrip('Clear Blue Water', 'clearbluewater', 'cbw'),
-           'cleats' :
-               UcomicsStrip('Cleats', 'cleats', 'cle'),
-           'closetohome' :
-               UcomicsStrip('Close to Home', 'closetohome', 'cl'),
-           'compu-toon' :
-               UcomicsStrip('Compu-toon', 'compu-toon', 'tmcom'),
-           'cornered' :
-               UcomicsStrip('Cornered', 'cornered', 'co'),
-           'culdesac' :
-               UcomicsStrip('Cul de Sac', 'culdesac', 'cds'),
-           'deepcover' :
-               UcomicsStrip('Deep Cover', 'deepcover', 'deep'),
-           'dicktracy' :
-               UcomicsStrip('Dick Tracy', 'dicktracy', 'tmdic'),
-           'dinetteset' :
-               UcomicsStrip('The Dinette Set', 'dinetteset', 'crdin'),
-           'dogeatdoug' :
-               UcomicsStrip('Dog Eat Doug', 'dogeatdoug', 'crdog'),
-           'domesticabuse' :
-               UcomicsStrip('Domestic Abuse', 'domesticabuse', 'dom'),
-           'doodles' :
-               UcomicsStrip('Doodles', 'doodles', 'tmdoo'),
-           'doonesbury' :
-               UcomicsStrip('Doonesbury', 'doonesbury', 'db'),
-           'duplex' :
-               UcomicsStrip('The Duplex', 'duplex', 'dp'),
-           'theelderberries' :
-               UcomicsStrip('The Elderberries', 'theelderberries', 'eld'),
-           'thefifthwave' :
-               UcomicsStrip('Fifth Wave', 'thefifthwave', 'fw'),
-           'flightdeck' :
-               UcomicsStrip('Flight Deck', 'flightdeck', 'crfd'),
-           'floandfriends' :
-               UcomicsStrip('Flo and Friends', 'floandfriends', 'crflo'),
-           'theflyingmccoys' :
-               UcomicsStrip('The Flying McCoys', 'theflyingmccoys', 'fmc'),
-           'forbetterorforworse' :
-               UcomicsStrip('For Better or For Worse', 'forbetterorforworse', 'fb'),
-           'forheavenssake' :
-               UcomicsStrip('For Heaven\'s Sake', 'forheavenssake', 'crfhs'),
-           '44unionavenue' :
-               UcomicsStrip('44 Union Avenue', '44unionavenue', 'fua'),
-           'foxtrot' :
-               UcomicsStrip('FoxTrot', 'foxtrot', 'ft'),
-           'foxtrotclassics' :
-               UcomicsStrip('FoxTrot Classics', 'foxtrotclassics', 'ftcl'),
-           'fredbasset' :
-               UcomicsStrip('Fred Basset', 'fredbasset', 'tmfba'),
-           'frogapplause' :
-               UcomicsStrip('Frog Applause', 'frogapplause', 'frog'),
-           'thefuscobrothers' :
-               UcomicsStrip('The Fusco Brothers', 'thefuscobrothers', 'fu'),
-           'garfield' :
-               UcomicsStrip('Garfield', 'garfield', 'ga'),
-           'gasolinealley' :
-               UcomicsStrip('Gasoline Alley', 'gasolinealley', 'tmgas'),
-           'gilthorp' :
-               UcomicsStrip('Gil Thorp', 'gilthorp', 'tmgil'),
-           'gingermeggs' :
-               UcomicsStrip('Ginger Meggs', 'gingermeggs', 'gin'),
-           'girlsandsports' :
-               UcomicsStrip('Girls & Sports', 'girlsandsports', 'crgis'),
-           'heartofthecity' :
-               UcomicsStrip('Heart of the City', 'heartofthecity', 'hc'),
-           'heathcliff' :
-               UcomicsStrip('Heathcliff', 'heathcliff', 'crhea'),
-           'herbandjamaal' :
-               UcomicsStrip('Herb and Jamaal', 'herbandjamaal', 'crher'),
-           'housebroken' :
-               UcomicsStrip('Housebroken', 'housebroken', 'tmhou'),
-           'hubertandabby' :
-               UcomicsStrip('Hubert and Abby', 'hubertandabby', 'haa'),
-           'idiotbox' :
-               UcomicsStrip('Idiot Box', 'idiotbox', 'ibox'),
-           'inthebleachers' :
-               UcomicsStrip('In the Bleachers', 'inthebleachers', 'bl'),
-           'inkpen' :
-               UcomicsStrip('Ink Pen', 'inkpen', 'ink'),
-           'thekchronicles' :
-               UcomicsStrip('The K Chronicles', 'thekchronicles', 'kk'),
-           'kudzu' :
-               UcomicsStrip('Kudzu', 'kudzu', 'tmkud'),
-           'lacucaracha' :
-               UcomicsStrip('La Cucaracha', 'lacucaracha', 'lc'),
-           'libertymeadows' :
-               UcomicsStrip('Liberty Meadows', 'libertymeadows', 'crlib'),
-           'lio' :
-               UcomicsStrip('Lio', 'lio', 'lio'),
-           'looseparts' :
-               UcomicsStrip('Loose Parts', 'looseparts', 'tmloo'),
-           'luckycow' :
-               UcomicsStrip('Lucky Cow', 'luckycow', 'luc'),
-           'meaningoflila' :
-               UcomicsStrip('The Meaning of Lila', 'meaningoflila', 'crlil'),
-           'themiddletons' :
-               UcomicsStrip('The Middletons', 'themiddletons', 'tmmid'),
-           'momma' :
-               UcomicsStrip('Momma', 'momma', 'crmom'),
-           'muttandjeff' :
-               UcomicsStrip('Mutt & Jeff', 'muttandjeff', 'mutt'),
-           'naturalselection' :
-               UcomicsStrip('Natural Selection', 'naturalselection', 'crns'),
-           'nestheads' :
-               UcomicsStrip('Nest Heads', 'nestheads', 'cpnst'),
-           'neurotica' :
-               UcomicsStrip('NEUROTICA', 'neurotica', 'neu'),
-           'nonsequitur' :
-               UcomicsStrip('Non Sequitur', 'nonsequitur', 'nq'),
-           'thenorm' :
-               UcomicsStrip('The Norm', 'thenorm', 'kfnrm'),
-           'onaclaireday' :
-               UcomicsStrip('On A Claire Day', 'onaclaireday', 'crocd'),
-           'theothercoast' :
-               UcomicsStrip('The Other Coast', 'theothercoast', 'crtoc'),
-           'outofthegenepool' :
-               UcomicsStrip('Out of the Gene Pool', 'outofthegenepool', 'wpgen'),
-           'overboard' :
-               UcomicsStrip('Overboard', 'overboard', 'ob'),
-           'pickles' :
-               UcomicsStrip('Pickles', 'pickles', 'wppic'),
-           'thepinkpanther' :
-               UcomicsStrip('The Pink Panther', 'thepinkpanther', 'tmpnk'),
-           'pluggers' :
-               UcomicsStrip('Pluggers', 'pluggers', 'tmplu'),
-           # FIXME: add an encoding so that cafe can have an accent
-           'poochcafe' :
-               UcomicsStrip('Pooch Cafe', 'poochcafe', 'poc'),
-           'popculture' :
-               UcomicsStrip('Pop Culture', 'popculture', 'pop'),
-           'preteena' :
-               UcomicsStrip('PreTeena', 'preteena', 'pr'),
-           'pricklycity' :
-               UcomicsStrip('Prickly City', 'pricklycity', 'prc'),
-           'thequigmans' :
-               UcomicsStrip('The Quigmans', 'thequigmans', 'tmqui'),
-           'reallifeadventures' :
-               UcomicsStrip('Real Life Adventures', 'reallifeadventures', 'rl'),
-           'redandrover' :
-               UcomicsStrip('Red and Rover', 'redandrover', 'wpred'),
-           'redmeat' :
-               UcomicsStrip('Red Meat', 'redmeat', 'red'),
-           'ronaldinhogaucho' :
-               UcomicsStrip('Ronaldinho Gaucho', 'ronaldinhogaucho', 'ron'),
-           'rubes' :
-               UcomicsStrip('Rubes', 'rubes', 'crrub'),
-           'shoe' :
-               UcomicsStrip('Shoe', 'shoe', 'tmsho'),
-           'shoecabbage' :
-               UcomicsStrip('Shoecabbage', 'shoecabbage', 'shcab'),
-           'slowpoke' :
-               UcomicsStrip('Slowpoke', 'slowpoke', 'slow'),
-           'smallworld' :
-               UcomicsStrip('Small World', 'smallworld', 'small'),
-           'spaceistheplace' :
-               UcomicsStrip('Space is the Place', 'spaceistheplace', 'sitp'),
-           'speedbump' :
-               UcomicsStrip('Speed Bump', 'speedbump', 'crspe'),
-           'stateoftheunion' :
-               UcomicsStrip('State of the Union', 'stateoftheunion', 'crsou'),
-           'stonesoup' :
-               UcomicsStrip('Stone Soup', 'stonesoup', 'ss'),
-           'strangebrew' :
-               UcomicsStrip('Strange Brew', 'strangebrew', 'ctstr'),
-           'suttonimpact' :
-               UcomicsStrip('Sutton Impact', 'suttonimpact', 'stn'),
-           'sylvia' :
-               UcomicsStrip('Sylvia', 'sylvia', 'tmsyl'),
-           'tankmcnamara' :
-               UcomicsStrip('Tank McNamara', 'tankmcnamara', 'tm'),
-           'teenagemutantninjaturtles' :
-               UcomicsStrip('Teenage Mutant Ninja Turtles', 'teenagemutantninjaturtles', 'tmnt'),
-           'think' :
-               UcomicsStrip('(th)ink', 'think', 'think'),
-           'thickandthin' :
-               UcomicsStrip('Through Thick and Thin', 'thickandthin', 'cpthk'),
-           'tinysepuku' :
-               UcomicsStrip('Tiny Sepuku', 'tinysepuku', 'tiny'),
-           'tomthedancingbug' :
-               UcomicsStrip('Tom the Dancing Bug', 'tomthedancingbug', 'td'),
-           'toomuchcoffeeman' :
-               UcomicsStrip('Too Much Coffee Man', 'toomuchcoffeeman', 'tmcm'),
-           'watchyourhead' :
-               UcomicsStrip('Watch Your Head', 'watchyourhead', 'wpwyh'),
-           'weepals' :
-               UcomicsStrip('Wee Pals', 'weepals', 'crwee'),
-           'winniethepooh' :
-               UcomicsStrip('Winnie the Pooh', 'winniethepooh', 'crwin'),
-           'wizardofid' :
-               UcomicsStrip('Wizard of Id', 'wizardofid', 'crwiz'),
-           'workingitout' :
-               UcomicsStrip('Working It Out', 'workingitout', 'crwio'),
-           'yenny' :
-               UcomicsStrip('Yenny', 'yenny', 'yen'),
-           'zackhill' :
-               UcomicsStrip('Zack Hill', 'zackhill', 'crzhi'),
-           'ziggy' :
-               UcomicsStrip('Ziggy', 'ziggy', 'zi'),
-
-           # united media
-           'chickweed' :
-               UnitedMediaStrip('9 Chickweed Lane', 'chickweed'),
-           'alleyoop' :
-               UnitedMediaStrip('Alley Oop', 'alleyoop'),
-           'arlonjanis' :
-               ComicsComStrip('Arlo & Janis', 'arlo&janis'),
-           'ben' :
-               UnitedMediaStrip('Ben', 'ben'),
-           'betty' :
-               UnitedMediaStrip('Betty', 'betty'),
-           'bignate' :
-               UnitedMediaStrip('Big Nate', 'bignate'),
-           'bornloser' :
-               UnitedMediaStrip('The Born Loser', 'bornloser'),
-           'brevity' :
-               UnitedMediaStrip('Brevity', 'brevity'),
-           'buckets' :
-               UnitedMediaStrip('The Buckets', 'buckets'),
-           'captainmurphey' :
-               UnitedMediaStrip('Captain Murphey', 'captainmurphey'),
-           'acaseinpoint' :
-               UnitedMediaStrip('A Case in Point', 'acaseinpoint'),
-           'committed' :
-               ComicsComStrip('Committed', 'committed'),
-           'cowandboy' :
-               UnitedMediaStrip('Cow & Boy', 'cowandboy'),
-           'diesel' :
-               UnitedMediaStrip('Diesel Sweeties', 'diesel'),
-           'dilbert' :
-               ComicsComStrip('Dilbert.com', 'dilbert', 'dilbert.com'),
-           'drabble' :
-               UnitedMediaStrip('Drabble', 'drabble'),
-           'farcus' :
-               UnitedMediaStrip('Farcus', 'farcus'),
-           'fatcats' :
-               UnitedMediaStrip('Fat Cats Classics', 'fatcats'),
-           'ferdnand' :
-               UnitedMediaStrip('Ferd\'nand', 'ferdnand'),
-           'fminus' :
-               UnitedMediaStrip('F Minus', 'fminus'),
-           'franknernest' :
-               UnitedMediaStrip('Frank and Ernest', 'franknernest'),
-           'frazz' :
-               UnitedMediaStrip('Frazz', 'frazz'),
-           'geech' :
-               UnitedMediaStrip('Geech Classics', 'geech'),
-           'getfuzzy' :
-               ComicsComStrip('Get Fuzzy', 'get_fuzzy'),
-           'gofish' :
-               UnitedMediaStrip('Go Fish', 'gofish'),
-           'graffiti' :
-               UnitedMediaStrip('Graffiti', 'graffiti'),
-           'grandave' :
-               UnitedMediaStrip('Grand Avenue', 'grandave'),
-           'grizzwells' :
-               UnitedMediaStrip('The Grizzwells', 'grizzwells'),
-           'herman' :
-               UnitedMediaStrip('Herman', 'herman'),
-           'humblestumble' :
-               UnitedMediaStrip('The Humble Stumble', 'humblestumble'),
-           'janesworld' :
-               UnitedMediaStrip('Jane\'s World Classics', 'janesworld'),
-           'jumpstart' :
-               UnitedMediaStrip('Jump Start', 'jumpstart'),
-           'kitncarlyle' :
-               UnitedMediaStrip('Kit \'N\' Carlyle', 'kitncarlyle'),
-           'littledee' :
-               UnitedMediaStrip('Little Dee', 'littledee'),
-           'lilabner' :
-               UnitedMediaStrip('Li\'l Abner Classics', 'lilabner'),
-           'lola' :
-               UnitedMediaStrip('Lola', 'lola'),
-           'luann' :
-               UnitedMediaStrip('Luann', 'luann'),
-           'marmaduke' :
-               UnitedMediaStrip('Marmaduke', 'marmaduke'),
-           'meg' :
-               UnitedMediaStrip('Meg! Classics', 'meg'),
-           'moderatelyconfused' :
-               UnitedMediaStrip('Moderately Confused', 'moderatelyconfused'),
-           'monty' :
-               ComicsComStrip('Monty', 'monty'),
-           'motley' :
-               UnitedMediaStrip('Motley Classics', 'motley'),
-           'nancy' :
-               UnitedMediaStrip('Nancy', 'nancy'),
-           'naturalselection' :
-               ComicsComStrip('Natural Selection', 'natural_selection'),
-           'offthemark' :
-               UnitedMediaStrip('Off The Mark', 'offthemark'),
-           'onebighappy' :
-               ComicsComStrip('One Big Happy', 'one_big_happy_classics'),
-           'hedge' :
-               UnitedMediaStrip('Over the Hedge', 'hedge'),
-           'peanuts' :
-               UnitedMediaStrip('Peanuts', 'peanuts'),
-           'pearls' :
-               ComicsComStrip('Pearls Before Swine', 'pearls_before_swine'),
-           'pibgorn' :
-               UnitedMediaStrip('Pibgorn', 'pibgorn'),
-           'raisingduncan' :
-               UnitedMediaStrip('Raising Duncan', 'raisingduncan'),
-           'reality' :
-               ComicsComStrip('Reality Check', 'reality_check'),
-           'ripleys' :
-               UnitedMediaStrip('Ripley\'s Believe It or Not!', 'ripleys'),
-           'roseisrose' :
-               UnitedMediaStrip('Rose Is Rose', 'roseisrose'),
-           'rudypark' :
-               UnitedMediaStrip('Rudy Park', 'rudypark'),
-           'shirleynson' :
-               UnitedMediaStrip('Shirley And Son Classics', 'shirleynson'),
-           'peanuts' :
-               UnitedMediaStrip('Snoopy.com', 'peanuts'),
-           'soup2nutz' :
-               UnitedMediaStrip('Soup To Nutz', 'soup2nutz'),
-           'spotthefrog' :
-               UnitedMediaStrip('Spot The Frog', 'spotthefrog'),
-           'sunshineclub' :
-               UnitedMediaStrip('The Sunshine Club', 'sunshineclub'),
-           'tarzan' :
-               UnitedMediaStrip('Tarzan Classics', 'tarzan'),
-           'workingdaze' :
-               ComicsComStrip('Working Daze', 'working_daze'),
-
-           # king features via sfgate
-           'babyblues' :
-               KingFeaturesSFGate('Baby Blues', 'Baby_Blues'),
-           'beetlebailey' :
-               KingFeaturesSFGate('Beetle Bailey', 'Beetle_Bailey'),
-           'bizarro' :
-               KingFeaturesSFGate('Bizarro', 'Bizarro'),
-           'blondie' :
-               KingFeaturesSFGate('Blondie', 'Blondie'),
-           'crock' :
-               KingFeaturesSFGate('Crock', 'Crock'),
-           'curtis' :
-               KingFeaturesSFGate('Curtis', 'Curtis'),
-           'dennisthemenace' :
-               KingFeaturesSFGate('Dennis the Menace', 'Dennis_The_Menace'),
-           'edgecity' :
-               KingFeaturesSFGate('Edge City', 'Edge_City'),
-           'funkywinkerbean' :
-               KingFeaturesSFGate('Funky Winkerbean', 'Funky_Winkerbean'),
-           'mallardfillmore' :
-               KingFeaturesSFGate('Mallard Fillmore', 'Mallard_Fillmore'),
-           'mutts' :
-               KingFeaturesSFGate('Mutts', 'Mutts'),
-           'thephantom' :
-               KingFeaturesSFGate('The Phantom', 'Phantom'),
-           'piranhaclub' :
-               KingFeaturesSFGate('Piranha Club', 'Piranha'),
-           'popeye' :
-               KingFeaturesSFGate('Popeye', 'Popeye'),
-           'rhymeswithorange' :
-               KingFeaturesSFGate('Rhymes with Orange', 'Rhymes_with_Orange'),
-           'sallyforth' :
-               KingFeaturesSFGate('Sally Forth', 'Sally_Forth'),
-           'shermanslagoon' :
-               KingFeaturesSFGate('Sherman\'s Lagoon', 'Shermans_Lagoon'),
-           'sixchix' :
-               KingFeaturesSFGate('Six Chix', '6Chix'),
-           'zits' :
-               KingFeaturesSFGate('Zits', 'Zits'),
-
-           'userfriendly' :
-               GenericSearchStrip('User Friendly',
-                                  'http://www.userfriendly.org/',
-                                  'http://ars.userfriendly.org/cartoons/?id=%Y%m%d&mode=classic',
-                                  '<img .*src="(http://www.userfriendly.org/cartoons/archives/%y.+/x?uf.+\.gif)".*>'),
-         }

lib/ucomics.py

-import datetime
-import logging
-
-class UcomicsStrip(object):
-    _HOME_PAGE = 'http://www.ucomics.com/%s/'
-    _STRIP_URL = 'http://images.ucomics.com/comics/%s/%s/%s%s.gif'
-
-    def __init__(self, strip, identifier, code):
-        self.logger = logging.getLogger('comicbox.ucomics.%s' % (strip))
-        self.strip = strip
-        self.identifier = identifier
-        self.code = code
-
-    def name(self):
-        return self.strip
-
-    def home(self):
-        return self._HOME_PAGE % (self.identifier)
-
-    def strip_url(self, date):
-        return self._STRIP_URL % (self.code, date.strftime('%Y'), 
-                                  self.code, date.strftime('%y%m%d'))

lib/united_media.py

-import datetime
-import logging
-import re
-import urllib2
-
-from request_builder import build_request
-
-class UnitedMediaStrip(object):
-    _BASE_URL = 'http://www.unitedmedia.com'
-    _HOME_PAGE = '/comics/%s/'
-    _SEARCH_PAGE = '/comics/%s/archive/%s-%s.html'
-    _SEARCH_PATTERN = '<img .*src="(/comics/%s/archive/images/%s.+?)".*>'
-
-    def __init__(self, strip, identifier):
-        self.logger = logging.getLogger('comicbox.unitedmedia.%s' % (strip))
-        self.strip = strip
-        self.identifier = identifier
-
-    def name(self):
-        return self.strip
-
-    def home(self):
-        home_page = self._HOME_PAGE % (self.identifier)
-        return self._BASE_URL + home_page
-
-    def strip_url(self, date):
-        search_page = self._SEARCH_PAGE % (self.identifier,
-                                           self.identifier,
-                                           date.strftime('%Y%m%d'))
-        search_url = self._BASE_URL + search_page
-        search_pattern = self._SEARCH_PATTERN % (self.identifier,
-                                                 self.identifier)
-        search_regex = re.compile(search_pattern, re.IGNORECASE)
-
-        try:
-            self.logger.debug('requesting search page "%s"' % (search_url))
-            search_req = build_request(search_url)
-            page = urllib2.urlopen(search_req).read()
-            matches = search_regex.search(page)
-            if matches:
-                return self._BASE_URL + matches.group(1)
-            else:
-                self.logger.error('strip url not found')
-                return ''
-        except urllib2.URLError, e:
-            self.logger.error('failed to download search page: %s' % (e))
-            return ''
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.