Skip to content

Commit 89dc312

Browse files
ChanMoclaude
andcommitted
Release v0.4.0: 添加配置文件支持和 Gallery 随机模式
主要更新: 1. 配置文件支持 - 支持 ~/.config/tiklocal/config.yaml 配置文件 - 添加命令行参数解析(支持 --host, --port) - 配置优先级:命令行参数 > 环境变量 > 配置文件 > 默认值 - 添加 PyYAML 依赖 2. Gallery 随机模式 - 新增右下角模式切换按钮 - 支持随机浏览所有层级的图片 - 无限滚动自动加载 - 使用 localStorage 持久化用户选择 - 保持 Pinterest 瀑布流布局 3. 界面优化 - 修复按钮与底部导航栏的层级问题 - 优化图片懒加载性能 - 改进模态框事件绑定逻辑 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f77e1ba commit 89dc312

9 files changed

Lines changed: 402 additions & 85 deletions

File tree

CLAUDE.md

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ TikLocal is a Flask-based web application that provides a TikTok-like interface
88

99
## Key Dependencies
1010

11-
- **Backend**: Flask 3.1.0, Waitress (WSGI server)
11+
- **Backend**: Flask 3.1.0, Waitress (WSGI server), PyYAML 6.0 (config file support)
1212
- **Frontend**: TailwindCSS v4, Feather Icons, Hammer.js
1313
- **Python**: Requires Python >=3.10,<4.0
1414
- **Package Management**: Poetry for Python dependencies, npm for CSS building
@@ -33,20 +33,29 @@ npm run build-css-prod
3333
# Install dependencies
3434
poetry install
3535

36-
# Run the application
37-
poetry run tiklocal /path/to/media/folder
36+
# Run the application (multiple ways)
37+
poetry run tiklocal /path/to/media/folder # Direct path argument
38+
poetry run tiklocal --port 9000 # Custom port with config file
39+
MEDIA_ROOT=/path tiklocal # Environment variable
40+
tiklocal # Use config file
3841

39-
# Or run directly
40-
python -m tiklocal.run
42+
# Configuration file (optional)
43+
# Create ~/.config/tiklocal/config.yaml:
44+
# media_root: /path/to/media
45+
# host: 0.0.0.0
46+
# port: 8000
47+
48+
# Get help
49+
tiklocal --help
4150
```
4251

4352
## Architecture
4453

4554
### Core Application Structure
4655

4756
- **`tiklocal/app.py`**: Main Flask application factory with all routes and view functions
48-
- **`tiklocal/run.py`**: Entry point that starts the Waitress WSGI server on port 8000
49-
- **`tiklocal/config.py`**: Configuration file (currently empty, uses environment variables)
57+
- **`tiklocal/run.py`**: CLI entry point with argument parsing, config file loading, and Waitress server startup
58+
- **`tiklocal/config.py`**: Configuration file (currently empty, configuration handled via config file/env vars)
5059

5160
### Key Routes and Features
5261

@@ -74,14 +83,63 @@ python -m tiklocal.run
7483

7584
### Configuration
7685

77-
- **Environment Variables**: `MEDIA_ROOT` (required) - path to media directory
86+
The application supports multiple configuration methods with the following priority (highest to lowest):
87+
88+
1. **Command Line Arguments**: Direct arguments passed to `tiklocal`
89+
- `tiklocal /path/to/media` - specify media root
90+
- `--host HOST` - server host (default: 0.0.0.0)
91+
- `--port PORT` - server port (default: 8000)
92+
93+
2. **Environment Variables**:
94+
- `MEDIA_ROOT` - path to media directory
95+
- `TIKLOCAL_HOST` - server host
96+
- `TIKLOCAL_PORT` - server port
97+
98+
3. **Configuration File**: `~/.config/tiklocal/config.yaml` or `~/.tiklocal/config.yaml`
99+
```yaml
100+
media_root: /path/to/media
101+
host: 0.0.0.0
102+
port: 8000
103+
```
104+
105+
4. **Defaults**: host=0.0.0.0, port=8000
106+
78107
- **Instance Config**: Uses Flask's instance-relative configuration
79108
- **Favorites**: Stored as JSON file in media root directory
80109
81110
## Development Notes
82111
83-
- The application uses environment variable `MEDIA_ROOT` to determine media location
112+
- Configuration priority: CLI args > Environment variables > Config file > Defaults
113+
- Config file locations: `~/.config/tiklocal/config.yaml` or `~/.tiklocal/config.yaml`
84114
- Templates include responsive design optimized for mobile and tablet usage
85115
- Custom Jinja2 filters for timestamp and file size formatting
86116
- Error handling includes user-friendly messages and proper HTTP status codes
87-
- Dark mode implementation uses CSS custom properties and data attributes
117+
- Dark mode implementation uses CSS custom properties and data attributes
118+
119+
## Release Process
120+
121+
When publishing a new version to PyPI:
122+
123+
1. **Update version number** in `pyproject.toml`:
124+
```toml
125+
version = "x.y.z"
126+
```
127+
128+
2. **Commit changes**:
129+
```bash
130+
git add .
131+
git commit -m "Release vx.y.z: description of changes"
132+
```
133+
134+
3. **Create and push git tag** (required for PyPI build):
135+
```bash
136+
git tag -a vx.y.z -m "Release vx.y.z: description"
137+
git push origin vx.y.z
138+
```
139+
140+
4. **Push commits**:
141+
```bash
142+
git push
143+
```
144+
145+
**Note**: The git tag triggers the CI/CD pipeline to automatically build and publish to PyPI.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"version": "0.1.6",
44
"description": "A local media server that combines the features of TikTok and Pinterest",
55
"scripts": {
6-
"build-css": "tailwindcss -i ./tiklocal/static/tailwind.css -o ./tiklocal/static/output.css --watch",
7-
"build-css-prod": "tailwindcss -i ./tiklocal/static/tailwind.css -o ./tiklocal/static/output.css --minify",
6+
"build-css": "tailwindcss -i ./tiklocal/static/input.css -o ./tiklocal/static/output.css --watch",
7+
"build-css-prod": "tailwindcss -i ./tiklocal/static/input.css -o ./tiklocal/static/output.css --minify",
88
"dev": "npm run build-css",
99
"build": "npm run build-css-prod"
1010
},

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "TikLocal"
3-
version = "0.3.8"
3+
version = "0.4.0"
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"
@@ -16,6 +16,7 @@ tiklocal = 'tiklocal.run:main'
1616
python = ">=3.10,<4.0"
1717
flask = "^3.1.0"
1818
waitress = "^3.0.2"
19+
pyyaml = "^6.0"
1920

2021
[[tool.poetry.source]]
2122
name = "aliyun"

tiklocal/app.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,40 @@ def api_videos():
180180

181181
return json.dumps(res)
182182

183+
@app.route('/api/random-images')
184+
def api_random_images():
185+
""" API to get random images with pagination """
186+
root = Path(app.config["MEDIA_ROOT"])
187+
page = int(request.args.get('page', 1))
188+
page_size = int(request.args.get('size', 30))
189+
190+
# 获取所有图片文件
191+
images = []
192+
for ext in ['*.jpg', '*.jpeg', '*.png', '*.gif', '*.webp', '*.bmp']:
193+
images.extend(root.glob(f'**/{ext}'))
194+
images.extend(root.glob(f'**/{ext.upper()}'))
195+
196+
# 随机打乱(使用固定种子确保同一会话中的一致性)
197+
seed = request.args.get('seed', str(random.randint(1, 999999)))
198+
random.seed(seed)
199+
random.shuffle(images)
200+
201+
# 分页
202+
total = len(images)
203+
start = (page - 1) * page_size
204+
end = start + page_size
205+
page_images = images[start:end]
206+
207+
res = [str(img.relative_to(root)) for img in page_images]
208+
209+
return {
210+
'images': res,
211+
'page': page,
212+
'total': total,
213+
'has_more': end < total,
214+
'seed': seed
215+
}
216+
183217

184218
@app.route('/settings/')
185219
def settings_view():

tiklocal/run.py

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,105 @@
1+
import os
2+
import sys
3+
import argparse
4+
from pathlib import Path
15
from waitress import serve
26
from tiklocal.app import create_app
37

8+
try:
9+
import yaml
10+
except ImportError:
11+
yaml = None
12+
13+
14+
def load_config():
15+
"""从配置文件加载配置"""
16+
config = {}
17+
18+
# 尝试读取配置文件
19+
config_paths = [
20+
Path.home() / '.config' / 'tiklocal' / 'config.yaml',
21+
Path.home() / '.tiklocal' / 'config.yaml',
22+
]
23+
24+
for config_path in config_paths:
25+
if config_path.exists():
26+
if yaml is None:
27+
print(f"警告: 找到配置文件 {config_path} 但未安装 PyYAML,跳过配置文件", file=sys.stderr)
28+
break
29+
try:
30+
with open(config_path, 'r', encoding='utf-8') as f:
31+
config = yaml.safe_load(f) or {}
32+
break
33+
except Exception as e:
34+
print(f"警告: 读取配置文件 {config_path} 失败: {e}", file=sys.stderr)
35+
36+
return config
37+
38+
439
def main():
5-
serve(create_app(), host='0.0.0.0', port=8000)
40+
# 读取配置文件
41+
config = load_config()
42+
43+
# 解析命令行参数
44+
parser = argparse.ArgumentParser(
45+
description='TikLocal - 本地媒体服务器',
46+
formatter_class=argparse.RawDescriptionHelpFormatter,
47+
epilog='''
48+
示例:
49+
tiklocal # 使用配置文件或环境变量
50+
tiklocal /path/to/media # 指定媒体目录
51+
tiklocal --port 9000 # 使用指定端口
52+
tiklocal /path/to/media --port 9000
53+
'''
54+
)
55+
56+
parser.add_argument(
57+
'media_root',
58+
nargs='?',
59+
help='媒体文件根目录路径'
60+
)
61+
parser.add_argument(
62+
'--host',
63+
default=None,
64+
help='服务器监听地址 (默认: 0.0.0.0)'
65+
)
66+
parser.add_argument(
67+
'--port',
68+
type=int,
69+
default=None,
70+
help='服务器端口 (默认: 8000)'
71+
)
72+
73+
args = parser.parse_args()
74+
75+
# 配置优先级: 命令行参数 > 环境变量 > 配置文件 > 默认值
76+
media_root = args.media_root or os.environ.get('MEDIA_ROOT') or config.get('media_root')
77+
host = args.host or os.environ.get('TIKLOCAL_HOST') or config.get('host', '0.0.0.0')
78+
port = args.port or int(os.environ.get('TIKLOCAL_PORT', 0)) or config.get('port', 8000)
79+
80+
# 验证媒体目录
81+
if not media_root:
82+
parser.error('必须指定媒体目录:\n - 通过命令行参数: tiklocal /path/to/media\n - 通过环境变量: MEDIA_ROOT=/path/to/media\n - 通过配置文件: ~/.config/tiklocal/config.yaml')
83+
84+
media_path = Path(media_root)
85+
if not media_path.exists():
86+
print(f"错误: 媒体目录不存在: {media_root}", file=sys.stderr)
87+
sys.exit(1)
88+
89+
if not media_path.is_dir():
90+
print(f"错误: 路径不是目录: {media_root}", file=sys.stderr)
91+
sys.exit(1)
92+
93+
# 设置环境变量供 Flask 使用
94+
os.environ['MEDIA_ROOT'] = str(media_path.absolute())
95+
96+
# 启动服务器
97+
print(f"启动 TikLocal 服务器...")
98+
print(f"媒体目录: {media_path.absolute()}")
99+
print(f"访问地址: http://{host}:{port}")
100+
101+
serve(create_app(), host=host, port=port)
102+
6103

7104
if __name__ == '__main__':
8105
main()

tiklocal/static/output.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tiklocal/templates/base.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<title>TikLocal</title>
66
<meta name="viewport" content="width=device-width, initial-scale=1" />
77
<link rel="stylesheet" type="text/css" href="{{url_for('static', filename='output.css')}}">
8+
<script src="{{url_for('static', filename='feather.min.js')}}"></script>
89
{% block extra_head %}{% endblock %}
910
</head>
1011
<body data-theme="{{ theme if theme else 'light' }}" class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-white min-h-screen pb-16 transition-colors duration-300">
@@ -39,7 +40,6 @@
3940
</nav>
4041

4142
{% block extra_body %}{% endblock %}
42-
<script src="{{url_for('static', filename='feather.min.js')}}"></script>
4343
<script>
4444
// 主题管理类
4545
class ThemeManager {

0 commit comments

Comments
 (0)