markdown_convert 1.2.49__py3-none-any.whl → 1.2.50__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.
- markdown_convert/__init__.py +1 -1
- markdown_convert/__main__.py +2 -2
- markdown_convert/default.css +2 -2
- markdown_convert/modules/autoinstall.py +71 -0
- markdown_convert/modules/convert.py +10 -3
- markdown_convert/modules/extras.py +100 -0
- markdown_convert/modules/resources.py +1 -1
- markdown_convert/modules/transform.py +20 -27
- {markdown_convert-1.2.49.dist-info → markdown_convert-1.2.50.dist-info}/METADATA +2 -1
- markdown_convert-1.2.50.dist-info/RECORD +18 -0
- markdown_convert-1.2.49.dist-info/RECORD +0 -16
- {markdown_convert-1.2.49.dist-info → markdown_convert-1.2.50.dist-info}/WHEEL +0 -0
- {markdown_convert-1.2.49.dist-info → markdown_convert-1.2.50.dist-info}/entry_points.txt +0 -0
- {markdown_convert-1.2.49.dist-info → markdown_convert-1.2.50.dist-info}/licenses/LICENSE +0 -0
markdown_convert/__init__.py
CHANGED
|
@@ -4,8 +4,8 @@ available to the user.
|
|
|
4
4
|
Author: @julynx
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from .modules.convert import convert, live_convert, convert_text
|
|
8
7
|
from .__main__ import main
|
|
8
|
+
from .modules.convert import convert, convert_text, live_convert
|
|
9
9
|
|
|
10
10
|
__version__ = "1.2.15"
|
|
11
11
|
__all__ = ["convert", "live_convert", "convert_text", "main"]
|
markdown_convert/__main__.py
CHANGED
|
@@ -8,7 +8,8 @@ Author: @julynx
|
|
|
8
8
|
from sys import exit as sys_exit
|
|
9
9
|
|
|
10
10
|
from argsdict import args
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
from .modules.constants import OPTIONS, OPTIONS_MODES, RED
|
|
12
13
|
from .modules.convert import convert, live_convert
|
|
13
14
|
from .modules.resources import get_css_path, get_output_path, get_usage
|
|
14
15
|
from .modules.utils import color
|
|
@@ -75,7 +76,6 @@ def main():
|
|
|
75
76
|
|
|
76
77
|
sys_exit(0)
|
|
77
78
|
|
|
78
|
-
# pylint: disable=W0718
|
|
79
79
|
except Exception as err:
|
|
80
80
|
asked_for_help = "--help" in arg or "-h" in arg
|
|
81
81
|
show_usage = isinstance(err, (IndexError, ValueError)) or asked_for_help
|
markdown_convert/default.css
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Autoinstall chromium headless browser for playwright.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from install_playwright import install
|
|
8
|
+
from playwright.sync_api import sync_playwright
|
|
9
|
+
|
|
10
|
+
from .constants import GREEN, RED
|
|
11
|
+
from .utils import color
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def ensure_chromium(loud=True):
|
|
15
|
+
"""
|
|
16
|
+
Ensures the chromium playwright browser is installed.
|
|
17
|
+
If not, tries to install it.
|
|
18
|
+
"""
|
|
19
|
+
with sync_playwright() as playwright:
|
|
20
|
+
if is_browser_installed(playwright.chromium):
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
if loud:
|
|
24
|
+
print(
|
|
25
|
+
"The Playwright Chromium browser was not found."
|
|
26
|
+
" Attempting to install it, please wait..."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
result = install([playwright.chromium])
|
|
31
|
+
if not result:
|
|
32
|
+
result = install([playwright.chromium], with_deps=True)
|
|
33
|
+
|
|
34
|
+
if loud:
|
|
35
|
+
if result:
|
|
36
|
+
print(
|
|
37
|
+
color(
|
|
38
|
+
GREEN,
|
|
39
|
+
"The Playwright Chromium browser was successfully installed.\n",
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
else:
|
|
43
|
+
print(
|
|
44
|
+
color(
|
|
45
|
+
RED,
|
|
46
|
+
"ERROR: The Playwright Chromium browser could not be automatically installed."
|
|
47
|
+
"\nPlease manually run 'playwright install' and try again.\n",
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
return result
|
|
52
|
+
|
|
53
|
+
except Exception as exc:
|
|
54
|
+
print(
|
|
55
|
+
color(
|
|
56
|
+
RED,
|
|
57
|
+
f"ERROR: There was an exception while trying to install the Playwright Chromium browser:\n{exc}",
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def is_browser_installed(browser):
|
|
64
|
+
"""
|
|
65
|
+
Checks if a specific browser is installed by verifying its executable path.
|
|
66
|
+
Browser type can be 'chromium', 'firefox', or 'webkit'.
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
return os.path.exists(browser.executable_path)
|
|
70
|
+
except Exception:
|
|
71
|
+
return False
|
|
@@ -12,13 +12,19 @@ from pathlib import Path
|
|
|
12
12
|
import markdown2
|
|
13
13
|
from playwright.sync_api import sync_playwright
|
|
14
14
|
|
|
15
|
-
from .
|
|
15
|
+
from .autoinstall import ensure_chromium
|
|
16
|
+
from .constants import (
|
|
17
|
+
BROWSER_ARGS,
|
|
18
|
+
CSP_TEMPLATE,
|
|
19
|
+
MARKDOWN_EXTENSIONS,
|
|
20
|
+
PDF_PARAMS,
|
|
21
|
+
)
|
|
16
22
|
from .resources import get_code_css_path, get_css_path, get_output_path
|
|
17
23
|
from .transform import (
|
|
18
|
-
create_sections,
|
|
19
|
-
render_mermaid_diagrams,
|
|
20
24
|
create_html_document,
|
|
25
|
+
create_sections,
|
|
21
26
|
render_extra_features,
|
|
27
|
+
render_mermaid_diagrams,
|
|
22
28
|
)
|
|
23
29
|
from .utils import drop_duplicates
|
|
24
30
|
|
|
@@ -50,6 +56,7 @@ def _generate_pdf_with_playwright(
|
|
|
50
56
|
csp = CSP_TEMPLATE.format(nonce=nonce)
|
|
51
57
|
full_html = create_html_document(html_content, css_content, csp)
|
|
52
58
|
|
|
59
|
+
ensure_chromium()
|
|
53
60
|
with sync_playwright() as playwright:
|
|
54
61
|
browser = playwright.chromium.launch(headless=True, args=BROWSER_ARGS)
|
|
55
62
|
context = browser.new_context(
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Extras are defined as helper functions called by
|
|
3
|
+
render_extra_features from transform.py
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def create_checkbox(soup, match):
|
|
8
|
+
"""
|
|
9
|
+
Render a tag for a checkbox.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
soup: HTML beautifulsoup
|
|
13
|
+
match: Element identified as a checkbox
|
|
14
|
+
Returns:
|
|
15
|
+
tag: Beautifulsoup tag representing the checkbox
|
|
16
|
+
"""
|
|
17
|
+
tag = soup.new_tag("input", type="checkbox")
|
|
18
|
+
if "[x]" in match.group("checkbox"):
|
|
19
|
+
tag["checked"] = ""
|
|
20
|
+
return tag
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def create_highlight(soup, match):
|
|
24
|
+
"""
|
|
25
|
+
Render a tag for a highlight.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
soup: HTML beautifulsoup
|
|
29
|
+
match: Element identified as a highlight
|
|
30
|
+
Returns:
|
|
31
|
+
tag: Beautifulsoup tag representing the highlight
|
|
32
|
+
"""
|
|
33
|
+
tag = soup.new_tag("span", attrs={"class": "highlight"})
|
|
34
|
+
tag.string = match.group("hl_content")
|
|
35
|
+
return tag
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def create_custom_span(soup, match):
|
|
39
|
+
"""
|
|
40
|
+
Render a tag for a custom span.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
soup: HTML beautifulsoup
|
|
44
|
+
match: Element identified as a custom span
|
|
45
|
+
Returns:
|
|
46
|
+
tag: Beautifulsoup tag representing the custom span
|
|
47
|
+
"""
|
|
48
|
+
tag = soup.new_tag("span", attrs={"class": match.group("cls")})
|
|
49
|
+
tag.string = match.group("sp_content")
|
|
50
|
+
return tag
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def create_toc(soup, match):
|
|
54
|
+
"""
|
|
55
|
+
Render a tag for a table of contents
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
soup: HTML beautifulsoup
|
|
59
|
+
match: Element identified as a table of contents
|
|
60
|
+
Returns:
|
|
61
|
+
tag: Beautifulsoup tag representing the table of contents
|
|
62
|
+
"""
|
|
63
|
+
max_level = match.group("depth")
|
|
64
|
+
max_level = 3 if max_level is None else int(max_level)
|
|
65
|
+
|
|
66
|
+
headers = [
|
|
67
|
+
header
|
|
68
|
+
for header in soup.find_all([f"h{index}" for index in range(1, max_level + 1)])
|
|
69
|
+
if header.get("id")
|
|
70
|
+
]
|
|
71
|
+
if not headers:
|
|
72
|
+
return ""
|
|
73
|
+
|
|
74
|
+
tag = soup.new_tag("ul", attrs={"class": "toc"})
|
|
75
|
+
active_list = {0: tag}
|
|
76
|
+
last_list_element = {}
|
|
77
|
+
|
|
78
|
+
for header in headers:
|
|
79
|
+
level = int(header.name[1])
|
|
80
|
+
|
|
81
|
+
if level not in active_list:
|
|
82
|
+
parent_lvl = max(key for key in active_list if key < level)
|
|
83
|
+
if last_list_element.get(parent_lvl):
|
|
84
|
+
sub_list = soup.new_tag("ul")
|
|
85
|
+
last_list_element[parent_lvl].append(sub_list)
|
|
86
|
+
active_list[level] = sub_list
|
|
87
|
+
else:
|
|
88
|
+
active_list[level] = active_list[parent_lvl]
|
|
89
|
+
|
|
90
|
+
active_list = {key: value for key, value in active_list.items() if key <= level}
|
|
91
|
+
|
|
92
|
+
list_item = soup.new_tag("li")
|
|
93
|
+
link = soup.new_tag("a", href=f"#{header['id']}")
|
|
94
|
+
link.string = header.get_text(strip=True)
|
|
95
|
+
list_item.append(link)
|
|
96
|
+
|
|
97
|
+
active_list[level].append(list_item)
|
|
98
|
+
last_list_element[level] = list_item
|
|
99
|
+
|
|
100
|
+
return tag
|
|
@@ -13,7 +13,7 @@ except ImportError:
|
|
|
13
13
|
# Fallback for older Python versions
|
|
14
14
|
from importlib_resources import files
|
|
15
15
|
|
|
16
|
-
from .constants import BLUE, CYAN, GREEN,
|
|
16
|
+
from .constants import BLUE, CYAN, GREEN, OPTIONS, OPTIONS_MODES, YELLOW
|
|
17
17
|
from .utils import color
|
|
18
18
|
|
|
19
19
|
|
|
@@ -6,6 +6,10 @@ import re
|
|
|
6
6
|
|
|
7
7
|
from bs4 import BeautifulSoup
|
|
8
8
|
|
|
9
|
+
from .constants import YELLOW
|
|
10
|
+
from .extras import create_checkbox, create_custom_span, create_highlight, create_toc
|
|
11
|
+
from .utils import color
|
|
12
|
+
|
|
9
13
|
|
|
10
14
|
def create_html_document(html_content, css_content, csp):
|
|
11
15
|
"""
|
|
@@ -34,8 +38,8 @@ def create_html_document(html_content, css_content, csp):
|
|
|
34
38
|
|
|
35
39
|
def create_sections(html_string):
|
|
36
40
|
"""
|
|
37
|
-
Wraps each h2
|
|
38
|
-
The section ends when the next h2
|
|
41
|
+
Wraps each h2 and its following content in a <section> tag.
|
|
42
|
+
The section ends when the next h2 is encountered, or the parent ends.
|
|
39
43
|
|
|
40
44
|
Args:
|
|
41
45
|
html_string (str): The input HTML string.
|
|
@@ -44,14 +48,12 @@ def create_sections(html_string):
|
|
|
44
48
|
"""
|
|
45
49
|
soup = BeautifulSoup(html_string, "html.parser")
|
|
46
50
|
|
|
47
|
-
for header in soup.find_all(
|
|
51
|
+
for header in soup.find_all("h2"):
|
|
48
52
|
new_section = soup.new_tag("section")
|
|
49
53
|
header.insert_before(new_section)
|
|
50
54
|
|
|
51
55
|
current = header
|
|
52
|
-
while current is not None and (
|
|
53
|
-
current == header or current.name not in ["h2", "h3"]
|
|
54
|
-
):
|
|
56
|
+
while current is not None and (current == header or current.name != "h2"):
|
|
55
57
|
next_sibling = current.next_sibling
|
|
56
58
|
new_section.append(current)
|
|
57
59
|
current = next_sibling
|
|
@@ -97,32 +99,18 @@ def render_extra_features(html):
|
|
|
97
99
|
str: HTML content with extra features rendered.
|
|
98
100
|
"""
|
|
99
101
|
|
|
100
|
-
def _create_checkbox(soup, match):
|
|
101
|
-
tag = soup.new_tag("input", type="checkbox")
|
|
102
|
-
if "[x]" in match.group("checkbox"):
|
|
103
|
-
tag["checked"] = ""
|
|
104
|
-
return tag
|
|
105
|
-
|
|
106
|
-
def _create_highlight(soup, match):
|
|
107
|
-
tag = soup.new_tag("span", attrs={"class": "highlight"})
|
|
108
|
-
tag.string = match.group("hl_content")
|
|
109
|
-
return tag
|
|
110
|
-
|
|
111
|
-
def _create_custom_span(soup, match):
|
|
112
|
-
tag = soup.new_tag("span", attrs={"class": match.group("cls")})
|
|
113
|
-
tag.string = match.group("sp_content")
|
|
114
|
-
return tag
|
|
115
|
-
|
|
116
102
|
handlers = {
|
|
117
|
-
"checkbox":
|
|
118
|
-
"highlight":
|
|
119
|
-
"span":
|
|
103
|
+
"checkbox": create_checkbox,
|
|
104
|
+
"highlight": create_highlight,
|
|
105
|
+
"span": create_custom_span,
|
|
106
|
+
"toc": create_toc,
|
|
120
107
|
}
|
|
121
108
|
|
|
122
109
|
master_pattern = re.compile(
|
|
123
110
|
r"(?P<checkbox>\[\s\]|\[x\])|"
|
|
124
111
|
r"(?P<highlight>==(?P<hl_content>.*?)==)|"
|
|
125
|
-
r"(?P<span>(?P<cls>[a-zA-Z0-9_-]+)\{\{\s*(?P<sp_content>.*?)\s*\}\})"
|
|
112
|
+
r"(?P<span>(?P<cls>[a-zA-Z0-9_-]+)\{\{\s*(?P<sp_content>.*?)\s*\}\})|"
|
|
113
|
+
r"(?P<toc>\[TOC(?:\s+depth=(?P<depth>\d+))?\])"
|
|
126
114
|
)
|
|
127
115
|
|
|
128
116
|
ignored_tags = {"code", "pre", "script", "style"}
|
|
@@ -156,7 +144,12 @@ def render_extra_features(html):
|
|
|
156
144
|
tag = handler(soup, match)
|
|
157
145
|
new_nodes.append(tag)
|
|
158
146
|
except Exception as exc:
|
|
159
|
-
print(
|
|
147
|
+
print(
|
|
148
|
+
color(
|
|
149
|
+
YELLOW,
|
|
150
|
+
f"WARNING: Handler for '{kind}' failed with exception: {exc}",
|
|
151
|
+
)
|
|
152
|
+
)
|
|
160
153
|
new_nodes.append(match.group(0))
|
|
161
154
|
|
|
162
155
|
last_end = end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: markdown_convert
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.50
|
|
4
4
|
Summary: Convert Markdown files to PDF from your command line.
|
|
5
5
|
Project-URL: homepage, https://github.com/Julynx/markdown_convert
|
|
6
6
|
Author-email: Julio Cabria <juliocabria@tutanota.com>
|
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: <3.15,>=3.11
|
|
13
13
|
Requires-Dist: argsdict==1.0.0
|
|
14
14
|
Requires-Dist: beautifulsoup4>=4.14.3
|
|
15
|
+
Requires-Dist: install-playwright>=1.0.0
|
|
15
16
|
Requires-Dist: latex2mathml>=3.78.1
|
|
16
17
|
Requires-Dist: markdown2<3,>=2.4.13
|
|
17
18
|
Requires-Dist: playwright>=1.57.0
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
markdown_convert/__init__.py,sha256=0hLMtJnCIuApqopx5P4tiDSw850AmnuVcohmAbPVEZ4,303
|
|
2
|
+
markdown_convert/__main__.py,sha256=AocRo1iF1El_-Uo0owJ-QLbJUF0rum5R_AlNrTTTSOQ,2780
|
|
3
|
+
markdown_convert/code.css,sha256=Wt4FqFqJcpT-jwY3GN-o4ZRCCXU8DQj-9lqKdGiuoyw,4935
|
|
4
|
+
markdown_convert/default.css,sha256=zplOTdw08l9JKadIOw5weyJVkSkdzhKgUVCJsBS3Tew,6355
|
|
5
|
+
markdown_convert/modules/__init__.py,sha256=PFPgiQhMXgyfjD8BkfLC_X8AR1jz-dCxfif2qmNofJs,65
|
|
6
|
+
markdown_convert/modules/autoinstall.py,sha256=Tnrde6MIcO11PWT7GZwhs_QTVRy6CSpUB_gIi9G5ve8,2063
|
|
7
|
+
markdown_convert/modules/constants.py,sha256=eUeIFRxZerP3E0Rgp_Nsl0Q38IBELwca5UO3fbUcxRA,1280
|
|
8
|
+
markdown_convert/modules/convert.py,sha256=1AjQfnOXJoxKyfqr4misDuTvE4YXnwaoWw668FUHiEQ,8972
|
|
9
|
+
markdown_convert/modules/extras.py,sha256=GwNx6nseztHOWExcYmovxomdvOs078dMeknQTwzTCJo,2730
|
|
10
|
+
markdown_convert/modules/resources.py,sha256=eskLLbrkLJWs-vqtCLq4qV2Hjy6XeGFCUdT0VN2b_tA,2488
|
|
11
|
+
markdown_convert/modules/transform.py,sha256=e4QllWx5BYKEQqIzOkYigtxcSAWqSUHsoKkvqzYzEpY,4567
|
|
12
|
+
markdown_convert/modules/utils.py,sha256=NX0WegM8e8MPKNNmweTujAWO8ZghdB8LSGDx20K2E44,655
|
|
13
|
+
markdown_convert/modules/validate.py,sha256=XV_k7cHeifEKDaltF26tCmabs2-Me5msP3enI_eVwfA,1517
|
|
14
|
+
markdown_convert-1.2.50.dist-info/METADATA,sha256=9AJbE8JuV_d3GW4WXETmjSweHzHhwdN09ytPz9XxalQ,4118
|
|
15
|
+
markdown_convert-1.2.50.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
16
|
+
markdown_convert-1.2.50.dist-info/entry_points.txt,sha256=RCmzC7C0sX-SpzIP2Cr34rhg3lMd7BRx-exaZPfK8bU,68
|
|
17
|
+
markdown_convert-1.2.50.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
|
|
18
|
+
markdown_convert-1.2.50.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
markdown_convert/__init__.py,sha256=ysW3pXsDGGK4PzZHcIBTpfVW58IkDUwHffDkf_GM6UU,303
|
|
2
|
-
markdown_convert/__main__.py,sha256=w6sHfJcJQpMOERPqiNoKgoRM38YUwK22eGBmVkpZj1g,2807
|
|
3
|
-
markdown_convert/code.css,sha256=Wt4FqFqJcpT-jwY3GN-o4ZRCCXU8DQj-9lqKdGiuoyw,4935
|
|
4
|
-
markdown_convert/default.css,sha256=ZL2f-SQIM2BG1sGD51wRqTN06K2rnIYyELXdJQq1xRs,6355
|
|
5
|
-
markdown_convert/modules/__init__.py,sha256=PFPgiQhMXgyfjD8BkfLC_X8AR1jz-dCxfif2qmNofJs,65
|
|
6
|
-
markdown_convert/modules/constants.py,sha256=eUeIFRxZerP3E0Rgp_Nsl0Q38IBELwca5UO3fbUcxRA,1280
|
|
7
|
-
markdown_convert/modules/convert.py,sha256=4UEZhx_v4lqcgTYeCiQ3V4Bkxz4siHCKQIHGvHICHuI,8888
|
|
8
|
-
markdown_convert/modules/resources.py,sha256=tnW8JmCrJNBRbzOcaOVG6GX5jPC8Kzj3dA7gX0B935A,2488
|
|
9
|
-
markdown_convert/modules/transform.py,sha256=_Pz-t_7g_n_IwtHtc386WDugkw6CfDpdgw_aFgiSM4Q,4796
|
|
10
|
-
markdown_convert/modules/utils.py,sha256=NX0WegM8e8MPKNNmweTujAWO8ZghdB8LSGDx20K2E44,655
|
|
11
|
-
markdown_convert/modules/validate.py,sha256=XV_k7cHeifEKDaltF26tCmabs2-Me5msP3enI_eVwfA,1517
|
|
12
|
-
markdown_convert-1.2.49.dist-info/METADATA,sha256=PkXwu7z2NarPFUwveuXstMJ-I2jQfdF8WzMZ9LwfZfA,4077
|
|
13
|
-
markdown_convert-1.2.49.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
14
|
-
markdown_convert-1.2.49.dist-info/entry_points.txt,sha256=RCmzC7C0sX-SpzIP2Cr34rhg3lMd7BRx-exaZPfK8bU,68
|
|
15
|
-
markdown_convert-1.2.49.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
|
|
16
|
-
markdown_convert-1.2.49.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|