告别手动刷新!用Python+Watchdog为你的Emby Server打造一个自动影片推送机器人(附Docker一键部署)

张开发
2026/4/15 9:34:16 15 分钟阅读

分享文章

告别手动刷新!用Python+Watchdog为你的Emby Server打造一个自动影片推送机器人(附Docker一键部署)
用PythonWatchdog构建Emby Server智能推送系统从监听文件到Docker化部署每次下载新影片后手动刷新Emby Server的日子该结束了。作为媒体库管理员我们都经历过这样的场景下载了一部期待已久的大片却因为忘记刷新媒体库而错过第一时间欣赏的机会。本文将带你用Python的Watchdog模块打造一个智能监控系统结合Telegram Bot实现入库即推送的自动化体验最后通过Docker容器化让部署变得轻而易举。1. 系统架构设计与核心组件构建一个高效的Emby Server自动推送系统需要考虑三个核心组件文件监听、信息处理和消息推送。Watchdog模块负责监听文件系统变化TMDB API提供影片元数据而Telegram Bot则作为用户交互的桥梁。关键组件对比表组件作用替代方案优势Watchdog实时监控文件系统变化pyinotify (Linux only)跨平台支持API简洁TMDB API获取影片元数据OMDb API数据全面更新及时Telegram Bot推送通知Discord Webhook用户友好功能丰富在Python生态中Watchdog提供了跨平台的文件系统事件监控能力。它基于操作系统底层API实现在Linux上使用inotify在macOS使用FSEvents在Windows使用ReadDirectoryChangesW确保了我们方案的可移植性。from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler class EmbyHandler(FileSystemEventHandler): def on_created(self, event): if event.src_path.endswith(.nfo): process_new_media(event.src_path)这段基础监听代码展示了系统的核心逻辑——当检测到新的.nfo文件创建时触发处理流程。Emby Server会自动为每个新入库的媒体文件生成这种XML格式的元数据文件。2. 深度解析.nfo文件与TMDB数据整合Emby生成的.nfo文件包含了媒体内容的基本信息但要想获得更丰富的元数据和高质量的海报图片我们需要结合TMDB的API进行补充查询。典型的.nfo文件结构示例movie title盗梦空间/title originaltitleInception/originaltitle year2010/year tmdbid27205/tmdbid plot一群特殊的盗贼能够潜入人们梦境.../plot /movie解析这个XML文件后我们可以提取关键字段如tmdbid然后用它查询TMDB获取更详细的信息def get_tmdb_details(tmdb_id, media_type): base_url https://api.themoviedb.org/3 headers {Authorization: fBearer {TMDB_API}} if media_type movie: url f{base_url}/movie/{tmdb_id} else: url f{base_url}/tv/{tmdb_id} response requests.get(url, headersheaders) return response.json()提示TMDB API有请求频率限制建议实现本地缓存机制避免重复查询相同内容处理剧集类内容时逻辑会稍复杂因为需要区分季(Season)和集(Episode)的信息。一个完整的实现应该能够处理以下情况新电影入库新剧集季入库剧集新一集入库3. Telegram Bot集成与消息模板设计Telegram Bot提供了极其灵活的消息格式支持我们可以根据媒体类型设计不同的消息模板让推送内容更具吸引力。电影类消息模板示例def format_movie_message(movie_info): return f *{movie_info[title]}* ({movie_info[year]}) ⭐ 评分: {movie_info[vote_average]}/10 | 上映: {movie_info[release_date]} {movie_info[overview]} [查看海报]({movie_info[poster_url]}) 对于剧集类内容可以包含更多季和集的信息def format_tv_message(tv_info): seasons \n.join([f• 第{season[season_number]}季 ({season[episode_count]}集) for season in tv_info[seasons]]) return f *{tv_info[name]}* 状态: {tv_info[status]} | 语言: {tv_info[original_language]} {seasons} {tv_info[overview]} 注意Telegram的Markdown解析器有些特殊确保正确转义特殊字符避免格式错误为了提升用户体验可以考虑添加交互按钮让用户可以直接从推送消息执行一些操作标记为已观看添加到收藏立即播放需要配合Emby API4. Docker化部署与生产环境优化将整个系统Docker化可以解决环境依赖问题并简化部署流程。基于Python的Alpine镜像可以大幅减小镜像体积提升部署效率。优化后的Dockerfile示例FROM python:3.10-alpine WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . RUN mkdir -p /var/tmp/emby-watchdog ENV PYTHONUNBUFFERED1 CMD [python, main.py]这个精简的Dockerfile相比原始版本减少了约70%的镜像体积。关键优化点包括使用Alpine基础镜像--no-cache-dir避免缓存pip包清理不必要的构建中间文件环境变量配置指南变量名必需示例值说明BOT_TOKEN是123456:ABC-DEF1234Telegram Bot TokenCHAT_ID是-100123456789目标聊天IDTMDB_API是abcdef1234567890TMDB API密钥MEDIA_PATH是/media/movies媒体库挂载路径LOG_LEVEL否INFO日志级别(DEBUG/INFO/WARNING)启动容器时确保正确挂载媒体库目录docker run -d --name emby-watchdog \ -v /path/to/media:/media/movies \ -e BOT_TOKENyour_token \ -e CHAT_IDyour_chat_id \ -e TMDB_APIyour_tmdb_key \ -e MEDIA_PATH/media/movies \ your-image-name对于长期运行的服务建议添加以下配置日志轮转策略健康检查端点资源使用限制自动重启策略5. 高级功能扩展与实践建议基础功能实现后可以考虑添加一些增强功能来提升系统的实用性和可靠性。可能的扩展方向多媒体库路径支持自定义消息模板用户偏好设置过滤某些类型的通知失败重试机制推送历史记录一个实用的扩展是实现媒体类型过滤让用户可以选择只接收特定类型的通知# 在环境变量中添加 FILTER_TYPES os.getenv(FILTER_TYPES, movie,tv).lower().split(,) # 在处理逻辑中添加检查 if media_type not in FILTER_TYPES: logging.info(fSkipping {media_type} as its not in filtered types) return性能优化建议使用异步IO处理网络请求如aiohttp实现本地缓存减少API调用批量处理短时间内的大量文件变更优化日志记录级别减少I/O压力监控方面可以集成Prometheus指标导出跟踪文件处理数量API调用次数推送成功率处理延迟from prometheus_client import Counter, Gauge processed_files Counter(emby_processed_files, Number of processed files) push_errors Counter(emby_push_errors, Number of push failures) processing_time Gauge(emby_processing_time, Time taken to process a file)实际部署中可能会遇到各种边缘情况比如临时文件导致的误报网络波动导致的API失败文件系统事件丢失权限问题针对这些问题我的经验是增加足够的日志记录并实现适当的重试逻辑。例如对于TMDB API调用def safe_tmdb_request(url, max_retries3): for attempt in range(max_retries): try: response requests.get(url, timeout10) response.raise_for_status() return response.json() except Exception as e: if attempt max_retries - 1: raise time.sleep(2 ** attempt)媒体文件处理本身可能很耗时特别是当需要下载高清海报时。可以考虑实现一个预热的机制在系统启动时扫描媒体库建立本地缓存避免首次推送时等待时间过长。

更多文章