Skip to content

Commit be00289

Browse files
committed
release: v0.8.6 双引擎下载与图片详情修复
1 parent e59d05b commit be00289

10 files changed

Lines changed: 371 additions & 39 deletions

File tree

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,14 @@ TikLocal includes a `/download` page where you can paste a media URL and enqueue
8282

8383
Requirements:
8484
- `yt-dlp` (required)
85+
- `gallery-dl` (recommended for image/gallery posts)
8586
- `ffmpeg` (recommended for format merge)
8687

88+
Download engine:
89+
- `yt-dlp`: video-oriented sites and links
90+
- `gallery-dl`: image posts/albums (Instagram/X/Pinterest, etc.)
91+
- Download form allows manual engine selection per task (default: `yt-dlp`)
92+
8793
Cookie for login-only content (optional):
8894
- Put exported cookie files in `~/.tiklocal/cookies`
8995
- Filename should include domain, e.g. `x.com.txt`, `youtube.com.cookies`
@@ -93,10 +99,10 @@ Cookie for login-only content (optional):
9399
Example installs:
94100
```bash
95101
# macOS (Homebrew)
96-
brew install yt-dlp ffmpeg
102+
brew install yt-dlp gallery-dl ffmpeg
97103

98104
# Ubuntu / Debian
99-
sudo apt install yt-dlp ffmpeg
105+
sudo apt install yt-dlp gallery-dl ffmpeg
100106
```
101107

102108
### Configuration

README_zh.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,14 @@ TikLocal 新增了 `/download` 页面,可粘贴媒体 URL 并创建后台下
8080

8181
依赖要求:
8282
- `yt-dlp`(必需)
83+
- `gallery-dl`(建议,用于图片帖/图集)
8384
- `ffmpeg`(建议,用于格式合并)
8485

86+
下载引擎说明:
87+
- `yt-dlp`:更适合视频链接
88+
- `gallery-dl`:更适合图片帖与图集(如 Instagram/X/Pinterest)
89+
- 下载表单支持按任务手动选择引擎(默认 `yt-dlp`
90+
8591
登录态内容(可选):
8692
- 将导出的 cookie 文件放到 `~/.tiklocal/cookies`
8793
- 文件名建议包含域名,例如 `x.com.txt``youtube.com.cookies`
@@ -91,10 +97,10 @@ TikLocal 新增了 `/download` 页面,可粘贴媒体 URL 并创建后台下
9197
安装示例:
9298
```bash
9399
# macOS (Homebrew)
94-
brew install yt-dlp ffmpeg
100+
brew install yt-dlp gallery-dl ffmpeg
95101

96102
# Ubuntu / Debian
97-
sudo apt install yt-dlp ffmpeg
103+
sudo apt install yt-dlp gallery-dl ffmpeg
98104
```
99105

100106
### 配置

docs/release_notes.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Release Notes
22

3+
## v0.8.6 (2026-02-21)
4+
- URL 下载中心升级为双引擎:支持按任务手动选择 `yt-dlp` / `gallery-dl`,并在运行状态中展示双引擎可用性与版本。
5+
- 下载任务模型增强:新增 `engine``engine_version``output_files_rel``file_count` 字段;兼容旧任务历史记录恢复。
6+
- 新增 `gallery-dl` 下载链路:支持 cookie 文件复用、`download-archive` 去重归档、临时目录收敛后写回媒体根目录(含重名自动避让)。
7+
- 修复下载列表“查看文件”跳转:图片文件改为进入 `/image` 详情页;并为 `/detail/<image>` 增加后端自动重定向兜底。
8+
- 更新中英文 README 与下载测试覆盖,补充双引擎使用与安装说明。
9+
310
## v0.8.5 (2026-02-20)
411
- 新增 URL 下载中心(`/download`)与后台任务队列:支持任务创建、取消、删除、清空历史与失败重试。
512
- 新增 cookie 文件方案:支持 `~/.tiklocal/cookies` 自动匹配/手动指定、页面上传即同名覆盖更新。

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tiklocal",
3-
"version": "0.8.5",
3+
"version": "0.8.6",
44
"description": "A local media server that combines the features of TikTok and Pinterest",
55
"scripts": {
66
"build-css": "tailwindcss -i ./tiklocal/static/input.css -o ./tiklocal/static/output.css --watch",

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "TikLocal"
3-
version = "0.8.5"
3+
version = "0.8.6"
44
description = "A local media server that combines the features of TikTok and Pinterest"
55
authors = ["ChanMo <chan.mo@outlook.com>"]
66
readme = "README.md"

tests/test_download.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ def test_download_config_api(client):
4848
assert res.status_code == 200
4949
assert data["success"] is True
5050
assert data["data"]["effective"]["max_concurrent"] == 2
51+
assert data["data"]["effective"]["gallery_archive_enabled"] is True
52+
assert "gallery_archive_file" in data["data"]["effective"]
5153

5254
res = client.post(
5355
"/api/download/config",
@@ -90,9 +92,42 @@ def test_create_download_job_success(client):
9092
final_job = _wait_for_job(client, job_id)
9193
assert final_job["status"] == "success"
9294
assert final_job["output_path_rel"] == "mock-output.mp4"
95+
assert final_job["output_files_rel"] == ["mock-output.mp4"]
96+
assert final_job["file_count"] == 1
97+
assert final_job["engine"] == "yt-dlp"
9398
assert final_job["cookie_match_mode"] == "none"
9499

95100

101+
def test_create_download_job_with_gallery_engine(client):
102+
res = client.post("/api/download/jobs", json={"url": "https://example.com/post", "engine": "gallery-dl"})
103+
data = res.get_json()
104+
assert res.status_code == 200
105+
assert data["success"] is True
106+
final_job = _wait_for_job(client, data["data"]["job"]["id"])
107+
assert final_job["status"] == "success"
108+
assert final_job["engine"] == "gallery-dl"
109+
110+
111+
def test_create_download_job_rejects_invalid_engine(client):
112+
res = client.post("/api/download/jobs", json={"url": "https://example.com/video", "engine": "wget"})
113+
data = res.get_json()
114+
assert res.status_code == 400
115+
assert data["success"] is False
116+
assert "engine" in data["error"]
117+
118+
119+
def test_detail_route_redirects_image_to_image_view(client):
120+
media_root = client.application.config["MEDIA_ROOT"]
121+
image_file = media_root / "from-download.JPG"
122+
image_file.write_bytes(b"\x89PNG\r\n")
123+
124+
res = client.get("/detail/from-download.JPG", follow_redirects=False)
125+
assert res.status_code in {301, 302, 308}
126+
location = res.headers.get("Location", "")
127+
assert location.startswith("/image?uri=")
128+
assert "from-download.JPG" in location
129+
130+
96131
def test_create_download_job_validation(client):
97132
res = client.post("/api/download/jobs", json={"url": "file:///tmp/a.mp4"})
98133
data = res.get_json()
@@ -128,6 +163,7 @@ def test_download_probe_api(client):
128163
assert res.status_code == 200
129164
assert data["success"] is True
130165
assert "yt_dlp_available" in data["data"]
166+
assert "gallery_dl_available" in data["data"]
131167
assert "ffmpeg_available" in data["data"]
132168

133169

@@ -200,7 +236,7 @@ def fail_execute(self, job_id): # noqa: ARG001
200236
return 1, "network", ""
201237

202238
monkeypatch.setattr("tiklocal.services.downloader.DownloadManager._execute_download", fail_execute)
203-
res = client.post("/api/download/jobs", json={"url": "https://example.com/fail"})
239+
res = client.post("/api/download/jobs", json={"url": "https://example.com/fail", "engine": "gallery-dl"})
204240
data = res.get_json()
205241
assert res.status_code == 200
206242
failed_job = _wait_for_job(client, data["data"]["job"]["id"])
@@ -216,6 +252,7 @@ def ok_execute(self, job_id): # noqa: ARG001
216252
new_job_id = retry_data["data"]["job"]["id"]
217253
assert new_job_id != failed_job["id"]
218254
assert retry_data["data"]["job"]["retry_of"] == failed_job["id"]
255+
assert retry_data["data"]["job"]["engine"] == "gallery-dl"
219256
final = _wait_for_job(client, new_job_id)
220257
assert final["status"] == "success"
221258

tiklocal/app.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from flask import Flask, render_template, send_from_directory, request, redirect, send_file
1111

1212
# Service Imports
13-
from tiklocal.services import LibraryService, FavoriteService, RecommendService
13+
from tiklocal.services import LibraryService, FavoriteService, RecommendService, IMAGE_EXTENSIONS
1414
from tiklocal.services.thumbnail import ThumbnailService
1515
from tiklocal.services.metadata import (
1616
ImageMetadataStore,
@@ -287,6 +287,9 @@ def detail_view(name):
287287
target = library_service.resolve_path(name)
288288
if not target or not target.exists():
289289
return "File not found", 404
290+
291+
if target.suffix.lower() in IMAGE_EXTENSIONS:
292+
return redirect(f"/image?uri={quote(name)}")
290293

291294
# Context navigation (prev/next)
292295
# Note: Re-scanning every request is inefficient for large libraries,
@@ -469,6 +472,7 @@ def api_download_jobs():
469472
job = download_manager.enqueue(
470473
validated['url'],
471474
save_mode=validated['save_mode'],
475+
engine=validated.get('engine', 'yt-dlp'),
472476
cookie_mode=validated.get('cookie_mode', 'auto'),
473477
cookie_file=validated.get('cookie_file', ''),
474478
)

0 commit comments

Comments
 (0)