Completed
Push — master ( 1bb579...51f5c7 )
by De
01:04
created

comics.py (49 issues)

Code
1
#! /usr/bin/python3
2
# vim: set expandtab tabstop=4 shiftwidth=4 :
3
"""Module to retrieve webcomics"""
4
5
from comic_abstract import GenericComic, get_date_for_comic
6
import re
7
from datetime import date, timedelta
8
import datetime
9
from urlfunctions import get_soup_at_url, urljoin_wrapper,\
10
    convert_iri_to_plain_ascii_uri, load_json_at_url, urlopen_wrapper
11
import json
12
import locale
13
import urllib
14
15
DEFAULT_LOCAL = 'en_GB.UTF-8'
16
17
18
class Xkcd(GenericComic):
19
    """Class to retrieve Xkcd comics."""
20
    name = 'xkcd'
21
    long_name = 'xkcd'
22
    url = 'http://xkcd.com'
23
24
    @classmethod
25
    def get_next_comic(cls, last_comic):
26
        """Generator to get the next comic. Implementation of GenericComic's abstract method."""
27
        json_url = urljoin_wrapper(cls.url, 'info.0.json')
28
        first_num = last_comic['num'] if last_comic else 0
29
        last_num = load_json_at_url(json_url)['num']
30
31
        for num in range(first_num + 1, last_num + 1):
32
            comic = cls.get_comic_info(num)
33
            if comic is not None:
34
                yield comic
35
36
    @classmethod
37
    def get_comic_info(cls, num):
38
        """Get information about a particular comics."""
39
        if num == 404:
40
            return None
41
        json_url = urljoin_wrapper(cls.url, '%d/info.0.json' % num)
42
        comic_json = load_json_at_url(json_url)
43
        assert comic_json['num'] == num, json_url
44
        return {
45
            'json_url': json_url,
46
            'num': num,
47
            'url': urljoin_wrapper(cls.url, str(num)),
48
            'prefix': '%d-' % num,
49
            'img': [comic_json['img']],
50
            'day': int(comic_json['day']),
51
            'month': int(comic_json['month']),
52
            'year': int(comic_json['year']),
53
            'link': comic_json['link'],
54
            'news': comic_json['news'],
55
            'safe_title': comic_json['safe_title'],
56
            'transcript': comic_json['transcript'],
57
            'alt': comic_json['alt'],
58
            'title': comic_json['title'],
59
        }
60
61
62
# Helper functions corresponding to get_url_from_link/get_url_from_archive_element
63
64
65
@classmethod
66
def get_href(cls, link):
67
    """Implementation of get_url_from_link/get_url_from_archive_element."""
68
    return link['href']
69
70
71
@classmethod
72
def join_cls_url_to_href(cls, link):
73
    """Implementation of get_url_from_link/get_url_from_archive_element."""
74
    return urljoin_wrapper(cls.url, link['href'])
75
76
77
class GenericNavigableComic(GenericComic):
78
    """Generic class for "navigable" comics : with first/next arrows.
79
80
    This class applies to comic where previous and next comics can be
81
    accessed from a given comic. Once given a starting point (either
82
    the first comic or the last comic retrieved), it will handle the
83
    navigation, the retrieval of the soup object and the setting of
84
    the 'url' attribute on retrieved comics. This limits a lot the
85
    amount of boilerplate code in the different implementation classes.
86
87
    The method `get_next_comic` methods is implemented in terms of new
88
    more specialized methods to be implemented/overridden:
89
        - get_first_comic_link
90
        - get_navi_link
91
        - get_comic_info
92
        - get_url_from_link
93
    """
94
    _categories = ('NAVIGABLE', )
95
96
    @classmethod
97
    def get_first_comic_link(cls):
98
        """Get link to first comics.
99
100
        Sometimes this can be retrieved of any comic page, sometimes on
101
        the archive page, sometimes it doesn't exist at all and one has
102
        to iterate backward to find it before hardcoding the result found.
103
        """
104
        raise NotImplementedError
105
106
    @classmethod
107
    def get_navi_link(cls, last_soup, next_):
108
        """Get link to next (or previous - for dev purposes) comic."""
109
        raise NotImplementedError
110
111
    @classmethod
112
    def get_comic_info(cls, soup, link):
113
        """Get information about a particular comics."""
114
        raise NotImplementedError
115
116
    @classmethod
117
    def get_url_from_link(cls, link):
118
        """Get url corresponding to a link. Default implementation is similar to get_href."""
119
        return link['href']
120
121
    @classmethod
122
    def get_next_link(cls, last_soup):
123
        """Get link to next comic."""
124
        link = cls.get_navi_link(last_soup, True)
125
        cls.log("Next link is %s" % link)
126
        return link
127
128
    @classmethod
129
    def get_prev_link(cls, last_soup):
130
        """Get link to previous comic."""
131
        link = cls.get_navi_link(last_soup, False)
132
        cls.log("Prev link is %s" % link)
133
        return link
134
135
    @classmethod
136
    def get_next_comic(cls, last_comic):
137
        """Generic implementation of get_next_comic for navigable comics."""
138
        url = last_comic['url'] if last_comic else None
139
        cls.log("starting 'get_next_comic' from %s" % url)
140
        next_comic = \
141
            cls.get_next_link(get_soup_at_url(url)) \
142
            if url else \
143
            cls.get_first_comic_link()
144
        cls.log("next/first comic will be %s (url is %s)" % (str(next_comic), url))
145
        # cls.check_navigation(url)
146
        while next_comic:
147
            prev_url, url = url, cls.get_url_from_link(next_comic)
148
            if prev_url == url:
149
                cls.log("got same url %s" % url)
150
                break
151
            cls.log("about to get %s (%s)" % (url, str(next_comic)))
152
            soup = get_soup_at_url(url)
153
            comic = cls.get_comic_info(soup, next_comic)
154
            if comic is not None:
155
                assert 'url' not in comic
156
                comic['url'] = url
157
                yield comic
158
            next_comic = cls.get_next_link(soup)
159
            cls.log("next comic will be %s" % str(next_comic))
160
161
    @classmethod
162
    def check_first_link(cls):
163
        """Check that navigation to first comic seems to be working - for dev purposes."""
164
        cls.log("about to check first link")
165
        ok = True
166
        firstlink = cls.get_first_comic_link()
167
        if firstlink is None:
168
            print("From %s : no first link" % cls.url)
169
            ok = False
170
        else:
171
            firsturl = cls.get_url_from_link(firstlink)
172
            try:
173
                get_soup_at_url(firsturl)
174
            except urllib.error.HTTPError:
175
                print("From %s : invalid first url" % cls.url)
176
                ok = False
177
        cls.log("checked first link -> returned %d" % ok)
178
        return ok
179
180
    @classmethod
181
    def check_prev_next_links(cls, url):
182
        """Check that navigation to prev/next from a given URL seems to be working - for dev purposes."""
183
        cls.log("about to check prev/next from %s" % url)
184
        ok = True
185
        if url is None:
186
            prevlink, nextlink = None, None
187
        else:
188
            soup = get_soup_at_url(url)
189
            prevlink, nextlink = cls.get_prev_link(soup), cls.get_next_link(soup)
190
        if prevlink is None and nextlink is None:
191
            print("From %s : no previous nor next" % url)
192
            ok = False
193
        else:
194
            if prevlink:
195
                prevurl = cls.get_url_from_link(prevlink)
196
                prevsoup = get_soup_at_url(prevurl)
197
                prevnextlink = cls.get_next_link(prevsoup)
198
                prevnext = cls.get_url_from_link(prevnextlink) if prevnextlink is not None else "NO URL"
199
                if prevnext != url:
200
                    print("From %s, going backward then forward leads to %s" % (url, prevnext))
201
                    ok = False
202
            if nextlink:
203
                nexturl = cls.get_url_from_link(nextlink)
204
                if nexturl != url:
205
                    nextsoup = get_soup_at_url(nexturl)
206
                    nextprevlink = cls.get_prev_link(nextsoup)
207
                    nextprev = cls.get_url_from_link(nextprevlink) if nextprevlink is not None else "NO URL"
208
                    if nextprev != url:
209
                        print("From %s, going forward then backward leads to %s" % (url, nextprev))
210
                        ok = False
211
        cls.log("checked prev/next from %s -> returned %d" % (url, ok))
212
        return ok
213
214
    @classmethod
215
    def check_navigation(cls, url):
216
        """Check that navigation functions seem to be working - for dev purposes."""
217
        cls.log("about to check navigation from %s" % url)
218
        first = cls.check_first_link()
219
        prevnext = cls.check_prev_next_links(url)
220
        ok = first and prevnext
221
        cls.log("checked navigation from %s -> returned %d" % (url, ok))
222
        return ok
223
224
225
class GenericListableComic(GenericComic):
226
    """Generic class for "listable" comics : with a list of comics (aka 'archive')
227
228
    The method `get_next_comic` methods is implemented in terms of new
229
    more specialized methods to be implemented/overridden:
230
        - get_archive_elements
231
        - get_url_from_archive_element
232
        - get_comic_info
233
    """
234
    _categories = ('LISTABLE', )
235
236
    @classmethod
237
    def get_archive_elements(cls):
238
        """Get the archive elements (iterable)."""
239
        raise NotImplementedError
240
241
    @classmethod
242
    def get_url_from_archive_element(cls, archive_elt):
243
        """Get url corresponding to an archive element."""
244
        raise NotImplementedError
245
246
    @classmethod
247
    def get_comic_info(cls, soup, archive_elt):
248
        """Get information about a particular comics."""
249
        raise NotImplementedError
250
251
    @classmethod
252
    def get_next_comic(cls, last_comic):
253
        """Generic implementation of get_next_comic for listable comics."""
254
        waiting_for_url = last_comic['url'] if last_comic else None
255
        archive_elts = list(cls.get_archive_elements())
256
        for archive_elt in archive_elts:
257
            url = cls.get_url_from_archive_element(archive_elt)
258
            cls.log("considering %s" % url)
259
            if waiting_for_url is None:
260
                cls.log("about to get %s (%s)" % (url, str(archive_elt)))
261
                soup = get_soup_at_url(url)
262
                comic = cls.get_comic_info(soup, archive_elt)
263
                if comic is not None:
264
                    assert 'url' not in comic
265
                    comic['url'] = url
266
                    yield comic
267
            elif waiting_for_url == url:
268
                waiting_for_url = None
269
        if waiting_for_url is not None:
270
            print("Did not find %s in the %d comics: there might be a problem" %
271
                  (waiting_for_url, len(archive_elts)))
272
273
# Helper functions corresponding to get_first_comic_link/get_navi_link
274
275
276
@classmethod
277
def get_link_rel_next(cls, last_soup, next_):
278
    """Implementation of get_navi_link."""
279
    return last_soup.find('link', rel='next' if next_ else 'prev')
280
281
282
@classmethod
283
def get_a_rel_next(cls, last_soup, next_):
284
    """Implementation of get_navi_link."""
285
    return last_soup.find('a', rel='next' if next_ else 'prev')
286
287
288
@classmethod
289
def get_a_navi_navinext(cls, last_soup, next_):
290
    """Implementation of get_navi_link."""
291
    # ComicPress (WordPress plugin)
292
    return last_soup.find('a', class_='navi navi-next' if next_ else 'navi navi-prev')
293
294
295
@classmethod
296
def get_a_navi_comicnavnext_navinext(cls, last_soup, next_):
297
    """Implementation of get_navi_link."""
298
    return last_soup.find('a', class_='navi comic-nav-next navi-next' if next_ else 'navi comic-nav-previous navi-prev')
299
300
301
@classmethod
302
def get_a_comicnavbase_comicnavnext(cls, last_soup, next_):
303
    """Implementation of get_navi_link."""
304
    return last_soup.find('a', class_='comic-nav-base comic-nav-next' if next_ else 'comic-nav-base comic-nav-previous')
305
306
307
@classmethod
308
def get_a_navi_navifirst(cls):
309
    """Implementation of get_first_comic_link."""
310
    # ComicPress (WordPress plugin)
311
    return get_soup_at_url(cls.url).find('a', class_='navi navi-first')
312
313
314
@classmethod
315
def get_div_navfirst_a(cls):
316
    """Implementation of get_first_comic_link."""
317
    return get_soup_at_url(cls.url).find('div', class_="nav-first").find('a')
318
319
320
@classmethod
321
def get_a_comicnavbase_comicnavfirst(cls):
322
    """Implementation of get_first_comic_link."""
323
    return get_soup_at_url(cls.url).find('a', class_='comic-nav-base comic-nav-first')
324
325
326
@classmethod
327
def simulate_first_link(cls):
328
    """Implementation of get_first_comic_link creating a link-like object from
329
    an URL provided by the class.
330
331
    Note: The first URL can easily be found using :
332
    `get_first_comic_link = navigate_to_first_comic`.
333
    """
334
    return {'href': cls.first_url}
335
336
337
@classmethod
338
def navigate_to_first_comic(cls):
339
    """Implementation of get_first_comic_link navigating from a user provided
340
    URL to the first comic.
341
342
    Sometimes, the first comic cannot be reached directly so to start
343
    from the first comic one has to go to the previous comic until
344
    there is no previous comics. Once this URL is reached, it
345
    is better to hardcode it but for development purposes, it
346
    is convenient to have an automatic way to find it.
347
348
    Then, the URL found can easily be used via `simulate_first_link`.
349
    """
350
    try:
351
        url = cls.first_url
352
    except AttributeError:
353
        url = input("Get starting URL: ")
354
    print(url)
355
    comic = cls.get_prev_link(get_soup_at_url(url))
356
    while comic:
357
        url = cls.get_url_from_link(comic)
358
        print(url)
359
        comic = cls.get_prev_link(get_soup_at_url(url))
360
    return {'href': url}
361
362
363
class GenericEmptyComic(GenericComic):
364
    """Generic class for comics where nothing is to be done.
365
366
    It can be useful to deactivate temporarily comics that do not work
367
    properly by replacing `def MyComic(GenericWhateverComic)` with
368
    `def MyComic(GenericEmptyComic, GenericWhateverComic)`."""
369
    _categories = ('EMPTY', )
370
371
    @classmethod
372
    def get_next_comic(cls, last_comic):
373
        """Implementation of get_next_comic returning no comics."""
374
        cls.log("comic is considered as empty - returning no comic")
375
        return []
376
377
378
class GenericComicNotWorking(GenericEmptyComic):
379
    """Subclass of GenericEmptyComic used when comic is not working.
380
381
    This is more explicit than GenericEmptyComic as it hilights that
382
    only the implementation is not working and it can be fixed."""
383
    _categories = ('NOTWORKING', )
384
385
386
class GenericUnavailableComic(GenericEmptyComic):
387
    """Subclass of GenericEmptyComic used when a comic is not available.
388
389
    This is more explicit than GenericEmptyComic as it hilights that
390
    the source of the comic is not available but we expect it to be back
391
    soonish. See also GenericDeletedComic."""
392
    _categories = ('UNAVAILABLE', )
393
394
395
class GenericDeletedComic(GenericEmptyComic):
396
    """Subclass of GenericEmptyComic used when a comic does not exist anymore.
397
398
    This is more explicit than GenericEmptyComic as it hilights that
399
    the source of the comic does not exist anymore and it probably cannot
400
    be fixed. Corresponding classes are kept as we can still use the
401
    downloaded data. See also GenericUnavailableComic."""
402
    _categories = ('DELETED', )
403
404
405 View Code Duplication
class ExtraFabulousComics(GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
406
    """Class to retrieve Extra Fabulous Comics."""
407
    # Also on https://extrafabulouscomics.tumblr.com
408
    name = 'efc'
409
    long_name = 'Extra Fabulous Comics'
410
    url = 'http://extrafabulouscomics.com'
411
    _categories = ('EFC', )
412
    get_navi_link = get_link_rel_next
413
    get_first_comic_link = simulate_first_link
414
    first_url = 'http://extrafabulouscomics.com/comic/buttfly/'
415
416
    @classmethod
417
    def get_comic_info(cls, soup, link):
418
        """Get information about a particular comics."""
419
        img_src_re = re.compile('^%s/wp-content/uploads/' % cls.url)
420
        imgs = soup.find_all('img', src=img_src_re)
421
        title = soup.find('meta', property='og:title')['content']
422
        date_str = soup.find('meta', property='article:published_time')['content'][:10]
423
        day = string_to_date(date_str, "%Y-%m-%d")
424
        return {
425
            'title': title,
426
            'img': [i['src'] for i in imgs],
427
            'month': day.month,
428
            'year': day.year,
429
            'day': day.day,
430
            'prefix': title + '-'
431
        }
432
433
434 View Code Duplication
class GenericLeMondeBlog(GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
435
    """Generic class to retrieve comics from Le Monde blogs."""
436
    _categories = ('LEMONDE', 'FRANCAIS')
437
    get_navi_link = get_link_rel_next
438
    get_first_comic_link = simulate_first_link
439
    first_url = NotImplemented
440
441
    @classmethod
442
    def get_comic_info(cls, soup, link):
443
        """Get information about a particular comics."""
444
        url2 = soup.find('link', rel='shortlink')['href']
445
        title = soup.find('meta', property='og:title')['content']
446
        date_str = soup.find("span", class_="entry-date").string
447
        day = string_to_date(date_str, "%d %B %Y", "fr_FR.utf8")
448
        imgs = soup.find_all('meta', property='og:image')
449
        return {
450
            'title': title,
451
            'url2': url2,
452
            'img': [convert_iri_to_plain_ascii_uri(i['content']) for i in imgs],
453
            'month': day.month,
454
            'year': day.year,
455
            'day': day.day,
456
        }
457
458
459
class ZepWorld(GenericLeMondeBlog):
460
    """Class to retrieve Zep World comics."""
461
    name = "zep"
462
    long_name = "Zep World"
463
    url = "http://zepworld.blog.lemonde.fr"
464
    first_url = "http://zepworld.blog.lemonde.fr/2014/10/31/bientot-le-blog-de-zep/"
465
466
467
class Vidberg(GenericLeMondeBlog):
468
    """Class to retrieve Vidberg comics."""
469
    name = 'vidberg'
470
    long_name = "Vidberg - l'actu en patates"
471
    url = "http://vidberg.blog.lemonde.fr"
472
    # Not the first but I didn't find an efficient way to retrieve it
473
    first_url = "http://vidberg.blog.lemonde.fr/2012/02/09/revue-de-campagne-la-campagne-du-modem-semballe/"
474
475
476
class Plantu(GenericLeMondeBlog):
477
    """Class to retrieve Plantu comics."""
478
    name = 'plantu'
479
    long_name = "Plantu"
480
    url = "http://plantu.blog.lemonde.fr"
481
    first_url = "http://plantu.blog.lemonde.fr/2014/10/28/stress-test-a-bruxelles/"
482
483
484
class XavierGorce(GenericLeMondeBlog):
485
    """Class to retrieve Xavier Gorce comics."""
486
    name = 'gorce'
487
    long_name = "Xavier Gorce"
488
    url = "http://xaviergorce.blog.lemonde.fr"
489
    first_url = "http://xaviergorce.blog.lemonde.fr/2015/01/09/distinction/"
490
491
492
class CartooningForPeace(GenericLeMondeBlog):
493
    """Class to retrieve Cartooning For Peace comics."""
494
    name = 'forpeace'
495
    long_name = "Cartooning For Peace"
496
    url = "http://cartooningforpeace.blog.lemonde.fr"
497
    first_url = "http://cartooningforpeace.blog.lemonde.fr/2014/12/15/bado/"
498
499
500
class Aurel(GenericLeMondeBlog):
501
    """Class to retrieve Aurel comics."""
502
    name = 'aurel'
503
    long_name = "Aurel"
504
    url = "http://aurel.blog.lemonde.fr"
505
    first_url = "http://aurel.blog.lemonde.fr/2014/09/29/le-senat-repasse-a-droite/"
506
507
508
class LesCulottees(GenericLeMondeBlog):
509
    """Class to retrieve Les Culottees comics."""
510
    name = 'culottees'
511
    long_name = 'Les Culottees'
512
    url = "http://lesculottees.blog.lemonde.fr"
513
    first_url = "http://lesculottees.blog.lemonde.fr/2016/01/11/clementine-delait-femme-a-barbe/"
514
515
516
class UneAnneeAuLycee(GenericLeMondeBlog):
517
    """Class to retrieve Une Annee Au Lycee comics."""
518
    name = 'lycee'
519
    long_name = 'Une Annee au Lycee'
520
    url = 'http://uneanneeaulycee.blog.lemonde.fr'
521
    first_url = "http://uneanneeaulycee.blog.lemonde.fr/2016/06/13/la-semaine-du-bac-est-arrivee/"
522
523
524 View Code Duplication
class Rall(GenericComicNotWorking, GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
525
    """Class to retrieve Ted Rall comics."""
526
    # Also on http://www.gocomics.com/tedrall
527
    name = 'rall'
528
    long_name = "Ted Rall"
529
    url = "http://rall.com/comic"
530
    _categories = ('RALL', )
531
    get_navi_link = get_link_rel_next
532
    get_first_comic_link = simulate_first_link
533
    # Not the first but I didn't find an efficient way to retrieve it
534
    first_url = "http://rall.com/2014/01/30/los-angeles-times-cartoon-well-miss-those-california-flowers"
535
536
    @classmethod
537
    def get_comic_info(cls, soup, link):
538
        """Get information about a particular comics."""
539
        title = soup.find('meta', property='og:title')['content']
540
        author = soup.find("span", class_="author vcard").find("a").string
541
        date_str = soup.find("span", class_="entry-date").string
542
        day = string_to_date(date_str, "%B %d, %Y")
543
        desc = soup.find('meta', property='og:description')['content']
544
        imgs = soup.find('div', class_='entry-content').find_all('img')
545
        imgs = imgs[:-7]  # remove social media buttons
546
        return {
547
            'title': title,
548
            'author': author,
549
            'month': day.month,
550
            'year': day.year,
551
            'day': day.day,
552
            'description': desc,
553
            'img': [i['src'] for i in imgs],
554
        }
555
556
557
class Dilem(GenericNavigableComic):
558
    """Class to retrieve Ali Dilem comics."""
559
    name = 'dilem'
560
    long_name = 'Ali Dilem'
561
    url = 'http://information.tv5monde.com/dilem'
562
    _categories = ('FRANCAIS', )
563
    get_url_from_link = join_cls_url_to_href
564
    get_first_comic_link = simulate_first_link
565
    first_url = "http://information.tv5monde.com/dilem/2004-06-26"
566
567
    @classmethod
568
    def get_navi_link(cls, last_soup, next_):
569
        """Get link to next or previous comic."""
570
        # prev is next / next is prev
571
        li = last_soup.find('li', class_='prev' if next_ else 'next')
572
        return li.find('a') if li else None
573
574
    @classmethod
575
    def get_comic_info(cls, soup, link):
576
        """Get information about a particular comics."""
577
        short_url = soup.find('link', rel='shortlink')['href']
578
        title = soup.find('meta', attrs={'name': 'twitter:title'})['content']
579
        imgs = soup.find_all('meta', property='og:image')
580
        date_str = soup.find('span', property='dc:date')['content']
581
        date_str = date_str[:10]
582
        day = string_to_date(date_str, "%Y-%m-%d")
583
        return {
584
            'short_url': short_url,
585
            'title': title,
586
            'img': [i['content'] for i in imgs],
587
            'day': day.day,
588
            'month': day.month,
589
            'year': day.year,
590
        }
591
592
593
class SpaceAvalanche(GenericNavigableComic):
594
    """Class to retrieve Space Avalanche comics."""
595
    name = 'avalanche'
596
    long_name = 'Space Avalanche'
597
    url = 'http://www.spaceavalanche.com'
598
    get_navi_link = get_link_rel_next
599
600
    @classmethod
601
    def get_first_comic_link(cls):
602
        """Get link to first comics."""
603
        return {'href': "http://www.spaceavalanche.com/2009/02/02/irish-sea/", 'title': "Irish Sea"}
604
605
    @classmethod
606
    def get_comic_info(cls, soup, link):
607
        """Get information about a particular comics."""
608
        url_date_re = re.compile('.*/([0-9]*)/([0-9]*)/([0-9]*)/.*$')
609
        title = link['title']
610
        url = cls.get_url_from_link(link)
611
        year, month, day = [int(s)
612
                            for s in url_date_re.match(url).groups()]
613
        imgs = soup.find("div", class_="entry").find_all("img")
614
        return {
615
            'title': title,
616
            'day': day,
617
            'month': month,
618
            'year': year,
619
            'img': [i['src'] for i in imgs],
620
        }
621
622
623
class ZenPencils(GenericNavigableComic):
624
    """Class to retrieve ZenPencils comics."""
625
    # Also on http://zenpencils.tumblr.com
626
    # Also on http://www.gocomics.com/zen-pencils
627
    name = 'zenpencils'
628
    long_name = 'Zen Pencils'
629
    url = 'http://zenpencils.com'
630
    _categories = ('ZENPENCILS', )
631
    get_navi_link = get_link_rel_next
632
    get_first_comic_link = simulate_first_link
633
    first_url = "http://zenpencils.com/comic/1-ralph-waldo-emerson-make-them-cry/"
634
635
    @classmethod
636
    def get_comic_info(cls, soup, link):
637
        """Get information about a particular comics."""
638
        imgs = soup.find('div', id='comic').find_all('img')
639
        # imgs2 = soup.find_all('meta', property='og:image')
640
        post = soup.find('div', class_='post-content')
641
        author = post.find("span", class_="post-author").find("a").string
642
        title = soup.find('h2', class_='post-title').string
643
        date_str = post.find('span', class_='post-date').string
644
        day = string_to_date(date_str, "%B %d, %Y")
645
        assert imgs
646
        assert all(i['alt'] == i['title'] for i in imgs)
647
        assert all(i['alt'] in (title, "") for i in imgs)
648
        return {
649
            'title': title,
650
            'author': author,
651
            'day': day.day,
652
            'month': day.month,
653
            'year': day.year,
654
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
655
        }
656
657
658
class ItsTheTie(GenericDeletedComic, GenericNavigableComic):
659
    """Class to retrieve It's the tie comics."""
660
    # Also on http://itsthetie.tumblr.com
661
    # Also on https://tapastic.com/series/itsthetie
662
    name = 'tie'
663
    long_name = "It's the tie"
664
    url = "http://itsthetie.com"
665
    _categories = ('TIE', )
666
    get_first_comic_link = get_div_navfirst_a
667
    get_navi_link = get_a_rel_next
668
669
    @classmethod
670
    def get_comic_info(cls, soup, link):
671
        """Get information about a particular comics."""
672
        title = soup.find('h1', class_='comic-title').find('a').string
673
        date_str = soup.find('header', class_='comic-meta entry-meta').find('a').string
674
        day = string_to_date(date_str, "%B %d, %Y")
675
        # Bonus images may or may not be in meta og:image.
676
        imgs = soup.find_all('meta', property='og:image')
677
        imgs_src = [i['content'] for i in imgs]
678
        bonus = soup.find_all('img', attrs={'data-oversrc': True})
679
        bonus_src = [b['data-oversrc'] for b in bonus]
680
        all_imgs_src = imgs_src + [s for s in bonus_src if s not in imgs_src]
681
        all_imgs_src = [s for s in all_imgs_src if not s.endswith("/2016/01/bonus-panel.png")]
682
        tag_meta = soup.find('meta', property='article:tag')
683
        tags = tag_meta['content'] if tag_meta else ""
684
        return {
685
            'title': title,
686
            'month': day.month,
687
            'year': day.year,
688
            'day': day.day,
689
            'img': all_imgs_src,
690
            'tags': tags,
691
        }
692
693
694 View Code Duplication
class PenelopeBagieu(GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
695
    """Class to retrieve comics from Penelope Bagieu's blog."""
696
    name = 'bagieu'
697
    long_name = 'Ma vie est tout a fait fascinante (Bagieu)'
698
    url = 'http://www.penelope-jolicoeur.com'
699
    _categories = ('FRANCAIS', )
700
    get_navi_link = get_link_rel_next
701
    get_first_comic_link = simulate_first_link
702
    first_url = 'http://www.penelope-jolicoeur.com/2007/02/ma-vie-mon-oeuv.html'
703
704
    @classmethod
705
    def get_comic_info(cls, soup, link):
706
        """Get information about a particular comics."""
707
        date_str = soup.find('h2', class_='date-header').string
708
        day = string_to_date(date_str, "%A %d %B %Y", "fr_FR.utf8")
709
        imgs = soup.find('div', class_='entry-body').find_all('img')
710
        title = soup.find('h3', class_='entry-header').string
711
        return {
712
            'title': title,
713
            'img': [i['src'] for i in imgs],
714
            'month': day.month,
715
            'year': day.year,
716
            'day': day.day,
717
        }
718
719
720 View Code Duplication
class OneOneOneOneComic(GenericComicNotWorking, GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
721
    """Class to retrieve 1111 Comics."""
722
    # Also on http://comics1111.tumblr.com
723
    # Also on https://tapastic.com/series/1111-Comics
724
    name = '1111'
725
    long_name = '1111 Comics'
726
    url = 'http://www.1111comics.me'
727
    _categories = ('ONEONEONEONE', )
728
    get_first_comic_link = get_div_navfirst_a
729
    get_navi_link = get_link_rel_next
730
731
    @classmethod
732
    def get_comic_info(cls, soup, link):
733
        """Get information about a particular comics."""
734
        title = soup.find('h1', class_='comic-title').find('a').string
735
        date_str = soup.find('header', class_='comic-meta entry-meta').find('a').string
736
        day = string_to_date(date_str, "%B %d, %Y")
737
        imgs = soup.find_all('meta', property='og:image')
738
        return {
739
            'title': title,
740
            'month': day.month,
741
            'year': day.year,
742
            'day': day.day,
743
            'img': [i['content'] for i in imgs],
744
        }
745
746
747 View Code Duplication
class AngryAtNothing(GenericDeletedComic, GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
748
    """Class to retrieve Angry at Nothing comics."""
749
    # Also on http://tapastic.com/series/Comics-yeah-definitely-comics-
750
    # Also on http://angryatnothing.tumblr.com
751
    name = 'angry'
752
    long_name = 'Angry At Nothing'
753
    url = 'http://www.angryatnothing.net'
754
    get_first_comic_link = get_div_navfirst_a
755
    get_navi_link = get_a_rel_next
756
757
    @classmethod
758
    def get_comic_info(cls, soup, link):
759
        """Get information about a particular comics."""
760
        title = soup.find('h1', class_='comic-title').find('a').string
761
        date_str = soup.find('header', class_='comic-meta entry-meta').find('a').string
762
        day = string_to_date(date_str, "%B %d, %Y")
763
        imgs = soup.find_all('meta', property='og:image')
764
        return {
765
            'title': title,
766
            'month': day.month,
767
            'year': day.year,
768
            'day': day.day,
769
            'img': [i['content'] for i in imgs],
770
        }
771
772
773
class NeDroid(GenericNavigableComic):
774
    """Class to retrieve NeDroid comics."""
775
    name = 'nedroid'
776
    long_name = 'NeDroid'
777
    url = 'http://nedroid.com'
778
    get_first_comic_link = get_div_navfirst_a
779
    get_navi_link = get_link_rel_next
780
    get_url_from_link = join_cls_url_to_href
781
782 View Code Duplication
    @classmethod
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
783
    def get_comic_info(cls, soup, link):
784
        """Get information about a particular comics."""
785
        short_url_re = re.compile('^%s/\\?p=([0-9]*)' % cls.url)
786
        short_url = cls.get_url_from_link(soup.find('link', rel='shortlink'))
787
        num = int(short_url_re.match(short_url).groups()[0])
788
        imgs = soup.find('div', id='comic').find_all('img')
789
        assert len(imgs) == 1, imgs
790
        title = imgs[0]['alt']
791
        title2 = imgs[0]['title']
792
        return {
793
            'short_url': short_url,
794
            'title': title,
795
            'title2': title2,
796
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
797
            'num': num,
798
        }
799
800
801
class Garfield(GenericNavigableComic):
802
    """Class to retrieve Garfield comics."""
803
    # Also on http://www.gocomics.com/garfield
804
    name = 'garfield'
805
    long_name = 'Garfield'
806
    url = 'https://garfield.com'
807
    _categories = ('GARFIELD', )
808
    get_first_comic_link = simulate_first_link
809
    first_url = 'https://garfield.com/comic/1978/06/19'
810
811
    @classmethod
812
    def get_navi_link(cls, last_soup, next_):
813
        """Get link to next or previous comic."""
814
        return last_soup.find('a', class_='comic-arrow-right' if next_ else 'comic-arrow-left')
815
816
    @classmethod
817
    def get_comic_info(cls, soup, link):
818
        """Get information about a particular comics."""
819
        url = cls.get_url_from_link(link)
820
        date_re = re.compile('^%s/comic/([0-9]*)/([0-9]*)/([0-9]*)' % cls.url)
821
        year, month, day = [int(s) for s in date_re.match(url).groups()]
822
        imgs = soup.find('div', class_='comic-display').find_all('img', class_='img-responsive')
823
        return {
824
            'month': month,
825
            'year': year,
826
            'day': day,
827
            'img': [i['src'] for i in imgs],
828
        }
829
830
831 View Code Duplication
class Dilbert(GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
832
    """Class to retrieve Dilbert comics."""
833
    # Also on http://www.gocomics.com/dilbert-classics
834
    name = 'dilbert'
835
    long_name = 'Dilbert'
836
    url = 'http://dilbert.com'
837
    get_url_from_link = join_cls_url_to_href
838
    get_first_comic_link = simulate_first_link
839
    first_url = 'http://dilbert.com/strip/1989-04-16'
840
841
    @classmethod
842
    def get_navi_link(cls, last_soup, next_):
843
        """Get link to next or previous comic."""
844
        link = last_soup.find('div', class_='nav-comic nav-right' if next_ else 'nav-comic nav-left')
845
        return link.find('a') if link else None
846
847
    @classmethod
848
    def get_comic_info(cls, soup, link):
849
        """Get information about a particular comics."""
850
        title = soup.find('meta', property='og:title')['content']
851
        imgs = soup.find_all('meta', property='og:image')
852
        desc = soup.find('meta', property='og:description')['content']
853
        date_str = soup.find('meta', property='article:publish_date')['content']
854
        day = string_to_date(date_str, "%B %d, %Y")
855
        author = soup.find('meta', property='article:author')['content']
856
        tags = soup.find('meta', property='article:tag')['content']
857
        return {
858
            'title': title,
859
            'description': desc,
860
            'img': [i['content'] for i in imgs],
861
            'author': author,
862
            'tags': tags,
863
            'day': day.day,
864
            'month': day.month,
865
            'year': day.year
866
        }
867
868
869
class VictimsOfCircumsolar(GenericDeletedComic, GenericNavigableComic):
870
    """Class to retrieve VictimsOfCircumsolar comics."""
871
    # Also on https://victimsofcomics.tumblr.com
872
    name = 'circumsolar'
873
    long_name = 'Victims Of Circumsolar'
874
    url = 'http://www.victimsofcircumsolar.com'
875
    get_navi_link = get_a_navi_comicnavnext_navinext
876
    get_first_comic_link = simulate_first_link
877
    first_url = 'http://www.victimsofcircumsolar.com/comic/modern-addiction'
878
879
    @classmethod
880
    def get_comic_info(cls, soup, link):
881
        """Get information about a particular comics."""
882
        # Date is on the archive page
883
        title = soup.find_all('meta', property='og:title')[-1]['content']
884
        desc = soup.find_all('meta', property='og:description')[-1]['content']
885
        imgs = soup.find('div', id='comic').find_all('img')
886
        assert all(i['title'] == i['alt'] == title for i in imgs)
887
        return {
888
            'title': title,
889
            'description': desc,
890
            'img': [i['src'] for i in imgs],
891
        }
892
893
894
class ThreeWordPhrase(GenericNavigableComic):
895
    """Class to retrieve Three Word Phrase comics."""
896
    # Also on http://www.threewordphrase.tumblr.com
897
    name = 'threeword'
898
    long_name = 'Three Word Phrase'
899
    url = 'http://threewordphrase.com'
900
    get_url_from_link = join_cls_url_to_href
901
902
    @classmethod
903
    def get_first_comic_link(cls):
904
        """Get link to first comics."""
905
        return get_soup_at_url(cls.url).find('img', src='/firstlink.gif').parent
906
907
    @classmethod
908
    def get_navi_link(cls, last_soup, next_):
909
        """Get link to next or previous comic."""
910
        link = last_soup.find('img', src='/nextlink.gif' if next_ else '/prevlink.gif').parent
911
        return None if link.get('href') is None else link
912
913
    @classmethod
914
    def get_comic_info(cls, soup, link):
915
        """Get information about a particular comics."""
916
        title = soup.find('title')
917
        imgs = [img for img in soup.find_all('img')
918
                if not img['src'].endswith(
919
                    ('link.gif', '32.png', 'twpbookad.jpg',
920
                     'merchad.jpg', 'header.gif', 'tipjar.jpg'))]
921
        return {
922
            'title': title.string if title else None,
923
            'title2': '  '.join(img.get('alt') for img in imgs if img.get('alt')),
924
            'img': [urljoin_wrapper(cls.url, img['src']) for img in imgs],
925
        }
926
927
928
class DeadlyPanel(GenericComicNotWorking, GenericNavigableComic):  # Not working on my machine
929
    """Class to retrieve Deadly Panel comics."""
930
    # Also on https://tapastic.com/series/deadlypanel
931
    # Also on https://deadlypanel.tumblr.com
932
    name = 'deadly'
933
    long_name = 'Deadly Panel'
934
    url = 'http://www.deadlypanel.com'
935
    get_first_comic_link = get_a_navi_navifirst
936
    get_navi_link = get_a_navi_comicnavnext_navinext
937
938
    @classmethod
939
    def get_comic_info(cls, soup, link):
940
        """Get information about a particular comics."""
941
        imgs = soup.find('div', id='comic').find_all('img')
942
        assert all(i['alt'] == i['title'] for i in imgs)
943
        return {
944
            'img': [i['src'] for i in imgs],
945
        }
946
947
948 View Code Duplication
class TheGentlemanArmchair(GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
949
    """Class to retrieve The Gentleman Armchair comics."""
950
    name = 'gentlemanarmchair'
951
    long_name = 'The Gentleman Armchair'
952
    url = 'http://thegentlemansarmchair.com'
953
    get_first_comic_link = get_a_navi_navifirst
954
    get_navi_link = get_link_rel_next
955
956
    @classmethod
957
    def get_comic_info(cls, soup, link):
958
        """Get information about a particular comics."""
959
        title = soup.find('h2', class_='post-title').string
960
        author = soup.find("span", class_="post-author").find("a").string
961
        date_str = soup.find('span', class_='post-date').string
962
        day = string_to_date(date_str, "%B %d, %Y")
963
        imgs = soup.find('div', id='comic').find_all('img')
964
        return {
965
            'img': [i['src'] for i in imgs],
966
            'title': title,
967
            'author': author,
968
            'month': day.month,
969
            'year': day.year,
970
            'day': day.day,
971
        }
972
973
974
class ImogenQuest(GenericNavigableComic):
975
    """Class to retrieve Imogen Quest comics."""
976
    # Also on http://imoquest.tumblr.com
977
    name = 'imogen'
978
    long_name = 'Imogen Quest'
979
    url = 'http://imogenquest.net'
980
    get_first_comic_link = get_div_navfirst_a
981
    get_navi_link = get_a_rel_next
982
983
    @classmethod
984
    def get_comic_info(cls, soup, link):
985
        """Get information about a particular comics."""
986
        title = soup.find('h2', class_='post-title').string
987
        author = soup.find("span", class_="post-author").find("a").string
988
        date_str = soup.find('span', class_='post-date').string
989
        day = string_to_date(date_str, '%B %d, %Y')
990
        imgs = soup.find('div', class_='comicpane').find_all('img')
991
        assert all(i['alt'] == i['title'] for i in imgs)
992
        title2 = imgs[0]['title']
993
        return {
994
            'day': day.day,
995
            'month': day.month,
996
            'year': day.year,
997
            'img': [i['src'] for i in imgs],
998
            'title': title,
999
            'title2': title2,
1000
            'author': author,
1001
        }
1002
1003
1004 View Code Duplication
class MyExtraLife(GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
1005
    """Class to retrieve My Extra Life comics."""
1006
    name = 'extralife'
1007
    long_name = 'My Extra Life'
1008
    url = 'http://www.myextralife.com'
1009
    get_navi_link = get_link_rel_next
1010
1011
    @classmethod
1012
    def get_first_comic_link(cls):
1013
        """Get link to first comics."""
1014
        return get_soup_at_url(cls.url).find('a', class_='comic_nav_link first_comic_link')
1015
1016
    @classmethod
1017
    def get_comic_info(cls, soup, link):
1018
        """Get information about a particular comics."""
1019
        title = soup.find("h1", class_="comic_title").string
1020
        date_str = soup.find("span", class_="comic_date").string
1021
        day = string_to_date(date_str, "%B %d, %Y")
1022
        imgs = soup.find_all("img", class_="comic")
1023
        assert all(i['alt'] == i['title'] == title for i in imgs)
1024
        return {
1025
            'title': title,
1026
            'img': [i['src'] for i in imgs if i["src"]],
1027
            'day': day.day,
1028
            'month': day.month,
1029
            'year': day.year
1030
        }
1031
1032
1033
class SaturdayMorningBreakfastCereal(GenericNavigableComic):
1034
    """Class to retrieve Saturday Morning Breakfast Cereal comics."""
1035
    # Also on http://www.gocomics.com/saturday-morning-breakfast-cereal
1036
    # Also on http://smbc-comics.tumblr.com
1037
    name = 'smbc'
1038
    long_name = 'Saturday Morning Breakfast Cereal'
1039
    url = 'http://www.smbc-comics.com'
1040
    _categories = ('SMBC', )
1041
    get_navi_link = get_a_rel_next
1042
1043
    @classmethod
1044
    def get_first_comic_link(cls):
1045
        """Get link to first comics."""
1046
        return get_soup_at_url(cls.url).find('a', rel='start')
1047
1048
    @classmethod
1049
    def get_comic_info(cls, soup, link):
1050
        """Get information about a particular comics."""
1051
        image1 = soup.find('img', id='cc-comic')
1052
        image_url1 = image1['src']
1053
        aftercomic = soup.find('div', id='aftercomic')
1054
        image_url2 = aftercomic.find('img')['src'] if aftercomic else ''
1055
        imgs = [image_url1] + ([image_url2] if image_url2 else [])
1056
        date_str = soup.find('div', class_='cc-publishtime').contents[0]
1057
        day = string_to_date(date_str, "%B %d, %Y")
1058
        return {
1059
            'title': image1['title'],
1060
            'img': [convert_iri_to_plain_ascii_uri(urljoin_wrapper(cls.url, i)) for i in imgs],
1061
            'day': day.day,
1062
            'month': day.month,
1063
            'year': day.year
1064
        }
1065
1066
1067 View Code Duplication
class PerryBibleFellowship(GenericListableComic):  # Is now navigable too
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
1068
    """Class to retrieve Perry Bible Fellowship comics."""
1069
    name = 'pbf'
1070
    long_name = 'Perry Bible Fellowship'
1071
    url = 'http://pbfcomics.com'
1072
    get_url_from_archive_element = join_cls_url_to_href
1073
1074
    @classmethod
1075
    def get_archive_elements(cls):
1076
        soup = get_soup_at_url(cls.url)
1077
        thumbnails = soup.find('div', id='all_thumbnails')
1078
        return reversed(thumbnails.find_all('a'))
1079
1080
    @classmethod
1081
    def get_comic_info(cls, soup, link):
1082
        """Get information about a particular comics."""
1083
        name = soup.find('meta', property='og:title')['content']
1084
        imgs = soup.find_all('meta', property='og:image')
1085
        assert len(imgs) == 1, imgs
1086
        return {
1087
            'name': name,
1088
            'img': [i['content'] for i in imgs],
1089
        }
1090
1091
1092 View Code Duplication
class Mercworks(GenericDeletedComic):  # Moved to Webtoons
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
1093
    """Class to retrieve Mercworks comics."""
1094
    # Also on http://mercworks.tumblr.com
1095
    # Also on http://www.webtoons.com/en/comedy/mercworks/list?title_no=426
1096
    # Also on https://tapastic.com/series/MercWorks
1097
    name = 'mercworks'
1098
    long_name = 'Mercworks'
1099
    url = 'http://mercworks.net'
1100
    _categories = ('MERCWORKS', )
1101
    get_first_comic_link = get_a_comicnavbase_comicnavfirst
1102
    get_navi_link = get_link_rel_next
1103
1104
    @classmethod
1105
    def get_comic_info(cls, soup, link):
1106
        """Get information about a particular comics."""
1107
        title = soup.find('meta', property='og:title')['content']
1108
        metadesc = soup.find('meta', property='og:description')
1109
        desc = metadesc['content'] if metadesc else ""
1110
        date_str = soup.find('meta', property='article:published_time')['content'][:10]
1111
        day = string_to_date(date_str, "%Y-%m-%d")
1112
        imgs = soup.find_all('meta', property='og:image')
1113
        return {
1114
            'img': [i['content'] for i in imgs],
1115
            'title': title,
1116
            'desc': desc,
1117
            'day': day.day,
1118
            'month': day.month,
1119
            'year': day.year
1120
        }
1121
1122
1123
class BerkeleyMews(GenericListableComic):
1124
    """Class to retrieve Berkeley Mews comics."""
1125
    # Also on http://mews.tumblr.com
1126
    # Also on http://www.gocomics.com/berkeley-mews
1127
    name = 'berkeley'
1128
    long_name = 'Berkeley Mews'
1129
    url = 'http://www.berkeleymews.com'
1130
    _categories = ('BERKELEY', )
1131
    get_url_from_archive_element = get_href
1132
    comic_num_re = re.compile('%s/\\?p=([0-9]*)$' % url)
1133
1134
    @classmethod
1135
    def get_archive_elements(cls):
1136
        archive_url = urljoin_wrapper(cls.url, "?page_id=2")
1137
        return reversed(get_soup_at_url(archive_url).find_all('a', href=cls.comic_num_re))
1138
1139
    @classmethod
1140
    def get_comic_info(cls, soup, link):
1141
        """Get information about a particular comics."""
1142
        comic_date_re = re.compile('.*/([0-9]*)-([0-9]*)-([0-9]*)-.*')
1143
        url = cls.get_url_from_archive_element(link)
1144
        num = int(cls.comic_num_re.match(url).groups()[0])
1145
        img = soup.find('div', id='comic').find('img')
1146
        assert all(i['alt'] == i['title'] for i in [img])
1147
        title2 = img['title']
1148
        img_url = img['src']
1149
        year, month, day = [int(s) for s in comic_date_re.match(img_url).groups()]
1150
        return {
1151
            'num': num,
1152
            'title': link.string,
1153
            'title2': title2,
1154
            'img': [img_url],
1155
            'year': year,
1156
            'month': month,
1157
            'day': day,
1158
        }
1159
1160
1161
class GenericBouletCorp(GenericNavigableComic):
1162
    """Generic class to retrieve BouletCorp comics in different languages."""
1163
    # Also on https://bouletcorp.tumblr.com
1164
    _categories = ('BOULET', )
1165
    get_navi_link = get_link_rel_next
1166
1167
    @classmethod
1168
    def get_first_comic_link(cls):
1169
        """Get link to first comics."""
1170
        return get_soup_at_url(cls.url).find('div', id='centered_nav').find_all('a')[0]
1171
1172
    @classmethod
1173
    def get_comic_info(cls, soup, link):
1174
        """Get information about a particular comics."""
1175
        url = cls.get_url_from_link(link)
1176
        date_re = re.compile('^%s/([0-9]*)/([0-9]*)/([0-9]*)/' % cls.url)
1177
        year, month, day = [int(s) for s in date_re.match(url).groups()]
1178
        imgs = soup.find('div', id='notes').find('div', class_='storycontent').find_all('img')
1179
        texts = '  '.join(t for t in (i.get('title') for i in imgs) if t)
1180
        title = soup.find('title').string
1181
        return {
1182
            'img': [convert_iri_to_plain_ascii_uri(i['src']) for i in imgs if i.get('src') is not None],
1183
            'title': title,
1184
            'texts': texts,
1185
            'year': year,
1186
            'month': month,
1187
            'day': day,
1188
        }
1189
1190
1191
class BouletCorp(GenericBouletCorp):
1192
    """Class to retrieve BouletCorp comics."""
1193
    name = 'boulet'
1194
    long_name = 'Boulet Corp'
1195
    url = 'http://www.bouletcorp.com'
1196
    _categories = ('FRANCAIS', )
1197
1198
1199
class BouletCorpEn(GenericBouletCorp):
1200
    """Class to retrieve EnglishBouletCorp comics."""
1201
    name = 'boulet_en'
1202
    long_name = 'Boulet Corp English'
1203
    url = 'http://english.bouletcorp.com'
1204
1205
1206 View Code Duplication
class AmazingSuperPowers(GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
1207
    """Class to retrieve Amazing Super Powers comics."""
1208
    name = 'asp'
1209
    long_name = 'Amazing Super Powers'
1210
    url = 'http://www.amazingsuperpowers.com'
1211
    get_first_comic_link = get_a_navi_navifirst
1212
    get_navi_link = get_a_navi_navinext
1213
1214
    @classmethod
1215
    def get_comic_info(cls, soup, link):
1216
        """Get information about a particular comics."""
1217
        author = soup.find("span", class_="post-author").find("a").string
1218
        date_str = soup.find('span', class_='post-date').string
1219
        day = string_to_date(date_str, "%B %d, %Y")
1220
        imgs = soup.find('div', id='comic').find_all('img')
1221
        title = ' '.join(i['title'] for i in imgs)
1222
        assert all(i['alt'] == i['title'] for i in imgs)
1223
        return {
1224
            'title': title,
1225
            'author': author,
1226
            'img': [img['src'] for img in imgs],
1227
            'day': day.day,
1228
            'month': day.month,
1229
            'year': day.year
1230
        }
1231
1232
1233
class ToonHole(GenericNavigableComic):
1234
    """Class to retrieve Toon Holes comics."""
1235
    # Also on http://tapastic.com/series/TOONHOLE
1236
    name = 'toonhole'
1237
    long_name = 'Toon Hole'
1238
    url = 'http://www.toonhole.com'
1239
    get_first_comic_link = get_a_comicnavbase_comicnavfirst
1240
    get_navi_link = get_a_comicnavbase_comicnavnext
1241
1242
    @classmethod
1243
    def get_comic_info(cls, soup, link):
1244
        """Get information about a particular comics."""
1245
        date_str = soup.find('div', class_='entry-meta').contents[0].strip()
1246
        day = string_to_date(date_str, "%B %d, %Y")
1247
        imgs = soup.find('div', id='comic').find_all('img')
1248
        if imgs:
1249
            img = imgs[0]
1250
            title = img['alt']
1251
            assert img['title'] == title
1252
        else:
1253
            title = ""
1254
        return {
1255
            'title': title,
1256
            'month': day.month,
1257
            'year': day.year,
1258
            'day': day.day,
1259
            'img': [convert_iri_to_plain_ascii_uri(i['src']) for i in imgs],
1260
        }
1261
1262
1263
class Channelate(GenericNavigableComic):
1264
    """Class to retrieve Channelate comics."""
1265
    name = 'channelate'
1266
    long_name = 'Channelate'
1267
    url = 'http://www.channelate.com'
1268
    get_first_comic_link = get_div_navfirst_a
1269
    get_navi_link = get_link_rel_next
1270
    get_url_from_link = join_cls_url_to_href
1271
1272
    @classmethod
1273
    def get_comic_info(cls, soup, link):
1274
        """Get information about a particular comics."""
1275
        author = soup.find("span", class_="post-author").find("a").string
1276
        date_str = soup.find('span', class_='post-date').string
1277
        day = string_to_date(date_str, '%Y/%m/%d')
1278
        title = soup.find('meta', property='og:title')['content']
1279
        post = soup.find('div', id='comic')
1280
        imgs = post.find_all('img') if post else []
1281
        extra_url = None
1282
        extra_div = soup.find('div', id='extrapanelbutton')
1283
        if extra_div:
1284
            extra_url = extra_div.find('a')['href']
1285
            extra_soup = get_soup_at_url(extra_url)
1286
            extra_imgs = extra_soup.find_all('img', class_='extrapanelimage')
1287
            imgs.extend(extra_imgs)
1288
        return {
1289
            'url_extra': extra_url,
1290
            'title': title,
1291
            'author': author,
1292
            'month': day.month,
1293
            'year': day.year,
1294
            'day': day.day,
1295
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
1296
        }
1297
1298
1299
class CyanideAndHappiness(GenericNavigableComic):
1300
    """Class to retrieve Cyanide And Happiness comics."""
1301
    name = 'cyanide'
1302
    long_name = 'Cyanide and Happiness'
1303
    url = 'http://explosm.net'
1304
    _categories = ('NSFW', )
1305
    get_url_from_link = join_cls_url_to_href
1306
1307
    @classmethod
1308
    def get_first_comic_link(cls):
1309
        """Get link to first comics."""
1310
        return get_soup_at_url(cls.url).find('a', title='Oldest comic')
1311
1312
    @classmethod
1313
    def get_navi_link(cls, last_soup, next_):
1314
        """Get link to next or previous comic."""
1315
        link = last_soup.find('a', class_='nav-next' if next_ else 'nav-previous')
1316
        return None if link.get('href') is None else link
1317
1318
    @classmethod
1319
    def get_comic_info(cls, soup, link):
1320
        """Get information about a particular comics."""
1321
        url2 = soup.find('meta', property='og:url')['content']
1322
        num = int(url2.split('/')[-2])
1323
        date_str, _, author = soup.find('div', id='comic-author').text.strip().partition('\nby ')
1324
        day = string_to_date(date_str, '%Y.%m.%d')
1325
        imgs = soup.find_all('img', id='main-comic')
1326
        return {
1327
            'num': num,
1328
            'author': author,
1329
            'month': day.month,
1330
            'year': day.year,
1331
            'day': day.day,
1332
            'prefix': '%d-' % num,
1333
            'img': [convert_iri_to_plain_ascii_uri(urljoin_wrapper(cls.url, i['src'])) for i in imgs]
1334
        }
1335
1336
1337
class MrLovenstein(GenericComic):
1338
    """Class to retrieve Mr Lovenstein comics."""
1339
    # Also on https://tapastic.com/series/MrLovenstein
1340
    name = 'mrlovenstein'
1341
    long_name = 'Mr. Lovenstein'
1342
    url = 'http://www.mrlovenstein.com'
1343
1344
    @classmethod
1345
    def get_next_comic(cls, last_comic):
1346
        """Generator to get the next comic. Implementation of GenericComic's abstract method."""
1347
        # TODO: more info from http://www.mrlovenstein.com/archive
1348
        comic_num_re = re.compile('^/comic/([0-9]*)$')
1349
        nums = [int(comic_num_re.match(link['href']).groups()[0])
1350
                for link in get_soup_at_url(cls.url).find_all('a', href=comic_num_re)]
1351
        first, last = min(nums), max(nums)
1352
        if last_comic:
1353
            first = last_comic['num'] + 1
1354
        for num in range(first, last + 1):
1355
            url = urljoin_wrapper(cls.url, '/comic/%d' % num)
1356
            soup = get_soup_at_url(url)
1357
            imgs = list(
1358
                reversed(soup.find_all('img', src=re.compile('^/images/comics/'))))
1359
            description = soup.find('meta', attrs={'name': 'description'})['content']
1360
            yield {
1361
                'url': url,
1362
                'num': num,
1363
                'texts': '  '.join(t for t in (i.get('title') for i in imgs) if t),
1364
                'img': [urljoin_wrapper(url, i['src']) for i in imgs],
1365
                'description': description,
1366
            }
1367
1368
1369
class DinosaurComics(GenericListableComic):
1370
    """Class to retrieve Dinosaur Comics comics."""
1371
    name = 'dinosaur'
1372
    long_name = 'Dinosaur Comics'
1373
    url = 'http://www.qwantz.com'
1374
    get_url_from_archive_element = get_href
1375
    comic_link_re = re.compile('^%s/index.php\\?comic=([0-9]*)$' % url)
1376
1377
    @classmethod
1378
    def get_archive_elements(cls):
1379
        archive_url = urljoin_wrapper(cls.url, 'archive.php')
1380
        # first link is random -> skip it
1381
        return reversed(get_soup_at_url(archive_url).find_all('a', href=cls.comic_link_re)[1:])
1382
1383
    @classmethod
1384
    def get_comic_info(cls, soup, link):
1385
        """Get information about a particular comics."""
1386
        url = cls.get_url_from_archive_element(link)
1387
        num = int(cls.comic_link_re.match(url).groups()[0])
1388
        date_str = link.string
1389
        text = link.next_sibling.string
1390
        day = string_to_date(remove_st_nd_rd_th_from_date(date_str), "%B %d, %Y")
1391
        comic_img_re = re.compile('^%s/comics/' % cls.url)
1392
        img = soup.find('img', src=comic_img_re)
1393
        return {
1394
            'month': day.month,
1395
            'year': day.year,
1396
            'day': day.day,
1397
            'img': [img.get('src')],
1398
            'title': img.get('title'),
1399
            'text': text,
1400
            'num': num,
1401
        }
1402
1403
1404 View Code Duplication
class ButterSafe(GenericListableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
1405
    """Class to retrieve Butter Safe comics."""
1406
    name = 'butter'
1407
    long_name = 'ButterSafe'
1408
    url = 'http://buttersafe.com'
1409
    get_url_from_archive_element = get_href
1410
    comic_link_re = re.compile('^%s/([0-9]*)/([0-9]*)/([0-9]*)/.*' % url)
1411
1412
    @classmethod
1413
    def get_archive_elements(cls):
1414
        archive_url = urljoin_wrapper(cls.url, 'archive/')
1415
        return reversed(get_soup_at_url(archive_url).find_all('a', href=cls.comic_link_re))
1416
1417
    @classmethod
1418
    def get_comic_info(cls, soup, link):
1419
        """Get information about a particular comics."""
1420
        url = cls.get_url_from_archive_element(link)
1421
        title = link.string
1422
        year, month, day = [int(s) for s in cls.comic_link_re.match(url).groups()]
1423
        img = soup.find('div', id='comic').find('img')
1424
        assert img['alt'] == title
1425
        return {
1426
            'title': title,
1427
            'day': day,
1428
            'month': month,
1429
            'year': year,
1430
            'img': [img['src']],
1431
        }
1432
1433
1434
class CalvinAndHobbes(GenericComic):
1435
    """Class to retrieve Calvin and Hobbes comics."""
1436
    # Also on http://www.gocomics.com/calvinandhobbes/
1437
    name = 'calvin'
1438
    long_name = 'Calvin and Hobbes'
1439
    # This is not through any official webpage but eh...
1440
    url = 'http://marcel-oehler.marcellosendos.ch/comics/ch/'
1441
1442
    @classmethod
1443
    def get_next_comic(cls, last_comic):
1444
        """Generator to get the next comic. Implementation of GenericComic's abstract method."""
1445
        last_date = get_date_for_comic(
1446
            last_comic) if last_comic else date(1985, 11, 1)
1447
        link_re = re.compile('^([0-9]*)/([0-9]*)/')
1448
        img_re = re.compile('')
1449
        for link in get_soup_at_url(cls.url).find_all('a', href=link_re):
1450
            url = link['href']
1451
            year, month = link_re.match(url).groups()
1452
            if date(int(year), int(month), 1) + timedelta(days=31) >= last_date:
1453
                img_re = re.compile('^%s%s([0-9]*)' % (year, month))
1454
                month_url = urljoin_wrapper(cls.url, url)
1455
                for img in get_soup_at_url(month_url).find_all('img', src=img_re):
1456
                    img_src = img['src']
1457
                    day = int(img_re.match(img_src).groups()[0])
1458
                    comic_date = date(int(year), int(month), day)
1459
                    if comic_date > last_date:
1460
                        yield {
1461
                            'url': month_url,
1462
                            'year': int(year),
1463
                            'month': int(month),
1464
                            'day': int(day),
1465
                            'img': ['%s%s/%s/%s' % (cls.url, year, month, img_src)],
1466
                        }
1467
                        last_date = comic_date
1468
1469
1470 View Code Duplication
class AbstruseGoose(GenericListableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
1471
    """Class to retrieve AbstruseGoose Comics."""
1472
    name = 'abstruse'
1473
    long_name = 'Abstruse Goose'
1474
    url = 'http://abstrusegoose.com'
1475
    get_url_from_archive_element = get_href
1476
    comic_url_re = re.compile('^%s/([0-9]*)$' % url)
1477
    comic_img_re = re.compile('^%s/strips/.*' % url)
1478
1479
    @classmethod
1480
    def get_archive_elements(cls):
1481
        archive_url = urljoin_wrapper(cls.url, 'archive')
1482
        return get_soup_at_url(archive_url).find_all('a', href=cls.comic_url_re)
1483
1484
    @classmethod
1485
    def get_comic_info(cls, soup, archive_elt):
1486
        comic_url = cls.get_url_from_archive_element(archive_elt)
1487
        num = int(cls.comic_url_re.match(comic_url).groups()[0])
1488
        imgs = soup.find_all('img', src=cls.comic_img_re)
1489
        return {
1490
            'num': num,
1491
            'title': archive_elt.string,
1492
            'img': [i['src'] for i in imgs],
1493
        }
1494
1495
1496
class PhDComics(GenericNavigableComic):
1497
    """Class to retrieve PHD Comics."""
1498
    name = 'phd'
1499
    long_name = 'PhD Comics'
1500
    url = 'http://phdcomics.com/comics/archive.php'
1501
1502
    @classmethod
1503
    def get_first_comic_link(cls):
1504
        """Get link to first comics."""
1505
        soup = get_soup_at_url(cls.url)
1506
        img = soup.find('img', src='http://phdcomics.com/comics/images/first_button.gif')
1507
        return None if img is None else img.parent
1508
1509
    @classmethod
1510
    def get_navi_link(cls, last_soup, next_):
1511
        """Get link to next or previous comic."""
1512
        url = 'http://phdcomics.com/comics/images/%s_button.gif' % ('next' if next_ else 'prev')
1513
        img = last_soup.find('img', src=url)
1514
        return None if img is None else img.parent
1515
1516
    @classmethod
1517
    def get_comic_info(cls, soup, link):
1518
        """Get information about a particular comics."""
1519
        title = soup.find('meta', attrs={'name': 'twitter:title'})['content']
1520
        imgs = soup.find_all('meta', property='og:image')
1521
        return {
1522
            'img': [i['content'] for i in imgs],
1523
            'title': title,
1524
        }
1525
1526
1527
class Quarktees(GenericNavigableComic):
1528
    """Class to retrieve the Quarktees comics."""
1529
    name = 'quarktees'
1530
    long_name = 'Quarktees'
1531
    url = 'http://www.quarktees.com/blogs/news'
1532
    get_url_from_link = join_cls_url_to_href
1533
    get_first_comic_link = simulate_first_link
1534
    first_url = 'http://www.quarktees.com/blogs/news/12486621-coming-soon'
1535
1536
    @classmethod
1537
    def get_navi_link(cls, last_soup, next_):
1538
        """Get link to next or previous comic."""
1539
        return last_soup.find('a', id='article-next' if next_ else 'article-prev')
1540
1541
    @classmethod
1542
    def get_comic_info(cls, soup, link):
1543
        """Get information about a particular comics."""
1544
        title = soup.find('meta', property='og:title')['content']
1545
        article = soup.find('div', class_='single-article')
1546
        imgs = article.find_all('img')
1547
        return {
1548
            'title': title,
1549
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
1550
        }
1551
1552
1553
class OverCompensating(GenericNavigableComic):
1554
    """Class to retrieve the Over Compensating comics."""
1555
    name = 'compensating'
1556
    long_name = 'Over Compensating'
1557
    url = 'http://www.overcompensating.com'
1558
    get_url_from_link = join_cls_url_to_href
1559
1560
    @classmethod
1561
    def get_first_comic_link(cls):
1562
        """Get link to first comics."""
1563
        return get_soup_at_url(cls.url).find('a', href=re.compile('comic=1$'))
1564
1565
    @classmethod
1566
    def get_navi_link(cls, last_soup, next_):
1567
        """Get link to next or previous comic."""
1568
        return last_soup.find('a', title='next comic' if next_ else 'go back already')
1569
1570
    @classmethod
1571
    def get_comic_info(cls, soup, link):
1572
        """Get information about a particular comics."""
1573
        img_src_re = re.compile('^/oc/comics/.*')
1574
        comic_num_re = re.compile('.*comic=([0-9]*)$')
1575
        comic_url = cls.get_url_from_link(link)
1576
        num = int(comic_num_re.match(comic_url).groups()[0])
1577
        img = soup.find('img', src=img_src_re)
1578
        return {
1579
            'num': num,
1580
            'img': [urljoin_wrapper(comic_url, img['src'])],
1581
            'title': img.get('title')
1582
        }
1583
1584
1585
class Oglaf(GenericNavigableComic):
1586
    """Class to retrieve Oglaf comics."""
1587
    name = 'oglaf'
1588
    long_name = 'Oglaf [NSFW]'
1589
    url = 'http://oglaf.com'
1590
    _categories = ('NSFW', )
1591
    get_url_from_link = join_cls_url_to_href
1592
1593
    @classmethod
1594
    def get_first_comic_link(cls):
1595
        """Get link to first comics."""
1596
        return get_soup_at_url(cls.url).find("div", id="st").parent
1597
1598
    @classmethod
1599
    def get_navi_link(cls, last_soup, next_):
1600
        """Get link to next or previous comic."""
1601
        div = last_soup.find("div", id="nx" if next_ else "pvs")
1602
        return div.parent if div else None
1603
1604
    @classmethod
1605
    def get_comic_info(cls, soup, link):
1606
        """Get information about a particular comics."""
1607
        title = soup.find('title').string
1608
        title_imgs = soup.find('div', id='tt').find_all('img')
1609
        assert len(title_imgs) == 1, title_imgs
1610
        strip_imgs = soup.find_all('img', id='strip')
1611
        assert len(strip_imgs) == 1, strip_imgs
1612
        imgs = title_imgs + strip_imgs
1613
        desc = ' '.join(i['title'] for i in imgs)
1614
        return {
1615
            'title': title,
1616
            'img': [i['src'] for i in imgs],
1617
            'description': desc,
1618
        }
1619
1620
1621
class ScandinaviaAndTheWorld(GenericNavigableComic):
1622
    """Class to retrieve Scandinavia And The World comics."""
1623
    name = 'satw'
1624
    long_name = 'Scandinavia And The World'
1625
    url = 'http://satwcomic.com'
1626
    get_first_comic_link = simulate_first_link
1627
    first_url = 'http://satwcomic.com/sweden-denmark-and-norway'
1628
1629
    @classmethod
1630
    def get_navi_link(cls, last_soup, next_):
1631
        """Get link to next or previous comic."""
1632
        return last_soup.find('a', accesskey='n' if next_ else 'p')
1633
1634
    @classmethod
1635
    def get_comic_info(cls, soup, link):
1636
        """Get information about a particular comics."""
1637
        title = soup.find('meta', attrs={'name': 'twitter:label1'})['content']
1638
        desc = soup.find('meta', property='og:description')['content']
1639
        imgs = soup.find_all('img', itemprop="image")
1640
        return {
1641
            'title': title,
1642
            'description': desc,
1643
            'img': [i['src'] for i in imgs],
1644
        }
1645
1646
1647
class SomethingOfThatIlk(GenericDeletedComic):
1648
    """Class to retrieve the Something Of That Ilk comics."""
1649
    name = 'somethingofthatilk'
1650
    long_name = 'Something Of That Ilk'
1651
    url = 'http://www.somethingofthatilk.com'
1652
1653
1654
class MonkeyUser(GenericNavigableComic):
1655
    """Class to retrieve Monkey User comics."""
1656
    name = 'monkeyuser'
1657
    long_name = 'Monkey User'
1658
    url = 'http://www.monkeyuser.com'
1659
    get_first_comic_link = simulate_first_link
1660
    first_url = 'http://www.monkeyuser.com/2016/project-lifecycle/'
1661
    get_url_from_link = join_cls_url_to_href
1662
1663
    @classmethod
1664
    def get_navi_link(cls, last_soup, next_):
1665
        """Get link to next or previous comic."""
1666
        div = last_soup.find('div', title='next' if next_ else 'previous')
1667
        return None if div is None else div.find('a')
1668
1669
    @classmethod
1670
    def get_comic_info(cls, soup, link):
1671
        """Get information about a particular comics."""
1672
        title = soup.find('meta', property='og:title')['content']
1673
        desc = soup.find('meta', property='og:description')['content']
1674
        imgs = soup.find_all('meta', property='og:image')
1675
        date_str = soup.find('span', class_='post-date').find('time').string
1676
        day = string_to_date(date_str, "%d %b %Y")
1677
        return {
1678
            'month': day.month,
1679
            'year': day.year,
1680
            'day': day.day,
1681
            'img': [i['content'] for i in imgs],
1682
            'title': title,
1683
            'description': desc,
1684
        }
1685
1686
1687
class InfiniteMonkeyBusiness(GenericNavigableComic):
1688
    """Class to retrieve InfiniteMonkeyBusiness comics."""
1689
    name = 'monkey'
1690
    long_name = 'Infinite Monkey Business'
1691
    url = 'http://infinitemonkeybusiness.net'
1692
    get_navi_link = get_a_navi_comicnavnext_navinext
1693
    get_first_comic_link = simulate_first_link
1694
    first_url = 'http://infinitemonkeybusiness.net/comic/pillory/'
1695
1696
    @classmethod
1697
    def get_comic_info(cls, soup, link):
1698
        """Get information about a particular comics."""
1699
        title = soup.find('meta', property='og:title')['content']
1700
        imgs = soup.find('div', id='comic').find_all('img')
1701
        return {
1702
            'title': title,
1703
            'img': [i['src'] for i in imgs],
1704
        }
1705
1706
1707
class Wondermark(GenericListableComic):
1708
    """Class to retrieve the Wondermark comics."""
1709
    name = 'wondermark'
1710
    long_name = 'Wondermark'
1711
    url = 'http://wondermark.com'
1712
    get_url_from_archive_element = get_href
1713
1714
    @classmethod
1715
    def get_archive_elements(cls):
1716
        archive_url = urljoin_wrapper(cls.url, 'archive/')
1717
        return reversed(get_soup_at_url(archive_url).find_all('a', rel='bookmark'))
1718
1719
    @classmethod
1720
    def get_comic_info(cls, soup, link):
1721
        """Get information about a particular comics."""
1722
        date_str = soup.find('div', class_='postdate').find('em').string
1723
        day = string_to_date(remove_st_nd_rd_th_from_date(date_str), "%B %d, %Y")
1724
        div = soup.find('div', id='comic')
1725
        if div:
1726
            img = div.find('img')
1727
            img_src = [img['src']]
1728
            alt = img['alt']
1729
            assert alt == img['title']
1730
            title = soup.find('meta', property='og:title')['content']
1731
        else:
1732
            img_src = []
1733
            alt = ''
1734
            title = ''
1735
        return {
1736
            'month': day.month,
1737
            'year': day.year,
1738
            'day': day.day,
1739
            'img': img_src,
1740
            'title': title,
1741
            'alt': alt,
1742
            'tags': ' '.join(t.string for t in soup.find('div', class_='postmeta').find_all('a', rel='tag')),
1743
        }
1744
1745
1746
class WarehouseComic(GenericNavigableComic):
1747
    """Class to retrieve Warehouse Comic comics."""
1748
    name = 'warehouse'
1749
    long_name = 'Warehouse Comic'
1750
    url = 'http://warehousecomic.com'
1751
    get_first_comic_link = get_a_navi_navifirst
1752
    get_navi_link = get_link_rel_next
1753
1754
    @classmethod
1755
    def get_comic_info(cls, soup, link):
1756
        """Get information about a particular comics."""
1757
        title = soup.find('h2', class_='post-title').string
1758
        date_str = soup.find('span', class_='post-date').string
1759
        day = string_to_date(date_str, "%B %d, %Y")
1760
        imgs = soup.find('div', id='comic').find_all('img')
1761
        return {
1762
            'img': [i['src'] for i in imgs],
1763
            'title': title,
1764
            'day': day.day,
1765
            'month': day.month,
1766
            'year': day.year,
1767
        }
1768
1769
1770
class JustSayEh(GenericNavigableComic):
1771
    """Class to retrieve Just Say Eh comics."""
1772
    # Also on http//tapastic.com/series/Just-Say-Eh
1773
    name = 'justsayeh'
1774
    long_name = 'Just Say Eh'
1775
    url = 'http://www.justsayeh.com'
1776
    get_first_comic_link = get_a_navi_navifirst
1777
    get_navi_link = get_a_navi_comicnavnext_navinext
1778
1779
    @classmethod
1780
    def get_comic_info(cls, soup, link):
1781
        """Get information about a particular comics."""
1782
        title = soup.find('h2', class_='post-title').string
1783
        imgs = soup.find("div", id="comic").find_all("img")
1784
        assert all(i['alt'] == i['title'] for i in imgs)
1785
        alt = imgs[0]['alt']
1786
        return {
1787
            'img': [i['src'] for i in imgs],
1788
            'title': title,
1789
            'alt': alt,
1790
        }
1791
1792
1793
class MouseBearComedy(GenericComicNotWorking):  # Website has changed
1794
    """Class to retrieve Mouse Bear Comedy comics."""
1795
    # Also on http://mousebearcomedy.tumblr.com
1796 View Code Duplication
    name = 'mousebear'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
1797
    long_name = 'Mouse Bear Comedy'
1798
    url = 'http://www.mousebearcomedy.com'
1799
    get_first_comic_link = get_a_navi_navifirst
1800
    get_navi_link = get_a_navi_comicnavnext_navinext
1801
1802
    @classmethod
1803
    def get_comic_info(cls, soup, link):
1804
        """Get information about a particular comics."""
1805
        title = soup.find('h2', class_='post-title').string
1806
        author = soup.find("span", class_="post-author").find("a").string
1807
        date_str = soup.find("span", class_="post-date").string
1808
        day = string_to_date(date_str, '%B %d, %Y')
1809
        imgs = soup.find("div", id="comic").find_all("img")
1810
        assert all(i['alt'] == i['title'] == title for i in imgs)
1811
        return {
1812
            'day': day.day,
1813
            'month': day.month,
1814
            'year': day.year,
1815
            'img': [i['src'] for i in imgs],
1816
            'title': title,
1817
            'author': author,
1818
        }
1819
1820
1821
class BigFootJustice(GenericNavigableComic):
1822
    """Class to retrieve Big Foot Justice comics."""
1823
    # Also on http://tapastic.com/series/bigfoot-justice
1824 View Code Duplication
    name = 'bigfoot'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
1825
    long_name = 'Big Foot Justice'
1826
    url = 'http://bigfootjustice.com'
1827
    get_first_comic_link = get_a_navi_navifirst
1828
    get_navi_link = get_a_navi_comicnavnext_navinext
1829
1830
    @classmethod
1831
    def get_comic_info(cls, soup, link):
1832
        """Get information about a particular comics."""
1833
        imgs = soup.find('div', id='comic').find_all('img')
1834
        assert all(i['title'] == i['alt'] for i in imgs)
1835
        title = ' '.join(i['title'] for i in imgs)
1836
        return {
1837
            'img': [i['src'] for i in imgs],
1838
            'title': title,
1839
        }
1840
1841
1842
class RespawnComic(GenericNavigableComic):
1843
    """Class to retrieve Respawn Comic."""
1844
    # Also on https://respawncomic.tumblr.com
1845
    name = 'respawn'
1846
    long_name = 'Respawn Comic'
1847
    url = 'http://respawncomic.com '
1848
    _categories = ('RESPAWN', )
1849
    get_navi_link = get_a_rel_next
1850
    get_first_comic_link = simulate_first_link
1851
    first_url = 'http://respawncomic.com/comic/c0001/'
1852
1853
    @classmethod
1854
    def get_comic_info(cls, soup, link):
1855
        """Get information about a particular comics."""
1856
        title = soup.find('meta', property='og:title')['content']
1857
        author = soup.find('meta', attrs={'name': 'shareaholic:article_author_name'})['content']
1858
        date_str = soup.find('meta', attrs={'name': 'shareaholic:article_published_time'})['content']
1859
        date_str = date_str[:10]
1860
        day = string_to_date(date_str, "%Y-%m-%d")
1861
        imgs = soup.find_all('meta', property='og:image')
1862
        skip_imgs = {
1863
            'http://respawncomic.com/wp-content/uploads/2016/03/site/HAROLD2.png',
1864
            'http://respawncomic.com/wp-content/uploads/2016/03/site/DEVA.png'
1865
        }
1866
        return {
1867
            'title': title,
1868
            'author': author,
1869
            'day': day.day,
1870
            'month': day.month,
1871
            'year': day.year,
1872
            'img': [i['content'] for i in imgs if i['content'] not in skip_imgs],
1873
        }
1874
1875
1876
class SafelyEndangered(GenericNavigableComic):
1877
    """Class to retrieve Safely Endangered comics."""
1878
    # Also on http://tumblr.safelyendangered.com
1879 View Code Duplication
    name = 'endangered'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
1880
    long_name = 'Safely Endangered'
1881
    url = 'http://www.safelyendangered.com'
1882
    get_navi_link = get_link_rel_next
1883
    get_first_comic_link = simulate_first_link
1884
    first_url = 'http://www.safelyendangered.com/comic/ignored/'
1885
1886
    @classmethod
1887
    def get_comic_info(cls, soup, link):
1888
        """Get information about a particular comics."""
1889
        title = soup.find('h2', class_='post-title').string
1890
        date_str = soup.find('span', class_='post-date').string
1891
        day = string_to_date(date_str, '%B %d, %Y')
1892
        imgs = soup.find('div', id='comic').find_all('img')
1893
        alt = imgs[0]['alt']
1894
        assert all(i['alt'] == i['title'] for i in imgs)
1895
        return {
1896
            'day': day.day,
1897
            'month': day.month,
1898
            'year': day.year,
1899
            'img': [i['src'] for i in imgs],
1900
            'title': title,
1901
            'alt': alt,
1902
        }
1903
1904
1905
class PicturesInBoxes(GenericNavigableComic):
1906
    """Class to retrieve Pictures In Boxes comics."""
1907
    # Also on https://picturesinboxescomic.tumblr.com
1908 View Code Duplication
    name = 'picturesinboxes'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
1909
    long_name = 'Pictures in Boxes'
1910
    url = 'http://www.picturesinboxes.com'
1911
    get_navi_link = get_a_navi_navinext
1912
    get_first_comic_link = simulate_first_link
1913
    first_url = 'http://www.picturesinboxes.com/2013/10/26/tetris/'
1914
1915
    @classmethod
1916
    def get_comic_info(cls, soup, link):
1917
        """Get information about a particular comics."""
1918
        title = soup.find('h2', class_='post-title').string
1919
        author = soup.find("span", class_="post-author").find("a").string
1920
        date_str = soup.find('span', class_='post-date').string
1921
        day = string_to_date(date_str, '%B %d, %Y')
1922
        imgs = soup.find('div', class_='comicpane').find_all('img')
1923
        assert imgs
1924
        assert all(i['title'] == i['alt'] == title for i in imgs)
1925
        return {
1926
            'day': day.day,
1927
            'month': day.month,
1928
            'year': day.year,
1929
            'img': [i['src'] for i in imgs],
1930
            'title': title,
1931
            'author': author,
1932
        }
1933
1934
1935
class Penmen(GenericComicNotWorking, GenericNavigableComic):
1936
    """Class to retrieve Penmen comics."""
1937
    name = 'penmen'
1938 View Code Duplication
    long_name = 'Penmen'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
1939
    url = 'http://penmen.com'
1940
    get_navi_link = get_link_rel_next
1941
    get_first_comic_link = simulate_first_link
1942
    first_url = 'http://penmen.com/index.php/2016/09/12/penmen-announces-grin-big-brand-clothing/'
1943
1944
    @classmethod
1945
    def get_comic_info(cls, soup, link):
1946
        """Get information about a particular comics."""
1947
        title = soup.find('title').string
1948
        imgs = soup.find('div', class_='entry-content').find_all('img')
1949
        short_url = soup.find('link', rel='shortlink')['href']
1950
        tags = ' '.join(t.string for t in soup.find_all('a', rel='tag'))
1951
        date_str = soup.find('time')['datetime'][:10]
1952
        day = string_to_date(date_str, "%Y-%m-%d")
1953
        return {
1954
            'title': title,
1955
            'short_url': short_url,
1956
            'img': [i['src'] for i in imgs],
1957
            'tags': tags,
1958
            'month': day.month,
1959
            'year': day.year,
1960
            'day': day.day,
1961
        }
1962
1963
1964
class TheDoghouseDiaries(GenericDeletedComic, GenericNavigableComic):
1965
    """Class to retrieve The Dog House Diaries comics."""
1966
    name = 'doghouse'
1967
    long_name = 'The Dog House Diaries'
1968
    url = 'http://thedoghousediaries.com'
1969
1970
    @classmethod
1971
    def get_first_comic_link(cls):
1972
        """Get link to first comics."""
1973
        return get_soup_at_url(cls.url).find('a', id='firstlink')
1974
1975
    @classmethod
1976
    def get_navi_link(cls, last_soup, next_):
1977
        """Get link to next or previous comic."""
1978
        return last_soup.find('a', id='nextlink' if next_ else 'previouslink')
1979
1980
    @classmethod
1981
    def get_comic_info(cls, soup, link):
1982
        """Get information about a particular comics."""
1983
        comic_img_re = re.compile('^dhdcomics/.*')
1984
        img = soup.find('img', src=comic_img_re)
1985
        comic_url = cls.get_url_from_link(link)
1986
        return {
1987
            'title': soup.find('h2', id='titleheader').string,
1988
            'title2': soup.find('div', id='subtext').string,
1989
            'alt': img.get('title'),
1990
            'img': [urljoin_wrapper(comic_url, img['src'].strip())],
1991
            'num': int(comic_url.split('/')[-1]),
1992
        }
1993
1994
1995
class InvisibleBread(GenericListableComic):
1996
    """Class to retrieve Invisible Bread comics."""
1997
    # Also on http://www.gocomics.com/invisible-bread
1998
    name = 'invisiblebread'
1999
    long_name = 'Invisible Bread'
2000
    url = 'http://invisiblebread.com'
2001
2002
    @classmethod
2003
    def get_archive_elements(cls):
2004
        archive_url = urljoin_wrapper(cls.url, 'archives/')
2005
        return reversed(get_soup_at_url(archive_url).find_all('td', class_='archive-title'))
2006
2007
    @classmethod
2008
    def get_url_from_archive_element(cls, td):
2009
        return td.find('a')['href']
2010
2011
    @classmethod
2012
    def get_comic_info(cls, soup, td):
2013
        """Get information about a particular comics."""
2014
        url = cls.get_url_from_archive_element(td)
2015
        title = td.find('a').string
2016
        month_and_day = td.previous_sibling.string
2017
        link_re = re.compile('^%s/([0-9]+)/' % cls.url)
2018
        year = link_re.match(url).groups()[0]
2019
        date_str = month_and_day + ' ' + year
2020
        day = string_to_date(date_str, '%b %d %Y')
2021
        imgs = [soup.find('div', id='comic').find('img')]
2022
        assert len(imgs) == 1, imgs
2023
        assert all(i['title'] == i['alt'] == title for i in imgs)
2024
        return {
2025
            'month': day.month,
2026
            'year': day.year,
2027
            'day': day.day,
2028
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
2029
            'title': title,
2030
        }
2031
2032
2033
class DiscoBleach(GenericDeletedComic):
2034
    """Class to retrieve Disco Bleach Comics."""
2035
    name = 'discobleach'
2036
    long_name = 'Disco Bleach'
2037
    url = 'http://discobleach.com'
2038
2039
2040
class TubeyToons(GenericDeletedComic):
2041
    """Class to retrieve TubeyToons comics."""
2042
    # Also on http://tapastic.com/series/Tubey-Toons
2043
    # Also on https://tubeytoons.tumblr.com
2044
    name = 'tubeytoons'
2045
    long_name = 'Tubey Toons'
2046
    url = 'http://tubeytoons.com'
2047
    _categories = ('TUNEYTOONS', )
2048
2049
2050
class CompletelySeriousComics(GenericNavigableComic):
2051
    """Class to retrieve Completely Serious comics."""
2052
    name = 'completelyserious'
2053 View Code Duplication
    long_name = 'Completely Serious Comics'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2054
    url = 'http://completelyseriouscomics.com'
2055
    get_first_comic_link = get_a_navi_navifirst
2056
    get_navi_link = get_a_navi_navinext
2057
2058
    @classmethod
2059
    def get_comic_info(cls, soup, link):
2060
        """Get information about a particular comics."""
2061
        title = soup.find('h2', class_='post-title').string
2062
        author = soup.find('span', class_='post-author').contents[1].string
2063
        date_str = soup.find('span', class_='post-date').string
2064
        day = string_to_date(date_str, '%B %d, %Y')
2065
        imgs = soup.find('div', class_='comicpane').find_all('img')
2066
        assert imgs
2067
        alt = imgs[0]['title']
2068
        assert all(i['title'] == i['alt'] == alt for i in imgs)
2069
        return {
2070
            'month': day.month,
2071
            'year': day.year,
2072
            'day': day.day,
2073
            'img': [i['src'] for i in imgs],
2074
            'title': title,
2075
            'alt': alt,
2076
            'author': author,
2077
        }
2078
2079
2080
class PoorlyDrawnLines(GenericListableComic):
2081
    """Class to retrieve Poorly Drawn Lines comics."""
2082
    # Also on http://pdlcomics.tumblr.com
2083 View Code Duplication
    name = 'poorlydrawn'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2084
    long_name = 'Poorly Drawn Lines'
2085
    url = 'https://www.poorlydrawnlines.com'
2086
    _categories = ('POORLYDRAWN', )
2087
    get_url_from_archive_element = get_href
2088
2089
    @classmethod
2090
    def get_comic_info(cls, soup, link):
2091
        """Get information about a particular comics."""
2092
        imgs = soup.find('div', class_='post').find_all('img')
2093
        assert len(imgs) <= 1, imgs
2094
        return {
2095
            'img': [i['src'] for i in imgs],
2096
            'title': imgs[0].get('title', "") if imgs else "",
2097
        }
2098
2099
    @classmethod
2100
    def get_archive_elements(cls):
2101
        archive_url = urljoin_wrapper(cls.url, 'archive')
2102
        url_re = re.compile('^%s/comic/.' % cls.url)
2103
        return reversed(get_soup_at_url(archive_url).find_all('a', href=url_re))
2104
2105
2106
class LoadingComics(GenericNavigableComic):
2107
    """Class to retrieve Loading Artist comics."""
2108
    name = 'loadingartist'
2109
    long_name = 'Loading Artist'
2110
    url = 'http://www.loadingartist.com/latest'
2111
2112
    @classmethod
2113
    def get_first_comic_link(cls):
2114
        """Get link to first comics."""
2115
        return get_soup_at_url(cls.url).find('a', title="First")
2116
2117
    @classmethod
2118
    def get_navi_link(cls, last_soup, next_):
2119
        """Get link to next or previous comic."""
2120
        return last_soup.find('a', title='Next' if next_ else 'Previous')
2121
2122
    @classmethod
2123
    def get_comic_info(cls, soup, link):
2124
        """Get information about a particular comics."""
2125
        title = soup.find('h1').string
2126
        date_str = soup.find('span', class_='date').string.strip()
2127
        day = string_to_date(date_str, "%B %d, %Y")
2128
        imgs = soup.find('div', class_='comic').find_all('img', alt='', title='')
2129
        return {
2130
            'title': title,
2131
            'img': [i['src'] for i in imgs],
2132
            'month': day.month,
2133
            'year': day.year,
2134
            'day': day.day,
2135
        }
2136
2137
2138
class ChuckleADuck(GenericNavigableComic):
2139
    """Class to retrieve Chuckle-A-Duck comics."""
2140
    name = 'chuckleaduck'
2141
    long_name = 'Chuckle-A-duck'
2142
    url = 'http://chuckleaduck.com'
2143
    get_first_comic_link = get_div_navfirst_a
2144
    get_navi_link = get_link_rel_next
2145
2146
    @classmethod
2147
    def get_comic_info(cls, soup, link):
2148
        """Get information about a particular comics."""
2149
        date_str = soup.find('span', class_='post-date').string
2150
        day = string_to_date(remove_st_nd_rd_th_from_date(date_str), "%B %d, %Y")
2151
        author = soup.find('span', class_='post-author').string
2152
        div = soup.find('div', id='comic')
2153
        imgs = div.find_all('img') if div else []
2154
        title = imgs[0]['title'] if imgs else ""
2155
        assert all(i['title'] == i['alt'] == title for i in imgs)
2156
        return {
2157
            'month': day.month,
2158
            'year': day.year,
2159
            'day': day.day,
2160
            'img': [i['src'] for i in imgs],
2161
            'title': title,
2162
            'author': author,
2163
        }
2164
2165
2166
class DepressedAlien(GenericNavigableComic):
2167
    """Class to retrieve Depressed Alien Comics."""
2168
    name = 'depressedalien'
2169
    long_name = 'Depressed Alien'
2170
    url = 'http://depressedalien.com'
2171
    get_url_from_link = join_cls_url_to_href
2172
2173
    @classmethod
2174
    def get_first_comic_link(cls):
2175
        """Get link to first comics."""
2176
        return get_soup_at_url(cls.url).find('img', attrs={'name': 'beginArrow'}).parent
2177
2178
    @classmethod
2179
    def get_navi_link(cls, last_soup, next_):
2180
        """Get link to next or previous comic."""
2181
        return last_soup.find('img', attrs={'name': 'rightArrow' if next_ else 'leftArrow'}).parent
2182
2183
    @classmethod
2184
    def get_comic_info(cls, soup, link):
2185
        """Get information about a particular comics."""
2186
        title = soup.find('meta', attrs={'name': 'twitter:title'})['content']
2187
        imgs = soup.find_all('meta', property='og:image')
2188
        return {
2189
            'title': title,
2190
            'img': [i['content'] for i in imgs],
2191
        }
2192
2193
2194
class TurnOffUs(GenericListableComic):
2195
    """Class to retrieve TurnOffUs comics."""
2196
    name = 'turnoffus'
2197 View Code Duplication
    long_name = 'Turn Off Us'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2198
    url = 'http://turnoff.us'
2199
    get_url_from_archive_element = join_cls_url_to_href
2200
2201
    @classmethod
2202
    def get_archive_elements(cls):
2203
        archive_url = urljoin_wrapper(cls.url, 'all')
2204
        post_list = get_soup_at_url(archive_url).find('ul', class_='post-list')
2205
        return reversed(post_list.find_all('a', class_='post-link'))
2206
2207
    @classmethod
2208
    def get_comic_info(cls, soup, archive_elt):
2209
        """Get information about a particular comics."""
2210
        title = soup.find('meta', property='og:title')['content']
2211
        imgs = soup.find_all('meta', property='og:image')
2212
        return {
2213
            'title': title,
2214
            'img': [i['content'] for i in imgs],
2215
        }
2216
2217
2218
class ThingsInSquares(GenericListableComic):
2219
    """Class to retrieve Things In Squares comics."""
2220
    # This can be retrieved in other languages
2221
    # Also on https://tapastic.com/series/Things-in-Squares
2222
    name = 'squares'
2223
    long_name = 'Things in squares'
2224
    url = 'http://www.thingsinsquares.com'
2225
2226
    @classmethod
2227
    def get_comic_info(cls, soup, tr):
2228
        """Get information about a particular comics."""
2229
        _, td2, td3 = tr.find_all('td')
2230
        a = td2.find('a')
2231
        date_str = td3.string
2232
        day = string_to_date(date_str, "%m.%d.%y")
2233
        title = a.string
2234
        title2 = soup.find('meta', property='og:title')['content']
2235
        desc = soup.find('meta', property='og:description')
2236
        description = desc['content'] if desc else ''
2237
        tags = ' '.join(t['content'] for t in soup.find_all('meta', property='article:tag'))
2238
        imgs = soup.find_all('meta', property='og:image')
2239
        return {
2240
            'day': day.day,
2241
            'month': day.month,
2242
            'year': day.year,
2243
            'title': title,
2244
            'title2': title2,
2245
            'description': description,
2246
            'tags': tags,
2247
            'img': [i['content'] for i in imgs],
2248
        }
2249
2250
    @classmethod
2251
    def get_url_from_archive_element(cls, tr):
2252
        _, td2, __ = tr.find_all('td')
2253
        return td2.find('a')['href']
2254
2255
    @classmethod
2256
    def get_archive_elements(cls):
2257
        archive_url = urljoin_wrapper(cls.url, 'archive-2')
2258
        return reversed(get_soup_at_url(archive_url).find('tbody').find_all('tr'))
2259
2260
2261
class HappleTea(GenericNavigableComic):
2262
    """Class to retrieve Happle Tea Comics."""
2263
    name = 'happletea'
2264 View Code Duplication
    long_name = 'Happle Tea'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2265
    url = 'http://www.happletea.com'
2266
    get_first_comic_link = get_a_navi_navifirst
2267
    get_navi_link = get_link_rel_next
2268
2269
    @classmethod
2270
    def get_comic_info(cls, soup, link):
2271
        """Get information about a particular comics."""
2272
        imgs = soup.find('div', id='comic').find_all('img')
2273
        post = soup.find('div', class_='post-content')
2274
        title = post.find('h2', class_='post-title').string
2275
        author = post.find('a', rel='author').string
2276
        date_str = post.find('span', class_='post-date').string
2277
        day = string_to_date(date_str, "%B %d, %Y")
2278
        assert all(i['alt'] == i['title'] for i in imgs)
2279
        return {
2280
            'title': title,
2281
            'img': [i['src'] for i in imgs],
2282
            'alt': ''.join(i['alt'] for i in imgs),
2283
            'month': day.month,
2284
            'year': day.year,
2285
            'day': day.day,
2286
            'author': author,
2287
        }
2288
2289
2290
class RockPaperScissors(GenericNavigableComic):
2291
    """Class to retrieve Rock Paper Scissors comics."""
2292
    name = 'rps'
2293
    long_name = 'Rock Paper Scissors'
2294
    url = 'http://rps-comics.com'
2295
    get_first_comic_link = get_a_navi_navifirst
2296
    get_navi_link = get_link_rel_next
2297
2298
    @classmethod
2299
    def get_comic_info(cls, soup, link):
2300
        """Get information about a particular comics."""
2301
        title = soup.find('title').string
2302
        imgs = soup.find_all('meta', property='og:image')
2303
        short_url = soup.find('link', rel='shortlink')['href']
2304
        transcript = soup.find('div', id='transcript-content').string
2305
        return {
2306
            'title': title,
2307
            'transcript': transcript,
2308
            'short_url': short_url,
2309
            'img': [i['content'] for i in imgs],
2310
        }
2311
2312
2313
class FatAwesomeComics(GenericNavigableComic):
2314
    """Class to retrieve Fat Awesome Comics."""
2315
    # Also on http://fatawesomecomedy.tumblr.com
2316
    name = 'fatawesome'
2317
    long_name = 'Fat Awesome'
2318
    url = 'http://fatawesome.com/comics'
2319
    get_navi_link = get_a_rel_next
2320
    get_first_comic_link = simulate_first_link
2321
    first_url = 'http://fatawesome.com/shortbus/'
2322
2323
    @classmethod
2324
    def get_comic_info(cls, soup, link):
2325
        """Get information about a particular comics."""
2326
        title = soup.find('meta', attrs={'name': 'twitter:title'})['content']
2327
        description = soup.find('meta', attrs={'name': 'description'})['content']
2328
        tags_prop = soup.find('meta', property='article:tag')
2329
        tags = tags_prop['content'] if tags_prop else ""
2330
        date_str = soup.find('meta', property='article:published_time')['content'][:10]
2331
        day = string_to_date(date_str, "%Y-%m-%d")
2332
        imgs = soup.find_all('img', attrs={'data-recalc-dims': "1"})
2333
        assert len(imgs) == 1, imgs
2334
        return {
2335
            'title': title,
2336
            'description': description,
2337
            'tags': tags,
2338
            'alt': "".join(i['alt'] for i in imgs),
2339
            'img': [i['src'].rsplit('?', 1)[0] for i in imgs],
2340
            'month': day.month,
2341
            'year': day.year,
2342
            'day': day.day,
2343
        }
2344
2345
2346
class PeterLauris(GenericNavigableComic):
2347
    """Class to retrieve Peter Lauris comics."""
2348
    name = 'peterlauris'
2349 View Code Duplication
    long_name = 'Peter Lauris'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2350
    url = 'http://peterlauris.com/comics'
2351
    get_navi_link = get_a_rel_next
2352
    get_first_comic_link = simulate_first_link
2353
    first_url = 'http://peterlauris.com/comics/just-in-case/'
2354
2355
    @classmethod
2356
    def get_comic_info(cls, soup, link):
2357
        """Get information about a particular comics."""
2358
        title = soup.find('meta', attrs={'name': 'twitter:title'})['content']
2359
        date_str = soup.find('meta', property='article:published_time')['content'][:10]
2360
        day = string_to_date(date_str, "%Y-%m-%d")
2361
        imgs = soup.find_all('meta', property='og:image')
2362
        return {
2363
            'title': title,
2364
            'img': [i['content'] for i in imgs],
2365
            'month': day.month,
2366
            'year': day.year,
2367
            'day': day.day,
2368
        }
2369
2370
2371 View Code Duplication
class RandomCrab(GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2372
    """Class to retrieve Random Crab comics."""
2373
    name = 'randomcrab'
2374
    long_name = 'Random Crab'
2375
    url = 'https://randomcrab.com'
2376
    get_navi_link = get_a_rel_next
2377
    get_first_comic_link = simulate_first_link
2378
    first_url = 'https://randomcrab.com/natural-elephant/'
2379
2380
    @classmethod
2381
    def get_comic_info(cls, soup, link):
2382
        """Get information about a particular comics."""
2383
        title = soup.find('meta', property='og:title')['content']
2384
        desc = soup.find('meta', property='og:description')['content']
2385
        date_str = soup.find('meta', property='article:published_time')['content'][:10]
2386
        day = string_to_date(date_str, "%Y-%m-%d")
2387
        imgs = soup.find_all('meta', property='og:image')
2388
        author = soup.find('a', rel='author').string
2389
        return {
2390
            'title': title,
2391
            'desc': desc,
2392
            'img': [i['content'] for i in imgs],
2393
            'month': day.month,
2394
            'year': day.year,
2395
            'day': day.day,
2396
            'author': author,
2397
        }
2398
2399
2400 View Code Duplication
class JuliasDrawings(GenericListableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2401
    """Class to retrieve Julia's Drawings."""
2402
    name = 'julia'
2403
    long_name = "Julia's Drawings"
2404
    url = 'https://drawings.jvns.ca'
2405
    get_url_from_archive_element = get_href
2406
2407
    @classmethod
2408
    def get_archive_elements(cls):
2409
        div = get_soup_at_url(cls.url).find('div', class_='drawings')
2410
        return reversed(div.find_all('a'))
2411
2412
    @classmethod
2413
    def get_comic_info(cls, soup, archive_elt):
2414
        """Get information about a particular comics."""
2415
        date_str = soup.find('meta', property='og:article:published_time')['content'][:10]
2416
        day = string_to_date(date_str, "%Y-%m-%d")
2417
        title = soup.find('h3', class_='p-post-title').string
2418
        imgs = soup.find('section', class_='post-content').find_all('img')
2419
        return {
2420
            'title': title,
2421
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
2422
            'month': day.month,
2423
            'year': day.year,
2424
            'day': day.day,
2425
        }
2426
2427
2428
class AnythingComic(GenericListableComic):
2429
    """Class to retrieve Anything Comics."""
2430
    # Also on http://tapastic.com/series/anything
2431
    name = 'anythingcomic'
2432
    long_name = 'Anything Comic'
2433
    url = 'http://www.anythingcomic.com'
2434
2435
    @classmethod
2436
    def get_archive_elements(cls):
2437
        archive_url = urljoin_wrapper(cls.url, 'archive/')
2438
        # The first 2 <tr>'s do not correspond to comics
2439
        return get_soup_at_url(archive_url).find('table', id='chapter_table').find_all('tr')[2:]
2440
2441
    @classmethod
2442
    def get_url_from_archive_element(cls, tr):
2443
        """Get url corresponding to an archive element."""
2444
        _, td_comic, td_date, _ = tr.find_all('td')
2445 View Code Duplication
        link = td_comic.find('a')
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2446
        return urljoin_wrapper(cls.url, link['href'])
2447
2448
    @classmethod
2449
    def get_comic_info(cls, soup, tr):
2450
        """Get information about a particular comics."""
2451
        td_num, td_comic, td_date, _ = tr.find_all('td')
2452
        num = int(td_num.string)
2453
        link = td_comic.find('a')
2454
        title = link.string
2455
        imgs = soup.find_all('img', id='comic_image')
2456
        date_str = td_date.string
2457
        day = string_to_date(remove_st_nd_rd_th_from_date(date_str), "%B %d, %Y, %I:%M %p")
2458
        assert len(imgs) == 1, imgs
2459
        assert all(i.get('alt') == i.get('title') for i in imgs)
2460
        return {
2461
            'num': num,
2462
            'title': title,
2463
            'alt': imgs[0].get('alt', ''),
2464
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
2465
            'month': day.month,
2466
            'year': day.year,
2467
            'day': day.day,
2468
        }
2469
2470
2471
class LonnieMillsap(GenericNavigableComic):
2472
    """Class to retrieve Lonnie Millsap's comics."""
2473
    name = 'millsap'
2474
    long_name = 'Lonnie Millsap'
2475
    url = 'http://www.lonniemillsap.com'
2476
    get_navi_link = get_link_rel_next
2477
    get_first_comic_link = simulate_first_link
2478
    first_url = 'http://www.lonniemillsap.com/?p=42'
2479
2480
    @classmethod
2481
    def get_comic_info(cls, soup, link):
2482
        """Get information about a particular comics."""
2483 View Code Duplication
        title = soup.find('h2', class_='post-title').string
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2484
        post = soup.find('div', class_='post-content')
2485
        author = post.find("span", class_="post-author").find("a").string
2486
        date_str = post.find("span", class_="post-date").string
2487
        day = string_to_date(date_str, "%B %d, %Y")
2488
        imgs = post.find("div", class_="entry").find_all("img")
2489
        return {
2490
            'title': title,
2491
            'author': author,
2492
            'img': [i['src'] for i in imgs],
2493
            'month': day.month,
2494
            'year': day.year,
2495
            'day': day.day,
2496
        }
2497
2498
2499
class LinsEditions(GenericDeletedComic):  # Permanently moved to warandpeas
2500
    """Class to retrieve L.I.N.S. Editions comics."""
2501
    # Also on https://linscomics.tumblr.com
2502
    # Now on https://warandpeas.com
2503
    name = 'lins'
2504
    long_name = 'L.I.N.S. Editions'
2505
    url = 'https://linsedition.com'
2506
    _categories = ('WARANDPEAS', 'LINS')
2507
2508
2509
class WarAndPeas(GenericNavigableComic):
2510
    """Class to retrieve War And Peas comics."""
2511
    name = 'warandpeas'
2512
    long_name = 'War And Peas'
2513
    url = 'https://warandpeas.com'
2514
    get_navi_link = get_link_rel_next
2515
    get_first_comic_link = simulate_first_link
2516
    first_url = 'https://warandpeas.com/2011/11/07/565/'
2517
    _categories = ('WARANDPEAS', 'LINS')
2518
2519
    @classmethod
2520
    def get_comic_info(cls, soup, link):
2521
        """Get information about a particular comics."""
2522
        title = soup.find('meta', property='og:title')['content']
2523
        imgs = soup.find_all('meta', property='og:image')
2524
        date_str = soup.find('meta', property='article:published_time')['content'][:10]
2525
        day = string_to_date(date_str, "%Y-%m-%d")
2526
        return {
2527
            'title': title,
2528
            'img': [i['content'] for i in imgs],
2529
            'month': day.month,
2530
            'year': day.year,
2531
            'day': day.day,
2532
        }
2533
2534
2535
class ThorsThundershack(GenericNavigableComic):
2536
    """Class to retrieve Thor's Thundershack comics."""
2537
    # Also on http://tapastic.com/series/Thors-Thundershac
2538
    name = 'thor'
2539
    long_name = 'Thor\'s Thundershack'
2540
    url = 'http://www.thorsthundershack.com'
2541
    _categories = ('THOR', )
2542
    get_url_from_link = join_cls_url_to_href
2543
2544
    @classmethod
2545
    def get_first_comic_link(cls):
2546
        """Get link to first comics."""
2547
        return get_soup_at_url(cls.url).find('a', class_='first navlink')
2548
2549
    @classmethod
2550
    def get_navi_link(cls, last_soup, next_):
2551
        """Get link to next or previous comic."""
2552
        for link in last_soup.find_all('a', rel='next' if next_ else 'prev'):
2553
            if link['href'] != '/comic':
2554 View Code Duplication
                return link
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2555
        return None
2556
2557
    @classmethod
2558
    def get_comic_info(cls, soup, link):
2559
        """Get information about a particular comics."""
2560
        title = soup.find('meta', attrs={'name': 'description'})["content"]
2561
        description = soup.find('div', itemprop='articleBody').text
2562
        author = soup.find('span', itemprop='author copyrightHolder').string
2563
        imgs = soup.find_all('img', itemprop='image')
2564
        assert all(i['title'] == i['alt'] for i in imgs)
2565
        alt = imgs[0]['alt'] if imgs else ""
2566
        date_str = soup.find('time', itemprop='datePublished')["datetime"]
2567
        day = string_to_date(date_str, "%Y-%m-%d %H:%M:%S")
2568
        return {
2569
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
2570
            'month': day.month,
2571
            'year': day.year,
2572
            'day': day.day,
2573
            'author': author,
2574
            'title': title,
2575
            'alt': alt,
2576
            'description': description,
2577
        }
2578
2579
2580
class GerbilWithAJetpack(GenericNavigableComic):
2581
    """Class to retrieve GerbilWithAJetpack comics."""
2582
    name = 'gerbil'
2583 View Code Duplication
    long_name = 'Gerbil With A Jetpack'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2584
    url = 'http://gerbilwithajetpack.com'
2585
    get_first_comic_link = get_a_navi_navifirst
2586
    get_navi_link = get_a_rel_next
2587
2588
    @classmethod
2589
    def get_comic_info(cls, soup, link):
2590
        """Get information about a particular comics."""
2591
        title = soup.find('h2', class_='post-title').string
2592
        author = soup.find("span", class_="post-author").find("a").string
2593
        date_str = soup.find("span", class_="post-date").string
2594
        day = string_to_date(date_str, "%B %d, %Y")
2595
        imgs = soup.find("div", id="comic").find_all("img")
2596
        alt = imgs[0]['alt']
2597
        assert all(i['alt'] == i['title'] == alt for i in imgs)
2598
        return {
2599
            'img': [i['src'] for i in imgs],
2600
            'title': title,
2601
            'alt': alt,
2602
            'author': author,
2603
            'day': day.day,
2604
            'month': day.month,
2605
            'year': day.year
2606
        }
2607
2608
2609
class EveryDayBlues(GenericDeletedComic, GenericNavigableComic):
2610
    """Class to retrieve EveryDayBlues Comics."""
2611 View Code Duplication
    name = "blues"
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2612
    long_name = "Every Day Blues"
2613
    url = "http://everydayblues.net"
2614
    get_first_comic_link = get_a_navi_navifirst
2615
    get_navi_link = get_link_rel_next
2616
2617
    @classmethod
2618
    def get_comic_info(cls, soup, link):
2619
        """Get information about a particular comics."""
2620
        title = soup.find("h2", class_="post-title").string
2621
        author = soup.find("span", class_="post-author").find("a").string
2622
        date_str = soup.find("span", class_="post-date").string
2623
        day = string_to_date(date_str, "%d. %B %Y", "de_DE.utf8")
2624
        imgs = soup.find("div", id="comic").find_all("img")
2625
        assert all(i['alt'] == i['title'] == title for i in imgs)
2626
        assert len(imgs) <= 1, imgs
2627
        return {
2628
            'img': [i['src'] for i in imgs],
2629
            'title': title,
2630
            'author': author,
2631
            'day': day.day,
2632
            'month': day.month,
2633
            'year': day.year
2634
        }
2635
2636
2637
class BiterComics(GenericNavigableComic):
2638
    """Class to retrieve Biter Comics."""
2639
    name = "biter"
2640
    long_name = "Biter Comics"
2641 View Code Duplication
    url = "http://www.bitercomics.com"
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2642
    get_first_comic_link = get_a_navi_navifirst
2643
    get_navi_link = get_link_rel_next
2644
2645
    @classmethod
2646
    def get_comic_info(cls, soup, link):
2647
        """Get information about a particular comics."""
2648
        title = soup.find("h1", class_="entry-title").string
2649
        author = soup.find("span", class_="author vcard").find("a").string
2650
        date_str = soup.find("span", class_="entry-date").string
2651
        day = string_to_date(date_str, "%B %d, %Y")
2652
        imgs = soup.find("div", id="comic").find_all("img")
2653
        assert all(i['alt'] == i['title'] for i in imgs)
2654
        assert len(imgs) == 1, imgs
2655
        alt = imgs[0]['alt']
2656
        return {
2657
            'img': [i['src'] for i in imgs],
2658
            'title': title,
2659
            'alt': alt,
2660
            'author': author,
2661
            'day': day.day,
2662
            'month': day.month,
2663
            'year': day.year
2664
        }
2665
2666
2667
class TheAwkwardYeti(GenericNavigableComic):
2668
    """Class to retrieve The Awkward Yeti comics."""
2669
    # Also on http://www.gocomics.com/the-awkward-yeti
2670
    # Also on http://larstheyeti.tumblr.com
2671
    # Also on https://tapastic.com/series/TheAwkwardYeti
2672
    name = 'yeti'
2673
    long_name = 'The Awkward Yeti'
2674
    url = 'http://theawkwardyeti.com'
2675
    _categories = ('YETI', )
2676
    get_first_comic_link = get_a_navi_navifirst
2677
    get_navi_link = get_link_rel_next
2678
2679
    @classmethod
2680
    def get_comic_info(cls, soup, link):
2681
        """Get information about a particular comics."""
2682
        title = soup.find('h2', class_='post-title').string
2683
        date_str = soup.find("span", class_="post-date").string
2684
        day = string_to_date(date_str, "%B %d, %Y")
2685
        imgs = soup.find("div", id="comic").find_all("img")
2686
        assert all(idx > 0 or i['alt'] == i['title'] for idx, i in enumerate(imgs))
2687
        return {
2688
            'img': [i['src'] for i in imgs],
2689
            'title': title,
2690
            'day': day.day,
2691
            'month': day.month,
2692
            'year': day.year
2693
        }
2694
2695
2696
class PleasantThoughts(GenericNavigableComic):
2697
    """Class to retrieve Pleasant Thoughts comics."""
2698
    name = 'pleasant'
2699
    long_name = 'Pleasant Thoughts'
2700
    url = 'http://pleasant-thoughts.com'
2701
    get_first_comic_link = get_a_navi_navifirst
2702
    get_navi_link = get_link_rel_next
2703
2704
    @classmethod
2705
    def get_comic_info(cls, soup, link):
2706
        """Get information about a particular comics."""
2707
        post = soup.find('div', class_='post-content')
2708
        title = post.find('h2', class_='post-title').string
2709
        imgs = post.find("div", class_="entry").find_all("img")
2710
        return {
2711
            'title': title,
2712
            'img': [i['src'] for i in imgs],
2713
        }
2714
2715
2716
class MisterAndMe(GenericNavigableComic):
2717
    """Class to retrieve Mister & Me Comics."""
2718
    # Also on http://www.gocomics.com/mister-and-me
2719
    # Also on https://tapastic.com/series/Mister-and-Me
2720
    name = 'mister'
2721
    long_name = 'Mister & Me'
2722
    url = 'http://www.mister-and-me.com'
2723
    get_first_comic_link = get_a_comicnavbase_comicnavfirst
2724
    get_navi_link = get_link_rel_next
2725
2726
    @classmethod
2727
    def get_comic_info(cls, soup, link):
2728
        """Get information about a particular comics."""
2729
        title = soup.find('h2', class_='post-title').string
2730
        author = soup.find("span", class_="post-author").find("a").string
2731
        date_str = soup.find("span", class_="post-date").string
2732
        day = string_to_date(date_str, "%B %d, %Y")
2733
        imgs = soup.find("div", id="comic").find_all("img")
2734
        assert all(i['alt'] == i['title'] for i in imgs)
2735
        assert len(imgs) <= 1, imgs
2736
        alt = imgs[0]['alt'] if imgs else ""
2737
        return {
2738
            'img': [i['src'] for i in imgs],
2739
            'title': title,
2740
            'alt': alt,
2741
            'author': author,
2742
            'day': day.day,
2743
            'month': day.month,
2744
            'year': day.year
2745
        }
2746
2747
2748
class LastPlaceComics(GenericNavigableComic):
2749
    """Class to retrieve Last Place Comics."""
2750
    name = 'lastplace'
2751
    long_name = 'Last Place Comics'
2752
    url = "http://lastplacecomics.com"
2753
    get_first_comic_link = get_a_comicnavbase_comicnavfirst
2754
    get_navi_link = get_link_rel_next
2755
2756
    @classmethod
2757
    def get_comic_info(cls, soup, link):
2758
        """Get information about a particular comics."""
2759
        title = soup.find('h2', class_='post-title').string
2760
        author = soup.find("span", class_="post-author").find("a").string
2761
        date_str = soup.find("span", class_="post-date").string
2762
        day = string_to_date(date_str, "%B %d, %Y")
2763
        imgs = soup.find("div", id="comic").find_all("img")
2764
        assert all(i['alt'] == i['title'] for i in imgs)
2765
        assert len(imgs) <= 1, imgs
2766
        alt = imgs[0]['alt'] if imgs else ""
2767
        return {
2768
            'img': [i['src'] for i in imgs],
2769
            'title': title,
2770
            'alt': alt,
2771
            'author': author,
2772
            'day': day.day,
2773
            'month': day.month,
2774
            'year': day.year
2775
        }
2776
2777
2778
class TalesOfAbsurdity(GenericNavigableComic):
2779
    """Class to retrieve Tales Of Absurdity comics."""
2780
    # Also on http://tapastic.com/series/Tales-Of-Absurdity
2781
    # Also on http://talesofabsurdity.tumblr.com
2782
    name = 'absurdity'
2783
    long_name = 'Tales of Absurdity'
2784
    url = 'http://talesofabsurdity.com'
2785
    _categories = ('ABSURDITY', )
2786
    get_first_comic_link = get_a_navi_navifirst
2787
    get_navi_link = get_a_navi_comicnavnext_navinext
2788
2789
    @classmethod
2790
    def get_comic_info(cls, soup, link):
2791
        """Get information about a particular comics."""
2792
        title = soup.find('h2', class_='post-title').string
2793
        author = soup.find("span", class_="post-author").find("a").string
2794
        date_str = soup.find("span", class_="post-date").string
2795
        day = string_to_date(date_str, "%B %d, %Y")
2796
        imgs = soup.find("div", id="comic").find_all("img")
2797
        assert all(i['alt'] == i['title'] for i in imgs)
2798
        alt = imgs[0]['alt'] if imgs else ""
2799
        return {
2800
            'img': [i['src'] for i in imgs],
2801
            'title': title,
2802
            'alt': alt,
2803
            'author': author,
2804
            'day': day.day,
2805
            'month': day.month,
2806
            'year': day.year
2807
        }
2808
2809
2810
class EndlessOrigami(GenericComicNotWorking, GenericNavigableComic):  # Nav not working
2811
    """Class to retrieve Endless Origami Comics."""
2812
    name = "origami"
2813
    long_name = "Endless Origami"
2814
    url = "http://endlessorigami.com"
2815
    get_first_comic_link = get_a_navi_navifirst
2816
    get_navi_link = get_link_rel_next
2817
2818
    @classmethod
2819
    def get_comic_info(cls, soup, link):
2820
        """Get information about a particular comics."""
2821
        title = soup.find('h2', class_='post-title').string
2822
        author = soup.find("span", class_="post-author").find("a").string
2823
        date_str = soup.find("span", class_="post-date").string
2824
        day = string_to_date(date_str, "%B %d, %Y")
2825
        imgs = soup.find("div", id="comic").find_all("img")
2826
        assert all(i['alt'] == i['title'] for i in imgs)
2827
        alt = imgs[0]['alt'] if imgs else ""
2828
        return {
2829
            'img': [i['src'] for i in imgs],
2830
            'title': title,
2831
            'alt': alt,
2832
            'author': author,
2833
            'day': day.day,
2834
            'month': day.month,
2835
            'year': day.year
2836
        }
2837 View Code Duplication
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2838
2839
class PlanC(GenericNavigableComic):
2840
    """Class to retrieve Plan C comics."""
2841
    name = 'planc'
2842
    long_name = 'Plan C'
2843
    url = 'http://www.plancomic.com'
2844
    get_first_comic_link = get_a_navi_navifirst
2845
    get_navi_link = get_a_navi_comicnavnext_navinext
2846
2847
    @classmethod
2848
    def get_comic_info(cls, soup, link):
2849
        """Get information about a particular comics."""
2850
        title = soup.find('h2', class_='post-title').string
2851
        date_str = soup.find("span", class_="post-date").string
2852
        day = string_to_date(date_str, "%B %d, %Y")
2853
        imgs = soup.find('div', id='comic').find_all('img')
2854
        return {
2855
            'title': title,
2856
            'img': [i['src'] for i in imgs],
2857 View Code Duplication
            'month': day.month,
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2858
            'year': day.year,
2859
            'day': day.day,
2860
        }
2861
2862
2863
class BuniComic(GenericNavigableComic):
2864
    """Class to retrieve Buni Comics."""
2865
    name = 'buni'
2866
    long_name = 'BuniComics'
2867
    url = 'http://www.bunicomic.com'
2868
    get_first_comic_link = get_a_comicnavbase_comicnavfirst
2869
    get_navi_link = get_link_rel_next
2870
2871
    @classmethod
2872
    def get_comic_info(cls, soup, link):
2873
        """Get information about a particular comics."""
2874
        imgs = soup.find('div', id='comic').find_all('img')
2875
        assert all(i['alt'] == i['title'] for i in imgs)
2876
        assert len(imgs) == 1, imgs
2877
        return {
2878
            'img': [i['src'] for i in imgs],
2879
            'title': imgs[0]['title'],
2880
        }
2881
2882
2883
class GenericCommitStrip(GenericNavigableComic):
2884
    """Generic class to retrieve Commit Strips in different languages."""
2885
    get_navi_link = get_a_rel_next
2886
    get_first_comic_link = simulate_first_link
2887
    first_url = NotImplemented
2888
2889
    @classmethod
2890
    def get_comic_info(cls, soup, link):
2891
        """Get information about a particular comics."""
2892
        desc = soup.find('meta', property='og:description')['content']
2893
        title = soup.find('meta', property='og:title')['content']
2894
        imgs = soup.find('div', class_='entry-content').find_all('img')
2895 View Code Duplication
        title2 = ' '.join(i.get('title', '') for i in imgs)
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2896
        return {
2897
            'title': title,
2898
            'title2': title2,
2899
            'description': desc,
2900
            'img': [urljoin_wrapper(cls.url, convert_iri_to_plain_ascii_uri(i['src'])) for i in imgs],
2901
        }
2902
2903
2904
class CommitStripFr(GenericCommitStrip):
2905
    """Class to retrieve Commit Strips in French."""
2906
    name = 'commit_fr'
2907
    long_name = 'Commit Strip (Fr)'
2908
    url = 'http://www.commitstrip.com/fr'
2909
    _categories = ('FRANCAIS', )
2910
    first_url = 'http://www.commitstrip.com/fr/2012/02/22/interview/'
2911
2912
2913
class CommitStripEn(GenericCommitStrip):
2914
    """Class to retrieve Commit Strips in English."""
2915
    name = 'commit_en'
2916
    long_name = 'Commit Strip (En)'
2917
    url = 'http://www.commitstrip.com/en'
2918
    first_url = 'http://www.commitstrip.com/en/2012/02/22/interview/'
2919
2920
2921
class GenericBoumerie(GenericNavigableComic):
2922
    """Generic class to retrieve Boumeries comics in different languages."""
2923
    # Also on http://boumeries.tumblr.com
2924
    get_first_comic_link = get_a_navi_navifirst
2925
    get_navi_link = get_link_rel_next
2926
    date_format = NotImplemented
2927
    lang = NotImplemented
2928
2929
    @classmethod
2930
    def get_comic_info(cls, soup, link):
2931
        """Get information about a particular comics."""
2932
        title = soup.find('h2', class_='post-title').string
2933
        short_url = soup.find('link', rel='shortlink')['href']
2934
        author = soup.find("span", class_="post-author").find("a").string
2935
        date_str = soup.find('span', class_='post-date').string
2936
        day = string_to_date(date_str, cls.date_format, cls.lang)
2937
        imgs = soup.find('div', id='comic').find_all('img')
2938
        assert all(i['alt'] == i['title'] for i in imgs)
2939
        return {
2940
            'short_url': short_url,
2941
            'img': [i['src'] for i in imgs],
2942
            'title': title,
2943
            'author': author,
2944
            'month': day.month,
2945
            'year': day.year,
2946
            'day': day.day,
2947
        }
2948
2949
2950
class BoumerieEn(GenericBoumerie):
2951
    """Class to retrieve Boumeries comics in English."""
2952
    name = 'boumeries_en'
2953
    long_name = 'Boumeries (En)'
2954
    url = 'http://comics.boumerie.com'
2955
    _categories = ('BOUMERIES', )
2956
    date_format = "%B %d, %Y"
2957
    lang = 'en_GB.UTF-8'
2958
2959
2960
class BoumerieFr(GenericBoumerie):
2961
    """Class to retrieve Boumeries comics in French."""
2962
    name = 'boumeries_fr'
2963
    long_name = 'Boumeries (Fr)'
2964
    url = 'http://bd.boumerie.com'
2965
    _categories = ('BOUMERIES', 'FRANCAIS')
2966
    date_format = "%B %d, %Y"  # Used to be "%A, %d %B %Y"
2967
    lang = "fr_FR.utf8"
2968
2969
2970 View Code Duplication
class UnearthedComics(GenericNavigableComic):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
2971
    """Class to retrieve Unearthed comics."""
2972
    # Also on http://tapastic.com/series/UnearthedComics
2973
    # Also on https://unearthedcomics.tumblr.com
2974
    name = 'unearthed'
2975
    long_name = 'Unearthed Comics'
2976
    url = 'http://unearthedcomics.com'
2977
    _categories = ('UNEARTHED', )
2978
    get_navi_link = get_link_rel_next
2979
    get_first_comic_link = simulate_first_link
2980
    first_url = 'http://unearthedcomics.com/comics/world-with-turn-signals/'
2981
2982
    @classmethod
2983
    def get_comic_info(cls, soup, link):
2984
        """Get information about a particular comics."""
2985
        short_url = soup.find('link', rel='shortlink')['href']
2986
        title_elt = soup.find('h1') or soup.find('h2')
2987
        title = title_elt.string if title_elt else ""
2988
        desc = soup.find('meta', property='og:description')
2989
        date_str = soup.find('time', class_='published updated hidden')['datetime']
2990
        day = string_to_date(date_str, "%Y-%m-%d")
2991
        post = soup.find('div', class_="entry content entry-content type-portfolio")
2992
        imgs = post.find_all('img')
2993
        return {
2994
            'title': title,
2995
            'description': desc,
2996
            'url2': short_url,
2997
            'img': [i['src'] for i in imgs],
2998
            'month': day.month,
2999
            'year': day.year,
3000
            'day': day.day,
3001
        }
3002
3003
3004
class Optipess(GenericNavigableComic):
3005
    """Class to retrieve Optipess comics."""
3006
    name = 'optipess'
3007
    long_name = 'Optipess'
3008
    url = 'http://www.optipess.com'
3009
    get_first_comic_link = get_a_navi_navifirst
3010
    get_navi_link = get_link_rel_next
3011
3012
    @classmethod
3013
    def get_comic_info(cls, soup, link):
3014
        """Get information about a particular comics."""
3015
        title = soup.find('h2', class_='post-title').string
3016
        author = soup.find("span", class_="post-author").find("a").string
3017
        comic = soup.find('div', id='comic')
3018
        imgs = comic.find_all('img') if comic else []
3019
        alt = imgs[0]['title'] if imgs else ""
3020
        assert all(i['alt'] == i['title'] == alt for i in imgs)
3021
        date_str = soup.find('span', class_='post-date').string
3022
        day = string_to_date(date_str, "%B %d, %Y")
3023
        return {
3024
            'title': title,
3025
            'alt': alt,
3026
            'author': author,
3027
            'img': [i['src'] for i in imgs],
3028
            'month': day.month,
3029
            'year': day.year,
3030
            'day': day.day,
3031
        }
3032
3033
3034
class PainTrainComic(GenericNavigableComic):
3035
    """Class to retrieve Pain Train Comics."""
3036
    name = 'paintrain'
3037
    long_name = 'Pain Train Comics'
3038
    url = 'http://paintraincomic.com'
3039
    get_first_comic_link = get_a_navi_navifirst
3040
    get_navi_link = get_link_rel_next
3041
3042
    @classmethod
3043
    def get_comic_info(cls, soup, link):
3044
        """Get information about a particular comics."""
3045
        title = soup.find('h2', class_='post-title').string
3046
        short_url = soup.find('link', rel='shortlink')['href']
3047
        short_url_re = re.compile('^%s/\\?p=([0-9]*)' % cls.url)
3048
        num = int(short_url_re.match(short_url).groups()[0])
3049
        imgs = soup.find('div', id='comic').find_all('img')
3050
        alt = imgs[0]['title']
3051
        assert all(i['alt'] == i['title'] == alt for i in imgs)
3052
        date_str = soup.find('span', class_='post-date').string
3053
        day = string_to_date(date_str, "%d/%m/%Y")
3054
        return {
3055
            'short_url': short_url,
3056
            'num': num,
3057
            'img': [i['src'] for i in imgs],
3058
            'month': day.month,
3059
            'year': day.year,
3060
            'day': day.day,
3061
            'alt': alt,
3062
            'title': title,
3063
        }
3064
3065
3066
class MoonBeard(GenericNavigableComic):
3067
    """Class to retrieve MoonBeard comics."""
3068
    # Also on http://squireseses.tumblr.com
3069
    # Also on http://www.webtoons.com/en/comedy/moon-beard/list?title_no=471
3070
    name = 'moonbeard'
3071
    long_name = 'Moon Beard'
3072
    url = 'http://moonbeard.com'
3073
    _categories = ('MOONBEARD', )
3074
    get_first_comic_link = get_a_navi_navifirst
3075
    get_navi_link = get_a_navi_navinext
3076
3077
    @classmethod
3078
    def get_comic_info(cls, soup, link):
3079
        """Get information about a particular comics."""
3080
        title = soup.find('h2', class_='post-title').string
3081
        short_url = soup.find('link', rel='shortlink')['href']
3082
        short_url_re = re.compile('^%s/\\?p=([0-9]*)' % cls.url)
3083
        num = int(short_url_re.match(short_url).groups()[0])
3084
        imgs = soup.find('div', id='comic').find_all('img')
3085
        alt = imgs[0]['title']
3086
        assert all(i['alt'] == i['title'] == alt for i in imgs)
3087
        date_str = soup.find('span', class_='post-date').string
3088
        day = string_to_date(date_str, "%B %d, %Y")
3089
        tags = ' '.join(t['content'] for t in soup.find_all('meta', property='article:tag'))
3090
        author = soup.find('span', class_='post-author').string
3091
        return {
3092
            'short_url': short_url,
3093
            'num': num,
3094
            'img': [i['src'] for i in imgs],
3095
            'month': day.month,
3096
            'year': day.year,
3097
            'day': day.day,
3098
            'title': title,
3099
            'tags': tags,
3100
            'alt': alt,
3101
            'author': author,
3102
        }
3103
3104
3105
class SystemComic(GenericNavigableComic):
3106
    """Class to retrieve System Comic."""
3107
    name = 'system'
3108
    long_name = 'System Comic'
3109 View Code Duplication
    url = 'http://www.systemcomic.com'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
3110
    get_navi_link = get_a_rel_next
3111
3112
    @classmethod
3113
    def get_first_comic_link(cls):
3114
        """Get link to first comics."""
3115
        return get_soup_at_url(cls.url).find('li', class_='first').find('a')
3116
3117
    @classmethod
3118
    def get_comic_info(cls, soup, link):
3119
        """Get information about a particular comics."""
3120
        title = soup.find('meta', property='og:title')['content']
3121
        desc = soup.find('meta', property='og:description')['content']
3122
        date_str = soup.find('time')["datetime"]
3123
        day = string_to_date(date_str, "%Y-%m-%d")
3124
        imgs = soup.find('figure').find_all('img')
3125
        return {
3126
            'title': title,
3127
            'description': desc,
3128
            'day': day.day,
3129
            'month': day.month,
3130
            'year': day.year,
3131
            'img': [i['src'] for i in imgs],
3132
        }
3133
3134
3135
class LittleLifeLines(GenericNavigableComic):
3136
    """Class to retrieve Little Life Lines comics."""
3137
    # Also on https://little-life-lines.tumblr.com
3138
    name = 'life'
3139
    long_name = 'Little Life Lines'
3140
    url = 'http://www.littlelifelines.com'
3141
    get_url_from_link = join_cls_url_to_href
3142
    get_first_comic_link = simulate_first_link
3143
    first_url = 'http://www.littlelifelines.com/comics/well-done'
3144
3145
    @classmethod
3146
    def get_navi_link(cls, last_soup, next_):
3147
        """Get link to next or previous comic."""
3148
        # prev is next / next is prev
3149
        li = last_soup.find('li', class_='prev' if next_ else 'next')
3150
        return li.find('a') if li else None
3151
3152
    @classmethod
3153
    def get_comic_info(cls, soup, link):
3154
        """Get information about a particular comics."""
3155
        title = soup.find('meta', property='og:title')['content']
3156
        desc = soup.find('meta', property='og:description')['content']
3157
        date_str = soup.find('time', class_='published')['datetime']
3158
        day = string_to_date(date_str, "%Y-%m-%d")
3159
        author = soup.find('a', rel='author').string
3160
        div_content = soup.find('div', class_="body entry-content")
3161
        imgs = div_content.find_all('img')
3162
        imgs = [i for i in imgs if i.get('src') is not None]
3163
        alt = imgs[0]['alt']
3164
        return {
3165
            'title': title,
3166
            'alt': alt,
3167
            'description': desc,
3168
            'author': author,
3169
            'day': day.day,
3170
            'month': day.month,
3171
            'year': day.year,
3172
            'img': [i['src'] for i in imgs],
3173
        }
3174
3175
3176
class GenericWordPressInkblot(GenericNavigableComic):
3177
    """Generic class to retrieve comics using WordPress with Inkblot."""
3178
    get_navi_link = get_link_rel_next
3179
3180
    @classmethod
3181
    def get_first_comic_link(cls):
3182
        """Get link to first comics."""
3183
        return get_soup_at_url(cls.url).find('a', class_='webcomic-link webcomic1-link first-webcomic-link first-webcomic1-link')
3184
3185
    @classmethod
3186
    def get_comic_info(cls, soup, link):
3187
        """Get information about a particular comics."""
3188
        title = soup.find('meta', property='og:title')['content']
3189
        imgs = soup.find('div', class_='webcomic-image').find_all('img')
3190
        date_str = soup.find('meta', property='article:published_time')['content'][:10]
3191
        day = string_to_date(date_str, "%Y-%m-%d")
3192
        return {
3193
            'title': title,
3194
            'day': day.day,
3195
            'month': day.month,
3196
            'year': day.year,
3197
            'img': [i['src'] for i in imgs],
3198
        }
3199
3200
3201
class EverythingsStupid(GenericWordPressInkblot):
3202
    """Class to retrieve Everything's stupid Comics."""
3203
    # Also on http://tapastic.com/series/EverythingsStupid
3204
    # Also on http://www.webtoons.com/en/challenge/everythings-stupid/list?title_no=14591
3205
    # Also on http://everythingsstupidcomics.tumblr.com
3206
    name = 'stupid'
3207
    long_name = "Everything's Stupid"
3208
    url = 'http://everythingsstupid.net'
3209
3210
3211
class TheIsmComics(GenericDeletedComic, GenericWordPressInkblot):
3212
    """Class to retrieve The Ism Comics."""
3213
    # Also on https://tapastic.com/series/TheIsm (?)
3214
    name = 'theism'
3215
    long_name = "The Ism"
3216
    url = 'http://www.theism-comics.com'
3217
3218
3219
class WoodenPlankStudios(GenericWordPressInkblot):
3220
    """Class to retrieve Wooden Plank Studios comics."""
3221
    name = 'woodenplank'
3222
    long_name = 'Wooden Plank Studios'
3223
    url = 'http://woodenplankstudios.com'
3224
3225
3226
class ElectricBunnyComic(GenericNavigableComic):
3227
    """Class to retrieve Electric Bunny Comics."""
3228
    # Also on http://electricbunnycomics.tumblr.com
3229
    name = 'bunny'
3230
    long_name = 'Electric Bunny Comic'
3231
    url = 'http://www.electricbunnycomics.com/View/Comic/153/Welcome+to+Hell'
3232
    get_url_from_link = join_cls_url_to_href
3233
3234
    @classmethod
3235
    def get_first_comic_link(cls):
3236
        """Get link to first comics."""
3237
        return get_soup_at_url(cls.url).find('img', alt='First').parent
3238
3239
    @classmethod
3240
    def get_navi_link(cls, last_soup, next_):
3241
        """Get link to next or previous comic."""
3242
        img = last_soup.find('img', alt='Next' if next_ else 'Back')
3243
        return img.parent if img else None
3244
3245
    @classmethod
3246
    def get_comic_info(cls, soup, link):
3247
        """Get information about a particular comics."""
3248
        title = soup.find('meta', property='og:title')['content']
3249
        imgs = soup.find_all('meta', property='og:image')
3250
        return {
3251
            'title': title,
3252
            'img': [i['content'] for i in imgs],
3253
        }
3254
3255
3256
class SheldonComics(GenericNavigableComic):
3257
    """Class to retrieve Sheldon comics."""
3258
    # Also on http://www.gocomics.com/sheldon
3259
    name = 'sheldon'
3260
    long_name = 'Sheldon Comics'
3261
    url = 'http://www.sheldoncomics.com'
3262
3263 View Code Duplication
    @classmethod
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
3264
    def get_first_comic_link(cls):
3265
        """Get link to first comics."""
3266
        return get_soup_at_url(cls.url).find("a", id="nav-first")
3267
3268
    @classmethod
3269
    def get_navi_link(cls, last_soup, next_):
3270
        """Get link to next or previous comic."""
3271
        for link in last_soup.find_all("a", id="nav-next" if next_ else "nav-prev"):
3272
            if link['href'] != 'http://www.sheldoncomics.com':
3273
                return link
3274
        return None
3275
3276
    @classmethod
3277
    def get_comic_info(cls, soup, link):
3278
        """Get information about a particular comics."""
3279
        imgs = soup.find("div", id="comic-foot").find_all("img")
3280
        assert all(i['alt'] == i['title'] for i in imgs)
3281
        assert len(imgs) == 1, imgs
3282
        title = imgs[0]['title']
3283
        return {
3284
            'title': title,
3285
            'img': [i['src'] for i in imgs],
3286
        }
3287
3288
3289
class ManVersusManatee(GenericNavigableComic):
3290
    """Class to retrieve Man Versus Manatee comics."""
3291
    url = 'http://manvsmanatee.com'
3292
    name = 'manvsmanatee'
3293
    long_name = 'Man Versus Manatee'
3294
    get_first_comic_link = get_a_comicnavbase_comicnavfirst
3295
    get_navi_link = get_a_comicnavbase_comicnavnext
3296
3297
    @classmethod
3298
    def get_comic_info(cls, soup, link):
3299
        """Get information about a particular comics."""
3300
        title = soup.find('h2', class_='post-title').string
3301
        imgs = soup.find('div', id='comic').find_all('img')
3302
        date_str = soup.find('span', class_='post-date').string
3303
        day = string_to_date(date_str, "%B %d, %Y")
3304
        return {
3305
            'img': [i['src'] for i in imgs],
3306 View Code Duplication
            'title': title,
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
3307
            'month': day.month,
3308
            'year': day.year,
3309
            'day': day.day,
3310
        }
3311
3312
3313
class TheMeerkatguy(GenericNavigableComic):
3314
    """Class to retrieve The Meerkatguy comics."""
3315
    long_name = 'The Meerkatguy'
3316
    url = 'http://www.themeerkatguy.com'
3317
    name = 'meerkatguy'
3318
    get_first_comic_link = get_a_comicnavbase_comicnavfirst
3319
    get_navi_link = get_a_comicnavbase_comicnavnext
3320
3321
    @classmethod
3322
    def get_comic_info(cls, soup, link):
3323
        """Get information about a particular comics."""
3324
        title = soup.find('title').string
3325
        imgs = soup.find_all('meta', property='og:image')
3326
        return {
3327
            'img': [i['content'] for i in imgs],
3328
            'title': title,
3329
        }
3330
3331
3332
class Ubertool(GenericNavigableComic):
3333
    """Class to retrieve Ubertool comics."""
3334
    # Also on https://ubertool.tumblr.com
3335
    # Also on https://tapastic.com/series/ubertool
3336
    name = 'ubertool'
3337
    long_name = 'Ubertool'
3338
    url = 'http://ubertoolcomic.com'
3339
    _categories = ('UBERTOOL', )
3340
    get_first_comic_link = get_a_comicnavbase_comicnavfirst
3341
    get_navi_link = get_a_comicnavbase_comicnavnext
3342
3343
    @classmethod
3344
    def get_comic_info(cls, soup, link):
3345
        """Get information about a particular comics."""
3346
        title = soup.find('h2', class_='post-title').string
3347
        date_str = soup.find('span', class_='post-date').string
3348
        day = string_to_date(date_str, "%B %d, %Y")
3349
        imgs = soup.find('div', id='comic').find_all('img')
3350
        return {
3351
            'img': [i['src'] for i in imgs],
3352
            'title': title,
3353
            'month': day.month,
3354
            'year': day.year,
3355
            'day': day.day,
3356
        }
3357
3358
3359
class EarthExplodes(GenericNavigableComic):
3360 View Code Duplication
    """Class to retrieve The Earth Explodes comics."""
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
3361
    name = 'earthexplodes'
3362
    long_name = 'The Earth Explodes'
3363
    url = 'http://www.earthexplodes.com'
3364
    get_url_from_link = join_cls_url_to_href
3365
    get_first_comic_link = simulate_first_link
3366
    first_url = 'http://www.earthexplodes.com/comics/000/'
3367
3368
    @classmethod
3369
    def get_navi_link(cls, last_soup, next_):
3370
        """Get link to next or previous comic."""
3371
        return last_soup.find('a', id='next' if next_ else 'prev')
3372
3373
    @classmethod
3374
    def get_comic_info(cls, soup, link):
3375
        """Get information about a particular comics."""
3376
        title = soup.find('title').string
3377
        imgs = soup.find('div', id='image').find_all('img')
3378
        alt = imgs[0].get('title', '')
3379
        return {
3380
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
3381
            'title': title,
3382
            'alt': alt,
3383
        }
3384
3385
3386
class PomComics(GenericNavigableComic):
3387
    """Class to retrieve PomComics."""
3388
    name = 'pom'
3389
    long_name = 'Pom Comics / Piece of Me'
3390
    url = 'http://www.pomcomic.com'
3391
    get_url_from_link = join_cls_url_to_href
3392
3393
    @classmethod
3394
    def get_first_comic_link(cls):
3395
        """Get link to first comics."""
3396
        return get_soup_at_url(cls.url).find('a', class_='btn-first')
3397
3398
    @classmethod
3399
    def get_navi_link(cls, last_soup, next_):
3400
        """Get link to next or previous comic."""
3401
        return last_soup.find('a', class_='btn-next' if next_ else 'btn-prev')
3402
3403
    @classmethod
3404
    def get_comic_info(cls, soup, link):
3405
        """Get information about a particular comics."""
3406
        title = soup.find('h1').string
3407
        desc = soup.find('meta', property='og:description')['content']
3408
        tags = soup.find('meta', attrs={'name': 'keywords'})['content']
3409
        imgs = soup.find('div', class_='comic').find_all('img')
3410
        return {
3411
            'title': title,
3412
            'desc': desc,
3413
            'tags': tags,
3414
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
3415
        }
3416
3417
3418
class CubeDrone(GenericComicNotWorking, GenericNavigableComic):  # Website has changed
3419
    """Class to retrieve Cube Drone comics."""
3420
    name = 'cubedrone'
3421
    long_name = 'Cube Drone'
3422
    url = 'http://cube-drone.com/comics'
3423
    get_url_from_link = join_cls_url_to_href
3424
3425
    @classmethod
3426
    def get_first_comic_link(cls):
3427
        """Get link to first comics."""
3428
        return get_soup_at_url(cls.url).find('span', class_='glyphicon glyphicon-backward').parent
3429
3430
    @classmethod
3431
    def get_navi_link(cls, last_soup, next_):
3432
        """Get link to next or previous comic."""
3433
        class_ = 'glyphicon glyphicon-chevron-' + ('right' if next_ else 'left')
3434
        return last_soup.find('span', class_=class_).parent
3435
3436
    @classmethod
3437
    def get_comic_info(cls, soup, link):
3438
        """Get information about a particular comics."""
3439
        title = soup.find('meta', attrs={'name': 'twitter:title'})['content']
3440
        url2 = soup.find('meta', attrs={'name': 'twitter:url'})['content']
3441
        # date_str = soup.find('h2', class_='comic_title').find('small').string
3442
        # day = string_to_date(date_str, "%B %d, %Y, %I:%M %p")
3443
        imgs = soup.find_all('img', class_='comic img-responsive')
3444
        title2 = imgs[0]['title']
3445
        alt = imgs[0]['alt']
3446
        return {
3447
            'url2': url2,
3448
            'title': title,
3449
            'title2': title2,
3450
            'alt': alt,
3451
            'img': [i['src'] for i in imgs],
3452
        }
3453
3454
3455
class MakeItStoopid(GenericDeletedComic, GenericNavigableComic):
3456
    """Class to retrieve Make It Stoopid Comics."""
3457
    name = 'stoopid'
3458
    long_name = 'Make it stoopid'
3459
    url = 'http://makeitstoopid.com/comic.php'
3460
3461
    @classmethod
3462
    def get_nav(cls, soup):
3463
        """Get the navigation elements from soup object."""
3464
        cnav = soup.find_all(class_='cnav')
3465
        nav1, nav2 = cnav[:5], cnav[5:]
3466
        assert nav1 == nav2
3467
        # begin, prev, archive, next_, end = nav1
3468
        return [None if i.get('href') is None else i for i in nav1]
3469
3470
    @classmethod
3471
    def get_first_comic_link(cls):
3472
        """Get link to first comics."""
3473
        return cls.get_nav(get_soup_at_url(cls.url))[0]
3474
3475
    @classmethod
3476
    def get_navi_link(cls, last_soup, next_):
3477
        """Get link to next or previous comic."""
3478
        return cls.get_nav(last_soup)[3 if next_ else 1]
3479
3480
    @classmethod
3481
    def get_comic_info(cls, soup, link):
3482
        """Get information about a particular comics."""
3483
        title = link['title']
3484
        imgs = soup.find_all('img', id='comicimg')
3485
        return {
3486
            'title': title,
3487
            'img': [i['src'] for i in imgs],
3488 View Code Duplication
        }
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
3489
3490
3491
class OffTheLeashDog(GenericNavigableComic):
3492
    """Class to retrieve Off The Leash Dog comics."""
3493
    # Also on http://rupertfawcettsdoggyblog.tumblr.com
3494
    # Also on http://www.rupertfawcettcartoons.com
3495
    name = 'offtheleash'
3496
    long_name = 'Off The Leash Dog'
3497
    url = 'http://offtheleashdogcartoons.com'
3498
    _categories = ('FAWCETT', )
3499
    get_navi_link = get_a_rel_next
3500
    get_first_comic_link = simulate_first_link
3501
    first_url = 'http://offtheleashdogcartoons.com/uncategorized/can-i-help-you/'
3502
3503
    @classmethod
3504
    def get_comic_info(cls, soup, link):
3505
        """Get information about a particular comics."""
3506
        title = soup.find("h1", class_="entry-title").string
3507
        imgs = soup.find('div', class_='entry-content').find_all('img')
3508
        return {
3509
            'title': title,
3510
            'img': [i['src'] for i in imgs],
3511
        }
3512
3513
3514
class MacadamValley(GenericNavigableComic):
3515
    """Class to retrieve Macadam Valley comics."""
3516 View Code Duplication
    name = 'macadamvalley'
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
3517
    long_name = 'Macadam Valley'
3518
    url = 'http://macadamvalley.com'
3519
    get_navi_link = get_a_rel_next
3520
    get_first_comic_link = simulate_first_link
3521
    first_url = 'http://macadamvalley.com/le-debut-de-la-fin/'
3522
3523
    @classmethod
3524
    def get_comic_info(cls, soup, link):
3525
        """Get information about a particular comics."""
3526
        title = soup.find("h1", class_="entry-title").string
3527
        img = soup.find('div', class_='entry-content').find('img')
3528
        date_str = soup.find('time', class_='entry-date')['datetime']
3529
        date_str = date_str[:10]
3530
        day = string_to_date(date_str, "%Y-%m-%d")
3531
        author = soup.find('a', rel='author').string
3532
        return {
3533
            'title': title,
3534
            'img': [i['src'] for i in [img]],
3535
            'day': day.day,
3536
            'month': day.month,
3537
            'year': day.year,
3538
            'author': author,
3539
        }
3540
3541 View Code Duplication
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
3542
class MarketoonistComics(GenericNavigableComic):
3543
    """Class to retrieve Marketoonist Comics."""
3544
    name = 'marketoonist'
3545
    long_name = 'Marketoonist'
3546
    url = 'https://marketoonist.com/cartoons'
3547
    get_first_comic_link = simulate_first_link
3548
    get_navi_link = get_link_rel_next
3549
    first_url = 'https://marketoonist.com/2002/10/the-8-types-of-brand-managers-2.html'
3550
3551
    @classmethod
3552
    def get_comic_info(cls, soup, link):
3553
        """Get information about a particular comics."""
3554
        imgs = soup.find_all('meta', property='og:image')
3555
        date_str = soup.find('meta', property='article:published_time')['content'][:10]
3556
        day = string_to_date(date_str, "%Y-%m-%d")
3557
        title = soup.find('meta', property='og:title')['content']
3558
        return {
3559
            'img': [i['content'] for i in imgs],
3560
            'day': day.day,
3561
            'month': day.month,
3562
            'year': day.year,
3563
            'title': title,
3564
        }
3565
3566
3567
class ConsoliaComics(GenericNavigableComic):
3568
    """Class to retrieve Consolia comics."""
3569
    name = 'consolia'
3570
    long_name = 'consolia'
3571
    url = 'https://consolia-comic.com'
3572
    get_url_from_link = join_cls_url_to_href
3573
3574
    @classmethod
3575
    def get_first_comic_link(cls):
3576
        """Get link to first comics."""
3577
        return get_soup_at_url(cls.url).find('a', class_='first')
3578
3579
    @classmethod
3580
    def get_navi_link(cls, last_soup, next_):
3581
        """Get link to next or previous comic."""
3582
        return last_soup.find('a', class_='next' if next_ else 'prev')
3583
3584
    @classmethod
3585
    def get_comic_info(cls, soup, link):
3586 View Code Duplication
        """Get information about a particular comics."""
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
3587
        title = soup.find('meta', property='og:title')['content']
3588
        date_str = soup.find('time')["datetime"]
3589
        day = string_to_date(date_str, "%Y-%m-%d")
3590
        imgs = soup.find_all('meta', property='og:image')
3591
        return {
3592
            'title': title,
3593
            'img': [i['content'] for i in imgs],
3594
            'day': day.day,
3595
            'month': day.month,
3596
            'year': day.year,
3597
        }
3598
3599
3600
class GenericBlogspotComic(GenericNavigableComic):
3601
    """Generic class to retrieve comics from Blogspot."""
3602
    get_first_comic_link = simulate_first_link
3603
    first_url = NotImplemented
3604
    _categories = ('BLOGSPOT', )
3605
3606
    @classmethod
3607 View Code Duplication
    def get_navi_link(cls, last_soup, next_):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
3608
        """Get link to next or previous comic."""
3609
        return last_soup.find('a', id='Blog1_blog-pager-newer-link' if next_ else 'Blog1_blog-pager-older-link')
3610
3611
3612
class TuMourrasMoinsBete(GenericBlogspotComic):
3613
    """Class to retrieve Tu Mourras Moins Bete comics."""
3614
    name = 'mourrasmoinsbete'
3615
    long_name = 'Tu Mourras Moins Bete'
3616
    url = 'http://tumourrasmoinsbete.blogspot.fr'
3617
    _categories = ('FRANCAIS', )
3618
    first_url = 'http://tumourrasmoinsbete.blogspot.fr/2008/06/essai.html'
3619
3620
    @classmethod
3621
    def get_comic_info(cls, soup, link):
3622
        """Get information about a particular comics."""
3623
        title = soup.find('title').string
3624
        imgs = soup.find('div', itemprop='description articleBody').find_all('img')
3625
        author = soup.find('span', itemprop='author').string
3626
        return {
3627
            'img': [i['src'] for i in imgs],
3628
            'author': author,
3629
            'title': title,
3630
        }
3631
3632
3633
class Octopuns(GenericBlogspotComic):
3634
    """Class to retrieve Octopuns comics."""
3635
    # Also on http://octopuns.tumblr.com
3636
    name = 'octopuns'
3637
    long_name = 'Octopuns'
3638
    url = 'http://www.octopuns.net'  # or http://octopuns.blogspot.fr/
3639
    first_url = 'http://octopuns.blogspot.com/2010/12/17122010-always-read-label.html'
3640
3641
    @classmethod
3642
    def get_comic_info(cls, soup, link):
3643
        """Get information about a particular comics."""
3644
        title = soup.find('h3', class_='post-title entry-title').string
3645
        date_str = soup.find('h2', class_='date-header').string
3646
        day = string_to_date(date_str, "%A, %B %d, %Y")
3647
        imgs = soup.find_all('link', rel='image_src')
3648
        return {
3649
            'img': [i['href'] for i in imgs],
3650
            'title': title,
3651
            'day': day.day,
3652
            'month': day.month,
3653
            'year': day.year,
3654
        }
3655
3656
3657
class GeekAndPoke(GenericNavigableComic):
3658
    """Class to retrieve Geek And Poke comics."""
3659
    name = 'geek'
3660
    long_name = 'Geek And Poke'
3661
    url = 'http://geek-and-poke.com'
3662
    get_url_from_link = join_cls_url_to_href
3663
    get_first_comic_link = simulate_first_link
3664
    first_url = 'http://geek-and-poke.com/geekandpoke/2006/8/27/a-new-place-for-a-not-so-old-blog.html'
3665
3666
    @classmethod
3667
    def get_navi_link(cls, last_soup, next_):
3668
        """Get link to next or previous comic."""
3669
        return last_soup.find('a', class_='prev-item' if next_ else 'next-item')
3670
3671
    @classmethod
3672 View Code Duplication
    def get_comic_info(cls, soup, link):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
3673
        """Get information about a particular comics."""
3674
        title = soup.find('meta', property='og:title')['content']
3675
        desc = soup.find('meta', property='og:description')
3676
        desc_str = "" if desc is None else desc['content']
3677
        date_str = soup.find('time', class_='published')['datetime']
3678
        day = string_to_date(date_str, "%Y-%m-%d")
3679
        author = soup.find('a', rel='author').string
3680
        div_content = (soup.find('div', class_="body entry-content") or
3681
                       soup.find('div', class_="special-content"))
3682
        imgs = div_content.find_all('img')
3683
        imgs = [i for i in imgs if i.get('src') is not None]
3684
        assert all('title' not in i or i['alt'] == i['title'] for i in imgs)
3685
        alt = imgs[0].get('alt', "") if imgs else []
3686
        return {
3687
            'title': title,
3688
            'alt': alt,
3689
            'description': desc_str,
3690
            'author': author,
3691
            'day': day.day,
3692
            'month': day.month,
3693 View Code Duplication
            'year': day.year,
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
3694
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
3695
        }
3696
3697
3698
class GloryOwlComix(GenericBlogspotComic):
3699
    """Class to retrieve Glory Owl comics."""
3700
    name = 'gloryowl'
3701
    long_name = 'Glory Owl'
3702
    url = 'http://gloryowlcomix.blogspot.fr'
3703
    _categories = ('NSFW', 'FRANCAIS')
3704
    first_url = 'http://gloryowlcomix.blogspot.fr/2013/02/1_7.html'
3705
3706
    @classmethod
3707
    def get_comic_info(cls, soup, link):
3708
        """Get information about a particular comics."""
3709
        title = soup.find('title').string
3710
        imgs = soup.find_all('link', rel='image_src')
3711
        author = soup.find('a', rel='author').string
3712
        return {
3713
            'img': [i['href'] for i in imgs],
3714
            'author': author,
3715
            'title': title,
3716
        }
3717
3718
3719
class GenericSquareSpace(GenericNavigableComic):
3720
    """Generic class to retrieve comics using SquareSpace."""
3721
    _categories = ('SQUARESPACE', )
3722
    get_url_from_link = join_cls_url_to_href
3723
    get_first_comic_link = simulate_first_link
3724
3725
    @classmethod
3726
    def get_navi_link(cls, last_soup, next_):
3727
        """Get link to next or previous comic."""
3728
        return last_soup.find('a', id='prevLink' if next_ else 'nextLink')
3729
3730
    @classmethod
3731
    def get_images(cls, soup):
3732
        """Get image URLs for a comic."""
3733
        raise NotImplementedError
3734
3735
    @classmethod
3736
    def get_comic_info(cls, soup, link):
3737
        """Get information about a particular comics."""
3738
        title = soup.find('meta', property='og:title')['content']
3739
        desc = soup.find('meta', property='og:description')['content']
3740
        date_str = soup.find('time', itemprop='datePublished')["datetime"]
3741
        day = string_to_date(date_str, "%Y-%m-%d")
3742
        author = soup.find('a', rel='author').string
3743
        return {
3744
            'title': title,
3745
            'img': cls.get_images(soup),
3746
            'month': day.month,
3747
            'year': day.year,
3748
            'day': day.day,
3749
            'author': author,
3750
            'description': desc,
3751
        }
3752
3753
3754
class AtRandomComics(GenericSquareSpace):
3755
    """Class to retrieve At Random Comics."""
3756
    name = 'atrandom'
3757
    long_name = 'At Random Comics'
3758
    url = 'http://www.atrandomcomics.com'
3759
    first_url = 'http://www.atrandomcomics.com/at-random-comics-home/2015/5/5/can-of-worms'
3760
3761
    @classmethod
3762
    def get_images(cls, soup):
3763
        """Get image URLs for a comic."""
3764
        imgs = soup.find_all('meta', property='og:image')
3765
        return [i['content'] for i in imgs]
3766
3767
3768
class NothingSuspicious(GenericSquareSpace):
3769
    """Class to retrieve Nothing Suspicious comics."""
3770
    name = 'nothingsuspicious'
3771
    long_name = 'Nothing Suspicious'
3772
    url = 'https://nothingsuspicio.us'
3773
    first_url = 'https://nothingsuspicio.us/?offset=1483592400908'
3774
3775
    @classmethod
3776
    def get_images(cls, soup):
3777
        """Get image URLs for a comic."""
3778
        imgs = soup.find('div', class_='content-wrapper').find('img')
3779
        return [i['src'] for i in [imgs]]
3780
3781
3782
class DeathBulge(GenericComic):
3783
    """Class to retrieve the DeathBulge comics."""
3784
    name = 'deathbulge'
3785
    long_name = 'Death Bulge'
3786
    url = 'http://www.deathbulge.com'
3787
3788
    @classmethod
3789
    def get_next_comic(cls, last_comic):
3790
        """Generator to get the next comic. Implementation of GenericComic's abstract method."""
3791
        json_url = urljoin_wrapper(cls.url, 'api/comics/1')
3792
        json = load_json_at_url(json_url)
3793
        pagination = json['pagination_links']
3794
        first_num = last_comic['num'] if last_comic else pagination['first']
3795
        last_num = pagination['last']
3796
        for num in range(first_num + 1, last_num):
3797
            json_url = urljoin_wrapper(cls.url, 'api/comics/%d' % num)
3798
            json = load_json_at_url(json_url)
3799
            pagination = json['pagination_links']
3800
            comic_json = json['comic']
3801
            date_str = comic_json['timestamp'][:10]
3802
            day = string_to_date(date_str, "%Y-%m-%d")
3803
            comic_id = comic_json['id']  # not exactly 'num' o_O
3804
            yield {
3805
                'json_url': json_url,
3806
                'num': comic_id,
3807
                'url': urljoin_wrapper(cls.url, 'comics/%d' % num),
3808
                'alt': comic_json['alt_text'],
3809
                'title': comic_json['title'],
3810
                'img': [urljoin_wrapper(cls.url, comic_json['comic'])],
3811
                'month': day.month,
3812
                'year': day.year,
3813
                'day': day.day,
3814
            }
3815
3816
3817
class GenericTumblrV1(GenericComic):
3818
    """Generic class to retrieve comics from Tumblr using the V1 API."""
3819
    _categories = ('TUMBLR', )
3820
3821
    @classmethod
3822
    def get_next_comic(cls, last_comic):
3823
        """Generic implementation of get_next_comic for Tumblr comics."""
3824
        for p in cls.get_posts(last_comic):
3825
            comic = cls.get_comic_info(p)
3826
            if comic is not None:
3827
                yield comic
3828
3829
    @classmethod
3830
    def check_url(cls, url):
3831
        if not url.startswith(cls.url):
3832
            print("url '%s' does not start with '%s'" % (url, cls.url))
3833
        return url
3834
3835
    @classmethod
3836
    def get_url_from_post(cls, post):
3837
        return cls.check_url(post['url'])
3838
3839
    @classmethod
3840
    def get_api_url(cls):
3841
        return urljoin_wrapper(cls.url, '/api/read/')
3842
3843
    @classmethod
3844
    def get_api_url_for_id(cls, tumblr_id):
3845
        return cls.get_api_url() + '?id=%d' % (tumblr_id)
3846
3847
    @classmethod
3848
    def get_comic_info(cls, post):
3849
        """Get information about a particular comics."""
3850
        type_ = post['type']
3851
        if type_ != 'photo':
3852
            return None
3853
        tumblr_id = int(post['id'])
3854
        api_url = cls.get_api_url_for_id(tumblr_id)
3855
        day = datetime.datetime.fromtimestamp(int(post['unix-timestamp'])).date()
3856
        caption = post.find('photo-caption')
3857
        title = caption.string if caption else ""
3858
        tags = ' '.join(t.string for t in post.find_all('tag'))
3859
        # Photos may appear in 'photo' tags and/or straight in the post
3860
        photo_tags = post.find_all('photo')
3861
        if not photo_tags:
3862
            photo_tags = [post]
3863
        # Images are in multiple resolutions - taking the first one
3864
        imgs = [photo.find('photo-url') for photo in photo_tags]
3865
        return {
3866
            'url': cls.get_url_from_post(post),
3867
            'url2': post['url-with-slug'],
3868
            'day': day.day,
3869
            'month': day.month,
3870
            'year': day.year,
3871
            'title': title,
3872
            'tags': tags,
3873
            'img': [i.string for i in imgs],
3874
            'tumblr-id': tumblr_id,
3875
            'api_url': api_url,
3876
        }
3877
3878
    @classmethod
3879
    def get_posts(cls, last_comic, nb_post_per_call=10):
3880
        """Get posts using API. nb_post_per_call is max 50.
3881
3882
        Posts are retrieved from newer to older as per the tumblr v1 api
3883
        but are returned in chronological order."""
3884
        waiting_for_id = last_comic['tumblr-id'] if last_comic else None
3885
        posts_acc = []
3886
        if last_comic is not None:
3887
            # cls.check_url(last_comic['url'])
3888
            cls.check_url(last_comic['api_url'])
3889
            # Sometimes, tumblr posts are deleted. When previous post is deleted, we
3890
            # might end up spending a lot of time looking for something that
3891
            # doesn't exist. Failing early and clearly might be a better option.
3892
            last_api_url = cls.get_api_url_for_id(waiting_for_id)
3893
            try:
3894
                get_soup_at_url(last_api_url)
3895
            except urllib.error.HTTPError:
3896
                try:
3897
                    get_soup_at_url(cls.url)
3898
                except urllib.error.HTTPError:
3899
                    print("Did not find previous post nor main url %s" % cls.url)
3900
                else:
3901
                    print("Did not find previous post %s : it might have been deleted" % last_api_url)
3902
                return reversed(posts_acc)
3903
        api_url = cls.get_api_url()
3904
        posts = get_soup_at_url(api_url).find('posts')
3905
        start, total = int(posts['start']), int(posts['total'])
3906
        assert start == 0
3907
        for starting_num in range(0, total, nb_post_per_call):
3908
            api_url2 = api_url + '?start=%d&num=%d' % (starting_num, nb_post_per_call)
3909
            posts2 = get_soup_at_url(api_url2).find('posts')
3910
            start2, total2 = int(posts2['start']), int(posts2['total'])
3911
            assert starting_num == start2, "%d != %d" % (starting_num, start2)
3912
            # This may happen and should be handled in the future
3913
            assert total == total2, "%d != %d" % (total, total2)
3914
            for p in posts2.find_all('post'):
3915
                tumblr_id = int(p['id'])
3916
                if waiting_for_id and waiting_for_id == tumblr_id:
3917
                    return reversed(posts_acc)
3918
                posts_acc.append(p)
3919
        if waiting_for_id is None:
3920
            return reversed(posts_acc)
3921
        print("Did not find %s : there might be a problem" % waiting_for_id)
3922
        return []
3923
3924
3925
class SaturdayMorningBreakfastCerealTumblr(GenericTumblrV1):
3926
    """Class to retrieve Saturday Morning Breakfast Cereal comics."""
3927
    # Also on http://www.gocomics.com/saturday-morning-breakfast-cereal
3928
    # Also on http://www.smbc-comics.com
3929
    name = 'smbc-tumblr'
3930
    long_name = 'Saturday Morning Breakfast Cereal (from Tumblr)'
3931
    url = 'http://smbc-comics.tumblr.com'
3932
    _categories = ('SMBC', )
3933
3934
3935
class AHammADay(GenericTumblrV1):
3936
    """Class to retrieve class A Hamm A Day comics."""
3937
    name = 'hamm'
3938
    long_name = 'A Hamm A Day'
3939
    url = 'http://www.ahammaday.com'
3940
3941
3942
class IrwinCardozo(GenericTumblrV1):
3943
    """Class to retrieve Irwin Cardozo Comics."""
3944
    name = 'irwinc'
3945
    long_name = 'Irwin Cardozo'
3946
    url = 'http://irwincardozocomics.tumblr.com'
3947
3948
3949
class AccordingToDevin(GenericTumblrV1):
3950
    """Class to retrieve According To Devin comics."""
3951
    name = 'devin'
3952
    long_name = 'According To Devin'
3953
    url = 'http://accordingtodevin.tumblr.com'
3954
3955
3956
class ItsTheTieTumblr(GenericTumblrV1):
3957
    """Class to retrieve It's the tie comics."""
3958
    # Also on http://itsthetie.com
3959
    # Also on https://tapastic.com/series/itsthetie
3960
    name = 'tie-tumblr'
3961
    long_name = "It's the tie (from Tumblr)"
3962
    url = "http://itsthetie.tumblr.com"
3963
    _categories = ('TIE', )
3964
3965
3966
class OctopunsTumblr(GenericTumblrV1):
3967
    """Class to retrieve Octopuns comics."""
3968
    # Also on http://www.octopuns.net
3969
    name = 'octopuns-tumblr'
3970
    long_name = 'Octopuns (from Tumblr)'
3971
    url = 'http://octopuns.tumblr.com'
3972
3973
3974
class PicturesInBoxesTumblr(GenericTumblrV1):
3975
    """Class to retrieve Pictures In Boxes comics."""
3976
    # Also on http://www.picturesinboxes.com
3977
    name = 'picturesinboxes-tumblr'
3978
    long_name = 'Pictures in Boxes (from Tumblr)'
3979
    url = 'https://picturesinboxescomic.tumblr.com'
3980
3981
3982
class TubeyToonsTumblr(GenericTumblrV1):
3983
    """Class to retrieve TubeyToons comics."""
3984
    # Also on http://tapastic.com/series/Tubey-Toons
3985
    # Also on http://tubeytoons.com
3986
    name = 'tubeytoons-tumblr'
3987
    long_name = 'Tubey Toons (from Tumblr)'
3988
    url = 'https://tubeytoons.tumblr.com'
3989
    _categories = ('TUNEYTOONS', )
3990
3991
3992
class UnearthedComicsTumblr(GenericTumblrV1):
3993
    """Class to retrieve Unearthed comics."""
3994
    # Also on http://tapastic.com/series/UnearthedComics
3995
    # Also on http://unearthedcomics.com
3996
    name = 'unearthed-tumblr'
3997
    long_name = 'Unearthed Comics (from Tumblr)'
3998
    url = 'https://unearthedcomics.tumblr.com'
3999
    _categories = ('UNEARTHED', )
4000
4001
4002
class PieComic(GenericTumblrV1):
4003
    """Class to retrieve Pie Comic comics."""
4004
    name = 'pie'
4005
    long_name = 'Pie Comic'
4006
    url = "http://piecomic.tumblr.com"
4007
4008
4009
class MrEthanDiamond(GenericTumblrV1):
4010
    """Class to retrieve Mr Ethan Diamond comics."""
4011
    name = 'diamond'
4012
    long_name = 'Mr Ethan Diamond'
4013
    url = 'http://mrethandiamond.tumblr.com'
4014
4015
4016
class Flocci(GenericTumblrV1):
4017
    """Class to retrieve floccinaucinihilipilification comics."""
4018
    name = 'flocci'
4019
    long_name = 'floccinaucinihilipilification'
4020
    url = "http://floccinaucinihilipilificationa.tumblr.com"
4021
4022
4023
class UpAndOut(GenericTumblrV1):
4024
    """Class to retrieve Up & Out comics."""
4025
    # Also on http://tapastic.com/series/UP-and-OUT
4026
    name = 'upandout'
4027
    long_name = 'Up And Out (from Tumblr)'
4028
    url = 'http://upandoutcomic.tumblr.com'
4029
4030
4031
class Pundemonium(GenericTumblrV1):
4032
    """Class to retrieve Pundemonium comics."""
4033
    name = 'pundemonium'
4034
    long_name = 'Pundemonium'
4035
    url = 'http://monstika.tumblr.com'
4036
4037
4038
class PoorlyDrawnLinesTumblr(GenericTumblrV1):
4039
    """Class to retrieve Poorly Drawn Lines comics."""
4040
    # Also on http://poorlydrawnlines.com
4041
    name = 'poorlydrawn-tumblr'
4042
    long_name = 'Poorly Drawn Lines (from Tumblr)'
4043
    url = 'http://pdlcomics.tumblr.com'
4044
    _categories = ('POORLYDRAWN', )
4045
4046
4047
class PearShapedComics(GenericTumblrV1):
4048
    """Class to retrieve Pear Shaped Comics."""
4049
    name = 'pearshaped'
4050
    long_name = 'Pear-Shaped Comics'
4051
    url = 'http://pearshapedcomics.com'
4052
4053
4054
class PondScumComics(GenericTumblrV1):
4055
    """Class to retrieve Pond Scum Comics."""
4056
    name = 'pond'
4057
    long_name = 'Pond Scum'
4058
    url = 'http://pondscumcomic.tumblr.com'
4059
4060
4061
class MercworksTumblr(GenericTumblrV1):
4062
    """Class to retrieve Mercworks comics."""
4063
    # Also on http://mercworks.net
4064
    # Also on http://www.webtoons.com/en/comedy/mercworks/list?title_no=426
4065
    # Also on https://tapastic.com/series/MercWorks
4066
    name = 'mercworks-tumblr'
4067
    long_name = 'Mercworks (from Tumblr)'
4068
    url = 'http://mercworks.tumblr.com'
4069
    _categories = ('MERCWORKS', )
4070
4071
4072
class OwlTurdTumblr(GenericTumblrV1):
4073
    """Class to retrieve Owl Turd / Shen comix."""
4074
    # Also on https://tapas.io/series/Shen-Comix
4075
    name = 'owlturd-tumblr'
4076
    long_name = 'Owl Turd / Shen Comix (from Tumblr)'
4077
    url = 'http://shencomix.com'
4078
    _categories = ('OWLTURD', 'SHENCOMIX')
4079
4080
4081
class VectorBelly(GenericTumblrV1):
4082
    """Class to retrieve Vector Belly comics."""
4083
    # Also on http://vectorbelly.com
4084
    name = 'vector'
4085
    long_name = 'Vector Belly'
4086
    url = 'http://vectorbelly.tumblr.com'
4087
4088
4089
class GoneIntoRapture(GenericTumblrV1):
4090
    """Class to retrieve Gone Into Rapture comics."""
4091
    # Also on http://goneintorapture.tumblr.com
4092
    # Also on http://tapastic.com/series/Goneintorapture
4093
    name = 'rapture'
4094
    long_name = 'Gone Into Rapture'
4095
    url = 'http://goneintorapture.com'
4096
4097
4098
class TheOatmealTumblr(GenericTumblrV1):
4099
    """Class to retrieve The Oatmeal comics."""
4100
    # Also on http://theoatmeal.com
4101
    name = 'oatmeal-tumblr'
4102
    long_name = 'The Oatmeal (from Tumblr)'
4103
    url = 'http://oatmeal.tumblr.com'
4104
4105
4106
class HeckIfIKnowComicsTumblr(GenericTumblrV1):
4107
    """Class to retrieve Heck If I Know Comics."""
4108
    # Also on http://tapastic.com/series/Regular
4109
    name = 'heck-tumblr'
4110
    long_name = 'Heck if I Know comics (from Tumblr)'
4111
    url = 'http://heckifiknowcomics.com'
4112
4113
4114
class MyJetPack(GenericTumblrV1):
4115
    """Class to retrieve My Jet Pack comics."""
4116
    name = 'jetpack'
4117
    long_name = 'My Jet Pack'
4118
    url = 'http://myjetpack.tumblr.com'
4119
4120
4121
class CheerUpEmoKidTumblr(GenericTumblrV1):
4122
    """Class to retrieve CheerUpEmoKid comics."""
4123
    # Also on http://www.cheerupemokid.com
4124
    # Also on http://tapastic.com/series/CUEK
4125
    name = 'cuek-tumblr'
4126
    long_name = 'Cheer Up Emo Kid (from Tumblr)'
4127
    url = 'https://enzocomics.tumblr.com'
4128
4129
4130
class ForLackOfABetterComic(GenericTumblrV1):
4131
    """Class to retrieve For Lack Of A Better Comics."""
4132
    # Also on http://forlackofabettercomic.com
4133
    name = 'lack'
4134
    long_name = 'For Lack Of A Better Comic'
4135
    url = 'http://forlackofabettercomic.tumblr.com'
4136
4137
4138
class ZenPencilsTumblr(GenericTumblrV1):
4139
    """Class to retrieve ZenPencils comics."""
4140
    # Also on http://zenpencils.com
4141
    # Also on http://www.gocomics.com/zen-pencils
4142
    name = 'zenpencils-tumblr'
4143
    long_name = 'Zen Pencils (from Tumblr)'
4144
    url = 'http://zenpencils.tumblr.com'
4145
    _categories = ('ZENPENCILS', )
4146
4147
4148
class ThreeWordPhraseTumblr(GenericTumblrV1):
4149
    """Class to retrieve Three Word Phrase comics."""
4150
    # Also on http://threewordphrase.com
4151
    name = 'threeword-tumblr'
4152
    long_name = 'Three Word Phrase (from Tumblr)'
4153
    url = 'http://threewordphrase.tumblr.com'
4154
4155
4156
class TimeTrabbleTumblr(GenericTumblrV1):
4157
    """Class to retrieve Time Trabble comics."""
4158
    # Also on http://timetrabble.com
4159
    name = 'timetrabble-tumblr'
4160
    long_name = 'Time Trabble (from Tumblr)'
4161
    url = 'http://timetrabble.tumblr.com'
4162
4163
4164
class SafelyEndangeredTumblr(GenericTumblrV1):
4165
    """Class to retrieve Safely Endangered comics."""
4166
    # Also on http://www.safelyendangered.com
4167
    name = 'endangered-tumblr'
4168
    long_name = 'Safely Endangered (from Tumblr)'
4169
    url = 'http://tumblr.safelyendangered.com'
4170
4171
4172
class MouseBearComedyTumblr(GenericTumblrV1):
4173
    """Class to retrieve Mouse Bear Comedy comics."""
4174
    # Also on http://www.mousebearcomedy.com
4175
    name = 'mousebear-tumblr'
4176
    long_name = 'Mouse Bear Comedy (from Tumblr)'
4177
    url = 'http://mousebearcomedy.tumblr.com'
4178
4179
4180
class BouletCorpTumblr(GenericTumblrV1):
4181
    """Class to retrieve BouletCorp comics."""
4182
    # Also on http://www.bouletcorp.com
4183
    name = 'boulet-tumblr'
4184
    long_name = 'Boulet Corp (from Tumblr)'
4185
    url = 'https://bouletcorp.tumblr.com'
4186
    _categories = ('BOULET', )
4187
4188
4189
class TheAwkwardYetiTumblr(GenericTumblrV1):
4190
    """Class to retrieve The Awkward Yeti comics."""
4191
    # Also on http://www.gocomics.com/the-awkward-yeti
4192
    # Also on http://theawkwardyeti.com
4193
    # Also on https://tapastic.com/series/TheAwkwardYeti
4194
    name = 'yeti-tumblr'
4195
    long_name = 'The Awkward Yeti (from Tumblr)'
4196
    url = 'http://larstheyeti.tumblr.com'
4197
    _categories = ('YETI', )
4198
4199
4200
class NellucNhoj(GenericTumblrV1):
4201
    """Class to retrieve NellucNhoj comics."""
4202
    name = 'nhoj'
4203
    long_name = 'Nelluc Nhoj'
4204
    url = 'http://nellucnhoj.com'
4205
4206
4207
class DownTheUpwardSpiralTumblr(GenericTumblrV1):
4208
    """Class to retrieve Down The Upward Spiral comics."""
4209
    # Also on http://www.downtheupwardspiral.com
4210
    # Also on https://tapastic.com/series/Down-the-Upward-Spiral
4211
    name = 'spiral-tumblr'
4212
    long_name = 'Down the Upward Spiral (from Tumblr)'
4213
    url = 'http://downtheupwardspiral.tumblr.com'
4214
4215
4216
class AsPerUsualTumblr(GenericTumblrV1):
4217
    """Class to retrieve As Per Usual comics."""
4218
    # Also on https://tapastic.com/series/AsPerUsual
4219
    name = 'usual-tumblr'
4220
    long_name = 'As Per Usual (from Tumblr)'
4221
    url = 'http://as-per-usual.tumblr.com'
4222
    categories = ('DAMILEE', )
4223
4224
4225
class HotComicsForCoolPeopleTumblr(GenericTumblrV1):
4226
    """Class to retrieve Hot Comics For Cool People."""
4227
    # Also on https://tapastic.com/series/Hot-Comics-For-Cool-People
4228
    # Also on http://hotcomics.biz (links to tumblr)
4229
    # Also on http://hcfcp.com (links to tumblr)
4230
    name = 'hotcomics-tumblr'
4231
    long_name = 'Hot Comics For Cool People (from Tumblr)'
4232
    url = 'http://hotcomicsforcoolpeople.tumblr.com'
4233
    categories = ('DAMILEE', )
4234
4235
4236
class OneOneOneOneComicTumblr(GenericTumblrV1):
4237
    """Class to retrieve 1111 Comics."""
4238
    # Also on http://www.1111comics.me
4239
    # Also on https://tapastic.com/series/1111-Comics
4240
    name = '1111-tumblr'
4241
    long_name = '1111 Comics (from Tumblr)'
4242
    url = 'http://comics1111.tumblr.com'
4243
    _categories = ('ONEONEONEONE', )
4244
4245
4246
class JhallComicsTumblr(GenericTumblrV1):
4247
    """Class to retrieve Jhall Comics."""
4248
    # Also on http://jhallcomics.com
4249
    name = 'jhall-tumblr'
4250
    long_name = 'Jhall Comics (from Tumblr)'
4251
    url = 'http://jhallcomics.tumblr.com'
4252
4253
4254
class BerkeleyMewsTumblr(GenericTumblrV1):
4255
    """Class to retrieve Berkeley Mews comics."""
4256
    # Also on http://www.gocomics.com/berkeley-mews
4257
    # Also on http://www.berkeleymews.com
4258
    name = 'berkeley-tumblr'
4259
    long_name = 'Berkeley Mews (from Tumblr)'
4260
    url = 'http://mews.tumblr.com'
4261
    _categories = ('BERKELEY', )
4262
4263
4264
class JoanCornellaTumblr(GenericTumblrV1):
4265
    """Class to retrieve Joan Cornella comics."""
4266
    # Also on http://joancornella.net
4267
    name = 'cornella-tumblr'
4268
    long_name = 'Joan Cornella (from Tumblr)'
4269
    url = 'http://cornellajoan.tumblr.com'
4270
4271
4272
class RespawnComicTumblr(GenericTumblrV1):
4273
    """Class to retrieve Respawn Comic."""
4274
    # Also on http://respawncomic.com
4275
    name = 'respawn-tumblr'
4276
    long_name = 'Respawn Comic (from Tumblr)'
4277
    url = 'https://respawncomic.tumblr.com'
4278
4279
4280
class ChrisHallbeckTumblr(GenericTumblrV1):
4281
    """Class to retrieve Chris Hallbeck comics."""
4282
    # Also on https://tapastic.com/ChrisHallbeck
4283
    # Also on http://maximumble.com
4284
    # Also on http://minimumble.com
4285
    # Also on http://thebookofbiff.com
4286
    name = 'hallbeck-tumblr'
4287
    long_name = 'Chris Hallback (from Tumblr)'
4288
    url = 'https://chrishallbeck.tumblr.com'
4289
    _categories = ('HALLBACK', )
4290
4291
4292
class ComicNuggets(GenericTumblrV1):
4293
    """Class to retrieve Comic Nuggets."""
4294
    name = 'nuggets'
4295
    long_name = 'Comic Nuggets'
4296
    url = 'http://comicnuggets.com'
4297
4298
4299
class PigeonGazetteTumblr(GenericTumblrV1):
4300
    """Class to retrieve The Pigeon Gazette comics."""
4301
    # Also on https://tapastic.com/series/The-Pigeon-Gazette
4302
    name = 'pigeon-tumblr'
4303
    long_name = 'The Pigeon Gazette (from Tumblr)'
4304
    url = 'http://thepigeongazette.tumblr.com'
4305
4306
4307
class CancerOwl(GenericTumblrV1):
4308
    """Class to retrieve Cancer Owl comics."""
4309
    # Also on http://cancerowl.com
4310
    name = 'cancerowl-tumblr'
4311
    long_name = 'Cancer Owl (from Tumblr)'
4312
    url = 'http://cancerowl.tumblr.com'
4313
4314
4315
class FowlLanguageTumblr(GenericTumblrV1):
4316
    """Class to retrieve Fowl Language comics."""
4317
    # Also on http://www.fowllanguagecomics.com
4318
    # Also on http://tapastic.com/series/Fowl-Language-Comics
4319
    # Also on http://www.gocomics.com/fowl-language
4320
    name = 'fowllanguage-tumblr'
4321
    long_name = 'Fowl Language Comics (from Tumblr)'
4322
    url = 'http://fowllanguagecomics.tumblr.com'
4323
    _categories = ('FOWLLANGUAGE', )
4324
4325
4326
class TheOdd1sOutTumblr(GenericTumblrV1):
4327
    """Class to retrieve The Odd 1s Out comics."""
4328
    # Also on http://theodd1sout.com
4329
    # Also on https://tapastic.com/series/Theodd1sout
4330
    name = 'theodd-tumblr'
4331
    long_name = 'The Odd 1s Out (from Tumblr)'
4332
    url = 'http://theodd1sout.tumblr.com'
4333
4334
4335
class TheUnderfoldTumblr(GenericTumblrV1):
4336
    """Class to retrieve The Underfold comics."""
4337
    # Also on http://theunderfold.com
4338
    name = 'underfold-tumblr'
4339
    long_name = 'The Underfold (from Tumblr)'
4340
    url = 'http://theunderfold.tumblr.com'
4341
4342
4343
class LolNeinTumblr(GenericTumblrV1):
4344
    """Class to retrieve Lol Nein comics."""
4345
    # Also on http://lolnein.com
4346
    name = 'lolnein-tumblr'
4347
    long_name = 'Lol Nein (from Tumblr)'
4348
    url = 'http://lolneincom.tumblr.com'
4349
4350
4351
class FatAwesomeComicsTumblr(GenericTumblrV1):
4352
    """Class to retrieve Fat Awesome Comics."""
4353
    # Also on http://fatawesome.com/comics
4354
    name = 'fatawesome-tumblr'
4355
    long_name = 'Fat Awesome (from Tumblr)'
4356
    url = 'http://fatawesomecomedy.tumblr.com'
4357
4358
4359
class TheWorldIsFlatTumblr(GenericTumblrV1):
4360
    """Class to retrieve The World Is Flat Comics."""
4361
    # Also on https://tapastic.com/series/The-World-is-Flat
4362
    name = 'flatworld-tumblr'
4363
    long_name = 'The World Is Flat (from Tumblr)'
4364
    url = 'http://theworldisflatcomics.com'
4365
4366
4367
class DorrisMc(GenericTumblrV1):
4368
    """Class to retrieve Dorris Mc Comics"""
4369
    # Also on http://www.gocomics.com/dorris-mccomics
4370
    name = 'dorrismc'
4371
    long_name = 'Dorris Mc'
4372
    url = 'http://dorrismccomics.com'
4373
4374
4375
class LeleozTumblr(GenericDeletedComic, GenericTumblrV1):
4376
    """Class to retrieve Leleoz comics."""
4377
    # Also on https://tapastic.com/series/Leleoz
4378
    name = 'leleoz-tumblr'
4379
    long_name = 'Leleoz (from Tumblr)'
4380
    url = 'http://leleozcomics.tumblr.com'
4381
4382
4383
class MoonBeardTumblr(GenericTumblrV1):
4384
    """Class to retrieve MoonBeard comics."""
4385
    # Also on http://moonbeard.com
4386
    # Also on http://www.webtoons.com/en/comedy/moon-beard/list?title_no=471
4387
    name = 'moonbeard-tumblr'
4388
    long_name = 'Moon Beard (from Tumblr)'
4389
    url = 'http://squireseses.tumblr.com'
4390
    _categories = ('MOONBEARD', )
4391
4392
4393
class AComik(GenericTumblrV1):
4394
    """Class to retrieve A Comik"""
4395
    name = 'comik'
4396
    long_name = 'A Comik'
4397
    url = 'http://acomik.com'
4398
4399
4400
class ClassicRandy(GenericTumblrV1):
4401
    """Class to retrieve Classic Randy comics."""
4402
    name = 'randy'
4403
    long_name = 'Classic Randy'
4404
    url = 'http://classicrandy.tumblr.com'
4405
4406
4407
class DagssonTumblr(GenericTumblrV1):
4408
    """Class to retrieve Dagsson comics."""
4409
    # Also on http://www.dagsson.com
4410
    name = 'dagsson-tumblr'
4411
    long_name = 'Dagsson Hugleikur (from Tumblr)'
4412
    url = 'https://hugleikurdagsson.tumblr.com'
4413
4414
4415
class LinsEditionsTumblr(GenericTumblrV1):
4416
    """Class to retrieve L.I.N.S. Editions comics."""
4417
    # Also on https://linsedition.com
4418
    # Now on http://warandpeas.tumblr.com
4419
    name = 'lins-tumblr'
4420
    long_name = 'L.I.N.S. Editions (from Tumblr)'
4421
    url = 'https://linscomics.tumblr.com'
4422
    _categories = ('WARANDPEAS', 'LINS')
4423
4424
4425
class WarAndPeasTumblr(GenericTumblrV1):
4426
    """Class to retrieve War And Peas comics."""
4427
    # Was on https://linscomics.tumblr.com
4428
    name = 'warandpeas-tumblr'
4429
    long_name = 'War And Peas (from Tumblr)'
4430
    url = 'http://warandpeas.tumblr.com'
4431
    _categories = ('WARANDPEAS', 'LINS')
4432
4433
4434
class OrigamiHotDish(GenericTumblrV1):
4435
    """Class to retrieve Origami Hot Dish comics."""
4436
    name = 'origamihotdish'
4437
    long_name = 'Origami Hot Dish'
4438
    url = 'http://origamihotdish.com'
4439
4440
4441
class HitAndMissComicsTumblr(GenericTumblrV1):
4442
    """Class to retrieve Hit and Miss Comics."""
4443
    name = 'hitandmiss'
4444
    long_name = 'Hit and Miss Comics'
4445
    url = 'https://hitandmisscomics.tumblr.com'
4446
4447
4448
class HMBlanc(GenericTumblrV1):
4449
    """Class to retrieve HM Blanc comics."""
4450
    name = 'hmblanc'
4451
    long_name = 'HM Blanc'
4452
    url = 'http://hmblanc.tumblr.com'
4453
4454
4455
class TalesOfAbsurdityTumblr(GenericTumblrV1):
4456
    """Class to retrieve Tales Of Absurdity comics."""
4457
    # Also on http://talesofabsurdity.com
4458
    # Also on http://tapastic.com/series/Tales-Of-Absurdity
4459
    name = 'absurdity-tumblr'
4460
    long_name = 'Tales of Absurdity (from Tumblr)'
4461
    url = 'http://talesofabsurdity.tumblr.com'
4462
    _categories = ('ABSURDITY', )
4463
4464
4465
class RobbieAndBobby(GenericTumblrV1):
4466
    """Class to retrieve Robbie And Bobby comics."""
4467
    # Also on http://robbieandbobby.com
4468
    name = 'robbie-tumblr'
4469
    long_name = 'Robbie And Bobby (from Tumblr)'
4470
    url = 'http://robbieandbobby.tumblr.com'
4471
4472
4473
class ElectricBunnyComicTumblr(GenericTumblrV1):
4474
    """Class to retrieve Electric Bunny Comics."""
4475
    # Also on http://www.electricbunnycomics.com/View/Comic/153/Welcome+to+Hell
4476
    name = 'bunny-tumblr'
4477
    long_name = 'Electric Bunny Comic (from Tumblr)'
4478
    url = 'http://electricbunnycomics.tumblr.com'
4479
4480
4481
class Hoomph(GenericTumblrV1):
4482
    """Class to retrieve Hoomph comics."""
4483
    name = 'hoomph'
4484
    long_name = 'Hoomph'
4485
    url = 'http://hoom.ph'
4486
4487
4488
class BFGFSTumblr(GenericTumblrV1):
4489
    """Class to retrieve BFGFS comics."""
4490
    # Also on https://tapastic.com/series/BFGFS
4491
    # Also on http://bfgfs.com
4492
    name = 'bfgfs-tumblr'
4493
    long_name = 'BFGFS (from Tumblr)'
4494
    url = 'https://bfgfs.tumblr.com'
4495
4496
4497
class DoodleForFood(GenericTumblrV1):
4498
    """Class to retrieve Doodle For Food comics."""
4499
    # Also on https://tapastic.com/series/Doodle-for-Food
4500
    name = 'doodle'
4501
    long_name = 'Doodle For Food'
4502
    url = 'http://www.doodleforfood.com'
4503
4504
4505
class CassandraCalinTumblr(GenericTumblrV1):
4506
    """Class to retrieve C. Cassandra comics."""
4507
    # Also on http://cassandracalin.com
4508
    # Also on https://tapastic.com/series/C-Cassandra-comics
4509
    name = 'cassandra-tumblr'
4510
    long_name = 'Cassandra Calin (from Tumblr)'
4511
    url = 'http://c-cassandra.tumblr.com'
4512
4513
4514
class DougWasTaken(GenericTumblrV1):
4515
    """Class to retrieve Doug Was Taken comics."""
4516
    name = 'doug'
4517
    long_name = 'Doug Was Taken'
4518
    url = 'https://dougwastaken.tumblr.com'
4519
4520
4521
class MandatoryRollerCoaster(GenericTumblrV1):
4522
    """Class to retrieve Mandatory Roller Coaster comics."""
4523
    name = 'rollercoaster'
4524
    long_name = 'Mandatory Roller Coaster'
4525
    url = 'http://mandatoryrollercoaster.com'
4526
4527
4528
class CEstPasEnRegardantSesPompes(GenericTumblrV1):
4529
    """Class to retrieve C'Est Pas En Regardant Ses Pompes (...)  comics."""
4530
    name = 'cperspqccltt'
4531
    long_name = 'C Est Pas En Regardant Ses Pompes (...)'
4532
    url = 'http://marcoandco.tumblr.com'
4533
4534
4535
class TheGrohlTroll(GenericTumblrV1):
4536
    """Class to retrieve The Grohl Troll comics."""
4537
    name = 'grohltroll'
4538
    long_name = 'The Grohl Troll'
4539
    url = 'http://thegrohltroll.com'
4540
4541
4542
class WebcomicName(GenericTumblrV1):
4543
    """Class to retrieve Webcomic Name comics."""
4544
    name = 'webcomicname'
4545
    long_name = 'Webcomic Name'
4546
    url = 'http://webcomicname.com'
4547
4548
4549
class BooksOfAdam(GenericTumblrV1):
4550
    """Class to retrieve Books of Adam comics."""
4551
    # Also on http://www.booksofadam.com
4552
    name = 'booksofadam'
4553
    long_name = 'Books of Adam'
4554
    url = 'http://booksofadam.tumblr.com'
4555
4556
4557
class HarkAVagrant(GenericTumblrV1):
4558
    """Class to retrieve Hark A Vagrant comics."""
4559
    # Also on http://www.harkavagrant.com
4560
    name = 'hark-tumblr'
4561
    long_name = 'Hark A Vagrant (from Tumblr)'
4562
    url = 'http://beatonna.tumblr.com'
4563
4564
4565
class OurSuperAdventureTumblr(GenericTumblrV1):
4566
    """Class to retrieve Our Super Adventure comics."""
4567
    # Also on https://tapastic.com/series/Our-Super-Adventure
4568
    # Also on http://www.oursuperadventure.com
4569
    # http://sarahgraley.com
4570
    name = 'superadventure-tumblr'
4571
    long_name = 'Our Super Adventure (from Tumblr)'
4572
    url = 'http://sarahssketchbook.tumblr.com'
4573
4574
4575
class JakeLikesOnions(GenericTumblrV1):
4576
    """Class to retrieve Jake Likes Onions comics."""
4577
    name = 'jake'
4578
    long_name = 'Jake Likes Onions'
4579
    url = 'http://jakelikesonions.com'
4580
4581
4582
class InYourFaceCakeTumblr(GenericTumblrV1):
4583
    """Class to retrieve In Your Face Cake comics."""
4584
    # Also on https://tapas.io/series/In-Your-Face-Cake
4585
    name = 'inyourfacecake-tumblr'
4586
    long_name = 'In Your Face Cake (from Tumblr)'
4587
    url = 'https://in-your-face-cake.tumblr.com'
4588
    _categories = ('INYOURFACECAKE', )
4589
4590
4591
class Robospunk(GenericTumblrV1):
4592
    """Class to retrieve Robospunk comics."""
4593
    name = 'robospunk'
4594
    long_name = 'Robospunk'
4595
    url = 'http://robospunk.com'
4596
4597
4598
class BananaTwinky(GenericTumblrV1):
4599
    """Class to retrieve Banana Twinky comics."""
4600
    name = 'banana'
4601
    long_name = 'Banana Twinky'
4602
    url = 'https://bananatwinky.tumblr.com'
4603
4604
4605
class YesterdaysPopcornTumblr(GenericTumblrV1):
4606
    """Class to retrieve Yesterday's Popcorn comics."""
4607
    # Also on http://www.yesterdayspopcorn.com
4608
    # Also on https://tapastic.com/series/Yesterdays-Popcorn
4609
    name = 'popcorn-tumblr'
4610
    long_name = 'Yesterday\'s Popcorn (from Tumblr)'
4611
    url = 'http://yesterdayspopcorn.tumblr.com'
4612
4613
4614
class TwistedDoodles(GenericTumblrV1):
4615
    """Class to retrieve Twisted Doodles comics."""
4616
    name = 'twisted'
4617
    long_name = 'Twisted Doodles'
4618
    url = 'http://www.twisteddoodles.com'
4619
4620
4621
class UbertoolTumblr(GenericTumblrV1):
4622
    """Class to retrieve Ubertool comics."""
4623
    # Also on http://ubertoolcomic.com
4624
    # Also on https://tapastic.com/series/ubertool
4625
    name = 'ubertool-tumblr'
4626
    long_name = 'Ubertool (from Tumblr)'
4627
    url = 'https://ubertool.tumblr.com'
4628
    _categories = ('UBERTOOL', )
4629
4630
4631
class LittleLifeLinesTumblr(GenericDeletedComic, GenericTumblrV1):
4632
    """Class to retrieve Little Life Lines comics."""
4633
    # Also on http://www.littlelifelines.com
4634
    name = 'life-tumblr'
4635
    long_name = 'Little Life Lines (from Tumblr)'
4636
    url = 'https://little-life-lines.tumblr.com'
4637
4638
4639
class TheyCanTalk(GenericTumblrV1):
4640
    """Class to retrieve They Can Talk comics."""
4641
    name = 'theycantalk'
4642
    long_name = 'They Can Talk'
4643
    url = 'http://theycantalk.com'
4644
4645
4646
class Will5NeverCome(GenericTumblrV1):
4647
    """Class to retrieve Will 5:00 Never Come comics."""
4648
    name = 'will5'
4649
    long_name = 'Will 5:00 Never Come ?'
4650
    url = 'http://will5nevercome.com'
4651
4652
4653
class Sephko(GenericTumblrV1):
4654
    """Class to retrieve Sephko Comics."""
4655
    # Also on http://www.sephko.com
4656
    name = 'sephko'
4657
    long_name = 'Sephko'
4658
    url = 'https://sephko.tumblr.com'
4659
4660
4661
class BlazersAtDawn(GenericTumblrV1):
4662
    """Class to retrieve Blazers At Dawn Comics."""
4663
    name = 'blazers'
4664
    long_name = 'Blazers At Dawn'
4665
    url = 'http://blazersatdawn.tumblr.com'
4666
4667
4668
class ArtByMoga(GenericEmptyComic, GenericTumblrV1):  # Deactivated because it downloads too many things
4669
    """Class to retrieve Art By Moga Comics."""
4670
    name = 'moga'
4671
    long_name = 'Art By Moga'
4672
    url = 'http://artbymoga.tumblr.com'
4673
4674
4675
class VerbalVomitTumblr(GenericTumblrV1):
4676
    """Class to retrieve Verbal Vomit comics."""
4677
    # Also on http://www.verbal-vomit.com
4678
    name = 'vomit-tumblr'
4679
    long_name = 'Verbal Vomit (from Tumblr)'
4680
    url = 'http://verbalvomits.tumblr.com'
4681
4682
4683
class LibraryComic(GenericTumblrV1):
4684
    """Class to retrieve LibraryComic."""
4685
    # Also on http://librarycomic.com
4686
    name = 'library-tumblr'
4687
    long_name = 'LibraryComic (from Tumblr)'
4688
    url = 'https://librarycomic.tumblr.com'
4689
4690
4691
class TizzyStitchBirdTumblr(GenericTumblrV1):
4692
    """Class to retrieve Tizzy Stitch Bird comics."""
4693
    # Also on http://tizzystitchbird.com
4694
    # Also on https://tapastic.com/series/TizzyStitchbird
4695
    # Also on http://www.webtoons.com/en/challenge/tizzy-stitchbird/list?title_no=50082
4696
    name = 'tizzy-tumblr'
4697
    long_name = 'Tizzy Stitch Bird (from Tumblr)'
4698
    url = 'http://tizzystitchbird.tumblr.com'
4699
4700
4701
class VictimsOfCircumsolarTumblr(GenericTumblrV1):
4702
    """Class to retrieve VictimsOfCircumsolar comics."""
4703
    # Also on http://www.victimsofcircumsolar.com
4704
    name = 'circumsolar-tumblr'
4705
    long_name = 'Victims Of Circumsolar (from Tumblr)'
4706
    url = 'https://victimsofcomics.tumblr.com'
4707
4708
4709
class RockPaperCynicTumblr(GenericTumblrV1):
4710
    """Class to retrieve RockPaperCynic comics."""
4711
    # Also on http://www.rockpapercynic.com
4712
    # Also on https://tapastic.com/series/rockpapercynic
4713
    name = 'rpc-tumblr'
4714
    long_name = 'Rock Paper Cynic (from Tumblr)'
4715
    url = 'http://rockpapercynic.tumblr.com'
4716
4717
4718
class DeadlyPanelTumblr(GenericTumblrV1):
4719
    """Class to retrieve Deadly Panel comics."""
4720
    # Also on http://www.deadlypanel.com
4721
    # Also on https://tapastic.com/series/deadlypanel
4722
    name = 'deadly-tumblr'
4723
    long_name = 'Deadly Panel (from Tumblr)'
4724
    url = 'https://deadlypanel.tumblr.com'
4725
4726
4727
class CatanaComics(GenericComicNotWorking):  # Not a Tumblr anymore ?
4728
    """Class to retrieve Catana comics."""
4729
    name = 'catana'
4730
    long_name = 'Catana'
4731
    url = 'http://www.catanacomics.com'
4732
4733
4734
class AngryAtNothingTumblr(GenericTumblrV1):
4735
    """Class to retrieve Angry at Nothing comics."""
4736
    # Also on http://www.angryatnothing.net
4737
    # Also on http://tapastic.com/series/Comics-yeah-definitely-comics-
4738
    name = 'angry-tumblr'
4739
    long_name = 'Angry At Nothing (from Tumblr)'
4740
    url = 'http://angryatnothing.tumblr.com'
4741
4742
4743
class ShanghaiTango(GenericTumblrV1):
4744
    """Class to retrieve Shanghai Tango comic."""
4745
    name = 'tango'
4746
    long_name = 'Shanghai Tango'
4747
    url = 'http://tango2010weibo.tumblr.com'
4748
4749
4750
class OffTheLeashDogTumblr(GenericTumblrV1):
4751
    """Class to retrieve Off The Leash Dog comics."""
4752
    # Also on http://offtheleashdogcartoons.com
4753
    # Also on http://www.rupertfawcettcartoons.com
4754
    name = 'offtheleash-tumblr'
4755
    long_name = 'Off The Leash Dog (from Tumblr)'
4756
    url = 'http://rupertfawcettsdoggyblog.tumblr.com'
4757
    _categories = ('FAWCETT', )
4758
4759
4760
class ImogenQuestTumblr(GenericTumblrV1):
4761
    """Class to retrieve Imogen Quest comics."""
4762
    # Also on http://imogenquest.net
4763
    name = 'imogen-tumblr'
4764
    long_name = 'Imogen Quest (from Tumblr)'
4765
    url = 'http://imoquest.tumblr.com'
4766
4767
4768
class Shitfest(GenericTumblrV1):
4769
    """Class to retrieve Shitfest comics."""
4770
    name = 'shitfest'
4771
    long_name = 'Shitfest'
4772
    url = 'http://shitfestcomic.com'
4773
4774
4775
class IceCreamSandwichComics(GenericTumblrV1):
4776
    """Class to retrieve Ice Cream Sandwich Comics."""
4777
    name = 'icecream'
4778
    long_name = 'Ice Cream Sandwich Comics'
4779
    url = 'http://icecreamsandwichcomics.com'
4780
4781
4782
class Dustinteractive(GenericTumblrV1):
4783
    """Class to retrieve Dustinteractive comics."""
4784
    name = 'dustinteractive'
4785
    long_name = 'Dustinteractive'
4786
    url = 'http://dustinteractive.com'
4787
4788
4789
class StickyCinemaFloor(GenericTumblrV1):
4790
    """Class to retrieve Sticky Cinema Floor comics."""
4791
    name = 'stickycinema'
4792
    long_name = 'Sticky Cinema Floor'
4793
    url = 'https://stickycinemafloor.tumblr.com'
4794
4795
4796
class IncidentalComicsTumblr(GenericTumblrV1):
4797
    """Class to retrieve Incidental Comics."""
4798
    # Also on http://www.incidentalcomics.com
4799
    name = 'incidental-tumblr'
4800
    long_name = 'Incidental Comics (from Tumblr)'
4801
    url = 'http://incidentalcomics.tumblr.com'
4802
4803
4804
class APleasantWasteOfTimeTumblr(GenericTumblrV1):
4805
    """Class to retrieve A Pleasant Waste Of Time comics."""
4806
    # Also on https://tapas.io/series/A-Pleasant-
4807
    name = 'pleasant-waste-tumblr'
4808
    long_name = 'A Pleasant Waste Of Time (from Tumblr)'
4809
    url = 'https://artjcf.tumblr.com'
4810
    _categories = ('WASTE', )
4811
4812
4813
class HorovitzComicsTumblr(GenericTumblrV1):
4814
    """Class to retrieve Horovitz new comics."""
4815
    # Also on http://www.horovitzcomics.com
4816
    name = 'horovitz-tumblr'
4817
    long_name = 'Horovitz (from Tumblr)'
4818
    url = 'https://horovitzcomics.tumblr.com'
4819
    _categories = ('HOROVITZ', )
4820
4821
4822
class DeepDarkFearsTumblr(GenericTumblrV1):
4823
    """Class to retrieve DeepvDarkvFears comics."""
4824
    name = 'deep-dark-fears-tumblr'
4825
    long_name = 'Deep Dark Fears (from Tumblr)'
4826
    url = 'http://deep-dark-fears.tumblr.com'
4827
4828
4829
class DakotaMcDadzean(GenericTumblrV1):
4830
    """Class to retrieve Dakota McDadzean comics."""
4831
    name = 'dakota'
4832
    long_name = 'Dakota McDadzean'
4833
    url = 'http://dakotamcfadzean.tumblr.com'
4834
4835
4836
class ExtraFabulousComicsTumblr(GenericTumblrV1):
4837
    """Class to retrieve Extra Fabulous Comics."""
4838
    # Also on http://extrafabulouscomics.com
4839
    name = 'efc-tumblr'
4840
    long_name = 'Extra Fabulous Comics (from Tumblr)'
4841
    url = 'https://extrafabulouscomics.tumblr.com'
4842
    _categories = ('EFC', )
4843
4844
4845
class AlexLevesque(GenericTumblrV1):
4846
    """Class to retrieve AlexLevesque comics."""
4847
    name = 'alevesque'
4848
    long_name = 'Alex Levesque'
4849
    url = 'http://alexlevesque.com'
4850
    _categories = ('FRANCAIS', )
4851
4852
4853
class JamesOfNoTradesTumblr(GenericTumblrV1):
4854
    """Class to retrieve JamesOfNoTrades comics."""
4855
    # Also on http://jamesofnotrades.com
4856
    # Also on http://www.webtoons.com/en/challenge/james-of-no-trades/list?title_no=43422
4857
    # Also on https://tapas.io/series/James-of-No-Trades
4858
    name = 'jamesofnotrades-tumblr'
4859
    long_name = 'James Of No Trades (from Tumblr)'
4860
    url = 'http://jamesfregan.tumblr.com'
4861
    _categories = ('JAMESOFNOTRADES', )
4862
4863
4864
class InfiniteGuff(GenericTumblrV1):
4865
    """Class to retrieve Infinite Guff comics."""
4866
    name = 'infiniteguff'
4867
    long_name = 'Infinite Guff'
4868
    url = 'http://infiniteguff.com'
4869
4870
4871
class SkeletonClaw(GenericTumblrV1):
4872
    """Class to retrieve Skeleton Claw comics."""
4873
    name = 'skeletonclaw'
4874
    long_name = 'Skeleton Claw'
4875
    url = 'http://skeletonclaw.com'
4876
4877
4878
class MrsFrolleinTumblr(GenericTumblrV1):
4879
    """Class to retrieve Mrs Frollein comics."""
4880
    # Also on http://www.webtoons.com/en/challenge/mrsfrollein/list?title_no=51710
4881
    name = 'frollein'
4882
    long_name = 'Mrs Frollein (from Tumblr)'
4883
    url = 'https://mrsfrollein.tumblr.com'
4884
4885
4886
class GoodBearComicsTumblr(GenericTumblrV1):
4887
    """Class to retrieve GoodBearComics."""
4888
    # Also on https://goodbearcomics.com
4889
    name = 'goodbear-tumblr'
4890
    long_name = 'Good Bear Comics (from Tumblr)'
4891
    url = 'https://goodbearcomics.tumblr.com'
4892
4893
4894
class BrooklynCartoonsTumblr(GenericTumblrV1):
4895
    """Class to retrieve Brooklyn Cartoons."""
4896
    # Also on https://www.brooklyncartoons.com
4897
    # Also on https://www.instagram.com/brooklyncartoons
4898
    name = 'brooklyn-tumblr'
4899
    long_name = 'Brooklyn Cartoons (from Tumblr)'
4900
    url = 'http://brooklyncartoons.tumblr.com'
4901
4902
4903
class GemmaCorrellTumblr(GenericTumblrV1):
4904
    # Also on http://www.gemmacorrell.com/portfolio/comics/
4905
    name = 'gemma-tumblr'
4906
    long_name = 'Gemma Correll (from Tumblr)'
4907
    url = 'http://gemmacorrell.tumblr.com'
4908
4909
4910
class RobotatertotTumblr(GenericTumblrV1):
4911
    """Class to retrieve Robotatertot comics."""
4912
    # Also on https://www.instagram.com/robotatertotcomics
4913
    name = 'robotatertot-tumblr'
4914
    long_name = 'Robotatertot (from Tumblr)'
4915
    url = 'https://robotatertot.tumblr.com'
4916
4917
4918
class HuffyPenguin(GenericTumblrV1):
4919
    """Class to retrieve Huffy Penguin comics."""
4920
    name = 'huffypenguin'
4921
    long_name = 'Huffy Penguin'
4922
    url = 'http://huffy-penguin.tumblr.com'
4923
4924
4925
class CowardlyComicsTumblr(GenericTumblrV1):
4926
    """Class to retrieve Cowardly Comics."""
4927
    # Also on https://tapas.io/series/CowardlyComics
4928
    # Also on http://www.webtoons.com/en/challenge/cowardly-comics/list?title_no=65893
4929
    name = 'cowardly-tumblr'
4930
    long_name = 'Cowardly Comics (from Tumblr)'
4931
    url = 'http://cowardlycomics.tumblr.com'
4932
4933
4934
class Caw4hwTumblr(GenericTumblrV1):
4935
    """Class to retrieve Caw4hw comics."""
4936
    # Also on https://tapas.io/series/CAW4HW
4937
    name = 'caw4hw-tumblr'
4938
    long_name = 'Caw4hw (from Tumblr)'
4939
    url = 'https://caw4hw.tumblr.com'
4940
4941
4942
class WeFlapsTumblr(GenericTumblrV1):
4943
    """Class to retrieve WeFlaps comics."""
4944
    name = 'weflaps-tumblr'
4945
    long_name = 'We Flaps (from Tumblr)'
4946
    url = 'https://weflaps.tumblr.com'
4947
4948
4949
class TheseInsideJokesTumblr(GenericTumblrV1):
4950
    """Class to retrieve These Inside Jokes comics."""
4951
    # Also on http://www.theseinsidejokes.com
4952
    name = 'theseinsidejokes-tumblr'
4953
    long_name = 'These Inside Jokes (from Tumblr)'
4954
    url = 'http://theseinsidejokes.tumblr.com'
4955
4956
4957
class RustledJimmies(GenericTumblrV1):
4958
    """Class to retrieve Rustled Jimmies comics."""
4959
    name = 'restled'
4960
    long_name = 'Rustled Jimmies'
4961
    url = 'http://rustledjimmies.net'
4962
4963
4964
class SinewynTumblr(GenericTumblrV1):
4965
    """Class to retrieve Sinewyn comics."""
4966
    # Also on https://sinewyn.wordpress.com
4967
    name = 'sinewyn-tumblr'
4968
    long_name = 'Sinewyn (from Tumblr)'
4969
    url = 'https://sinewyn.tumblr.com'
4970
4971
4972
class ItFoolsAMonster(GenericTumblrV1):
4973
    """Class to retrieve It Fools A Monster comics."""
4974
    name = 'itfoolsamonster'
4975
    long_name = 'It Fools A Monster'
4976
    url = 'http://itfoolsamonster.com'
4977
4978
4979
class BoumeriesTumblr(GenericTumblrV1):
4980
    """Class to retrieve Boumeries comics."""
4981
    # Also on http://bd.boumerie.com
4982
    # Also on http://comics.boumerie.com
4983
    name = 'boumeries-tumblr'
4984
    long_name = 'Boumeries (from Tumblr)'
4985
    url = 'http://boumeries.tumblr.com/'
4986
    _categories = ('BOUMERIES', )
4987
4988
4989
class InfiniteImmortalBensTumblr(GenericTumblrV1):
4990
    """Class to retrieve Infinite Immortal Bens comics."""
4991
    # Also on http://www.webtoons.com/en/challenge/infinite-immortal-bens/list?title_no=32847
4992
    # Also on https://tapas.io/series/Infinite-Immortal-Bens
4993
    url = 'https://infiniteimmortalbens.tumblr.com'
4994
    name = 'infiniteimmortal-tumblr'
4995
    long_name = 'Infinite Immortal Bens (from Tumblr)'
4996
    _categories = ('INFINITEIMMORTAL', )
4997
4998
4999
class CheeseCornzTumblr(GenericTumblrV1):
5000
    """Class to retrieve Cheese Cornz comics."""
5001
    name = 'cheesecornz-tumblr'
5002
    long_name = 'Cheese Cornz (from Tumblr)'
5003
    url = 'https://cheesecornz.tumblr.com'
5004
5005
5006
class CinismoIlustrado(GenericTumblrV1):
5007
    """Class to retrieve CinismoIlustrado comics."""
5008
    name = 'cinismo'
5009
    long_name = 'Cinismo Ilustrado'
5010
    url = 'http://cinismoilustrado.com'
5011
    _categories = ('ESPANOL', )
5012
5013 View Code Duplication
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
5014
class EatMyPaintTumblr(GenericTumblrV1):
5015
    """Class to retrieve Eat My Paint comics."""
5016
    # Also on https://tapas.io/series/eatmypaint
5017
    name = 'eatmypaint-tumblr'
5018
    long_name = 'Eat My Paint (from Tumblr)'
5019
    url = 'https://eatmypaint.tumblr.com'
5020
    _categories = ('EATMYPAINT', )
5021
5022
5023
class AnomalyTownFromTumblr(GenericTumblrV1):
5024
    """Class to retrieve Anomaly Town."""
5025
    name = 'anomalytown-tumblr'
5026
    long_name = 'Anomaly Town (from Tumblr)'
5027
    url = 'https://anomalytown.tumblr.com'
5028
5029
5030
class RoryTumblr(GenericTumblrV1):
5031
    """Class to retrieve Rory comics."""
5032
    # Also on https://tapas.io/series/Share-Your-Vulnerability
5033
    name = 'rory-tumblr'
5034
    long_name = 'Rory (from Tumblr)'
5035
    url = 'https://rorycomics.tumblr.com/'
5036
    _categories = ('RORY',)
5037
5038
5039
class HorovitzComics(GenericDeletedComic, GenericListableComic):
5040
    """Generic class to handle the logic common to the different comics from Horovitz."""
5041
    # Also on https://horovitzcomics.tumblr.com
5042
    url = 'http://www.horovitzcomics.com'
5043
    _categories = ('HOROVITZ', )
5044
    img_re = re.compile('.*comics/([0-9]*)/([0-9]*)/([0-9]*)/.*$')
5045
    link_re = NotImplemented
5046
    get_url_from_archive_element = join_cls_url_to_href
5047
5048
    @classmethod
5049
    def get_comic_info(cls, soup, link):
5050
        """Get information about a particular comics."""
5051
        href = link['href']
5052
        num = int(cls.link_re.match(href).groups()[0])
5053
        title = link.string
5054
        imgs = soup.find_all('img', id='comic')
5055
        assert len(imgs) == 1, imgs
5056
        year, month, day = [int(s)
5057
                            for s in cls.img_re.match(imgs[0]['src']).groups()]
5058
        return {
5059
            'title': title,
5060
            'day': day,
5061
            'month': month,
5062
            'year': year,
5063
            'img': [i['src'] for i in imgs],
5064
            'num': num,
5065
        }
5066
5067
    @classmethod
5068
    def get_archive_elements(cls):
5069
        archive_url = 'http://www.horovitzcomics.com/comics/archive/'
5070
        return reversed(get_soup_at_url(archive_url).find_all('a', href=cls.link_re))
5071
5072
5073
class HorovitzNew(HorovitzComics):
5074
    """Class to retrieve Horovitz new comics."""
5075
    name = 'horovitznew'
5076
    long_name = 'Horovitz New'
5077
    link_re = re.compile('^/comics/new/([0-9]+)$')
5078
5079
5080
class HorovitzClassic(HorovitzComics):
5081
    """Class to retrieve Horovitz classic comics."""
5082
    name = 'horovitzclassic'
5083
    long_name = 'Horovitz Classic'
5084
    link_re = re.compile('^/comics/classic/([0-9]+)$')
5085
5086
5087
class GenericGoComic(GenericNavigableComic):
5088
    """Generic class to handle the logic common to comics from gocomics.com."""
5089
    _categories = ('GOCOMIC', )
5090
5091
    @classmethod
5092
    def get_first_comic_link(cls):
5093
        """Get link to first comics."""
5094
        div = get_soup_at_url(cls.url).find('div', class_='gc-deck gc-deck--cta-1')
5095
        return div.find('a')
5096
5097
    @classmethod
5098
    def get_navi_link(cls, last_soup, next_):
5099
        """Get link to next or previous comic."""
5100
        PREV = 'fa btn btn-outline-default btn-circle fa-caret-left sm js-previous-comic '
5101
        NEXT = 'fa btn btn-outline-default btn-circle fa-caret-right sm '
5102
        return last_soup.find('a', class_=NEXT if next_ else PREV)
5103
5104
    @classmethod
5105
    def get_url_from_link(cls, link):
5106
        gocomics = 'http://www.gocomics.com'
5107
        return urljoin_wrapper(gocomics, link['href'])
5108
5109
    @classmethod
5110
    def get_comic_info(cls, soup, link):
5111
        """Get information about a particular comics."""
5112
        date_str = soup.find('meta', property='article:published_time')['content']
5113
        day = string_to_date(date_str, "%Y-%m-%d")
5114
        imgs = soup.find_all('meta', property='og:image')
5115
        author = soup.find('meta', property='article:author')['content']
5116
        tags = soup.find('meta', property='article:tag')['content']
5117
        return {
5118
            'day': day.day,
5119
            'month': day.month,
5120
            'year': day.year,
5121
            'img': [i['content'] for i in imgs],
5122
            'author': author,
5123
            'tags': tags,
5124
        }
5125
5126
5127
class PearlsBeforeSwine(GenericGoComic):
5128
    """Class to retrieve Pearls Before Swine comics."""
5129
    name = 'pearls'
5130
    long_name = 'Pearls Before Swine'
5131
    url = 'http://www.gocomics.com/pearlsbeforeswine'
5132
5133
5134
class Peanuts(GenericGoComic):
5135
    """Class to retrieve Peanuts comics."""
5136
    name = 'peanuts'
5137
    long_name = 'Peanuts'
5138
    url = 'http://www.gocomics.com/peanuts'
5139
5140
5141
class MattWuerker(GenericGoComic):
5142
    """Class to retrieve Matt Wuerker comics."""
5143
    name = 'wuerker'
5144
    long_name = 'Matt Wuerker'
5145
    url = 'http://www.gocomics.com/mattwuerker'
5146
5147
5148
class TomToles(GenericGoComic):
5149
    """Class to retrieve Tom Toles comics."""
5150
    name = 'toles'
5151
    long_name = 'Tom Toles'
5152
    url = 'http://www.gocomics.com/tomtoles'
5153
5154
5155
class BreakOfDay(GenericGoComic):
5156
    """Class to retrieve Break Of Day comics."""
5157
    name = 'breakofday'
5158
    long_name = 'Break Of Day'
5159
    url = 'http://www.gocomics.com/break-of-day'
5160
5161
5162
class Brevity(GenericGoComic):
5163
    """Class to retrieve Brevity comics."""
5164
    name = 'brevity'
5165
    long_name = 'Brevity'
5166
    url = 'http://www.gocomics.com/brevity'
5167
5168
5169
class MichaelRamirez(GenericGoComic):
5170
    """Class to retrieve Michael Ramirez comics."""
5171
    name = 'ramirez'
5172
    long_name = 'Michael Ramirez'
5173
    url = 'http://www.gocomics.com/michaelramirez'
5174
5175
5176
class MikeLuckovich(GenericGoComic):
5177
    """Class to retrieve Mike Luckovich comics."""
5178
    name = 'luckovich'
5179
    long_name = 'Mike Luckovich'
5180
    url = 'http://www.gocomics.com/mikeluckovich'
5181
5182
5183
class JimBenton(GenericGoComic):
5184
    """Class to retrieve Jim Benton comics."""
5185
    # Also on http://jimbenton.tumblr.com
5186
    name = 'benton'
5187
    long_name = 'Jim Benton'
5188
    url = 'http://www.gocomics.com/jim-benton-cartoons'
5189
5190
5191
class TheArgyleSweater(GenericGoComic):
5192
    """Class to retrieve the Argyle Sweater comics."""
5193
    name = 'argyle'
5194
    long_name = 'Argyle Sweater'
5195
    url = 'http://www.gocomics.com/theargylesweater'
5196
5197
5198
class SunnyStreet(GenericGoComic):
5199
    """Class to retrieve Sunny Street comics."""
5200
    # Also on http://www.sunnystreetcomics.com
5201
    name = 'sunny'
5202
    long_name = 'Sunny Street'
5203
    url = 'http://www.gocomics.com/sunny-street'
5204
5205
5206
class OffTheMark(GenericGoComic):
5207
    """Class to retrieve Off The Mark comics."""
5208
    # Also on https://www.offthemark.com
5209
    name = 'offthemark'
5210
    long_name = 'Off The Mark'
5211
    url = 'http://www.gocomics.com/offthemark'
5212
5213
5214
class WuMo(GenericGoComic):
5215
    """Class to retrieve WuMo comics."""
5216
    # Also on http://wumo.com
5217
    name = 'wumo'
5218
    long_name = 'WuMo'
5219
    url = 'http://www.gocomics.com/wumo'
5220
5221
5222
class LunarBaboon(GenericGoComic):
5223
    """Class to retrieve Lunar Baboon comics."""
5224
    # Also on http://www.lunarbaboon.com
5225
    # Also on https://tapastic.com/series/Lunarbaboon
5226
    name = 'lunarbaboon'
5227
    long_name = 'Lunar Baboon'
5228
    url = 'http://www.gocomics.com/lunarbaboon'
5229
5230
5231
class SandersenGocomic(GenericGoComic):
5232
    """Class to retrieve Sarah Andersen comics."""
5233
    # Also on http://sarahcandersen.com
5234
    # Also on http://tapastic.com/series/Doodle-Time
5235
    name = 'sandersen-goc'
5236
    long_name = 'Sarah Andersen (from GoComics)'
5237
    url = 'http://www.gocomics.com/sarahs-scribbles'
5238
5239
5240
class SaturdayMorningBreakfastCerealGoComic(GenericGoComic):
5241
    """Class to retrieve Saturday Morning Breakfast Cereal comics."""
5242
    # Also on http://smbc-comics.tumblr.com
5243
    # Also on http://www.smbc-comics.com
5244
    name = 'smbc-goc'
5245
    long_name = 'Saturday Morning Breakfast Cereal (from GoComics)'
5246
    url = 'http://www.gocomics.com/saturday-morning-breakfast-cereal'
5247
    _categories = ('SMBC', )
5248
5249
5250
class CalvinAndHobbesGoComic(GenericGoComic):
5251
    """Class to retrieve Calvin and Hobbes comics."""
5252
    # From gocomics, not http://marcel-oehler.marcellosendos.ch/comics/ch/
5253
    name = 'calvin-goc'
5254
    long_name = 'Calvin and Hobbes (from GoComics)'
5255
    url = 'http://www.gocomics.com/calvinandhobbes'
5256
5257
5258
class RallGoComic(GenericGoComic):
5259
    """Class to retrieve Ted Rall comics."""
5260
    # Also on http://rall.com/comic
5261
    name = 'rall-goc'
5262
    long_name = "Ted Rall (from GoComics)"
5263
    url = "http://www.gocomics.com/ted-rall"
5264
    _categories = ('RALL', )
5265
5266
5267
class TheAwkwardYetiGoComic(GenericGoComic):
5268
    """Class to retrieve The Awkward Yeti comics."""
5269
    # Also on http://larstheyeti.tumblr.com
5270
    # Also on http://theawkwardyeti.com
5271
    # Also on https://tapastic.com/series/TheAwkwardYeti
5272
    name = 'yeti-goc'
5273
    long_name = 'The Awkward Yeti (from GoComics)'
5274
    url = 'http://www.gocomics.com/the-awkward-yeti'
5275
    _categories = ('YETI', )
5276
5277
5278
class BerkeleyMewsGoComics(GenericGoComic):
5279
    """Class to retrieve Berkeley Mews comics."""
5280
    # Also on http://mews.tumblr.com
5281
    # Also on http://www.berkeleymews.com
5282
    name = 'berkeley-goc'
5283
    long_name = 'Berkeley Mews (from GoComics)'
5284
    url = 'http://www.gocomics.com/berkeley-mews'
5285
    _categories = ('BERKELEY', )
5286
5287
5288
class SheldonGoComics(GenericGoComic):
5289
    """Class to retrieve Sheldon comics."""
5290
    # Also on http://www.sheldoncomics.com
5291
    name = 'sheldon-goc'
5292
    long_name = 'Sheldon Comics (from GoComics)'
5293
    url = 'http://www.gocomics.com/sheldon'
5294
5295
5296
class FowlLanguageGoComics(GenericGoComic):
5297
    """Class to retrieve Fowl Language comics."""
5298
    # Also on http://www.fowllanguagecomics.com
5299
    # Also on http://tapastic.com/series/Fowl-Language-Comics
5300
    # Also on http://fowllanguagecomics.tumblr.com
5301
    name = 'fowllanguage-goc'
5302
    long_name = 'Fowl Language Comics (from GoComics)'
5303
    url = 'http://www.gocomics.com/fowl-language'
5304
    _categories = ('FOWLLANGUAGE', )
5305
5306
5307
class NickAnderson(GenericGoComic):
5308
    """Class to retrieve Nick Anderson comics."""
5309
    name = 'nickanderson'
5310
    long_name = 'Nick Anderson'
5311
    url = 'http://www.gocomics.com/nickanderson'
5312
5313
5314
class GarfieldGoComics(GenericGoComic):
5315
    """Class to retrieve Garfield comics."""
5316
    # Also on http://garfield.com
5317
    name = 'garfield-goc'
5318
    long_name = 'Garfield (from GoComics)'
5319
    url = 'http://www.gocomics.com/garfield'
5320
    _categories = ('GARFIELD', )
5321
5322
5323
class DorrisMcGoComics(GenericGoComic):
5324
    """Class to retrieve Dorris Mc Comics"""
5325
    # Also on http://dorrismccomics.com
5326
    name = 'dorrismc-goc'
5327
    long_name = 'Dorris Mc (from GoComics)'
5328
    url = 'http://www.gocomics.com/dorris-mccomics'
5329
5330
5331
class FoxTrot(GenericGoComic):
5332
    """Class to retrieve FoxTrot comics."""
5333
    name = 'foxtrot'
5334
    long_name = 'FoxTrot'
5335
    url = 'http://www.gocomics.com/foxtrot'
5336
5337
5338
class FoxTrotClassics(GenericGoComic):
5339
    """Class to retrieve FoxTrot Classics comics."""
5340
    name = 'foxtrot-classics'
5341
    long_name = 'FoxTrot Classics'
5342
    url = 'http://www.gocomics.com/foxtrotclassics'
5343
5344
5345
class MisterAndMeGoComics(GenericDeletedComic, GenericGoComic):
5346
    """Class to retrieve Mister & Me Comics."""
5347
    # Also on http://www.mister-and-me.com
5348
    # Also on https://tapastic.com/series/Mister-and-Me
5349
    name = 'mister-goc'
5350
    long_name = 'Mister & Me (from GoComics)'
5351
    url = 'http://www.gocomics.com/mister-and-me'
5352
5353
5354
class NonSequitur(GenericGoComic):
5355
    """Class to retrieve Non Sequitur (Wiley Miller) comics."""
5356
    name = 'nonsequitur'
5357
    long_name = 'Non Sequitur'
5358
    url = 'http://www.gocomics.com/nonsequitur'
5359
5360
5361
class JoeyAlisonSayers(GenericGoComic):
5362
    """Class to retrieve Joey Alison Sayers comics."""
5363
    name = 'joeyalison'
5364
    long_name = 'Joey Alison Sayers (from GoComics)'
5365
    url = 'http://www.gocomics.com/joey-alison-sayers-comics'
5366
5367
5368
class SavageChickenGoComics(GenericGoComic):
5369
    """Class to retrieve Savage Chicken comics."""
5370
    # Also on http://www.savagechickens.com
5371
    name = 'savage-goc'
5372
    long_name = 'Savage Chicken (from GoComics)'
5373
    url = 'http://www.gocomics.com/savage-chickens'
5374
5375
5376
class GenericTapasticComic(GenericListableComic):
5377
    """Generic class to handle the logic common to comics from tapastic.com."""
5378
    _categories = ('TAPASTIC', )
5379
5380
    @classmethod
5381
    def get_comic_info(cls, soup, archive_elt):
5382
        """Get information about a particular comics."""
5383
        timestamp = int(archive_elt['publishDate']) / 1000.0
5384
        day = datetime.datetime.fromtimestamp(timestamp).date()
5385
        imgs = soup.find_all('img', class_='art-image')
5386
        if not imgs:
5387
            # print("Comic %s is being uploaded, retry later" % cls.get_url_from_archive_element(archive_elt))
5388
            return None
5389
        assert len(imgs) > 0, imgs
5390
        return {
5391
            'day': day.day,
5392
            'year': day.year,
5393
            'month': day.month,
5394
            'img': [i['src'] for i in imgs],
5395
            'title': archive_elt['title'],
5396
        }
5397
5398
    @classmethod
5399
    def get_url_from_archive_element(cls, archive_elt):
5400
        return 'http://tapastic.com/episode/' + str(archive_elt['id'])
5401
5402
    @classmethod
5403
    def get_archive_elements(cls):
5404
        pref, suff = 'episodeList : ', ','
5405
        # Information is stored in the javascript part
5406
        # I don't know the clean way to get it so this is the ugly way.
5407
        string = [s[len(pref):-len(suff)] for s in (s.decode('utf-8').strip() for s in urlopen_wrapper(cls.url).readlines()) if s.startswith(pref) and s.endswith(suff)][0]
5408
        return json.loads(string)
5409
5410
5411
class VegetablesForDessert(GenericTapasticComic):
5412
    """Class to retrieve Vegetables For Dessert comics."""
5413
    # Also on http://vegetablesfordessert.tumblr.com
5414
    name = 'vegetables'
5415
    long_name = 'Vegetables For Dessert'
5416
    url = 'http://tapastic.com/series/vegetablesfordessert'
5417
5418
5419
class FowlLanguageTapa(GenericTapasticComic):
5420
    """Class to retrieve Fowl Language comics."""
5421
    # Also on http://www.fowllanguagecomics.com
5422
    # Also on http://fowllanguagecomics.tumblr.com
5423
    # Also on http://www.gocomics.com/fowl-language
5424
    name = 'fowllanguage-tapa'
5425
    long_name = 'Fowl Language Comics (from Tapastic)'
5426
    url = 'http://tapastic.com/series/Fowl-Language-Comics'
5427
    _categories = ('FOWLLANGUAGE', )
5428
5429
5430
class OscillatingProfundities(GenericTapasticComic):
5431
    """Class to retrieve Oscillating Profundities comics."""
5432
    name = 'oscillating'
5433
    long_name = 'Oscillating Profundities'
5434
    url = 'http://tapastic.com/series/oscillatingprofundities'
5435
5436
5437
class ZnoflatsComics(GenericTapasticComic):
5438
    """Class to retrieve Znoflats comics."""
5439
    name = 'znoflats'
5440
    long_name = 'Znoflats Comics'
5441
    url = 'http://tapastic.com/series/Znoflats-Comics'
5442
5443
5444
class SandersenTapastic(GenericTapasticComic):
5445
    """Class to retrieve Sarah Andersen comics."""
5446
    # Also on http://sarahcandersen.com
5447
    # Also on http://www.gocomics.com/sarahs-scribbles
5448
    name = 'sandersen-tapa'
5449
    long_name = 'Sarah Andersen (from Tapastic)'
5450
    url = 'http://tapastic.com/series/Doodle-Time'
5451
5452
5453
class TubeyToonsTapastic(GenericTapasticComic):
5454
    """Class to retrieve TubeyToons comics."""
5455
    # Also on http://tubeytoons.com
5456
    # Also on https://tubeytoons.tumblr.com
5457
    name = 'tubeytoons-tapa'
5458
    long_name = 'Tubey Toons (from Tapastic)'
5459
    url = 'http://tapastic.com/series/Tubey-Toons'
5460
    _categories = ('TUNEYTOONS', )
5461
5462
5463
class AnythingComicTapastic(GenericTapasticComic):
5464
    """Class to retrieve Anything Comics."""
5465
    # Also on http://www.anythingcomic.com
5466
    name = 'anythingcomic-tapa'
5467
    long_name = 'Anything Comic (from Tapastic)'
5468
    url = 'http://tapastic.com/series/anything'
5469
5470
5471
class UnearthedComicsTapastic(GenericTapasticComic):
5472
    """Class to retrieve Unearthed comics."""
5473
    # Also on http://unearthedcomics.com
5474
    # Also on https://unearthedcomics.tumblr.com
5475
    name = 'unearthed-tapa'
5476
    long_name = 'Unearthed Comics (from Tapastic)'
5477
    url = 'http://tapastic.com/series/UnearthedComics'
5478
    _categories = ('UNEARTHED', )
5479
5480
5481
class EverythingsStupidTapastic(GenericTapasticComic):
5482
    """Class to retrieve Everything's stupid Comics."""
5483
    # Also on http://www.webtoons.com/en/challenge/everythings-stupid/list?title_no=14591
5484
    # Also on http://everythingsstupid.net
5485
    name = 'stupid-tapa'
5486
    long_name = "Everything's Stupid (from Tapastic)"
5487
    url = 'http://tapastic.com/series/EverythingsStupid'
5488
5489
5490
class JustSayEhTapastic(GenericTapasticComic):
5491
    """Class to retrieve Just Say Eh comics."""
5492
    # Also on http://www.justsayeh.com
5493
    name = 'justsayeh-tapa'
5494
    long_name = 'Just Say Eh (from Tapastic)'
5495
    url = 'http://tapastic.com/series/Just-Say-Eh'
5496
5497
5498
class ThorsThundershackTapastic(GenericTapasticComic):
5499
    """Class to retrieve Thor's Thundershack comics."""
5500
    # Also on http://www.thorsthundershack.com
5501
    name = 'thor-tapa'
5502
    long_name = 'Thor\'s Thundershack (from Tapastic)'
5503
    url = 'http://tapastic.com/series/Thors-Thundershac'
5504
    _categories = ('THOR', )
5505
5506
5507
class OwlTurdTapastic(GenericTapasticComic):
5508
    """Class to retrieve Owl Turd / Shen comix."""
5509
    # Also on http://shencomix.com
5510
    name = 'owlturd-tapa'
5511
    long_name = 'Owl Turd / Shen Comix (from Tapastic)'
5512
    url = 'https://tapas.io/series/Shen-Comix'
5513
    _categories = ('OWLTURD', 'SHENCOMIX')
5514
5515
5516
class GoneIntoRaptureTapastic(GenericTapasticComic):
5517
    """Class to retrieve Gone Into Rapture comics."""
5518
    # Also on http://goneintorapture.tumblr.com
5519
    # Also on http://goneintorapture.com
5520
    name = 'rapture-tapa'
5521
    long_name = 'Gone Into Rapture (from Tapastic)'
5522
    url = 'http://tapastic.com/series/Goneintorapture'
5523
5524
5525
class HeckIfIKnowComicsTapa(GenericTapasticComic):
5526
    """Class to retrieve Heck If I Know Comics."""
5527
    # Also on http://heckifiknowcomics.com
5528
    name = 'heck-tapa'
5529
    long_name = 'Heck if I Know comics (from Tapastic)'
5530
    url = 'http://tapastic.com/series/Regular'
5531
5532
5533
class CheerUpEmoKidTapa(GenericTapasticComic):
5534
    """Class to retrieve CheerUpEmoKid comics."""
5535
    # Also on http://www.cheerupemokid.com
5536
    # Also on https://enzocomics.tumblr.com
5537
    name = 'cuek-tapa'
5538
    long_name = 'Cheer Up Emo Kid (from Tapastic)'
5539
    url = 'http://tapastic.com/series/CUEK'
5540
5541
5542
class BigFootJusticeTapa(GenericTapasticComic):
5543
    """Class to retrieve Big Foot Justice comics."""
5544
    # Also on http://bigfootjustice.com
5545
    name = 'bigfoot-tapa'
5546
    long_name = 'Big Foot Justice (from Tapastic)'
5547
    url = 'http://tapastic.com/series/bigfoot-justice'
5548
5549
5550
class UpAndOutTapa(GenericTapasticComic):
5551
    """Class to retrieve Up & Out comics."""
5552
    # Also on http://upandoutcomic.tumblr.com
5553
    name = 'upandout-tapa'
5554
    long_name = 'Up And Out (from Tapastic)'
5555
    url = 'http://tapastic.com/series/UP-and-OUT'
5556
5557
5558
class ToonHoleTapa(GenericTapasticComic):
5559
    """Class to retrieve Toon Holes comics."""
5560
    # Also on http://www.toonhole.com
5561
    name = 'toonhole-tapa'
5562
    long_name = 'Toon Hole (from Tapastic)'
5563
    url = 'http://tapastic.com/series/TOONHOLE'
5564
5565
5566
class AngryAtNothingTapa(GenericTapasticComic):
5567
    """Class to retrieve Angry at Nothing comics."""
5568
    # Also on http://www.angryatnothing.net
5569
    # Also on http://angryatnothing.tumblr.com
5570
    name = 'angry-tapa'
5571
    long_name = 'Angry At Nothing (from Tapastic)'
5572
    url = 'http://tapastic.com/series/Comics-yeah-definitely-comics-'
5573
5574
5575
class LeleozTapa(GenericTapasticComic):
5576
    """Class to retrieve Leleoz comics."""
5577
    # Also on http://leleozcomics.tumblr.com
5578
    name = 'leleoz-tapa'
5579
    long_name = 'Leleoz (from Tapastic)'
5580
    url = 'https://tapastic.com/series/Leleoz'
5581
5582
5583
class TheAwkwardYetiTapa(GenericTapasticComic):
5584
    """Class to retrieve The Awkward Yeti comics."""
5585
    # Also on http://www.gocomics.com/the-awkward-yeti
5586
    # Also on http://theawkwardyeti.com
5587
    # Also on http://larstheyeti.tumblr.com
5588
    name = 'yeti-tapa'
5589
    long_name = 'The Awkward Yeti (from Tapastic)'
5590
    url = 'https://tapastic.com/series/TheAwkwardYeti'
5591
    _categories = ('YETI', )
5592
5593
5594
class AsPerUsualTapa(GenericTapasticComic):
5595
    """Class to retrieve As Per Usual comics."""
5596
    # Also on http://as-per-usual.tumblr.com
5597
    name = 'usual-tapa'
5598
    long_name = 'As Per Usual (from Tapastic)'
5599
    url = 'https://tapastic.com/series/AsPerUsual'
5600
    categories = ('DAMILEE', )
5601
5602
5603
class HotComicsForCoolPeopleTapa(GenericTapasticComic):
5604
    """Class to retrieve Hot Comics For Cool People."""
5605
    # Also on http://hotcomicsforcoolpeople.tumblr.com
5606
    # Also on http://hotcomics.biz (links to tumblr)
5607
    # Also on http://hcfcp.com (links to tumblr)
5608
    name = 'hotcomics-tapa'
5609
    long_name = 'Hot Comics For Cool People (from Tapastic)'
5610
    url = 'https://tapastic.com/series/Hot-Comics-For-Cool-People'
5611
    categories = ('DAMILEE', )
5612
5613
5614
class OneOneOneOneComicTapa(GenericTapasticComic):
5615
    """Class to retrieve 1111 Comics."""
5616
    # Also on http://www.1111comics.me
5617
    # Also on http://comics1111.tumblr.com
5618
    name = '1111-tapa'
5619
    long_name = '1111 Comics (from Tapastic)'
5620
    url = 'https://tapastic.com/series/1111-Comics'
5621
    _categories = ('ONEONEONEONE', )
5622
5623
5624
class TumbleDryTapa(GenericTapasticComic):
5625
    """Class to retrieve Tumble Dry comics."""
5626
    # Also on http://tumbledrycomics.com
5627
    name = 'tumbledry-tapa'
5628
    long_name = 'Tumblr Dry (from Tapastic)'
5629
    url = 'https://tapastic.com/series/TumbleDryComics'
5630
5631
5632
class DeadlyPanelTapa(GenericTapasticComic):
5633
    """Class to retrieve Deadly Panel comics."""
5634
    # Also on http://www.deadlypanel.com
5635
    # Also on https://deadlypanel.tumblr.com
5636
    name = 'deadly-tapa'
5637
    long_name = 'Deadly Panel (from Tapastic)'
5638
    url = 'https://tapastic.com/series/deadlypanel'
5639
5640
5641
class ChrisHallbeckMaxiTapa(GenericTapasticComic):
5642
    """Class to retrieve Chris Hallbeck comics."""
5643
    # Also on https://chrishallbeck.tumblr.com
5644
    # Also on http://maximumble.com
5645
    name = 'hallbeckmaxi-tapa'
5646
    long_name = 'Chris Hallback - Maximumble (from Tapastic)'
5647
    url = 'https://tapastic.com/series/Maximumble'
5648
    _categories = ('HALLBACK', )
5649
5650
5651
class ChrisHallbeckMiniTapa(GenericDeletedComic, GenericTapasticComic):
5652
    """Class to retrieve Chris Hallbeck comics."""
5653
    # Also on https://chrishallbeck.tumblr.com
5654
    # Also on http://minimumble.com
5655
    name = 'hallbeckmini-tapa'
5656
    long_name = 'Chris Hallback - Minimumble (from Tapastic)'
5657
    url = 'https://tapastic.com/series/Minimumble'
5658
    _categories = ('HALLBACK', )
5659
5660
5661
class ChrisHallbeckBiffTapa(GenericDeletedComic, GenericTapasticComic):
5662
    """Class to retrieve Chris Hallbeck comics."""
5663
    # Also on https://chrishallbeck.tumblr.com
5664
    # Also on http://thebookofbiff.com
5665
    name = 'hallbeckbiff-tapa'
5666
    long_name = 'Chris Hallback - The Book of Biff (from Tapastic)'
5667
    url = 'https://tapastic.com/series/Biff'
5668
    _categories = ('HALLBACK', )
5669
5670
5671
class RandoWisTapa(GenericTapasticComic):
5672
    """Class to retrieve RandoWis comics."""
5673
    # Also on https://randowis.com
5674
    name = 'randowis-tapa'
5675
    long_name = 'RandoWis (from Tapastic)'
5676
    url = 'https://tapastic.com/series/RandoWis'
5677
5678
5679
class PigeonGazetteTapa(GenericTapasticComic):
5680
    """Class to retrieve The Pigeon Gazette comics."""
5681
    # Also on http://thepigeongazette.tumblr.com
5682
    name = 'pigeon-tapa'
5683
    long_name = 'The Pigeon Gazette (from Tapastic)'
5684
    url = 'https://tapastic.com/series/The-Pigeon-Gazette'
5685
5686
5687
class TheOdd1sOutTapa(GenericTapasticComic):
5688
    """Class to retrieve The Odd 1s Out comics."""
5689
    # Also on http://theodd1sout.com
5690
    # Also on http://theodd1sout.tumblr.com
5691
    name = 'theodd-tapa'
5692
    long_name = 'The Odd 1s Out (from Tapastic)'
5693
    url = 'https://tapastic.com/series/Theodd1sout'
5694
5695
5696
class TheWorldIsFlatTapa(GenericTapasticComic):
5697
    """Class to retrieve The World Is Flat Comics."""
5698
    # Also on http://theworldisflatcomics.tumblr.com
5699
    name = 'flatworld-tapa'
5700
    long_name = 'The World Is Flat (from Tapastic)'
5701
    url = 'https://tapastic.com/series/The-World-is-Flat'
5702
5703
5704
class MisterAndMeTapa(GenericTapasticComic):
5705
    """Class to retrieve Mister & Me Comics."""
5706
    # Also on http://www.mister-and-me.com
5707
    # Also on http://www.gocomics.com/mister-and-me
5708
    name = 'mister-tapa'
5709
    long_name = 'Mister & Me (from Tapastic)'
5710
    url = 'https://tapastic.com/series/Mister-and-Me'
5711
5712
5713
class TalesOfAbsurdityTapa(GenericDeletedComic, GenericTapasticComic):
5714
    """Class to retrieve Tales Of Absurdity comics."""
5715
    # Also on http://talesofabsurdity.com
5716
    # Also on http://talesofabsurdity.tumblr.com
5717
    name = 'absurdity-tapa'
5718
    long_name = 'Tales of Absurdity (from Tapastic)'
5719
    url = 'http://tapastic.com/series/Tales-Of-Absurdity'
5720
    _categories = ('ABSURDITY', )
5721
5722
5723
class BFGFSTapa(GenericTapasticComic):
5724
    """Class to retrieve BFGFS comics."""
5725
    # Also on http://bfgfs.com
5726
    # Also on https://bfgfs.tumblr.com
5727
    name = 'bfgfs-tapa'
5728
    long_name = 'BFGFS (from Tapastic)'
5729
    url = 'https://tapastic.com/series/BFGFS'
5730
5731
5732
class DoodleForFoodTapa(GenericTapasticComic):
5733
    """Class to retrieve Doodle For Food comics."""
5734
    # Also on http://www.doodleforfood.com
5735
    name = 'doodle-tapa'
5736
    long_name = 'Doodle For Food (from Tapastic)'
5737
    url = 'https://tapastic.com/series/Doodle-for-Food'
5738
5739
5740
class MrLovensteinTapa(GenericTapasticComic):
5741
    """Class to retrieve Mr Lovenstein comics."""
5742
    # Also on  https://tapastic.com/series/MrLovenstein
5743
    name = 'mrlovenstein-tapa'
5744
    long_name = 'Mr. Lovenstein (from Tapastic)'
5745
    url = 'https://tapastic.com/series/MrLovenstein'
5746
5747
5748
class CassandraCalinTapa(GenericTapasticComic):
5749
    """Class to retrieve C. Cassandra comics."""
5750
    # Also on http://cassandracalin.com
5751
    # Also on http://c-cassandra.tumblr.com
5752
    name = 'cassandra-tapa'
5753
    long_name = 'Cassandra Calin (from Tapastic)'
5754
    url = 'https://tapastic.com/series/C-Cassandra-comics'
5755
5756
5757
class WafflesAndPancakes(GenericTapasticComic):
5758
    """Class to retrieve Waffles And Pancakes comics."""
5759
    # Also on http://wandpcomic.com
5760
    name = 'waffles'
5761
    long_name = 'Waffles And Pancakes'
5762
    url = 'https://tapastic.com/series/Waffles-and-Pancakes'
5763
5764
5765
class YesterdaysPopcornTapastic(GenericTapasticComic):
5766
    """Class to retrieve Yesterday's Popcorn comics."""
5767
    # Also on http://www.yesterdayspopcorn.com
5768
    # Also on http://yesterdayspopcorn.tumblr.com
5769
    name = 'popcorn-tapa'
5770
    long_name = 'Yesterday\'s Popcorn (from Tapastic)'
5771
    url = 'https://tapastic.com/series/Yesterdays-Popcorn'
5772
5773
5774
class OurSuperAdventureTapastic(GenericDeletedComic, GenericTapasticComic):
5775
    """Class to retrieve Our Super Adventure comics."""
5776
    # Also on http://www.oursuperadventure.com
5777
    # http://sarahssketchbook.tumblr.com
5778
    # http://sarahgraley.com
5779
    name = 'superadventure-tapastic'
5780
    long_name = 'Our Super Adventure (from Tapastic)'
5781
    url = 'https://tapastic.com/series/Our-Super-Adventure'
5782
5783
5784
class NamelessPCs(GenericTapasticComic):
5785
    """Class to retrieve Nameless PCs comics."""
5786
    # Also on http://namelesspcs.com
5787
    name = 'namelesspcs-tapa'
5788
    long_name = 'NamelessPCs (from Tapastic)'
5789
    url = 'https://tapastic.com/series/NamelessPC'
5790
5791
5792
class DownTheUpwardSpiralTapa(GenericTapasticComic):
5793
    """Class to retrieve Down The Upward Spiral comics."""
5794
    # Also on http://www.downtheupwardspiral.com
5795
    # Also on http://downtheupwardspiral.tumblr.com
5796
    name = 'spiral-tapa'
5797
    long_name = 'Down the Upward Spiral (from Tapastic)'
5798
    url = 'https://tapastic.com/series/Down-the-Upward-Spiral'
5799
5800
5801
class UbertoolTapa(GenericTapasticComic):
5802
    """Class to retrieve Ubertool comics."""
5803
    # Also on http://ubertoolcomic.com
5804
    # Also on https://ubertool.tumblr.com
5805
    name = 'ubertool-tapa'
5806
    long_name = 'Ubertool (from Tapastic)'
5807
    url = 'https://tapastic.com/series/ubertool'
5808
    _categories = ('UBERTOOL', )
5809
5810
5811
class BarteNerdsTapa(GenericDeletedComic, GenericTapasticComic):
5812
    """Class to retrieve BarteNerds comics."""
5813
    # Also on http://www.bartenerds.com
5814
    name = 'bartenerds-tapa'
5815
    long_name = 'BarteNerds (from Tapastic)'
5816
    url = 'https://tapastic.com/series/BarteNERDS'
5817
5818
5819
class SmallBlueYonderTapa(GenericTapasticComic):
5820
    """Class to retrieve Small Blue Yonder comics."""
5821
    # Also on http://www.smallblueyonder.com
5822
    name = 'smallblue-tapa'
5823
    long_name = 'Small Blue Yonder (from Tapastic)'
5824
    url = 'https://tapastic.com/series/Small-Blue-Yonder'
5825
5826
5827
class TizzyStitchBirdTapa(GenericTapasticComic):
5828
    """Class to retrieve Tizzy Stitch Bird comics."""
5829
    # Also on http://tizzystitchbird.com
5830
    # Also on http://tizzystitchbird.tumblr.com
5831
    # Also on http://www.webtoons.com/en/challenge/tizzy-stitchbird/list?title_no=50082
5832
    name = 'tizzy-tapa'
5833
    long_name = 'Tizzy Stitch Bird (from Tapastic)'
5834
    url = 'https://tapastic.com/series/TizzyStitchbird'
5835
5836
5837
class RockPaperCynicTapa(GenericTapasticComic):
5838
    """Class to retrieve RockPaperCynic comics."""
5839
    # Also on http://www.rockpapercynic.com
5840
    # Also on http://rockpapercynic.tumblr.com
5841
    name = 'rpc-tapa'
5842
    long_name = 'Rock Paper Cynic (from Tapastic)'
5843
    url = 'https://tapastic.com/series/rockpapercynic'
5844
5845
5846
class IsItCanonTapa(GenericTapasticComic):
5847
    """Class to retrieve Is It Canon comics."""
5848
    # Also on http://www.isitcanon.com
5849
    name = 'canon-tapa'
5850
    long_name = 'Is It Canon (from Tapastic)'
5851
    url = 'http://tapastic.com/series/isitcanon'
5852
5853
5854
class ItsTheTieTapa(GenericTapasticComic):
5855
    """Class to retrieve It's the tie comics."""
5856
    # Also on http://itsthetie.com
5857
    # Also on http://itsthetie.tumblr.com
5858
    name = 'tie-tapa'
5859
    long_name = "It's the tie (from Tapastic)"
5860
    url = "https://tapastic.com/series/itsthetie"
5861
    _categories = ('TIE', )
5862
5863
5864
class JamesOfNoTradesTapa(GenericTapasticComic):
5865
    """Class to retrieve JamesOfNoTrades comics."""
5866
    # Also on http://jamesofnotrades.com
5867
    # Also on http://www.webtoons.com/en/challenge/james-of-no-trades/list?title_no=43422
5868
    # Also on http://jamesfregan.tumblr.com
5869
    name = 'jamesofnotrades-tapa'
5870
    long_name = 'James Of No Trades (from Tapastic)'
5871
    url = 'https://tapas.io/series/James-of-No-Trades'
5872
    _categories = ('JAMESOFNOTRADES', )
5873
5874
5875
class MomentumTapa(GenericTapasticComic):
5876
    """Class to retrieve Momentum comics."""
5877
    # Also on http://www.momentumcomic.com
5878
    name = 'momentum-tapa'
5879
    long_name = 'Momentum (from Tapastic)'
5880
    url = 'https://tapastic.com/series/momentum'
5881
5882
5883
class InYourFaceCakeTapa(GenericTapasticComic):
5884
    """Class to retrieve In Your Face Cake comics."""
5885
    # Also on https://in-your-face-cake.tumblr.com
5886
    name = 'inyourfacecake-tapa'
5887
    long_name = 'In Your Face Cake (from Tapastic)'
5888
    url = 'https://tapas.io/series/In-Your-Face-Cake'
5889
    _categories = ('INYOURFACECAKE', )
5890
5891
5892
class CowardlyComicsTapa(GenericTapasticComic):
5893
    """Class to retrieve Cowardly Comics."""
5894
    # Also on http://cowardlycomics.tumblr.com
5895
    # Also on http://www.webtoons.com/en/challenge/cowardly-comics/list?title_no=65893
5896
    name = 'cowardly-tapa'
5897
    long_name = 'Cowardly Comics (from Tapastic)'
5898
    url = 'https://tapas.io/series/CowardlyComics'
5899
5900
5901
class Caw4hwTapa(GenericTapasticComic):
5902
    """Class to retrieve Caw4hw comics."""
5903
    # Also on https://caw4hw.tumblr.com
5904
    name = 'caw4hw-tapa'
5905
    long_name = 'Caw4hw (from Tapastic)'
5906
    url = 'https://tapas.io/series/CAW4HW'
5907
5908
5909
class DontBeDadTapa(GenericTapasticComic):
5910
    """Class to retrieve Don't Be Dad comics."""
5911
    # Also on https://dontbedad.com/
5912
    # Also on http://www.webtoons.com/en/challenge/dontbedad/list?title_no=123074
5913
    name = 'dontbedad-tapa'
5914
    long_name = "Don't Be Dad (from Tapastic)"
5915
    url = 'https://tapas.io/series/DontBeDad-Comics'
5916
5917
5918
class APleasantWasteOfTimeTapa(GenericTapasticComic):
5919
    """Class to retrieve A Pleasant Waste Of Time comics."""
5920
    # Also on https://artjcf.tumblr.com
5921
    name = 'pleasant-waste-tapa'
5922
    long_name = 'A Pleasant Waste Of Time (from Tapastic)'
5923
    url = 'https://tapas.io/series/A-Pleasant-'
5924
    _categories = ('WASTE', )
5925
5926
5927
class InfiniteImmortalBensTapa(GenericTapasticComic):
5928
    """Class to retrieve Infinite Immortal Bens comics."""
5929
    # Also on http://www.webtoons.com/en/challenge/infinite-immortal-bens/list?title_no=32847
5930
    # Also on https://infiniteimmortalbens.tumblr.com
5931
    url = 'https://tapas.io/series/Infinite-Immortal-Bens'
5932
    name = 'infiniteimmortal-tapa'
5933
    long_name = 'Infinite Immortal Bens (from Tapastic)'
5934
    _categories = ('INFINITEIMMORTAL', )
5935
5936
5937
class EatMyPaintTapa(GenericTapasticComic):
5938
    """Class to retrieve Eat My Paint comics."""
5939
    # Also on https://eatmypaint.tumblr.com
5940
    name = 'eatmypaint-tapa'
5941
    long_name = 'Eat My Paint (from Tapastic)'
5942
    url = 'https://tapas.io/series/eatmypaint'
5943
    _categories = ('EATMYPAINT', )
5944
5945
5946
class RoryTapastic(GenericTapasticComic):
5947
    """Class to retrieve Rory comics."""
5948
    # Also on https://rorycomics.tumblr.com/
5949
    name = 'rory-tapa'
5950
    long_name = 'Rory (from Tapastic)'
5951
    url = 'https://tapas.io/series/Share-Your-Vulnerability'
5952
    _categories = ('RORY',)
5953
5954
5955
class MercworksTapa(GenericTapasticComic):
5956
    """Class to retrieve Mercworks comics."""
5957
    # Also on http://mercworks.net
5958
    # Also on http://www.webtoons.com/en/comedy/mercworks/list?title_no=426
5959
    # Also on http://mercworks.tumblr.com
5960
    name = 'mercworks-tapa'
5961
    long_name = 'Mercworks (from Tapastic)'
5962
    url = 'https://tapastic.com/series/MercWorks'
5963
    _categories = ('MERCWORKS', )
5964
5965
5966
class AbsurdoLapin(GenericNavigableComic):
5967
    """Class to retrieve Absurdo Lapin comics."""
5968
    name = 'absurdo'
5969
    long_name = 'Absurdo'
5970
    url = 'https://absurdo.lapin.org'
5971
    get_url_from_link = join_cls_url_to_href
5972
5973
    @classmethod
5974
    def get_nav(cls, soup):
5975
        """Get the navigation elements from soup object."""
5976
        cont = soup.find('div', id='content')
5977
        _, b2 = cont.find_all('div', class_='buttons')
5978
        # prev, first, last, next
5979
        return [li.find('a') for li in b2.find_all('li')]
5980
5981
    @classmethod
5982
    def get_first_comic_link(cls):
5983
        """Get link to first comics."""
5984
        return cls.get_nav(get_soup_at_url(cls.url))[1]
5985
5986
    @classmethod
5987
    def get_navi_link(cls, last_soup, next_):
5988
        """Get link to next or previous comic."""
5989
        return cls.get_nav(last_soup)[3 if next_ else 0]
5990
5991
    @classmethod
5992
    def get_comic_info(cls, soup, link):
5993
        """Get information about a particular comics."""
5994
        author = soup.find('meta', attrs={'name': 'author'})['content']
5995
        tags = soup.find('meta', attrs={'name': 'keywords'})['content']
5996
        title = soup.find('title').string
5997
        imgs = soup.find('div', id='content').find_all('img')
5998
        return {
5999
            'title': title,
6000
            'img': [urljoin_wrapper(cls.url, i['src']) for i in imgs],
6001
            'tags': tags,
6002
            'author': author,
6003
        }
6004
6005
6006
def get_subclasses(klass):
6007
    """Gets the list of direct/indirect subclasses of a class"""
6008
    subclasses = klass.__subclasses__()
6009
    for derived in list(subclasses):
6010
        subclasses.extend(get_subclasses(derived))
6011
    return subclasses
6012
6013
6014
def remove_st_nd_rd_th_from_date(string):
6015
    """Function to transform 1st/2nd/3rd/4th in a parsable date format."""
6016
    # Hackish way to convert string with numeral "1st"/"2nd"/etc to date
6017
    return (string.replace('st', '')
6018
            .replace('nd', '')
6019
            .replace('rd', '')
6020
            .replace('th', '')
6021
            .replace('Augu', 'August'))
6022
6023
6024
def string_to_date(string, date_format, local=DEFAULT_LOCAL):
6025
    """Function to convert string to date object.
6026
    Wrapper around datetime.datetime.strptime."""
6027
    # format described in https://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior
6028
    prev_locale = locale.setlocale(locale.LC_ALL)
6029
    if local != prev_locale:
6030
        locale.setlocale(locale.LC_ALL, local)
6031
    ret = datetime.datetime.strptime(string, date_format).date()
6032
    if local != prev_locale:
6033
        locale.setlocale(locale.LC_ALL, prev_locale)
6034
    return ret
6035
6036
6037
COMICS = set(get_subclasses(GenericComic))
6038
VALID_COMICS = [c for c in COMICS if c.name is not None]
6039
COMIC_NAMES = {c.name: c for c in VALID_COMICS}
6040
assert len(VALID_COMICS) == len(COMIC_NAMES)
6041
CLASS_NAMES = {c.__name__ for c in VALID_COMICS}
6042
assert len(VALID_COMICS) == len(CLASS_NAMES)
6043