from dataclasses import dataclass from PIL import Image, ImageDraw from lxml.html import parse from io import StringIO from lxml import etree import requests import logging import urllib import random import utils import time import json import cv2 import os with open("config.json", "r") as f: CONFIG = json.load(f) logging.basicConfig( format = "%(levelname)s\t[%(asctime)s]\t%(message)s", level = logging.INFO, handlers=[ logging.FileHandler(CONFIG["logpath"]), logging.StreamHandler() ]) # all of these tags are added to all queries. Preceded with '-' to blacklist base_tags = CONFIG["base_tags"] # one of these will be added search_tags = CONFIG["search_tags"] def get_random_searchtag(): return [random.choice(search_tags)] @dataclass class SafebooruImage: id: int tags: list source: str imurl: str def get_id_from_url(url): return int(urllib.parse.parse_qs(url)["id"][0]) def get_image(tags): search_url = "https://safebooru.org/index.php?page=post&s=list&tags=%s&pid=%i" % ("+".join(base_tags+tags), (random.randint(1, get_num_pages(tags))-1)*5*8) tree = etree.parse(StringIO(requests.get(search_url).text), etree.HTMLParser()) elements = [e for e in tree.xpath("/html/body/div[6]/div/div[2]/div[1]")[0].iter(tag = "a")] try: element = random.choice(elements) except IndexError: raise ConnectionError("Couldn't find any images") simg = SafebooruImage( id = get_id_from_url(element.get("href")), tags = element.find("img").get("alt").split(), source = get_source("https://safebooru.org/" + element.get("href")), imurl = get_imurl("https://safebooru.org/" + element.get("href")) ) if simg.source is None: print("https://safebooru.org/" + element.get("href")) return simg def get_source(url): tree = etree.parse(StringIO(requests.get(url).text), etree.HTMLParser()) for element in tree.xpath('//*[@id="stats"]')[0].iter("li"): if element.text.startswith("Source: h"): return element.text[8:] elif element.text.startswith("Source:"): for child in element.iter(): if child.get("href") is not None: return child.get("href") raise ConnectionError("Couldn't find source image for id %i" % get_id_from_url(url)) def get_imurl(url): tree = etree.parse(StringIO(requests.get(url).text), etree.HTMLParser()) return tree.xpath('//*[@id="image"]')[0].get("src") def get_num_pages(tags): search_url = "https://safebooru.org/index.php?page=post&s=list&tags=%s" % "+".join(base_tags+tags) html = requests.get(search_url).text tree = etree.parse(StringIO(html), etree.HTMLParser()) try: page_element = tree.xpath("/html/body/div[6]/div/div[2]/div[2]/div/a[12]")[0] except IndexError: return 1 else: return int(int(urllib.parse.parse_qs(page_element.get("href"))["pid"][0]) / (5*8)) def check_pixiv_404(url): text = requests.get(url).text return text[text.find("") + 7 : text.find("")] == "イラストコミュニケーションサービス[pixiv]" def fix_source_url(url): parsed = urllib.parse.urlparse(url) if parsed.netloc == "www.pixiv.net": return "https://www.pixiv.net/en/artworks/" + urllib.parse.parse_qs(parsed.query)["illust_id"][0] elif parsed.netloc in ["bishie.booru.org", "www.secchan.net"]: return ConnectionError("Couldn't get source") elif "pximg.net" in parsed.netloc or "pixiv.net" in parsed.netloc: return "https://www.pixiv.net/en/artworks/" + parsed.path.split("/")[-1][:8] elif parsed.netloc == "twitter.com": return url.replace("twitter.com", "nitter.net") elif parsed.netloc == "x.com": return url.replace("x.com", "nitter.net") return url def append_blacklisted(id_): with open(CONFIG["blacklist"], "a") as f: f.write(str(id_) + "\n") def id_is_blacklisted(id_): if not os.path.exists(CONFIG["blacklist"]): return False with open(CONFIG["blacklist"], "r") as f: return str(id_) in f.read().splitlines() @dataclass class DownloadedImage: imurl: str def __enter__(self): self.filename = urllib.parse.urlparse(self.imurl).path.split("/")[-1] req = urllib.request.Request(self.imurl, headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.50.2 (KHTML, like Gecko) Version/5.0.6 Safari/533.22.3'}) mediaContent = urllib.request.urlopen(req).read() with open(self.filename, "wb") as f: f.write(mediaContent) return self.filename def __exit__(self, type, value, traceback): os.remove(self.filename) def main(draw_faces = False): try: simg = get_image(get_random_searchtag()) except ConnectionError: logging.warning("Retried since couldn't get source...") return main() if id_is_blacklisted(simg.id): logging.info("Retried, already posted image...") return main() if check_pixiv_404(fix_source_url(simg.source)): logging.warning("Skipping since pixiv linked 404'd") return main() append_blacklisted(simg.id) with DownloadedImage(simg.imurl) as impath: img = cv2.imread(impath) cascade = cascade = cv2.CascadeClassifier(os.path.join("lbpcascade_animeface", "lbpcascade_animeface.xml")) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) gray = cv2.equalizeHist(gray) faces = cascade.detectMultiScale(gray, # detector options scaleFactor = 1.1, minNeighbors = 5, minSize = (24, 24)) if draw_faces: for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2) logging.info("Found image %i faces, id: %i" % (len(faces), simg.id)) pilimg = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) text = utils.get_quote(CONFIG["texts"]) logging.info(text) font = utils.set_font(pilimg, text) draw = ImageDraw.Draw(pilimg) lines = utils.messages_multiline(text, font, pilimg) colours = utils.get_colors(impath) (x, y, faces) = utils.randomize_location(pilimg, lines, font, faces) for line in lines: height = font.getsize(line[1])[1] utils.draw_with_border(x, y, line, colours[0], colours[1], font, draw) y = y + height pilimg.save("img.png") return "img.png", fix_source_url(simg.source), text if __name__ == "__main__": print(main())