thumbnail-maker 0.1.1__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.
Potentially problematic release.
This version of thumbnail-maker might be problematic. Click here for more details.
- thumbnail_maker/__init__.py +9 -0
- thumbnail_maker/__main__.py +71 -0
- thumbnail_maker/cli.py +140 -0
- thumbnail_maker/cli_new.py +38 -0
- thumbnail_maker/gui.py +861 -0
- thumbnail_maker/renderer.py +544 -0
- thumbnail_maker-0.1.1.dist-info/METADATA +158 -0
- thumbnail_maker-0.1.1.dist-info/RECORD +10 -0
- thumbnail_maker-0.1.1.dist-info/WHEEL +4 -0
- thumbnail_maker-0.1.1.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
thumbnail_maker: 단일 엔트리포인트 (subcommands: gui, generate-thumbnail, genthumb)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import argparse
|
|
9
|
+
|
|
10
|
+
from .gui import main as gui_main
|
|
11
|
+
from .cli import main as generate_main, main_cli as genthumb_main
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main() -> None:
|
|
15
|
+
parser = argparse.ArgumentParser(prog='thumbnail_maker', description='썸네일 메이커')
|
|
16
|
+
subparsers = parser.add_subparsers(dest='command', required=True)
|
|
17
|
+
|
|
18
|
+
# gui
|
|
19
|
+
subparsers.add_parser('gui', help='GUI 실행')
|
|
20
|
+
|
|
21
|
+
# generate-thumbnail (DSL만 사용)
|
|
22
|
+
gen = subparsers.add_parser('generate-thumbnail', help='DSL로 썸네일 생성')
|
|
23
|
+
gen.add_argument('dsl', nargs='?', default='thumbnail.json', help='DSL 파일 경로')
|
|
24
|
+
gen.add_argument('-o', '--output', default='thumbnail.png', help='출력 파일 경로')
|
|
25
|
+
|
|
26
|
+
# genthumb (간편 CLI: 제목/부제목 덮어쓰기 등)
|
|
27
|
+
gt = subparsers.add_parser('genthumb', help='간편 CLI로 썸네일 생성')
|
|
28
|
+
gt.add_argument('dsl', nargs='?', default='thumbnail.json', help='DSL 파일 경로')
|
|
29
|
+
gt.add_argument('-o', '--output', default='thumbnail.png', help='출력 파일 경로')
|
|
30
|
+
gt.add_argument('--title', help='제목 덮어쓰기 (\\n 또는 실제 줄바꿈 지원)')
|
|
31
|
+
gt.add_argument('--subtitle', help='부제목 덮어쓰기 (\\n 또는 실제 줄바꿈 지원)')
|
|
32
|
+
gt.add_argument('--bgImg', help='배경 이미지 경로')
|
|
33
|
+
|
|
34
|
+
args, unknown = parser.parse_known_args()
|
|
35
|
+
|
|
36
|
+
if args.command == 'gui':
|
|
37
|
+
gui_main()
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
if args.command == 'generate-thumbnail':
|
|
41
|
+
# generate_main은 자체 argparse를 사용하므로 여기서 직접 동작 위임이 어려움
|
|
42
|
+
# 동일 기능을 직접 수행하기보다는 해당 모듈의 메인 로직을 그대로 호출하도록 유지
|
|
43
|
+
# 간단하게는 모듈 내부가 파일 인자를 읽도록 짜여 있으므로, 여기서 args를 재적용
|
|
44
|
+
# 하지만 기존 main()은 sys.argv를 파싱하므로, 안전하게 별도 경로로 수행
|
|
45
|
+
# 대신 renderer를 직접 호출하지 않고, cli.main의 구현을 차용하기 위해 임시 argv 구성
|
|
46
|
+
sys.argv = ['generate-thumbnail', args.dsl, '-o', args.output]
|
|
47
|
+
generate_main()
|
|
48
|
+
return
|
|
49
|
+
|
|
50
|
+
if args.command == 'genthumb':
|
|
51
|
+
# 동일 이유로 간편 CLI도 기존 파서를 활용하기 위해 argv 재구성
|
|
52
|
+
new_argv = ['genthumb']
|
|
53
|
+
if args.dsl:
|
|
54
|
+
new_argv.append(args.dsl)
|
|
55
|
+
if args.output:
|
|
56
|
+
new_argv += ['-o', args.output]
|
|
57
|
+
if args.title:
|
|
58
|
+
new_argv += ['--title', args.title]
|
|
59
|
+
if args.subtitle:
|
|
60
|
+
new_argv += ['--subtitle', args.subtitle]
|
|
61
|
+
if args.bgImg:
|
|
62
|
+
new_argv += ['--bgImg', args.bgImg]
|
|
63
|
+
sys.argv = new_argv
|
|
64
|
+
genthumb_main()
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == '__main__':
|
|
69
|
+
main()
|
|
70
|
+
|
|
71
|
+
|
thumbnail_maker/cli.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
CLI 스크립트
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
import json
|
|
10
|
+
import argparse
|
|
11
|
+
import base64
|
|
12
|
+
from .renderer import ThumbnailRenderer
|
|
13
|
+
import tempfile
|
|
14
|
+
import zipfile
|
|
15
|
+
import shutil
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def main():
|
|
19
|
+
"""메인 CLI 진입점"""
|
|
20
|
+
parser = argparse.ArgumentParser(description='썸네일 생성')
|
|
21
|
+
parser.add_argument('dsl', nargs='?', default='thumbnail.json', help='DSL 파일 경로')
|
|
22
|
+
parser.add_argument('-o', '--output', default='thumbnail.png', help='출력 파일 경로')
|
|
23
|
+
|
|
24
|
+
args = parser.parse_args()
|
|
25
|
+
staging = None
|
|
26
|
+
cwd_backup = os.getcwd()
|
|
27
|
+
# 출력 경로를 절대경로로 확보 (staging 디렉토리 변경 전)
|
|
28
|
+
output_path = args.output
|
|
29
|
+
if not os.path.isabs(output_path):
|
|
30
|
+
output_path = os.path.abspath(output_path)
|
|
31
|
+
try:
|
|
32
|
+
# .thl 패키지 지원: 임시 폴더에 풀어서 작업
|
|
33
|
+
if args.dsl.lower().endswith('.thl') and os.path.exists(args.dsl):
|
|
34
|
+
staging = tempfile.mkdtemp(prefix='thl_run_')
|
|
35
|
+
with zipfile.ZipFile(args.dsl, 'r') as zf:
|
|
36
|
+
zf.extractall(staging)
|
|
37
|
+
# 작업 디렉토리를 패키지 루트로 변경 (renderer의 'fonts/' 탐색을 위함)
|
|
38
|
+
os.chdir(staging)
|
|
39
|
+
dsl_path = os.path.join(staging, 'thumbnail.json')
|
|
40
|
+
else:
|
|
41
|
+
dsl_path = args.dsl
|
|
42
|
+
|
|
43
|
+
# DSL 파일 확인
|
|
44
|
+
if not os.path.exists(dsl_path):
|
|
45
|
+
print(f"오류: DSL 파일을 찾을 수 없습니다: {dsl_path}")
|
|
46
|
+
sys.exit(1)
|
|
47
|
+
|
|
48
|
+
# DSL 읽기
|
|
49
|
+
with open(dsl_path, 'r', encoding='utf-8') as f:
|
|
50
|
+
dsl = json.load(f)
|
|
51
|
+
|
|
52
|
+
# 썸네일 생성
|
|
53
|
+
ThumbnailRenderer.render_thumbnail(dsl, output_path)
|
|
54
|
+
finally:
|
|
55
|
+
try:
|
|
56
|
+
os.chdir(cwd_backup)
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
if staging:
|
|
60
|
+
shutil.rmtree(staging, ignore_errors=True)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def main_cli():
|
|
64
|
+
"""간편 CLI 진입점"""
|
|
65
|
+
parser = argparse.ArgumentParser(description='썸네일 생성 (간편 CLI)')
|
|
66
|
+
parser.add_argument('dsl', nargs='?', default='thumbnail.json', help='DSL 파일 경로')
|
|
67
|
+
parser.add_argument('-o', '--output', default='thumbnail.png', help='출력 파일 경로')
|
|
68
|
+
parser.add_argument('--title', help='제목 덮어쓰기 (\\n 또는 실제 줄바꿈 지원)')
|
|
69
|
+
parser.add_argument('--subtitle', help='부제목 덮어쓰기 (\\n 또는 실제 줄바꿈 지원)')
|
|
70
|
+
parser.add_argument('--bgImg', help='배경 이미지 경로')
|
|
71
|
+
|
|
72
|
+
args = parser.parse_args()
|
|
73
|
+
|
|
74
|
+
def normalize_text(s: str) -> str:
|
|
75
|
+
"""CLI에서 전달된 텍스트의 줄바꿈 시퀀스를 실제 줄바꿈으로 변환"""
|
|
76
|
+
if s is None:
|
|
77
|
+
return s
|
|
78
|
+
# 리터럴 \n, \r\n, \r 처리
|
|
79
|
+
# 먼저 \r\n -> \n 으로 통일, 이후 리터럴 역슬래시-n 치환
|
|
80
|
+
s = s.replace('\r\n', '\n').replace('\r', '\n')
|
|
81
|
+
s = s.replace('\\n', '\n')
|
|
82
|
+
return s
|
|
83
|
+
|
|
84
|
+
staging = None
|
|
85
|
+
cwd_backup = os.getcwd()
|
|
86
|
+
# 출력 경로를 절대경로로 확보 (staging 디렉토리 변경 전)
|
|
87
|
+
output_path = args.output
|
|
88
|
+
if not os.path.isabs(output_path):
|
|
89
|
+
output_path = os.path.abspath(output_path)
|
|
90
|
+
try:
|
|
91
|
+
# .thl 패키지 지원
|
|
92
|
+
if args.dsl and args.dsl.lower().endswith('.thl') and os.path.exists(args.dsl):
|
|
93
|
+
staging = tempfile.mkdtemp(prefix='thl_run_')
|
|
94
|
+
with zipfile.ZipFile(args.dsl, 'r') as zf:
|
|
95
|
+
zf.extractall(staging)
|
|
96
|
+
os.chdir(staging)
|
|
97
|
+
dsl_path = os.path.join(staging, 'thumbnail.json')
|
|
98
|
+
else:
|
|
99
|
+
dsl_path = args.dsl
|
|
100
|
+
|
|
101
|
+
# DSL 파일 확인
|
|
102
|
+
if not os.path.exists(dsl_path):
|
|
103
|
+
print(f"오류: DSL 파일을 찾을 수 없습니다: {dsl_path}")
|
|
104
|
+
sys.exit(1)
|
|
105
|
+
|
|
106
|
+
# DSL 읽기
|
|
107
|
+
with open(dsl_path, 'r', encoding='utf-8') as f:
|
|
108
|
+
dsl = json.load(f)
|
|
109
|
+
|
|
110
|
+
# 배경 이미지 처리
|
|
111
|
+
if args.bgImg and os.path.exists(args.bgImg):
|
|
112
|
+
with open(args.bgImg, 'rb') as f:
|
|
113
|
+
image_data = f.read()
|
|
114
|
+
base64_str = base64.b64encode(image_data).decode('utf-8')
|
|
115
|
+
data_url = f"data:image/png;base64,{base64_str}"
|
|
116
|
+
|
|
117
|
+
dsl['Thumbnail']['Background']['type'] = 'image'
|
|
118
|
+
dsl['Thumbnail']['Background']['imagePath'] = data_url
|
|
119
|
+
|
|
120
|
+
# 제목/부제목 덮어쓰기
|
|
121
|
+
if 'Texts' in dsl.get('Thumbnail', {}):
|
|
122
|
+
for txt in dsl['Thumbnail']['Texts']:
|
|
123
|
+
if args.title and txt.get('type') == 'title':
|
|
124
|
+
txt['content'] = normalize_text(args.title)
|
|
125
|
+
if args.subtitle and txt.get('type') == 'subtitle':
|
|
126
|
+
txt['content'] = normalize_text(args.subtitle)
|
|
127
|
+
|
|
128
|
+
# 썸네일 생성
|
|
129
|
+
ThumbnailRenderer.render_thumbnail(dsl, output_path)
|
|
130
|
+
finally:
|
|
131
|
+
try:
|
|
132
|
+
os.chdir(cwd_backup)
|
|
133
|
+
except Exception:
|
|
134
|
+
pass
|
|
135
|
+
if staging:
|
|
136
|
+
shutil.rmtree(staging, ignore_errors=True)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
if __name__ == '__main__':
|
|
140
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
메인 썸네일 생성 스크립트
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
import json
|
|
10
|
+
import argparse
|
|
11
|
+
from .renderer import ThumbnailRenderer
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def main():
|
|
15
|
+
parser = argparse.ArgumentParser(description='썸네일 생성')
|
|
16
|
+
parser.add_argument('dsl', nargs='?', default='thumbnail.json', help='DSL 파일 경로')
|
|
17
|
+
parser.add_argument('-o', '--output', default='thumbnail.png', help='출력 파일 경로')
|
|
18
|
+
|
|
19
|
+
args = parser.parse_args()
|
|
20
|
+
|
|
21
|
+
# DSL 파일 확인
|
|
22
|
+
if not os.path.exists(args.dsl):
|
|
23
|
+
print(f"오류: DSL 파일을 찾을 수 없습니다: {args.dsl}")
|
|
24
|
+
sys.exit(1)
|
|
25
|
+
|
|
26
|
+
# DSL 읽기
|
|
27
|
+
with open(args.dsl, 'r', encoding='utf-8') as f:
|
|
28
|
+
dsl = json.load(f)
|
|
29
|
+
|
|
30
|
+
# 썸네일 생성
|
|
31
|
+
ThumbnailRenderer.render_thumbnail(dsl, args.output)
|
|
32
|
+
|
|
33
|
+
print(f"✅ 썸네일 생성 완료: {args.output}")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if __name__ == '__main__':
|
|
37
|
+
main()
|
|
38
|
+
|