mirror of
https://github.com/kemayo/leech
synced 2025-12-06 08:22:56 +01:00
Use the newer syntax for attrs
This commit is contained in:
parent
d6d23e4c60
commit
a39e1e9f89
3 changed files with 56 additions and 60 deletions
|
|
@ -8,7 +8,7 @@ import html
|
||||||
import unicodedata
|
import unicodedata
|
||||||
import datetime
|
import datetime
|
||||||
import requests
|
import requests
|
||||||
import attr
|
from attrs import define, asdict
|
||||||
|
|
||||||
html_template = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
html_template = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
|
||||||
|
|
@ -66,17 +66,16 @@ frontmatter_template = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@define
|
||||||
class CoverOptions:
|
class CoverOptions:
|
||||||
fontname = attr.ib(default=None, converter=attr.converters.optional(str))
|
fontname: str = None
|
||||||
fontsize = attr.ib(default=None, converter=attr.converters.optional(int))
|
fontsize: int = None
|
||||||
width = attr.ib(default=None, converter=attr.converters.optional(int))
|
width: int = None
|
||||||
height = attr.ib(default=None, converter=attr.converters.optional(int))
|
height: int = None
|
||||||
wrapat = attr.ib(default=None, converter=attr.converters.optional(int))
|
wrapat: int = None
|
||||||
bgcolor = attr.ib(default=None, converter=attr.converters.optional(tuple))
|
bgcolor: tuple = None
|
||||||
textcolor = attr.ib(
|
textcolor: tuple = None
|
||||||
default=None, converter=attr.converters.optional(tuple))
|
cover_url: str = None
|
||||||
cover_url = attr.ib(default=None, converter=attr.converters.optional(str))
|
|
||||||
|
|
||||||
|
|
||||||
def chapter_html(
|
def chapter_html(
|
||||||
|
|
@ -187,8 +186,7 @@ def generate_epub(story, cover_options={}, image_options=None, output_filename=
|
||||||
'height', 'wrapat', 'bgcolor', 'textcolor', 'cover_url')
|
'height', 'wrapat', 'bgcolor', 'textcolor', 'cover_url')
|
||||||
cover_options = CoverOptions(
|
cover_options = CoverOptions(
|
||||||
**{k: v for k, v in cover_options.items() if k in valid_cover_options})
|
**{k: v for k, v in cover_options.items() if k in valid_cover_options})
|
||||||
cover_options = attr.asdict(
|
cover_options = asdict(cover_options, filter=lambda k, v: v is not None)
|
||||||
cover_options, filter=lambda k, v: v is not None, retain_collection_types=True)
|
|
||||||
|
|
||||||
if cover_options and "cover_url" in cover_options:
|
if cover_options and "cover_url" in cover_options:
|
||||||
image = make_cover_from_url(
|
image = make_cover_from_url(
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ import glob
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import uuid
|
import uuid
|
||||||
|
import datetime
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
import urllib
|
import urllib
|
||||||
import re
|
import re
|
||||||
import attr
|
from attrs import define, field, Factory
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
@ -21,32 +22,32 @@ def _default_uuid_string(self):
|
||||||
return str(uuid.UUID(int=rd.getrandbits(8*16), version=4))
|
return str(uuid.UUID(int=rd.getrandbits(8*16), version=4))
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@define
|
||||||
class Image:
|
class Image:
|
||||||
path = attr.ib()
|
path: str
|
||||||
contents = attr.ib()
|
contents: str
|
||||||
content_type = attr.ib()
|
content_type: str
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@define
|
||||||
class Chapter:
|
class Chapter:
|
||||||
title = attr.ib()
|
title: str
|
||||||
contents = attr.ib()
|
contents: str
|
||||||
date = attr.ib(default=False)
|
date: datetime.datetime = False
|
||||||
images = attr.ib(default=attr.Factory(list))
|
images: list = Factory(list)
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@define
|
||||||
class Section:
|
class Section:
|
||||||
title = attr.ib()
|
title: str
|
||||||
author = attr.ib()
|
author: str
|
||||||
url = attr.ib()
|
url: str
|
||||||
cover_url = attr.ib(default='')
|
cover_url: str = ''
|
||||||
id = attr.ib(default=attr.Factory(_default_uuid_string, takes_self=True), converter=str)
|
id: str = Factory(_default_uuid_string, takes_self=True)
|
||||||
contents = attr.ib(default=attr.Factory(list))
|
contents: list = Factory(list)
|
||||||
footnotes = attr.ib(default=attr.Factory(list))
|
footnotes: list = Factory(list)
|
||||||
tags = attr.ib(default=attr.Factory(list))
|
tags: list = Factory(list)
|
||||||
summary = attr.ib(default='')
|
summary: str = ''
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self.contents.__iter__()
|
return self.contents.__iter__()
|
||||||
|
|
@ -74,17 +75,17 @@ class Section:
|
||||||
yield chapter.date
|
yield chapter.date
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@define
|
||||||
class Site:
|
class Site:
|
||||||
"""A Site handles checking whether a URL might represent a site, and then
|
"""A Site handles checking whether a URL might represent a site, and then
|
||||||
extracting the content of a story from said site.
|
extracting the content of a story from said site.
|
||||||
"""
|
"""
|
||||||
session = attr.ib()
|
session: object = field()
|
||||||
footnotes = attr.ib(factory=list, init=False)
|
footnotes: list = field(factory=list, init=False)
|
||||||
options = attr.ib(default=attr.Factory(
|
options: dict = Factory(
|
||||||
lambda site: site.get_default_options(),
|
lambda site: site.get_default_options(),
|
||||||
True
|
takes_self=True
|
||||||
))
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def site_key(cls):
|
def site_key(cls):
|
||||||
|
|
@ -288,17 +289,17 @@ class Site:
|
||||||
return contents
|
return contents
|
||||||
|
|
||||||
|
|
||||||
@attr.s(hash=True)
|
@define(unsafe_hash=True, frozen=True)
|
||||||
class SiteSpecificOption:
|
class SiteSpecificOption:
|
||||||
"""Represents a site-specific option that can be configured.
|
"""Represents a site-specific option that can be configured.
|
||||||
|
|
||||||
Will be added to the CLI as a click.option -- many of these
|
Will be added to the CLI as a click.option -- many of these
|
||||||
fields correspond to click.option arguments."""
|
fields correspond to click.option arguments."""
|
||||||
name = attr.ib()
|
name: str
|
||||||
flag_pattern = attr.ib()
|
flag_pattern: str
|
||||||
type = attr.ib(default=None)
|
type: object = None
|
||||||
help = attr.ib(default=None)
|
default: bool = False
|
||||||
default = attr.ib(default=None)
|
help: str = None
|
||||||
|
|
||||||
def as_click_option(self):
|
def as_click_option(self):
|
||||||
return click.option(
|
return click.option(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import attr
|
from attrs import define
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
@ -24,26 +24,23 @@ Example JSON:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@define
|
||||||
class SiteDefinition:
|
class SiteDefinition:
|
||||||
url = attr.ib()
|
url: str
|
||||||
title = attr.ib()
|
title: str
|
||||||
author = attr.ib()
|
author: str
|
||||||
content_selector = attr.ib()
|
content_selector: str
|
||||||
# If present, find something within `content` to use a chapter title; if not found, the link text to it will be used
|
# If present, find something within `content` to use a chapter title; if not found, the link text to it will be used
|
||||||
content_title_selector = attr.ib(default=False)
|
content_title_selector: str = False
|
||||||
# If present, find a specific element in the `content` to be the chapter text
|
# If present, find a specific element in the `content` to be the chapter text
|
||||||
content_text_selector = attr.ib(default=False)
|
content_text_selector: str = False
|
||||||
# If present, it looks for chapters linked from `url`. If not, it assumes `url` points to a chapter.
|
# If present, it looks for chapters linked from `url`. If not, it assumes `url` points to a chapter.
|
||||||
chapter_selector = attr.ib(default=False)
|
chapter_selector: str = False
|
||||||
# If present, use to find a link to the next content page (only used if not using chapter_selector)
|
# If present, use to find a link to the next content page (only used if not using chapter_selector)
|
||||||
next_selector = attr.ib(default=False)
|
next_selector: str = False
|
||||||
# If present, use to filter out content that matches the selector
|
# If present, use to filter out content that matches the selector
|
||||||
filter_selector = attr.ib(default=False)
|
filter_selector: str = False
|
||||||
cover_url = attr.ib(default='')
|
cover_url: str = ''
|
||||||
|
|
||||||
# If present, use to also download the images and embed them into the epub.
|
|
||||||
image_selector = attr.ib(default=False)
|
|
||||||
|
|
||||||
|
|
||||||
@register
|
@register
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue