thumby-cli 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- thumby_cli-0.1.0/.github/workflows/publish.yml +34 -0
- thumby_cli-0.1.0/.gitignore +39 -0
- thumby_cli-0.1.0/LICENSE +21 -0
- thumby_cli-0.1.0/PKG-INFO +11 -0
- thumby_cli-0.1.0/README.md +71 -0
- thumby_cli-0.1.0/logo.png +0 -0
- thumby_cli-0.1.0/pyproject.toml +22 -0
- thumby_cli-0.1.0/src/thumby/__init__.py +1 -0
- thumby_cli-0.1.0/src/thumby/cli.py +137 -0
- thumby_cli-0.1.0/src/thumby/thumbnailer.py +439 -0
- thumby_cli-0.1.0/uv.lock +263 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Publish Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build-and-publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
environment:
|
|
16
|
+
name: pypi
|
|
17
|
+
url: https://pypi.org/p/thumby-cli
|
|
18
|
+
steps:
|
|
19
|
+
- name: Check out repository
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.12"
|
|
26
|
+
|
|
27
|
+
- name: Install uv
|
|
28
|
+
uses: astral-sh/setup-uv@v5
|
|
29
|
+
|
|
30
|
+
- name: Build distributions
|
|
31
|
+
run: uv build
|
|
32
|
+
|
|
33
|
+
- name: Publish to PyPI
|
|
34
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
*.so
|
|
7
|
+
|
|
8
|
+
# Packaging / build artifacts
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
|
|
13
|
+
# Virtual environments
|
|
14
|
+
.venv/
|
|
15
|
+
venv/
|
|
16
|
+
env/
|
|
17
|
+
|
|
18
|
+
# Tooling caches
|
|
19
|
+
.ruff_cache/
|
|
20
|
+
.mypy_cache/
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
|
|
23
|
+
# Editors / OS
|
|
24
|
+
.vscode/
|
|
25
|
+
.idea/
|
|
26
|
+
.DS_Store
|
|
27
|
+
Thumbs.db
|
|
28
|
+
|
|
29
|
+
# Local media files (avoid accidental commits)
|
|
30
|
+
*.ts
|
|
31
|
+
*.mp4
|
|
32
|
+
*.mkv
|
|
33
|
+
*.mov
|
|
34
|
+
*.avi
|
|
35
|
+
*.flv
|
|
36
|
+
*.webm
|
|
37
|
+
|
|
38
|
+
# Temporary artifacts produced by app
|
|
39
|
+
filelist.txt
|
thumby_cli-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: thumby-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: High-performance CLI tool to generate video thumbnail sheets with technical metadata headers.
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: av>=9.0.0
|
|
8
|
+
Requires-Dist: pillow>=9.2.0
|
|
9
|
+
Requires-Dist: pymediainfo>=4.3
|
|
10
|
+
Requires-Dist: rich>=13.0.0
|
|
11
|
+
Requires-Dist: typer>=0.15.0
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+

|
|
2
|
+
# thumby
|
|
3
|
+
|
|
4
|
+
**thumby** is a high-performance media utility designed to create beautiful video thumbnail sheets from a provided video file. It extracts frames at regular intervals and arranges them into a grid with a technical header.
|
|
5
|
+
|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ✨ Features
|
|
12
|
+
|
|
13
|
+
- 🖼️ **Professional Layout**: Creates a grid of thumbnails with a full technical metadata header (size, duration, resolution, codecs).
|
|
14
|
+
- ⚡ **High Performance**: Uses `PyAV` (FFmpeg bindings) for fast frame extraction and `Pillow` for image processing.
|
|
15
|
+
- 📊 **Progress Bar**: Real-time progress tracking while extracting frames.
|
|
16
|
+
- 🛠️ **Customizable**: Control rows, columns, tile width, skip time, and JPEG quality.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 🚀 Quick Start
|
|
21
|
+
|
|
22
|
+
### 1. Installation
|
|
23
|
+
|
|
24
|
+
Install **thumby** as a global tool using [uv](https://github.com/astral-sh/uv):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv tool install thumby-cli
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. Usage
|
|
31
|
+
|
|
32
|
+
Generate a thumbnail sheet for a video:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
thumby "my_video.mp4"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This will create `my_video_preview.jpg` in the same directory.
|
|
39
|
+
|
|
40
|
+
### 3. Options
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Custom grid and tile width
|
|
44
|
+
thumby "video.mp4" --rows 5 --cols 5 --width 300
|
|
45
|
+
|
|
46
|
+
# Specify output path
|
|
47
|
+
thumby "video.mp4" --output "preview.jpg"
|
|
48
|
+
|
|
49
|
+
# Skip the first 30 seconds
|
|
50
|
+
thumby "video.mp4" --skip 30
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 🛠️ Configuration
|
|
56
|
+
|
|
57
|
+
| Option | Shorthand | Default | Description |
|
|
58
|
+
|--------|-----------|---------|-------------|
|
|
59
|
+
| `--output` | `-o` | `auto` | Output path for the JPEG image. |
|
|
60
|
+
| `--rows` | `-r` | `9` | Number of rows in the grid. |
|
|
61
|
+
| `--cols` | `-c` | `3` | Number of columns in the grid. |
|
|
62
|
+
| `--width` | `-w` | `400` | Width of each tile in pixels. |
|
|
63
|
+
| `--skip` | `-s` | `10.0` | Seconds to skip from the start. |
|
|
64
|
+
| `--quality` | `-q` | `95` | JPEG quality (1-100). |
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 📝 Requirements
|
|
69
|
+
|
|
70
|
+
- **Python 3.10+**
|
|
71
|
+
- **FFmpeg**: Must be installed and available in your system `PATH`.
|
|
Binary file
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "thumby-cli"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "High-performance CLI tool to generate video thumbnail sheets with technical metadata headers."
|
|
5
|
+
requires-python = ">=3.10"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"typer>=0.15.0",
|
|
8
|
+
"av>=9.0.0",
|
|
9
|
+
"pymediainfo>=4.3",
|
|
10
|
+
"Pillow>=9.2.0",
|
|
11
|
+
"rich>=13.0.0",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.scripts]
|
|
15
|
+
thumby = "thumby.cli:run"
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = ["hatchling"]
|
|
19
|
+
build-backend = "hatchling.build"
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["src/thumby"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.progress import BarColumn, Progress, TextColumn
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
|
|
10
|
+
from . import __version__
|
|
11
|
+
from .thumbnailer import Thumbnailer, ThumbnailerParams
|
|
12
|
+
|
|
13
|
+
ASCII_LOGO = r"""
|
|
14
|
+
|
|
15
|
+
████████╗ ██╗ ██╗ ██╗ ██╗ ███╗ ███╗ ██████╗ ██╗ ██╗
|
|
16
|
+
╚══██╔══╝ ██║ ██║ ██║ ██║ ████╗ ████║ ██╔══██╗ ╚██╗ ██╔╝
|
|
17
|
+
██║ ███████║ ██║ ██║ ██╔████╔██║ ██████╔╝ ╚████╔╝
|
|
18
|
+
██║ ██╔══██║ ██║ ██║ ██║╚██╔╝██║ ██╔══██╗ ╚██╔╝
|
|
19
|
+
██║ ██║ ██║ ╚██████╔╝ ██║ ╚═╝ ██║ ██████╔╝ ██║
|
|
20
|
+
╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def print_logo():
|
|
27
|
+
console = Console()
|
|
28
|
+
lines = ASCII_LOGO.strip("\n").split("\n")
|
|
29
|
+
# Mint palette colors from oh-my-logo: #00d2ff -> #3a7bd5
|
|
30
|
+
start_color = (0, 210, 255)
|
|
31
|
+
end_color = (58, 123, 213)
|
|
32
|
+
|
|
33
|
+
# empty line padding on top
|
|
34
|
+
console.print(Text("\n"))
|
|
35
|
+
|
|
36
|
+
for i, line in enumerate(lines):
|
|
37
|
+
t = i / (len(lines) - 1) if len(lines) > 1 else 0
|
|
38
|
+
r = int(start_color[0] + (end_color[0] - start_color[0]) * t)
|
|
39
|
+
g = int(start_color[1] + (end_color[1] - start_color[1]) * t)
|
|
40
|
+
b = int(start_color[2] + (end_color[2] - start_color[2]) * t)
|
|
41
|
+
|
|
42
|
+
color = f"rgb({r},{g},{b})"
|
|
43
|
+
console.print(Text(line, style=color))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
app = typer.Typer(
|
|
47
|
+
help="Create video thumbnail sheets from a provided video file.",
|
|
48
|
+
pretty_exceptions_show_locals=False,
|
|
49
|
+
rich_markup_mode="rich",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def version_callback(value: bool) -> None:
|
|
54
|
+
if not value:
|
|
55
|
+
return
|
|
56
|
+
print_logo()
|
|
57
|
+
typer.echo(f"thumby {__version__}")
|
|
58
|
+
raise typer.Exit()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@app.command()
|
|
62
|
+
def main(
|
|
63
|
+
video_path: Path = typer.Argument(
|
|
64
|
+
..., help="Path to the video file.", exists=True, file_okay=True, dir_okay=False
|
|
65
|
+
),
|
|
66
|
+
output: Optional[Path] = typer.Option(
|
|
67
|
+
None, "--output", "-o", help="Output path for the JPEG image."
|
|
68
|
+
),
|
|
69
|
+
rows: int = typer.Option(9, "--rows", "-r", help="Number of rows in the grid."),
|
|
70
|
+
cols: int = typer.Option(3, "--cols", "-c", help="Number of columns in the grid."),
|
|
71
|
+
width: int = typer.Option(
|
|
72
|
+
400, "--width", "-w", help="Width of each tile in pixels."
|
|
73
|
+
),
|
|
74
|
+
skip: float = typer.Option(
|
|
75
|
+
10.0, "--skip", "-s", help="Seconds to skip from the start."
|
|
76
|
+
),
|
|
77
|
+
quality: int = typer.Option(95, "--quality", "-q", help="JPEG quality (1-100)."),
|
|
78
|
+
version: Optional[bool] = typer.Option(
|
|
79
|
+
None,
|
|
80
|
+
"--version",
|
|
81
|
+
help="Show version and exit.",
|
|
82
|
+
callback=version_callback,
|
|
83
|
+
is_eager=True,
|
|
84
|
+
),
|
|
85
|
+
) -> None:
|
|
86
|
+
"""
|
|
87
|
+
Generate a thumbnail sheet (preview) for a video file.
|
|
88
|
+
"""
|
|
89
|
+
print_logo()
|
|
90
|
+
if output is None:
|
|
91
|
+
output = video_path.with_name(f"{video_path.stem}_preview.jpg")
|
|
92
|
+
|
|
93
|
+
params = ThumbnailerParams(
|
|
94
|
+
columns=cols,
|
|
95
|
+
rows=rows,
|
|
96
|
+
tile_width=width,
|
|
97
|
+
skip_seconds=skip,
|
|
98
|
+
jpeg_quality=quality,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
thumbnailer = Thumbnailer(params)
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
with Progress(
|
|
105
|
+
TextColumn("[progress.description]{task.description}"),
|
|
106
|
+
BarColumn(),
|
|
107
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
108
|
+
TextColumn("({task.completed}/{task.total} frames)"),
|
|
109
|
+
) as progress:
|
|
110
|
+
task_id = progress.add_task("Extracting frames", total=rows * cols)
|
|
111
|
+
|
|
112
|
+
def progress_callback(done: int, total: int) -> None:
|
|
113
|
+
progress.update(task_id, completed=done, total=total)
|
|
114
|
+
|
|
115
|
+
thumbnailer.create_and_save_preview_thumbnails_for(
|
|
116
|
+
video_path,
|
|
117
|
+
output,
|
|
118
|
+
progress_callback=progress_callback,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
typer.secho(
|
|
122
|
+
f"\nSuccess! Thumbnail sheet saved to: {output}", fg=typer.colors.GREEN
|
|
123
|
+
)
|
|
124
|
+
except Exception as exc:
|
|
125
|
+
typer.secho(f"\nError: {exc}", fg=typer.colors.RED)
|
|
126
|
+
raise typer.Exit(code=1)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def run() -> None:
|
|
130
|
+
# Show logo if it's the help command
|
|
131
|
+
if len(sys.argv) > 1 and sys.argv[1] in ["-h", "--help"]:
|
|
132
|
+
print_logo()
|
|
133
|
+
app()
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
run()
|
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
from typing import Callable
|
|
7
|
+
from typing import cast
|
|
8
|
+
|
|
9
|
+
import av
|
|
10
|
+
from PIL import Image, ImageColor, ImageDraw, ImageFont
|
|
11
|
+
from pymediainfo import MediaInfo
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(slots=True)
|
|
15
|
+
class ThumbnailerParams:
|
|
16
|
+
columns: int = 3
|
|
17
|
+
rows: int = 9
|
|
18
|
+
tile_width: int = 400
|
|
19
|
+
spacing: int = 2
|
|
20
|
+
background_color: str = "black"
|
|
21
|
+
header_font_color: str = "white"
|
|
22
|
+
timestamp_font_color: str = "white"
|
|
23
|
+
timestamp_shadow_color: str = "black"
|
|
24
|
+
skip_seconds: float = 10.0
|
|
25
|
+
jpeg_quality: int = 95
|
|
26
|
+
fast_keyframes: bool = True
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Thumbnailer:
|
|
30
|
+
def __init__(self, params: ThumbnailerParams) -> None:
|
|
31
|
+
self.params = params
|
|
32
|
+
self.background_rgb = ImageColor.getrgb(params.background_color)
|
|
33
|
+
self.header_font_rgb = ImageColor.getrgb(params.header_font_color)
|
|
34
|
+
self.timestamp_font_rgb = ImageColor.getrgb(params.timestamp_font_color)
|
|
35
|
+
self.timestamp_shadow_rgb = ImageColor.getrgb(params.timestamp_shadow_color)
|
|
36
|
+
self.header_font = ImageFont.load_default()
|
|
37
|
+
self.timestamp_font = ImageFont.load_default()
|
|
38
|
+
|
|
39
|
+
def create_and_save_preview_thumbnails_for(
|
|
40
|
+
self,
|
|
41
|
+
video_path: Path,
|
|
42
|
+
output_path: Path,
|
|
43
|
+
progress_callback: Callable[[int, int], None] | None = None,
|
|
44
|
+
) -> None:
|
|
45
|
+
image = self.create_preview_thumbnails_for(
|
|
46
|
+
video_path,
|
|
47
|
+
progress_callback=progress_callback,
|
|
48
|
+
)
|
|
49
|
+
image.save(output_path, quality=self.params.jpeg_quality)
|
|
50
|
+
|
|
51
|
+
def create_preview_thumbnails_for(
|
|
52
|
+
self,
|
|
53
|
+
video_path: Path,
|
|
54
|
+
progress_callback: Callable[[int, int], None] | None = None,
|
|
55
|
+
) -> Image.Image:
|
|
56
|
+
metadata = self._read_metadata(video_path)
|
|
57
|
+
duration_seconds = float(cast(float, metadata["duration_seconds"]))
|
|
58
|
+
if duration_seconds <= 0:
|
|
59
|
+
raise ValueError(f"Video has no valid duration: {video_path}")
|
|
60
|
+
|
|
61
|
+
tile_count = self.params.columns * self.params.rows
|
|
62
|
+
if tile_count <= 0:
|
|
63
|
+
raise ValueError("rows and columns must produce at least one thumbnail")
|
|
64
|
+
|
|
65
|
+
skip = min(
|
|
66
|
+
max(self.params.skip_seconds, 0.0), max(duration_seconds - 0.001, 0.0)
|
|
67
|
+
)
|
|
68
|
+
timeline = duration_seconds - skip
|
|
69
|
+
if timeline <= 0:
|
|
70
|
+
skip = 0.0
|
|
71
|
+
timeline = duration_seconds
|
|
72
|
+
|
|
73
|
+
step = timeline / tile_count
|
|
74
|
+
timestamps = [skip + ((i + 0.5) * step) for i in range(tile_count)]
|
|
75
|
+
|
|
76
|
+
thumbnails = self._capture_thumbnails(
|
|
77
|
+
video_path,
|
|
78
|
+
timestamps,
|
|
79
|
+
progress_callback=progress_callback,
|
|
80
|
+
)
|
|
81
|
+
if not thumbnails:
|
|
82
|
+
raise ValueError(f"Could not capture thumbnails from: {video_path}")
|
|
83
|
+
|
|
84
|
+
first_width, first_height = thumbnails[0].size
|
|
85
|
+
if first_width <= 0 or first_height <= 0:
|
|
86
|
+
raise ValueError("Captured thumbnail has invalid dimensions")
|
|
87
|
+
|
|
88
|
+
aspect_ratio = first_width / first_height
|
|
89
|
+
thumb_width = self.params.tile_width
|
|
90
|
+
thumb_height = max(1, int(thumb_width / aspect_ratio))
|
|
91
|
+
image_width = thumb_width * self.params.columns + self.params.spacing * (
|
|
92
|
+
self.params.columns + 1
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
header_lines = self._build_header_lines(video_path, metadata)
|
|
96
|
+
text_line_spacing = 2
|
|
97
|
+
header_height = self.params.spacing
|
|
98
|
+
for line in header_lines:
|
|
99
|
+
header_height += self._font_height(line, self.header_font)
|
|
100
|
+
header_height += text_line_spacing
|
|
101
|
+
if header_lines:
|
|
102
|
+
header_height -= text_line_spacing
|
|
103
|
+
|
|
104
|
+
image_height = (
|
|
105
|
+
header_height
|
|
106
|
+
+ thumb_height * self.params.rows
|
|
107
|
+
+ self.params.spacing * (self.params.rows + 1)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
output = Image.new(
|
|
111
|
+
"RGB", (image_width, image_height), color=self.background_rgb
|
|
112
|
+
)
|
|
113
|
+
draw = ImageDraw.Draw(output)
|
|
114
|
+
|
|
115
|
+
x = self.params.spacing
|
|
116
|
+
y = self.params.spacing
|
|
117
|
+
for line in header_lines:
|
|
118
|
+
draw.text((x, y), line, fill=self.header_font_rgb, font=self.header_font)
|
|
119
|
+
y += self._font_height(line, self.header_font) + text_line_spacing
|
|
120
|
+
|
|
121
|
+
y = header_height + self.params.spacing
|
|
122
|
+
idx = 0
|
|
123
|
+
for _ in range(self.params.rows):
|
|
124
|
+
x = self.params.spacing
|
|
125
|
+
for _ in range(self.params.columns):
|
|
126
|
+
if idx >= len(thumbnails):
|
|
127
|
+
break
|
|
128
|
+
resized = thumbnails[idx].resize((thumb_width, thumb_height))
|
|
129
|
+
output.paste(resized, (x, y))
|
|
130
|
+
timestamp_label = self._format_time(timestamps[idx])
|
|
131
|
+
self._draw_timestamp(draw, x, y, thumb_height, timestamp_label)
|
|
132
|
+
x += thumb_width + self.params.spacing
|
|
133
|
+
idx += 1
|
|
134
|
+
y += thumb_height + self.params.spacing
|
|
135
|
+
|
|
136
|
+
return output
|
|
137
|
+
|
|
138
|
+
def _capture_thumbnails(
|
|
139
|
+
self,
|
|
140
|
+
video_path: Path,
|
|
141
|
+
timestamps: list[float],
|
|
142
|
+
progress_callback: Callable[[int, int], None] | None = None,
|
|
143
|
+
) -> list[Image.Image]:
|
|
144
|
+
images: list[Image.Image] = []
|
|
145
|
+
total = len(timestamps)
|
|
146
|
+
captured = 0
|
|
147
|
+
|
|
148
|
+
if progress_callback is not None:
|
|
149
|
+
progress_callback(0, total)
|
|
150
|
+
|
|
151
|
+
with av.open(str(video_path)) as container:
|
|
152
|
+
stream = container.streams.video[0]
|
|
153
|
+
stream.thread_type = "AUTO"
|
|
154
|
+
stream.thread_count = 0
|
|
155
|
+
filter_graph, graph_src, graph_sink = self._build_scale_filter_graph(stream)
|
|
156
|
+
|
|
157
|
+
for target_second in timestamps:
|
|
158
|
+
image = self._capture_frame(
|
|
159
|
+
container,
|
|
160
|
+
stream,
|
|
161
|
+
filter_graph,
|
|
162
|
+
graph_src,
|
|
163
|
+
graph_sink,
|
|
164
|
+
target_second,
|
|
165
|
+
)
|
|
166
|
+
if image is not None:
|
|
167
|
+
images.append(image)
|
|
168
|
+
captured += 1
|
|
169
|
+
if progress_callback is not None:
|
|
170
|
+
progress_callback(captured, total)
|
|
171
|
+
|
|
172
|
+
if images and len(images) < len(timestamps):
|
|
173
|
+
last = images[-1]
|
|
174
|
+
images.extend(last.copy() for _ in range(len(timestamps) - len(images)))
|
|
175
|
+
|
|
176
|
+
return images
|
|
177
|
+
|
|
178
|
+
def _build_scale_filter_graph(
|
|
179
|
+
self,
|
|
180
|
+
stream: av.video.stream.VideoStream,
|
|
181
|
+
) -> tuple[av.filter.Graph, Any, Any]:
|
|
182
|
+
graph = av.filter.Graph()
|
|
183
|
+
graph_src = cast(Any, graph.add_buffer(template=stream))
|
|
184
|
+
graph_scale = graph.add("scale", args=f"{self.params.tile_width}:-1")
|
|
185
|
+
graph_sink = cast(Any, graph.add("buffersink"))
|
|
186
|
+
graph_src.link_to(graph_scale)
|
|
187
|
+
graph_scale.link_to(graph_sink)
|
|
188
|
+
graph.configure()
|
|
189
|
+
return graph, graph_src, graph_sink
|
|
190
|
+
|
|
191
|
+
def _capture_frame(
|
|
192
|
+
self,
|
|
193
|
+
container: av.container.input.InputContainer,
|
|
194
|
+
stream: av.video.stream.VideoStream,
|
|
195
|
+
filter_graph: av.filter.Graph,
|
|
196
|
+
graph_src: Any,
|
|
197
|
+
graph_sink: Any,
|
|
198
|
+
target_second: float,
|
|
199
|
+
) -> Image.Image | None:
|
|
200
|
+
seek_second = max(0.0, target_second)
|
|
201
|
+
time_base = float(stream.time_base) if stream.time_base is not None else 0.0
|
|
202
|
+
if time_base <= 0.0:
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
seek_position = int(seek_second / time_base)
|
|
206
|
+
if stream.start_time is not None:
|
|
207
|
+
seek_position += int(stream.start_time)
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
container.seek(seek_position, stream=stream, any_frame=False, backward=True)
|
|
211
|
+
codec_context = getattr(stream, "codec_context", None)
|
|
212
|
+
if codec_context is not None:
|
|
213
|
+
codec_context.flush_buffers()
|
|
214
|
+
except Exception:
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
fallback: Image.Image | None = None
|
|
219
|
+
for frame in container.decode(video=stream.index):
|
|
220
|
+
if frame.pts is None:
|
|
221
|
+
continue
|
|
222
|
+
|
|
223
|
+
if self.params.fast_keyframes:
|
|
224
|
+
return self._filter_frame(
|
|
225
|
+
frame, filter_graph, graph_src, graph_sink
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
frame_second = float(frame.pts * stream.time_base)
|
|
229
|
+
filtered = self._filter_frame(
|
|
230
|
+
frame, filter_graph, graph_src, graph_sink
|
|
231
|
+
)
|
|
232
|
+
if fallback is None:
|
|
233
|
+
fallback = filtered
|
|
234
|
+
if frame_second >= target_second:
|
|
235
|
+
return filtered
|
|
236
|
+
if frame_second > target_second + 5.0:
|
|
237
|
+
break
|
|
238
|
+
|
|
239
|
+
return fallback
|
|
240
|
+
except Exception:
|
|
241
|
+
return None
|
|
242
|
+
|
|
243
|
+
def _filter_frame(
|
|
244
|
+
self,
|
|
245
|
+
frame: av.VideoFrame,
|
|
246
|
+
filter_graph: av.filter.Graph,
|
|
247
|
+
graph_src: Any,
|
|
248
|
+
graph_sink: Any,
|
|
249
|
+
) -> Image.Image:
|
|
250
|
+
graph_src.push(frame)
|
|
251
|
+
filtered_frame = graph_sink.pull()
|
|
252
|
+
return filtered_frame.to_image()
|
|
253
|
+
|
|
254
|
+
def _build_header_lines(
|
|
255
|
+
self, video_path: Path, metadata: dict[str, object]
|
|
256
|
+
) -> list[str]:
|
|
257
|
+
duration_seconds = float(cast(float, metadata["duration_seconds"]))
|
|
258
|
+
file_size = int(cast(int, metadata["file_size"]))
|
|
259
|
+
width = int(cast(int, metadata["width"]))
|
|
260
|
+
height = int(cast(int, metadata["height"]))
|
|
261
|
+
video_format = str(metadata.get("video_format", "unknown"))
|
|
262
|
+
frame_rate = self._first_numeric(metadata.get("frame_rate"), default=None)
|
|
263
|
+
video_bit_rate = self._first_numeric(
|
|
264
|
+
metadata.get("video_bit_rate"), default=None
|
|
265
|
+
)
|
|
266
|
+
audio_format = metadata.get("audio_format")
|
|
267
|
+
audio_bit_rate = self._first_numeric(
|
|
268
|
+
metadata.get("audio_bit_rate"), default=None
|
|
269
|
+
)
|
|
270
|
+
audio_sampling_rate = self._first_numeric(
|
|
271
|
+
metadata.get("audio_sampling_rate"), default=None
|
|
272
|
+
)
|
|
273
|
+
audio_channels = self._first_numeric(
|
|
274
|
+
metadata.get("audio_channels"), default=None
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
lines = [f"File: {video_path.name}"]
|
|
278
|
+
lines.append(
|
|
279
|
+
f"Size: {file_size} B ({self._format_size(file_size)}), Duration: {self._format_time(duration_seconds)}"
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
video_parts = [video_format, f"{width}x{height}"]
|
|
283
|
+
if width > 0 and height > 0:
|
|
284
|
+
video_parts.append(f"({width / height:.2f}:1)")
|
|
285
|
+
if frame_rate:
|
|
286
|
+
video_parts.append(f"{float(frame_rate):.2f} fps")
|
|
287
|
+
if video_bit_rate:
|
|
288
|
+
video_parts.append(self._format_bit_rate(int(video_bit_rate)))
|
|
289
|
+
lines.append(f"Video: {', '.join(video_parts)}")
|
|
290
|
+
|
|
291
|
+
if not audio_format:
|
|
292
|
+
lines.append("Audio: None")
|
|
293
|
+
else:
|
|
294
|
+
audio_parts = [str(audio_format)]
|
|
295
|
+
if audio_sampling_rate:
|
|
296
|
+
audio_parts.append(f"{int(audio_sampling_rate)} Hz")
|
|
297
|
+
if audio_channels:
|
|
298
|
+
channel_count = int(audio_channels)
|
|
299
|
+
if channel_count == 1:
|
|
300
|
+
audio_parts.append("mono")
|
|
301
|
+
elif channel_count == 2:
|
|
302
|
+
audio_parts.append("stereo")
|
|
303
|
+
else:
|
|
304
|
+
audio_parts.append(f"{channel_count} channels")
|
|
305
|
+
if audio_bit_rate:
|
|
306
|
+
audio_parts.append(self._format_bit_rate(int(audio_bit_rate)))
|
|
307
|
+
lines.append(f"Audio: {', '.join(audio_parts)}")
|
|
308
|
+
|
|
309
|
+
return lines
|
|
310
|
+
|
|
311
|
+
def _read_metadata(self, video_path: Path) -> dict[str, object]:
|
|
312
|
+
general_track = None
|
|
313
|
+
video_track = None
|
|
314
|
+
audio_track = None
|
|
315
|
+
|
|
316
|
+
for track in MediaInfo.parse(str(video_path)).tracks:
|
|
317
|
+
if track.track_type == "General" and general_track is None:
|
|
318
|
+
general_track = track.to_data()
|
|
319
|
+
elif track.track_type == "Video" and video_track is None:
|
|
320
|
+
video_track = track.to_data()
|
|
321
|
+
elif track.track_type == "Audio" and audio_track is None:
|
|
322
|
+
audio_track = track.to_data()
|
|
323
|
+
|
|
324
|
+
if video_track is None:
|
|
325
|
+
raise ValueError(f"Unable to read video metadata: {video_path}")
|
|
326
|
+
|
|
327
|
+
duration_ms = self._first_numeric(
|
|
328
|
+
video_track.get("duration") if video_track else None,
|
|
329
|
+
general_track.get("duration") if general_track else None,
|
|
330
|
+
default=0.0,
|
|
331
|
+
)
|
|
332
|
+
duration_seconds = (duration_ms or 0.0) / 1000.0
|
|
333
|
+
|
|
334
|
+
width = int(self._first_numeric(video_track.get("width"), default=0.0) or 0)
|
|
335
|
+
height = int(self._first_numeric(video_track.get("height"), default=0.0) or 0)
|
|
336
|
+
if width <= 0 or height <= 0:
|
|
337
|
+
with av.open(str(video_path)) as container:
|
|
338
|
+
stream = container.streams.video[0]
|
|
339
|
+
width = int(stream.width or 0)
|
|
340
|
+
height = int(stream.height or 0)
|
|
341
|
+
|
|
342
|
+
file_size = int(
|
|
343
|
+
self._first_numeric(
|
|
344
|
+
general_track.get("file_size") if general_track else None,
|
|
345
|
+
default=float(video_path.stat().st_size),
|
|
346
|
+
)
|
|
347
|
+
or 0
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
"duration_seconds": duration_seconds,
|
|
352
|
+
"file_size": file_size,
|
|
353
|
+
"width": width,
|
|
354
|
+
"height": height,
|
|
355
|
+
"video_format": video_track.get("format", "unknown"),
|
|
356
|
+
"frame_rate": self._first_numeric(
|
|
357
|
+
video_track.get("frame_rate"), default=None
|
|
358
|
+
),
|
|
359
|
+
"video_bit_rate": self._first_numeric(
|
|
360
|
+
video_track.get("bit_rate"), default=None
|
|
361
|
+
),
|
|
362
|
+
"audio_format": audio_track.get("format") if audio_track else None,
|
|
363
|
+
"audio_sampling_rate": self._first_numeric(
|
|
364
|
+
audio_track.get("sampling_rate") if audio_track else None,
|
|
365
|
+
default=None,
|
|
366
|
+
),
|
|
367
|
+
"audio_channels": self._first_numeric(
|
|
368
|
+
audio_track.get("channel_s") if audio_track else None,
|
|
369
|
+
default=None,
|
|
370
|
+
),
|
|
371
|
+
"audio_bit_rate": self._first_numeric(
|
|
372
|
+
audio_track.get("bit_rate") if audio_track else None,
|
|
373
|
+
default=None,
|
|
374
|
+
),
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
@staticmethod
|
|
378
|
+
def _first_numeric(*values: object, default: float | None = None) -> float | None:
|
|
379
|
+
for value in values:
|
|
380
|
+
if value is None:
|
|
381
|
+
continue
|
|
382
|
+
if isinstance(value, (int, float)):
|
|
383
|
+
return float(value)
|
|
384
|
+
if not isinstance(value, str):
|
|
385
|
+
continue
|
|
386
|
+
try:
|
|
387
|
+
return float(value)
|
|
388
|
+
except (TypeError, ValueError):
|
|
389
|
+
continue
|
|
390
|
+
return default
|
|
391
|
+
|
|
392
|
+
@staticmethod
|
|
393
|
+
def _format_size(size: int) -> str:
|
|
394
|
+
value = float(size)
|
|
395
|
+
for unit in ("B", "KiB", "MiB", "GiB", "TiB"):
|
|
396
|
+
if value < 1024.0 or unit == "TiB":
|
|
397
|
+
return f"{value:.2f} {unit}"
|
|
398
|
+
value /= 1024.0
|
|
399
|
+
return f"{value:.2f} TiB"
|
|
400
|
+
|
|
401
|
+
@staticmethod
|
|
402
|
+
def _format_time(duration_in_seconds: float) -> str:
|
|
403
|
+
duration = max(0, int(duration_in_seconds))
|
|
404
|
+
hours = duration // 3600
|
|
405
|
+
minutes = (duration % 3600) // 60
|
|
406
|
+
seconds = duration % 60
|
|
407
|
+
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
408
|
+
|
|
409
|
+
@staticmethod
|
|
410
|
+
def _format_bit_rate(bits_per_second: int) -> str:
|
|
411
|
+
return f"{int(round(bits_per_second / 1000.0, 0))} kb/s"
|
|
412
|
+
|
|
413
|
+
@staticmethod
|
|
414
|
+
def _font_height(text: str, font: ImageFont.ImageFont) -> int:
|
|
415
|
+
bbox = font.getbbox(text)
|
|
416
|
+
return int(bbox[3] - bbox[1]) + 1
|
|
417
|
+
|
|
418
|
+
def _draw_timestamp(
|
|
419
|
+
self,
|
|
420
|
+
draw: ImageDraw.ImageDraw,
|
|
421
|
+
x: int,
|
|
422
|
+
y: int,
|
|
423
|
+
thumb_height: int,
|
|
424
|
+
text: str,
|
|
425
|
+
) -> None:
|
|
426
|
+
text_x = x + 6
|
|
427
|
+
text_y = y + thumb_height - self._font_height(text, self.timestamp_font) - 6
|
|
428
|
+
draw.text(
|
|
429
|
+
(text_x + 1, text_y + 1),
|
|
430
|
+
text,
|
|
431
|
+
fill=self.timestamp_shadow_rgb,
|
|
432
|
+
font=self.timestamp_font,
|
|
433
|
+
)
|
|
434
|
+
draw.text(
|
|
435
|
+
(text_x, text_y),
|
|
436
|
+
text,
|
|
437
|
+
fill=self.timestamp_font_rgb,
|
|
438
|
+
font=self.timestamp_font,
|
|
439
|
+
)
|
thumby_cli-0.1.0/uv.lock
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 3
|
|
3
|
+
requires-python = ">=3.10"
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "annotated-doc"
|
|
7
|
+
version = "0.0.4"
|
|
8
|
+
source = { registry = "https://pypi.org/simple" }
|
|
9
|
+
sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" }
|
|
10
|
+
wheels = [
|
|
11
|
+
{ url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "av"
|
|
16
|
+
version = "17.0.0"
|
|
17
|
+
source = { registry = "https://pypi.org/simple" }
|
|
18
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b2/eb/abca886df3a091bc406feb5ff71b4c4f426beaae6b71b9697264ce8c7211/av-17.0.0.tar.gz", hash = "sha256:c53685df73775a8763c375c7b2d62a6cb149d992a26a4b098204da42ade8c3df", size = 4410769, upload-time = "2026-03-14T14:38:45.868Z" }
|
|
19
|
+
wheels = [
|
|
20
|
+
{ url = "https://files.pythonhosted.org/packages/95/4d/ea1ac272eeea83014daca1783679a9e9f894e1e68e5eb4f717dd8813da2a/av-17.0.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:4b21bcff4144acae658c0efb011fa8668c7a9638384f3ae7f5add33f35b907c6", size = 23407827, upload-time = "2026-03-14T14:37:47.337Z" },
|
|
21
|
+
{ url = "https://files.pythonhosted.org/packages/54/1a/e433766470c57c9c1c8558021de4d2466b3403ed629e48722d39d12baa6c/av-17.0.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:17cd518fc88dc449ce9dcfd0b40e9b3530266927375a743efc80d510adfb188b", size = 18829899, upload-time = "2026-03-14T14:37:50.493Z" },
|
|
22
|
+
{ url = "https://files.pythonhosted.org/packages/5f/25/95ad714f950c188495ffbfef235d06a332123d6f266026a534801ffc2171/av-17.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:9a8b7b63a92d8dc7cbe5000546e4684176124ddd49fdd9c12570e3aa6dadf11a", size = 35348062, upload-time = "2026-03-14T14:37:52.964Z" },
|
|
23
|
+
{ url = "https://files.pythonhosted.org/packages/7a/db/7f3f9e92f2ac8dba639ab01d69a33b723aa16b5e3e612dbfe667fbc02dcd/av-17.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:8706ce9b5d8d087d093b46a9781e7532c4a9e13874bca1da468be78efc56cecc", size = 37684503, upload-time = "2026-03-14T14:37:55.628Z" },
|
|
24
|
+
{ url = "https://files.pythonhosted.org/packages/c1/53/3b356b14ba72354688c8d9777cf67b707769b6e14b63aaeb0cddeeac8d32/av-17.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3a074835ce807434451086993fedfb3b223dacedb2119ab9d7a72480f2d77f32", size = 36547601, upload-time = "2026-03-14T14:37:58.465Z" },
|
|
25
|
+
{ url = "https://files.pythonhosted.org/packages/cd/8d/f489cd6f9fe9c8b38dca00ecb39dc38836761767a4ec07dd95e62e124ac3/av-17.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f8ef8e8f1a0cbb2e0ad49266015e2277801a916e2186ac9451b493ff6dfdec27", size = 38815129, upload-time = "2026-03-14T14:38:01.277Z" },
|
|
26
|
+
{ url = "https://files.pythonhosted.org/packages/fb/bd/e42536234e37caffd1a054de1a0e6abca226c5686e9672726a8d95511422/av-17.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:a795e153ff31a6430e974b4e6ad0d0fab695b78e3f17812293a0a34cd03ee6a9", size = 28984602, upload-time = "2026-03-14T14:38:03.632Z" },
|
|
27
|
+
{ url = "https://files.pythonhosted.org/packages/b1/fb/55e3b5b5d1fc61466292f26fbcbabafa2642f378dc48875f8f554591e1a4/av-17.0.0-cp311-abi3-macosx_11_0_x86_64.whl", hash = "sha256:ed4013fac77c309a4a68141dcf6148f1821bb1073a36d4289379762a6372f711", size = 23238424, upload-time = "2026-03-14T14:38:05.856Z" },
|
|
28
|
+
{ url = "https://files.pythonhosted.org/packages/52/03/9ace1acc08bc9ae38c14bf3a4b1360e995e4d999d1d33c2cbd7c9e77582a/av-17.0.0-cp311-abi3-macosx_14_0_arm64.whl", hash = "sha256:e44b6c83e9f3be9f79ee87d0b77a27cea9a9cd67bd630362c86b7e56a748dfbb", size = 18709043, upload-time = "2026-03-14T14:38:08.288Z" },
|
|
29
|
+
{ url = "https://files.pythonhosted.org/packages/00/c0/637721f3cd5bb8bd16105a1a08efd781fc12f449931bdb3a4d0cfd63fa55/av-17.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b440da6ac47da0629d509316f24bcd858f33158dbdd0f1b7293d71e99beb26de", size = 34018780, upload-time = "2026-03-14T14:38:10.45Z" },
|
|
30
|
+
{ url = "https://files.pythonhosted.org/packages/d2/59/d19bc3257dd985d55337d7f0414c019414b97e16cd3690ebf9941a847543/av-17.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1060cba85f97f4a337311169d92c0b5e143452cfa5ca0e65fa499d7955e8592e", size = 36358757, upload-time = "2026-03-14T14:38:13.092Z" },
|
|
31
|
+
{ url = "https://files.pythonhosted.org/packages/52/6c/a1f4f2677bae6f2ade7a8a18e90ebdcf70690c9b1c4e40e118aa30fa313f/av-17.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:deda202e6021cfc7ba3e816897760ec5431309d59a4da1f75df3c0e9413d71e7", size = 35195281, upload-time = "2026-03-14T14:38:15.789Z" },
|
|
32
|
+
{ url = "https://files.pythonhosted.org/packages/90/ea/52b0fc6f69432c7bf3f5fbe6f707113650aa40a1a05b9096ffc2bba4f77d/av-17.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ffaf266a1a9c2148072de0a4b5ae98061465178d2cfaa69ee089761149342974", size = 37444817, upload-time = "2026-03-14T14:38:18.563Z" },
|
|
33
|
+
{ url = "https://files.pythonhosted.org/packages/34/ad/d2172966282cb8f146c13b6be7416efefde74186460c5e1708ddfc13dba6/av-17.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:45a35a40b2875bf2f98de7c952d74d960f92f319734e6d28e03b4c62a49e6f49", size = 28888553, upload-time = "2026-03-14T14:38:21.223Z" },
|
|
34
|
+
{ url = "https://files.pythonhosted.org/packages/b0/bb/c5a4c4172c514d631fb506e6366b503576b8c7f29809cf42aca73e28ff01/av-17.0.0-cp311-abi3-win_arm64.whl", hash = "sha256:3d32e9b5c5bbcb872a0b6917b352a1db8a42142237826c9b49a36d5dbd9e9c26", size = 21916910, upload-time = "2026-03-14T14:38:23.706Z" },
|
|
35
|
+
{ url = "https://files.pythonhosted.org/packages/7f/8e/c40ac08e63f79387c59f6ecc38f47d4c942b549130eee579ec1a91f6a291/av-17.0.0-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:d13250fb4b4522e9a6bec32da082556d5f257110ea223758151375748d9bbe25", size = 23483029, upload-time = "2026-03-14T14:38:25.758Z" },
|
|
36
|
+
{ url = "https://files.pythonhosted.org/packages/a9/fb/b4419494bfc249163ec393c613966d66db7e95c76da3345711cd115a79df/av-17.0.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:dbb56aa3b7ae72451d1bf6e9d37c7d83d39b97af712f73583ff419fbf08fc237", size = 18920446, upload-time = "2026-03-14T14:38:27.905Z" },
|
|
37
|
+
{ url = "https://files.pythonhosted.org/packages/30/62/c2306d91602ddad2c56106f21dcb334fd51d5ea2e952f7fa025bb8aa39fc/av-17.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a213ac9e83b7ab12c2e9f277a09cac8e9d85cf0883efdab7a87a60e2e4e48879", size = 37477266, upload-time = "2026-03-14T14:38:30.404Z" },
|
|
38
|
+
{ url = "https://files.pythonhosted.org/packages/28/cd/c8510a9607886785c0b3ca019d503e888c3757529be42a7287fe2bfa92d5/av-17.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:e15c88bb0921f9435bcc5a27a0863dba571a80ad5e1389c4fcf2073833bb4a74", size = 39572988, upload-time = "2026-03-14T14:38:32.984Z" },
|
|
39
|
+
{ url = "https://files.pythonhosted.org/packages/7d/2d/207d9361e25b5abec9be335bbab4df6b6b838e2214be4b374f4cfb285427/av-17.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:096cfd1e9fc896506726c7c42aaf9b370e78c2f257cde4d6ddb6c889bfcc49ec", size = 38399591, upload-time = "2026-03-14T14:38:35.465Z" },
|
|
40
|
+
{ url = "https://files.pythonhosted.org/packages/73/ca/307740c6aa2980966bf11383ffcb04bacc5b13f3d268ab4cfb274ad6f793/av-17.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3649ab3d2c7f58049ded1a36e100c0d8fd529cf258f41dd88678ba824034d8c9", size = 40590681, upload-time = "2026-03-14T14:38:38.269Z" },
|
|
41
|
+
{ url = "https://files.pythonhosted.org/packages/35/f2/6fdb26d0651adf409864cb2a0d60da107e467d3d1aabc94b234ead54324a/av-17.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e5002271ab2135b551d980c2db8f3299d452e3b9d3633f24f6bb57fffe91cd10", size = 29216337, upload-time = "2026-03-14T14:38:40.83Z" },
|
|
42
|
+
{ url = "https://files.pythonhosted.org/packages/41/0a/0896b829a39b5669a2d811e1a79598de661693685cd62b31f11d0c18e65b/av-17.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dba98603fc4665b4f750de86fbaf6c0cfaece970671a9b529e0e3d1711e8367e", size = 22071058, upload-time = "2026-03-14T14:38:43.663Z" },
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[[package]]
|
|
46
|
+
name = "click"
|
|
47
|
+
version = "8.3.1"
|
|
48
|
+
source = { registry = "https://pypi.org/simple" }
|
|
49
|
+
dependencies = [
|
|
50
|
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
51
|
+
]
|
|
52
|
+
sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
|
|
53
|
+
wheels = [
|
|
54
|
+
{ url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[[package]]
|
|
58
|
+
name = "colorama"
|
|
59
|
+
version = "0.4.6"
|
|
60
|
+
source = { registry = "https://pypi.org/simple" }
|
|
61
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
|
62
|
+
wheels = [
|
|
63
|
+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
[[package]]
|
|
67
|
+
name = "markdown-it-py"
|
|
68
|
+
version = "4.0.0"
|
|
69
|
+
source = { registry = "https://pypi.org/simple" }
|
|
70
|
+
dependencies = [
|
|
71
|
+
{ name = "mdurl" },
|
|
72
|
+
]
|
|
73
|
+
sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
|
|
74
|
+
wheels = [
|
|
75
|
+
{ url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
[[package]]
|
|
79
|
+
name = "mdurl"
|
|
80
|
+
version = "0.1.2"
|
|
81
|
+
source = { registry = "https://pypi.org/simple" }
|
|
82
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
|
|
83
|
+
wheels = [
|
|
84
|
+
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
[[package]]
|
|
88
|
+
name = "pillow"
|
|
89
|
+
version = "12.1.1"
|
|
90
|
+
source = { registry = "https://pypi.org/simple" }
|
|
91
|
+
sdist = { url = "https://files.pythonhosted.org/packages/1f/42/5c74462b4fd957fcd7b13b04fb3205ff8349236ea74c7c375766d6c82288/pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4", size = 46980264, upload-time = "2026-02-11T04:23:07.146Z" }
|
|
92
|
+
wheels = [
|
|
93
|
+
{ url = "https://files.pythonhosted.org/packages/1d/30/5bd3d794762481f8c8ae9c80e7b76ecea73b916959eb587521358ef0b2f9/pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0", size = 5304099, upload-time = "2026-02-11T04:20:06.13Z" },
|
|
94
|
+
{ url = "https://files.pythonhosted.org/packages/bd/c1/aab9e8f3eeb4490180e357955e15c2ef74b31f64790ff356c06fb6cf6d84/pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713", size = 4657880, upload-time = "2026-02-11T04:20:09.291Z" },
|
|
95
|
+
{ url = "https://files.pythonhosted.org/packages/f1/0a/9879e30d56815ad529d3985aeff5af4964202425c27261a6ada10f7cbf53/pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b", size = 6222587, upload-time = "2026-02-11T04:20:10.82Z" },
|
|
96
|
+
{ url = "https://files.pythonhosted.org/packages/5a/5f/a1b72ff7139e4f89014e8d451442c74a774d5c43cd938fb0a9f878576b37/pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b", size = 8027678, upload-time = "2026-02-11T04:20:12.455Z" },
|
|
97
|
+
{ url = "https://files.pythonhosted.org/packages/e2/c2/c7cb187dac79a3d22c3ebeae727abee01e077c8c7d930791dc592f335153/pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4", size = 6335777, upload-time = "2026-02-11T04:20:14.441Z" },
|
|
98
|
+
{ url = "https://files.pythonhosted.org/packages/0c/7b/f9b09a7804ec7336effb96c26d37c29d27225783dc1501b7d62dcef6ae25/pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4", size = 7027140, upload-time = "2026-02-11T04:20:16.387Z" },
|
|
99
|
+
{ url = "https://files.pythonhosted.org/packages/98/b2/2fa3c391550bd421b10849d1a2144c44abcd966daadd2f7c12e19ea988c4/pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e", size = 6449855, upload-time = "2026-02-11T04:20:18.554Z" },
|
|
100
|
+
{ url = "https://files.pythonhosted.org/packages/96/ff/9caf4b5b950c669263c39e96c78c0d74a342c71c4f43fd031bb5cb7ceac9/pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff", size = 7151329, upload-time = "2026-02-11T04:20:20.646Z" },
|
|
101
|
+
{ url = "https://files.pythonhosted.org/packages/7b/f8/4b24841f582704da675ca535935bccb32b00a6da1226820845fac4a71136/pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40", size = 6325574, upload-time = "2026-02-11T04:20:22.43Z" },
|
|
102
|
+
{ url = "https://files.pythonhosted.org/packages/f8/f9/9f6b01c0881d7036063aa6612ef04c0e2cad96be21325a1e92d0203f8e91/pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23", size = 7032347, upload-time = "2026-02-11T04:20:23.932Z" },
|
|
103
|
+
{ url = "https://files.pythonhosted.org/packages/79/13/c7922edded3dcdaf10c59297540b72785620abc0538872c819915746757d/pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9", size = 2453457, upload-time = "2026-02-11T04:20:25.392Z" },
|
|
104
|
+
{ url = "https://files.pythonhosted.org/packages/2b/46/5da1ec4a5171ee7bf1a0efa064aba70ba3d6e0788ce3f5acd1375d23c8c0/pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32", size = 5304084, upload-time = "2026-02-11T04:20:27.501Z" },
|
|
105
|
+
{ url = "https://files.pythonhosted.org/packages/78/93/a29e9bc02d1cf557a834da780ceccd54e02421627200696fcf805ebdc3fb/pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38", size = 4657866, upload-time = "2026-02-11T04:20:29.827Z" },
|
|
106
|
+
{ url = "https://files.pythonhosted.org/packages/13/84/583a4558d492a179d31e4aae32eadce94b9acf49c0337c4ce0b70e0a01f2/pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5", size = 6232148, upload-time = "2026-02-11T04:20:31.329Z" },
|
|
107
|
+
{ url = "https://files.pythonhosted.org/packages/d5/e2/53c43334bbbb2d3b938978532fbda8e62bb6e0b23a26ce8592f36bcc4987/pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090", size = 8038007, upload-time = "2026-02-11T04:20:34.225Z" },
|
|
108
|
+
{ url = "https://files.pythonhosted.org/packages/b8/a6/3d0e79c8a9d58150dd98e199d7c1c56861027f3829a3a60b3c2784190180/pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af", size = 6345418, upload-time = "2026-02-11T04:20:35.858Z" },
|
|
109
|
+
{ url = "https://files.pythonhosted.org/packages/a2/c8/46dfeac5825e600579157eea177be43e2f7ff4a99da9d0d0a49533509ac5/pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b", size = 7034590, upload-time = "2026-02-11T04:20:37.91Z" },
|
|
110
|
+
{ url = "https://files.pythonhosted.org/packages/af/bf/e6f65d3db8a8bbfeaf9e13cc0417813f6319863a73de934f14b2229ada18/pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5", size = 6458655, upload-time = "2026-02-11T04:20:39.496Z" },
|
|
111
|
+
{ url = "https://files.pythonhosted.org/packages/f9/c2/66091f3f34a25894ca129362e510b956ef26f8fb67a0e6417bc5744e56f1/pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d", size = 7159286, upload-time = "2026-02-11T04:20:41.139Z" },
|
|
112
|
+
{ url = "https://files.pythonhosted.org/packages/7b/5a/24bc8eb526a22f957d0cec6243146744966d40857e3d8deb68f7902ca6c1/pillow-12.1.1-cp311-cp311-win32.whl", hash = "sha256:7311c0a0dcadb89b36b7025dfd8326ecfa36964e29913074d47382706e516a7c", size = 6328663, upload-time = "2026-02-11T04:20:43.184Z" },
|
|
113
|
+
{ url = "https://files.pythonhosted.org/packages/31/03/bef822e4f2d8f9d7448c133d0a18185d3cce3e70472774fffefe8b0ed562/pillow-12.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563", size = 7031448, upload-time = "2026-02-11T04:20:44.696Z" },
|
|
114
|
+
{ url = "https://files.pythonhosted.org/packages/49/70/f76296f53610bd17b2e7d31728b8b7825e3ac3b5b3688b51f52eab7c0818/pillow-12.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:b81b5e3511211631b3f672a595e3221252c90af017e399056d0faabb9538aa80", size = 2453651, upload-time = "2026-02-11T04:20:46.243Z" },
|
|
115
|
+
{ url = "https://files.pythonhosted.org/packages/07/d3/8df65da0d4df36b094351dce696f2989bec731d4f10e743b1c5f4da4d3bf/pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052", size = 5262803, upload-time = "2026-02-11T04:20:47.653Z" },
|
|
116
|
+
{ url = "https://files.pythonhosted.org/packages/d6/71/5026395b290ff404b836e636f51d7297e6c83beceaa87c592718747e670f/pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984", size = 4657601, upload-time = "2026-02-11T04:20:49.328Z" },
|
|
117
|
+
{ url = "https://files.pythonhosted.org/packages/b1/2e/1001613d941c67442f745aff0f7cc66dd8df9a9c084eb497e6a543ee6f7e/pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79", size = 6234995, upload-time = "2026-02-11T04:20:51.032Z" },
|
|
118
|
+
{ url = "https://files.pythonhosted.org/packages/07/26/246ab11455b2549b9233dbd44d358d033a2f780fa9007b61a913c5b2d24e/pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293", size = 8045012, upload-time = "2026-02-11T04:20:52.882Z" },
|
|
119
|
+
{ url = "https://files.pythonhosted.org/packages/b2/8b/07587069c27be7535ac1fe33874e32de118fbd34e2a73b7f83436a88368c/pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397", size = 6349638, upload-time = "2026-02-11T04:20:54.444Z" },
|
|
120
|
+
{ url = "https://files.pythonhosted.org/packages/ff/79/6df7b2ee763d619cda2fb4fea498e5f79d984dae304d45a8999b80d6cf5c/pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0", size = 7041540, upload-time = "2026-02-11T04:20:55.97Z" },
|
|
121
|
+
{ url = "https://files.pythonhosted.org/packages/2c/5e/2ba19e7e7236d7529f4d873bdaf317a318896bac289abebd4bb00ef247f0/pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3", size = 6462613, upload-time = "2026-02-11T04:20:57.542Z" },
|
|
122
|
+
{ url = "https://files.pythonhosted.org/packages/03/03/31216ec124bb5c3dacd74ce8efff4cc7f52643653bad4825f8f08c697743/pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35", size = 7166745, upload-time = "2026-02-11T04:20:59.196Z" },
|
|
123
|
+
{ url = "https://files.pythonhosted.org/packages/1f/e7/7c4552d80052337eb28653b617eafdef39adfb137c49dd7e831b8dc13bc5/pillow-12.1.1-cp312-cp312-win32.whl", hash = "sha256:5d1f9575a12bed9e9eedd9a4972834b08c97a352bd17955ccdebfeca5913fa0a", size = 6328823, upload-time = "2026-02-11T04:21:01.385Z" },
|
|
124
|
+
{ url = "https://files.pythonhosted.org/packages/3d/17/688626d192d7261bbbf98846fc98995726bddc2c945344b65bec3a29d731/pillow-12.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:21329ec8c96c6e979cd0dfd29406c40c1d52521a90544463057d2aaa937d66a6", size = 7033367, upload-time = "2026-02-11T04:21:03.536Z" },
|
|
125
|
+
{ url = "https://files.pythonhosted.org/packages/ed/fe/a0ef1f73f939b0eca03ee2c108d0043a87468664770612602c63266a43c4/pillow-12.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:af9a332e572978f0218686636610555ae3defd1633597be015ed50289a03c523", size = 2453811, upload-time = "2026-02-11T04:21:05.116Z" },
|
|
126
|
+
{ url = "https://files.pythonhosted.org/packages/d5/11/6db24d4bd7685583caeae54b7009584e38da3c3d4488ed4cd25b439de486/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e", size = 4062689, upload-time = "2026-02-11T04:21:06.804Z" },
|
|
127
|
+
{ url = "https://files.pythonhosted.org/packages/33/c0/ce6d3b1fe190f0021203e0d9b5b99e57843e345f15f9ef22fcd43842fd21/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9", size = 4138535, upload-time = "2026-02-11T04:21:08.452Z" },
|
|
128
|
+
{ url = "https://files.pythonhosted.org/packages/a0/c6/d5eb6a4fb32a3f9c21a8c7613ec706534ea1cf9f4b3663e99f0d83f6fca8/pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6", size = 3601364, upload-time = "2026-02-11T04:21:10.194Z" },
|
|
129
|
+
{ url = "https://files.pythonhosted.org/packages/14/a1/16c4b823838ba4c9c52c0e6bbda903a3fe5a1bdbf1b8eb4fff7156f3e318/pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60", size = 5262561, upload-time = "2026-02-11T04:21:11.742Z" },
|
|
130
|
+
{ url = "https://files.pythonhosted.org/packages/bb/ad/ad9dc98ff24f485008aa5cdedaf1a219876f6f6c42a4626c08bc4e80b120/pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2", size = 4657460, upload-time = "2026-02-11T04:21:13.786Z" },
|
|
131
|
+
{ url = "https://files.pythonhosted.org/packages/9e/1b/f1a4ea9a895b5732152789326202a82464d5254759fbacae4deea3069334/pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850", size = 6232698, upload-time = "2026-02-11T04:21:15.949Z" },
|
|
132
|
+
{ url = "https://files.pythonhosted.org/packages/95/f4/86f51b8745070daf21fd2e5b1fe0eb35d4db9ca26e6d58366562fb56a743/pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289", size = 8041706, upload-time = "2026-02-11T04:21:17.723Z" },
|
|
133
|
+
{ url = "https://files.pythonhosted.org/packages/29/9b/d6ecd956bb1266dd1045e995cce9b8d77759e740953a1c9aad9502a0461e/pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e", size = 6346621, upload-time = "2026-02-11T04:21:19.547Z" },
|
|
134
|
+
{ url = "https://files.pythonhosted.org/packages/71/24/538bff45bde96535d7d998c6fed1a751c75ac7c53c37c90dc2601b243893/pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717", size = 7038069, upload-time = "2026-02-11T04:21:21.378Z" },
|
|
135
|
+
{ url = "https://files.pythonhosted.org/packages/94/0e/58cb1a6bc48f746bc4cb3adb8cabff73e2742c92b3bf7a220b7cf69b9177/pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a", size = 6460040, upload-time = "2026-02-11T04:21:23.148Z" },
|
|
136
|
+
{ url = "https://files.pythonhosted.org/packages/6c/57/9045cb3ff11eeb6c1adce3b2d60d7d299d7b273a2e6c8381a524abfdc474/pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029", size = 7164523, upload-time = "2026-02-11T04:21:25.01Z" },
|
|
137
|
+
{ url = "https://files.pythonhosted.org/packages/73/f2/9be9cb99f2175f0d4dbadd6616ce1bf068ee54a28277ea1bf1fbf729c250/pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b", size = 6332552, upload-time = "2026-02-11T04:21:27.238Z" },
|
|
138
|
+
{ url = "https://files.pythonhosted.org/packages/3f/eb/b0834ad8b583d7d9d42b80becff092082a1c3c156bb582590fcc973f1c7c/pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1", size = 7040108, upload-time = "2026-02-11T04:21:29.462Z" },
|
|
139
|
+
{ url = "https://files.pythonhosted.org/packages/d5/7d/fc09634e2aabdd0feabaff4a32f4a7d97789223e7c2042fd805ea4b4d2c2/pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a", size = 2453712, upload-time = "2026-02-11T04:21:31.072Z" },
|
|
140
|
+
{ url = "https://files.pythonhosted.org/packages/19/2a/b9d62794fc8a0dd14c1943df68347badbd5511103e0d04c035ffe5cf2255/pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da", size = 5264880, upload-time = "2026-02-11T04:21:32.865Z" },
|
|
141
|
+
{ url = "https://files.pythonhosted.org/packages/26/9d/e03d857d1347fa5ed9247e123fcd2a97b6220e15e9cb73ca0a8d91702c6e/pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc", size = 4660616, upload-time = "2026-02-11T04:21:34.97Z" },
|
|
142
|
+
{ url = "https://files.pythonhosted.org/packages/f7/ec/8a6d22afd02570d30954e043f09c32772bfe143ba9285e2fdb11284952cd/pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c", size = 6269008, upload-time = "2026-02-11T04:21:36.623Z" },
|
|
143
|
+
{ url = "https://files.pythonhosted.org/packages/3d/1d/6d875422c9f28a4a361f495a5f68d9de4a66941dc2c619103ca335fa6446/pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8", size = 8073226, upload-time = "2026-02-11T04:21:38.585Z" },
|
|
144
|
+
{ url = "https://files.pythonhosted.org/packages/a1/cd/134b0b6ee5eda6dc09e25e24b40fdafe11a520bc725c1d0bbaa5e00bf95b/pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20", size = 6380136, upload-time = "2026-02-11T04:21:40.562Z" },
|
|
145
|
+
{ url = "https://files.pythonhosted.org/packages/7a/a9/7628f013f18f001c1b98d8fffe3452f306a70dc6aba7d931019e0492f45e/pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13", size = 7067129, upload-time = "2026-02-11T04:21:42.521Z" },
|
|
146
|
+
{ url = "https://files.pythonhosted.org/packages/1e/f8/66ab30a2193b277785601e82ee2d49f68ea575d9637e5e234faaa98efa4c/pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf", size = 6491807, upload-time = "2026-02-11T04:21:44.22Z" },
|
|
147
|
+
{ url = "https://files.pythonhosted.org/packages/da/0b/a877a6627dc8318fdb84e357c5e1a758c0941ab1ddffdafd231983788579/pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524", size = 7190954, upload-time = "2026-02-11T04:21:46.114Z" },
|
|
148
|
+
{ url = "https://files.pythonhosted.org/packages/83/43/6f732ff85743cf746b1361b91665d9f5155e1483817f693f8d57ea93147f/pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986", size = 6336441, upload-time = "2026-02-11T04:21:48.22Z" },
|
|
149
|
+
{ url = "https://files.pythonhosted.org/packages/3b/44/e865ef3986611bb75bfabdf94a590016ea327833f434558801122979cd0e/pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c", size = 7045383, upload-time = "2026-02-11T04:21:50.015Z" },
|
|
150
|
+
{ url = "https://files.pythonhosted.org/packages/a8/c6/f4fb24268d0c6908b9f04143697ea18b0379490cb74ba9e8d41b898bd005/pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3", size = 2456104, upload-time = "2026-02-11T04:21:51.633Z" },
|
|
151
|
+
{ url = "https://files.pythonhosted.org/packages/03/d0/bebb3ffbf31c5a8e97241476c4cf8b9828954693ce6744b4a2326af3e16b/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af", size = 4062652, upload-time = "2026-02-11T04:21:53.19Z" },
|
|
152
|
+
{ url = "https://files.pythonhosted.org/packages/2d/c0/0e16fb0addda4851445c28f8350d8c512f09de27bbb0d6d0bbf8b6709605/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f", size = 4138823, upload-time = "2026-02-11T04:22:03.088Z" },
|
|
153
|
+
{ url = "https://files.pythonhosted.org/packages/6b/fb/6170ec655d6f6bb6630a013dd7cf7bc218423d7b5fa9071bf63dc32175ae/pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642", size = 3601143, upload-time = "2026-02-11T04:22:04.909Z" },
|
|
154
|
+
{ url = "https://files.pythonhosted.org/packages/59/04/dc5c3f297510ba9a6837cbb318b87dd2b8f73eb41a43cc63767f65cb599c/pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd", size = 5266254, upload-time = "2026-02-11T04:22:07.656Z" },
|
|
155
|
+
{ url = "https://files.pythonhosted.org/packages/05/30/5db1236b0d6313f03ebf97f5e17cda9ca060f524b2fcc875149a8360b21c/pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202", size = 4657499, upload-time = "2026-02-11T04:22:09.613Z" },
|
|
156
|
+
{ url = "https://files.pythonhosted.org/packages/6f/18/008d2ca0eb612e81968e8be0bbae5051efba24d52debf930126d7eaacbba/pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f", size = 6232137, upload-time = "2026-02-11T04:22:11.434Z" },
|
|
157
|
+
{ url = "https://files.pythonhosted.org/packages/70/f1/f14d5b8eeb4b2cd62b9f9f847eb6605f103df89ef619ac68f92f748614ea/pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f", size = 8042721, upload-time = "2026-02-11T04:22:13.321Z" },
|
|
158
|
+
{ url = "https://files.pythonhosted.org/packages/5a/d6/17824509146e4babbdabf04d8171491fa9d776f7061ff6e727522df9bd03/pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f", size = 6347798, upload-time = "2026-02-11T04:22:15.449Z" },
|
|
159
|
+
{ url = "https://files.pythonhosted.org/packages/d1/ee/c85a38a9ab92037a75615aba572c85ea51e605265036e00c5b67dfafbfe2/pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e", size = 7039315, upload-time = "2026-02-11T04:22:17.24Z" },
|
|
160
|
+
{ url = "https://files.pythonhosted.org/packages/ec/f3/bc8ccc6e08a148290d7523bde4d9a0d6c981db34631390dc6e6ec34cacf6/pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0", size = 6462360, upload-time = "2026-02-11T04:22:19.111Z" },
|
|
161
|
+
{ url = "https://files.pythonhosted.org/packages/f6/ab/69a42656adb1d0665ab051eec58a41f169ad295cf81ad45406963105408f/pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb", size = 7165438, upload-time = "2026-02-11T04:22:21.041Z" },
|
|
162
|
+
{ url = "https://files.pythonhosted.org/packages/02/46/81f7aa8941873f0f01d4b55cc543b0a3d03ec2ee30d617a0448bf6bd6dec/pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f", size = 6431503, upload-time = "2026-02-11T04:22:22.833Z" },
|
|
163
|
+
{ url = "https://files.pythonhosted.org/packages/40/72/4c245f7d1044b67affc7f134a09ea619d4895333d35322b775b928180044/pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15", size = 7176748, upload-time = "2026-02-11T04:22:24.64Z" },
|
|
164
|
+
{ url = "https://files.pythonhosted.org/packages/e4/ad/8a87bdbe038c5c698736e3348af5c2194ffb872ea52f11894c95f9305435/pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f", size = 2544314, upload-time = "2026-02-11T04:22:26.685Z" },
|
|
165
|
+
{ url = "https://files.pythonhosted.org/packages/6c/9d/efd18493f9de13b87ede7c47e69184b9e859e4427225ea962e32e56a49bc/pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8", size = 5268612, upload-time = "2026-02-11T04:22:29.884Z" },
|
|
166
|
+
{ url = "https://files.pythonhosted.org/packages/f8/f1/4f42eb2b388eb2ffc660dcb7f7b556c1015c53ebd5f7f754965ef997585b/pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9", size = 4660567, upload-time = "2026-02-11T04:22:31.799Z" },
|
|
167
|
+
{ url = "https://files.pythonhosted.org/packages/01/54/df6ef130fa43e4b82e32624a7b821a2be1c5653a5fdad8469687a7db4e00/pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60", size = 6269951, upload-time = "2026-02-11T04:22:33.921Z" },
|
|
168
|
+
{ url = "https://files.pythonhosted.org/packages/a9/48/618752d06cc44bb4aae8ce0cd4e6426871929ed7b46215638088270d9b34/pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7", size = 8074769, upload-time = "2026-02-11T04:22:35.877Z" },
|
|
169
|
+
{ url = "https://files.pythonhosted.org/packages/c3/bd/f1d71eb39a72fa088d938655afba3e00b38018d052752f435838961127d8/pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f", size = 6381358, upload-time = "2026-02-11T04:22:37.698Z" },
|
|
170
|
+
{ url = "https://files.pythonhosted.org/packages/64/ef/c784e20b96674ed36a5af839305f55616f8b4f8aa8eeccf8531a6e312243/pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586", size = 7068558, upload-time = "2026-02-11T04:22:39.597Z" },
|
|
171
|
+
{ url = "https://files.pythonhosted.org/packages/73/cb/8059688b74422ae61278202c4e1ad992e8a2e7375227be0a21c6b87ca8d5/pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce", size = 6493028, upload-time = "2026-02-11T04:22:42.73Z" },
|
|
172
|
+
{ url = "https://files.pythonhosted.org/packages/c6/da/e3c008ed7d2dd1f905b15949325934510b9d1931e5df999bb15972756818/pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8", size = 7191940, upload-time = "2026-02-11T04:22:44.543Z" },
|
|
173
|
+
{ url = "https://files.pythonhosted.org/packages/01/4a/9202e8d11714c1fc5951f2e1ef362f2d7fbc595e1f6717971d5dd750e969/pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36", size = 6438736, upload-time = "2026-02-11T04:22:46.347Z" },
|
|
174
|
+
{ url = "https://files.pythonhosted.org/packages/f3/ca/cbce2327eb9885476b3957b2e82eb12c866a8b16ad77392864ad601022ce/pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b", size = 7182894, upload-time = "2026-02-11T04:22:48.114Z" },
|
|
175
|
+
{ url = "https://files.pythonhosted.org/packages/ec/d2/de599c95ba0a973b94410477f8bf0b6f0b5e67360eb89bcb1ad365258beb/pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334", size = 2546446, upload-time = "2026-02-11T04:22:50.342Z" },
|
|
176
|
+
{ url = "https://files.pythonhosted.org/packages/56/11/5d43209aa4cb58e0cc80127956ff1796a68b928e6324bbf06ef4db34367b/pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f", size = 5228606, upload-time = "2026-02-11T04:22:52.106Z" },
|
|
177
|
+
{ url = "https://files.pythonhosted.org/packages/5f/d5/3b005b4e4fda6698b371fa6c21b097d4707585d7db99e98d9b0b87ac612a/pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9", size = 4622321, upload-time = "2026-02-11T04:22:53.827Z" },
|
|
178
|
+
{ url = "https://files.pythonhosted.org/packages/df/36/ed3ea2d594356fd8037e5a01f6156c74bc8d92dbb0fa60746cc96cabb6e8/pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e", size = 5247579, upload-time = "2026-02-11T04:22:56.094Z" },
|
|
179
|
+
{ url = "https://files.pythonhosted.org/packages/54/9a/9cc3e029683cf6d20ae5085da0dafc63148e3252c2f13328e553aaa13cfb/pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9", size = 6989094, upload-time = "2026-02-11T04:22:58.288Z" },
|
|
180
|
+
{ url = "https://files.pythonhosted.org/packages/00/98/fc53ab36da80b88df0967896b6c4b4cd948a0dc5aa40a754266aa3ae48b3/pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3", size = 5313850, upload-time = "2026-02-11T04:23:00.554Z" },
|
|
181
|
+
{ url = "https://files.pythonhosted.org/packages/30/02/00fa585abfd9fe9d73e5f6e554dc36cc2b842898cbfc46d70353dae227f8/pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735", size = 5963343, upload-time = "2026-02-11T04:23:02.934Z" },
|
|
182
|
+
{ url = "https://files.pythonhosted.org/packages/f2/26/c56ce33ca856e358d27fda9676c055395abddb82c35ac0f593877ed4562e/pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e", size = 7029880, upload-time = "2026-02-11T04:23:04.783Z" },
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
[[package]]
|
|
186
|
+
name = "pygments"
|
|
187
|
+
version = "2.19.2"
|
|
188
|
+
source = { registry = "https://pypi.org/simple" }
|
|
189
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
|
|
190
|
+
wheels = [
|
|
191
|
+
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
[[package]]
|
|
195
|
+
name = "pymediainfo"
|
|
196
|
+
version = "7.0.1"
|
|
197
|
+
source = { registry = "https://pypi.org/simple" }
|
|
198
|
+
sdist = { url = "https://files.pythonhosted.org/packages/4d/80/80a6fb21005b81e30f6193d45cba13857df09f5d483e0551fa6fbb3aaeed/pymediainfo-7.0.1.tar.gz", hash = "sha256:0d5df59ecc615e24c56f303b8f651579c6accab7265715e5d429186d7ba21514", size = 441563, upload-time = "2025-02-12T14:33:15.038Z" }
|
|
199
|
+
wheels = [
|
|
200
|
+
{ url = "https://files.pythonhosted.org/packages/a6/4a/d895646df3d3ff617b54d7f06a02ed9d6f5b86673030a543927310e0f7ed/pymediainfo-7.0.1-py3-none-macosx_10_10_universal2.whl", hash = "sha256:286f3bf6299be0997093254e0f371855bc5cf2aaf8641d19455a011e3ee3a84d", size = 6983332, upload-time = "2025-02-12T14:42:47.412Z" },
|
|
201
|
+
{ url = "https://files.pythonhosted.org/packages/77/df/bc6b5a08e908c64a81f6ff169716d408ce7380ceff44e1eceb095f49e0dc/pymediainfo-7.0.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:3648e2379fa67bd02433d1e28c707df3a53834dd480680615a9fefd2266f1182", size = 5768082, upload-time = "2025-02-12T14:33:10.543Z" },
|
|
202
|
+
{ url = "https://files.pythonhosted.org/packages/02/10/a9bc1446a48d3a15940eb1af79a71978f368f27e2cc86f9ec3ec2d206a20/pymediainfo-7.0.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:cde98112f1ce486589b17a12e5da42085faea996224f7c67fa45b8c1dca719c6", size = 6001553, upload-time = "2025-02-12T14:33:12.663Z" },
|
|
203
|
+
{ url = "https://files.pythonhosted.org/packages/ed/7f/c48f8514cb60c9ff9be81b6f383e73e66c7461ef854a1b62628e3c823f13/pymediainfo-7.0.1-py3-none-win32.whl", hash = "sha256:01bcaf82b72cefbf4b96f13b2547e1b2e0e734bab7173d7c33f7f01acc07c98b", size = 3125046, upload-time = "2025-02-12T15:04:39.89Z" },
|
|
204
|
+
{ url = "https://files.pythonhosted.org/packages/e7/26/9d50c2a330541bc36c0ea7ce29eeff5b0c35c2624139660df8bcfa9ae3ce/pymediainfo-7.0.1-py3-none-win_amd64.whl", hash = "sha256:13224fa7590e198763b8baf072e704ea81d334e71aa32a469091460e243893c7", size = 3271232, upload-time = "2025-02-12T15:07:13.672Z" },
|
|
205
|
+
]
|
|
206
|
+
|
|
207
|
+
[[package]]
|
|
208
|
+
name = "rich"
|
|
209
|
+
version = "14.3.3"
|
|
210
|
+
source = { registry = "https://pypi.org/simple" }
|
|
211
|
+
dependencies = [
|
|
212
|
+
{ name = "markdown-it-py" },
|
|
213
|
+
{ name = "pygments" },
|
|
214
|
+
]
|
|
215
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" }
|
|
216
|
+
wheels = [
|
|
217
|
+
{ url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" },
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
[[package]]
|
|
221
|
+
name = "shellingham"
|
|
222
|
+
version = "1.5.4"
|
|
223
|
+
source = { registry = "https://pypi.org/simple" }
|
|
224
|
+
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
|
|
225
|
+
wheels = [
|
|
226
|
+
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
[[package]]
|
|
230
|
+
name = "thumby"
|
|
231
|
+
version = "0.1.0"
|
|
232
|
+
source = { editable = "." }
|
|
233
|
+
dependencies = [
|
|
234
|
+
{ name = "av" },
|
|
235
|
+
{ name = "pillow" },
|
|
236
|
+
{ name = "pymediainfo" },
|
|
237
|
+
{ name = "rich" },
|
|
238
|
+
{ name = "typer" },
|
|
239
|
+
]
|
|
240
|
+
|
|
241
|
+
[package.metadata]
|
|
242
|
+
requires-dist = [
|
|
243
|
+
{ name = "av", specifier = ">=9.0.0" },
|
|
244
|
+
{ name = "pillow", specifier = ">=9.2.0" },
|
|
245
|
+
{ name = "pymediainfo", specifier = ">=4.3" },
|
|
246
|
+
{ name = "rich", specifier = ">=13.0.0" },
|
|
247
|
+
{ name = "typer", specifier = ">=0.15.0" },
|
|
248
|
+
]
|
|
249
|
+
|
|
250
|
+
[[package]]
|
|
251
|
+
name = "typer"
|
|
252
|
+
version = "0.24.1"
|
|
253
|
+
source = { registry = "https://pypi.org/simple" }
|
|
254
|
+
dependencies = [
|
|
255
|
+
{ name = "annotated-doc" },
|
|
256
|
+
{ name = "click" },
|
|
257
|
+
{ name = "rich" },
|
|
258
|
+
{ name = "shellingham" },
|
|
259
|
+
]
|
|
260
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" }
|
|
261
|
+
wheels = [
|
|
262
|
+
{ url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" },
|
|
263
|
+
]
|