Update app.py
Browse files
app.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
from asyncio import create_subprocess_shell, gather, sleep
|
|
|
|
| 2 |
from pathlib import Path
|
| 3 |
from random import choice
|
|
|
|
| 4 |
from subprocess import CalledProcessError, PIPE
|
| 5 |
from typing import Any, List
|
| 6 |
from uuid import uuid4
|
|
@@ -11,6 +13,11 @@ from httpx import AsyncClient, HTTPStatusError, RequestError
|
|
| 11 |
from pydantic import BaseModel, HttpUrl
|
| 12 |
from uvicorn import run as uvicorn_run
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
oxipng_bin = Path(__file__).parent / 'oxipng'
|
| 15 |
if not oxipng_bin.stat().st_mode & 0o111:
|
| 16 |
oxipng_bin.chmod(0o755)
|
|
@@ -25,12 +32,14 @@ tokens = [
|
|
| 25 |
]
|
| 26 |
|
| 27 |
|
| 28 |
-
async def download_png(url: str, client: AsyncClient, retries: int = 5) -> Path:
|
|
|
|
| 29 |
for attempt in range(retries):
|
| 30 |
try:
|
| 31 |
response = await client.get(url, timeout=30.0)
|
| 32 |
response.raise_for_status()
|
| 33 |
-
file_path = Path(f'{uuid4()}.png'
|
|
|
|
| 34 |
file_path.write_bytes(response.content)
|
| 35 |
return file_path
|
| 36 |
except (HTTPStatusError, RequestError) as e:
|
|
@@ -42,13 +51,15 @@ async def download_png(url: str, client: AsyncClient, retries: int = 5) -> Path:
|
|
| 42 |
|
| 43 |
async def download_pngs(urls: str | list[str]) -> list[Any]:
|
| 44 |
urls = [urls] if isinstance(urls, str) else urls
|
|
|
|
| 45 |
async with AsyncClient() as client:
|
| 46 |
-
tasks = [download_png(url, client) for url in urls]
|
| 47 |
return list(await gather(*tasks))
|
| 48 |
|
| 49 |
|
| 50 |
async def optimize_png(image_path: Path, retries: int = 3) -> None:
|
| 51 |
command = f'{oxipng_bin.resolve()} --opt 2 --strip safe --out {image_path} {image_path}'
|
|
|
|
| 52 |
for attempt in range(retries):
|
| 53 |
try:
|
| 54 |
process = await create_subprocess_shell(command, stdout=PIPE, stderr=PIPE)
|
|
@@ -58,6 +69,7 @@ async def optimize_png(image_path: Path, retries: int = 3) -> None:
|
|
| 58 |
else:
|
| 59 |
raise CalledProcessError(process.returncode, command, output=stdout, stderr=stderr)
|
| 60 |
except CalledProcessError as e:
|
|
|
|
| 61 |
if attempt < retries - 1:
|
| 62 |
await sleep(2 ** attempt)
|
| 63 |
else:
|
|
@@ -66,6 +78,7 @@ async def optimize_png(image_path: Path, retries: int = 3) -> None:
|
|
| 66 |
|
| 67 |
async def optimize_pngs(image_paths: list[str | Path] | str | Path) -> None:
|
| 68 |
image_paths = [Path(image_file) for image_file in ([image_paths] if not isinstance(image_paths, list) else image_paths)]
|
|
|
|
| 69 |
tasks = [optimize_png(image_path) for image_path in image_paths]
|
| 70 |
await gather(*tasks)
|
| 71 |
|
|
@@ -119,6 +132,7 @@ async def upload_image(file_path: Path | str) -> str | None:
|
|
| 119 |
|
| 120 |
async def optimize_and_upload(images_urls: list[str] | str) -> list[str]:
|
| 121 |
images_urls = [images_urls] if isinstance(images_urls, str) else images_urls
|
|
|
|
| 122 |
images_paths = await download_pngs(images_urls)
|
| 123 |
await optimize_pngs(images_paths)
|
| 124 |
new_images_urls = []
|
|
@@ -126,7 +140,16 @@ async def optimize_and_upload(images_urls: list[str] | str) -> list[str]:
|
|
| 126 |
new_url = await upload_image(image_path)
|
| 127 |
if new_url:
|
| 128 |
new_images_urls.append(new_url)
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
return new_images_urls
|
| 131 |
|
| 132 |
|
|
|
|
| 1 |
from asyncio import create_subprocess_shell, gather, sleep
|
| 2 |
+
from logging import ERROR, INFO, basicConfig, getLogger
|
| 3 |
from pathlib import Path
|
| 4 |
from random import choice
|
| 5 |
+
from shutil import rmtree
|
| 6 |
from subprocess import CalledProcessError, PIPE
|
| 7 |
from typing import Any, List
|
| 8 |
from uuid import uuid4
|
|
|
|
| 13 |
from pydantic import BaseModel, HttpUrl
|
| 14 |
from uvicorn import run as uvicorn_run
|
| 15 |
|
| 16 |
+
need_logging = True
|
| 17 |
+
|
| 18 |
+
basicConfig(level = INFO if need_logging else ERROR)
|
| 19 |
+
logger = getLogger(__name__)
|
| 20 |
+
|
| 21 |
oxipng_bin = Path(__file__).parent / 'oxipng'
|
| 22 |
if not oxipng_bin.stat().st_mode & 0o111:
|
| 23 |
oxipng_bin.chmod(0o755)
|
|
|
|
| 32 |
]
|
| 33 |
|
| 34 |
|
| 35 |
+
async def download_png(url: str, folder: str, client: AsyncClient, retries: int = 5) -> Path:
|
| 36 |
+
logger.info(f'загрузка изображения: {url}')
|
| 37 |
for attempt in range(retries):
|
| 38 |
try:
|
| 39 |
response = await client.get(url, timeout=30.0)
|
| 40 |
response.raise_for_status()
|
| 41 |
+
file_path = Path(__file__).parent / folder / f'{uuid4()}.png'
|
| 42 |
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
| 43 |
file_path.write_bytes(response.content)
|
| 44 |
return file_path
|
| 45 |
except (HTTPStatusError, RequestError) as e:
|
|
|
|
| 51 |
|
| 52 |
async def download_pngs(urls: str | list[str]) -> list[Any]:
|
| 53 |
urls = [urls] if isinstance(urls, str) else urls
|
| 54 |
+
logger.info(f'скачивается список список из {len(urls)}: {urls}')
|
| 55 |
async with AsyncClient() as client:
|
| 56 |
+
tasks = [download_png(url, str(uuid4()), client) for url in urls]
|
| 57 |
return list(await gather(*tasks))
|
| 58 |
|
| 59 |
|
| 60 |
async def optimize_png(image_path: Path, retries: int = 3) -> None:
|
| 61 |
command = f'{oxipng_bin.resolve()} --opt 2 --strip safe --out {image_path} {image_path}'
|
| 62 |
+
logger.info(f'оптимизация картинки {image_path}')
|
| 63 |
for attempt in range(retries):
|
| 64 |
try:
|
| 65 |
process = await create_subprocess_shell(command, stdout=PIPE, stderr=PIPE)
|
|
|
|
| 69 |
else:
|
| 70 |
raise CalledProcessError(process.returncode, command, output=stdout, stderr=stderr)
|
| 71 |
except CalledProcessError as e:
|
| 72 |
+
logger.error(f'ошибка при оптимизации {image_path}')
|
| 73 |
if attempt < retries - 1:
|
| 74 |
await sleep(2 ** attempt)
|
| 75 |
else:
|
|
|
|
| 78 |
|
| 79 |
async def optimize_pngs(image_paths: list[str | Path] | str | Path) -> None:
|
| 80 |
image_paths = [Path(image_file) for image_file in ([image_paths] if not isinstance(image_paths, list) else image_paths)]
|
| 81 |
+
logger.info(f'оптимизируется список список из {len(image_paths)}: {image_paths}')
|
| 82 |
tasks = [optimize_png(image_path) for image_path in image_paths]
|
| 83 |
await gather(*tasks)
|
| 84 |
|
|
|
|
| 132 |
|
| 133 |
async def optimize_and_upload(images_urls: list[str] | str) -> list[str]:
|
| 134 |
images_urls = [images_urls] if isinstance(images_urls, str) else images_urls
|
| 135 |
+
logger.info(f'принятые ссылки в обработку ({len(images_urls)}): {images_urls}')
|
| 136 |
images_paths = await download_pngs(images_urls)
|
| 137 |
await optimize_pngs(images_paths)
|
| 138 |
new_images_urls = []
|
|
|
|
| 140 |
new_url = await upload_image(image_path)
|
| 141 |
if new_url:
|
| 142 |
new_images_urls.append(new_url)
|
| 143 |
+
logger.info(f'загружено изображение {image_path} в {new_url}')
|
| 144 |
+
try:
|
| 145 |
+
image_path.unlink()
|
| 146 |
+
except Exception as e:
|
| 147 |
+
logger.error(f'не удалось удалить файл {image_path}: {e}')
|
| 148 |
+
logger.info(f'новые ссылки: ({len(new_images_urls)}): {new_images_urls}')
|
| 149 |
+
try:
|
| 150 |
+
rmtree(images_paths[0].parent)
|
| 151 |
+
except Exception as e:
|
| 152 |
+
logger.error(f'не удалось удалить файл {images_paths[0].parent}: {e}')
|
| 153 |
return new_images_urls
|
| 154 |
|
| 155 |
|