hikari_uploader/hikari/hikari.py

173 lines
4.7 KiB
Python
Raw Permalink Normal View History

2022-02-27 04:35:40 +00:00
#!/usr/bin/env python3
2024-01-01 22:22:31 +00:00
# hikari_uploader
# Copyright (C) 2024 odrling
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
2022-02-27 04:35:40 +00:00
import hashlib
import os
2022-10-06 06:19:50 +00:00
from dataclasses import dataclass
from functools import cache
from io import BufferedIOBase, BufferedReader
2022-02-27 04:35:40 +00:00
from pathlib import Path
from typing import Literal, Optional
2022-02-27 04:35:40 +00:00
import httpx
2022-02-27 04:35:40 +00:00
import pyperclip
import typer
2022-10-06 07:39:36 +00:00
from rich.console import Console
2024-03-21 23:34:36 +00:00
from rich.progress import (
BarColumn,
DownloadColumn,
Progress,
TextColumn,
TimeRemainingColumn,
TransferSpeedColumn,
)
2022-10-06 04:41:26 +00:00
HIKARI_BASE = "https://hikari.butaishoujo.moe"
HIKARI_URL = f"{HIKARI_BASE}/upload"
2022-02-27 04:35:40 +00:00
2022-10-06 07:39:36 +00:00
console = Console()
@cache
def get_client() -> httpx.Client:
return httpx.Client()
2022-10-06 07:39:36 +00:00
@dataclass
class UploadResponse:
status: Literal["exists"] | Literal["uploaded"]
url: str
2022-02-27 04:35:40 +00:00
2022-10-06 05:31:38 +00:00
def check_hash(filename: str, file: Path):
h = hashlib.sha256()
with file.open('br') as f:
while buf := f.read(1024**2):
h.update(buf)
h.hexdigest()[:8]
url = f"{HIKARI_URL}/{h.hexdigest()[:8]}"
with file.open('br') as f:
files = {'file': (filename, f.read(2048))}
req = get_client().post(url, files=files)
if req.status_code == 404:
return
2022-10-06 05:31:38 +00:00
req.raise_for_status()
return UploadResponse(**req.json())
2022-10-06 05:31:38 +00:00
2022-10-06 07:39:36 +00:00
def output(copy: bool, resp: UploadResponse):
2022-10-06 05:31:38 +00:00
if copy:
2022-10-06 07:39:36 +00:00
pyperclip.copy(resp.url)
action = "Copied"
elif resp.status == "exists":
action = "Found"
elif resp.status == "uploaded":
action = "Uploaded"
2022-10-06 05:31:38 +00:00
else:
2022-10-06 07:39:36 +00:00
action = ""
console.print(action, resp.url, style="bold green")
2022-10-06 05:31:38 +00:00
2022-10-06 06:19:50 +00:00
@dataclass
class ProgressMonitor:
2022-10-06 06:30:21 +00:00
filename: str
file: BufferedReader
2022-10-06 06:19:50 +00:00
def __post_init__(self) -> None:
self.size = get_file_size(self.file)
self._file_read = self.file.read
self.file.read = self.read # type: ignore
2022-10-06 06:30:21 +00:00
self.progress = Progress(
TextColumn("[bold blue]{task.fields[filename]}", justify="right"),
BarColumn(bar_width=None),
"[progress.percentage]{task.percentage:>3.1f}%",
"",
DownloadColumn(),
"",
TransferSpeedColumn(),
"",
TimeRemainingColumn(),
)
self.task = self.progress.add_task("[green]Uploading",
filename=self.filename,
total=self.size)
2022-10-06 06:19:50 +00:00
self.progress.start()
def read(self, size: Optional[int] = None, /) -> bytes:
data = self._file_read(size)
self.progress.update(self.task, completed=self.file.tell())
return data
def get_file_size(fd: BufferedIOBase) -> int:
fd.seek(0, os.SEEK_END)
size: int = fd.tell()
fd.seek(0)
return size
2022-10-06 06:19:50 +00:00
2022-02-27 04:35:40 +00:00
def upload(file: Path,
obstruct: bool = typer.Option(
False, "--obstruct", "-o",
help="Obstruct filename (by hashing)"
),
filename: str = typer.Option(
None, "--name", "-n",
help="Rename the uploaded file."
),
copy: bool = typer.Option(
False, "--copy", "-c",
help="Copy file URL to clipboard."
2022-10-06 04:41:26 +00:00
),
hash: bool = typer.Option(
2022-10-06 05:31:38 +00:00
True, "--hash/--no-hash", "-h/-H",
2024-03-21 23:34:36 +00:00
help="Check file hash before uploading."
2022-02-27 04:35:40 +00:00
)):
with get_client():
if copy:
pyperclip.copy("")
2022-02-27 04:35:40 +00:00
if obstruct:
filename = f"{hashlib.sha256(file.name.encode()).hexdigest()}{file.suffix}"
elif filename is None:
filename = file.name
2022-10-06 04:41:26 +00:00
if hash and (resp := check_hash(filename, file)):
return output(copy, resp)
2022-10-06 04:41:26 +00:00
with file.open('br') as f:
monitor = ProgressMonitor(filename, f)
files = {'file': (filename, f)}
2022-02-27 04:35:40 +00:00
2024-04-29 19:23:02 +00:00
timeout = httpx.Timeout(5.0, read=None)
req = get_client().post(HIKARI_URL, files=files, timeout=timeout)
monitor.progress.stop()
2022-10-06 06:19:50 +00:00
req.raise_for_status()
2022-10-06 07:41:42 +00:00
data = UploadResponse(**req.json())
2022-10-06 06:19:50 +00:00
output(copy, data)
2022-02-27 04:35:40 +00:00
def main():
typer.run(upload)