Добавить основную функциональность загрузки YouTube Shorts с использованием файла сессии и обработкой ошибок

This commit is contained in:
ВяткинАртём
2026-05-27 16:33:22 +03:00
parent 2d1f671d5a
commit d06bd989f3
23 changed files with 884 additions and 3 deletions
+50
View File
@@ -0,0 +1,50 @@
from __future__ import annotations
from pathlib import Path
import pytest
from yt_shorts_downloader import api
from yt_shorts_downloader.exceptions import InvalidSessionError, InvalidUrlError
from yt_shorts_downloader.models import SessionValidation
def test_download_returns_path_from_downloader(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
expected_path = tmp_path / "video.mp4"
monkeypatch.setattr(
api,
"validate_session_file",
lambda path: SessionValidation(True, True, True, True, str(path)),
)
monkeypatch.setattr(
api,
"download_video",
lambda *, url, output_dir, session_path: expected_path,
)
downloaded_path = api.download(
"https://youtube.com/shorts/example",
session_path="cookies.txt",
output_dir=tmp_path,
)
assert downloaded_path == expected_path
def test_download_rejects_invalid_url() -> None:
with pytest.raises(InvalidUrlError):
api.download("https://example.com/watch?v=1", session_path="cookies.txt")
def test_download_rejects_invalid_session(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
api,
"validate_session_file",
lambda path: SessionValidation(False, False, False, False, "bad session"),
)
with pytest.raises(InvalidSessionError, match="bad session"):
api.download("https://youtube.com/shorts/example", session_path="cookies.txt")
+43
View File
@@ -0,0 +1,43 @@
from __future__ import annotations
from pathlib import Path
import pytest
from yt_shorts_downloader import cli
from yt_shorts_downloader.exceptions import InvalidSessionError
def test_cli_prints_downloaded_path(
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
tmp_path: Path,
) -> None:
expected_path = tmp_path / "video.mp4"
monkeypatch.setattr(cli, "download", lambda **kwargs: expected_path)
exit_code = cli.main(
[
"https://youtube.com/shorts/example",
"cookies.txt",
"--output-dir",
str(tmp_path),
]
)
captured = capsys.readouterr()
assert exit_code == 0
assert captured.out.strip() == str(expected_path)
def test_cli_exits_with_error(monkeypatch: pytest.MonkeyPatch) -> None:
def raise_error(**kwargs: object) -> Path:
raise InvalidSessionError("session error")
monkeypatch.setattr(cli, "download", raise_error)
with pytest.raises(SystemExit) as exc_info:
cli.main(["https://youtube.com/shorts/example", "cookies.txt"])
assert exc_info.value.code == 1
+52
View File
@@ -0,0 +1,52 @@
from __future__ import annotations
from pathlib import Path
import pytest
from yt_shorts_downloader import runtime
def test_find_supported_js_runtimes_prefers_deno(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(
runtime,
"which",
lambda executable: "/usr/bin/deno" if executable == "deno" else None,
)
runtimes = runtime.find_supported_js_runtimes()
assert runtimes == {"deno": {"path": "/usr/bin/deno"}}
def test_find_supported_js_runtimes_uses_best_supported_node(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
first_node = tmp_path / ".nvm" / "versions" / "node" / "v22.1.0" / "bin" / "node"
second_node = tmp_path / ".nvm" / "versions" / "node" / "v22.9.0" / "bin" / "node"
second_node.parent.mkdir(parents=True)
first_node.parent.mkdir(parents=True)
first_node.write_text("", encoding="utf-8")
second_node.write_text("", encoding="utf-8")
monkeypatch.setattr(runtime, "which", lambda executable: None)
monkeypatch.setattr(Path, "home", lambda: tmp_path)
monkeypatch.setattr(
runtime,
"_probe_executable_version",
lambda executable: (22, 9, 0)
if executable.endswith("v22.9.0/bin/node")
else (22, 1, 0),
)
runtimes = runtime.find_supported_js_runtimes()
assert runtimes == {"node": {"path": str(second_node.resolve())}}
def test_parse_semver_handles_prefixed_versions() -> None:
assert runtime._parse_semver("v22.12.1") == (22, 12, 1)
assert runtime._parse_semver("node") is None
+38
View File
@@ -0,0 +1,38 @@
from __future__ import annotations
from pathlib import Path
from yt_shorts_downloader.session import validate_session_file
def test_validate_session_file_accepts_valid_youtube_session(tmp_path: Path) -> None:
session_file = tmp_path / "cookies.txt"
session_file.write_text(
"\n".join(
[
"# Netscape HTTP Cookie File",
".youtube.com\tTRUE\t/\tFALSE\t9999999999\tSID\tvalue1",
".youtube.com\tTRUE\t/\tTRUE\t9999999999\tSAPISID\tvalue2",
".youtube.com\tTRUE\t/\tTRUE\t9999999999\tLOGIN_INFO\tvalue3",
]
),
encoding="utf-8",
)
validation = validate_session_file(session_file)
assert validation.exists is True
assert validation.structurally_valid is True
assert validation.fresh is True
assert validation.is_usable is True
def test_validate_session_file_rejects_invalid_format(tmp_path: Path) -> None:
session_file = tmp_path / "cookies.txt"
session_file.write_text("broken\trow", encoding="utf-8")
validation = validate_session_file(session_file)
assert validation.exists is True
assert validation.structurally_valid is False
assert validation.is_usable is False