├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ └── lint_python.yml
├── .gitignore
├── .pylintrc
├── Dockerfile
├── LICENSE
├── MANIFEST.in
├── README.md
├── bin
└── video-to-ascii
├── images
├── 8-bit_color_table.png
├── Luminosity.svg
├── LuminosityFunction.svg
├── Simpsons.apng
├── Simpsons.gif
├── Simpsonsv2.gif
├── Strategies.png
├── display-colors.jpg
├── export.png
├── imgBrightnes.png
├── imgPixelImage.png
├── imgPixelSection.png
├── imgResizing.png
├── imgTerminal.png
├── imgVideoFrames.png
├── logo.png
└── logo.svg
├── requirements.txt
├── setup.py
├── translations
├── README_es.md
└── README_zh-TW.md
└── video_to_ascii
├── __init__.py
├── cli.py
├── player.py
├── render_strategy
├── __init__.py
├── ascii_bw_strategy.py
├── ascii_color_filled_strategy.py
├── ascii_color_strategy.py
├── ascii_strategy.py
├── image_processor.py
├── image_processor_win.py
└── render_strategy.py
└── video_engine.py
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | open_collective: video-to-ascii
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/lint_python.yml:
--------------------------------------------------------------------------------
1 | name: lint_python
2 | on: [pull_request, push]
3 | jobs:
4 | lint_python:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v2
8 | - uses: actions/setup-python@v2
9 | - run: pip install bandit black codespell flake8 isort mypy pytest pyupgrade safety
10 | - run: bandit --recursive --skip B101 . || true # B101 is assert statements
11 | - run: black --check . || true
12 | - run: codespell || true # --ignore-words-list="" --skip=""
13 | - run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
14 | - run: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 --show-source --statistics
15 | - run: isort --check-only --profile black . || true
16 | - run: pip install -r requirements.txt || true
17 | - run: mypy --ignore-missing-imports . || true
18 | - run: pytest . || true
19 | - run: pytest --doctest-modules . || true
20 | - run: shopt -s globstar && pyupgrade --py36-plus **/*.py || true
21 | - run: safety check
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .pyc
2 | .env
3 | .DS_Store
4 | *.pyc
5 | videos
6 | .vscode
7 | dist
8 | video_to_ascii.egg-info
9 | build/
10 | test.py
11 | env/
--------------------------------------------------------------------------------
/.pylintrc:
--------------------------------------------------------------------------------
1 | [MASTER]
2 |
3 | # A comma-separated list of package or module names from where C extensions may
4 | # be loaded. Extensions are loading into the active Python interpreter and may
5 | # run arbitrary code
6 | extension-pkg-whitelist=cv2
7 |
8 | # Add files or directories to the blacklist. They should be base names, not
9 | # paths.
10 | ignore=CVS
11 |
12 | # Add files or directories matching the regex patterns to the blacklist. The
13 | # regex matches against base names, not paths.
14 | ignore-patterns=
15 |
16 | # Python code to execute, usually for sys.path manipulation such as
17 | # pygtk.require().
18 | #init-hook=
19 |
20 | # Use multiple processes to speed up Pylint.
21 | jobs=1
22 |
23 | # List of plugins (as comma separated values of python modules names) to load,
24 | # usually to register additional checkers.
25 | load-plugins=
26 |
27 | # Pickle collected data for later comparisons.
28 | persistent=yes
29 |
30 | # Specify a configuration file.
31 | #rcfile=
32 |
33 | # When enabled, pylint would attempt to guess common misconfiguration and emit
34 | # user-friendly hints instead of false-positive error messages
35 | suggestion-mode=yes
36 |
37 | # Allow loading of arbitrary C extensions. Extensions are imported into the
38 | # active Python interpreter and may run arbitrary code.
39 | unsafe-load-any-extension=no
40 |
41 |
42 | [MESSAGES CONTROL]
43 |
44 | # Only show warnings with the listed confidence levels. Leave empty to show
45 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
46 | confidence=
47 |
48 | # Disable the message, report, category or checker with the given id(s). You
49 | # can either give multiple identifiers separated by comma (,) or put this
50 | # option multiple times (only on the command line, not in the configuration
51 | # file where it should appear only once).You can also use "--disable=all" to
52 | # disable everything first and then reenable specific checks. For example, if
53 | # you want to run only the similarities checker, you can use "--disable=all
54 | # --enable=similarities". If you want to run only the classes checker, but have
55 | # no Warning level messages displayed, use"--disable=all --enable=classes
56 | # --disable=W"
57 | disable=print-statement,
58 | parameter-unpacking,
59 | unpacking-in-except,
60 | old-raise-syntax,
61 | backtick,
62 | long-suffix,
63 | old-ne-operator,
64 | old-octal-literal,
65 | import-star-module-level,
66 | non-ascii-bytes-literal,
67 | invalid-unicode-literal,
68 | raw-checker-failed,
69 | bad-inline-option,
70 | locally-disabled,
71 | locally-enabled,
72 | file-ignored,
73 | suppressed-message,
74 | useless-suppression,
75 | deprecated-pragma,
76 | apply-builtin,
77 | basestring-builtin,
78 | buffer-builtin,
79 | cmp-builtin,
80 | coerce-builtin,
81 | execfile-builtin,
82 | file-builtin,
83 | long-builtin,
84 | raw_input-builtin,
85 | reduce-builtin,
86 | standarderror-builtin,
87 | unicode-builtin,
88 | xrange-builtin,
89 | coerce-method,
90 | delslice-method,
91 | getslice-method,
92 | setslice-method,
93 | no-absolute-import,
94 | old-division,
95 | dict-iter-method,
96 | dict-view-method,
97 | next-method-called,
98 | metaclass-assignment,
99 | indexing-exception,
100 | raising-string,
101 | reload-builtin,
102 | oct-method,
103 | hex-method,
104 | nonzero-method,
105 | cmp-method,
106 | input-builtin,
107 | round-builtin,
108 | intern-builtin,
109 | unichr-builtin,
110 | map-builtin-not-iterating,
111 | zip-builtin-not-iterating,
112 | range-builtin-not-iterating,
113 | filter-builtin-not-iterating,
114 | using-cmp-argument,
115 | eq-without-hash,
116 | div-method,
117 | idiv-method,
118 | rdiv-method,
119 | exception-message-attribute,
120 | invalid-str-codec,
121 | sys-max-int,
122 | bad-python3-import,
123 | deprecated-string-function,
124 | deprecated-str-translate-call,
125 | deprecated-itertools-function,
126 | deprecated-types-field,
127 | next-method-defined,
128 | dict-items-not-iterating,
129 | dict-keys-not-iterating,
130 | dict-values-not-iterating,
131 | deprecated-operator-function,
132 | deprecated-urllib-function,
133 | xreadlines-attribute,
134 | deprecated-sys-function,
135 | exception-escape,
136 | comprehension-escape
137 |
138 | # Enable the message, report, category or checker with the given id(s). You can
139 | # either give multiple identifier separated by comma (,) or put this option
140 | # multiple time (only on the command line, not in the configuration file where
141 | # it should appear only once). See also the "--disable" option for examples.
142 | enable=c-extension-no-member
143 |
144 |
145 | [REPORTS]
146 |
147 | # Python expression which should return a note less than 10 (10 is the highest
148 | # note). You have access to the variables errors warning, statement which
149 | # respectively contain the number of errors / warnings messages and the total
150 | # number of statements analyzed. This is used by the global evaluation report
151 | # (RP0004).
152 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
153 |
154 | # Template used to display messages. This is a python new-style format string
155 | # used to format the message information. See doc for all details
156 | #msg-template=
157 |
158 | # Set the output format. Available formats are text, parseable, colorized, json
159 | # and msvs (visual studio).You can also give a reporter class, eg
160 | # mypackage.mymodule.MyReporterClass.
161 | output-format=text
162 |
163 | # Tells whether to display a full report or only the messages
164 | reports=no
165 |
166 | # Activate the evaluation score.
167 | score=yes
168 |
169 |
170 | [REFACTORING]
171 |
172 | # Maximum number of nested blocks for function / method body
173 | max-nested-blocks=5
174 |
175 | # Complete name of functions that never returns. When checking for
176 | # inconsistent-return-statements if a never returning function is called then
177 | # it will be considered as an explicit return statement and no message will be
178 | # printed.
179 | never-returning-functions=optparse.Values,sys.exit
180 |
181 |
182 | [LOGGING]
183 |
184 | # Logging modules to check that the string format arguments are in logging
185 | # function parameter format
186 | logging-modules=logging
187 |
188 |
189 | [SPELLING]
190 |
191 | # Limits count of emitted suggestions for spelling mistakes
192 | max-spelling-suggestions=4
193 |
194 | # Spelling dictionary name. Available dictionaries: none. To make it working
195 | # install python-enchant package.
196 | spelling-dict=
197 |
198 | # List of comma separated words that should not be checked.
199 | spelling-ignore-words=
200 |
201 | # A path to a file that contains private dictionary; one word per line.
202 | spelling-private-dict-file=
203 |
204 | # Tells whether to store unknown words to indicated private dictionary in
205 | # --spelling-private-dict-file option instead of raising a message.
206 | spelling-store-unknown-words=no
207 |
208 |
209 | [MISCELLANEOUS]
210 |
211 | # List of note tags to take in consideration, separated by a comma.
212 | notes=FIXME,
213 | XXX,
214 | TODO
215 |
216 |
217 | [SIMILARITIES]
218 |
219 | # Ignore comments when computing similarities.
220 | ignore-comments=yes
221 |
222 | # Ignore docstrings when computing similarities.
223 | ignore-docstrings=yes
224 |
225 | # Ignore imports when computing similarities.
226 | ignore-imports=no
227 |
228 | # Minimum lines number of a similarity.
229 | min-similarity-lines=4
230 |
231 |
232 | [TYPECHECK]
233 |
234 | # List of decorators that produce context managers, such as
235 | # contextlib.contextmanager. Add to this list to register other decorators that
236 | # produce valid context managers.
237 | contextmanager-decorators=contextlib.contextmanager
238 |
239 | # List of members which are set dynamically and missed by pylint inference
240 | # system, and so shouldn't trigger E1101 when accessed. Python regular
241 | # expressions are accepted.
242 | generated-members=
243 |
244 | # Tells whether missing members accessed in mixin class should be ignored. A
245 | # mixin class is detected if its name ends with "mixin" (case insensitive).
246 | ignore-mixin-members=yes
247 |
248 | # This flag controls whether pylint should warn about no-member and similar
249 | # checks whenever an opaque object is returned when inferring. The inference
250 | # can return multiple potential results while evaluating a Python object, but
251 | # some branches might not be evaluated, which results in partial inference. In
252 | # that case, it might be useful to still emit no-member and other checks for
253 | # the rest of the inferred objects.
254 | ignore-on-opaque-inference=yes
255 |
256 | # List of class names for which member attributes should not be checked (useful
257 | # for classes with dynamically set attributes). This supports the use of
258 | # qualified names.
259 | ignored-classes=optparse.Values,thread._local,_thread._local
260 |
261 | # List of module names for which member attributes should not be checked
262 | # (useful for modules/projects where namespaces are manipulated during runtime
263 | # and thus existing member attributes cannot be deduced by static analysis. It
264 | # supports qualified module names, as well as Unix pattern matching.
265 | ignored-modules=
266 |
267 | # Show a hint with possible names when a member name was not found. The aspect
268 | # of finding the hint is based on edit distance.
269 | missing-member-hint=yes
270 |
271 | # The minimum edit distance a name should have in order to be considered a
272 | # similar match for a missing member name.
273 | missing-member-hint-distance=1
274 |
275 | # The total number of similar names that should be taken in consideration when
276 | # showing a hint for a missing member.
277 | missing-member-max-choices=1
278 |
279 |
280 | [VARIABLES]
281 |
282 | # List of additional names supposed to be defined in builtins. Remember that
283 | # you should avoid to define new builtins when possible.
284 | additional-builtins=
285 |
286 | # Tells whether unused global variables should be treated as a violation.
287 | allow-global-unused-variables=yes
288 |
289 | # List of strings which can identify a callback function by name. A callback
290 | # name must start or end with one of those strings.
291 | callbacks=cb_,
292 | _cb
293 |
294 | # A regular expression matching the name of dummy variables (i.e. expectedly
295 | # not used).
296 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
297 |
298 | # Argument names that match this expression will be ignored. Default to name
299 | # with leading underscore
300 | ignored-argument-names=_.*|^ignored_|^unused_
301 |
302 | # Tells whether we should check for unused import in __init__ files.
303 | init-import=no
304 |
305 | # List of qualified module names which can have objects that can redefine
306 | # builtins.
307 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,io,builtins
308 |
309 |
310 | [FORMAT]
311 |
312 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
313 | expected-line-ending-format=
314 |
315 | # Regexp for a line that is allowed to be longer than the limit.
316 | ignore-long-lines=^\s*(# )??$
317 |
318 | # Number of spaces of indent required inside a hanging or continued line.
319 | indent-after-paren=4
320 |
321 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
322 | # tab).
323 | indent-string=' '
324 |
325 | # Maximum number of characters on a single line.
326 | max-line-length=100
327 |
328 | # Maximum number of lines in a module
329 | max-module-lines=1000
330 |
331 | # List of optional constructs for which whitespace checking is disabled. `dict-
332 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
333 | # `trailing-comma` allows a space between comma and closing bracket: (a, ).
334 | # `empty-line` allows space-only lines.
335 | no-space-check=trailing-comma,
336 | dict-separator
337 |
338 | # Allow the body of a class to be on the same line as the declaration if body
339 | # contains single statement.
340 | single-line-class-stmt=no
341 |
342 | # Allow the body of an if to be on the same line as the test if there is no
343 | # else.
344 | single-line-if-stmt=no
345 |
346 |
347 | [BASIC]
348 |
349 | # Naming style matching correct argument names
350 | argument-naming-style=snake_case
351 |
352 | # Regular expression matching correct argument names. Overrides argument-
353 | # naming-style
354 | #argument-rgx=
355 |
356 | # Naming style matching correct attribute names
357 | attr-naming-style=snake_case
358 |
359 | # Regular expression matching correct attribute names. Overrides attr-naming-
360 | # style
361 | #attr-rgx=
362 |
363 | # Bad variable names which should always be refused, separated by a comma
364 | bad-names=foo,
365 | bar,
366 | baz,
367 | toto,
368 | tutu,
369 | tata
370 |
371 | # Naming style matching correct class attribute names
372 | class-attribute-naming-style=any
373 |
374 | # Regular expression matching correct class attribute names. Overrides class-
375 | # attribute-naming-style
376 | #class-attribute-rgx=
377 |
378 | # Naming style matching correct class names
379 | class-naming-style=PascalCase
380 |
381 | # Regular expression matching correct class names. Overrides class-naming-style
382 | #class-rgx=
383 |
384 | # Naming style matching correct constant names
385 | const-naming-style=UPPER_CASE
386 |
387 | # Regular expression matching correct constant names. Overrides const-naming-
388 | # style
389 | #const-rgx=
390 |
391 | # Minimum line length for functions/classes that require docstrings, shorter
392 | # ones are exempt.
393 | docstring-min-length=-1
394 |
395 | # Naming style matching correct function names
396 | function-naming-style=snake_case
397 |
398 | # Regular expression matching correct function names. Overrides function-
399 | # naming-style
400 | #function-rgx=
401 |
402 | # Good variable names which should always be accepted, separated by a comma
403 | good-names=i,
404 | j,
405 | k,
406 | ex,
407 | Run,
408 | _
409 |
410 | # Include a hint for the correct naming format with invalid-name
411 | include-naming-hint=no
412 |
413 | # Naming style matching correct inline iteration names
414 | inlinevar-naming-style=any
415 |
416 | # Regular expression matching correct inline iteration names. Overrides
417 | # inlinevar-naming-style
418 | #inlinevar-rgx=
419 |
420 | # Naming style matching correct method names
421 | method-naming-style=snake_case
422 |
423 | # Regular expression matching correct method names. Overrides method-naming-
424 | # style
425 | #method-rgx=
426 |
427 | # Naming style matching correct module names
428 | module-naming-style=snake_case
429 |
430 | # Regular expression matching correct module names. Overrides module-naming-
431 | # style
432 | #module-rgx=
433 |
434 | # Colon-delimited sets of names that determine each other's naming style when
435 | # the name regexes allow several styles.
436 | name-group=
437 |
438 | # Regular expression which should only match function or class names that do
439 | # not require a docstring.
440 | no-docstring-rgx=^_
441 |
442 | # List of decorators that produce properties, such as abc.abstractproperty. Add
443 | # to this list to register other decorators that produce valid properties.
444 | property-classes=abc.abstractproperty
445 |
446 | # Naming style matching correct variable names
447 | variable-naming-style=snake_case
448 |
449 | # Regular expression matching correct variable names. Overrides variable-
450 | # naming-style
451 | #variable-rgx=
452 |
453 |
454 | [DESIGN]
455 |
456 | # Maximum number of arguments for function / method
457 | max-args=5
458 |
459 | # Maximum number of attributes for a class (see R0902).
460 | max-attributes=7
461 |
462 | # Maximum number of boolean expressions in a if statement
463 | max-bool-expr=5
464 |
465 | # Maximum number of branch for function / method body
466 | max-branches=12
467 |
468 | # Maximum number of locals for function / method body
469 | max-locals=15
470 |
471 | # Maximum number of parents for a class (see R0901).
472 | max-parents=7
473 |
474 | # Maximum number of public methods for a class (see R0904).
475 | max-public-methods=20
476 |
477 | # Maximum number of return / yield for function / method body
478 | max-returns=6
479 |
480 | # Maximum number of statements in function / method body
481 | max-statements=50
482 |
483 | # Minimum number of public methods for a class (see R0903).
484 | min-public-methods=2
485 |
486 |
487 | [CLASSES]
488 |
489 | # List of method names used to declare (i.e. assign) instance attributes.
490 | defining-attr-methods=__init__,
491 | __new__,
492 | setUp
493 |
494 | # List of member names, which should be excluded from the protected access
495 | # warning.
496 | exclude-protected=_asdict,
497 | _fields,
498 | _replace,
499 | _source,
500 | _make
501 |
502 | # List of valid names for the first argument in a class method.
503 | valid-classmethod-first-arg=cls
504 |
505 | # List of valid names for the first argument in a metaclass class method.
506 | valid-metaclass-classmethod-first-arg=mcs
507 |
508 |
509 | [IMPORTS]
510 |
511 | # Allow wildcard imports from modules that define __all__.
512 | allow-wildcard-with-all=no
513 |
514 | # Analyse import fallback blocks. This can be used to support both Python 2 and
515 | # 3 compatible code, which means that the block might have code that exists
516 | # only in one or another interpreter, leading to false positives when analysed.
517 | analyse-fallback-blocks=no
518 |
519 | # Deprecated modules which should not be used, separated by a comma
520 | deprecated-modules=regsub,
521 | TERMIOS,
522 | Bastion,
523 | rexec
524 |
525 | # Create a graph of external dependencies in the given file (report RP0402 must
526 | # not be disabled)
527 | ext-import-graph=
528 |
529 | # Create a graph of every (i.e. internal and external) dependencies in the
530 | # given file (report RP0402 must not be disabled)
531 | import-graph=
532 |
533 | # Create a graph of internal dependencies in the given file (report RP0402 must
534 | # not be disabled)
535 | int-import-graph=
536 |
537 | # Force import order to recognize a module as part of the standard
538 | # compatibility libraries.
539 | known-standard-library=
540 |
541 | # Force import order to recognize a module as part of a third party library.
542 | known-third-party=enchant
543 |
544 |
545 | [EXCEPTIONS]
546 |
547 | # Exceptions that will emit a warning when being caught. Defaults to
548 | # "Exception"
549 | overgeneral-exceptions=Exception
550 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3
2 |
3 | ADD . /source/
4 |
5 | RUN pip3 install -e /source --install-option="--with-audio"
6 |
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Jan Kozik
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.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include requirements.txt
2 | prune videos
3 | prune build
4 | prune dist
5 | global-exclude *.md *.in
6 | recursive-include video_to_ascii *.py
7 | recursive-exclude video_to_ascii *.pyc *.txt
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 |
5 |
6 |
7 | It's a simple python package to play videos in a terminal using [ASCII](https://en.wikipedia.org/wiki/ASCII) characters.
8 |
9 |
24 |
25 | ## Requirements
26 |
27 | - Python3
28 | - PortAudio (_Only required for installation with audio support_)
29 | - FFmpeg (_Only required for installation with audio support_)
30 | - Linux or MacOS ... by now
31 |
32 | ## Installation
33 |
34 | Standard installation
35 |
36 | ```bash
37 | $ pip3 install video-to-ascii
38 | ```
39 |
40 | With audio support installation
41 |
42 | ```bash
43 | $ pip3 install video-to-ascii --install-option="--with-audio"
44 | ```
45 |
46 | ## How to use
47 |
48 | Just run `video-to-ascii` in your terminal
49 |
50 | ```bash
51 | $ video-to-ascii -f myvideo.mp4
52 | ```
53 |
54 | ### Options
55 |
56 | **`--strategy`**
57 | Allow to choose a strategy to render the output.
58 |
59 | 
60 |
61 | **`-o --output`**
62 | Export the rendering output to a bash file to share with someone.
63 |
64 | 
65 |
66 | **`-a --with-audio`**
67 | If an installation with audio support was made, you can use this option to play the audio track while rendering the video ascii characters.
68 |
69 | ## How it works
70 |
71 | Every video is composed by a set of frames that are played at a certain frame rate.
72 |
73 | 
74 |
75 | Since a terminal has a specific number of rows and columns, we have to resize our video to adjust to the terminal size limitations.
76 |
77 | 
78 |
79 | To reach a correct visualization of an entire frame we need to adjust the _frame height_ to match the _terminal rows_, avoiding using more _characters_ than the number of _terminal columns_.
80 |
81 | 
82 |
83 | When picking a character to represent a pixel we need to measure the relevance of that pixel's color in the frame, based on that we can then select the most appropriate character based on the [relative luminance](https://en.wikipedia.org/wiki/Relative_luminance) in colorimetric spaces, using a simplified version of the luminosity function.
84 |
85 |
86 |
87 |
88 |
89 | > Green light contributes the most to the intensity perceived by humans, and blue light the least.
90 |
91 | This function returns an integer in the range from 0 to 255, we assign a character according to density to show more colored surface for areas with more intense color (highest values).
92 |
93 | ```python
94 | CHARS_LIGHT = [' ', ' ', '.', ':', '!', '+', '*', 'e', '$', '@', '8']
95 | CHARS_COLOR = ['.', '*', 'e', 's', '@']
96 | CHARS_FILLED = ['░', '▒', '▓', '█']
97 | ```
98 |
99 | The reduced range of colors supported by the terminal is a problem we need to account for. Modern terminals support up to 256 colors, so we need to find the closest 8 bit color that matches the original pixel in 16 or 24 bit color, we call this set of 256 colors [ANSI colors](https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences).
100 |
101 | 
102 |
103 | 
104 |
105 | Finally, when putting it all together, we will have an appropriate character for each pixel and a new color.
106 |
107 | 
108 |
109 | ## As Seen On
110 |
111 |
--------------------------------------------------------------------------------
/bin/video-to-ascii:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import argparse
4 | from video_to_ascii import player
5 |
6 | CLI_DESC = "It is a simple python package to play videos in the terminal using colored characters as pixels or other useful outputs"
7 | EPILOG = ("\033[1;37mThanks for trying video-to-ascii!\033[0m")
8 |
9 | PARSER = argparse.ArgumentParser(prog='video-to-ascii', description=CLI_DESC, epilog=EPILOG)
10 | PARSER.add_argument('-f', '--file', type=str, dest='file', help='input video file', action='store', required=True)
11 | PARSER.add_argument('--strategy', default='ascii-color', type=str, dest='strategy',
12 | choices=["ascii-color", "just-ascii", "filled-ascii"], help='choose an strategy to render the output', action='store')
13 | PARSER.add_argument('-o', '--output', type=str, dest='output', help='output file to export', action='store')
14 | PARSER.add_argument('--output-format', default='sh', type=str, dest='output_format',
15 | choices=["sh", "json"], help='choose an output format to render the output', action='store')
16 | PARSER.add_argument('--with-audio', dest='with_audio', help='play audio', action='store_true')
17 |
18 | ARGS = PARSER.parse_args()
19 |
20 | player.play(ARGS.file, strategy=ARGS.strategy, output=ARGS.output, output_format=ARGS.output_format, play_audio=ARGS.with_audio)
21 |
--------------------------------------------------------------------------------
/images/8-bit_color_table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/8-bit_color_table.png
--------------------------------------------------------------------------------
/images/Luminosity.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
47 |
--------------------------------------------------------------------------------
/images/LuminosityFunction.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
47 |
--------------------------------------------------------------------------------
/images/Simpsons.apng:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/Simpsons.apng
--------------------------------------------------------------------------------
/images/Simpsons.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/Simpsons.gif
--------------------------------------------------------------------------------
/images/Simpsonsv2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/Simpsonsv2.gif
--------------------------------------------------------------------------------
/images/Strategies.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/Strategies.png
--------------------------------------------------------------------------------
/images/display-colors.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/display-colors.jpg
--------------------------------------------------------------------------------
/images/export.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/export.png
--------------------------------------------------------------------------------
/images/imgBrightnes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/imgBrightnes.png
--------------------------------------------------------------------------------
/images/imgPixelImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/imgPixelImage.png
--------------------------------------------------------------------------------
/images/imgPixelSection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/imgPixelSection.png
--------------------------------------------------------------------------------
/images/imgResizing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/imgResizing.png
--------------------------------------------------------------------------------
/images/imgTerminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/imgTerminal.png
--------------------------------------------------------------------------------
/images/imgVideoFrames.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/imgVideoFrames.png
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jankozik/video2ascii/5c9ae77234e4e7c6effb5935b30c77b19d367598/images/logo.png
--------------------------------------------------------------------------------
/images/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | opencv-python
2 | xtermcolor
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from setuptools import setup, find_packages
3 | from setuptools.command.install import install
4 | from setuptools.command.develop import develop
5 | from setuptools.command.egg_info import egg_info
6 |
7 | def install_package(package):
8 | import pip
9 | try:
10 | from pip._internal import main
11 | main.main(['install', package])
12 | except AttributeError:
13 | from pip import __main__
14 | __main__._main(['install', package])
15 |
16 | if "--with-audio" in sys.argv:
17 | install_package('opencv-python')
18 | install_package('pyaudio')
19 | sys.argv.remove("--with-audio")
20 | else:
21 | install_package('opencv-python')
22 |
23 | setup(
24 | name="video_to_ascii",
25 | version="1.3.0",
26 | author="Joel Ibaceta",
27 | author_email="mail@joelibaceta.com",
28 | license='MIT',
29 | description="It is a simple python package to play videos in the terminal",
30 | long_description="A simple tool to play a video using ascii characters instead of pixels",
31 | url="https://github.com/joelibaceta/video-to-ascii",
32 | project_urls={
33 | 'Source': 'https://github.com/joelibaceta/video-to-ascii',
34 | 'Tracker': 'https://github.com/joelibaceta/video-to-ascii/issues'
35 | },
36 | packages=find_packages(),
37 | include_package_data=True,
38 | install_requires=['xtermcolor', 'ffmpeg-python'],
39 | classifiers=[
40 | "Programming Language :: Python :: 3",
41 | "Intended Audience :: Developers",
42 | "License :: OSI Approved :: MIT License",
43 | "Operating System :: OS Independent",
44 | ],
45 | keywords='video ascii terminal opencv',
46 | entry_points={
47 | "console_scripts": [
48 | 'video-to-ascii=video_to_ascii.cli:main'
49 | ]
50 | }
51 | )
52 |
--------------------------------------------------------------------------------
/translations/README_es.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 |
5 |
6 |
7 | Es un paquete de Python simple para reproducir videos en una terminal usando caracteres [ASCII](https://en.wikipedia.org/wiki/ASCII).
8 |
9 | [](https://opencollective.com/video-to-ascii) [](https://badge.fury.io/py/video-to-ascii)
10 | [](https://codeclimate.com/github/joelibaceta/video-to-terminal/maintainability)
11 | [](https://github.com/joelibaceta/video-to-ascii)
12 | [](http://hits.dwyl.io/joelibaceta/https://github.com/joelibaceta/video-to-ascii)
13 |
14 | 
15 |
16 |
29 |
30 | ## Requisitos
31 |
32 | - Python3
33 | - PortAudio (_Solo se requiere para la instalación con soporte de audio_)
34 | - FFmpeg (_Solo se requiere para la instalación con soporte de audio_)
35 |
36 | ## Instalación
37 |
38 | Instalación estándar
39 |
40 | ```bash
41 | $ pip3 install video-to-ascii
42 | ```
43 |
44 | Instalación con soporte de audio
45 |
46 | ```bash
47 | $ pip3 install video-to-ascii --install-option="--with-audio"
48 | ```
49 |
50 | ## Cómo usarlo
51 |
52 | Simplemente ejecute `video-to-ascii` en su terminal
53 |
54 | ```bash
55 | $ video-to-ascii -f myvideo.mp4
56 | ```
57 |
58 | ### Opciones
59 |
60 | **`--strategy`**
61 | Permite elegir una estrategia para renderizar la salida.
62 |
63 | 
64 |
65 | **`-o --output`**
66 | Exporte la salida de renderizado a un archivo bash para compartir con alguien.
67 |
68 | 
69 |
70 | **`-a --with-audio`**
71 | Si se realizó una instalación con soporte de audio, puede usar esta opción para reproducir la pista de audio mientras renderiza los caracteres ascii del video.
72 |
73 | ## Cómo funciona
74 |
75 | Cada video está compuesto por un conjunto de fotogramas que se reproducen a una determinada velocidad de fotogramas.
76 |
77 | 
78 |
79 | Dado que un terminal tiene un número específico de filas y columnas, tenemos que cambiar el tamaño de nuestro video para ajustarlo a las limitaciones de tamaño del terminal.
80 |
81 | 
82 |
83 | Para alcanzar una visualización correcta de un marco completo, necesitamos ajustar la _frame height_ para que coincida con las _terminal rows_, evitando usar más _caracteres_ que el número de _terminal columns_.
84 |
85 | 
86 |
87 | Al elegir un carácter para representar un píxel, necesitamos medir la relevancia del color de ese píxel en el marco, en base a eso podemos seleccionar el carácter más apropiado en función de la [luminancia relativa](https://en.wikipedia.org/wiki/Relative_luminance) en los espacios colorimétricos, usando una versión simplificada de la función de luminosidad .
88 |
89 |
90 |
91 |
92 |
93 | > La luz verde es la que más contribuye a la intensidad percibida por los humanos y la luz azul, la que menos.
94 |
95 | Esta función devuelve un número entero en el rango de 0 a 255, asignamos un carácter según la densidad para mostrar la superficie más coloreada para las áreas con color más intenso (valores más altos).
96 |
97 | ```python
98 | CHARS_LIGHT = [' ', ' ', '.', ':', '!', '+', '*', 'e', '$', '@', '8']
99 | CHARS_COLOR = ['.', '*', 'e', 's', '@']
100 | CHARS_FILLED = ['░', '▒', '▓', '█']
101 | ```
102 |
103 | La reducida gama de colores que admite el terminal es un problema que debemos tener en cuenta. Los terminales modernos admiten hasta 256 colores, por lo que necesitamos encontrar el color de 8 bits más cercano que coincida con el píxel original en color de 16 o 24 bits, a este conjunto de [colores ANSI](https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences) de 256 colores lo llamamos.
104 |
105 | 
106 |
107 | 
108 |
109 | Finalmente, al ponerlo todo junto, tendremos un carácter apropiado para cada píxel y un nuevo color.
110 |
111 | 
112 |
113 | ## Contribuyentes
114 |
115 | ### Contribuyentes de Código
116 |
117 | Este proyecto existe gracias a todas las personas que contribuyen. [[Contribute](../CONTRIBUTING.md)].
118 |
119 |
120 |
121 | ### Contribuyentes Financieros
122 |
123 | Conviértete en un contribuyente financiero y ayúdanos a sostener nuestra comunidad.. [[Contribute](https://opencollective.com/video-to-ascii/contribute/)].
124 |
125 | O tal vez sólo me [compre un café](https://ko-fi.com/joelibaceta).
126 |
127 | #### Individuos
128 |
129 |
130 |
131 | #### Organizaciones
132 |
133 | Apoye este proyecto con su organización. Su logotipo se mostrará aquí con un enlace a su sitio web. [[Contribute](https://opencollective.com/video-to-ascii/contribute)]
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/translations/README_zh-TW.md:
--------------------------------------------------------------------------------
1 |