效率工具

Disney+ 离线下载提醒:检查 30 天联网规则和设备上限

Disney+ 的离线下载有两条容易忘的规则:设备至少每 30 天要联网一次,下载最多只能放在 10 台移动设备上。这个小工具读取你自己记录的设备清单,列出快到期或已经过期的设备,再生成一个 .ics 日历文件,导入手机日历后,会在截止前 5 天提醒你让设备连一次网。

BINBHO · 图文教程

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