openmd-cli 0.1.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.
- openmd/__init__.py +3 -0
- openmd/__main__.py +5 -0
- openmd/adapters/__init__.py +1 -0
- openmd/adapters/base.py +95 -0
- openmd/adapters/bilibili/__init__.py +11 -0
- openmd/adapters/bilibili/adapter.py +104 -0
- openmd/adapters/bilibili/discovery.py +33 -0
- openmd/adapters/douyin/__init__.py +11 -0
- openmd/adapters/douyin/adapter.py +117 -0
- openmd/adapters/douyin/discovery.py +36 -0
- openmd/adapters/douyin/sidecar.py +77 -0
- openmd/adapters/generic_media/__init__.py +14 -0
- openmd/adapters/high_privacy.py +77 -0
- openmd/adapters/local_audio/__init__.py +14 -0
- openmd/adapters/local_audio/adapter.py +169 -0
- openmd/adapters/local_audio/asr.py +58 -0
- openmd/adapters/local_audio/backends/__init__.py +1 -0
- openmd/adapters/local_audio/backends/auto.py +177 -0
- openmd/adapters/local_audio/backends/faster_whisper.py +285 -0
- openmd/adapters/local_audio/backends/funasr_sense_voice.py +14 -0
- openmd/adapters/local_audio/backends/qwen3_asr.py +254 -0
- openmd/adapters/opencli.py +770 -0
- openmd/adapters/pdf/__init__.py +11 -0
- openmd/adapters/pdf/adapter.py +153 -0
- openmd/adapters/pdf/backends/__init__.py +1 -0
- openmd/adapters/pdf/backends/markitdown.py +165 -0
- openmd/adapters/pdf/parser.py +56 -0
- openmd/adapters/qq_group/__init__.py +11 -0
- openmd/adapters/qq_group/adapter.py +289 -0
- openmd/adapters/qq_group/parsers/__init__.py +1 -0
- openmd/adapters/qq_group/parsers/html.py +60 -0
- openmd/adapters/qq_group/parsers/json.py +71 -0
- openmd/adapters/registry.py +149 -0
- openmd/adapters/screenshot/__init__.py +11 -0
- openmd/adapters/screenshot/adapter.py +154 -0
- openmd/adapters/screenshot/backends/__init__.py +1 -0
- openmd/adapters/screenshot/backends/paddle.py +15 -0
- openmd/adapters/screenshot/backends/rapidocr.py +176 -0
- openmd/adapters/screenshot/backends/tesseract.py +15 -0
- openmd/adapters/screenshot/ocr.py +84 -0
- openmd/adapters/wechat_group/__init__.py +11 -0
- openmd/adapters/wechat_group/adapter.py +160 -0
- openmd/adapters/wechat_group/wx_cli.py +268 -0
- openmd/adapters/wechat_moments/__init__.py +11 -0
- openmd/adapters/wechat_moments/adapter.py +167 -0
- openmd/adapters/wechat_moments/wx_cli.py +265 -0
- openmd/adapters/weixin/__init__.py +11 -0
- openmd/adapters/weixin/adapter.py +148 -0
- openmd/adapters/weixin/discovery.py +510 -0
- openmd/adapters/weixin/report.py +278 -0
- openmd/adapters/xiaohongshu.py +158 -0
- openmd/backends/__init__.py +1 -0
- openmd/backends/base.py +204 -0
- openmd/backends/bilibili.py +1480 -0
- openmd/backends/bilibili_wbi.py +277 -0
- openmd/backends/discourse.py +426 -0
- openmd/backends/douyin.py +868 -0
- openmd/backends/douyin_detail.py +124 -0
- openmd/backends/douyin_providers.py +602 -0
- openmd/backends/executor.py +546 -0
- openmd/backends/feishu.py +861 -0
- openmd/backends/generic_media.py +77 -0
- openmd/backends/interactive.py +60 -0
- openmd/backends/ocr.py +41 -0
- openmd/backends/opencli.py +82 -0
- openmd/backends/pdf.py +122 -0
- openmd/backends/registry.py +234 -0
- openmd/backends/screenshot.py +79 -0
- openmd/backends/video_metadata.py +167 -0
- openmd/backends/web_article.py +363 -0
- openmd/backends/wechat_group.py +35 -0
- openmd/backends/wechat_moments.py +35 -0
- openmd/backends/weixin.py +767 -0
- openmd/backends/x.py +455 -0
- openmd/backends/xiaoe.py +940 -0
- openmd/backends/xiaohongshu.py +911 -0
- openmd/backends/xiaohongshu_providers.py +876 -0
- openmd/backends/xiaoyuzhou.py +1017 -0
- openmd/backends/youtube.py +280 -0
- openmd/backends/youtube_discovery.py +1245 -0
- openmd/backends/youtube_transcript.py +213 -0
- openmd/browser_extension/background.js +231 -0
- openmd/browser_extension/icons/icon-128.png +0 -0
- openmd/browser_extension/icons/icon-16.png +0 -0
- openmd/browser_extension/icons/icon-32.png +0 -0
- openmd/browser_extension/icons/icon-48.png +0 -0
- openmd/browser_extension/icons/icon-512.png +0 -0
- openmd/browser_extension/idle.html +6 -0
- openmd/browser_extension/manifest.json +43 -0
- openmd/browser_extension/options.html +42 -0
- openmd/browser_extension/options.js +117 -0
- openmd/browser_extension/popup.css +71 -0
- openmd/browser_extension/popup.html +17 -0
- openmd/browser_extension/popup.js +59 -0
- openmd/browser_extension/vendor/opencli-bridge/LICENSE +190 -0
- openmd/browser_extension/vendor/opencli-bridge/VENDOR.md +31 -0
- openmd/browser_extension/vendor/opencli-bridge/background.js +2742 -0
- openmd/browser_host.py +503 -0
- openmd/browser_setup.py +106 -0
- openmd/cli.py +2012 -0
- openmd/console.py +32 -0
- openmd/core/__init__.py +1 -0
- openmd/core/capabilities.py +162 -0
- openmd/core/content_validation.py +323 -0
- openmd/core/coverage.py +163 -0
- openmd/core/detector.py +247 -0
- openmd/core/doctor.py +104 -0
- openmd/core/execution_policy.py +164 -0
- openmd/core/filename.py +64 -0
- openmd/core/input_validation.py +91 -0
- openmd/core/local_config.py +96 -0
- openmd/core/manifest.py +156 -0
- openmd/core/models.py +168 -0
- openmd/core/opencli_output.py +34 -0
- openmd/core/operation_contract.py +60 -0
- openmd/core/output.py +194 -0
- openmd/core/package_contract.py +335 -0
- openmd/core/package_validator.py +489 -0
- openmd/core/package_writer.py +666 -0
- openmd/core/planner.py +345 -0
- openmd/core/preferences.py +61 -0
- openmd/core/readable.py +129 -0
- openmd/core/receipt.py +285 -0
- openmd/core/renderer.py +185 -0
- openmd/core/runlog.py +208 -0
- openmd/core/secrets.py +313 -0
- openmd/doctor_view.py +68 -0
- openmd/interactive.py +453 -0
- openmd/ocr/__init__.py +1 -0
- openmd/ocr/breakers.py +84 -0
- openmd/ocr/config.py +54 -0
- openmd/ocr/documents.py +522 -0
- openmd/ocr/endpoint.py +72 -0
- openmd/ocr/layout.py +92 -0
- openmd/ocr/layout_filter.py +118 -0
- openmd/ocr/layout_worker.py +89 -0
- openmd/ocr/native_math.py +220 -0
- openmd/ocr/office.py +305 -0
- openmd/ocr/ollama.py +200 -0
- openmd/ocr/otsl.py +51 -0
- openmd/ocr/ovis-prompt.txt +1 -0
- openmd/ocr/pipeline.py +384 -0
- openmd/ocr/settings.json +25 -0
- openmd/ocr/tiling.py +631 -0
- openmd/post_video.py +154 -0
- openmd/probe.py +397 -0
- openmd/routing/__init__.py +1 -0
- openmd/routing/cards/builtin.json +620 -0
- openmd/routing/cards/defuddle.json +70 -0
- openmd/routing/cards/x-tweet-fetcher.json +71 -0
- openmd/routing/cards/xyz-dl.json +60 -0
- openmd/routing/cards/yutto.json +68 -0
- openmd/routing/entry_shim.py +71 -0
- openmd/routing/lifecycle.py +459 -0
- openmd/routing/loader.py +138 -0
- openmd/routing/providers.py +158 -0
- openmd/routing/readers.py +108 -0
- openmd/routing/routes.json +504 -0
- openmd/routing/runner.py +192 -0
- openmd/routing/view.py +40 -0
- openmd/runtime/__init__.py +1 -0
- openmd/runtime/_windows_job.py +234 -0
- openmd/runtime/asr.py +217 -0
- openmd/runtime/asr_worker.py +119 -0
- openmd/runtime/gpu_lock.py +81 -0
- openmd/runtime/gpu_quarantine.py +85 -0
- openmd/runtime/interpreters.py +407 -0
- openmd/runtime/managed_tools.py +204 -0
- openmd/runtime/models.py +202 -0
- openmd/runtime/services.py +175 -0
- openmd/runtime/tempdirs.py +227 -0
- openmd_cli-0.1.0.dist-info/METADATA +168 -0
- openmd_cli-0.1.0.dist-info/RECORD +177 -0
- openmd_cli-0.1.0.dist-info/WHEEL +5 -0
- openmd_cli-0.1.0.dist-info/entry_points.txt +2 -0
- openmd_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- openmd_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Owned, recoverable temp directories; sweep only direct Openmd children of system temp."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import ctypes
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import shutil
|
|
9
|
+
import stat
|
|
10
|
+
import tempfile
|
|
11
|
+
import threading
|
|
12
|
+
import time
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
OWNER_FILE = ".openmd-owner.json"
|
|
16
|
+
MAX_AGE_S = 24 * 60 * 60
|
|
17
|
+
MAX_OWNER_PIDS = 256
|
|
18
|
+
MAX_OWNER_BYTES = 4096
|
|
19
|
+
_startup_report = None
|
|
20
|
+
_owned_directories: set[Path] = set()
|
|
21
|
+
_owner_lock = threading.RLock()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def pid_alive(pid: int) -> bool:
|
|
25
|
+
"""Treat inaccessible process state as live. Never use Windows os.kill(pid, 0)."""
|
|
26
|
+
if pid <= 0:
|
|
27
|
+
return False
|
|
28
|
+
if os.name == "nt":
|
|
29
|
+
from ctypes import wintypes
|
|
30
|
+
|
|
31
|
+
kernel = ctypes.WinDLL("kernel32", use_last_error=True)
|
|
32
|
+
kernel.OpenProcess.argtypes = (wintypes.DWORD, wintypes.BOOL, wintypes.DWORD)
|
|
33
|
+
kernel.OpenProcess.restype = wintypes.HANDLE
|
|
34
|
+
kernel.GetExitCodeProcess.argtypes = (wintypes.HANDLE, ctypes.POINTER(wintypes.DWORD))
|
|
35
|
+
kernel.CloseHandle.argtypes = (wintypes.HANDLE,)
|
|
36
|
+
handle = kernel.OpenProcess(0x1000, False, pid)
|
|
37
|
+
if not handle:
|
|
38
|
+
return ctypes.get_last_error() != 87 # invalid parameter means no such PID
|
|
39
|
+
try:
|
|
40
|
+
code = wintypes.DWORD()
|
|
41
|
+
return not kernel.GetExitCodeProcess(handle, ctypes.byref(code)) or code.value == 259
|
|
42
|
+
finally:
|
|
43
|
+
kernel.CloseHandle(handle)
|
|
44
|
+
try:
|
|
45
|
+
os.kill(pid, 0)
|
|
46
|
+
except ProcessLookupError:
|
|
47
|
+
return False
|
|
48
|
+
except PermissionError:
|
|
49
|
+
return True
|
|
50
|
+
# A killed-but-unreaped (zombie) process still answers signal 0, yet holds
|
|
51
|
+
# no files and never runs again; orphans wait for init to reap them.
|
|
52
|
+
try:
|
|
53
|
+
with open(f"/proc/{pid}/stat", encoding="ascii", errors="replace") as stat:
|
|
54
|
+
state = stat.read().rsplit(")", 1)[1].split()[0]
|
|
55
|
+
except (OSError, IndexError):
|
|
56
|
+
return True # no /proc (macOS) or vanished mid-read: keep the safe answer
|
|
57
|
+
return state not in {"Z", "X"}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _read_owner(marker: Path) -> dict:
|
|
61
|
+
"""One bounded schema/size contract for readers and writers."""
|
|
62
|
+
if _linked(marker):
|
|
63
|
+
raise ValueError("linked owner marker")
|
|
64
|
+
with marker.open("rb") as source:
|
|
65
|
+
data = source.read(MAX_OWNER_BYTES + 1)
|
|
66
|
+
if len(data) > MAX_OWNER_BYTES:
|
|
67
|
+
raise ValueError("oversized owner marker")
|
|
68
|
+
owner = json.loads(data)
|
|
69
|
+
pid, pids = owner.get("pid"), owner.get("pids", [])
|
|
70
|
+
if not isinstance(pids, list) or len(pids) > MAX_OWNER_PIDS or not all(
|
|
71
|
+
isinstance(p, int) and not isinstance(p, bool) and 0 < p <= 0xFFFFFFFF
|
|
72
|
+
for p in [pid, *pids]
|
|
73
|
+
) or not isinstance(owner.get("incomplete", False), bool):
|
|
74
|
+
raise ValueError("invalid owner marker")
|
|
75
|
+
return owner
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _write_owner(marker: Path, owner: dict) -> None:
|
|
79
|
+
data = json.dumps(owner, separators=(",", ":")).encode("utf-8")
|
|
80
|
+
if len(data) > MAX_OWNER_BYTES or len(owner.get("pids", [])) > MAX_OWNER_PIDS:
|
|
81
|
+
raise ValueError("owner marker exceeds shared contract")
|
|
82
|
+
# Readers see the old or the new complete record, never a truncated write.
|
|
83
|
+
fd, name = tempfile.mkstemp(prefix=OWNER_FILE + ".", suffix=".tmp", dir=marker.parent)
|
|
84
|
+
try:
|
|
85
|
+
with os.fdopen(fd, "wb") as destination:
|
|
86
|
+
destination.write(data)
|
|
87
|
+
os.replace(name, marker)
|
|
88
|
+
finally:
|
|
89
|
+
try:
|
|
90
|
+
os.unlink(name)
|
|
91
|
+
except FileNotFoundError:
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _mark(directory) -> None:
|
|
96
|
+
try:
|
|
97
|
+
_write_owner(Path(directory) / OWNER_FILE, {"pid": os.getpid()})
|
|
98
|
+
with _owner_lock:
|
|
99
|
+
_owned_directories.add(Path(directory).resolve())
|
|
100
|
+
except OSError:
|
|
101
|
+
# Failure to establish ownership must never leave an unprotected live directory.
|
|
102
|
+
shutil.rmtree(directory, ignore_errors=True)
|
|
103
|
+
raise
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def owned_mkdtemp(*args, **kwargs):
|
|
107
|
+
directory = tempfile.mkdtemp(*args, **kwargs)
|
|
108
|
+
_mark(directory)
|
|
109
|
+
return directory
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def owned_temporary_directory(*args, **kwargs):
|
|
113
|
+
directory = tempfile.TemporaryDirectory(*args, **kwargs)
|
|
114
|
+
_mark(directory.name)
|
|
115
|
+
return directory
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def note_process_owner(process) -> None:
|
|
119
|
+
"""Protect active workspaces even if the controller dies before its workers.
|
|
120
|
+
|
|
121
|
+
A conservative association with all active workspaces in this controller is
|
|
122
|
+
intentional: retain an extra directory rather than remove a live worker's
|
|
123
|
+
input. Prune exited owners on EVERY write, including duplicate/dead inputs.
|
|
124
|
+
Inaccessible PIDs count as alive; true simultaneous overflow is unknown.
|
|
125
|
+
"""
|
|
126
|
+
pid = getattr(process, "pid", None)
|
|
127
|
+
if not isinstance(pid, int) or pid <= 0:
|
|
128
|
+
return
|
|
129
|
+
with _owner_lock:
|
|
130
|
+
for directory in tuple(_owned_directories):
|
|
131
|
+
marker = directory / OWNER_FILE
|
|
132
|
+
try:
|
|
133
|
+
if not marker.is_file():
|
|
134
|
+
_owned_directories.discard(directory)
|
|
135
|
+
continue
|
|
136
|
+
owner = _read_owner(marker)
|
|
137
|
+
candidates = dict.fromkeys([owner["pid"], *owner.get("pids", []), os.getpid(), pid])
|
|
138
|
+
pids = [p for p in candidates if pid_alive(p)]
|
|
139
|
+
incomplete = owner.get("incomplete", False) or len(pids) > MAX_OWNER_PIDS
|
|
140
|
+
updated = {"pid": os.getpid(), "pids": pids[:MAX_OWNER_PIDS]}
|
|
141
|
+
if incomplete:
|
|
142
|
+
# Never silently drop untracked live ownership. Such rare
|
|
143
|
+
# overflow stays unknown until an explicit manual cleanup.
|
|
144
|
+
updated["incomplete"] = True
|
|
145
|
+
_write_owner(marker, updated)
|
|
146
|
+
except (OSError, ValueError, AttributeError, TypeError):
|
|
147
|
+
pass # Unreadable/malformed markers are retained by the sweeper.
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _linked(path: Path) -> bool:
|
|
151
|
+
info = path.lstat()
|
|
152
|
+
return stat.S_ISLNK(info.st_mode) or bool(getattr(info, "st_file_attributes", 0) & 0x400)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _size(path: Path) -> int:
|
|
156
|
+
total = 0
|
|
157
|
+
try:
|
|
158
|
+
for child in path.iterdir():
|
|
159
|
+
if not _linked(child):
|
|
160
|
+
total += _size(child) if child.is_dir() else child.stat().st_size
|
|
161
|
+
except OSError:
|
|
162
|
+
pass
|
|
163
|
+
return total
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def sweep_tempdirs(*, include_sizes: bool = True) -> dict:
|
|
167
|
+
root = Path(tempfile.gettempdir()).resolve()
|
|
168
|
+
report = {"root": str(root), "deleted": 0, "retained": 0, "failed": 0, "deleted_bytes": 0, "retained_bytes": 0, "sizes_measured": include_sizes, "errors": []}
|
|
169
|
+
now = time.time()
|
|
170
|
+
try:
|
|
171
|
+
with os.scandir(root) as entries:
|
|
172
|
+
for entry in entries:
|
|
173
|
+
if not entry.name.startswith("openmd-"):
|
|
174
|
+
continue
|
|
175
|
+
path = Path(entry.path)
|
|
176
|
+
try:
|
|
177
|
+
# Check lexical and resolved parent immediately before any removal.
|
|
178
|
+
if _linked(path) or not entry.is_dir(follow_symlinks=False) or path.resolve().parent != root:
|
|
179
|
+
continue
|
|
180
|
+
age = now - entry.stat(follow_symlinks=False).st_mtime
|
|
181
|
+
size = _size(path) if include_sizes else 0
|
|
182
|
+
retain = age <= MAX_AGE_S
|
|
183
|
+
marker = path / OWNER_FILE
|
|
184
|
+
if not retain and marker.exists():
|
|
185
|
+
# Invalid/unreadable markers are unknown ownership, so retain.
|
|
186
|
+
try:
|
|
187
|
+
owner = _read_owner(marker)
|
|
188
|
+
retain = owner.get("incomplete", False) or any(pid_alive(p) for p in [owner["pid"], *owner.get("pids", [])])
|
|
189
|
+
except (OSError, ValueError, AttributeError):
|
|
190
|
+
retain = True
|
|
191
|
+
if retain:
|
|
192
|
+
report["retained"] += 1
|
|
193
|
+
report["retained_bytes"] += size
|
|
194
|
+
continue
|
|
195
|
+
# Revalidate after ownership and size inspection; never follow a swapped link.
|
|
196
|
+
if _linked(path) or path.resolve().parent != root:
|
|
197
|
+
continue
|
|
198
|
+
try:
|
|
199
|
+
shutil.rmtree(path)
|
|
200
|
+
except OSError:
|
|
201
|
+
report["retained"] += 1
|
|
202
|
+
report["retained_bytes"] += size
|
|
203
|
+
raise
|
|
204
|
+
report["deleted"] += 1
|
|
205
|
+
report["deleted_bytes"] += size
|
|
206
|
+
except OSError as exc:
|
|
207
|
+
report["failed"] += 1
|
|
208
|
+
report["errors"].append({"directory": entry.name, "error": type(exc).__name__})
|
|
209
|
+
except OSError as exc:
|
|
210
|
+
report["failed"] += 1
|
|
211
|
+
report["errors"].append({"error": type(exc).__name__})
|
|
212
|
+
return report
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def sweep_at_startup() -> dict:
|
|
216
|
+
global _startup_report
|
|
217
|
+
if _startup_report is None:
|
|
218
|
+
# Never delay the CLI for directory removal or size scans. An interrupted
|
|
219
|
+
# sweep is safe to retry on the next startup; doctor runs synchronously.
|
|
220
|
+
report = {"started": True}
|
|
221
|
+
_startup_report = report
|
|
222
|
+
|
|
223
|
+
def sweep():
|
|
224
|
+
report.update(sweep_tempdirs(include_sizes=False))
|
|
225
|
+
|
|
226
|
+
threading.Thread(target=sweep, name="openmd-temp-sweep", daemon=True).start()
|
|
227
|
+
return _startup_report
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openmd-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: 把网页文章、公众号、视频字幕等链接保存成干净的 Markdown 原文 / 逐字稿。
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/baigao417/openmd
|
|
7
|
+
Project-URL: Issues, https://github.com/baigao417/openmd/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/baigao417/openmd/commits/main
|
|
9
|
+
Keywords: markdown,transcript,subtitle,wechat,bilibili,youtube,obsidian,clipper
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Environment :: Console
|
|
18
|
+
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
19
|
+
Classifier: Natural Language :: Chinese (Simplified)
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: trafilatura<3,>=2
|
|
24
|
+
Requires-Dist: youtube-transcript-api<2,>=1.2
|
|
25
|
+
Requires-Dist: beautifulsoup4>=4.12
|
|
26
|
+
Requires-Dist: markdownify>=0.13
|
|
27
|
+
Requires-Dist: nodejs-wheel-binaries<23,>=22.12
|
|
28
|
+
Provides-Extra: asr
|
|
29
|
+
Requires-Dist: faster-whisper>=1.1.0; extra == "asr"
|
|
30
|
+
Requires-Dist: opencc-python-reimplemented>=0.1.7; extra == "asr"
|
|
31
|
+
Provides-Extra: web
|
|
32
|
+
Provides-Extra: pdf
|
|
33
|
+
Requires-Dist: markitdown[pdf]>=0.1.6; extra == "pdf"
|
|
34
|
+
Provides-Extra: ocr
|
|
35
|
+
Requires-Dist: rapidocr-onnxruntime>=1.4.4; extra == "ocr"
|
|
36
|
+
Requires-Dist: Pillow>=10; extra == "ocr"
|
|
37
|
+
Provides-Extra: ocr-router
|
|
38
|
+
Requires-Dist: Pillow>=10; extra == "ocr-router"
|
|
39
|
+
Requires-Dist: numpy>=1.24; extra == "ocr-router"
|
|
40
|
+
Requires-Dist: PyMuPDF>=1.24; extra == "ocr-router"
|
|
41
|
+
Provides-Extra: document
|
|
42
|
+
Requires-Dist: markitdown[docx,pdf,pptx,xlsx]>=0.1.6; extra == "document"
|
|
43
|
+
Requires-Dist: rapidocr-onnxruntime>=1.4.4; extra == "document"
|
|
44
|
+
Requires-Dist: Pillow>=10; extra == "document"
|
|
45
|
+
Provides-Extra: dev
|
|
46
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
47
|
+
Requires-Dist: Pillow>=10; extra == "dev"
|
|
48
|
+
Requires-Dist: numpy>=1.24; extra == "dev"
|
|
49
|
+
Requires-Dist: PyMuPDF>=1.24; extra == "dev"
|
|
50
|
+
Dynamic: license-file
|
|
51
|
+
|
|
52
|
+
# Openmd
|
|
53
|
+
|
|
54
|
+
`Openmd` 是一个多平台内容摄取工具:把平台 URL、作者主页、合集、课程目录、playlist、网页文章或通用可访问视频转成统一 Markdown。
|
|
55
|
+
|
|
56
|
+
它不是漂亮笔记生成器,也不是完整 Agent 工作站。它只是 Markdown-first 工作流中的内容摄取节点,目标是 **高效、可靠地取得原始正文 / 逐字稿**。
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
platform url / author / playlist / catalog / web page / generic media
|
|
60
|
+
-> router
|
|
61
|
+
-> backend capability registry
|
|
62
|
+
-> platform adapter
|
|
63
|
+
-> native text / transcript / media fallback / ASR
|
|
64
|
+
-> normalized raw Markdown
|
|
65
|
+
-> local archive / truth-source review / downstream agent workflow
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 30 秒上手
|
|
69
|
+
|
|
70
|
+
```powershell
|
|
71
|
+
pip install openmd-cli # 或 pipx install openmd-cli;需要 Python 3.10+
|
|
72
|
+
openmd "https://sspai.com/post/73145" # 粘贴链接 → 回车选保存位置 → 得到 标题.md
|
|
73
|
+
openmd "https://..." -o D:\notes # 脚本 / AI Agent:给了目录就直接跑完
|
|
74
|
+
openmd doctor # 看看现在能处理哪些内容、缺什么怎么装
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| 内容 | 装完即可用 | 需要额外安装 |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| 网页文章、YouTube 有字幕的视频 | ✓ | — |
|
|
80
|
+
| B 站字幕、公众号、X 等需要浏览器读取的平台 | 首次自动准备浏览器读取组件(约 20MB,一次) | Openmd 浏览器插件(`openmd browser-setup`),借用你已登录的浏览器在后台读取,窗口自动最小化 |
|
|
81
|
+
| 无字幕视频、播客(本机转录) | | `pip install "openmd-cli[asr]"` + 模型,见 [工具接入规范](https://github.com/baigao417/openmd/blob/main/docs/tool-integration.md) |
|
|
82
|
+
| 图片、PDF、Office | | `pip install "openmd-cli[document]"` |
|
|
83
|
+
| 浏览器一键保存 | | `openmd browser-setup`(目前仅 Windows,见下文) |
|
|
84
|
+
|
|
85
|
+
所需运行环境随 `pip install openmd-cli` 一起装好,不需要另装 Node.js 或其他工具。不想自动下载可设 `OPENMD_AUTO_INSTALL_TOOLS=0`,npm 慢可设 `OPENMD_NPM_REGISTRY` 指向镜像;想看到后台读取窗口(排查问题时)可设 `OPENMD_SHOW_BROWSER=1`。第三方组件与许可证见 [THIRD_PARTY_NOTICES](https://github.com/baigao417/openmd/blob/main/THIRD_PARTY_NOTICES.md)。
|
|
86
|
+
|
|
87
|
+
取不到时 Openmd 不会存一份垃圾文件:它会说明原因,并打印一行 `提 issue 请附上:…`,复制到 [链接提取失败](https://github.com/baigao417/openmd/blob/main/.github/ISSUE_TEMPLATE/link-failure.yml) 即可。想参与开发见 [CONTRIBUTING](https://github.com/baigao417/openmd/blob/main/CONTRIBUTING.md)。
|
|
88
|
+
|
|
89
|
+
## 输入链接后保存
|
|
90
|
+
|
|
91
|
+
在终端执行 `openmd` 后粘贴链接,或直接执行 `openmd "https://example.com/article"`。粘贴后直接开始提取,不做任何确认:公开读取、浏览器读取(可能使用现有登录会话)、必要时下载媒体和本机转录按路线需要自动开启;云服务从不自动开启。非交互运行(脚本 / Agent)只要给出保存目录(`-o` / `--save-to`)就按同一档自动开启;显式传入能力开关时只开这些,`--yes` 仅为兼容保留。提取成功后选择保存目录,回车沿用上次成功保存的位置,输入 `n` 取消。默认直接保存 `标题.md`,同名时才加后缀;运行日志放在 `~/.openmd/runs/`,不混入所选文件夹。终端显示“已取得正文”和“已保存”,`--verbose` 显示工具路线,`--json` 输出机器回执。
|
|
92
|
+
|
|
93
|
+
带视频的帖子(目前 X):默认只查视频时长,在正文后注明“本帖含视频(约 N 分钟),未转文字”;`--video text` 临时下载视频、本机转写并把逐字稿接在正文后(下载文件用完即删);`--video file` 另把视频文件存进同名 assets 文件夹并在 Markdown 里嵌入。视频这一步失败不影响正文保存,只在文件里写明原因。
|
|
94
|
+
|
|
95
|
+
默认 Markdown 按说话人、章节、停顿或约 30 秒合并字幕段落,保留原字词和错误,不补写标点或总结。`--raw` 保留原字幕逐行/表格排版。YouTube/B 站标题作者缺失时,在公开网络许可内补取元数据,失败仍保存正文并说明缺失;取得正文不等于已核对全文准确性与完整性。
|
|
96
|
+
|
|
97
|
+
```powershell
|
|
98
|
+
openmd
|
|
99
|
+
openmd go "https://example.com/article"
|
|
100
|
+
openmd "https://example.com/video" --raw
|
|
101
|
+
# 自动化:给了目录即可;只想开公开网络时显式写 --enable-network-backends 收窄
|
|
102
|
+
openmd "https://example.com/article" -o "D:\notes" --json
|
|
103
|
+
openmd doctor --profile media
|
|
104
|
+
openmd doctor --profile tools
|
|
105
|
+
openmd probe --enable-network-backends --enable-browser-backends # 本机 out/probe-config.json 配样本,模板见 docs/examples/probe-config.example.json;--platform bilibili 只测一个平台,报告在 out/probe/<date>/,不下载媒体或跑 ASR
|
|
106
|
+
openmd routes --format markdown
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`openmd --help` 只列日常命令(`go`、`batch`),`openmd go --help` 只列 `--raw / --save-to / --video / --json / --verbose`;其余子命令和参数照常可用,只是不在帮助里显示(`openmd <命令> -h` 查看)。
|
|
110
|
+
|
|
111
|
+
### 浏览器插件(Chrome / Edge)
|
|
112
|
+
|
|
113
|
+
1. 终端运行一次 `openmd browser-setup`(只写当前用户注册表和 `~/.openmd/browser/`;`--uninstall` 撤销)。
|
|
114
|
+
2. 打开 `chrome://extensions`,开启“开发者模式”,“加载已解压的扩展程序”,选 `browser-setup` 打印并自动打开的文件夹(`~/.openmd/browser/extension`,升级 openmd 后重跑一次 `browser-setup` 即同步)。插件 ID 固定为 `lceckiadcfloncbklcojdiimbpollngh`。
|
|
115
|
+
3. 点工具栏的 Openmd(仓鼠)图标:直接保存当前页到默认位置,图标出现 ✓ 即完成;失败才弹系统通知说明原因。
|
|
116
|
+
- 多选标签页:按住 Ctrl / Shift 点选多个标签,再点图标,一次全部保存。
|
|
117
|
+
- 保存一片链接:在课程目录、合集列表等页面拖选一段内容,右键“用 Openmd 保存选中的链接”。
|
|
118
|
+
- 右键“用 Openmd 保存此页 / 此链接”同样可用。
|
|
119
|
+
- X 帖子带视频:直接点图标只注明有视频;在 X 页面(或帖子链接、视频上)右键“用 Openmd 保存(含视频文字版)”附上逐字稿,“(含视频文字版和视频文件)”再留下视频。
|
|
120
|
+
4. 设置页(右键图标 → 选项):添加多个保存位置(名字 + 文件夹,可用“选择…”弹出文件夹对话框),选默认位置;“点击图标时”可切换为“先选择保存位置”;“帖子里带视频时”可改成默认附上逐字稿。
|
|
121
|
+
|
|
122
|
+
借用浏览器读取(B 站、公众号、X 等)时,Chrome 可能在顶部显示“Openmd 已开始调试此浏览器”:这是 Chrome 对使用调试接口的插件统一显示的提示,插件自身无法隐藏;读取只在 Openmd 的后台窗口里进行。
|
|
123
|
+
|
|
124
|
+
插件通过 Chrome Native Messaging 调用本机 `openmd`,不开本地端口;只有上面这个插件 ID 能连。所有保存进入后台队列,一次只跑一个(避免本机转录同时占满显卡),后台不弹任何窗口。关机时没跑完的任务,开机后插件一联系本机就自动重新排队(最多重试一次)。重启电脑后无需任何操作。任务记录在 `~/.openmd/browser-jobs/`。
|
|
125
|
+
|
|
126
|
+
ASR 步骤自动选择已有 CUDA worker;主 CLI 保留自己的环境。本地模型目录或缓存不存在时明确报错,不自动从 HF 下载。工具位置、ASR Python 和模型根目录统一放在本机配置(`OPENMD_CONFIG` 指定 JSON,源码 checkout 默认 `out/local-config.json`,安装版默认 `~/.openmd/local.json`);只存安装位置,不存凭证。配置示例与安装边界见 [工具接入规范](https://github.com/baigao417/openmd/blob/main/docs/tool-integration.md),实际路由由打包 JSON 生成:[当前路由](https://github.com/baigao417/openmd/blob/main/docs/routes.generated.md)。架构图和各平台工具一览:[Openmd 全景](https://github.com/baigao417/openmd/blob/main/docs/architecture-map.html)(浏览器打开)。
|
|
127
|
+
|
|
128
|
+
## 输出格式
|
|
129
|
+
|
|
130
|
+
```md
|
|
131
|
+
---
|
|
132
|
+
title: ...
|
|
133
|
+
author: ...
|
|
134
|
+
url: ...
|
|
135
|
+
published: "2026-11-07"
|
|
136
|
+
platform: bilibili
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
# 标题
|
|
140
|
+
|
|
141
|
+
**00:00:01**
|
|
142
|
+
|
|
143
|
+
合并成段的原始逐字稿……
|
|
144
|
+
|
|
145
|
+
*来源:自动字幕;识别错误保持原样。*
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 状态口径
|
|
149
|
+
|
|
150
|
+
- `verified`:已成功产出可用正文 / 逐字稿,包括原生正文、官方/作者字幕、自动字幕、第三方 transcript、ASR 转写、飞书原生 Markdown 导出。
|
|
151
|
+
- `partial`:产出文本但明显不完整,例如只抓到预览、部分章节缺失、视频只转写一段、collection 部分失败。
|
|
152
|
+
- `failed`:没有产出可用文字稿 / 正文。
|
|
153
|
+
|
|
154
|
+
`source_level` 表示来源等级,不等同于 `status`。这些字段保留在运行记录和显式 package 中,不在普通 Markdown 中重复展开。
|
|
155
|
+
|
|
156
|
+
## 文档
|
|
157
|
+
|
|
158
|
+
- [子系统地图](https://github.com/baigao417/openmd/blob/main/docs/subsystems/README.md):每个模块的职责、入口和验证命令
|
|
159
|
+
- [接入新平台](https://github.com/baigao417/openmd/blob/main/docs/adding-a-platform.md):大多数平台只需加一张工具卡
|
|
160
|
+
- [工具接入规范](https://github.com/baigao417/openmd/blob/main/docs/tool-integration.md)、[当前路由](https://github.com/baigao417/openmd/blob/main/docs/routes.generated.md)、[全景图](https://github.com/baigao417/openmd/blob/main/docs/architecture-map.html)
|
|
161
|
+
|
|
162
|
+
## 参与开发
|
|
163
|
+
|
|
164
|
+
Windows / macOS / Linux 都可以开发:`python scripts/dev_setup.py` 一条命令装好环境。流程、issue 与 PR 要求见 [CONTRIBUTING](https://github.com/baigao417/openmd/blob/main/CONTRIBUTING.md),硬规则见 [code-standards](https://github.com/baigao417/openmd/blob/main/docs/code-standards.md)。
|
|
165
|
+
|
|
166
|
+
## 许可证
|
|
167
|
+
|
|
168
|
+
[MIT](https://github.com/baigao417/openmd/blob/main/LICENSE)。第三方组件见 [THIRD_PARTY_NOTICES](https://github.com/baigao417/openmd/blob/main/THIRD_PARTY_NOTICES.md)。
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
openmd/__init__.py,sha256=GtdieGClGQfkKP6QZ3ODKVBY5V4ha2fPPun1i_EtiNY,45
|
|
2
|
+
openmd/__main__.py,sha256=PSQ4rpL0dG6f-qH4N7H-gD9igQkdHzH4yVZDcW8lfZo,80
|
|
3
|
+
openmd/browser_host.py,sha256=tmydV9DFbtEW9oAQIuWijuyZdta-VRAmtHyDop0jErY,19683
|
|
4
|
+
openmd/browser_setup.py,sha256=Uj3Lp6zoJ5BSpdz15EBiY0ix-Bp0IGB3yI7sWMa3Tp8,4328
|
|
5
|
+
openmd/cli.py,sha256=_xp0Jmx2mbkCDdmwgDTo-_tp7v8Z1q4V9387cI1Mwxw,85265
|
|
6
|
+
openmd/console.py,sha256=sYYrOjM2_2N1tsRinphWXlnvFbhcSI5_dqf91eHDvMA,1241
|
|
7
|
+
openmd/doctor_view.py,sha256=SehE5pl_oYfU8mkv90pjQ-6xhNFv7vrkalK72dBpDxc,3275
|
|
8
|
+
openmd/interactive.py,sha256=Zqkf7cjMT9kfstqWWucRPoK6WO-4tI5HtUxwYG4tb6I,23800
|
|
9
|
+
openmd/post_video.py,sha256=SYHyq7WTEXXp_DbyiUcjrCygniV_nhsrQb4uax1PZU8,6648
|
|
10
|
+
openmd/probe.py,sha256=3gwyGKFWEBQkPf7ilL_yYUa28kakiPcyH6s462Vyljc,19901
|
|
11
|
+
openmd/adapters/__init__.py,sha256=srLL8U-cyZzFFb-pkNPAW6BlvenF_KcLRNXgzPWYFTI,48
|
|
12
|
+
openmd/adapters/base.py,sha256=CtTDQAiE0WK1va9Qvik7Clbn5c4POxwuknsROe3HChs,3225
|
|
13
|
+
openmd/adapters/high_privacy.py,sha256=WBbZ_vwOA_vgRSnzN81qChybM5d0a-xR37e-ZTe12zk,2218
|
|
14
|
+
openmd/adapters/opencli.py,sha256=PL72iZN0RxIpI9O2aEnFehC8hmYfBkxwlJj7OsQLvXk,28173
|
|
15
|
+
openmd/adapters/registry.py,sha256=rTnKav5ONZjOvn4L_6UYzNt__aVJRL3onM-OzD9nDQE,4547
|
|
16
|
+
openmd/adapters/xiaohongshu.py,sha256=iSvvrOyeWovt7QhpItIOlB33MzrHsSqCeqYg-QXiEYk,6400
|
|
17
|
+
openmd/adapters/bilibili/__init__.py,sha256=E8OpuGZ5c2cQ6PtefPTdYLXGdFsNsTx8ThwjFKoj4ms,298
|
|
18
|
+
openmd/adapters/bilibili/adapter.py,sha256=y2nRG6dLjFtbSqgLkQ7KI1vG9AYxf_bnwUllPYJVXEk,3805
|
|
19
|
+
openmd/adapters/bilibili/discovery.py,sha256=CLMlpSqkpiZl5NTnh6ReV4X6dpUnMn4mlInoJ4ViV5U,1046
|
|
20
|
+
openmd/adapters/douyin/__init__.py,sha256=QR_rgG8cEgsKooVP3W9-NN8wZTy1dRVa8bH8CPs5bgw,292
|
|
21
|
+
openmd/adapters/douyin/adapter.py,sha256=grIdY6M6Lq3dwngqI__y0e46GaEG4w3xqZUkjFAU4_s,4581
|
|
22
|
+
openmd/adapters/douyin/discovery.py,sha256=t653ZmfyYKQDiHKfvVE_o1IdOG0IM2wicw7-yVItp3A,1184
|
|
23
|
+
openmd/adapters/douyin/sidecar.py,sha256=ZvQ3UYylkeKuIhfl8fI3J6qQB5qLRwiG1P3pLgEF0SM,2340
|
|
24
|
+
openmd/adapters/generic_media/__init__.py,sha256=ldkiXT2OBKmAUj1sEUtJ1OyhSB8xTfLhZhQiayOw2Wg,470
|
|
25
|
+
openmd/adapters/local_audio/__init__.py,sha256=IAXusBo56nM_1vsII0HGL5NAw-hT09FqxmoXp-OkOIw,470
|
|
26
|
+
openmd/adapters/local_audio/adapter.py,sha256=bslG4Ls24pTw3s6jmNfOH_W8UmsE2o_DdCnNjXRv9_E,5268
|
|
27
|
+
openmd/adapters/local_audio/asr.py,sha256=VQ7DzAVqiFTpVfWbQfJNSC9pcp-UZZ92khkfJC9RUyw,1858
|
|
28
|
+
openmd/adapters/local_audio/backends/__init__.py,sha256=wUbtKlx2PWPpvWqz62iO6WOuEisL5MY-aXn2RCcRLvU,50
|
|
29
|
+
openmd/adapters/local_audio/backends/auto.py,sha256=38SsF6L5ZREHtiZQzlzPhg937XI0YVndTMKtDkOAfPg,7728
|
|
30
|
+
openmd/adapters/local_audio/backends/faster_whisper.py,sha256=wgBR71xv3QVBnGV40s2kh2eMv0Jwu9tywiRgGrX9yWY,11767
|
|
31
|
+
openmd/adapters/local_audio/backends/funasr_sense_voice.py,sha256=5Z9WL6g1Y8gFw4droFICBls4gCu4bueMxwTgJ2gW5Nc,432
|
|
32
|
+
openmd/adapters/local_audio/backends/qwen3_asr.py,sha256=he-icfcXmcPrkbgNsIUUaPUq74SLZ4A83SJMY55c2Ws,10892
|
|
33
|
+
openmd/adapters/pdf/__init__.py,sha256=z7XmeBR0pp3W63WpWRquJfjkc92yIC7lXmlxLQiWl2w,307
|
|
34
|
+
openmd/adapters/pdf/adapter.py,sha256=G6W0OzY0Ai8kgfNPN7Qj8A7c9PRNtpkBwZJO38eadKA,5340
|
|
35
|
+
openmd/adapters/pdf/parser.py,sha256=bmdOo2AKq05yt4itLxZEwlKI3vOWeLyAh68a2QIQ33E,1919
|
|
36
|
+
openmd/adapters/pdf/backends/__init__.py,sha256=lB0BWP_c4ZyOMMuK0u179HY2qSPwnaQyQ3XWL2QQsIY,41
|
|
37
|
+
openmd/adapters/pdf/backends/markitdown.py,sha256=XELi2jlYCGHjSbjqm_1dfrVpEK843XSH4gDW3oIOu4s,6537
|
|
38
|
+
openmd/adapters/qq_group/__init__.py,sha256=KVftIlwwTzOwBeV3xYdaNyq0Tv7nGWrvIR4Skxb7lGc,305
|
|
39
|
+
openmd/adapters/qq_group/adapter.py,sha256=r1VivgA0PUBnZKngBY-ussl4EJNrNzl2IMWfOm0agP8,11475
|
|
40
|
+
openmd/adapters/qq_group/parsers/__init__.py,sha256=dvJEdqQHWLogWL2ZPEigfhoWUgLyUqj-ssv9nGzE5LA,55
|
|
41
|
+
openmd/adapters/qq_group/parsers/html.py,sha256=-gQXHA1l2BstL4YnLl1-MN1D3LPgmpFKGs_25w3pGKQ,1932
|
|
42
|
+
openmd/adapters/qq_group/parsers/json.py,sha256=jKGHPjkgJHCDNcIpSoQcy9U_kXJuLux307nyMJ9vNBc,2410
|
|
43
|
+
openmd/adapters/screenshot/__init__.py,sha256=fXX_29-lF-9tPoKlY_wYUWx4VHnw0J1CI-peQKvoGvM,318
|
|
44
|
+
openmd/adapters/screenshot/adapter.py,sha256=YCGQB_Q5v8dvHEH8vCC7dcKYdtMj7hVrEwHANn_eAMc,5401
|
|
45
|
+
openmd/adapters/screenshot/ocr.py,sha256=S9ZT9XWalGLvzjrD8qNfBM2IO4CTT6yi4mVBq5Z5B-I,2766
|
|
46
|
+
openmd/adapters/screenshot/backends/__init__.py,sha256=EUf5R3VT_cDSBgF84w_HTkoOh_-IzX_gwNN1RVrLMkA,52
|
|
47
|
+
openmd/adapters/screenshot/backends/paddle.py,sha256=u36VP8Z4IRE2vIAyJCz0rxbCmJAOR-1SFr9uZEl_Ddg,461
|
|
48
|
+
openmd/adapters/screenshot/backends/rapidocr.py,sha256=DI0CFQ3aHOMHEISblZuhsTgSFSfudhfebakEfqGHBuU,6511
|
|
49
|
+
openmd/adapters/screenshot/backends/tesseract.py,sha256=E_4vbWk4FkyB8x1l93D0Hx85beRxHf5S5_IHQN_Ojtk,467
|
|
50
|
+
openmd/adapters/wechat_group/__init__.py,sha256=rcH9ubJMSiUTOfS_ik82NVRMiBVtoy7M6yHpSTqJxLA,317
|
|
51
|
+
openmd/adapters/wechat_group/adapter.py,sha256=JRPUR2447opk0QU87MdTM6CwJypo_IbDYLJCvGuw0tY,6296
|
|
52
|
+
openmd/adapters/wechat_group/wx_cli.py,sha256=Y-clCuMC4ilB-1HC9v8ZW6nsDc600iyScSDaX_r0j50,9746
|
|
53
|
+
openmd/adapters/wechat_moments/__init__.py,sha256=ymH9Y2j_9VEOWeNoJuH-GqgG-IeESSz0jord4nJ_MjI,323
|
|
54
|
+
openmd/adapters/wechat_moments/adapter.py,sha256=GYkclGAcaxr4QogpPomoaN2OV7eU3wLRXepyXivnRFI,6449
|
|
55
|
+
openmd/adapters/wechat_moments/wx_cli.py,sha256=qchAHryfmO4884CF2zi9w9qHJsItqkLvQ1iLxkpvDk0,9510
|
|
56
|
+
openmd/adapters/weixin/__init__.py,sha256=MYrxXFxhoWH-iVWmI9auTcqUYuJk9MNyEVc4RDryJGk,346
|
|
57
|
+
openmd/adapters/weixin/adapter.py,sha256=GhjPD1beALdSf8roZVViJCnj7BpNtfQ5_j4yD7-RD3Y,5189
|
|
58
|
+
openmd/adapters/weixin/discovery.py,sha256=XUqahJ9fqFdw4Q44IJLKZoXZqbYznB42p_mxcDn8jZ0,20539
|
|
59
|
+
openmd/adapters/weixin/report.py,sha256=3BPPJtzGx7Jb2eAV1QLAfYVkjcdCRC8Ba8VjkyRk5EI,11459
|
|
60
|
+
openmd/backends/__init__.py,sha256=n4X9gekJnqkPgV10YKRoIO0cFwq9_WsYtGa2egJRQlA,34
|
|
61
|
+
openmd/backends/base.py,sha256=gN7Rbah7xG1AMnwZbbjJbpHfvbA8rCauCpD48wkyZnA,5194
|
|
62
|
+
openmd/backends/bilibili.py,sha256=wuGcErvxbgIbP8KHD43nwwmxeDszGRFxAsaR5gzjnWQ,55129
|
|
63
|
+
openmd/backends/bilibili_wbi.py,sha256=odIOHxh-15yNjiVWfSqrv5OF1qN8Rrhm4bSmQOS_988,7983
|
|
64
|
+
openmd/backends/discourse.py,sha256=dQj-AKc4Da7gKfeta2_xBQNTQlfT2iqTE44BOff9Ql8,20742
|
|
65
|
+
openmd/backends/douyin.py,sha256=e3lNOdkOgY_okoQdlLDlVfqjy8e3V95n3l6PYk7v5ig,32158
|
|
66
|
+
openmd/backends/douyin_detail.py,sha256=9LwdaAjPSGnhZ5pmCdd1tZZ4UIPuHulLSNScoMFj9Sw,6575
|
|
67
|
+
openmd/backends/douyin_providers.py,sha256=klb_97kduPq23JlDLfm3pOXTWv_vWEDGuOExpgxwKI8,30369
|
|
68
|
+
openmd/backends/executor.py,sha256=S_p4Nc5-Ejbe_9njzQZ4jMS7sP-ngxiASL1fSXbP9KE,20813
|
|
69
|
+
openmd/backends/feishu.py,sha256=QR2JgFNVeatbtzuOZb0ug37vVCTaxNJ5NOyDHZjVMFs,30995
|
|
70
|
+
openmd/backends/generic_media.py,sha256=LoD6-tremmgdEVme58qqpedJVb729NtYIE2NalWtjhI,3102
|
|
71
|
+
openmd/backends/interactive.py,sha256=J6HDGKHAaYE29lKxNFShFY4bWdX1HHsxagGNeupccA0,2417
|
|
72
|
+
openmd/backends/ocr.py,sha256=djKktgx2UDJNqwTdypCQ9m3TdeM-cayoXt_JumrmFeI,2298
|
|
73
|
+
openmd/backends/opencli.py,sha256=GB2IASAtt-0p3A5u0zTlb0wg7KNXA7OOMZd04VpHf2c,2903
|
|
74
|
+
openmd/backends/pdf.py,sha256=9mTYAdWkClwEkUuApRM6RHwD6h2De_TTDPcbEAVGRJc,5989
|
|
75
|
+
openmd/backends/registry.py,sha256=YYKR5EXRRhb4Ra1Cb4u0K-m0SmEVyVSBkc7gne3bxQM,8577
|
|
76
|
+
openmd/backends/screenshot.py,sha256=d7mGoOciAjz85ckr5O571KelqnUUTLeossm_XBhDNJY,3130
|
|
77
|
+
openmd/backends/video_metadata.py,sha256=bP-xwuwdTytbkxcTX5CE-hMPC2n3GVExGvD9bGVvrpw,7300
|
|
78
|
+
openmd/backends/web_article.py,sha256=j0TOrh0QDoSLpcBxZFYj0koLtO56mRjmQmgOHCYCrU0,13816
|
|
79
|
+
openmd/backends/wechat_group.py,sha256=tYifX0hQ73futYv8im9qBQZ3Ud9tgxuuQIH9j7JxFeQ,1217
|
|
80
|
+
openmd/backends/wechat_moments.py,sha256=zy6egFOLAIfjM7bd_PCUuIpxjM6Qe13Znq1ekxhA21E,1215
|
|
81
|
+
openmd/backends/weixin.py,sha256=nUmQaIulIlSy88x7Mqem5EP83rMCh4wcWyP4EdVpH_w,29468
|
|
82
|
+
openmd/backends/x.py,sha256=GIg43uyj0FS9azioTASUVdDog88XDmCSsATILHfiYcM,15628
|
|
83
|
+
openmd/backends/xiaoe.py,sha256=pgV66AwvYehc7Zd3mGkPEQGGBS6zp5d2c7ux_4GQwxY,33481
|
|
84
|
+
openmd/backends/xiaohongshu.py,sha256=8lkQ9UVdfFsed01HmIw9YuFP0rPKvw8RrdLowcfbvfo,34129
|
|
85
|
+
openmd/backends/xiaohongshu_providers.py,sha256=v3Z52Mn1GPAQKmKkbU63lTUDBZ4_oLu43VTv_TPKYAs,37978
|
|
86
|
+
openmd/backends/xiaoyuzhou.py,sha256=EGkt-dPmaWTlsd6yMLNenLsIb870kZxWNcqMsF5ylag,37922
|
|
87
|
+
openmd/backends/youtube.py,sha256=yP8-bfk_185ASCMvcxDmHuYcPfmw0mQVJuPcc7F_JSQ,10860
|
|
88
|
+
openmd/backends/youtube_discovery.py,sha256=-sfFI9JMMmRch-P6fOuH0uK9Xdua99yNuUDUjBztXqU,52639
|
|
89
|
+
openmd/backends/youtube_transcript.py,sha256=HIZTdcygWDGo0R_t_OmEPS_EomYV_k1FetDIIjRUvM0,7747
|
|
90
|
+
openmd/browser_extension/background.js,sha256=KcbN9IiRiEpcJ2FNcb1QdTJ5sikIE8Q-YnVuv-pMVVs,10450
|
|
91
|
+
openmd/browser_extension/idle.html,sha256=1CvUITKM8g8kItVeb5q1lrz7glpusWQwzoq1ehA0RB0,342
|
|
92
|
+
openmd/browser_extension/manifest.json,sha256=RiP212Uf3znt2nvALzGmD--ZIgAygQq1HeoAZOrl4i0,1427
|
|
93
|
+
openmd/browser_extension/options.html,sha256=Al451FfqvDwsC_HDmMuyME0FheN67nyzf98lky_mWbo,1600
|
|
94
|
+
openmd/browser_extension/options.js,sha256=Q0YpmkpGAdYuVzqNYLG835gI-zOpW-G5GRMPW-MG5PY,4277
|
|
95
|
+
openmd/browser_extension/popup.css,sha256=MX2OWf9s7Lv-MMFqFYlzuo2rrU75bv6EcNATOLkbCU4,3700
|
|
96
|
+
openmd/browser_extension/popup.html,sha256=XoKs9uQeqPFJcBLBL7C71Yky_ruCO7mtsgMbKColEUo,451
|
|
97
|
+
openmd/browser_extension/popup.js,sha256=RcNbRBxLdsu7O52ZdWdnWNJIapfOGkSS3YYGEOFQ21U,2287
|
|
98
|
+
openmd/browser_extension/icons/icon-128.png,sha256=bQR0pGTN3fY8jTYDi43-7s_ZIDb5ezq-3aGLuyGGDZQ,9045
|
|
99
|
+
openmd/browser_extension/icons/icon-16.png,sha256=Vl6U3sSjnkDVgd0doi5TB_O9pQ6ufn0o4s859LKTWGM,789
|
|
100
|
+
openmd/browser_extension/icons/icon-32.png,sha256=fgJuA5hN8irHrqtPghuG9XrUkGgt7_EXBHuxpSeWsEc,1772
|
|
101
|
+
openmd/browser_extension/icons/icon-48.png,sha256=mgQG6nOHXGVtoRWE96P2LBB8p-D2sjnxYTpmGqBnCZY,2880
|
|
102
|
+
openmd/browser_extension/icons/icon-512.png,sha256=NkWGPF7GaV5TEqvy-mcuBJ-u-Tg2nVoDGah2uNNolKI,38855
|
|
103
|
+
openmd/browser_extension/vendor/opencli-bridge/LICENSE,sha256=AhC4tmzwA1gkLLkhuivjpG3-AZAVmxuVI4ijiAzh_1Q,10760
|
|
104
|
+
openmd/browser_extension/vendor/opencli-bridge/VENDOR.md,sha256=5dzJ0zEre3siK8_XdO7vAP4xVcGvVc7HtAi0Se5WG_U,1801
|
|
105
|
+
openmd/browser_extension/vendor/opencli-bridge/background.js,sha256=H2nAYExIeEOBAd4bD8ljRdXXG6WwVg1hfDsRW8kG43c,107283
|
|
106
|
+
openmd/core/__init__.py,sha256=sK0yIGpTRdnFrLdAc_-sHdYvorthOHFe70VKOusN_Jk,40
|
|
107
|
+
openmd/core/capabilities.py,sha256=YXBrAT-gzZ4WObYgOKATDYq1QhFewRSppNXNu8rCrY0,6553
|
|
108
|
+
openmd/core/content_validation.py,sha256=7YhBoKjo3HRP3Oy0cgkd7k7uXrvmEVsvOfR8hzwDKdA,16605
|
|
109
|
+
openmd/core/coverage.py,sha256=NhaTE2Q4uY3Kp0IXLqycdv6h24MMJILOVT0p_p77L1k,6409
|
|
110
|
+
openmd/core/detector.py,sha256=YzM_-V7cO9abib6J-q-Z_uR4q7qRs19HNc3spGuBgYw,7583
|
|
111
|
+
openmd/core/doctor.py,sha256=wYq6Bi6R_0nNhUyGNy9ptBU_TVN3sM02EgffohoawEM,5274
|
|
112
|
+
openmd/core/execution_policy.py,sha256=YKamVsUqoHS0LVJUB51UFdTTDyx8P-jwoRJ9z-BRVb4,6470
|
|
113
|
+
openmd/core/filename.py,sha256=9_LuK7uo5ovKcrWxt-QE_72bpTWuT13M8cJE7l7GU-g,1757
|
|
114
|
+
openmd/core/input_validation.py,sha256=olt-6zz6MpF0JI3bCWxUERXYG6I4aFQSFTqAkGvIZ0o,3634
|
|
115
|
+
openmd/core/local_config.py,sha256=KWzq5Uoxcy0zK-foxVbtQq7iS3gJRousCkUwllDYYeM,4352
|
|
116
|
+
openmd/core/manifest.py,sha256=4cRFpAre_HYSCpZy0fGfZgfHYH3js86D4Zh81orDWKk,5459
|
|
117
|
+
openmd/core/models.py,sha256=NqHHkd__6fM8cEXqDxAMf04pRl4pgclepbczpFZJNoQ,4260
|
|
118
|
+
openmd/core/opencli_output.py,sha256=3bzSYhAENdRwQhKU15z765lBttT52Vii3W8Lk7XlWNo,1148
|
|
119
|
+
openmd/core/operation_contract.py,sha256=zFGw6fINqL-uIpkmg1r5uXgTuqG9-BARkQlBBIEM35I,2869
|
|
120
|
+
openmd/core/output.py,sha256=aLUflF5zcU6wvVRlkliIGjtpDZEi-fJY5X8_P6OiH5s,6672
|
|
121
|
+
openmd/core/package_contract.py,sha256=u81eRX9NNvOkMutv5uVTLH0ihQQPuHn7BxYtCrhrV1o,11066
|
|
122
|
+
openmd/core/package_validator.py,sha256=epSEEgv0Nc_j-PNnfSpbIiF9DO0wXOmfGtPYTSuWRYo,19212
|
|
123
|
+
openmd/core/package_writer.py,sha256=4L52u7_VwM2dmcl-oGSAlPVwk6fNFTAAA_zAQwRIzBY,22815
|
|
124
|
+
openmd/core/planner.py,sha256=Ah-NgJNGauH74GEZl_k4sVsDrQpKyVzlk9YyLlMUkhE,16339
|
|
125
|
+
openmd/core/preferences.py,sha256=Xvi6zrkzaIfle3Elz4lRguIsRGoKR6rpJQkP6H_Ames,2187
|
|
126
|
+
openmd/core/readable.py,sha256=cIHiygC4OrHvd_GxtRq7WRab-xT9Kmeg70EYuQSPzoc,4662
|
|
127
|
+
openmd/core/receipt.py,sha256=ng_t_jg6LjNsl4sJlUBAY_3NUWjxPy8ocIjnDNaaJTo,11841
|
|
128
|
+
openmd/core/renderer.py,sha256=wYz4yGWgb4HyCTP-5GdDc4PENJ4E1Tx5HB-jjGjGiNA,7584
|
|
129
|
+
openmd/core/runlog.py,sha256=YCpMC8MKHXiwYQv0DaJIMXj8mxtIPp7FxKCPwuKmfzY,7387
|
|
130
|
+
openmd/core/secrets.py,sha256=SdOyqViLqkiT1JMJdP_8080golWzP36cHHQjkIuwTFc,11042
|
|
131
|
+
openmd/ocr/__init__.py,sha256=uTvJedcA338NurFnA_ea5rNcdB7ZWx0FNdqd72JoocM,78
|
|
132
|
+
openmd/ocr/breakers.py,sha256=HnG8kYvTrymX3Uvz1VK3JDf1GENkcG8ZjjhhGj6fJ3E,3746
|
|
133
|
+
openmd/ocr/config.py,sha256=clKinBw6vE-n5wq_Xjh8E9fEVrMp7IKlbWZcjNDRM54,2942
|
|
134
|
+
openmd/ocr/documents.py,sha256=R32yxsFjkwzu5UcyN3x2wTVEr0IEiLXilNBlAOo90eI,29617
|
|
135
|
+
openmd/ocr/endpoint.py,sha256=F_D86hAAsJbTjXpvdR1alKF_W1OQXdOi4RWidelbBDk,3635
|
|
136
|
+
openmd/ocr/layout.py,sha256=P2aojdMel57rzYNRJwyL_J8N7OE-NJOsPe3cLvrv-P4,4768
|
|
137
|
+
openmd/ocr/layout_filter.py,sha256=4Xvu7E1SY7-vbq2b6_JNQDxmCtzZ-OJk97qt3porfs0,4545
|
|
138
|
+
openmd/ocr/layout_worker.py,sha256=5AURXUDsM9wXGWSMS2fA7h_hQR9WFk0ffTtrNTRu8M4,4389
|
|
139
|
+
openmd/ocr/native_math.py,sha256=wjvAEhs8tRC_OOIknGbm1ZsAXOw4kWgNd3owJRJECW0,11075
|
|
140
|
+
openmd/ocr/office.py,sha256=6Cf7wZy9vDvcbpdyIfwH6NUohkLGyWS3-CsI9yvDnBw,15730
|
|
141
|
+
openmd/ocr/ollama.py,sha256=djhdavrK54XNTyyscCp2MOKTdn-oXbuZYaV8N5o2gVg,8688
|
|
142
|
+
openmd/ocr/otsl.py,sha256=zWH-Lt_llCtRmofQpyopw2oLR2waDP_qHD9uwxA2uqk,2042
|
|
143
|
+
openmd/ocr/ovis-prompt.txt,sha256=2_MdWJmoYEfODNTQ0SzcVhbfG-pdmmALgjCYRCwymXw,512
|
|
144
|
+
openmd/ocr/pipeline.py,sha256=8GtEyikEsfVkAcWqI5YfUxL9fa7Ka2r-ON2MHSGOnQM,24196
|
|
145
|
+
openmd/ocr/settings.json,sha256=hEW0ylpo_IRDw85BoM3yZhJ1pSa-5Oe5ZiuqHbkUWkA,637
|
|
146
|
+
openmd/ocr/tiling.py,sha256=lHewkeE62L1e-7ICJ8icDLNHhK3hXrddjB-PeVqLRWA,28109
|
|
147
|
+
openmd/routing/__init__.py,sha256=g4EqPf1Lt8nOdVDzQI-sBDBk01AgO7phTdDQx0QqjDc,60
|
|
148
|
+
openmd/routing/entry_shim.py,sha256=eE4uZItwt2D3TIDqlC1OiXWVH-bP2PgoZQoeFeNVoFY,2530
|
|
149
|
+
openmd/routing/lifecycle.py,sha256=5DWTiV6cclzWRkSmSLzlvr_w2ITxoyUzu5_1-LSkxLY,25128
|
|
150
|
+
openmd/routing/loader.py,sha256=HeTI3kdtSLX_1eBvQsOqXnFupEevxhO4o10JeIGoN7M,7252
|
|
151
|
+
openmd/routing/providers.py,sha256=v7W3AZ7Wn7E1paOy68AYvtDVLrEKshfTdZ4-o5O9hEM,10267
|
|
152
|
+
openmd/routing/readers.py,sha256=1aPOBsXOCiDBAV7-kTauISgg1AG7wU2yh9_5lLEWqpQ,5227
|
|
153
|
+
openmd/routing/routes.json,sha256=88vHh7Ujh9GLLhOXScJ3avjsbO0-F6Wf0E0dGvr2PEM,10095
|
|
154
|
+
openmd/routing/runner.py,sha256=86XCpUbg7myOaPAPESZRQOy84-YB7b51MIQql4MM_MU,10229
|
|
155
|
+
openmd/routing/view.py,sha256=120Moeh5wDVaerN7uQ6cjObojANQizUKVvTKgB4zmsY,1735
|
|
156
|
+
openmd/routing/cards/builtin.json,sha256=HaOFuKZ3YbfoC2nmkrGvg2LFm0G59zFjs7DcAikiNTE,14741
|
|
157
|
+
openmd/routing/cards/defuddle.json,sha256=vDzPYfDx6-Muc7I1LDD7vsPu4J6-7jkeU7YBjY5PsAE,1727
|
|
158
|
+
openmd/routing/cards/x-tweet-fetcher.json,sha256=9M9gQqd4q4sClkihfrxDtmrkWHYpPzZQokEig4hslOg,1827
|
|
159
|
+
openmd/routing/cards/xyz-dl.json,sha256=RV0mQ5uznCWsFQC1hpxTqqVK62hX4Yeu1fuL8d1uQUY,1513
|
|
160
|
+
openmd/routing/cards/yutto.json,sha256=PXd6jN9ocpwqth3o0p-O35O6sbHIxOAu5a2MB1f5jqk,1633
|
|
161
|
+
openmd/runtime/__init__.py,sha256=mYCHaOY_HoKDXBZCaaCONlJP3MzfGC4960S761oKdME,78
|
|
162
|
+
openmd/runtime/_windows_job.py,sha256=7KIGe55pJegJiXbRYH6oNARF6QNW13b7YCXIyFuE3OQ,11103
|
|
163
|
+
openmd/runtime/asr.py,sha256=4bqK44i5vXI1G-hIq_mEB9vaNy3QBmxoUiABx80qSNI,12387
|
|
164
|
+
openmd/runtime/asr_worker.py,sha256=MaU8RQOc0Fziv-yad-gnM3ofLYdVUjffp0NrrX37qZ0,5897
|
|
165
|
+
openmd/runtime/gpu_lock.py,sha256=8TyBHOj7DFpqyjM6E3bSJGOffJxxITKm6wvkiAfz1tA,2962
|
|
166
|
+
openmd/runtime/gpu_quarantine.py,sha256=JUKkXVXVtHTirHGOrzUP7s4SjYsYsNkhcxA85Fnc-48,3724
|
|
167
|
+
openmd/runtime/interpreters.py,sha256=p7612gApD-C0tmBXuidlM8bvGo-jvUUTfBoktk24CmM,20172
|
|
168
|
+
openmd/runtime/managed_tools.py,sha256=dggp5RMjBK-Ub-1_kgVnqh9VS7wvZg8KBIdJKVgY_94,8209
|
|
169
|
+
openmd/runtime/models.py,sha256=-_t_tLTyu4tLaP62FXEI9MUwTryY-kZX8ZSzBH78Pwk,9517
|
|
170
|
+
openmd/runtime/services.py,sha256=g2qa8SMEinkvMgaDUaXBhid4tPciT1TFJImGEu_akSw,7867
|
|
171
|
+
openmd/runtime/tempdirs.py,sha256=mzhmPJ9fjYK5rvvaXOHUJICNLcy9Q-1f1FNfd4SM-vo,9139
|
|
172
|
+
openmd_cli-0.1.0.dist-info/licenses/LICENSE,sha256=7Bw0bCz-dhYgU-WqpWRZZCx4l-5q5auk7QiAweAeMoY,1066
|
|
173
|
+
openmd_cli-0.1.0.dist-info/METADATA,sha256=-kb5EQKcQhPM1vtSWNcqrMsSNGMTPSrMEDs4X5GtnCQ,12092
|
|
174
|
+
openmd_cli-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
175
|
+
openmd_cli-0.1.0.dist-info/entry_points.txt,sha256=XM7lITA5SJNLa4Wxv-MbU-CFXWT5RxtqsEhkB6asmJo,43
|
|
176
|
+
openmd_cli-0.1.0.dist-info/top_level.txt,sha256=C_6VqPIAKSjzb2LCEE5pNh9fPWURfeBMDB_94-BjyXA,7
|
|
177
|
+
openmd_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 baigao417
|
|
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 @@
|
|
|
1
|
+
openmd
|