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.
Files changed (20) hide show
  1. {ascii_art_python-1.0.1/ascii_art_python.egg-info → ascii_art_python-1.2.0}/PKG-INFO +30 -36
  2. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/README.md +26 -35
  3. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/__init__.py +1 -1
  4. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/ascii_base.py +117 -59
  5. ascii_art_python-1.2.0/ascii_art_python/cli.py +101 -0
  6. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/full_mode.py +19 -4
  7. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/new_skool.py +18 -3
  8. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/old_skool.py +5 -1
  9. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0/ascii_art_python.egg-info}/PKG-INFO +30 -36
  10. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python.egg-info/SOURCES.txt +2 -0
  11. ascii_art_python-1.2.0/ascii_art_python.egg-info/entry_points.txt +3 -0
  12. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python.egg-info/requires.txt +1 -0
  13. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/pyproject.toml +8 -1
  14. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/LICENSE +0 -0
  15. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/MANIFEST.in +0 -0
  16. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/assets/fonts/GoogleSansCode-Regular.ttf +0 -0
  17. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python/assets/fonts/KreativeSquareSM.ttf +0 -0
  18. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python.egg-info/dependency_links.txt +0 -0
  19. {ascii_art_python-1.0.1 → ascii_art_python-1.2.0}/ascii_art_python.egg-info/top_level.txt +0 -0
  20. {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.1
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
  ![Example with the Nyan Cat video played in a terminal.](https://media0.giphy.com/media/v1.Y2lkPTc5MGI3NjExczN5eXo0cHRyMnRzZjVxOHI4NXN1cjc2ZzIyNHQ5cTkyMW8weHVhYSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/n0MEinoLqWEdHb129d/giphy.gif)
@@ -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
  ![Example with the Nyan Cat video played in a terminal.](https://media0.giphy.com/media/v1.Y2lkPTc5MGI3NjExczN5eXo0cHRyMnRzZjVxOHI4NXN1cjc2ZzIyNHQ5cTkyMW8weHVhYSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/n0MEinoLqWEdHb129d/giphy.gif)
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Module from https://gitlab.pprriieeuurr.fr/guill_prieur/ascii-art-python
3
3
  """
4
- __version__ = "1.0.1"
4
+ __version__ = "1.2.0"
5
5
  from . import new_skool
6
6
  from . import old_skool
7
7
  from . import full_mode
@@ -1,23 +1,26 @@
1
1
  """
2
- Contains all the tools necessary to manage and convert images and videos into ASCII art of all types.
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, PIL.ImageDraw, PIL.ImageFont
13
- # pylint: disable=no-member
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
- import time
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
- SECOND_FONT = resources.files("ascii_art_python.assets.fonts").joinpath("GoogleSansCode-Regular.ttf")
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
- Do not return anything.
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
- content = content.split("\n")
171
+ content_list = content.split("\n")
163
172
  try:
164
- font = PIL.ImageFont.truetype(SECOND_FONT, font_size)
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(content, key=len)
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(content) * font_size)
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(content):
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(content) * font_size)
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: VideoFileClip = target_video.with_audio(audio)
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
- lines_lenght, time_between = int(metadata[0]), float(metadata[1])
257
+ lines_length, time_between = int(metadata[0]), float(metadata[1])
239
258
  next_sleep = time.time() + time_between
240
- for i in range(len(lines) // lines_lenght):
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*lines_lenght:(i+1) * lines_lenght]))
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[Image], frame_size: int = 12_000, fps: int = 10) -> None:
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
- def __iter__(self) -> iter:
252
- cap = cv2.VideoCapture(self.path)
271
+ self._cap = None
272
+ self._frame_skip = 1
273
+ self._frame_count = 0
253
274
 
254
- if not cap.isOpened():
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
- frame_skip = max(1, int(self.fps / self.max_fps))
258
- frame_count = 0
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 = cap.read()
300
+ succes, frame = self._cap.read()
262
301
 
263
302
  if not succes:
264
- break
303
+ self._cap.release()
304
+ self._cap = None
305
+ raise StopIteration
265
306
 
266
- if frame_count % frame_skip == 0:
267
- yield self.cls(
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
- frame_count += 1
272
- cap.release()
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
- lenght = int(cap.get(cv2.CAP_PROP_FRAME_COUNT) / frame_skip)
331
+ length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT) / frame_skip)
282
332
  cap.release()
283
- return lenght
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(self, filename: str = "mika_export", export_type: ExportType = 0, quality: ExportQuality = 0, sound: bool = False):
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 : ExportType, optional
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
- Do not return anything.
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(iter(self), "Exporting frames", len(self)):
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(*'FFV1')
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(*'mp4v')
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(iter(self), "Exporting frames", len(self)):
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 and quality:
376
- self.transfer_audio(self.path, filename + ".avi", filename + "_audio.avi")
377
- elif sound:
378
- self.transfer_audio(self.path, filename + ".mp4", filename + "_audio.mp4")
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
- Do not return anything.
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
- for frame in iter(self):
448
+
449
+ for frame in self:
392
450
  time.sleep(max(next_sleep - time.time(), 0))
393
- print(frame.get_alternate_lines())
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
- from . import ascii_base
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
- def __init__(self, image: PIL.Image.Image, max_size: int = 100_000, grid: list[str] = None) -> None:
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
- def __init__(self, path: str, frame_size: int = 12_000, fps: int = 10):
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
- from . import ascii_base
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
- def __init__(self, image: PIL.Image.Image, max_size: int = 100_000, grid: list[str] = None) -> None:
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.1
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
  ![Example with the Nyan Cat video played in a terminal.](https://media0.giphy.com/media/v1.Y2lkPTc5MGI3NjExczN5eXo0cHRyMnRzZjVxOHI4NXN1cjc2ZzIyNHQ5cTkyMW8weHVhYSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/n0MEinoLqWEdHb129d/giphy.gif)
@@ -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
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ aap-echo = ascii_art_python.cli:create_and_echo
3
+ aap-export = ascii_art_python.cli:create_and_export
@@ -3,6 +3,7 @@ tqdm>=4.65.0
3
3
  opencv-python-headless>=4.8.0
4
4
  moviepy>=2.0.0
5
5
  numpy>=1.24.0
6
+ click
6
7
 
7
8
  [:python_version < "3.10"]
8
9
  importlib_resources
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ascii_art_python"
7
- version = "1.0.1"
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"