auto-editor 28.1.0__py3-none-any.whl → 29.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {auto_editor-28.1.0.dist-info → auto_editor-29.0.0.dist-info}/METADATA +4 -3
- auto_editor-29.0.0.dist-info/RECORD +5 -0
- auto_editor-29.0.0.dist-info/top_level.txt +1 -0
- auto_editor/__init__.py +0 -1
- auto_editor/__main__.py +0 -504
- auto_editor/analyze.py +0 -393
- auto_editor/cmds/__init__.py +0 -0
- auto_editor/cmds/cache.py +0 -69
- auto_editor/cmds/desc.py +0 -32
- auto_editor/cmds/info.py +0 -213
- auto_editor/cmds/levels.py +0 -199
- auto_editor/cmds/palet.py +0 -29
- auto_editor/cmds/repl.py +0 -113
- auto_editor/cmds/subdump.py +0 -72
- auto_editor/cmds/test.py +0 -816
- auto_editor/edit.py +0 -560
- auto_editor/exports/__init__.py +0 -0
- auto_editor/exports/fcp11.py +0 -195
- auto_editor/exports/fcp7.py +0 -313
- auto_editor/exports/json.py +0 -63
- auto_editor/exports/kdenlive.py +0 -322
- auto_editor/exports/shotcut.py +0 -147
- auto_editor/ffwrapper.py +0 -187
- auto_editor/help.py +0 -224
- auto_editor/imports/__init__.py +0 -0
- auto_editor/imports/fcp7.py +0 -275
- auto_editor/imports/json.py +0 -234
- auto_editor/json.py +0 -297
- auto_editor/lang/__init__.py +0 -0
- auto_editor/lang/libintrospection.py +0 -10
- auto_editor/lang/libmath.py +0 -23
- auto_editor/lang/palet.py +0 -724
- auto_editor/lang/stdenv.py +0 -1179
- auto_editor/lib/__init__.py +0 -0
- auto_editor/lib/contracts.py +0 -235
- auto_editor/lib/data_structs.py +0 -278
- auto_editor/lib/err.py +0 -2
- auto_editor/make_layers.py +0 -315
- auto_editor/preview.py +0 -93
- auto_editor/render/__init__.py +0 -0
- auto_editor/render/audio.py +0 -517
- auto_editor/render/subtitle.py +0 -205
- auto_editor/render/video.py +0 -307
- auto_editor/timeline.py +0 -331
- auto_editor/utils/__init__.py +0 -0
- auto_editor/utils/bar.py +0 -142
- auto_editor/utils/chunks.py +0 -2
- auto_editor/utils/cmdkw.py +0 -206
- auto_editor/utils/container.py +0 -101
- auto_editor/utils/func.py +0 -128
- auto_editor/utils/log.py +0 -126
- auto_editor/utils/types.py +0 -277
- auto_editor/vanparse.py +0 -313
- auto_editor-28.1.0.dist-info/RECORD +0 -57
- auto_editor-28.1.0.dist-info/entry_points.txt +0 -6
- auto_editor-28.1.0.dist-info/top_level.txt +0 -2
- docs/build.py +0 -70
- {auto_editor-28.1.0.dist-info → auto_editor-29.0.0.dist-info}/WHEEL +0 -0
- {auto_editor-28.1.0.dist-info → auto_editor-29.0.0.dist-info}/licenses/LICENSE +0 -0
auto_editor/utils/container.py
DELETED
@@ -1,101 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from dataclasses import dataclass
|
4
|
-
from typing import TypedDict
|
5
|
-
|
6
|
-
from av import Codec, open
|
7
|
-
|
8
|
-
from auto_editor.utils.log import Log
|
9
|
-
|
10
|
-
|
11
|
-
class DictContainer(TypedDict, total=False):
|
12
|
-
max_videos: int | None
|
13
|
-
max_audios: int | None
|
14
|
-
max_subtitles: int | None
|
15
|
-
samplerate: list[int] | None
|
16
|
-
|
17
|
-
|
18
|
-
@dataclass(slots=True)
|
19
|
-
class Container:
|
20
|
-
allow_image: bool
|
21
|
-
vcodecs: set[str]
|
22
|
-
acodecs: set[str]
|
23
|
-
scodecs: set[str]
|
24
|
-
default_vid: str
|
25
|
-
default_aud: str
|
26
|
-
default_sub: str
|
27
|
-
max_videos: int | None = None
|
28
|
-
max_audios: int | None = None
|
29
|
-
max_subtitles: int | None = None
|
30
|
-
samplerate: list[int] | None = None # Any samplerate is allowed
|
31
|
-
|
32
|
-
|
33
|
-
containers: dict[str, DictContainer] = {
|
34
|
-
"aac": {"max_audios": 1},
|
35
|
-
"adts": {"max_audios": 1},
|
36
|
-
"ass": {"max_subtitles": 1},
|
37
|
-
"ssa": {"max_subtitles": 1},
|
38
|
-
"apng": {"max_videos": 1},
|
39
|
-
"gif": {"max_videos": 1},
|
40
|
-
"wav": {"max_audios": 1},
|
41
|
-
"ast": {"max_audios": 1},
|
42
|
-
"mp3": {"max_audios": 1},
|
43
|
-
"flac": {"max_audios": 1},
|
44
|
-
"srt": {"max_subtitles": 1},
|
45
|
-
"vtt": {"max_subtitles": 1},
|
46
|
-
"swf": {"samplerate": [44100, 22050, 11025]},
|
47
|
-
}
|
48
|
-
|
49
|
-
|
50
|
-
def codec_type(x: str) -> str:
|
51
|
-
if x in {"vp9", "vp8", "h264", "hevc", "av1", "gif", "apng"}:
|
52
|
-
return "video"
|
53
|
-
if x in {"aac", "flac", "mp3"}:
|
54
|
-
return "audio"
|
55
|
-
if x in {"ass", "ssa", "srt"}:
|
56
|
-
return "subtitle"
|
57
|
-
|
58
|
-
try:
|
59
|
-
return Codec(x, "w").type
|
60
|
-
except Exception:
|
61
|
-
return ""
|
62
|
-
|
63
|
-
|
64
|
-
def container_constructor(ext: str, log: Log) -> Container:
|
65
|
-
try:
|
66
|
-
container = open(f".{ext}", "w")
|
67
|
-
except ValueError:
|
68
|
-
log.error(f"Could not find a suitable format for extension: {ext}")
|
69
|
-
|
70
|
-
codecs = container.supported_codecs
|
71
|
-
if ext == "webm":
|
72
|
-
vdefault = "vp9"
|
73
|
-
else:
|
74
|
-
vdefault = container.default_video_codec
|
75
|
-
adefault = container.default_audio_codec
|
76
|
-
sdefault = container.default_subtitle_codec
|
77
|
-
if sdefault == "none" and ext == "mp4":
|
78
|
-
sdefault = "srt"
|
79
|
-
|
80
|
-
container.close()
|
81
|
-
vcodecs = set()
|
82
|
-
acodecs = set()
|
83
|
-
scodecs = set()
|
84
|
-
|
85
|
-
for codec in codecs:
|
86
|
-
if ext == "wav" and codec == "aac":
|
87
|
-
continue
|
88
|
-
kind = codec_type(codec)
|
89
|
-
if kind == "video":
|
90
|
-
vcodecs.add(codec)
|
91
|
-
if kind == "audio":
|
92
|
-
acodecs.add(codec)
|
93
|
-
if kind == "subtitle":
|
94
|
-
scodecs.add(codec)
|
95
|
-
|
96
|
-
allow_image = ext in {"mp4", "mkv"}
|
97
|
-
kwargs = containers[ext] if ext in containers else {}
|
98
|
-
|
99
|
-
return Container(
|
100
|
-
allow_image, vcodecs, acodecs, scodecs, vdefault, adefault, sdefault, **kwargs
|
101
|
-
)
|
auto_editor/utils/func.py
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from typing import TYPE_CHECKING
|
4
|
-
|
5
|
-
import numpy as np
|
6
|
-
|
7
|
-
from auto_editor.utils.log import Log
|
8
|
-
from auto_editor.utils.types import split_num_str
|
9
|
-
|
10
|
-
if TYPE_CHECKING:
|
11
|
-
from collections.abc import Callable
|
12
|
-
from fractions import Fraction
|
13
|
-
|
14
|
-
from numpy.typing import NDArray
|
15
|
-
|
16
|
-
BoolList = NDArray[np.bool_]
|
17
|
-
BoolOperand = Callable[[BoolList, BoolList], BoolList]
|
18
|
-
|
19
|
-
|
20
|
-
def boolop(a: BoolList, b: BoolList, call: BoolOperand) -> BoolList:
|
21
|
-
if len(a) > len(b):
|
22
|
-
k = np.copy(b)
|
23
|
-
k.resize(len(a))
|
24
|
-
b = k
|
25
|
-
if len(b) > len(a):
|
26
|
-
k = np.copy(a)
|
27
|
-
k.resize(len(b))
|
28
|
-
a = k
|
29
|
-
|
30
|
-
return call(a, b)
|
31
|
-
|
32
|
-
|
33
|
-
def to_timecode(secs: float | Fraction, fmt: str) -> str:
|
34
|
-
sign = ""
|
35
|
-
if secs < 0:
|
36
|
-
sign = "-"
|
37
|
-
secs = -secs
|
38
|
-
|
39
|
-
_m, _s = divmod(secs, 60)
|
40
|
-
_h, _m = divmod(_m, 60)
|
41
|
-
s, m, h = float(_s), int(_m), int(_h)
|
42
|
-
|
43
|
-
if fmt == "webvtt":
|
44
|
-
if h == 0:
|
45
|
-
return f"{sign}{m:02d}:{s:06.3f}"
|
46
|
-
return f"{sign}{h:02d}:{m:02d}:{s:06.3f}"
|
47
|
-
if fmt in {"srt", "mov_text"}:
|
48
|
-
return f"{sign}{h:02d}:{m:02d}:" + f"{s:06.3f}".replace(".", ",", 1)
|
49
|
-
if fmt == "standard":
|
50
|
-
return f"{sign}{h:02d}:{m:02d}:{s:06.3f}"
|
51
|
-
if fmt == "ass":
|
52
|
-
return f"{sign}{h:d}:{m:02d}:{s:05.2f}"
|
53
|
-
if fmt == "rass":
|
54
|
-
return f"{sign}{h:d}:{m:02d}:{s:02.0f}"
|
55
|
-
|
56
|
-
raise ValueError("to_timecode: Unreachable")
|
57
|
-
|
58
|
-
|
59
|
-
def mut_margin(arr: BoolList, start_m: int, end_m: int) -> None:
|
60
|
-
# Find start and end indexes
|
61
|
-
start_index = []
|
62
|
-
end_index = []
|
63
|
-
arrlen = len(arr)
|
64
|
-
for j in range(1, arrlen):
|
65
|
-
if arr[j] != arr[j - 1]:
|
66
|
-
if arr[j]:
|
67
|
-
start_index.append(j)
|
68
|
-
else:
|
69
|
-
end_index.append(j)
|
70
|
-
|
71
|
-
# Apply margin
|
72
|
-
if start_m > 0:
|
73
|
-
for i in start_index:
|
74
|
-
arr[max(i - start_m, 0) : i] = True
|
75
|
-
if start_m < 0:
|
76
|
-
for i in start_index:
|
77
|
-
arr[i : min(i - start_m, arrlen)] = False
|
78
|
-
|
79
|
-
if end_m > 0:
|
80
|
-
for i in end_index:
|
81
|
-
arr[i : min(i + end_m, arrlen)] = True
|
82
|
-
if end_m < 0:
|
83
|
-
for i in end_index:
|
84
|
-
arr[max(i + end_m, 0) : i] = False
|
85
|
-
|
86
|
-
|
87
|
-
def get_stdout(cmd: list[str]) -> str:
|
88
|
-
from subprocess import DEVNULL, PIPE, Popen
|
89
|
-
|
90
|
-
stdout = Popen(cmd, stdin=DEVNULL, stdout=PIPE, stderr=PIPE).communicate()[0]
|
91
|
-
return stdout.decode("utf-8", "replace")
|
92
|
-
|
93
|
-
|
94
|
-
def get_stdout_bytes(cmd: list[str]) -> bytes:
|
95
|
-
from subprocess import DEVNULL, PIPE, Popen
|
96
|
-
|
97
|
-
return Popen(cmd, stdin=DEVNULL, stdout=PIPE, stderr=PIPE).communicate()[0]
|
98
|
-
|
99
|
-
|
100
|
-
def aspect_ratio(width: int, height: int) -> tuple[int, int]:
|
101
|
-
if height == 0:
|
102
|
-
return (0, 0)
|
103
|
-
|
104
|
-
def gcd(a: int, b: int) -> int:
|
105
|
-
while b:
|
106
|
-
a, b = b, a % b
|
107
|
-
return a
|
108
|
-
|
109
|
-
c = gcd(width, height)
|
110
|
-
return width // c, height // c
|
111
|
-
|
112
|
-
|
113
|
-
def parse_bitrate(input_: str, log: Log) -> int:
|
114
|
-
try:
|
115
|
-
val, unit = split_num_str(input_)
|
116
|
-
except Exception as e:
|
117
|
-
log.error(e)
|
118
|
-
|
119
|
-
if unit.lower() == "k":
|
120
|
-
return int(val * 1000)
|
121
|
-
if unit == "M":
|
122
|
-
return int(val * 1_000_000)
|
123
|
-
if unit == "G":
|
124
|
-
return int(val * 1_000_000_000)
|
125
|
-
if unit == "":
|
126
|
-
return int(val)
|
127
|
-
|
128
|
-
log.error(f"Unknown bitrate: {input_}")
|
auto_editor/utils/log.py
DELETED
@@ -1,126 +0,0 @@
|
|
1
|
-
import sys
|
2
|
-
from datetime import timedelta
|
3
|
-
from shutil import get_terminal_size, rmtree
|
4
|
-
from tempfile import mkdtemp
|
5
|
-
from time import perf_counter, sleep
|
6
|
-
from typing import NoReturn
|
7
|
-
|
8
|
-
|
9
|
-
class Log:
|
10
|
-
__slots__ = ("is_debug", "quiet", "machine", "no_color", "_temp", "_ut", "_s")
|
11
|
-
|
12
|
-
def __init__(
|
13
|
-
self,
|
14
|
-
is_debug: bool = False,
|
15
|
-
quiet: bool = False,
|
16
|
-
temp_dir: str | None = None,
|
17
|
-
machine: bool = False,
|
18
|
-
no_color: bool = True,
|
19
|
-
):
|
20
|
-
self.is_debug = is_debug
|
21
|
-
self.quiet = quiet
|
22
|
-
self.machine = machine
|
23
|
-
self.no_color = no_color
|
24
|
-
self._temp: str | None = None
|
25
|
-
self._ut = temp_dir
|
26
|
-
self._s = 0 if self.quiet or self.machine else perf_counter()
|
27
|
-
|
28
|
-
def debug(self, message: object) -> None:
|
29
|
-
if self.is_debug:
|
30
|
-
self.conwrite("")
|
31
|
-
sys.stderr.write(f"Debug: {message}\n")
|
32
|
-
|
33
|
-
@property
|
34
|
-
def temp(self) -> str:
|
35
|
-
if self._temp is not None:
|
36
|
-
return self._temp
|
37
|
-
|
38
|
-
if self._ut is None:
|
39
|
-
result = mkdtemp()
|
40
|
-
else:
|
41
|
-
import os.path
|
42
|
-
from os import listdir, mkdir
|
43
|
-
|
44
|
-
if os.path.isfile(self._ut):
|
45
|
-
self.error("Temp directory cannot be an already existing file.")
|
46
|
-
|
47
|
-
if os.path.isdir(self._ut):
|
48
|
-
if len(listdir(self._ut)) != 0:
|
49
|
-
self.error("Temp directory should be empty!")
|
50
|
-
else:
|
51
|
-
mkdir(self._ut)
|
52
|
-
result = self._ut
|
53
|
-
|
54
|
-
self.debug(f"Temp Directory: {result}")
|
55
|
-
self._temp = result
|
56
|
-
return result
|
57
|
-
|
58
|
-
def cleanup(self) -> None:
|
59
|
-
if self._temp is None:
|
60
|
-
return
|
61
|
-
try:
|
62
|
-
rmtree(self._temp)
|
63
|
-
self.debug("Removed Temp Directory.")
|
64
|
-
except FileNotFoundError:
|
65
|
-
pass
|
66
|
-
except PermissionError:
|
67
|
-
sleep(0.1)
|
68
|
-
try:
|
69
|
-
rmtree(self._temp)
|
70
|
-
self.debug("Removed Temp Directory.")
|
71
|
-
except Exception as e:
|
72
|
-
self.debug(f"Failed to delete temp dir:\n{e}")
|
73
|
-
|
74
|
-
def conwrite(self, message: str) -> None:
|
75
|
-
if self.machine:
|
76
|
-
print(message, flush=True)
|
77
|
-
elif not self.quiet:
|
78
|
-
buffer = " " * (get_terminal_size().columns - len(message) - 3)
|
79
|
-
sys.stdout.write(f" {message}{buffer}\r")
|
80
|
-
|
81
|
-
def print(self, message: str) -> None:
|
82
|
-
if not self.quiet:
|
83
|
-
self.conwrite("")
|
84
|
-
sys.stdout.write(f"{message}\n")
|
85
|
-
|
86
|
-
def warning(self, message: str) -> None:
|
87
|
-
if not self.quiet:
|
88
|
-
self.conwrite("")
|
89
|
-
sys.stderr.write(f"Warning! {message}\n")
|
90
|
-
|
91
|
-
def stop_timer(self) -> None:
|
92
|
-
if not self.quiet and not self.machine:
|
93
|
-
second_len = round(perf_counter() - self._s, 2)
|
94
|
-
minute_len = timedelta(seconds=round(second_len))
|
95
|
-
|
96
|
-
sys.stdout.write(f"Finished. took {second_len} seconds ({minute_len})\n")
|
97
|
-
|
98
|
-
@staticmethod
|
99
|
-
def deprecated(message: str) -> None:
|
100
|
-
sys.stderr.write(f"\033[1m\033[33m{message}\033[0m\n")
|
101
|
-
|
102
|
-
def error(self, message: str | Exception) -> NoReturn:
|
103
|
-
if self.is_debug:
|
104
|
-
self.cleanup()
|
105
|
-
if isinstance(message, str):
|
106
|
-
raise Exception(message)
|
107
|
-
raise message
|
108
|
-
|
109
|
-
self.conwrite("")
|
110
|
-
if self.no_color:
|
111
|
-
sys.stderr.write(f"Error! {message}\n")
|
112
|
-
else:
|
113
|
-
sys.stderr.write(f"\033[31;40mError! {message}\033[0m\n")
|
114
|
-
|
115
|
-
self.cleanup()
|
116
|
-
from platform import system
|
117
|
-
|
118
|
-
if system() == "Linux":
|
119
|
-
sys.exit(1)
|
120
|
-
else:
|
121
|
-
try:
|
122
|
-
sys.exit(1)
|
123
|
-
except SystemExit:
|
124
|
-
import os
|
125
|
-
|
126
|
-
os._exit(1)
|
auto_editor/utils/types.py
DELETED
@@ -1,277 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import re
|
4
|
-
from fractions import Fraction
|
5
|
-
|
6
|
-
|
7
|
-
class CoerceError(Exception):
|
8
|
-
pass
|
9
|
-
|
10
|
-
|
11
|
-
def split_num_str(val: str | float) -> tuple[float, str]:
|
12
|
-
if isinstance(val, float | int):
|
13
|
-
return val, ""
|
14
|
-
|
15
|
-
index = 0
|
16
|
-
for char in val:
|
17
|
-
if char not in "0123456789_ .-":
|
18
|
-
break
|
19
|
-
index += 1
|
20
|
-
num, unit = val[:index], val[index:]
|
21
|
-
try:
|
22
|
-
float(num)
|
23
|
-
except ValueError:
|
24
|
-
raise CoerceError(f"Invalid number: '{val}'")
|
25
|
-
return float(num), unit
|
26
|
-
|
27
|
-
|
28
|
-
# Numbers: 0, 1, 2, 3, ...
|
29
|
-
def natural(val: str | float) -> int:
|
30
|
-
num, unit = split_num_str(val)
|
31
|
-
if unit != "":
|
32
|
-
raise CoerceError(f"'{val}': Natural does not allow units.")
|
33
|
-
if not isinstance(num, int) and not num.is_integer():
|
34
|
-
raise CoerceError(f"'{val}': Natural must be a valid integer.")
|
35
|
-
if num < 0:
|
36
|
-
raise CoerceError(f"'{val}': Natural cannot be negative.")
|
37
|
-
return int(num)
|
38
|
-
|
39
|
-
|
40
|
-
def number(val: str | float) -> float:
|
41
|
-
if isinstance(val, str) and "/" in val:
|
42
|
-
nd = val.split("/")
|
43
|
-
if len(nd) != 2:
|
44
|
-
raise CoerceError(f"'{val}': One divisor allowed.")
|
45
|
-
vs = []
|
46
|
-
for v in nd:
|
47
|
-
try:
|
48
|
-
vs.append(int(v))
|
49
|
-
except ValueError:
|
50
|
-
raise CoerceError(
|
51
|
-
f"'{val}': Numerator and Denominator must be integers."
|
52
|
-
)
|
53
|
-
if vs[1] == 0:
|
54
|
-
raise CoerceError(f"'{val}': Denominator must not be zero.")
|
55
|
-
return vs[0] / vs[1]
|
56
|
-
|
57
|
-
num, unit = split_num_str(val)
|
58
|
-
if unit == "%":
|
59
|
-
return num / 100
|
60
|
-
if unit == "":
|
61
|
-
return num
|
62
|
-
raise CoerceError(f"Unknown unit: '{unit}'")
|
63
|
-
|
64
|
-
|
65
|
-
def frame_rate(val: str) -> Fraction:
|
66
|
-
if val == "ntsc":
|
67
|
-
return Fraction(30000, 1001)
|
68
|
-
if val == "ntsc_film":
|
69
|
-
return Fraction(24000, 1001)
|
70
|
-
if val == "pal":
|
71
|
-
return Fraction(25)
|
72
|
-
if val == "film":
|
73
|
-
return Fraction(24)
|
74
|
-
return Fraction(val)
|
75
|
-
|
76
|
-
|
77
|
-
def time(val: str, tb: Fraction) -> int:
|
78
|
-
if ":" in val:
|
79
|
-
boxes = val.split(":")
|
80
|
-
if len(boxes) == 2:
|
81
|
-
return round((int(boxes[0]) * 60 + float(boxes[1])) * tb)
|
82
|
-
if len(boxes) == 3:
|
83
|
-
return round(
|
84
|
-
(int(boxes[0]) * 3600 + int(boxes[1]) * 60 + float(boxes[2])) * tb
|
85
|
-
)
|
86
|
-
raise CoerceError(f"'{val}': Invalid time format")
|
87
|
-
|
88
|
-
num, unit = split_num_str(val)
|
89
|
-
if unit in {"s", "sec", "secs", "second", "seconds"}:
|
90
|
-
return round(num * tb)
|
91
|
-
if unit in {"min", "mins", "minute", "minutes"}:
|
92
|
-
return round(num * tb * 60)
|
93
|
-
if unit == "hour":
|
94
|
-
return round(num * tb * 3600)
|
95
|
-
|
96
|
-
if unit != "":
|
97
|
-
raise CoerceError(f"'{val}': Time format got unknown unit: `{unit}`")
|
98
|
-
if not num.is_integer():
|
99
|
-
raise CoerceError(f"'{val}': Time format expects: int?")
|
100
|
-
return int(num)
|
101
|
-
|
102
|
-
|
103
|
-
def parse_color(val: str) -> str:
|
104
|
-
"""
|
105
|
-
Convert a color str into an RGB tuple
|
106
|
-
|
107
|
-
Accepts:
|
108
|
-
- color names (black, red, blue)
|
109
|
-
- 3 digit hex codes (#FFF, #3AE)
|
110
|
-
- 6 digit hex codes (#3F0401, #005601)
|
111
|
-
"""
|
112
|
-
|
113
|
-
color = val.lower()
|
114
|
-
|
115
|
-
if color in colormap:
|
116
|
-
color = colormap[color]
|
117
|
-
|
118
|
-
if re.match("#[a-f0-9]{3}$", color):
|
119
|
-
return "#" + "".join([x * 2 for x in color[1:]])
|
120
|
-
|
121
|
-
if re.match("#[a-f0-9]{6}$", color):
|
122
|
-
return color
|
123
|
-
|
124
|
-
raise ValueError(f"Invalid Color: '{color}'")
|
125
|
-
|
126
|
-
|
127
|
-
colormap = {
|
128
|
-
# Taken from https://www.w3.org/TR/css-color-4/#named-color
|
129
|
-
"aliceblue": "#f0f8ff",
|
130
|
-
"antiquewhite": "#faebd7",
|
131
|
-
"aqua": "#00ffff",
|
132
|
-
"aquamarine": "#7fffd4",
|
133
|
-
"azure": "#f0ffff",
|
134
|
-
"beige": "#f5f5dc",
|
135
|
-
"bisque": "#ffe4c4",
|
136
|
-
"black": "#000000",
|
137
|
-
"blanchedalmond": "#ffebcd",
|
138
|
-
"blue": "#0000ff",
|
139
|
-
"blueviolet": "#8a2be2",
|
140
|
-
"brown": "#a52a2a",
|
141
|
-
"burlywood": "#deb887",
|
142
|
-
"cadetblue": "#5f9ea0",
|
143
|
-
"chartreuse": "#7fff00",
|
144
|
-
"chocolate": "#d2691e",
|
145
|
-
"coral": "#ff7f50",
|
146
|
-
"cornflowerblue": "#6495ed",
|
147
|
-
"cornsilk": "#fff8dc",
|
148
|
-
"crimson": "#dc143c",
|
149
|
-
"cyan": "#00ffff",
|
150
|
-
"darkblue": "#00008b",
|
151
|
-
"darkcyan": "#008b8b",
|
152
|
-
"darkgoldenrod": "#b8860b",
|
153
|
-
"darkgray": "#a9a9a9",
|
154
|
-
"darkgrey": "#a9a9a9",
|
155
|
-
"darkgreen": "#006400",
|
156
|
-
"darkkhaki": "#bdb76b",
|
157
|
-
"darkmagenta": "#8b008b",
|
158
|
-
"darkolivegreen": "#556b2f",
|
159
|
-
"darkorange": "#ff8c00",
|
160
|
-
"darkorchid": "#9932cc",
|
161
|
-
"darkred": "#8b0000",
|
162
|
-
"darksalmon": "#e9967a",
|
163
|
-
"darkseagreen": "#8fbc8f",
|
164
|
-
"darkslateblue": "#483d8b",
|
165
|
-
"darkslategray": "#2f4f4f",
|
166
|
-
"darkslategrey": "#2f4f4f",
|
167
|
-
"darkturquoise": "#00ced1",
|
168
|
-
"darkviolet": "#9400d3",
|
169
|
-
"deeppink": "#ff1493",
|
170
|
-
"deepskyblue": "#00bfff",
|
171
|
-
"dimgray": "#696969",
|
172
|
-
"dimgrey": "#696969",
|
173
|
-
"dodgerblue": "#1e90ff",
|
174
|
-
"firebrick": "#b22222",
|
175
|
-
"floralwhite": "#fffaf0",
|
176
|
-
"forestgreen": "#228b22",
|
177
|
-
"fuchsia": "#ff00ff",
|
178
|
-
"gainsboro": "#dcdcdc",
|
179
|
-
"ghostwhite": "#f8f8ff",
|
180
|
-
"gold": "#ffd700",
|
181
|
-
"goldenrod": "#daa520",
|
182
|
-
"gray": "#808080",
|
183
|
-
"grey": "#808080",
|
184
|
-
"green": "#008000",
|
185
|
-
"greenyellow": "#adff2f",
|
186
|
-
"honeydew": "#f0fff0",
|
187
|
-
"hotpink": "#ff69b4",
|
188
|
-
"indianred": "#cd5c5c",
|
189
|
-
"indigo": "#4b0082",
|
190
|
-
"ivory": "#fffff0",
|
191
|
-
"khaki": "#f0e68c",
|
192
|
-
"lavender": "#e6e6fa",
|
193
|
-
"lavenderblush": "#fff0f5",
|
194
|
-
"lawngreen": "#7cfc00",
|
195
|
-
"lemonchiffon": "#fffacd",
|
196
|
-
"lightblue": "#add8e6",
|
197
|
-
"lightcoral": "#f08080",
|
198
|
-
"lightcyan": "#e0ffff",
|
199
|
-
"lightgoldenrodyellow": "#fafad2",
|
200
|
-
"lightgreen": "#90ee90",
|
201
|
-
"lightgray": "#d3d3d3",
|
202
|
-
"lightgrey": "#d3d3d3",
|
203
|
-
"lightpink": "#ffb6c1",
|
204
|
-
"lightsalmon": "#ffa07a",
|
205
|
-
"lightseagreen": "#20b2aa",
|
206
|
-
"lightskyblue": "#87cefa",
|
207
|
-
"lightslategray": "#778899",
|
208
|
-
"lightslategrey": "#778899",
|
209
|
-
"lightsteelblue": "#b0c4de",
|
210
|
-
"lightyellow": "#ffffe0",
|
211
|
-
"lime": "#00ff00",
|
212
|
-
"limegreen": "#32cd32",
|
213
|
-
"linen": "#faf0e6",
|
214
|
-
"magenta": "#ff00ff",
|
215
|
-
"maroon": "#800000",
|
216
|
-
"mediumaquamarine": "#66cdaa",
|
217
|
-
"mediumblue": "#0000cd",
|
218
|
-
"mediumorchid": "#ba55d3",
|
219
|
-
"mediumpurple": "#9370db",
|
220
|
-
"mediumseagreen": "#3cb371",
|
221
|
-
"mediumslateblue": "#7b68ee",
|
222
|
-
"mediumspringgreen": "#00fa9a",
|
223
|
-
"mediumturquoise": "#48d1cc",
|
224
|
-
"mediumvioletred": "#c71585",
|
225
|
-
"midnightblue": "#191970",
|
226
|
-
"mintcream": "#f5fffa",
|
227
|
-
"mistyrose": "#ffe4e1",
|
228
|
-
"moccasin": "#ffe4b5",
|
229
|
-
"navajowhite": "#ffdead",
|
230
|
-
"navy": "#000080",
|
231
|
-
"oldlace": "#fdf5e6",
|
232
|
-
"olive": "#808000",
|
233
|
-
"olivedrab": "#6b8e23",
|
234
|
-
"orange": "#ffa500",
|
235
|
-
"orangered": "#ff4500",
|
236
|
-
"orchid": "#da70d6",
|
237
|
-
"palegoldenrod": "#eee8aa",
|
238
|
-
"palegreen": "#98fb98",
|
239
|
-
"paleturquoise": "#afeeee",
|
240
|
-
"palevioletred": "#db7093",
|
241
|
-
"papayawhip": "#ffefd5",
|
242
|
-
"peachpuff": "#ffdab9",
|
243
|
-
"peru": "#cd853f",
|
244
|
-
"pink": "#ffc0cb",
|
245
|
-
"plum": "#dda0dd",
|
246
|
-
"powderblue": "#b0e0e6",
|
247
|
-
"purple": "#800080",
|
248
|
-
"rebeccapurple": "#663399",
|
249
|
-
"red": "#ff0000",
|
250
|
-
"rosybrown": "#bc8f8f",
|
251
|
-
"royalblue": "#4169e1",
|
252
|
-
"saddlebrown": "#8b4513",
|
253
|
-
"salmon": "#fa8072",
|
254
|
-
"sandybrown": "#f4a460",
|
255
|
-
"seagreen": "#2e8b57",
|
256
|
-
"seashell": "#fff5ee",
|
257
|
-
"sienna": "#a0522d",
|
258
|
-
"silver": "#c0c0c0",
|
259
|
-
"skyblue": "#87ceeb",
|
260
|
-
"slateblue": "#6a5acd",
|
261
|
-
"slategray": "#708090",
|
262
|
-
"slategrey": "#708090",
|
263
|
-
"snow": "#fffafa",
|
264
|
-
"springgreen": "#00ff7f",
|
265
|
-
"steelblue": "#4682b4",
|
266
|
-
"tan": "#d2b48c",
|
267
|
-
"teal": "#008080",
|
268
|
-
"thistle": "#d8bfd8",
|
269
|
-
"tomato": "#ff6347",
|
270
|
-
"turquoise": "#40e0d0",
|
271
|
-
"violet": "#ee82ee",
|
272
|
-
"wheat": "#f5deb3",
|
273
|
-
"white": "#ffffff",
|
274
|
-
"whitesmoke": "#f5f5f5",
|
275
|
-
"yellow": "#ffff00",
|
276
|
-
"yellowgreen": "#9acd32",
|
277
|
-
}
|