manim-captcha 1.0.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jose
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.4
2
+ Name: manim-captcha
3
+ Version: 1.0.0
4
+ Summary: Animated visual captcha generation library using Manim.
5
+ Author: Jose Miguel Rios Rubio
6
+ License: MIT
7
+ Keywords: captcha,manim,animation,video,gif,mp4
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.12
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: manim==0.19.2
15
+ Dynamic: license-file
16
+
17
+ # Manim-Captcha
18
+
19
+ <div style="display: flex;">
20
+ <img src="https://github.com/user-attachments/assets/62677f79-c7a3-49ad-b01a-0143e48cb7ae" alt="captcha_circle" width="270" />
21
+ <img src="https://github.com/user-attachments/assets/223b7e2d-99c4-410e-ab6e-4966ad47d2e8" alt="captcha_matrix" width="270" />
22
+ <img src="https://github.com/user-attachments/assets/3c606acd-24bf-45bf-8a45-6127f392ea73" alt="captcha_piramid" width="270" />
23
+ </div>
24
+
25
+ ## Overview
26
+
27
+ Animated visual captcha generation library using [Manim Framework](https://github.com/ManimCommunity/manim).
28
+
29
+ Traditional CAPTCHAs are static images. Manim-Captcha generates animated visual challenges (e.g., moving selectors, distributed numbers, dynamic noise).
30
+
31
+ This library offers:
32
+
33
+ - A "Captcha Generator" that allows to request generation of captcha files.
34
+ - A "Captcha Automatic Generator" for async operation that acts as a background process to automatically generate captcha files in the filesystem for a custom interval and keeping a maximum number of captchas files (file rotation).
35
+ - Some builtin captchas animations that you can select to use.
36
+ - Kind of "plugin" based system were you can provide any external custom Manim captcha animation script (scene) and make the library use it.
37
+ - Easy way to pass custom properties to the captcha scenes for customization (like scenes).
38
+
39
+ ## Table of Contents
40
+
41
+ - [Requirements](#requirements)
42
+ - [Installation](#installation)
43
+ - [Usage](#usage)
44
+ - [Library Development](#library-development)
45
+
46
+ ## Requirements
47
+
48
+ The manim-captcha library requires python 3 (tested with v3.12), Manim Community framework, and the corresponding requirements for Manim (like Cairo and Pango libraries).
49
+
50
+ ## Installation
51
+
52
+ **Note:** The next installation instructions are for Debian based Linux systems.
53
+
54
+ From a command line, install the different requirements:
55
+
56
+ ```bash
57
+ sudo apt-get update
58
+ sudo apt-get install build-essential make python3 python3-dev python3-pip
59
+ sudo apt-get install libcairo2-dev libpango1.0-dev
60
+ ```
61
+
62
+ The library has been published to pypi so can be easily installed via pip:
63
+
64
+ ```bash
65
+ pip install manim-captcha
66
+ ```
67
+
68
+ ## Usage
69
+
70
+ Basic captcha generator:
71
+
72
+ ```python
73
+ from manim_captcha.generator import CaptchaGenerator
74
+ from manim_captcha.scenes import CaptchaScene
75
+ from pathlib import Path
76
+
77
+ generator = CaptchaGenerator()
78
+
79
+ captcha = generator.generate(
80
+ code="1234",
81
+ scene=CaptchaScene.CIRCLE_NUMS,
82
+ out_dir=Path("./captchas"),
83
+ properties={
84
+ "theme": "dark",
85
+ "noise": True
86
+ }
87
+ )
88
+
89
+ if captcha.error:
90
+ print("Fail to create the captcha:")
91
+ print(captcha.error_info)
92
+ else:
93
+ print("Captcha successfully created")
94
+ print(f" Code: \"{captcha.code}\"")
95
+ print(f" File: {captcha.file}")
96
+ print("")
97
+ ```
98
+
99
+ Async and non-blocking automatic captcha generator:
100
+
101
+ ```python
102
+ import asyncio
103
+ import logging
104
+ from manim_captcha.auto_generator import CaptchaAutoGenerator
105
+ from pathlib import Path
106
+
107
+ logging.basicConfig(
108
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
109
+ level=logging.INFO
110
+ )
111
+ logger = logging.getLogger(__name__)
112
+
113
+ async def main():
114
+ # Setup the CaptchaAutoGenerator
115
+ MAX_NUM_CAPTCHAS = 5
116
+ TIME_GEN_INTERVAL_S = 10
117
+ OUT_DIR = Path("./captchas")
118
+ auto_generator = CaptchaAutoGenerator(
119
+ OUT_DIR, TIME_GEN_INTERVAL_S, MAX_NUM_CAPTCHAS)
120
+ # Start the generator process
121
+ start_success = await auto_generator.start()
122
+ if not start_success:
123
+ logger.error("Fail to Start CaptchaAutoGenerator")
124
+ return
125
+ # Wait and get some captchas during 1 minute
126
+ TIME_CHECK = 60
127
+ time_pass_s = 0
128
+ run = True
129
+ while run:
130
+ # Show number of captchas availables
131
+ num_captchas = auto_generator.num_captchas()
132
+ logger.info("Num Captchas Availables: %d", num_captchas)
133
+ # Try to get a captcha
134
+ captcha = auto_generator.get_captcha()
135
+ if not captcha.error:
136
+ logger.info("Captcha retrieved:")
137
+ logger.info(" Code: \"%s\"", captcha.code)
138
+ logger.info(" File: %s", str(captcha.file))
139
+ await asyncio.sleep(TIME_GEN_INTERVAL_S)
140
+ # Check if check end time has arrive to exit the loop
141
+ time_pass_s = time_pass_s + TIME_GEN_INTERVAL_S
142
+ if time_pass_s >= TIME_CHECK:
143
+ run = False
144
+ # Stop the generator process
145
+ await auto_generator.stop()
146
+
147
+ if __name__ == '__main__':
148
+ asyncio.run(main())
149
+ ```
150
+
151
+ **Note:** You can find some extra usages in the *examples/* directory.
152
+
153
+ ## Library Development
154
+
155
+ For library development, to ease project setup and installation of all the requirements, a Makefile and some bash scripts are provided for Linux systems based on Debian.
156
+
157
+ Setup the project (this will install all system requirements and creates a python virtual environment):
158
+
159
+ ```bash
160
+ make setup
161
+ ```
162
+
163
+ Extra actions can be performed through make, just run make without argments to see the usage help:
164
+
165
+ ```bash
166
+ make
167
+
168
+ Usage:
169
+ setup: Setup Project and install requirements
170
+ test: Run project tests
171
+ check_code_style: Run Code Style Checks
172
+ check_static_types: Run Static Types Checks
173
+ install: Install local package in edit mode
174
+ publish: Publish the library to pypi
175
+ ```
176
+
177
+ Now open any code editor (i.e. vscode) withing the python environment and start coding!
@@ -0,0 +1,161 @@
1
+ # Manim-Captcha
2
+
3
+ <div style="display: flex;">
4
+ <img src="https://github.com/user-attachments/assets/62677f79-c7a3-49ad-b01a-0143e48cb7ae" alt="captcha_circle" width="270" />
5
+ <img src="https://github.com/user-attachments/assets/223b7e2d-99c4-410e-ab6e-4966ad47d2e8" alt="captcha_matrix" width="270" />
6
+ <img src="https://github.com/user-attachments/assets/3c606acd-24bf-45bf-8a45-6127f392ea73" alt="captcha_piramid" width="270" />
7
+ </div>
8
+
9
+ ## Overview
10
+
11
+ Animated visual captcha generation library using [Manim Framework](https://github.com/ManimCommunity/manim).
12
+
13
+ Traditional CAPTCHAs are static images. Manim-Captcha generates animated visual challenges (e.g., moving selectors, distributed numbers, dynamic noise).
14
+
15
+ This library offers:
16
+
17
+ - A "Captcha Generator" that allows to request generation of captcha files.
18
+ - A "Captcha Automatic Generator" for async operation that acts as a background process to automatically generate captcha files in the filesystem for a custom interval and keeping a maximum number of captchas files (file rotation).
19
+ - Some builtin captchas animations that you can select to use.
20
+ - Kind of "plugin" based system were you can provide any external custom Manim captcha animation script (scene) and make the library use it.
21
+ - Easy way to pass custom properties to the captcha scenes for customization (like scenes).
22
+
23
+ ## Table of Contents
24
+
25
+ - [Requirements](#requirements)
26
+ - [Installation](#installation)
27
+ - [Usage](#usage)
28
+ - [Library Development](#library-development)
29
+
30
+ ## Requirements
31
+
32
+ The manim-captcha library requires python 3 (tested with v3.12), Manim Community framework, and the corresponding requirements for Manim (like Cairo and Pango libraries).
33
+
34
+ ## Installation
35
+
36
+ **Note:** The next installation instructions are for Debian based Linux systems.
37
+
38
+ From a command line, install the different requirements:
39
+
40
+ ```bash
41
+ sudo apt-get update
42
+ sudo apt-get install build-essential make python3 python3-dev python3-pip
43
+ sudo apt-get install libcairo2-dev libpango1.0-dev
44
+ ```
45
+
46
+ The library has been published to pypi so can be easily installed via pip:
47
+
48
+ ```bash
49
+ pip install manim-captcha
50
+ ```
51
+
52
+ ## Usage
53
+
54
+ Basic captcha generator:
55
+
56
+ ```python
57
+ from manim_captcha.generator import CaptchaGenerator
58
+ from manim_captcha.scenes import CaptchaScene
59
+ from pathlib import Path
60
+
61
+ generator = CaptchaGenerator()
62
+
63
+ captcha = generator.generate(
64
+ code="1234",
65
+ scene=CaptchaScene.CIRCLE_NUMS,
66
+ out_dir=Path("./captchas"),
67
+ properties={
68
+ "theme": "dark",
69
+ "noise": True
70
+ }
71
+ )
72
+
73
+ if captcha.error:
74
+ print("Fail to create the captcha:")
75
+ print(captcha.error_info)
76
+ else:
77
+ print("Captcha successfully created")
78
+ print(f" Code: \"{captcha.code}\"")
79
+ print(f" File: {captcha.file}")
80
+ print("")
81
+ ```
82
+
83
+ Async and non-blocking automatic captcha generator:
84
+
85
+ ```python
86
+ import asyncio
87
+ import logging
88
+ from manim_captcha.auto_generator import CaptchaAutoGenerator
89
+ from pathlib import Path
90
+
91
+ logging.basicConfig(
92
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
93
+ level=logging.INFO
94
+ )
95
+ logger = logging.getLogger(__name__)
96
+
97
+ async def main():
98
+ # Setup the CaptchaAutoGenerator
99
+ MAX_NUM_CAPTCHAS = 5
100
+ TIME_GEN_INTERVAL_S = 10
101
+ OUT_DIR = Path("./captchas")
102
+ auto_generator = CaptchaAutoGenerator(
103
+ OUT_DIR, TIME_GEN_INTERVAL_S, MAX_NUM_CAPTCHAS)
104
+ # Start the generator process
105
+ start_success = await auto_generator.start()
106
+ if not start_success:
107
+ logger.error("Fail to Start CaptchaAutoGenerator")
108
+ return
109
+ # Wait and get some captchas during 1 minute
110
+ TIME_CHECK = 60
111
+ time_pass_s = 0
112
+ run = True
113
+ while run:
114
+ # Show number of captchas availables
115
+ num_captchas = auto_generator.num_captchas()
116
+ logger.info("Num Captchas Availables: %d", num_captchas)
117
+ # Try to get a captcha
118
+ captcha = auto_generator.get_captcha()
119
+ if not captcha.error:
120
+ logger.info("Captcha retrieved:")
121
+ logger.info(" Code: \"%s\"", captcha.code)
122
+ logger.info(" File: %s", str(captcha.file))
123
+ await asyncio.sleep(TIME_GEN_INTERVAL_S)
124
+ # Check if check end time has arrive to exit the loop
125
+ time_pass_s = time_pass_s + TIME_GEN_INTERVAL_S
126
+ if time_pass_s >= TIME_CHECK:
127
+ run = False
128
+ # Stop the generator process
129
+ await auto_generator.stop()
130
+
131
+ if __name__ == '__main__':
132
+ asyncio.run(main())
133
+ ```
134
+
135
+ **Note:** You can find some extra usages in the *examples/* directory.
136
+
137
+ ## Library Development
138
+
139
+ For library development, to ease project setup and installation of all the requirements, a Makefile and some bash scripts are provided for Linux systems based on Debian.
140
+
141
+ Setup the project (this will install all system requirements and creates a python virtual environment):
142
+
143
+ ```bash
144
+ make setup
145
+ ```
146
+
147
+ Extra actions can be performed through make, just run make without argments to see the usage help:
148
+
149
+ ```bash
150
+ make
151
+
152
+ Usage:
153
+ setup: Setup Project and install requirements
154
+ test: Run project tests
155
+ check_code_style: Run Code Style Checks
156
+ check_static_types: Run Static Types Checks
157
+ install: Install local package in edit mode
158
+ publish: Publish the library to pypi
159
+ ```
160
+
161
+ Now open any code editor (i.e. vscode) withing the python environment and start coding!
@@ -0,0 +1,29 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "manim-captcha"
7
+ version = "1.0.0"
8
+ description = "Animated visual captcha generation library using Manim."
9
+ authors = [
10
+ { name="Jose Miguel Rios Rubio" }
11
+ ]
12
+ readme = "README.md"
13
+ license = { text = "MIT" }
14
+ keywords = ["captcha", "manim", "animation", "video", "gif", "mp4"]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent"
19
+ ]
20
+ requires-python = ">=3.12"
21
+ dependencies = [
22
+ "manim==0.19.2"
23
+ ]
24
+
25
+ [tool.setuptools]
26
+ package-dir = {"" = "src"}
27
+
28
+ [tool.setuptools.packages.find]
29
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,234 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Script:
6
+ auto_generator.py
7
+ Description:
8
+ Automatic captcha generation manager.
9
+ This component act as a producer that periodically creates and store
10
+ random Manim Captchas into a directory of the filesystem, allowing
11
+ external consumers (users of the library) to request any of these
12
+ captchas. It keeps control of the number of generated captchas, and
13
+ apply some kind of "files rotation" of the storage, removing and
14
+ replacing older captchas with newer ones to control and limit the
15
+ maximum number of captchas in the filesytem.
16
+ Author:
17
+ Jose Miguel Rios Rubio
18
+ Creation date:
19
+ 15/02/2026
20
+ Last modified date:
21
+ 16/02/2026
22
+ Version:
23
+ 1.0.0
24
+ """
25
+
26
+ ###############################################################################
27
+ # Standard Libraries
28
+ ###############################################################################
29
+
30
+ import asyncio
31
+ import logging
32
+ import secrets
33
+
34
+ from pathlib import Path
35
+ from traceback import format_exc
36
+
37
+
38
+ ###############################################################################
39
+ # Third-Party Libraries
40
+ ###############################################################################
41
+
42
+ # None
43
+
44
+
45
+ ###############################################################################
46
+ # Local Libraries
47
+ ###############################################################################
48
+
49
+ from .data import CaptchaData
50
+ from .generator import CaptchaGenerator
51
+
52
+
53
+ ###############################################################################
54
+ # Logger Setup
55
+ ###############################################################################
56
+
57
+ logger = logging.getLogger(__name__)
58
+
59
+
60
+ ###############################################################################
61
+ # Class ManimCaptchaGenerator
62
+ ###############################################################################
63
+
64
+ class CaptchaAutoGenerator:
65
+ """Automatic captcha generation manager."""
66
+
67
+ ###########################################################################
68
+
69
+ ### Constants ###
70
+
71
+ # None
72
+
73
+ ###########################################################################
74
+
75
+ ### Data Types ###
76
+
77
+ # None
78
+
79
+ ###########################################################################
80
+
81
+ ### Constructor ###
82
+
83
+ def __init__(self,
84
+ out_dir: Path = Path("./captchas"),
85
+ interval_s: int = 60,
86
+ max_items: int = 100):
87
+ '''
88
+ Automatic captcha generation manager construction.
89
+ Arguments:
90
+ - out_dir: Output directory to place the generated captchas.
91
+ - interval: Time for creation of each new Captcha (in seconds).
92
+ - max_items: Maximum number of Captchas allowed in the
93
+ filesystem (once this number of captchas are generated, the file
94
+ rotate mechanism will be replacing older captchas with newer
95
+ ones).
96
+ '''
97
+ self.out_dir = out_dir
98
+ self.interval_s = interval_s
99
+ self.max_items = max_items
100
+ self.format: str = "mp4"
101
+ self._task_create: asyncio.Task | None = None
102
+ self._task_remove: asyncio.Task | None = None
103
+ self._running: bool = False
104
+ self._lock = asyncio.Lock()
105
+ self._generator = CaptchaGenerator()
106
+ self.available: bool = self._generator.is_available()
107
+
108
+ ###########################################################################
109
+
110
+ ### Public Methods ###
111
+
112
+ async def start(self) -> bool:
113
+ '''Launch the manager to start creating captchas.'''
114
+ if not self.available:
115
+ return False
116
+ if self._running:
117
+ return False
118
+ self._running = True
119
+ self._task_create = asyncio.create_task(self._producer_loop())
120
+ self._task_remove = asyncio.create_task(self._remove_loop())
121
+ return True
122
+
123
+ async def stop(self):
124
+ '''Stop the manager and the captcha creation.'''
125
+ self._running = False
126
+ if self._task_create:
127
+ await self._task_create
128
+ self._task_create = None
129
+ if self._task_remove:
130
+ await self._task_remove
131
+ self._task_remove = None
132
+
133
+ def is_running(self) -> bool:
134
+ '''Get Captcha Generator manager running status.'''
135
+ return self._running
136
+
137
+ def num_captchas(self) -> int:
138
+ '''
139
+ Get the number of generated captcha files availables in the
140
+ file system.
141
+ '''
142
+ try:
143
+ return sum(1 for _ in self.out_dir.glob(f"*.{self.format}"))
144
+ except Exception:
145
+ logger.exception("Fail to get number of captchas files available")
146
+ return 0
147
+
148
+ def get_captcha(self) -> CaptchaData:
149
+ '''Return a random captcha file from storage.'''
150
+ captcha_result = CaptchaData()
151
+ if not self.available:
152
+ captcha_result.error = True
153
+ captcha_result.error_info = "Manim not found in the system"
154
+ return captcha_result
155
+ try:
156
+ files = list(self.out_dir.glob(f"*.{self.format}"))
157
+ if not files:
158
+ captcha_result.error = True
159
+ captcha_result.error_info = "None captcha files generated"
160
+ return captcha_result
161
+ captcha_result.file = secrets.choice(files)
162
+ captcha_result.code = captcha_result.file.stem
163
+ except Exception:
164
+ logger.error(format_exc())
165
+ captcha_result.error = True
166
+ captcha_result.error_info = "Fail to get captcha from filesystem"
167
+ return captcha_result
168
+
169
+ ###########################################################################
170
+
171
+ ### Private Methods - Create Captchas ###
172
+
173
+ async def _producer_loop(self):
174
+ """Internal async captcha creation producer loop."""
175
+ while self._running:
176
+ try:
177
+ async with self._lock:
178
+ await self._generate_captcha()
179
+ except Exception:
180
+ logger.error(format_exc())
181
+ await asyncio.sleep(self.interval_s)
182
+
183
+ async def _generate_captcha(self):
184
+ """Generate a captcha."""
185
+ loop = asyncio.get_running_loop()
186
+ result = await loop.run_in_executor(
187
+ None,
188
+ self._generator.generate,
189
+ None, # code
190
+ None, # scene
191
+ self.out_dir
192
+ )
193
+ if result.error:
194
+ logger.error("Captcha generation failed: %s", result.error_info)
195
+ else:
196
+ logger.debug("Generated captcha: %s", result.file)
197
+
198
+ ###########################################################################
199
+
200
+ ### Private Methods - Remove Captchas ###
201
+
202
+ async def _remove_loop(self):
203
+ """Internal async captcha remove/rotate loop."""
204
+ while self._running:
205
+ try:
206
+ async with self._lock:
207
+ self._process_remove()
208
+ except Exception:
209
+ logger.error(format_exc())
210
+ await asyncio.sleep(self.interval_s)
211
+
212
+ def _process_remove(self):
213
+ """Remove oldest file if limit exceeded."""
214
+ # Dont remove if number of captchas is less than max_items limit
215
+ files = list(self.out_dir.glob(f"*.{self.format}"))
216
+ if len(files) < self.max_items:
217
+ return
218
+ # Remove oldest captcha file
219
+ files.sort(key=lambda p: p.stat().st_mtime)
220
+ while len(files) > self.max_items:
221
+ oldest = files.pop(0)
222
+ try:
223
+ # Atomic rename (protect race condition from consumers)
224
+ # and delete the file
225
+ deleting = oldest.with_suffix(oldest.suffix + ".deleting")
226
+ oldest.rename(deleting)
227
+ deleting.unlink(missing_ok=True)
228
+ logger.info("Removed old captcha: %s", oldest)
229
+ except PermissionError:
230
+ logger.warning("File in use, skipping removal: %s", oldest)
231
+ except Exception:
232
+ logger.error(format_exc())
233
+
234
+ ###############################################################################