thumbnail-maker 0.1.6__py3-none-any.whl
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.
- thumbnail_maker/__init__.py +9 -0
- thumbnail_maker/__main__.py +112 -0
- thumbnail_maker/cli.py +140 -0
- thumbnail_maker/gui/__init__.py +10 -0
- thumbnail_maker/gui/dsl_manager.py +351 -0
- thumbnail_maker/gui/font_utils.py +39 -0
- thumbnail_maker/gui/handlers.py +299 -0
- thumbnail_maker/gui/main_window.py +177 -0
- thumbnail_maker/gui/preview_thread.py +28 -0
- thumbnail_maker/gui/widgets.py +386 -0
- thumbnail_maker/renderer.py +597 -0
- thumbnail_maker/upload.py +279 -0
- thumbnail_maker-0.1.6.dist-info/METADATA +159 -0
- thumbnail_maker-0.1.6.dist-info/RECORD +16 -0
- thumbnail_maker-0.1.6.dist-info/WHEEL +4 -0
- thumbnail_maker-0.1.6.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
이미지 업로드 모듈
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Awaitable, Callable, Dict, Optional
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
from loguru import logger
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_img_ext(img: bytes) -> str:
|
|
17
|
+
"""이미지 바이너리에서 확장자 추출"""
|
|
18
|
+
if not img:
|
|
19
|
+
return "bin"
|
|
20
|
+
if img[:2] == b"\xff\xd8":
|
|
21
|
+
return "jpg"
|
|
22
|
+
if img[:8] == b"\x89PNG\r\n\x1a\n":
|
|
23
|
+
return "png"
|
|
24
|
+
if img[:6] in (b"GIF87a", b"GIF89a"):
|
|
25
|
+
return "gif"
|
|
26
|
+
if len(img) > 12 and img[:4] == b"RIFF" and img[8:12] == b"WEBP":
|
|
27
|
+
return "webp"
|
|
28
|
+
if img[:2] == b"BM":
|
|
29
|
+
return "bmp"
|
|
30
|
+
if img[:4] in (b"II*\x00", b"MM\x00*"):
|
|
31
|
+
return "tiff"
|
|
32
|
+
return "bin"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def log_on_error(response: httpx.Response):
|
|
36
|
+
"""에러 로깅"""
|
|
37
|
+
logger.error(f"Request failed: [{response.status_code}] {response.request.method} {response.url}")
|
|
38
|
+
try:
|
|
39
|
+
logger.debug(f"Response Body: {response.text[:500]}...")
|
|
40
|
+
except Exception as e:
|
|
41
|
+
logger.warning(f"Could not log response body: {e}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# --- 개별 업로드 함수들 --- #
|
|
45
|
+
|
|
46
|
+
@logger.catch(message="Error in anhmoe_upload", default=None)
|
|
47
|
+
async def anhmoe_upload(client: httpx.AsyncClient, img: bytes) -> Optional[str]:
|
|
48
|
+
"""anhmoe 업로드"""
|
|
49
|
+
response = await client.post(
|
|
50
|
+
"https://anh.moe/api/1/upload",
|
|
51
|
+
data={"key": "anh.moe_public_api"},
|
|
52
|
+
files={"source": img},
|
|
53
|
+
timeout=60,
|
|
54
|
+
)
|
|
55
|
+
if response.is_error:
|
|
56
|
+
log_on_error(response)
|
|
57
|
+
return None
|
|
58
|
+
try:
|
|
59
|
+
return response.json()["image"]["url"]
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.error(f"anhmoe parse error: {e} - Resp: {response.text}")
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@logger.catch(message="Error in beeimg_upload", default=None)
|
|
66
|
+
async def beeimg_upload(client: httpx.AsyncClient, img: bytes) -> Optional[str]:
|
|
67
|
+
"""beeimg 업로드"""
|
|
68
|
+
ext = get_img_ext(img)
|
|
69
|
+
if ext == "bin":
|
|
70
|
+
logger.warning("Beeimg: Skip unknown ext")
|
|
71
|
+
return None
|
|
72
|
+
name = f"image.{ext}"
|
|
73
|
+
content_type = f"image/{ext}"
|
|
74
|
+
logger.debug(f"Beeimg: Uploading {name} type: {content_type}")
|
|
75
|
+
response = await client.post(
|
|
76
|
+
"https://beeimg.com/api/upload/file/json/",
|
|
77
|
+
files={"file": (name, img, content_type)},
|
|
78
|
+
timeout=60,
|
|
79
|
+
)
|
|
80
|
+
if response.is_error:
|
|
81
|
+
log_on_error(response)
|
|
82
|
+
try:
|
|
83
|
+
logger.error(f"Beeimg API Error: {response.json()}")
|
|
84
|
+
except Exception:
|
|
85
|
+
pass
|
|
86
|
+
return None
|
|
87
|
+
try:
|
|
88
|
+
relative_url = response.json().get("files", {}).get("url")
|
|
89
|
+
if relative_url:
|
|
90
|
+
return f"https:{relative_url}" if relative_url.startswith("//") else relative_url
|
|
91
|
+
else:
|
|
92
|
+
logger.error(f"beeimg missing URL: {response.text}")
|
|
93
|
+
return None
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.error(f"beeimg parse error: {e} - Resp: {response.text}")
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@logger.catch(message="Error in fastpic_upload", default=None)
|
|
100
|
+
async def fastpic_upload(client: httpx.AsyncClient, img: bytes) -> Optional[str]:
|
|
101
|
+
"""fastpic 업로드"""
|
|
102
|
+
response = await client.post(
|
|
103
|
+
"https://fastpic.org/upload?api=1",
|
|
104
|
+
data={"method": "file", "check_thumb": "no", "uploading": "1"},
|
|
105
|
+
files={"file1": img},
|
|
106
|
+
timeout=60,
|
|
107
|
+
)
|
|
108
|
+
if response.is_error:
|
|
109
|
+
log_on_error(response)
|
|
110
|
+
return None
|
|
111
|
+
match = re.search(r"<imagepath>(.+?)</imagepath>", response.text)
|
|
112
|
+
if match:
|
|
113
|
+
return match[1].strip()
|
|
114
|
+
else:
|
|
115
|
+
logger.error(f"fastpic missing imagepath: {response.text}")
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@logger.catch(message="Error in imagebin_upload", default=None)
|
|
120
|
+
async def imagebin_upload(client: httpx.AsyncClient, img: bytes) -> Optional[str]:
|
|
121
|
+
"""imagebin 업로드"""
|
|
122
|
+
response = await client.post(url="https://imagebin.ca/upload.php", files={"file": img}, timeout=60)
|
|
123
|
+
if response.is_error:
|
|
124
|
+
log_on_error(response)
|
|
125
|
+
return None
|
|
126
|
+
match = re.search(r"url:\s*(.+?)$", response.text, flags=re.MULTILINE)
|
|
127
|
+
if match:
|
|
128
|
+
return match[1].strip()
|
|
129
|
+
else:
|
|
130
|
+
logger.error(f"imagebin missing URL pattern: {response.text}")
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@logger.catch(message="Error in pixhost_upload", default=None)
|
|
135
|
+
async def pixhost_upload(client: httpx.AsyncClient, img: bytes) -> Optional[str]:
|
|
136
|
+
"""pixhost 업로드"""
|
|
137
|
+
try:
|
|
138
|
+
response = await client.post(
|
|
139
|
+
"https://api.pixhost.to/images",
|
|
140
|
+
data={"content_type": 0},
|
|
141
|
+
files={"img": img},
|
|
142
|
+
timeout=60,
|
|
143
|
+
)
|
|
144
|
+
response.raise_for_status()
|
|
145
|
+
json_response = response.json()
|
|
146
|
+
show_url = json_response.get("show_url")
|
|
147
|
+
direct_image_url = json_response.get("url")
|
|
148
|
+
result = direct_image_url if direct_image_url else show_url
|
|
149
|
+
if result:
|
|
150
|
+
return result
|
|
151
|
+
else:
|
|
152
|
+
logger.error(f"pixhost missing URL/show_url: {json_response}")
|
|
153
|
+
return None
|
|
154
|
+
except httpx.HTTPStatusError as e:
|
|
155
|
+
if e.response.status_code == 414:
|
|
156
|
+
logger.error(f"Pixhost 414 type: {get_img_ext(img)}")
|
|
157
|
+
log_on_error(e.response)
|
|
158
|
+
return None
|
|
159
|
+
except httpx.RequestError as e:
|
|
160
|
+
logger.error(f"Pixhost request failed: {e}")
|
|
161
|
+
return None
|
|
162
|
+
except Exception as e:
|
|
163
|
+
logger.error(f"Pixhost general error: {e} - Resp: {getattr(response, 'text', 'N/A')}")
|
|
164
|
+
return None
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@logger.catch(message="Error in sxcu_upload", default=None)
|
|
168
|
+
async def sxcu_upload(client: httpx.AsyncClient, img: bytes, retry_delay=5) -> Optional[str]:
|
|
169
|
+
"""sxcu 업로드"""
|
|
170
|
+
headers = {"User-Agent": "Mozilla/5.0"}
|
|
171
|
+
try:
|
|
172
|
+
response = await client.post(
|
|
173
|
+
"https://sxcu.net/api/files/create",
|
|
174
|
+
headers=headers,
|
|
175
|
+
files={"file": img},
|
|
176
|
+
timeout=60,
|
|
177
|
+
)
|
|
178
|
+
if response.status_code == 429:
|
|
179
|
+
logger.warning(f"Sxcu rate limit (429). Wait {retry_delay}s...")
|
|
180
|
+
await asyncio.sleep(retry_delay)
|
|
181
|
+
logger.info("Retrying sxcu upload...")
|
|
182
|
+
response = await client.post(
|
|
183
|
+
"https://sxcu.net/api/files/create",
|
|
184
|
+
headers=headers,
|
|
185
|
+
files={"file": img},
|
|
186
|
+
timeout=60,
|
|
187
|
+
)
|
|
188
|
+
if response.is_error:
|
|
189
|
+
log_on_error(response)
|
|
190
|
+
return None
|
|
191
|
+
json_data = response.json()
|
|
192
|
+
base_url = json_data.get("url")
|
|
193
|
+
if base_url:
|
|
194
|
+
return base_url
|
|
195
|
+
else:
|
|
196
|
+
logger.error(f"sxcu missing URL/error: {json_data.get('error', 'Unknown')} - Resp: {response.text}")
|
|
197
|
+
return None
|
|
198
|
+
except httpx.RequestError as e:
|
|
199
|
+
logger.error(f"sxcu request failed: {e}")
|
|
200
|
+
return None
|
|
201
|
+
except Exception as e:
|
|
202
|
+
logger.error(f"sxcu general error: {e} - Resp: {getattr(response, 'text', 'N/A')}")
|
|
203
|
+
return None
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# --- 업로드 대상 서비스 모음 --- #
|
|
207
|
+
|
|
208
|
+
UPLOAD_TARGETS: Dict[str, Callable[[httpx.AsyncClient, bytes], Awaitable[Optional[str]]]] = {
|
|
209
|
+
"anhmoe": anhmoe_upload,
|
|
210
|
+
"beeimg": beeimg_upload,
|
|
211
|
+
"fastpic": fastpic_upload,
|
|
212
|
+
"imagebin": imagebin_upload,
|
|
213
|
+
"pixhost": pixhost_upload,
|
|
214
|
+
"sxcu": sxcu_upload,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
async def upload_file_async(file_path: str) -> Optional[str]:
|
|
219
|
+
"""
|
|
220
|
+
파일을 업로드하고 URL을 반환합니다.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
file_path: 업로드할 파일 경로
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
업로드된 URL 또는 None (실패 시)
|
|
227
|
+
"""
|
|
228
|
+
if not os.path.exists(file_path):
|
|
229
|
+
logger.error(f"파일을 찾을 수 없습니다: {file_path}")
|
|
230
|
+
return None
|
|
231
|
+
|
|
232
|
+
# 파일 읽기
|
|
233
|
+
try:
|
|
234
|
+
with open(file_path, "rb") as f:
|
|
235
|
+
img_data = f.read()
|
|
236
|
+
except Exception as e:
|
|
237
|
+
logger.error(f"파일 읽기 실패: {file_path}, {e}")
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
if not img_data:
|
|
241
|
+
logger.error(f"파일이 비어있습니다: {file_path}")
|
|
242
|
+
return None
|
|
243
|
+
|
|
244
|
+
headers = {
|
|
245
|
+
"User-Agent": "Mozilla/5.0",
|
|
246
|
+
"Accept": "image/*,*/*;q=0.8",
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
# 각 서비스를 순차적으로 시도
|
|
250
|
+
async with httpx.AsyncClient(timeout=60, follow_redirects=True, headers=headers) as client:
|
|
251
|
+
for service_name, upload_func in UPLOAD_TARGETS.items():
|
|
252
|
+
try:
|
|
253
|
+
logger.info(f"[{service_name}] 업로드 시도 중...")
|
|
254
|
+
result_url = await upload_func(client, img_data)
|
|
255
|
+
if result_url:
|
|
256
|
+
logger.success(f"[{service_name}] 업로드 성공: {result_url}")
|
|
257
|
+
return result_url
|
|
258
|
+
else:
|
|
259
|
+
logger.warning(f"[{service_name}] 업로드 실패, 다음 서비스 시도...")
|
|
260
|
+
except Exception as e:
|
|
261
|
+
logger.error(f"[{service_name}] 업로드 중 오류: {e}")
|
|
262
|
+
continue
|
|
263
|
+
|
|
264
|
+
logger.error("모든 업로드 서비스 실패")
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def upload_file(file_path: str) -> Optional[str]:
|
|
269
|
+
"""
|
|
270
|
+
파일을 업로드하고 URL을 반환합니다 (동기 함수).
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
file_path: 업로드할 파일 경로
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
업로드된 URL 또는 None (실패 시)
|
|
277
|
+
"""
|
|
278
|
+
return asyncio.run(upload_file_async(file_path))
|
|
279
|
+
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: thumbnail-maker
|
|
3
|
+
Version: 0.1.6
|
|
4
|
+
Summary: 썸네일 생성 도구 - Pillow 기반
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: fonttools>=4.47.0
|
|
7
|
+
Requires-Dist: pillow>=10.0.0
|
|
8
|
+
Requires-Dist: requests>=2.31.0
|
|
9
|
+
Requires-Dist: uv-easy>=0.2.5
|
|
10
|
+
Provides-Extra: gui
|
|
11
|
+
Requires-Dist: pyside6>=6.10.0; extra == 'gui'
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# 썸네일 생성기 (Python)
|
|
15
|
+
|
|
16
|
+
JavaScript 기반 썸네일 생성기를 Python으로 변환한 프로젝트입니다.
|
|
17
|
+
|
|
18
|
+
## 주요 변경사항
|
|
19
|
+
|
|
20
|
+
- **Pillow**: 이미지 생성 라이브러리로 사용
|
|
21
|
+
- **PySide6**: GUI 프레임워크로 사용
|
|
22
|
+
- **Python**: 모든 코드를 Python으로 변환
|
|
23
|
+
|
|
24
|
+
## 설치 방법
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install -r requirements.txt
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 사용 방법
|
|
31
|
+
|
|
32
|
+
uv를 사용하여 설치:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv sync
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
또는 직접 실행:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
uv run python -m thumbnail_maker
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 1. GUI 사용 (추천)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv run thumbnail-gui
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
PySide6 기반 GUI에서 썸네일을 생성할 수 있습니다.
|
|
51
|
+
|
|
52
|
+
### 2. CLI 사용
|
|
53
|
+
|
|
54
|
+
#### 기본 사용
|
|
55
|
+
```bash
|
|
56
|
+
uv run generate-thumbnail
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
#### DSL 파일 지정
|
|
60
|
+
```bash
|
|
61
|
+
uv run generate-thumbnail mydsl.json -o output.png
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### 간편 CLI (genthumb)
|
|
65
|
+
```bash
|
|
66
|
+
# 기본
|
|
67
|
+
uv run genthumb
|
|
68
|
+
|
|
69
|
+
# 제목/부제목 덮어쓰기
|
|
70
|
+
uv run genthumb --title "새 제목" --subtitle "새 부제목"
|
|
71
|
+
|
|
72
|
+
# 배경 이미지 설정
|
|
73
|
+
uv run genthumb --bgImg bg.png
|
|
74
|
+
|
|
75
|
+
# 출력 파일 지정
|
|
76
|
+
uv run genthumb -o result.png
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 파일 구조
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
thumbnail_maker/
|
|
83
|
+
├── requirements.txt # Python 패키지 의존성
|
|
84
|
+
├── thumbnailRenderer.py # 핵심 렌더링 로직
|
|
85
|
+
├── generateThumbnail.py # 메인 생성 스크립트
|
|
86
|
+
├── genthumb.py # 간편 CLI 스크립트
|
|
87
|
+
├── main_gui.py # PySide6 GUI 애플리케이션
|
|
88
|
+
└── thumbnail.json # DSL 예제 파일
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## DSL 파일 형식
|
|
92
|
+
|
|
93
|
+
```json
|
|
94
|
+
{
|
|
95
|
+
"Thumbnail": {
|
|
96
|
+
"Resolution": {
|
|
97
|
+
"type": "preset",
|
|
98
|
+
"value": "16:9"
|
|
99
|
+
},
|
|
100
|
+
"Background": {
|
|
101
|
+
"type": "solid",
|
|
102
|
+
"color": "#a3e635"
|
|
103
|
+
},
|
|
104
|
+
"Texts": [
|
|
105
|
+
{
|
|
106
|
+
"type": "title",
|
|
107
|
+
"content": "제목 텍스트",
|
|
108
|
+
"gridPosition": "tl",
|
|
109
|
+
"font": {
|
|
110
|
+
"name": "SBAggroB",
|
|
111
|
+
"faces": [...]
|
|
112
|
+
},
|
|
113
|
+
"fontSize": 48,
|
|
114
|
+
"color": "#4ade80",
|
|
115
|
+
"outline": {
|
|
116
|
+
"thickness": 7,
|
|
117
|
+
"color": "#000000"
|
|
118
|
+
},
|
|
119
|
+
"enabled": true
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 해상도 설정
|
|
127
|
+
|
|
128
|
+
### Preset 모드
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"type": "preset",
|
|
132
|
+
"value": "16:9" // "16:9", "9:16", "4:3", "1:1"
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Fixed Ratio 모드
|
|
137
|
+
```json
|
|
138
|
+
{
|
|
139
|
+
"type": "fixedRatio",
|
|
140
|
+
"ratioValue": "16:9",
|
|
141
|
+
"width": 480 // 또는 height 지정
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Custom 모드
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"type": "custom",
|
|
149
|
+
"width": 480,
|
|
150
|
+
"height": 270
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 기타
|
|
155
|
+
|
|
156
|
+
- JavaScript 버전의 파일들은 유지됩니다.
|
|
157
|
+
- 기존 DSL 파일과 호환됩니다.
|
|
158
|
+
- 폰트는 `fonts/` 디렉토리에 저장됩니다.
|
|
159
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
thumbnail_maker/__init__.py,sha256=ybcxf-LnaQ2LA3lf9oiDPuQ0ggIoeX2D0U07TF-dJGk,144
|
|
2
|
+
thumbnail_maker/__main__.py,sha256=TbnhYzU8y-IsEq2c1Vt2pnRLkP_vjO6yCHM1f6_bKZo,4535
|
|
3
|
+
thumbnail_maker/cli.py,sha256=bH1bSjhPuvGNkHe3kF3stNQ7ujBU07b1dyPspoMWBgY,5256
|
|
4
|
+
thumbnail_maker/renderer.py,sha256=wNmooCpdZ45_susu9zIChlalho-ubSI0q3iXrkJQRLE,25142
|
|
5
|
+
thumbnail_maker/upload.py,sha256=5uYqql5OyZc27D35QQaxHn2_363q8AA3pa74Ph-eZvc,9454
|
|
6
|
+
thumbnail_maker/gui/__init__.py,sha256=ZnX03kIRCm5BdNfua2TVqs6z1NXz0bDaO1H6Qq7t7y0,170
|
|
7
|
+
thumbnail_maker/gui/dsl_manager.py,sha256=vd5hskxDyQ4YjsYR3mfsbCCHqTwh5usCU2IKWX8bpn8,15385
|
|
8
|
+
thumbnail_maker/gui/font_utils.py,sha256=qGuw-4AtvysOy3M1URQcwg1j85MbHVb5gXxs79b6QM8,1213
|
|
9
|
+
thumbnail_maker/gui/handlers.py,sha256=d-v-Yv2k59ImQ94B7I37_0km2of1tEGErHBM9n9zf2I,11559
|
|
10
|
+
thumbnail_maker/gui/main_window.py,sha256=RwHujw-kjYIhcdFIOEyUysYPfvWphBYbdtMbWnT7z3Q,6000
|
|
11
|
+
thumbnail_maker/gui/preview_thread.py,sha256=b5LaZsMujXwuEKftzEvwqzlOyqOaCBV_yvd04nExGyg,753
|
|
12
|
+
thumbnail_maker/gui/widgets.py,sha256=G81t46aRL2gHYr-EXhE2x1dWf_81HOLkGETVybLsvNE,16433
|
|
13
|
+
thumbnail_maker-0.1.6.dist-info/METADATA,sha256=YrK7BYAl1RK8W3QrrhrGL0DpDt4rdUUYcTdspAbQwN8,2937
|
|
14
|
+
thumbnail_maker-0.1.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
15
|
+
thumbnail_maker-0.1.6.dist-info/entry_points.txt,sha256=rqhlHR3PzlOlRmCL745NshLfST4yxtGNubqAghAJ5hA,66
|
|
16
|
+
thumbnail_maker-0.1.6.dist-info/RECORD,,
|