mirror of
https://github.com/JefferyHcool/BiliNote.git
synced 2026-09-05 15:46:48 +08:00
Merge pull request #446 from JefferyHcool/codex/release-v2-4-5
chore(release): v2.4.5
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||||
"productName": "BiliNote",
|
"productName": "BiliNote",
|
||||||
"version": "2.4.4",
|
"version": "2.4.5",
|
||||||
"identifier": "com.jefferyhuang.bilinote",
|
"identifier": "com.jefferyhuang.bilinote",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../dist",
|
"frontendDist": "../dist",
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
"""Model-selection contract tests without loading the Whisper runtime."""
|
||||||
|
import importlib.util
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
MODULE_PATH = ROOT / "app" / "transcriber" / "transcriber_provider.py"
|
||||||
|
|
||||||
|
|
||||||
|
class _Logger:
|
||||||
|
def info(self, *_args, **_kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def warning(self, *_args, **_kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def error(self, *_args, **_kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class _Whisper:
|
||||||
|
def __init__(self, model_size, device):
|
||||||
|
self.model_size = model_size
|
||||||
|
self.device = device
|
||||||
|
|
||||||
|
|
||||||
|
def _stub(monkeypatch, name, **attrs):
|
||||||
|
module = types.ModuleType(name)
|
||||||
|
for key, value in attrs.items():
|
||||||
|
setattr(module, key, value)
|
||||||
|
monkeypatch.setitem(sys.modules, name, module)
|
||||||
|
|
||||||
|
|
||||||
|
def _load_provider(monkeypatch):
|
||||||
|
_stub(monkeypatch, "app")
|
||||||
|
_stub(monkeypatch, "app.transcriber")
|
||||||
|
_stub(monkeypatch, "app.utils")
|
||||||
|
_stub(monkeypatch, "app.transcriber.groq", GroqTranscriber=object)
|
||||||
|
_stub(monkeypatch, "app.transcriber.whisper", WhisperTranscriber=_Whisper)
|
||||||
|
_stub(monkeypatch, "app.transcriber.bcut", BcutTranscriber=object)
|
||||||
|
_stub(monkeypatch, "app.transcriber.kuaishou", KuaishouTranscriber=object)
|
||||||
|
_stub(monkeypatch, "app.utils.logger", get_logger=lambda _name: _Logger())
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location("transcriber_provider_under_test", MODULE_PATH)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
assert spec.loader is not None
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def test_explicit_whisper_model_overrides_docker_default(monkeypatch):
|
||||||
|
"""A saved UI choice must not be replaced by WHISPER_MODEL_SIZE."""
|
||||||
|
provider = _load_provider(monkeypatch)
|
||||||
|
monkeypatch.setenv("WHISPER_MODEL_SIZE", "tiny")
|
||||||
|
|
||||||
|
transcriber = provider.get_transcriber(
|
||||||
|
transcriber_type="fast-whisper",
|
||||||
|
model_size="large-v3-turbo",
|
||||||
|
device="cpu",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert transcriber.model_size == "large-v3-turbo"
|
||||||
|
|
||||||
|
|
||||||
|
def test_switching_whisper_models_rebuilds_the_cached_instance(monkeypatch):
|
||||||
|
"""Caching only by transcriber type would keep returning the first model."""
|
||||||
|
provider = _load_provider(monkeypatch)
|
||||||
|
monkeypatch.delenv("WHISPER_MODEL_SIZE", raising=False)
|
||||||
|
|
||||||
|
base = provider.get_transcriber("fast-whisper", model_size="base", device="cpu")
|
||||||
|
turbo = provider.get_transcriber("fast-whisper", model_size="large-v3-turbo", device="cpu")
|
||||||
|
|
||||||
|
assert turbo is not base
|
||||||
|
assert turbo.model_size == "large-v3-turbo"
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
"""Regression coverage for the retry contract at the yt-dlp boundary."""
|
||||||
|
import importlib.util
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
import types
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = pathlib.Path(__file__).resolve().parents[1]
|
||||||
|
MODULE_PATH = ROOT / "app" / "downloaders" / "youtube_downloader.py"
|
||||||
|
|
||||||
|
|
||||||
|
def _stub(monkeypatch, name, **attrs):
|
||||||
|
module = types.ModuleType(name)
|
||||||
|
for key, value in attrs.items():
|
||||||
|
setattr(module, key, value)
|
||||||
|
monkeypatch.setitem(sys.modules, name, module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
class _Downloader:
|
||||||
|
def __init__(self):
|
||||||
|
self.cache_data = "/tmp"
|
||||||
|
|
||||||
|
|
||||||
|
class _AudioDownloadResult:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.__dict__.update(kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def _load_downloader_module(monkeypatch):
|
||||||
|
_stub(monkeypatch, "app")
|
||||||
|
_stub(monkeypatch, "app.downloaders")
|
||||||
|
_stub(monkeypatch, "app.models")
|
||||||
|
_stub(monkeypatch, "app.services")
|
||||||
|
_stub(monkeypatch, "app.utils")
|
||||||
|
_stub(
|
||||||
|
monkeypatch,
|
||||||
|
"app.downloaders.base",
|
||||||
|
Downloader=_Downloader,
|
||||||
|
DownloadQuality=str,
|
||||||
|
YDL_RETRY_OPTS={"retries": 3, "fragment_retries": 3, "socket_timeout": 30},
|
||||||
|
)
|
||||||
|
_stub(monkeypatch, "app.downloaders.youtube_subtitle", YouTubeSubtitleFetcher=object)
|
||||||
|
_stub(monkeypatch, "app.models.notes_model", AudioDownloadResult=_AudioDownloadResult)
|
||||||
|
_stub(monkeypatch, "app.models.transcriber_model", TranscriptResult=object)
|
||||||
|
_stub(
|
||||||
|
monkeypatch,
|
||||||
|
"app.services.proxy_config_manager",
|
||||||
|
ProxyConfigManager=type("ProxyConfigManager", (), {"get_proxy_url": lambda self: None}),
|
||||||
|
)
|
||||||
|
_stub(monkeypatch, "app.utils.path_helper", get_data_dir=lambda: "/tmp")
|
||||||
|
_stub(monkeypatch, "app.utils.url_parser", extract_video_id=lambda url, platform: "video-id")
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location("youtube_downloader_under_test", MODULE_PATH)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
assert spec.loader is not None
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
class _CapturingYoutubeDL:
|
||||||
|
options = None
|
||||||
|
|
||||||
|
def __init__(self, options):
|
||||||
|
type(self).options = options
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, *exc_info):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def extract_info(self, _url, download=True):
|
||||||
|
return {
|
||||||
|
"id": "video-id",
|
||||||
|
"title": "Example",
|
||||||
|
"duration": 1,
|
||||||
|
"thumbnail": None,
|
||||||
|
"ext": "m4a",
|
||||||
|
"tags": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_audio_download_configures_a_nonzero_retry_budget(tmp_path, monkeypatch):
|
||||||
|
"""Removing retry options must make transient download errors unprotected."""
|
||||||
|
module = _load_downloader_module(monkeypatch)
|
||||||
|
original_youtube_dl = module.yt_dlp.YoutubeDL
|
||||||
|
module.yt_dlp.YoutubeDL = _CapturingYoutubeDL
|
||||||
|
try:
|
||||||
|
module.YoutubeDownloader().download("https://youtu.be/example", str(tmp_path))
|
||||||
|
finally:
|
||||||
|
module.yt_dlp.YoutubeDL = original_youtube_dl
|
||||||
|
|
||||||
|
options = _CapturingYoutubeDL.options
|
||||||
|
assert options["retries"] > 0
|
||||||
|
assert options["fragment_retries"] > 0
|
||||||
|
assert options["socket_timeout"] > 0
|
||||||
@@ -31,7 +31,11 @@ def _stub(name, **attrs):
|
|||||||
module = types.ModuleType(name)
|
module = types.ModuleType(name)
|
||||||
for key, value in attrs.items():
|
for key, value in attrs.items():
|
||||||
setattr(module, key, value)
|
setattr(module, key, value)
|
||||||
sys.modules.setdefault(name, module)
|
# Tests in this repository load downloader modules with lightweight app
|
||||||
|
# stubs. Replace a previous test's stub instead of inheriting it; otherwise
|
||||||
|
# a prior import can leave AudioDownloadResult as ``object`` and turn this
|
||||||
|
# metadata-only test into a TypeError unrelated to the behavior under test.
|
||||||
|
sys.modules[name] = module
|
||||||
return module
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user