Skip to content

Commit

Permalink
feat: add anhmoe image hosting (#174)
Browse files Browse the repository at this point in the history
* feat: add `anhmoe` image hosting
* build(aur): update `PKGBUILD`
* chore: adjust `--notify` help msg
* chore: update some error messages
* test: add more tests
  • Loading branch information
DeadNews authored Feb 10, 2024
1 parent aabe90a commit c1f401b
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 9 deletions.
3 changes: 3 additions & 0 deletions PKGBUILD
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ depends=(
makedepends=(
"python-installer"
)
optdepends=(
"libnotify: sending desktop notifications"
)
license=("MIT")
arch=("any")
source=("https://files.pythonhosted.org/packages/py3/${_name::1}/${_name}/${_name}-$pkgver-py3-none-any.whl")
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ yay -S python-images-upload-cli

| host | key required | return example |
| :------------------------------------ | :----------: | :--------------------------------------------------- |
| [anhmoe](https://anh.moe/) | - | `https://cdn.anh.moe/c/{id}.png` |
| [beeimg](https://beeimg.com/) | - | `https://beeimg.com/images/{id}.png` |
| [catbox](https://catbox.moe/) | - | `https://files.catbox.moe/{id}` |
| [fastpic](https://fastpic.org/) | - | `https://i120.fastpic.org/big/2022/0730/d9/{id}.png` |
Expand Down Expand Up @@ -61,11 +62,11 @@ Usage: images-upload-cli [OPTIONS] IMAGES...
Upload images via APIs.

Options:
-h, --hosting [beeimg|catbox|fastpic|filecoffee|freeimage|gyazo|imageban|imagebin|imgbb|imgchest|imgur|lensdump|pixeldrain|pixhost|ptpimg|smms|sxcu|telegraph|thumbsnap|tixte|up2sha|uplio|uploadcare|vgy]
-h, --hosting [anhmoe|beeimg|catbox|fastpic|filecoffee|freeimage|gyazo|imageban|imagebin|imgbb|imgchest|imgur|lensdump|pixeldrain|pixhost|ptpimg|smms|sxcu|telegraph|thumbsnap|tixte|up2sha|uplio|uploadcare|vgy]
[default: imgur]
-b, --bbcode Generate BBCode tags.
-t, --thumbnail Create captioned thumbnails. Generate BBCode tags.
-n, --notify Send desktop notification using libnotify.
-n, --notify Send desktop notification on completion. Required libnotify.
-c, --clipboard / -C, --no-clipboard
Copy the result to the clipboard. Copies by default.
--env-file FILE The path to the environment file. Take precedence over the default config file.
Expand Down
7 changes: 6 additions & 1 deletion src/images_upload_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@
is_flag=True,
help="Create captioned thumbnails. Generate BBCode tags.",
)
@click.option("-n", "--notify", is_flag=True, help="Send desktop notification using libnotify.")
@click.option(
"-n",
"--notify",
is_flag=True,
help="Send desktop notification on completion. Required libnotify.",
)
@click.option(
"-c/-C",
"--clipboard/--no-clipboard",
Expand Down
31 changes: 29 additions & 2 deletions src/images_upload_cli/upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,32 @@
from images_upload_cli.util import get_env, get_img_ext


async def anhmoe_upload(client: AsyncClient, img: bytes) -> str:
"""
Uploads an image to the `anh.moe`.
Args:
client (httpx.AsyncClient): An instance of AsyncClient.
img (bytes): A byte string representing an image.
Returns:
str: The URL of the uploaded image.
Raises:
httpx.HTTPStatusError: If the response status code is not successful.
"""
key = "anh.moe_public_api"

response = await client.post(
url="https://anh.moe/api/1/upload",
data={"key": key},
files={"source": img},
)
response.raise_for_status()

return response.json()["image"]["url"]


async def beeimg_upload(client: AsyncClient, img: bytes) -> str:
"""
Uploads an image to the `beeimg.com`.
Expand Down Expand Up @@ -89,7 +115,7 @@ async def fastpic_upload(client: AsyncClient, img: bytes) -> str:

match = search(r"<imagepath>(.+?)</imagepath>", response.text)
if match is None:
msg = f"Image link not found in response:\n\n{response.text}"
msg = f"Image link not found in '{response.url}' response:\n\n{response.text}"
raise HTTPError(msg)

return match[1].strip()
Expand Down Expand Up @@ -218,7 +244,7 @@ async def imagebin_upload(client: AsyncClient, img: bytes) -> str:

match = search(r"url:(.+?)$", response.text)
if match is None:
msg = f"Image link not found in response:\n\n{response.text}"
msg = f"Image link not found in '{response.url}' response:\n\n{response.text}"
raise HTTPError(msg)

return match[1].strip()
Expand Down Expand Up @@ -657,6 +683,7 @@ async def vgy_upload(client: AsyncClient, img: bytes) -> str:


UPLOAD: dict[str, Callable] = {
"anhmoe": anhmoe_upload,
"beeimg": beeimg_upload,
"catbox": catbox_upload,
"fastpic": fastpic_upload,
Expand Down
3 changes: 2 additions & 1 deletion src/images_upload_cli/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ def make_thumbnail(


def notify_send(text_to_print: str) -> None:
"""Send desktop notifications via libnotify.
"""
Send desktop notifications via libnotify.
Args:
text_to_print: The text to be displayed in the desktop notification.
Expand Down
3 changes: 2 additions & 1 deletion tests/mock.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Mock data."""


anhmoe = """{"status_code":200,"success":{"message":"image uploaded","code":200},"image":{"name":"Lyw3o","extension":"png","size":91,"width":100,"height":100,"date":"2024-01-01 00:00:00","date_gmt":"2024-01-01 00:00:00","title":null,"description":null,"nsfw":0,"storage_mode":"direct","md5":"45927ce6c3a6ba2e48a260328dc57d3d","source_md5":null,"original_filename":"upload","original_exifdata":null,"views":0,"category_id":null,"chain":5,"thumb_size":301,"medium_size":0,"expiration_date_gmt":null,"likes":0,"is_animated":0,"is_approved":1,"is_360":0,"file":{"resource":{"type":"url"}},"id_encoded":"Lyw3o","filename":"Lyw3o.png","mime":"image\\/png","url":"https:\\/\\/cdn.anh.moe\\/c\\/Lyw3o.png","ratio":1,"size_formatted":"91 B","url_viewer":"https:\\/\\/anh.moe\\/view\\/Lyw3o","path_viewer":"\\/view\\/Lyw3o","url_short":"https:\\/\\/anh.moe\\/view\\/Lyw3o","image":{"filename":"Lyw3o.png","name":"Lyw3o","mime":"image\\/png","extension":"png","url":"https:\\/\\/cdn.anh.moe\\/c\\/Lyw3o.png","size":91},"thumb":{"filename":"Lyw3o.th.png","name":"Lyw3o.th","mime":"image\\/png","extension":"png","url":"https:\\/\\/cdn.anh.moe\\/c\\/Lyw3o.th.png","size":301},"display_url":"https:\\/\\/cdn.anh.moe\\/c\\/Lyw3o.png","display_width":100,"display_height":100,"views_label":"l\u01b0\u1ee3t xem","likes_label":"th\u00edch","how_long_ago":"m\u1edbi \u0111\u00e2y","date_fixed_peer":"2024-01-01 00:00:00","title_truncated":"","title_truncated_html":"","is_use_loader":false,"delete_url":"https:\\/\\/anh.moe\\/view\\/Lyw3o\\/delete\\/e2ace748e2e1f9ca82ad47ee194dc6fdd0b6c440a55e9419"},"status_txt":"OK"}"""
beeimg = """{"files":{"name":"x8078479702","size":"","url":"\\/\\/beeimg.com\\/images\\/x80784797021.png","thumbnail_url":"\\/\\/i.beeimg.com\\/images\\/thumb\\/x80784797021-xs.png","view_url":"\\/\\/beeimg.com\\/view\\/x8078479702\\/","delete_url":"N\\/A","delete_type":"DELETE","status":"Duplicate","code":"200"}}"""
catbox = """https://files.catbox.moe/4yt1tj"""
fastpic = """<?xml version="1.0" encoding="UTF-8"?>
Expand Down Expand Up @@ -50,6 +50,7 @@


RESPONSE: dict[str, tuple[str, str]] = {
"anhmoe": (anhmoe, "https://cdn.anh.moe/c/Lyw3o.png"),
"beeimg": (beeimg, "https://beeimg.com/images/x80784797021.png"),
"catbox": (catbox, "https://files.catbox.moe/4yt1tj"),
"fastpic": (
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def test_cli_error(runner: CliRunner) -> None:
Args:
runner (CliRunner): An instance of CliRunner used to invoke the cli function.
"""
args = ["tests/data/nonexistent.png", "-C", "-h", "uploadcare"]
args = ["tests/data/nonexistent", "-C", "-h", "nonexistent"]
assert runner.invoke(cli=cli, args=args).exit_code == 2


Expand Down
27 changes: 26 additions & 1 deletion tests/test_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import pytest
from dotenv import load_dotenv
from httpx import AsyncClient
from httpx import AsyncClient, HTTPError
from images_upload_cli.cli import upload_images
from images_upload_cli.upload import UPLOAD
from pytest_httpx import HTTPXMock
Expand Down Expand Up @@ -51,6 +51,31 @@ async def test_upload_funcs(
assert link == mock_link


@pytest.mark.asyncio()
@pytest.mark.parametrize("hosting", ["fastpic", "imagebin"])
async def test_upload_funcs_error(httpx_mock: HTTPXMock, hosting: str, img: bytes) -> None:
"""
Test the error handling of image upload functionality for specific hosting services.
Args:
httpx_mock (HTTPXMock): An instance of the HTTPXMock class used for mocking HTTP responses.
hosting (str): A string representing the hosting service to test.
img (bytes): Bytes of the image to be uploaded.
Raises:
HTTPError: If an HTTP error occurs during the image upload process.
"""
# Mock the response.
httpx_mock.add_response(text="Random non relevant text.")

# Upload the image.
async with AsyncClient() as client:
upload_func = UPLOAD[hosting]
# Error handling.
with pytest.raises(HTTPError):
await upload_func(client, img)


@pytest.mark.asyncio()
@pytest.mark.parametrize(
"thumbnail",
Expand Down

0 comments on commit c1f401b

Please sign in to comment.