Skip to content

Instantly share code, notes, and snippets.

@0xallie
Last active April 4, 2024 21:14
Show Gist options
  • Save 0xallie/5475a077d600c75060bffac6a84f7423 to your computer and use it in GitHub Desktop.
Save 0xallie/5475a077d600c75060bffac6a84f7423 to your computer and use it in GitHub Desktop.
Code to get chapters from Disney+ (for BAMSDK v7.3.0)
def get_chapters(self, title):
milestones = []
for type_, type_milestones in title.service_data.get("milestone").items():
for milestone in type_milestones:
milestones.append((type_, milestone))
if not milestones:
return []
types = {
# https://github.com/IMFTool/IMFTool/blob/master/src/ImfCommon.cpp#L458-L504
"FFER": "Recap", # recap_start
"LFER": "Scene {i}", # recap_end
"FFEI": "Intro", # intro_start
"FFTC": "Intro", # First Frame of Title Credits. This may be after the actual episode has already started.
"LFEI": "Scene {i}", # intro_end
"LFTC": "Scene {i}", # Last Frame of Title Credits (see above).
"FPCI": "Scene {i}", # Placeholder for commercial breaks
"FFEC": "Credits",
# TODO: Are these used for anything else?
"tag_start": "Post-Credits Scene {j}",
# This is when the player shrinks and the next episode countdown starts.
# May be later than the actual start of credits.
"up_next": "Credits",
}
alternate_types = {
("FFER",): "recap_start",
("LFER",): "recap_end",
("FFEI",): "intro_start",
("FFEI", "intro_start"): "FFTC", # Not an exact match
("LFEI",): "intro_end",
("LFEI", "intro_end"): "LFTC", # Not an exact match
("FFEC",): "up_next", # Not an exact match
}
ignore_types = [
"FFOC", # First Frame of Composition. Seems to include intro and maybe recap too.
"LFEC", # End of Credits
"tag_end", # End of Post-Credits Scene
"LFOC", # Last Frame of Composition. Seems to include credits.
]
chapters = []
scene = 0
post_credit = 0
num_post_credit = len([1 for t, _ in milestones if t == "tag_start"])
for main_types, alternate in alternate_types.items():
if any(type_ in main_types for type_, _ in milestones):
ignore_types.append(alternate)
for type_, milestone in sorted(milestones, key=lambda tm: tm[1]["milestoneTime"][0]["startMillis"]):
if type_ in ignore_types:
continue
if not (name := types.get(type_)):
self.log.warning(f"Skipping unknown chapter type {type_}")
continue
ms = int(milestone["milestoneTime"][0]["startMillis"])
if "{i}" in name:
scene += 1
if "{j}" in name:
post_credit += 1
chapter = MenuTrack(
number=len(chapters) + 1,
title=name.format(i=scene, j=post_credit if num_post_credit > 1 else "").strip(),
timecode=ms / 1000,
)
chapter.note = type_
chapters.append(chapter)
return chapters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment