chore: import kevin
This commit is contained in:
3
kevin/.gitignore
vendored
Normal file
3
kevin/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.direnv
|
||||||
|
.envrc
|
||||||
|
.history
|
||||||
3
kevin/epub.css
Normal file
3
kevin/epub.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
body{margin:40px auto;max-width:650px;line-height:1.6;font-size:18px;color:#444;padding:0}
|
||||||
|
a{color:inherit;text-decoration:none}
|
||||||
|
a:hover{text-decoration:underline}
|
||||||
61
kevin/keinverlag
Executable file
61
kevin/keinverlag
Executable file
@@ -0,0 +1,61 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
alias to_utf8='iconv -f latin1 -t utf8'
|
||||||
|
alias curl_GET='curl -X GET -s -G'
|
||||||
|
|
||||||
|
BASE_URL=https://www.keinverlag.de
|
||||||
|
|
||||||
|
kv_author_id () {
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo Please call kv_author_id with an author name. >/dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
author_name=$1
|
||||||
|
|
||||||
|
curl_GET "$BASE_URL/$author_name.kv" \
|
||||||
|
| to_utf8 \
|
||||||
|
| sed -n 's/.*autor=\([0-9]\+\).*/\1/p' \
|
||||||
|
| head -1
|
||||||
|
}
|
||||||
|
|
||||||
|
kv_text () {
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo Please call kv_text with a text ID. >/dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
text_id=$1
|
||||||
|
|
||||||
|
curl_GET "$BASE_URL/$text_id.text" \
|
||||||
|
| to_utf8 \
|
||||||
|
| sed -n '/<h1>/,/<!-- Kommentarbox -->/p' \
|
||||||
|
| sed 's/<h3>.\+<\/h3>//g' \
|
||||||
|
| pandoc -f html -t plain
|
||||||
|
}
|
||||||
|
|
||||||
|
kv_author_texts () {
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo Please call kv_author_texts with an author ID. >/dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
author_id=$1
|
||||||
|
|
||||||
|
curl_GET "$BASE_URL/autorentexte.php" -d sortby=tnr -d start=0 -d limit=10000 -d autor="$author_id" \
|
||||||
|
| to_utf8 \
|
||||||
|
| sed -n 's/.*<li><a href="\([0-9]\+\)\.text">.*/\1/p'
|
||||||
|
}
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
text)
|
||||||
|
shift
|
||||||
|
kv_text "$@";;
|
||||||
|
author)
|
||||||
|
shift
|
||||||
|
for text_id in $(kv_author_texts "$(kv_author_id "$@")" | uniq); do
|
||||||
|
kv_text "$text_id"
|
||||||
|
done ;;
|
||||||
|
*)
|
||||||
|
echo >/dev/stderr "Usage: $0 text|author ID"
|
||||||
|
esac
|
||||||
99
kevin/kevin.py
Executable file
99
kevin/kevin.py
Executable file
@@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from typing import List
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
def soup_from(response):
|
||||||
|
return BeautifulSoup(response.text, "lxml")
|
||||||
|
|
||||||
|
|
||||||
|
class Author:
|
||||||
|
def __init__(self, author_id: int) -> None:
|
||||||
|
response = requests.get(
|
||||||
|
"https://www.keinverlag.de/autorentexte.php",
|
||||||
|
params={"start": 0, "limit": 10000, "sortby": "tnr", "author": author_id},
|
||||||
|
)
|
||||||
|
soup = soup_from(response)
|
||||||
|
self.texts = [] # type: List[Text]
|
||||||
|
for text in soup.select('ul.textliste > li > a[href$=".text"]'):
|
||||||
|
# strip off the last five characters (".text")
|
||||||
|
text_id = int(text["href"][:-5])
|
||||||
|
try:
|
||||||
|
self.texts.append(Text(text_id))
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
def markdown(self, *, with_type: bool = False) -> str:
|
||||||
|
name = self.texts[0].author
|
||||||
|
|
||||||
|
def __gen():
|
||||||
|
yield "% {}".format(name)
|
||||||
|
for text in self.texts:
|
||||||
|
yield "\n\n* * *\n\n"
|
||||||
|
yield text.markdown(with_author=False, with_type=with_type)
|
||||||
|
|
||||||
|
return "\n".join(__gen())
|
||||||
|
|
||||||
|
|
||||||
|
class Text:
|
||||||
|
def __init__(self, text_id: int) -> None:
|
||||||
|
normalization = {
|
||||||
|
132: '"',
|
||||||
|
147: '"',
|
||||||
|
0x96: "--",
|
||||||
|
0x91: "'",
|
||||||
|
0x92: "'",
|
||||||
|
0x97: "---",
|
||||||
|
}
|
||||||
|
text_url = "https://www.keinverlag.de/{}.text".format(text_id)
|
||||||
|
soup = soup_from(text_url)
|
||||||
|
try:
|
||||||
|
self.title = soup.select("h1 > span")[0].text.translate(normalization)
|
||||||
|
self.content = BeautifulSoup(
|
||||||
|
re.sub(
|
||||||
|
r'<span style="font-style: italic;">(([\n\r]|.)*?)</span>',
|
||||||
|
r"_\1_",
|
||||||
|
str(soup.select(".fliesstext > span")[0]),
|
||||||
|
),
|
||||||
|
"lxml",
|
||||||
|
).text.translate(normalization)
|
||||||
|
self.author = soup.select("h3 > a")[2].text
|
||||||
|
self.type = soup.select("h1 ~ h3")[0].text
|
||||||
|
except IndexError:
|
||||||
|
raise ValueError("Text {} not available.".format(text_id))
|
||||||
|
|
||||||
|
def markdown(self, *, with_author: bool = True, with_type: bool = False) -> str:
|
||||||
|
return "#### {maybe_author}{title}{maybe_type}\n\n{content}".format(
|
||||||
|
title=self.title,
|
||||||
|
maybe_author=self.author + ": " if with_author else "",
|
||||||
|
maybe_type=" (" + self.type + ")" if with_type else "",
|
||||||
|
content="\n".join(
|
||||||
|
line + "\\" if line else "" for line in self.content.splitlines()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = ArgumentParser()
|
||||||
|
parser.add_argument("--type", help="Include text type", action="store_true")
|
||||||
|
subparsers = parser.add_subparsers()
|
||||||
|
|
||||||
|
handle_text = subparsers.add_parser("text", help="Handle one text")
|
||||||
|
handle_text.add_argument("tid", help="KeinVerlag text id", type=int)
|
||||||
|
handle_text.set_defaults(
|
||||||
|
func=lambda a: print(Text(a.tid).markdown(with_type=a.type))
|
||||||
|
)
|
||||||
|
|
||||||
|
handle_author = subparsers.add_parser(
|
||||||
|
"author", help="Handle all texts by an author"
|
||||||
|
)
|
||||||
|
handle_author.add_argument("aid", help="KeinVerlag author id", type=str)
|
||||||
|
handle_author.set_defaults(
|
||||||
|
func=lambda a: print(Author(a.aid).markdown(with_type=a.type))
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
args.func(args)
|
||||||
2
kevin/kevin.sh
Executable file
2
kevin/kevin.sh
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
python3 kevin.py author "$1" | pandoc -f markdown+smart --table-of-contents --toc-depth=6 --standalone --css=epub.css -o "$2"
|
||||||
10
kevin/shell.nix
Normal file
10
kevin/shell.nix
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
pandoc
|
||||||
|
python3Packages.beautifulsoup4
|
||||||
|
python3Packages.requests
|
||||||
|
python3Packages.lxml
|
||||||
|
];
|
||||||
|
shellHook = "export HISTFILE=${toString ./.history}";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user