commit 9db1272c8b9b6d0ff774c77bc2eacc2d7d2c4f9d Author: scoliono Date: Wed Feb 23 01:12:08 2022 -0800 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a5e323 --- /dev/null +++ b/.gitignore @@ -0,0 +1,153 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + diff --git a/booru/__init__.py b/booru/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/booru/__main__.py b/booru/__main__.py new file mode 100644 index 0000000..d7cae0d --- /dev/null +++ b/booru/__main__.py @@ -0,0 +1,75 @@ +from io import BytesIO +import PIL.Image, PIL.ImageTk +import requests +from threading import Thread +from tkinter import * +from tkinter import ttk +from .parser import FembooruClient + +client = FembooruClient() +page = 1 + +root = Tk() +root.geometry('640x480') +root.title('Fembooru Browser') +root.resizable(0, 0) + +root.columnconfigure(0, weight=1) +root.rowconfigure(0, weight=1) +root.rowconfigure(1, weight=8) +root.rowconfigure(2, weight=1) + +tagline = StringVar() +ttk.Label(root, textvariable=tagline).grid(column=0, row=0, sticky=W) + +gallery = ttk.Frame(root) +labels = [] +captions = [] +for y in range(3): + gallery.columnconfigure(y, weight=1) + for x in range(3): + gallery.rowconfigure(x, weight=1) + captions.append(StringVar()) + i = x + y*3 + labels.append(ttk.Label(gallery, textvariable=captions[i])) + labels[i].grid(column=x, row=y) +gallery.grid(column=0, row=1, sticky=NS+EW) + +nav = ttk.Frame(root) +nav.columnconfigure(0, weight=1) +nav.columnconfigure(1, weight=1) +progress = ttk.Progressbar(nav, orient="horizontal", mode="indeterminate") +progress.grid(column=0, row=0) +progress_label = ttk.Label(nav, text="Connecting...") +progress_label.grid(column=1, row=0) +nav.grid(column=0, row=2, sticky=EW) +progress.start() + +def nav_layout(): + progress.destroy() + progress_label.destroy() + nav.columnconfigure(2, weight=1) + nav.columnconfigure(3, weight=1) + ttk.Button(nav, text="<<").grid(column=0, row=0) + ttk.Button(nav, text="<").grid(column=1, row=0) + ttk.Button(nav, text=">").grid(column=2, row=0) + ttk.Button(nav, text=">>").grid(column=3, row=0) + +def refresh(): + tagline.set(client.get_random_tagline()) + images = client.get_images(page) + for i in range(len(captions)): + captions[i].set(images[i]["label"].replace(" // ", "\n")) + for i in range(len(captions)): + res = requests.get(images[i]["thumb_url"]) + image = PIL.Image.open(BytesIO(res.content)) + photo_img = PIL.ImageTk.PhotoImage(image) + labels[i].configure(image=photo_img) + labels[i].image = photo_img + + nav_layout() + +thread = Thread(target=refresh) +thread.start() +root.mainloop() +thread.join() diff --git a/booru/parser.py b/booru/parser.py new file mode 100644 index 0000000..7b72042 --- /dev/null +++ b/booru/parser.py @@ -0,0 +1,26 @@ +from bs4 import BeautifulSoup +import requests +from typing import List + + +class FembooruClient: + def __init__(self): + self.BASE_URL = "https://fembooru.jp" + + def parse_page(self, page: int) -> str: + res = requests.get(f"{self.BASE_URL}/post/list/{page}") + return BeautifulSoup(res.text, "html.parser") + + def get_random_tagline(self) -> str: + soup = self.parse_page(1) + tip = soup.find(id="tips") + return tip.text + + def get_images(self, page: int) -> List[dict]: + soup = self.parse_page(page) + imgs = soup.find(id="image-list").find_all("img") + imgs_map = map(lambda i: { + "label": i.get("alt"), + "thumb_url": self.BASE_URL + i.get("src") + }, imgs) + return list(imgs_map) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7f98c56 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +beautifulsoup4==4.10.0 +certifi==2021.10.8 +charset-normalizer==2.0.12 +idna==3.3 +Pillow==9.0.1 +requests==2.27.1 +soupsieve==2.3.1 +urllib3==1.26.8