Disney+ 的离线下载有两条容易忘的规则:设备至少每 30 天要联网一次,下载最多只能放在 10 台移动设备上。这个小工具读取你自己记录的设备清单,列出快到期或已经过期的设备,再生成一个
.ics日历文件,导入手机日历后,会在截止前 5 天提醒你让设备连一次网。
先看清成品
pytest 跑了 2 个测试,全部通过;用示例清单(3 台设备,今天按 2026-10-21 算)运行,输出“孩子的 iPad:3 天内需要联网一次”和“旧平板:已经 11 天超过 30 天没有联网”,并生成了 2 条提醒;生成的 .ics 用 Python 的
icalendar库解析通过。没有在真实手机日历里导入测试过,也没有用真实的 Disney+ 设备核对过过期行为。规则来自帮助中心页面(2026-10-21 查看)。
注意:Disney+ 没有面向个人开发者的公开 API。本系列只用官方帮助中心公布的数字和规则,以及你自己填写的数据。不要用 VPN 或代理切换地区,不要绕过 DRM、不要录屏或导出下载文件,不要在家庭以外共享账号(以订阅协议为准)。套餐价格和可用地区以官方页面为准,本文不列数字。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
downloads_check.py |
检查 30 天联网规则和 10 台设备上限,生成 .ics 提醒 |
devices.json |
你手动记录的设备、最后联网日期和下载的内容 |
reminders.ics |
可以导入 iOS、Android 或电脑日历 |
需要准备哪些环境?
| 项目 | 要求 | 说明 |
|---|---|---|
| Python | 3.8 以上 | 只用标准库(测试用 pytest) |
| 日历 App | 支持导入 .ics | 系统自带的日历即可 |
官方规则(帮助中心 “Downloads”)
| 规则 | 页面写法要点 |
|---|---|
| 设备数 | 最多可以在 10 台支持的移动设备上下载 |
| 联网 | 保持订阅有效,并且设备至少每 30 天联网一次,下载才会保留 |
| 会被删除的情况 | 退出登录会删除下载;部分内容只能下载有限的时间,或者不能下载 |
| 画质 | Standard / Medium / High,画质越高,占用空间越大 |
Disney+ 不提供查询“哪台设备何时联网”的接口,所以 last_online 需要你自己记录,比如每次打开 App 时顺手改一下。
文件结构
34-disney-plus-B-src/
├── downloads_check.py
├── devices.json
└── test_downloads.py
第 1 步:脚本
downloads_check.py:
"""Disney+ 离线下载提醒。规则来自帮助中心 “Downloads on Disney+”(2026-10-21 抄录):
- 下载内容在你保持订阅、并且设备每 30 天至少联网一次的情况下保留;
- 最多可以在 10 台支持的移动设备上下载;
- 退出登录或卸载 App 会删除下载;部分内容只能保留有限时间。
脚本读你自己维护的 devices.json(Disney+ 不提供查询接口),列出风险,并生成 .ics 日历提醒。
用法:python downloads_check.py devices.json [今天YYYY-MM-DD]"""
import json, sys
from datetime import date, datetime, timedelta, timezone
RECONNECT_DAYS, MAX_DEVICES, WARN_BEFORE = 30, 10, 5
def check(devices, today):
issues, reminders = [], []
if len(devices) > MAX_DEVICES:
issues.append(f"登记了 {len(devices)} 台下载设备,官方上限是 {MAX_DEVICES} 台")
for d in devices:
last = date.fromisoformat(d["last_online"])
due = last + timedelta(days=RECONNECT_DAYS)
left = (due - today).days
if left < 0: issues.append(f"{d['name']}:已经 {-left} 天超过 30 天没有联网,离线内容可能已经无法播放")
elif left <= WARN_BEFORE: issues.append(f"{d['name']}:{left} 天内需要联网一次(截止 {due})")
if d.get("downloads") and left >= 0: # 已过期的不再提醒,提醒日不早于今天
reminders.append((d["name"], max(today, due - timedelta(days=WARN_BEFORE))))
return issues, reminders
def ics(reminders, now=None):
now = (now or datetime.now(timezone.utc)).strftime("%Y%m%dT%H%M%SZ")
ev = []
for name, day in reminders:
ev += ["BEGIN:VEVENT", f"UID:dplus-{name}-{day.isoformat()}@local", f"DTSTAMP:{now}",
f"DTSTART;VALUE=DATE:{day:%Y%m%d}", f"DTEND;VALUE=DATE:{day + timedelta(days=1):%Y%m%d}",
f"SUMMARY:让「{name}」连一次网(Disney+ 离线下载 30 天规则)", "END:VEVENT"]
return "\r\n".join(["BEGIN:VCALENDAR", "VERSION:2.0", "PRODID:-//binbho//dplus-downloads//ZH", *ev, "END:VCALENDAR"]) + "\r\n"
if __name__ == "__main__":
devs = json.load(open(sys.argv[1], encoding="utf-8"))
today = date.fromisoformat(sys.argv[2]) if len(sys.argv) > 2 else date.today()
issues, rem = check(devs, today)
print("\n".join(issues) if issues else "没有发现风险")
open("reminders.ics", "w", encoding="utf-8", newline="").write(ics(rem))
print(f"已生成 reminders.ics({len(rem)} 条提醒)")
devices.json:
[
{"name": "孩子的 iPad", "last_online": "2026-09-24", "downloads": ["示例动画 第 1–3 集"]},
{"name": "我的手机", "last_online": "2026-10-20", "downloads": ["示例电影"]},
{"name": "旧平板", "last_online": "2026-09-10", "downloads": []}
]
第 2 步:测试和运行
from datetime import date, datetime, timezone
from downloads_check import check, ics
def test_rules():
devs = [{"name": "A", "last_online": "2026-09-24", "downloads": ["x"]},
{"name": "B", "last_online": "2026-09-01", "downloads": []}] + \
[{"name": f"D{i}", "last_online": "2026-10-20"} for i in range(9)]
issues, rem = check(devs, date(2026, 10, 21))
assert any("11 台" in i for i in issues)
assert any(i.startswith("A:3 天内") for i in issues) and any(i.startswith("B:已经 20 天") for i in issues)
assert rem == [("A", date(2026, 10, 21))] # 提醒日不能早于今天
def test_ics_format():
s = ics([("A", date(2026, 10, 19))], now=datetime(2026, 10, 21, tzinfo=timezone.utc))
assert s.startswith("BEGIN:VCALENDAR\r\n") and "DTSTART;VALUE=DATE:20261019" in s and s.endswith("END:VCALENDAR\r\n")
$ python -m pytest -q
2 passed
$ python downloads_check.py devices.json 2026-10-21
孩子的 iPad:3 天内需要联网一次(截止 2026-10-24)
旧平板:已经 11 天超过 30 天没有联网,离线内容可能已经无法播放
已生成 reminders.ics(2 条提醒)
检查点:旧平板上没有下载内容,所以不会生成提醒;我的手机的提醒日期是 2026-11-14,也就是截止日期 11-19 往前 5 天。用 icalendar 解析,可以读出两条事件,日期分别是 2026-10-21 和 2026-11-14。
调试示例:提醒日期在过去
第一版直接用“截止日期 − 5 天”作为提醒日。孩子的 iPad 截止日期是 10-24,提醒日就算成了 2026-10-19,比今天还早,导入日历后不会弹出任何通知。修改方法:提醒日取 max(今天, 截止 − 5 天);已经过期的设备不再生成提醒,只在终端里报出来。测试里的期望值也相应改成了 10-21。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 导入后看不到提醒 | 全天事件默认不弹通知 | 在日历 App 里给事件加通知 |
| 重新运行后提醒重复了 | 截止日期变了,UID 也跟着变 | 先删掉旧日历再导入 |
| 下载在 30 天内就不见了 | 退出了登录、内容授权到期,或者订阅失效 | 按帮助页面的说明重新下载 |
模板规则
- 只提醒你按规则使用,不要尝试延长授权或导出下载文件。
- 设备清单只记录自己家里的设备。
- 日期计算要写测试,特别是边界情况。
项目资源
| 用途 | 地址 |
|---|---|
| 离线下载规则 | https://help.disneyplus.com/article/disneyplus-downloads |
| iCalendar 规范 RFC 5545 | https://www.rfc-editor.org/rfc/rfc5545 |
| icalendar(用于验证) | https://pypi.org/project/icalendar/ |
| Disney+ 订阅协议 | https://www.disneyplus.com/legal/subscriber-agreement |