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