Publish Source Available licenses and remove Mutagen in YouTubeDL 1.4.7 / helper 1.3.3
This commit is contained in:
1 parent
71a808b298
commit
50457bc35b
27 files changed
+201
-58
No files matched your search
@@ -7,6 +7,7 @@ from pathlib import Path
|
||||
from host.bundle import ROOT, executable, manifest
|
||||
from host.common import EXTENSION_ID, HOST_NAME
|
||||
from host.dependencies import status
|
||||
from host.python_packages import validate_installed
|
||||
|
||||
|
||||
def verify():
|
||||
@@ -14,6 +15,7 @@ def verify():
|
||||
raise ValueError("Autonomous bundle metadata is missing.")
|
||||
if Path(sys.executable).resolve() != Path(executable("python")):
|
||||
raise ValueError("The helper must use its embedded Python.")
|
||||
validate_installed(ROOT / "runtime/python/Lib/site-packages")
|
||||
measured = status()
|
||||
if any(item["status"] != "installed" for item in measured):
|
||||
raise ValueError("Bundled components failed verification: " + json.dumps(measured))
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ from pathlib import Path
|
||||
from urllib.parse import parse_qs, urlsplit
|
||||
|
||||
HOST_NAME = "com.youtubedl.native"
|
||||
VERSION = "1.3.2"
|
||||
VERSION = "1.3.3"
|
||||
EXTENSION_ID = "ndnoeiaecgmjnjneekaeapeecmmjheli"
|
||||
VIDEO_CONTAINERS = {"mp4", "mkv", "webm"}
|
||||
AUDIO_CONTAINERS = {"m4a", "mp3", "opus", "flac", "wav"}
|
||||
|
||||
@@ -64,6 +64,7 @@ def install(key, progress, workspace=None):
|
||||
raise ValueError("Нажмите «Обновить компоненты» для установки новых версий из интернета или «Восстановить» для переустановки встроенного комплекта.")
|
||||
if key not in {"node", "ffmpeg", "yt_dlp", "ejs"}:
|
||||
raise ValueError("Неизвестный компонент.")
|
||||
from host.python_packages import requirements
|
||||
runtime = ROOT / ".runtime"
|
||||
runtime.mkdir(exist_ok=True)
|
||||
cfg = config()
|
||||
@@ -73,8 +74,8 @@ def install(key, progress, workspace=None):
|
||||
workspace(str(scratch))
|
||||
if key in {"yt_dlp", "ejs"}:
|
||||
progress(None, "Устанавливаю Python-пакет")
|
||||
package = "yt-dlp[default]>=2026.8.19" if key == "yt_dlp" else "yt-dlp-ejs"
|
||||
measured = subprocess.run([sys.executable, "-m", "pip", "install", "--disable-pip-version-check", "--upgrade", package],
|
||||
packages = requirements() if key == "yt_dlp" else ["yt-dlp-ejs"]
|
||||
measured = subprocess.run([sys.executable, "-m", "pip", "install", "--disable-pip-version-check", "--upgrade", *packages],
|
||||
stdin=subprocess.DEVNULL, capture_output=True, text=True, encoding="utf-8", errors="replace", creationflags=NO_WINDOW, timeout=600)
|
||||
if measured.returncode:
|
||||
raise ValueError("Не удалось установить компонент. " + measured.stderr[-1200:])
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
"""Python dependencies used by the helper; optional cover tagging is excluded."""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.metadata
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def requirements(yt_dlp="yt-dlp>=2026.8.19", ejs="yt-dlp-ejs"):
|
||||
return [yt_dlp, ejs, "brotli", "certifi", "pycryptodomex",
|
||||
"requests>=2.32.2,<3", "urllib3>=2,<3", "websockets>=13"]
|
||||
|
||||
|
||||
def validate_metadata(records):
|
||||
"""Reject strong copyleft Python libraries before activating a bundle.
|
||||
|
||||
FFmpeg is a separately executed program, so this policy concerns Python
|
||||
libraries loaded into the helper process, not the FFmpeg distribution.
|
||||
"""
|
||||
for metadata in records:
|
||||
name = re.sub(r"[-_.]+", "-", metadata.get("name", "").lower())
|
||||
licenses = [metadata.get("license_expression") or "", metadata.get("license") or "",
|
||||
*metadata.get("classifier", [])]
|
||||
incompatible = name == "mutagen" or any(
|
||||
re.search(r"(?<![A-Z])(?:A?GPL)(?:[-\s\dv]|$)|GNU (?:Affero )?General Public License",
|
||||
license, re.IGNORECASE) for license in licenses)
|
||||
if incompatible:
|
||||
raise ValueError(f"Python-пакет {name} требует отдельной проверки совместимости лицензии; комплект не изменён.")
|
||||
|
||||
|
||||
def validate_installed(directory: Path):
|
||||
records = []
|
||||
for distribution in importlib.metadata.distributions(path=[str(directory)]):
|
||||
metadata = distribution.metadata
|
||||
records.append({"name": metadata.get("Name", ""),
|
||||
"license_expression": metadata.get("License-Expression", ""),
|
||||
"license": metadata.get("License", ""),
|
||||
"classifier": metadata.get_all("Classifier") or []})
|
||||
validate_metadata(records)
|
||||
return {distribution.metadata["Name"]: distribution.version
|
||||
for distribution in importlib.metadata.distributions(path=[str(directory)])}
|
||||
+4
-1
@@ -19,6 +19,7 @@ from urllib.parse import urlsplit
|
||||
import requests
|
||||
from host.bundle import ROOT, installation_root, manifest
|
||||
from host.media import NO_WINDOW, run_small
|
||||
from host.python_packages import requirements, validate_metadata, validate_installed
|
||||
|
||||
ALLOWED_HOSTS = {"www.python.org", "nodejs.org", "api.github.com", "github.com", "release-assets.githubusercontent.com", "objects.githubusercontent.com", "pypi.org", "files.pythonhosted.org"}
|
||||
FFMPEG_ASSET = "ffmpeg-master-latest-win64-gpl-shared.zip"
|
||||
@@ -183,11 +184,12 @@ def resolve_packages(stage, work, progress):
|
||||
report = work / "python-packages.json"
|
||||
script = "import sys;sys.path.insert(0,sys.argv.pop(1));from pip._internal.cli.main import main;raise SystemExit(main(sys.argv[1:]))"
|
||||
command = [str(stage / "runtime/python/python.exe"), "-I", "-X", "utf8", "-B", "-c", script, str(pip), "--isolated", "--disable-pip-version-check", "install",
|
||||
"--dry-run", "--ignore-installed", "--only-binary=:all:", "--no-cache-dir", "--quiet", "--report", str(report), "--index-url", "https://pypi.org/simple", "yt-dlp[default]", "yt-dlp-ejs"]
|
||||
"--dry-run", "--ignore-installed", "--only-binary=:all:", "--no-cache-dir", "--quiet", "--report", str(report), "--index-url", "https://pypi.org/simple", *requirements()]
|
||||
result = subprocess.run(command, capture_output=True, text=True, encoding="utf-8", errors="replace", timeout=600, creationflags=NO_WINDOW,
|
||||
env={**os.environ, "TEMP": str(work), "TMP": str(work)}, stdin=subprocess.DEVNULL)
|
||||
if result.returncode: raise ValueError("Не удалось подобрать совместимые Python-пакеты. " + result.stderr[-1200:])
|
||||
records = json.loads(report.read_text(encoding="utf-8"))["install"]
|
||||
validate_metadata([record["metadata"] for record in records])
|
||||
packages = stage / "runtime/python/Lib/site-packages"
|
||||
if packages.exists(): shutil.rmtree(packages)
|
||||
packages.mkdir(parents=True)
|
||||
@@ -206,6 +208,7 @@ def resolve_packages(stage, work, progress):
|
||||
if packages.resolve() not in destination.parents: raise ValueError("Некорректный путь Python-пакета.")
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
destination.write_bytes(wheel.read(member))
|
||||
validate_installed(packages)
|
||||
return {item["metadata"]["name"]: item["metadata"]["version"] for item in records}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user