让每一张照片以更沉浸、更有温度的方式被看见
10K+
让每一张照片以更沉浸、更有温度的方式被看见。 Memories live on.
Memory Gallery(回忆画廊) 是一款开源、轻量、全平台兼容的智能照片屏显系统。基于 Python + Flask 构建,适配矿渣设备、旧平板、智能电视与多种 ARM 架构硬件,低成本打造属于自己的家用 / 商用智能屏。
| 功能 | 说明 |
|---|---|
| 多格式图片 | JPG / PNG / WEBP / HEIC / HEIF / DNG,按年/月归档 |
| 历史的今天 | 仅播放历年今日的照片视频;无匹配时自动扩大至当月 ±3 个月 |
| 智能轮播 | 可配置轮播间隔(默认 10 秒),支持随机 / 顺序播放 |
| 视频播放 | 支持 MP4 / MOV / AVI / MKV / WebM,自动转码为网页友好格式 |
| 屏幕常亮 | 防止设备休眠,保持展示不中断 |
| 功能 | 说明 |
|---|---|
| Ken Burns 微动效 | 缓慢缩放 + 智能焦点(基于三分法则的 8 个黄金比例点),4 档强度(微弱 / 标准 / 强烈 / 极强) |
| 横竖屏自适应 | 横向图片全屏展示,竖向图片居中并自动填充背景 |
| 过渡效果 | 支持淡入淡出 / 滑动切换 / 无特效 |
| 功能 | 说明 |
|---|---|
| AI Caption | 本地 LLM(Ollama 等)异步为每张照片生成"每图一语",不阻塞启动 |
| 地理位置 | 从 EXIF 读取 GPS 信息,逆地理编码为省/市地名 |
| Bark 通知 | 资源告警(CPU / 内存 / 磁盘)推送到 iOS 设备 |
| 监控面板 | 实时 CPU、内存、磁盘占用与 API 响应时间 |
| 上传自动压缩 | 用户库照片默认自动压缩(JPEG quality 90 + 24MP 缩放 + EXIF 保留),节省 40-60% 空间,画质接近无损;GIF / 含透明 PNG / 压缩后变大 → 自动跳过 |
| 轮播提前加载 | 自适应池深(小库直接吃满,大库封顶 50 张 LRU),按需异步预加载避免切换黑屏 |
| 日志系统 | 文件日志(按天轮转)+ 数据库日志,支持日志标签页查看 |
| 功能 | 说明 |
|---|---|
| 自动导入 | 监控 import/ 目录,新文件自动入库(可配置间隔) |
| 示例库 | sample_mode 开关,启用内置示例轮播(适合演示) |
| 库隔离 | 支持用户库(media_up/)和示例库(samples/)分离,可在不同模式下独立管理 |
docker run -d \
--name memory-gallery \
-p 8080:8080 \
-v ./data:/app/data \
-e TZ=Asia/Shanghai \
-e MONITOR_USERNAME=admin \
-e MONITOR_PASSWORD=admin123 \
--restart unless-stopped \
bayelc/memorygallery:latest
# 访问
open http://localhost:8080
services:
memory-gallery:
image: bayelc/memorygallery:latest
container_name: memory-gallery
ports:
- "8080:8080"
volumes:
- ./data:/app/data # 数据持久化(媒体文件、数据库、配置均在此目录)
env_file:
- .env
restart: unless-stopped
将图片/视频放入挂载的 <your-data-dir>/memorygallery-data/data/media/ 目录(默认配置):
# 假设你的媒体目录在 /path/to/photos
cp -r /path/to/photos/* "<your-data-dir>/memorygallery-data/data/media/"
> 服务启动时会自动扫描该目录并建立索引,放入照片后无需任何操作。
> 数据目录独立于项目源码,不会随 git 推送或 Docker 镜像分发。
---
## 环境变量
> 完整配置请参考 `.env.example` 模板文件。
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `TZ` | Asia/Shanghai | 时区 |
| `MONITOR_USERNAME` | admin | 管理界面用户名 |
| `MONITOR_PASSWORD` | admin123 | 管理界面密码 |
| `AMAP_API_KEY` | - | 高德地图 API Key(用于地理编码) |
| `LOCAL_LLM_ENABLED` | false | 启用本地 LLM 生成 Caption |
| `LOCAL_LLM_API_URL` | - | 本地 LLM API 地址(如 `http://192.168.1.100:11434`) |
| `LOCAL_LLM_MODEL` | - | LLM 模型名(如 `qwen2.5vl:latest`) |
| `LOCAL_LLM_PROMPT` | - | 自定义 Caption prompt |
| `AI_CAPTION_ENABLED` | false | 启用云端 AI Caption |
| `AI_CAPTION_API_URL` | - | AI Caption API 地址 |
| `AI_CAPTION_API_KEY` | - | AI Caption API Key |
| `AI_CAPTION_MODEL` | - | AI Caption 模型名 |
| `AI_CAPTION_PROMPT` | - | Caption 生成提示词 |
| `BARK_ENABLED` | false | 启用 Bark 告警推送 |
| `BARK_URL` | - | Bark 通知地址(如 `https://api.day.app/YOUR_KEY`) |
| `WECHAT_WEBHOOK_URL` | - | 企业微信 Webhook 通知地址 |
| `MG_HEALTH_INTERVAL_SEC` | 300 | 健康检查上报间隔(秒),设为 0 关闭 |
---
## 配置说明
配置文件位于容器内 `/app/data/config.json`,可通过挂载覆盖:
```json
{
"server": {
"port": 8080,
"host": "0.0.0.0",
"media_dir": "media",
"user_media_dir": "media_up",
"import_dir": "import",
"sample_dir": "samples"
},
"slideshow": {
"interval": 10,
"mode": "random",
"preload_count": 2
},
"display": {
"ken_burns": true,
"ken_burns_style": "subtle",
"ken_burns_duration": 6,
"show_time": true,
"time_position": "center",
"date_format": "YYYY年MM月DD日",
"show_weekday": true
},
"advanced": {
"supported_formats": [".jpg",".jpeg",".png",".webp",".heic",".heif",".dng",".cr2",".cr3",".arw",".nef",".nrw",".raf",".orf",".rw2",".srw",".pef",".mp4",".mov",".avi",".mkv",".webm"],
"video_transcode": true,
"video_fps": 30,
"video_codec": "h264",
"video_crf": 23
},
"features": {
"sample_mode": true,
"auto_import": true,
"watch_interval": 30,
"play_video": true
}
}
架构原则:项目源码与运行时数据严格分离。
# 项目源码(git 仓库,Docker 镜像源)
./MemoryGallery/
├── backend/ # Flask 后端代码
├── frontend/ # 前端(HTML/CSS/JS)
├── bundled_samples/ # 项目自带示例库(公开,构建时嵌入镜像)
├── scripts/ # 构建/部署/运维脚本
├── tests/ # pytest 测试用例
├── Dockerfile # 镜像构建
├── docker-compose.yaml # 生产部署
└── .github/workflows/ # CI/CD
# 运行时数据(独立目录,git 忽略,不会推送/打包)
<your-data-dir>/memorygallery-data/
├── data/ # 容器内 /app/data 的实际存储
│ ├── config.json # 配置文件(运行时生成)
│ ├── media/ # 用户媒体库
│ ├── media_up/ # 用户上传目录
│ ├── import/ # 自动导入监控目录
│ └── samples/ # 用户自定义示例
└── logs/ # 应用日志、监控日志
└── memory-gallery.db # 监控数据库(IP、API 错误、告警历史)
容器内完整路径:
/app/
├── backend/ # 后端代码
│ ├── server.py # Flask 主服务
│ ├── config.py # 配置管理
│ ├── medias.py # 媒体扫描、EXIF、地理编码
│ ├── database.py # SQLite 媒体库 + 异步 Caption 队列
│ ├── media_manager.py # 文件管理、自动导入
│ ├── ai_caption.py # 本地 LLM / 云端 AI Caption 生成
│ ├── monitor_db.py # 指标/告警/事件数据库
│ ├── logger.py # 统一日志模块
│ └── video_utils.py # ffmpeg 视频处理
└── frontend/ # 前端界面
|| 组件 | 版本 | 来源 / 备注 |
||------|------|------|
|| Python | 3.11 | 基础镜像 python:3.11-alpine |
|| Flask | 3.0.0 | requirements.txt 锁定 |
|| Werkzeug | 3.0.3 | Flask 依赖(随 Flask 锁定) |
|| Flask-Cors | 5.0.0 | 前后端分离 / 跨域请求 |
|| Pillow | 12.3.0 | 图片解码 + 缩略图 + EXIF |
|| pillow-heif | 1.6.0 | iPhone HEIC/HEIF 解码 |
|| psutil | 7.2.2 | 监控面板 CPU / 内存 / 磁盘 |
|| waitress | 3.0.1 | 生产 WSGI 服务器(多线程) |
|| requests | 2.31.0 | 高德 API / Bark / 企微 Webhook |
|| ffmpeg | 6.1.2-r1(Alpine community) | 视频转码 + Raw 解码 + 缩略图抽帧 |
|| SQLite | (内置于 Python) | 媒体库 + 监控数据库(库非服务) |
基础镜像:python:3.11-alpine(基于 Alpine Linux 3.21+)
说明:上述版本以
requirements.txt与 Alpineapk包索引为准。 如需精确构建后版本,可运行docker exec memory-gallery apk info ffmpeg查看。
镜像通过 docker buildx 同时构建 linux/amd64 + linux/arm64 双架构(默认平台由
scripts/build_and_push.py 的 DEFAULT_PLATFORMS 控制),可在树莓派 / 矿渣小主机 /
NAS / x86 服务器同一镜像 tag 下无缝运行。
Docker Hub: bayelc/memorygallery
浏览器访问后,点击右下角 全屏按钮 或按 F11。
在设置面板中开启 显示照片信息,可选择信息位置(左下/右下)。
确保文件格式在 supported_formats 中,且 play_video: true。视频会自动转码为 H.264/MP4,首次播放可能稍慢。
LOCAL_LLM_ENABLED=true 及相关环境变量# 进入容器检查
docker exec -it memory-gallery /bin/sh
# 示例库:数据库位于 /app/sample/media.db
# 用户库:数据库位于 /app/data/logs/media.db(需先创建)
sqlite3 /app/sample/media.db "SELECT COUNT(*) FROM media;"
# 查看媒体文件
find /app/samples -type f | wc -l # 示例库
find /app/data/media -type f | wc -l # 用户库(如有)
cd /path/to/MemoryGallery
docker compose pull
docker compose up -d
数据在 bind mount
./data目录中,更新镜像不会丢失任何数据。
Content type
Image
Digest
sha256:5e7cbe517…
Size
126.5 MB
Last updated
26 days ago
docker pull bayelc/memorygallery