深色模式
通知模板
定位:NanShare 的三类通知模板(文件整理、订阅、Emby)统一使用 Gonja(Go 版 Jinja2)语法。文件整理与 Emby 是
title/text/image结构化模板,订阅是纯文本模板;每类模板的可用变量都要落在对应字段目录内,保存时会被逐项校验。
前置条件
- 已配置 Telegram Bot。
- Emby 通知模板需在 Emby 中把 Webhook 指向 NanShare。
- 文件整理通知需先开启整理基础配置中的整理通知开关,触发时机跟整理/监控任务绑定,参考 监控配置。
- 订阅通知由订阅策略触发,相关开关与策略在 订阅设置 中维护。
文件整理通知
模板结构
- 结构键固定为
title、text、image、link,缺键按空串处理。 - 出现其它键:报「通知模板存在未支持键:…」。
- 完全没有结构键:报「整理通知模板需要使用 title/text/image/link 结构键」。
字段目录(29 项)
| key | label | snippet | 说明 |
|---|---|---|---|
title_year | 标题+年份 | | 如 年会不能停! (2023) |
title | 标题 | | 媒体标题,不含年份 |
year | 年份 | | 媒体年份 |
season_episode | 季集 | | 如 S01 E03,电影为空 |
media_type | 媒体类型 | | 如 电影、电视剧 |
category | 分类 | | 整理后的二级分类 |
resource_quality | 质量 | | 如 WEB-DL DV HDR10 2160p DV P7 |
releaseGroup | 压制组/字幕组 | | 制作组/字幕组 |
release_group | 压制组/字幕组(别名) | | 等同于 releaseGroup |
resource_term | 资源规格 | | 资源类型 + 特效 + 分辨率 + 杜比视界类型 |
webSource | 流媒体平台 | | 如 HBO、Netflix |
audioCodec | 音频编码 | | 如 DDP5.1、TrueHD |
vote_average | 评分 | | TMDB/豆瓣评分,0 表示暂无 |
overview | 简介 | | 媒体简介 |
time_usage | 用时 | | 从开始识别到完成移动/复制的耗时 |
total_size | 体积 | | 格式化后的大小 |
file_count | 文件数 | | 本次整理涉及的文件数 |
tmdbid | TMDB ID | | TMDB ID(可能为空) |
imdbid | IMDB ID | | IMDB ID(可能为空) |
tvdbid | TVDB ID | | TVDB ID(剧集场景更常见,可能为空) |
doubanid | 豆瓣 ID | | 豆瓣 ID(可能为空) |
meta_weight_score | 洗版源总分 | | 源文件加权总分,如 84.0 |
meta_weight_dest_score | 洗版目标总分 | | 被比较目标的加权总分,如 55.5 |
meta_weight_delta | 洗版分差 | | 源总分减目标总分 |
meta_weight_rule | 洗版规则 | | 本次使用的洗版规则名 |
meta_weight_version | 洗版版本 | | 源命中并入库的版本名 |
meta_weight_note | 洗版说明 | | 如 替换掉 1 个旧版(1080p WEB-DL) |
meta_weight_deleted_count | 洗版删除数 | | 本次覆盖删除的目标文件数量 |
meta_weight_detail | 洗版评分明细 | | 逐组评分明细,供高级模板使用 |
TIP
洗版字段只在本次整理触发洗版判定(分数比较、覆盖删除)时才有值,模板里建议用 {% if meta_weight_score %} 包裹;release_group 与 releaseGroup 完全等价,写哪个都行。
默认模板片段
text
{
"title": "🎬 {{ title_year }} {{ season_episode if season_episode else '' }} 已入库\n\n",
"text": (
"✨ 质量:{{ resource_quality }}\n"
"{% if meta_weight_score %}"
"⚖️ 洗版:{{ meta_weight_score }} → {{ meta_weight_dest_score }}\n"
"{% endif %}"
"{% if meta_weight_note %}"
"📝 说明:{{ meta_weight_note }}\n"
"{% endif %}"
"{% if meta_weight_deleted_count %}"
"🗑 已删除其他版本:{{ meta_weight_deleted_count }} 个\n"
"{% endif %}"
"{% if vote_average %}"
"🌟 评分:{{ vote_average }}/10\n"
"{% endif %}"
"{% if media_type or category %}"
"🎞 类型:{{ media_type }} · 🏷 类别:{{ category }}\n"
"{% endif %}"
"📂 数量:{{ file_count }} 个 · 💾 大小:{{ total_size }}\n"
"{% if release_group %}"
"👥 小组:{{ release_group }}\n"
"{% endif %}"
"⚡ 用时:{{ time_usage if time_usage else '未知' }}\n\n"
"🔗 :[TMDB](https://www.themoviedb.org/{% if '电视' in media_type %}tv{% else %}movie{% endif %}/{{ tmdbid }}) · [豆瓣](https://m.douban.com/search/?query={{ title }}) · [IMDB](https://www.imdb.com/title/{{ imdbid }}) · [TVDB](https://thetvdb.com/?id={{ tvdbid }}&tab=series)\n"
"{% if overview %}\n"
"📝 简介:\n{{ overview | truncate(50, True, '...') }}"
"{% endif %}"
)
}渲染规则
failed/exists状态完全不渲染模板:走代码里的固定文案,标题为🎬 … 整理失败,正文固定包含质量、评分、类型/类别、数量/大小、小组、用时、失败原因等行。image渲染为空时,自动回退backdrop_path,再回退poster_path。link非空时,会在消息末尾追加一行[查看详情](link)。- 模板渲染失败时该条通知整体失败并记录日志;未知变量统一报「存在未支持字段:…」。
订阅通知
变量
| 变量 | 说明 |
|---|---|
name | 订阅名称 |
year | 年份 |
season_ep | 季集范围,如 S02 E01-03 |
mode_text | 策略中文名:分享追更 / 订阅转存 |
version | 版本信息组合(分辨率 · 特效 · 资源类型 · 视频编码 · 音频编码 · 制作组) |
ep_progress | 剧集进度文本 |
strm_result | STRM 生成结果摘要 |
strm_files | 本次生成的 STRM 文件列表 |
share_link | 本次使用的分享链接 |
done_eps | 本次完成的集数列表(整数) |
completed_episodes | 当前已完成集数列表 |
media_type | movie 或 tv |
resolution | 分辨率 |
effect | 特效 |
resource_type | 资源类型 |
release_group | 制作组 |
video_codec | 视频编码 |
audio_codec | 音频编码 |
sub | 当前订阅对象完整数据 |
selected | 本次选中的资源项 |
all_items | 本次处理的所有资源项 |
默认模板
text
🎬 订阅追更通知
📌 {{ name }}{% if year %} ({{ year }}){% endif %}
{% if season_ep %}📺 {{ season_ep }}
{% endif %}📋 {{ mode_text }}
{% if version %}🏷️ {{ version }}
{% endif %}{% if strm_result %}
📁 {{ strm_result }}
{% endif %}{% if ep_progress %}
📊 {{ ep_progress }}
{% endif %}渲染与回退
- 模板留空时直接使用代码内置的固定文案(与默认模板同形的
📌 / 📺 / 📋 / 🏷️ / 📁 / 📊几行)。 - 自定义模板渲染失败时会记录「Jinja2 模板渲染失败,使用默认模板」并回退到同一份固定文案,不会中断订阅任务。
- 前端提示原文:「支持 Jinja2 语法。转存和分享订阅共用这套模板,常用变量如下:」并列出
、、、(列表) (列表) (订阅对象)。 - 输入框 hint 原文:「支持 Jinja2 语法;常用变量:name、year、season_ep、mode_text、version、ep_progress、strm_result。」,placeholder 为「留空使用默认模板」,右上角提供「恢复默认模板」。
Emby 通知
事件映射
Webhook 事件名大小写不敏感,_、- 会先规整为 . 再匹配。支持的动作与全部别名:
| 动作 | Webhook 事件别名 |
|---|---|
| 新入库 | library.new、item.added、ItemAdded |
| 开始播放 | playback.start、media.play、PlaybackStart |
| 暂停播放 | playback.pause、media.pause、PlaybackPause |
| 恢复播放 | playback.unpause、media.resume、PlaybackUnpause、PlaybackResume |
| 停止播放 | playback.stop、media.stop、PlaybackStop |
| 登录成功 | user.authenticated、UserAuthenticated、UserLoggedIn |
| 登录失败 | user.authenticationfailed、UserAuthenticationFailed、UserLoginFailed、UserLockedOut |
| 标记了 | item.rate、ItemRated、item.markplayed、item.markunplayed、item.markfavorite、item.unmarkfavorite、ItemMarkedPlayed、ItemMarkedUnplayed、ItemMarkedFavorite、ItemUnmarkedFavorite |
未识别的事件不会走模板,而是直接发送一条「Emby 原始通知」并附带原始字段;开启调试或「记录原始字段到 log.log」后,未识别事件只写日志、不再推送 TG。
字段目录(30 项)
消息字段(18):
| key | label | snippet | 说明 |
|---|---|---|---|
action | 动作 | | 映射后的中文动作,如 新入库、开始播放、暂停播放、恢复播放、停止播放、登录失败 |
mark_status | 标记状态 | | 标记事件的具体状态,如 已观看、未观看、已收藏 |
action_detail | 动作详情 | | 当前动作的补充说明,标记事件等同于标记状态 |
media_type | 媒体类型 | | 媒体类型,如 电影、剧集、有声书 |
item_name | 媒体名称 | | 电影名或剧集季集名称 |
title_year | 标题年份 | | 媒体标题和年份,如 低智商犯罪 (2026) |
season_episode | 季集 | | 剧集季集,如 S01 E04-E05 |
user_name | 用户 | | 触发事件的 Emby 用户名 |
user_id | 用户 ID | | Webhook 中的 Emby 用户 ID |
client | 客户端 | | 播放或登录客户端名称 |
device_name | 设备 | | 播放或登录设备名称 |
ip_address | 来源 IP | | Webhook 中携带的远端 IP |
percentage | 播放进度 | | 播放百分比,优先读取 Emby 进度字段 |
vote_average | 评分 | | 媒体评分,通常来自 CommunityRating |
overview | 简介 | | 媒体简介 |
tmdbid | TMDB ID | | Item.ProviderIds.Tmdb |
imdbid | IMDb ID | | Item.ProviderIds.Imdb |
now_time | 操作时间 | | NanShare 收到 Webhook 的时间 |
扩展字段(12):
| key | label | snippet | 说明 |
|---|---|---|---|
event | 原始事件 | | Webhook 中的事件类型原值 |
item_id | Item ID | | Emby Item Id |
series_id | 剧集 ID | | Episode 所属 SeriesId |
season_id | 季 ID | | SeasonId 或 ParentId |
episode_id | 集 ID | | 集项目 ID |
begin_season | 起始季 | | 解析出的起始季号 |
begin_episode | 起始集 | | 解析出的起始集号 |
end_episode | 结束集 | | 解析出的结束集号,多集或非连续时存在 |
episode_list | 集数列表 | | 解析出的集号列表 |
server_name | 服务器名称 | | Webhook 中的 Emby 服务器名称 |
image_url | 图片地址 | | 自动用 Emby 助手和秒传播放配置生成的背景图或封面图地址 |
raw_json | 原始 JSON | | 解析后的 Webhook payload JSON 文本 |
保存校验白名单 = 以上 30 项 + cycler / dict / joiner / lipsum / namespace / range / true / false / none / None,并且 {% set %} / {% for %} 定义的自定义名会被跳过不报错(出厂默认模板就是这样写的)。结构键之外的键会报「通知模板存在未支持键:…」;无法按 title/text/image 解析时报「Emby 通知模板仅支持 JSON/YAML 对象格式,或兼容的 Python 字典字面量格式,并需使用 title/text/image 结构键(image 可留空)」。
默认模板
title 片段(按动作分派 emoji,再拼用户与媒体名):
text
{% if action == '新入库' and media_type == '电影' %}🎬 {% elif action == '新入库' and media_type == '剧集' %}📺 {% elif action == '新入库' and media_type == '有声书' %}📚 {% elif action == '新入库' %}🆕 {% elif action == '开始播放' %}▶️ {% elif action == '暂停播放' %}⏸️ {% elif action == '恢复播放' %}🔁 {% elif action == '停止播放' %}⏹️ {% elif action == '登录成功' %}✅ {% elif action == '登录失败' %}❌ {% elif action == '标记了' %}🏷️ {% endif %}{% if user_name %}【{{ user_name }}】{% endif %}{{ action }}{% if media_type %} {{media_type}} {% endif %}{{ item_name }}{% if mark_status %} {{ mark_status }}{% endif %}默认模板完整内容(含超长 text 片段,点开复制)
text
{
"title": "{% if action == '新入库' and media_type == '电影' %}🎬 {% elif action == '新入库' and media_type == '剧集' %}📺 {% elif action == '新入库' and media_type == '有声书' %}📚 {% elif action == '新入库' %}🆕 {% elif action == '开始播放' %}▶️ {% elif action == '暂停播放' %}⏸️ {% elif action == '恢复播放' %}🔁 {% elif action == '停止播放' %}⏹️ {% elif action == '登录成功' %}✅ {% elif action == '登录失败' %}❌ {% elif action == '标记了' %}🏷️ {% endif %}{% if user_name %}【{{ user_name }}】{% endif %}{{ action }}{% if media_type %} {{media_type}} {% endif %}{{ item_name }}{% if mark_status %} {{ mark_status }}{% endif %}",
"text":
"{% set va = vote_average|default(0)|float(0) %}"
"{% set mt_lower = media_type|default('', true)|lower %}"
"{% set action_lower = action|default('', true)|lower %}"
"{% set is_new_content = '新入库' in action %}"
"{% set is_playing = '开始播放' in action %}"
"{% set is_paused = '暂停播放' in action %}"
"{% set is_resumed = '恢复播放' in action %}"
"{% set is_stopped = '停止播放' in action %}"
"{% set is_login_success = '登录成功' in action %}"
"{% set is_login_failed = '登录失败' in action %}"
"{% set is_marked = '标记了' in action %}"
"{% set progress_num = percentage|default(0)|int(0) %}"
"{% set time_str = now_time|default('12:00:00') %}"
"{% if ' ' in time_str %}{% set time_part = time_str.split(' ')[1] %}{% else %}{% set time_part = time_str %}{% endif %}"
"{% set hour = time_part.split(':')[0]|int if ':' in time_part else 12 %}"
"{% set is_late_night = hour >= 23 or hour < 6 %}"
"{% set is_early_morning = hour >= 6 and hour < 9 %}"
"{% set is_morning = hour >= 9 and hour < 12 %}"
"{% set is_noon = hour >= 12 and hour < 14 %}"
"{% set is_afternoon = hour >= 14 and hour < 18 %}"
"{% set is_evening = hour >= 18 and hour < 23 %}"
"{% if vote_average %}⭐️ 评分:{{ vote_average }}/10\n{% endif %}"
"{% if media_type == '电影' %}🎦{% elif media_type == '剧集' %}📺{% elif media_type == '有声书' %}📚{% else %}📱{% endif %} 媒体类型:{{ media_type }}\n"
"{% if percentage %}🔄 播放进度:{{ percentage }}%\n{% endif %}"
"{% if ip_address %}🌐 来源IP:{{ ip_address }}\n{% endif %}"
"{% if device_name %}📱 播放设备:{{ client }} {{ device_name }}\n{% endif %}"
"{% if user_name and not is_new_content %}👤 用户:{{ user_name }}\n{% endif %}"
"{% if tmdbid %}🍿 TMDB ID:{{ tmdbid }}\n{% endif %}"
"{% if imdbid %}🌟 IMDB ID:{{ imdbid }}\n{% endif %}"
"🕒 操作时间:{{ now_time }}\n"
"{% if overview and is_new_content %}"
"\n📝 简介:{{ overview[:100] }}…\n"
"{% elif overview and not is_new_content %}\n📝 剧情:{{ overview }}{% endif %}"
"{% if tmdbid or imdbid or item_name %}"
"\n\n{% set has_links = false %}"
"{% if tmdbid %}🔗 [TMDB](https://www.themoviedb.org/{% if media_type == '电影' %}movie{% elif media_type == '剧集' %}tv{% else %}movie{% endif %}/{{ tmdbid }}){% set has_links = true %}{% endif %}"
"{% set douban_search_possible = imdbid or item_name %}"
"{% if has_links and douban_search_possible %} | {% endif %}"
"{% if douban_search_possible %}✳️ {% if imdbid %}[豆瓣](https://www.douban.com/search?cat=1002&q={{ imdbid }}){% else %}[豆瓣](https://www.douban.com/search?cat=1002&q={{ item_name | urlencode }}){% endif %}{% set has_links = true %}{% endif %}"
"{% if imdbid %}{% if has_links %} | {% endif %}🌟 [IMDb](https://www.imdb.com/title/{{ imdbid }}/){% set has_links = true %}{% endif %}"
"{% if has_links %}\n{% endif %}"
"{% endif %}"
"\n"
"{% if is_new_content %}"
"{% if va >= 9.8 %}👑🔥 这TM才是真·封神榜!跪着看就完事了,建议全体起立!"
"{% elif va >= 9.5 %}💥 卧槽这分是充了多少钱?但有一说一确实能打,建议申遗!"
"{% elif va >= 9.0 %}🤙 这波在大气层!年度装逼必备作品,不看不是社会人!"
"{% elif va >= 8.5 %}👍 这分对得起你熬的夜!建议搭配炸鸡啤酒食用更佳~"
"{% elif va >= 8.0 %}🍺 中规中矩电子榨菜,拉屎时看刚刚好!"
"{% elif va >= 7.5 %}🤏 也就图一乐,建议开2倍速别浪费生命!"
"{% elif va >= 7.0 %}🙄 这分是刷的吧?建议下载反诈APP!"
"{% elif va >= 6.0 %}😅 6分保底神作,建议配合弹幕护体食用!"
"{% elif va >= 5.0 %}💩 卧槽这答辩也敢入库?建议申报金扫帚奖!"
"{% elif va >= 4.0 %}🤮 导演是喝高了拍的?建议众筹给编剧寄刀片!"
"{% elif va > 0 %}👻 这分是来搞行为艺术的?建议配合降压药观看!"
"{% elif media_type == '电影' %}🎬 新片到货!是骡子是马拉出来溜溜~"
"{% elif media_type == '剧集' %}📺 新剧上架!建议备好瓜子板凳开喷!"
"{% else %}🆕 新货到仓!是惊喜还是惊吓你说了算!{% endif %}"
"{% elif is_playing %}"
"{% if va > 0 %}"
"{% if va >= 9.8 %}🤯 全体注意!前方高能!建议暂停呼吸!"
"{% elif va >= 9.5 %}🚨 这不是演习!请系好安全带准备起飞!"
"{% elif va >= 9.0 %}💥 前方名场面预警!建议录屏反复观看!"
"{% elif va >= 8.5 %}🎇 建议关闭手机!错过镜头血亏一个亿!"
"{% elif va >= 8.0 %}🍿 瓜子饮料准备好,这个能下三碗饭!"
"{% elif va >= 7.5 %}📱 建议开弹幕,没网友吐槽根本看不下去!"
"{% elif va >= 7.0 %}😑 就当听个响,建议边刷抖音边看!"
"{% elif va >= 6.0 %}🤏 建议2倍速观看,时间就是金钱!"
"{% elif va >= 5.0 %}💣 勇士拔剑吧!建议呼叫沙雕网友陪看!"
"{% elif va >= 4.0 %}⚠️ 非战斗人员请撤离!辣眼睛预警!"
"{% else %}☢️ 注意这不是演习!烂片核爆倒计时!"
"{% endif %}"
"{% else %}"
"{% if is_late_night %}"
"{% if progress_num <= 5 %}🌙 大半夜不睡觉是吧?年轻人就是肝!"
"{% elif progress_num <= 20 %}🌃 夜猫子认证!建议来包辣条提神!"
"{% elif progress_num <= 50 %}🌌 这是要决战到天亮?瑞思拜!"
"{% elif progress_num <= 80 %}🌅 看到这个点?明天上班等着摸鱼吧!"
"{% else %}😴 好家伙直接看到天亮?建议申遗!{% endif %}"
"{% else %}"
"{% if progress_num <= 5 %}🚀 刚开局就想跑?给导演个面子!"
"{% elif progress_num <= 20 %}👀 前戏还没完!精彩的在后面!"
"{% elif progress_num <= 50 %}🎢 过山车开到半山腰!坐稳了!"
"{% elif progress_num <= 80 %}🔥 高潮要来了!纸巾备好!"
"{% else %}🏁 最后冲刺!憋住别上厕所!{% endif %}"
"{% endif %}"
"{% endif %}"
"{% elif is_paused %}"
"{% if va > 0 %}"
"{% if va >= 9.8 %}⏸️ 暂停神片?建议立即续播否则浑身蚂蚁爬!"
"{% elif va >= 9.5 %}⏸️ 看到一半暂停?你是真能忍!"
"{% elif va >= 9.0 %}🚫 暂停装逼现场?建议立即恢复!"
"{% elif va >= 8.5 %}🛑 看到高潮刹车?兄弟你好狠!"
"{% elif va >= 8.0 %}⏸️ 暂停干饭?建议边吃边看!"
"{% elif va >= 7.5 %}⏹️ 暂时停一手也行,别回头忘了续上!"
"{% elif va >= 7.0 %}🛌 暂停得好!建议先换换脑子再回来!"
"{% elif va >= 6.0 %}⏸️ 明智选择!建议转战王者峡谷!"
"{% elif va >= 5.0 %}🗑️ 暂停得好!建议冷静三秒再决定!"
"{% elif va >= 4.0 %}💢 暂停看这玩意?建议立即洗眼睛!"
"{% else %}☠️ 暂停保命!建议领取抗烂片补助!"
"{% endif %}"
"{% else %}"
"{% if is_late_night %}"
"{% if progress_num >= 90 %}🌙 都快看完了还暂停?你是懂吊胃口的!"
"{% elif progress_num >= 70 %}😴 看到这程度先歇会儿也合理,梦里记得续上!"
"{% elif progress_num >= 30 %}🌃 暂停在这?你是懂吊胃口的!"
"{% else %}👋 才看这点就暂停?建议先别急着下结论!{% endif %}"
"{% else %}"
"{% if progress_num >= 90 %}🎉 都到最后了先歇口气,回来收尾刚刚好!"
"{% elif progress_num >= 70 %}🛋️ 看到这程度暂停,回头接着看也不亏!"
"{% elif progress_num >= 30 %}⏸️ 暂停在这?你是懂吊胃口的!"
"{% else %}👀 刚开场先暂停?后面说不定真有活!{% endif %}"
"{% endif %}"
"{% endif %}"
"{% elif is_resumed %}"
"{% if va > 0 %}"
"{% if va >= 9.8 %}🔁 神片续播成功!建议把其他事统统往后放!"
"{% elif va >= 9.5 %}🚀 高能继续!这会儿退出血亏!"
"{% elif va >= 9.0 %}💥 名场面重新开张,准备接招!"
"{% elif va >= 8.5 %}🎬 续上这个节奏,今天这波值回票价!"
"{% elif va >= 8.0 %}🍿 瓜子饮料重新就位,继续整!"
"{% elif va >= 7.5 %}📺 电子榨菜回锅加热,继续下饭!"
"{% elif va >= 7.0 %}😏 又点回来了?说明它多少还有点东西!"
"{% elif va >= 6.0 %}🔁 既然续播了,那就再给它一次机会!"
"{% elif va >= 5.0 %}🧯 勇士回归现场,继续顶着看吧!"
"{% elif va >= 4.0 %}⚠️ 这都能恢复播放?只能说你是真勇!"
"{% else %}☢️ 核爆现场重新启动,请系好安全带!"
"{% endif %}"
"{% else %}"
"{% if progress_num >= 80 %}🏁 续播冲线阶段,离收工就差最后一口气!"
"{% elif progress_num >= 40 %}🔁 接上进度继续看,故事这会儿才有味道!"
"{% elif progress_num >= 10 %}📼 断点续播成功,继续看看它能整出什么活!"
"{% else %}▶️ 刚暂停就恢复?这节奏主打一个无缝衔接!{% endif %}"
"{% endif %}"
"{% elif is_stopped %}"
"{% if progress_num >= 95 %}🏁 本次基本看完收工,起身活动两步刚刚好!"
"{% elif progress_num >= 70 %}📴 本次播放先结束,下次从这里接着看也不亏!"
"{% elif progress_num >= 30 %}🛑 今天先看到这,回头续上也还在节奏里!"
"{% else %}👋 刚开场就收工?下次再给它一次机会!{% endif %}"
"{% elif is_login_success %}"
"{% if is_late_night %}🌙 夜猫子登录!建议开启隐身模式!"
"{% elif is_early_morning %}🌅 早起的鸟儿被抓包!建议装睡!"
"{% else %}🎉 欢迎老铁!建议备好肥宅快乐水!{% endif %}"
"{% elif is_login_failed %}⚠️ 密码不对还想白嫖?建议充值智商!"
"{% elif is_marked %}🏷️ 标记成功!建议同步发朋友圈装逼!"
"{% else %}📱 系统诈尸!建议立即截图发贴吧!{% endif %}\n"
}TIP
默认模板文案很长,随时可以在工具箱 → Emby 通知里点「恢复默认」;image 留空时会自动使用 image_url。
图片与推送规则
image_type默认backdrop:优先背景图(ParentBackdropItemId→series_id→item_id),缺失时回退封面;改为primary则优先封面、再回退背景。登录事件会优先使用用户头像。- 图片下载失败且「下载失败使用内置 Emby 原图」开启时,改用内置图继续发送;再失败才退化为纯文本通知。
- 同一「媒体 + 客户端 + 用户」的停止播放事件在 600 秒内只会推送一次;同一对象的开始播放会重置这条去重记录。
- Webhook 地址:
/api/emby_notify/webhook?token=nanshare,完整示例为http://你的IP:8115/api/emby_notify/webhook?token=nanshare。
使用流程
- 在「设置 → Telegram Bot」配置并启用 Bot,勾选需要的通知场景。
- 文件整理通知:到整理配置页的基础配置填写整理 Telegram 通知模板,并开启整理通知。
- 订阅通知:到 [订阅设置](/subscribe/overview) 填写订阅通知模板,留空即用默认模板。
- Emby 通知:到工具箱 → Emby 通知填写模板,并在 Emby 配置 Webhook 地址。
- 用测试按钮或触发一次真实事件验证渲染效果。
常见问题
模板不渲染或报错
TIP
先确认变量名与模板类型是否匹配:整理/Emby 模板是 title/text/image 结构,订阅模板是纯文本。变量不存在时会渲染为空,建议用 or 或 |default(...) 提供兜底。
保存时报「存在未支持字段」
TIP
说明模板里用到了字段目录之外的变量。文件整理通知只认 29 个字段,且不支持 {% set %} 自定义变量(会被当作未支持字段),把计算内联写进 / {% if %} 即可;Emby 通知认 30 个字段,{% set %} / {% for %} 的自定义名不受影响。
整理失败时为什么没走我的模板
DANGER
failed / exists 状态刻意不渲染模板,统一走固定文案,保证失败原因格式稳定;只有成功整理(moved / copied / unchanged)才会使用自定义模板。
Emby 通知收不到
TIP
确认 Webhook 地址带 token 参数且与 Emby 通知配置一致、Emby 能访问 NanShare 的 8115 端口;未识别事件只会发「原始字段」通知,可在 Emby 通知页开启记录原始字段排查;同一媒体+客户端+用户的停止播放 600 秒内只发一次。