Completed
Push — master ( b821fd...05b9ad )
by De
26s
created

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