ascii-art-python 1.0.1__tar.gz → 1.2.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.
- {ascii_art_python-1.0.1/ascii_art_python.egg-info → ascii_art_python-1.2.0}/PKG-INFO +30 -36
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/README.md +26 -35
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/__init__.py +1 -1
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/ascii_base.py +117 -59
- ascii_art_python-1.2.0/ascii_art_python/cli.py +101 -0
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/full_mode.py +19 -4
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/new_skool.py +18 -3
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/old_skool.py +5 -1
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0/ascii_art_python.egg-info}/PKG-INFO +30 -36
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python.egg-info/SOURCES.txt +2 -0
- ascii_art_python-1.2.0/ascii_art_python.egg-info/entry_points.txt +3 -0
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python.egg-info/requires.txt +1 -0
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/pyproject.toml +8 -1
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/LICENSE +0 -0
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/MANIFEST.in +0 -0
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/assets/fonts/GoogleSansCode-Regular.ttf +0 -0
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/assets/fonts/KreativeSquareSM.ttf +0 -0
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python.egg-info/dependency_links.txt +0 -0
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python.egg-info/top_level.txt +0 -0
- {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ascii_art_python
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: A Python library and CLI tool for converting images and videos into ASCII art.
|
|
5
5
|
Author-email: Guillem Prieur <prieurguillem38@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://gitlab.pprriieeuurr.fr/guill_prieur/ascii-art-python
|
|
@@ -19,6 +19,8 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
24
|
Classifier: License :: OSI Approved :: MIT License
|
|
23
25
|
Classifier: Operating System :: OS Independent
|
|
24
26
|
Requires-Python: >=3.9
|
|
@@ -29,6 +31,7 @@ Requires-Dist: tqdm>=4.65.0
|
|
|
29
31
|
Requires-Dist: opencv-python-headless>=4.8.0
|
|
30
32
|
Requires-Dist: moviepy>=2.0.0
|
|
31
33
|
Requires-Dist: numpy>=1.24.0
|
|
34
|
+
Requires-Dist: click
|
|
32
35
|
Requires-Dist: importlib_resources; python_version < "3.10"
|
|
33
36
|
Dynamic: license-file
|
|
34
37
|
|
|
@@ -48,41 +51,32 @@ This module provides advanced tools for converting classic images and videos int
|
|
|
48
51
|
## 👀 Examples
|
|
49
52
|
|
|
50
53
|
```
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
{
|
|
67
|
-
{
|
|
68
|
-
{{{{{{{{
|
|
69
|
-
{{{{
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
@@@@@@@@@$$$$$$@@$:.:::::::::::::.=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
78
|
-
@@@@@@@@@@@@@@@@@$:.:::::::::::::::================*@@@@@@@@@@@@@@@@@@
|
|
79
|
-
@@@@@@@@@@@@@@@@@$:::::::::::::::::::::::::::::::::=@@@@@@@@@@@@@@@@@@
|
|
80
|
-
@@@@@@@@@@@@@@@@@$:::::::::::::::::::::::::({(:::::=@@@@@@@@@@@@@@@@@@
|
|
81
|
-
@@@@@@@@@@@@@@@@@$:::::::::::::::::::::::(@@@@@(:::=@@@@@@@@@@@@@@@@@@
|
|
82
|
-
@@@@@@@@@@@@@@@@@@;.:::::::::::::::::::::*@@@@@*:::=@@@@@@@@@@@@@@@@@@
|
|
83
|
-
@@@@@@@@@@@@@@@@@@$::::::::::::::::::::::::*#*:::::#@@@@@@@@@@@@@@@@@@
|
|
84
|
-
@@@@@@@@@@@@@@@@@@@@#:.::::::::::::::::::::::::::(@@@@@@@@@@@@@@@@@@@@
|
|
85
|
-
@@@@@@@@@@@@@@@@@@@@@@@$*;::::::::::::::::::;(#@@@@@@@@@@@@@@@@@@@@@@@
|
|
54
|
+
||||||*{{*||||||
|
|
55
|
+
///{{{****{{{{{{{{{{{\\
|
|
56
|
+
|{{//-\*{{{{{{{{{{{{{{{\
|
|
57
|
+
|*{*| |{{{{{{{{{{{{{{{{|
|
|
58
|
+
|*{{{*-*{{{{{{{{{{{{{{{{{|
|
|
59
|
+
\{{{{{{{{{{{{{{{{{{{{{{{|
|
|
60
|
+
----- /*{{{{{{{{{{{| -----
|
|
61
|
+
//{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{| |::::::\\
|
|
62
|
+
//{*****{{{{{{{{{{{{{{{{{{{{{{{{{{{{| |::::::::\
|
|
63
|
+
-******{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{| |:::::::::-
|
|
64
|
+
-{***{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{| .::::::::::-
|
|
65
|
+
-{**{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{// /:::::::::::
|
|
66
|
+
*{{{{{{{{{{{{{/----------------- .:::::::::::::
|
|
67
|
+
-{{{{{{{{{{{// .----------------:::::::::::::::::
|
|
68
|
+
-{{{{{{{{{{| .......::::::::::::::::::::::::::::-
|
|
69
|
+
-{{{{{{{{{{| |.....::::::::::::::::::::::::::::::-
|
|
70
|
+
|{{{{{{{{{| |....::::::::::::::::::::::::::::::/
|
|
71
|
+
\\{{{{{{{{| |..:::::::::::::::::::::::::::::::/
|
|
72
|
+
\\-{{{{/| |::::::::::::-----------:::::---/
|
|
73
|
+
|:::::::::::.\----------\
|
|
74
|
+
|::::::::::::::::::::::::|
|
|
75
|
+
|::::::::::::::::::.:::::|
|
|
76
|
+
|::::::::::::::::| |:::|
|
|
77
|
+
|::::::::::::::::\-//::|
|
|
78
|
+
\\::::::::::::::::::///
|
|
79
|
+
|||||:::::::||||
|
|
86
80
|
```
|
|
87
81
|
|
|
88
82
|

|
|
@@ -14,41 +14,32 @@ This module provides advanced tools for converting classic images and videos int
|
|
|
14
14
|
## 👀 Examples
|
|
15
15
|
|
|
16
16
|
```
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
{
|
|
33
|
-
{
|
|
34
|
-
{{{{{{{{
|
|
35
|
-
{{{{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@@@@@@@@@$$$$$$@@$:.:::::::::::::.=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
44
|
-
@@@@@@@@@@@@@@@@@$:.:::::::::::::::================*@@@@@@@@@@@@@@@@@@
|
|
45
|
-
@@@@@@@@@@@@@@@@@$:::::::::::::::::::::::::::::::::=@@@@@@@@@@@@@@@@@@
|
|
46
|
-
@@@@@@@@@@@@@@@@@$:::::::::::::::::::::::::({(:::::=@@@@@@@@@@@@@@@@@@
|
|
47
|
-
@@@@@@@@@@@@@@@@@$:::::::::::::::::::::::(@@@@@(:::=@@@@@@@@@@@@@@@@@@
|
|
48
|
-
@@@@@@@@@@@@@@@@@@;.:::::::::::::::::::::*@@@@@*:::=@@@@@@@@@@@@@@@@@@
|
|
49
|
-
@@@@@@@@@@@@@@@@@@$::::::::::::::::::::::::*#*:::::#@@@@@@@@@@@@@@@@@@
|
|
50
|
-
@@@@@@@@@@@@@@@@@@@@#:.::::::::::::::::::::::::::(@@@@@@@@@@@@@@@@@@@@
|
|
51
|
-
@@@@@@@@@@@@@@@@@@@@@@@$*;::::::::::::::::::;(#@@@@@@@@@@@@@@@@@@@@@@@
|
|
17
|
+
||||||*{{*||||||
|
|
18
|
+
///{{{****{{{{{{{{{{{\\
|
|
19
|
+
|{{//-\*{{{{{{{{{{{{{{{\
|
|
20
|
+
|*{*| |{{{{{{{{{{{{{{{{|
|
|
21
|
+
|*{{{*-*{{{{{{{{{{{{{{{{{|
|
|
22
|
+
\{{{{{{{{{{{{{{{{{{{{{{{|
|
|
23
|
+
----- /*{{{{{{{{{{{| -----
|
|
24
|
+
//{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{| |::::::\\
|
|
25
|
+
//{*****{{{{{{{{{{{{{{{{{{{{{{{{{{{{| |::::::::\
|
|
26
|
+
-******{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{| |:::::::::-
|
|
27
|
+
-{***{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{| .::::::::::-
|
|
28
|
+
-{**{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{// /:::::::::::
|
|
29
|
+
*{{{{{{{{{{{{{/----------------- .:::::::::::::
|
|
30
|
+
-{{{{{{{{{{{// .----------------:::::::::::::::::
|
|
31
|
+
-{{{{{{{{{{| .......::::::::::::::::::::::::::::-
|
|
32
|
+
-{{{{{{{{{{| |.....::::::::::::::::::::::::::::::-
|
|
33
|
+
|{{{{{{{{{| |....::::::::::::::::::::::::::::::/
|
|
34
|
+
\\{{{{{{{{| |..:::::::::::::::::::::::::::::::/
|
|
35
|
+
\\-{{{{/| |::::::::::::-----------:::::---/
|
|
36
|
+
|:::::::::::.\----------\
|
|
37
|
+
|::::::::::::::::::::::::|
|
|
38
|
+
|::::::::::::::::::.:::::|
|
|
39
|
+
|::::::::::::::::| |:::|
|
|
40
|
+
|::::::::::::::::\-//::|
|
|
41
|
+
\\::::::::::::::::::///
|
|
42
|
+
|||||:::::::||||
|
|
52
43
|
```
|
|
53
44
|
|
|
54
45
|

|
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Contains
|
|
2
|
+
Contains the tools necessary to manage and convert images and videos into ASCII art of all types.
|
|
3
3
|
"""
|
|
4
|
+
from typing import Union, Any
|
|
4
5
|
from abc import ABC, abstractmethod
|
|
5
6
|
import sys
|
|
6
|
-
if sys.version_info < (3, 10):
|
|
7
|
-
import importlib_resources as resources
|
|
8
|
-
else:
|
|
9
|
-
from importlib import resources
|
|
10
7
|
from enum import IntEnum
|
|
8
|
+
import time
|
|
11
9
|
from tqdm import tqdm
|
|
12
|
-
import PIL.Image
|
|
13
|
-
|
|
10
|
+
import PIL.Image
|
|
11
|
+
import PIL.ImageDraw
|
|
12
|
+
import PIL.ImageFont
|
|
14
13
|
import cv2
|
|
15
14
|
from moviepy import VideoFileClip
|
|
16
15
|
import numpy
|
|
17
|
-
|
|
16
|
+
if sys.version_info < (3, 10):
|
|
17
|
+
import importlib_resources as resources
|
|
18
|
+
else:
|
|
19
|
+
from importlib import resources
|
|
20
|
+
|
|
18
21
|
|
|
19
22
|
FONT = resources.files("ascii_art_python.assets.fonts").joinpath("KreativeSquareSM.ttf")
|
|
20
|
-
|
|
23
|
+
FONT2 = resources.files("ascii_art_python.assets.fonts").joinpath("GoogleSansCode-Regular.ttf")
|
|
21
24
|
ASCII_CHARS: list[str] = ["@", "$", "&", "#", "{", "*", "(", "=", ";", ":", ".", " "]
|
|
22
25
|
|
|
23
26
|
class ExportType(IntEnum):
|
|
@@ -37,12 +40,18 @@ class Image(ABC):
|
|
|
37
40
|
|
|
38
41
|
@property
|
|
39
42
|
def ratio(self) -> float:
|
|
43
|
+
"""
|
|
44
|
+
The compression ratio between the original image and the desired image.
|
|
45
|
+
"""
|
|
40
46
|
return min(1.0, numpy.sqrt(
|
|
41
47
|
self._max_len / (self._source_wb.width * self._source_wb.height)
|
|
42
48
|
))
|
|
43
49
|
|
|
44
50
|
@property
|
|
45
51
|
def size(self) -> tuple[int, int]:
|
|
52
|
+
"""
|
|
53
|
+
Dimensions (length, width) of the generated image.
|
|
54
|
+
"""
|
|
46
55
|
return (
|
|
47
56
|
int(self._source_wb.size[0] * self.ratio),
|
|
48
57
|
int(self._source_wb.size[1] * self.ratio)
|
|
@@ -73,10 +82,10 @@ class Image(ABC):
|
|
|
73
82
|
def __str__(self) -> str:
|
|
74
83
|
return "\n".join(self.to_list())
|
|
75
84
|
|
|
76
|
-
def __len__(self):
|
|
85
|
+
def __len__(self) -> int:
|
|
77
86
|
return min(self._max_len, self._source_wb.width * self._source_wb.height)
|
|
78
87
|
|
|
79
|
-
def export(self, filename: str = "mika_export", mode: ExportType = 0) -> None:
|
|
88
|
+
def export(self, filename: str = "mika_export", mode: Union[ExportType, int] = 0) -> None:
|
|
80
89
|
"""
|
|
81
90
|
Exports the ASCII image to a text file or an image.
|
|
82
91
|
|
|
@@ -84,13 +93,13 @@ class Image(ABC):
|
|
|
84
93
|
----------
|
|
85
94
|
filename : str, optional
|
|
86
95
|
The export file name (without extension), default is "mika_export".
|
|
87
|
-
mode : ExportType, optional
|
|
96
|
+
mode : ExportType or int, optional
|
|
88
97
|
The export format (text or image), default is 0 (IMAGE_FILE).
|
|
89
98
|
|
|
90
99
|
Returns
|
|
91
100
|
-------
|
|
92
101
|
None
|
|
93
|
-
|
|
102
|
+
Does not return anything.
|
|
94
103
|
"""
|
|
95
104
|
if mode:
|
|
96
105
|
with open(f"{filename}.txt", "w", encoding="ascii") as f:
|
|
@@ -98,7 +107,7 @@ class Image(ABC):
|
|
|
98
107
|
else:
|
|
99
108
|
self.to_image().save(f"{filename}.png")
|
|
100
109
|
|
|
101
|
-
def to_image(self):
|
|
110
|
+
def to_image(self) -> PIL.Image.Image:
|
|
102
111
|
"""
|
|
103
112
|
Converts the generated ASCII art into a readable PIL image object.
|
|
104
113
|
|
|
@@ -124,7 +133,7 @@ class Image(ABC):
|
|
|
124
133
|
return output_image
|
|
125
134
|
|
|
126
135
|
@classmethod
|
|
127
|
-
def from_path(cls, path: str, max_size: int = 10_000):
|
|
136
|
+
def from_path(cls, path: str, max_size: int = 10_000) -> "Image":
|
|
128
137
|
"""
|
|
129
138
|
Creates an Image instance from an image file path.
|
|
130
139
|
|
|
@@ -143,7 +152,7 @@ class Image(ABC):
|
|
|
143
152
|
return cls(PIL.Image.open(path), max_size)
|
|
144
153
|
|
|
145
154
|
@classmethod
|
|
146
|
-
def from_string(cls, content: str, font_size: int = 15):
|
|
155
|
+
def from_string(cls, content: str, font_size: int = 15) -> "Image":
|
|
147
156
|
"""
|
|
148
157
|
Creates an Image instance from a string.
|
|
149
158
|
|
|
@@ -159,26 +168,26 @@ class Image(ABC):
|
|
|
159
168
|
Image
|
|
160
169
|
A new instance of the Image class initialized with the text.
|
|
161
170
|
"""
|
|
162
|
-
|
|
171
|
+
content_list = content.split("\n")
|
|
163
172
|
try:
|
|
164
|
-
font = PIL.ImageFont.truetype(
|
|
173
|
+
font = PIL.ImageFont.truetype(FONT2, font_size)
|
|
165
174
|
except IOError:
|
|
166
175
|
print(f"Warning: {FONT} not found for export. Using default font.")
|
|
167
176
|
font = PIL.ImageFont.load_default()
|
|
168
177
|
|
|
169
|
-
longest_line = max(
|
|
178
|
+
longest_line = max(content_list, key=len)
|
|
170
179
|
|
|
171
180
|
text_width = int(font.getlength(longest_line))
|
|
172
181
|
|
|
173
|
-
size_export = (text_width, len(
|
|
182
|
+
size_export = (text_width, len(content_list) * font_size)
|
|
174
183
|
|
|
175
184
|
output_image = PIL.Image.new("L", size_export, color=255)
|
|
176
185
|
draw = PIL.ImageDraw.Draw(output_image)
|
|
177
186
|
|
|
178
|
-
for i, s in enumerate(
|
|
187
|
+
for i, s in enumerate(content_list):
|
|
179
188
|
draw.text((0, i * font_size), s, fill=0, font=font)
|
|
180
189
|
|
|
181
|
-
return cls(output_image, max_size = text_width * len(
|
|
190
|
+
return cls(output_image, max_size = text_width * len(content_list) * font_size)
|
|
182
191
|
|
|
183
192
|
class Video:
|
|
184
193
|
"""
|
|
@@ -192,7 +201,7 @@ class Video:
|
|
|
192
201
|
LOW = 0
|
|
193
202
|
|
|
194
203
|
@staticmethod
|
|
195
|
-
def transfer_audio(source_video_path, target_video_path, output_path):
|
|
204
|
+
def transfer_audio(source_video_path: str, target_video_path: str, output_path: str) -> None:
|
|
196
205
|
"""
|
|
197
206
|
Transfers audio from a source video to a target (muted) video.
|
|
198
207
|
|
|
@@ -204,12 +213,17 @@ class Video:
|
|
|
204
213
|
The path of the target video generated in ASCII art.
|
|
205
214
|
output_path : str
|
|
206
215
|
The save path for the final video combining image and sound.
|
|
216
|
+
|
|
217
|
+
Returns
|
|
218
|
+
-------
|
|
219
|
+
None
|
|
220
|
+
Does not return anything.
|
|
207
221
|
"""
|
|
208
222
|
source_video = VideoFileClip(source_video_path)
|
|
209
223
|
audio = source_video.audio
|
|
210
224
|
target_video = VideoFileClip(target_video_path)
|
|
211
225
|
|
|
212
|
-
final_video
|
|
226
|
+
final_video = target_video.with_audio(audio)
|
|
213
227
|
|
|
214
228
|
final_video.write_videofile(
|
|
215
229
|
output_path,
|
|
@@ -224,7 +238,7 @@ class Video:
|
|
|
224
238
|
final_video.close()
|
|
225
239
|
|
|
226
240
|
@staticmethod
|
|
227
|
-
def print_from_txt(txt: str):
|
|
241
|
+
def print_from_txt(txt: str) -> None:
|
|
228
242
|
"""
|
|
229
243
|
Reads and displays an ASCII animation in the terminal from a text string.
|
|
230
244
|
|
|
@@ -232,44 +246,80 @@ class Video:
|
|
|
232
246
|
----------
|
|
233
247
|
txt : str
|
|
234
248
|
The raw text containing the metadata and frames of the ASCII animation.
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
None
|
|
253
|
+
Does not return anything.
|
|
235
254
|
"""
|
|
236
255
|
lines = txt.split("\n")
|
|
237
256
|
metadata = lines[0].split("@")
|
|
238
|
-
|
|
257
|
+
lines_length, time_between = int(metadata[0]), float(metadata[1])
|
|
239
258
|
next_sleep = time.time() + time_between
|
|
240
|
-
|
|
259
|
+
|
|
260
|
+
for i in range(len(lines) // lines_length):
|
|
241
261
|
time.sleep(max(next_sleep - time.time(), 0))
|
|
242
|
-
print("\n".join(lines[i*
|
|
262
|
+
print("\n".join(lines[i * lines_length:(i + 1) * lines_length]))
|
|
243
263
|
next_sleep += time_between
|
|
244
264
|
|
|
245
|
-
def __init__(self, path: str, cls: type
|
|
265
|
+
def __init__(self, path: str, cls: type, frame_size: int = 12_000, fps: int = 10) -> None:
|
|
246
266
|
self.cls = cls
|
|
247
267
|
self.path = path
|
|
248
268
|
self.max_fps = fps
|
|
249
269
|
self.max_frame_size = frame_size
|
|
250
270
|
|
|
251
|
-
|
|
252
|
-
|
|
271
|
+
self._cap = None
|
|
272
|
+
self._frame_skip = 1
|
|
273
|
+
self._frame_count = 0
|
|
253
274
|
|
|
254
|
-
|
|
275
|
+
def __iter__(self) -> "Video":
|
|
276
|
+
"""
|
|
277
|
+
Initializes the video capture and returns the iterator (itself).
|
|
278
|
+
"""
|
|
279
|
+
if self._cap is not None:
|
|
280
|
+
self._cap.release()
|
|
281
|
+
|
|
282
|
+
self._cap = cv2.VideoCapture(self.path)
|
|
283
|
+
|
|
284
|
+
if not self._cap.isOpened():
|
|
255
285
|
raise ValueError(f"Error: Unable to open the video at the location '{self.path}'")
|
|
256
286
|
|
|
257
|
-
|
|
258
|
-
|
|
287
|
+
self._frame_skip = max(1, int(self.fps / self.max_fps))
|
|
288
|
+
self._frame_count = 0
|
|
289
|
+
|
|
290
|
+
return self
|
|
291
|
+
|
|
292
|
+
def __next__(self) -> Any:
|
|
293
|
+
"""
|
|
294
|
+
Retrieves and returns the next valid processed video frame.
|
|
295
|
+
"""
|
|
296
|
+
if self._cap is None:
|
|
297
|
+
raise StopIteration
|
|
259
298
|
|
|
260
299
|
while True:
|
|
261
|
-
succes, frame =
|
|
300
|
+
succes, frame = self._cap.read()
|
|
262
301
|
|
|
263
302
|
if not succes:
|
|
264
|
-
|
|
303
|
+
self._cap.release()
|
|
304
|
+
self._cap = None
|
|
305
|
+
raise StopIteration
|
|
265
306
|
|
|
266
|
-
if
|
|
267
|
-
|
|
307
|
+
if self._frame_count % self._frame_skip == 0:
|
|
308
|
+
result_frame = self.cls(
|
|
268
309
|
PIL.Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)),
|
|
269
310
|
self.max_frame_size
|
|
270
311
|
)
|
|
271
|
-
|
|
272
|
-
|
|
312
|
+
self._frame_count += 1
|
|
313
|
+
return result_frame
|
|
314
|
+
|
|
315
|
+
self._frame_count += 1
|
|
316
|
+
|
|
317
|
+
def __del__(self) -> None:
|
|
318
|
+
"""
|
|
319
|
+
Ensures the release of video resources when the object is destroyed.
|
|
320
|
+
"""
|
|
321
|
+
if hasattr(self, "_cap") and self._cap is not None:
|
|
322
|
+
self._cap.release()
|
|
273
323
|
|
|
274
324
|
def __len__(self) -> int:
|
|
275
325
|
cap = cv2.VideoCapture(self.path)
|
|
@@ -278,9 +328,9 @@ class Video:
|
|
|
278
328
|
if not cap.isOpened():
|
|
279
329
|
raise ValueError(f"Error: Unable to open the video at the location '{self.path}'")
|
|
280
330
|
|
|
281
|
-
|
|
331
|
+
length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT) / frame_skip)
|
|
282
332
|
cap.release()
|
|
283
|
-
return
|
|
333
|
+
return length
|
|
284
334
|
|
|
285
335
|
@property
|
|
286
336
|
def fps(self) -> float:
|
|
@@ -314,6 +364,7 @@ class Video:
|
|
|
314
364
|
cap = cv2.VideoCapture(self.path)
|
|
315
365
|
|
|
316
366
|
succes, frame = cap.read()
|
|
367
|
+
cap.release()
|
|
317
368
|
|
|
318
369
|
if not succes:
|
|
319
370
|
raise ValueError("Error: Unable to read video frames to determine size.")
|
|
@@ -324,7 +375,11 @@ class Video:
|
|
|
324
375
|
).size
|
|
325
376
|
return (size[0] * 20, size[1] * 20)
|
|
326
377
|
|
|
327
|
-
def export(
|
|
378
|
+
def export(
|
|
379
|
+
self,
|
|
380
|
+
filename: str = "mika_export", export_type: int = 0,
|
|
381
|
+
quality: Union[ExportQuality, int] = 0, sound: bool = False
|
|
382
|
+
):
|
|
328
383
|
"""
|
|
329
384
|
Exports the ASCII video animation to text or video (.mp4 / .avi) format.
|
|
330
385
|
|
|
@@ -332,7 +387,7 @@ class Video:
|
|
|
332
387
|
----------
|
|
333
388
|
filename : str, optional
|
|
334
389
|
The destination file name (without extension), default is "mika_export".
|
|
335
|
-
export_type :
|
|
390
|
+
export_type : int, optional
|
|
336
391
|
The output format (text or video), default is 0.
|
|
337
392
|
quality : ExportQuality, optional
|
|
338
393
|
The quality/compression level of the video, default is 0.
|
|
@@ -342,16 +397,16 @@ class Video:
|
|
|
342
397
|
Returns
|
|
343
398
|
-------
|
|
344
399
|
None
|
|
345
|
-
|
|
400
|
+
Does not return anything.
|
|
346
401
|
"""
|
|
347
402
|
if export_type:
|
|
348
403
|
with open(filename + ".vid.txt", "w", encoding="ascii") as f:
|
|
349
|
-
f.write(str(self.size[1] // 40) + "@" + str(1/min(self.fps, self.max_fps)) + "\n")
|
|
350
|
-
for frame in tqdm(
|
|
404
|
+
f.write(str(self.size[1] // 40) + "@" + str(1 / min(self.fps, self.max_fps)) + "\n")
|
|
405
|
+
for frame in tqdm(self, "Exporting frames", len(self)):
|
|
351
406
|
f.write(frame.get_alternate_lines() + "\n")
|
|
352
407
|
else:
|
|
353
408
|
if quality:
|
|
354
|
-
fourcc = cv2.VideoWriter_fourcc(*
|
|
409
|
+
fourcc = cv2.VideoWriter_fourcc(*"FFV1")
|
|
355
410
|
out = cv2.VideoWriter(
|
|
356
411
|
filename + ".avi",
|
|
357
412
|
fourcc,
|
|
@@ -359,7 +414,7 @@ class Video:
|
|
|
359
414
|
self.size, False
|
|
360
415
|
)
|
|
361
416
|
else:
|
|
362
|
-
fourcc = cv2.VideoWriter_fourcc(*
|
|
417
|
+
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
|
363
418
|
out = cv2.VideoWriter(
|
|
364
419
|
filename + ".mp4",
|
|
365
420
|
fourcc,
|
|
@@ -367,28 +422,31 @@ class Video:
|
|
|
367
422
|
self.size, False
|
|
368
423
|
)
|
|
369
424
|
|
|
370
|
-
for frame in tqdm(
|
|
425
|
+
for frame in tqdm(self, "Exporting frames", len(self)):
|
|
371
426
|
tab = numpy.array(frame.to_image())
|
|
372
427
|
out.write(tab)
|
|
373
428
|
out.release()
|
|
374
429
|
|
|
375
|
-
if sound
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
430
|
+
if sound:
|
|
431
|
+
print("▶ Adding audio...")
|
|
432
|
+
if quality:
|
|
433
|
+
self.transfer_audio(self.path, filename + ".avi", filename + "_audio.avi")
|
|
434
|
+
else:
|
|
435
|
+
self.transfer_audio(self.path, filename + ".mp4", filename + "_audio.mp4")
|
|
379
436
|
|
|
380
|
-
def print_in_terminal(self) -> None:
|
|
437
|
+
def print_in_terminal(self, func_print: callable = print) -> None:
|
|
381
438
|
"""
|
|
382
439
|
Plays the ASCII video animation by displaying it directly in the terminal.
|
|
383
440
|
|
|
384
441
|
Returns
|
|
385
442
|
-------
|
|
386
443
|
None
|
|
387
|
-
|
|
444
|
+
Does not return anything.
|
|
388
445
|
"""
|
|
389
|
-
time_between = 1/min(self.fps, self.max_fps)
|
|
446
|
+
time_between = 1 / min(self.fps, self.max_fps)
|
|
390
447
|
next_sleep = time.time() + time_between
|
|
391
|
-
|
|
448
|
+
|
|
449
|
+
for frame in self:
|
|
392
450
|
time.sleep(max(next_sleep - time.time(), 0))
|
|
393
|
-
|
|
451
|
+
func_print(frame.get_alternate_lines())
|
|
394
452
|
next_sleep += time_between
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
An ASCII art image in a style new-skool.
|
|
3
|
+
"""
|
|
4
|
+
import mimetypes
|
|
5
|
+
import sys
|
|
6
|
+
import click
|
|
7
|
+
import ascii_art_python as aap
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command()
|
|
11
|
+
@click.argument("source_path", type=click.Path(exists=True))
|
|
12
|
+
@click.argument("target_filename", type=click.STRING)
|
|
13
|
+
@click.option("--length", "-l", default=15_000, help="The number of characters in the output")
|
|
14
|
+
@click.option("--mode", "-m", default="full_mode", help="full_mode / old_skool / new_skool")
|
|
15
|
+
def create_and_export(source_path, target_filename, length, mode):
|
|
16
|
+
"""
|
|
17
|
+
Creates and exports an ASCII art image or video.
|
|
18
|
+
"""
|
|
19
|
+
click.echo(f"▶ Converting '{source_path}'...")
|
|
20
|
+
click.echo(f"▶ Settings: length={length}, mode={mode}")
|
|
21
|
+
|
|
22
|
+
type_mime, _ = mimetypes.guess_type(source_path)
|
|
23
|
+
|
|
24
|
+
if type_mime is None:
|
|
25
|
+
click.secho("✗ Error: Unable to determine which export type to use")
|
|
26
|
+
sys.exit(1)
|
|
27
|
+
elif type_mime.startswith('image'):
|
|
28
|
+
click.echo("▶ Export type: image")
|
|
29
|
+
if mode == "full_mode":
|
|
30
|
+
image = aap.full_mode.Image.from_path(source_path, length)
|
|
31
|
+
elif mode == "old_skool":
|
|
32
|
+
image = aap.old_skool.Image.from_path(source_path, length)
|
|
33
|
+
elif mode == "new_skool":
|
|
34
|
+
image = aap.new_skool.Image.from_path(source_path, length)
|
|
35
|
+
else:
|
|
36
|
+
click.secho(f"✗ Error: Mode '{mode}' is not recognized.")
|
|
37
|
+
sys.exit(1)
|
|
38
|
+
image.export(target_filename)
|
|
39
|
+
click.echo("✓ Conversion complete!")
|
|
40
|
+
elif type_mime.startswith('video'):
|
|
41
|
+
click.echo("▶ Export type: video")
|
|
42
|
+
if mode == "full_mode":
|
|
43
|
+
video = aap.full_mode.Video(source_path, length, 10)
|
|
44
|
+
elif mode == "old_skool":
|
|
45
|
+
video = aap.old_skool.Video(source_path, length, 10)
|
|
46
|
+
elif mode == "new_skool":
|
|
47
|
+
video = aap.new_skool.Video(source_path, length, 10)
|
|
48
|
+
else:
|
|
49
|
+
click.secho(f"✗ Error: Mode '{mode}' is not recognized.")
|
|
50
|
+
sys.exit(1)
|
|
51
|
+
video.export(target_filename, sound=True)
|
|
52
|
+
click.echo("✓ Conversion complete!")
|
|
53
|
+
else:
|
|
54
|
+
click.secho("✗ Error: Unable to determine which export type to use")
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
|
|
57
|
+
@click.command()
|
|
58
|
+
@click.argument("source_path", type=click.Path(exists=True))
|
|
59
|
+
@click.option("--length", "-l", default=3_000, help="The number of characters in the output")
|
|
60
|
+
@click.option("--mode", "-m", default="full_mode", help="full_mode / old_skool / new_skool")
|
|
61
|
+
def create_and_echo(source_path, length, mode):
|
|
62
|
+
"""
|
|
63
|
+
Creates and prints an ASCII art image or video.
|
|
64
|
+
"""
|
|
65
|
+
click.echo(f"▶ Converting '{source_path}'...")
|
|
66
|
+
click.echo(f"▶ Settings: length={length}, mode={mode}")
|
|
67
|
+
|
|
68
|
+
type_mime, _ = mimetypes.guess_type(source_path)
|
|
69
|
+
|
|
70
|
+
if type_mime is None:
|
|
71
|
+
click.secho("✗ Error: Unable to determine which export type to use")
|
|
72
|
+
sys.exit(1)
|
|
73
|
+
elif type_mime.startswith('image'):
|
|
74
|
+
click.echo("▶ Echo type: image")
|
|
75
|
+
if mode == "full_mode":
|
|
76
|
+
image = aap.full_mode.Image.from_path(source_path, length)
|
|
77
|
+
elif mode == "old_skool":
|
|
78
|
+
image = aap.old_skool.Image.from_path(source_path, length)
|
|
79
|
+
elif mode == "new_skool":
|
|
80
|
+
image = aap.new_skool.Image.from_path(source_path, length)
|
|
81
|
+
else:
|
|
82
|
+
click.secho(f"✗ Error: Mode '{mode}' is not recognized.")
|
|
83
|
+
sys.exit(1)
|
|
84
|
+
click.echo(image.get_alternate_lines())
|
|
85
|
+
click.echo("✓ Conversion complete!")
|
|
86
|
+
elif type_mime.startswith('video'):
|
|
87
|
+
click.echo("▶ Echo type: video")
|
|
88
|
+
if mode == "full_mode":
|
|
89
|
+
video = aap.full_mode.Video(source_path, length, 5)
|
|
90
|
+
elif mode == "old_skool":
|
|
91
|
+
video = aap.old_skool.Video(source_path, length, 5)
|
|
92
|
+
elif mode == "new_skool":
|
|
93
|
+
video = aap.new_skool.Video(source_path, length, 5)
|
|
94
|
+
else:
|
|
95
|
+
click.secho(f"✗ Error: Mode '{mode}' is not recognized.")
|
|
96
|
+
sys.exit(1)
|
|
97
|
+
video.print_in_terminal(click.echo)
|
|
98
|
+
click.echo("✓ Conversion complete!")
|
|
99
|
+
else:
|
|
100
|
+
click.secho("✗ Error: Unable to determine which export type to use")
|
|
101
|
+
sys.exit(1)
|
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
"""
|
|
2
|
+
ASCII art tools in a style that blends old-skool and new-skool elements.
|
|
3
|
+
"""
|
|
4
|
+
from typing import Union
|
|
2
5
|
import PIL.Image
|
|
3
6
|
import cv2
|
|
4
7
|
import numpy as np
|
|
8
|
+
from . import ascii_base
|
|
5
9
|
|
|
6
10
|
DEFAULT_GRID: list[str] = ["@", "$", "&", "#", "{", "*", "(", "=", ";", ":", ".", " "]
|
|
7
11
|
|
|
8
12
|
class Image(ascii_base.Image):
|
|
9
|
-
|
|
13
|
+
"""
|
|
14
|
+
An ASCII art image in a style that blends old-skool and new-skool elements.
|
|
15
|
+
"""
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
image: PIL.Image.Image,
|
|
19
|
+
max_size: int = 100_000,
|
|
20
|
+
grid: Union[list[str], None] = None
|
|
21
|
+
) -> None:
|
|
10
22
|
super().__init__(image, max_size)
|
|
11
23
|
if grid is None:
|
|
12
24
|
self.grid = DEFAULT_GRID
|
|
13
25
|
else:
|
|
14
26
|
self.grid = grid
|
|
15
|
-
|
|
27
|
+
|
|
16
28
|
def to_list(self) -> list[str]:
|
|
17
29
|
size = self.size
|
|
18
30
|
image = self._source_wb.resize(self.size)
|
|
@@ -41,5 +53,8 @@ class Image(ascii_base.Image):
|
|
|
41
53
|
return ascii_list
|
|
42
54
|
|
|
43
55
|
class Video(ascii_base.Video):
|
|
44
|
-
|
|
56
|
+
"""
|
|
57
|
+
An ASCII art video in a style that blends old-skool and new-skool elements.
|
|
58
|
+
"""
|
|
59
|
+
def __init__(self, path: str, frame_size: int = 12_000, fps: int = 10) -> None:
|
|
45
60
|
super().__init__(path, Image, frame_size, fps)
|
|
@@ -1,16 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
"""
|
|
2
|
+
ASCII art tools in a style new-skool.
|
|
3
|
+
"""
|
|
4
|
+
from typing import Union
|
|
2
5
|
import PIL.Image
|
|
6
|
+
from . import ascii_base
|
|
3
7
|
|
|
4
8
|
DEFAULT_GRID: list[str] = ["@", "$", "&", "#", "{", "*", "(", "=", ";", ":", ".", " "]
|
|
5
9
|
|
|
6
10
|
class Image(ascii_base.Image):
|
|
7
|
-
|
|
11
|
+
"""
|
|
12
|
+
An ASCII art image in a style new-skool.
|
|
13
|
+
"""
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
image: PIL.Image.Image,
|
|
17
|
+
max_size: int = 100_000,
|
|
18
|
+
grid: Union[list[str], None] = None
|
|
19
|
+
) -> None:
|
|
8
20
|
super().__init__(image, max_size)
|
|
9
21
|
if grid is None:
|
|
10
22
|
self.grid = DEFAULT_GRID
|
|
11
23
|
else:
|
|
12
24
|
self.grid = grid
|
|
13
|
-
|
|
25
|
+
|
|
14
26
|
def to_list(self) -> list[str]:
|
|
15
27
|
size = self.size
|
|
16
28
|
ascii_list = [""] * size[1]
|
|
@@ -22,5 +34,8 @@ class Image(ascii_base.Image):
|
|
|
22
34
|
return ascii_list
|
|
23
35
|
|
|
24
36
|
class Video(ascii_base.Video):
|
|
37
|
+
"""
|
|
38
|
+
An ASCII art video in a style new-skool.
|
|
39
|
+
"""
|
|
25
40
|
def __init__(self, path: str, frame_size: int = 12_000, fps: int = 10):
|
|
26
41
|
super().__init__(path, Image, frame_size, fps)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Contains all the tools necessary to manage and convert images and videos into Old School ASCII art.
|
|
3
3
|
"""
|
|
4
|
-
from . import ascii_base
|
|
5
4
|
import cv2
|
|
6
5
|
import numpy as np
|
|
6
|
+
from . import ascii_base
|
|
7
|
+
|
|
7
8
|
class Image(ascii_base.Image):
|
|
8
9
|
"""
|
|
9
10
|
Class to convert and manipulate a classic image into Old School ASCII art.
|
|
@@ -34,5 +35,8 @@ class Image(ascii_base.Image):
|
|
|
34
35
|
return ascii_list
|
|
35
36
|
|
|
36
37
|
class Video(ascii_base.Video):
|
|
38
|
+
"""
|
|
39
|
+
An ASCII art video in a style old-skool.
|
|
40
|
+
"""
|
|
37
41
|
def __init__(self, path: str, frame_size: int = 12_000, fps: int = 10):
|
|
38
42
|
super().__init__(path, Image, frame_size, fps)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ascii_art_python
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: A Python library and CLI tool for converting images and videos into ASCII art.
|
|
5
5
|
Author-email: Guillem Prieur <prieurguillem38@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://gitlab.pprriieeuurr.fr/guill_prieur/ascii-art-python
|
|
@@ -19,6 +19,8 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
24
|
Classifier: License :: OSI Approved :: MIT License
|
|
23
25
|
Classifier: Operating System :: OS Independent
|
|
24
26
|
Requires-Python: >=3.9
|
|
@@ -29,6 +31,7 @@ Requires-Dist: tqdm>=4.65.0
|
|
|
29
31
|
Requires-Dist: opencv-python-headless>=4.8.0
|
|
30
32
|
Requires-Dist: moviepy>=2.0.0
|
|
31
33
|
Requires-Dist: numpy>=1.24.0
|
|
34
|
+
Requires-Dist: click
|
|
32
35
|
Requires-Dist: importlib_resources; python_version < "3.10"
|
|
33
36
|
Dynamic: license-file
|
|
34
37
|
|
|
@@ -48,41 +51,32 @@ This module provides advanced tools for converting classic images and videos int
|
|
|
48
51
|
## 👀 Examples
|
|
49
52
|
|
|
50
53
|
```
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
{
|
|
67
|
-
{
|
|
68
|
-
{{{{{{{{
|
|
69
|
-
{{{{
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
@@@@@@@@@$$$$$$@@$:.:::::::::::::.=@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
|
78
|
-
@@@@@@@@@@@@@@@@@$:.:::::::::::::::================*@@@@@@@@@@@@@@@@@@
|
|
79
|
-
@@@@@@@@@@@@@@@@@$:::::::::::::::::::::::::::::::::=@@@@@@@@@@@@@@@@@@
|
|
80
|
-
@@@@@@@@@@@@@@@@@$:::::::::::::::::::::::::({(:::::=@@@@@@@@@@@@@@@@@@
|
|
81
|
-
@@@@@@@@@@@@@@@@@$:::::::::::::::::::::::(@@@@@(:::=@@@@@@@@@@@@@@@@@@
|
|
82
|
-
@@@@@@@@@@@@@@@@@@;.:::::::::::::::::::::*@@@@@*:::=@@@@@@@@@@@@@@@@@@
|
|
83
|
-
@@@@@@@@@@@@@@@@@@$::::::::::::::::::::::::*#*:::::#@@@@@@@@@@@@@@@@@@
|
|
84
|
-
@@@@@@@@@@@@@@@@@@@@#:.::::::::::::::::::::::::::(@@@@@@@@@@@@@@@@@@@@
|
|
85
|
-
@@@@@@@@@@@@@@@@@@@@@@@$*;::::::::::::::::::;(#@@@@@@@@@@@@@@@@@@@@@@@
|
|
54
|
+
||||||*{{*||||||
|
|
55
|
+
///{{{****{{{{{{{{{{{\\
|
|
56
|
+
|{{//-\*{{{{{{{{{{{{{{{\
|
|
57
|
+
|*{*| |{{{{{{{{{{{{{{{{|
|
|
58
|
+
|*{{{*-*{{{{{{{{{{{{{{{{{|
|
|
59
|
+
\{{{{{{{{{{{{{{{{{{{{{{{|
|
|
60
|
+
----- /*{{{{{{{{{{{| -----
|
|
61
|
+
//{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{| |::::::\\
|
|
62
|
+
//{*****{{{{{{{{{{{{{{{{{{{{{{{{{{{{| |::::::::\
|
|
63
|
+
-******{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{| |:::::::::-
|
|
64
|
+
-{***{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{| .::::::::::-
|
|
65
|
+
-{**{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{// /:::::::::::
|
|
66
|
+
*{{{{{{{{{{{{{/----------------- .:::::::::::::
|
|
67
|
+
-{{{{{{{{{{{// .----------------:::::::::::::::::
|
|
68
|
+
-{{{{{{{{{{| .......::::::::::::::::::::::::::::-
|
|
69
|
+
-{{{{{{{{{{| |.....::::::::::::::::::::::::::::::-
|
|
70
|
+
|{{{{{{{{{| |....::::::::::::::::::::::::::::::/
|
|
71
|
+
\\{{{{{{{{| |..:::::::::::::::::::::::::::::::/
|
|
72
|
+
\\-{{{{/| |::::::::::::-----------:::::---/
|
|
73
|
+
|:::::::::::.\----------\
|
|
74
|
+
|::::::::::::::::::::::::|
|
|
75
|
+
|::::::::::::::::::.:::::|
|
|
76
|
+
|::::::::::::::::| |:::|
|
|
77
|
+
|::::::::::::::::\-//::|
|
|
78
|
+
\\::::::::::::::::::///
|
|
79
|
+
|||||:::::::||||
|
|
86
80
|
```
|
|
87
81
|
|
|
88
82
|

|
|
@@ -4,12 +4,14 @@ README.md
|
|
|
4
4
|
pyproject.toml
|
|
5
5
|
ascii_art_python/__init__.py
|
|
6
6
|
ascii_art_python/ascii_base.py
|
|
7
|
+
ascii_art_python/cli.py
|
|
7
8
|
ascii_art_python/full_mode.py
|
|
8
9
|
ascii_art_python/new_skool.py
|
|
9
10
|
ascii_art_python/old_skool.py
|
|
10
11
|
ascii_art_python.egg-info/PKG-INFO
|
|
11
12
|
ascii_art_python.egg-info/SOURCES.txt
|
|
12
13
|
ascii_art_python.egg-info/dependency_links.txt
|
|
14
|
+
ascii_art_python.egg-info/entry_points.txt
|
|
13
15
|
ascii_art_python.egg-info/requires.txt
|
|
14
16
|
ascii_art_python.egg-info/top_level.txt
|
|
15
17
|
ascii_art_python/assets/fonts/GoogleSansCode-Regular.ttf
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ascii_art_python"
|
|
7
|
-
version = "1.0
|
|
7
|
+
version = "1.2.0"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="Guillem Prieur", email="prieurguillem38@gmail.com" },
|
|
10
10
|
]
|
|
@@ -37,6 +37,8 @@ classifiers = [
|
|
|
37
37
|
"Programming Language :: Python :: 3.10",
|
|
38
38
|
"Programming Language :: Python :: 3.11",
|
|
39
39
|
"Programming Language :: Python :: 3.12",
|
|
40
|
+
"Programming Language :: Python :: 3.13",
|
|
41
|
+
"Programming Language :: Python :: 3.14",
|
|
40
42
|
"License :: OSI Approved :: MIT License",
|
|
41
43
|
"Operating System :: OS Independent",
|
|
42
44
|
]
|
|
@@ -47,6 +49,7 @@ dependencies = [
|
|
|
47
49
|
"opencv-python-headless>=4.8.0",
|
|
48
50
|
"moviepy>=2.0.0",
|
|
49
51
|
"numpy>=1.24.0",
|
|
52
|
+
"click",
|
|
50
53
|
"importlib_resources; python_version < '3.10'",
|
|
51
54
|
]
|
|
52
55
|
|
|
@@ -54,3 +57,7 @@ dependencies = [
|
|
|
54
57
|
"Homepage" = "https://gitlab.pprriieeuurr.fr/guill_prieur/ascii-art-python"
|
|
55
58
|
"Bug Tracker" = "https://gitlab.pprriieeuurr.fr/guill_prieur/ascii-art-python/-/issues"
|
|
56
59
|
"Source Code" = "https://gitlab.pprriieeuurr.fr/guill_prieur/ascii-art-python"
|
|
60
|
+
|
|
61
|
+
[project.scripts]
|
|
62
|
+
aap-export = "ascii_art_python.cli:create_and_export"
|
|
63
|
+
aap-echo = "ascii_art_python.cli:create_and_echo"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/assets/fonts/KreativeSquareSM.ttf
RENAMED
|
File without changes
|
{ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|