Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/common.py (Python)

Code before:
                    continue
                if idx == 0:
                    last_chapter = {
                        'start_time': part.get('startOffset') or 0,
                        'end_time': part.get('endOffset'),
                    }
                    last_chapter.update({'title': part['name']} if part.get('name') else {})
                else:
                    # Fix up the previous chapter using this chapter's data
                    last_chapter['end_time'] = last_chapter.get('end_time') or part.get('startOffset')
                    if None in last_chapter.values():
                        # If there's null values now, the chapter listing is broken somehow. Emit warning and bail.
                        self.report_warning(f'Chapter {last_chapter.get("title") or idx} contains broken data. Not extracting chapters.')
                        return
                    else:
                        chapters.append(last_chapter)
                    last_chapter = {
                        'start_time': part.get('startOffset') or last_chapter.get('end_time'),
                        'end_time': part.get('endOffset')
                    }
                    last_chapter.update({'title': part['name']} if part.get('name') else {})
            if chapters:
                last_chapter.update({} if last_chapter.get('end_time') else {'end_time': info['duration']})
                chapters.append(last_chapter)
                info['chapters'] = chapters

        def extract_video_object(e):
            assert e['@type'] == 'VideoObject'
            author = e.get('author')
            info.update({
                'url': url_or_none(e.get('contentUrl')),
                'title': unescapeHTML(e.get('name')),
                'description': unescapeHTML(e.get('description')),
                'thumbnail': url_or_none(e.get('thumbnailUrl') or e.get('thumbnailURL')),
                'duration': parse_duration(e.get('duration')),
                'timestamp': unified_timestamp(e.get('uploadDate')),
                # author can be an instance of 'Organization' or 'Person' types.
                # both types can have 'name' property(inherited from 'Thing' type). [1]
                # however some websites are using 'Text' type instead.
                # 1. https://schema.org/VideoObject
                'uploader': author.get('name') if isinstance(author, dict) else author if isinstance(author, compat_str) else None,
                'filesize': float_or_none(e.get('contentSize')),
                'tbr': int_or_none(e.get('bitrate')),
                'width': int_or_none(e.get('width')),
                'height': int_or_none(e.get('height')),
                'view_count': int_or_none(e.get('interactionCount')),
            })
            extract_interaction_statistic(e)
            extract_chapter_information(e)

        def traverse_json_ld(json_ld, at_top_level=True):

Review comment (bug):
why is this so complicated?
(untested)
```py
        def extract_chapter_information(e):
            chapters = [{
                'title': part.get('name'),
                'start_time': part.get('startOffset'),
                'end_time': part.get('endOffset'),
            } for part in (e.get('hasPart') or []) if part.get('@type') != 'Clip']
            if chapters and chapters[0]['start_time']:  # Chapters must always at 0
                chapters[:0] = [{'start_time': 0, 'end_time': None, 'title': '<Untitled>'}]
            for idx, (last_c, current_c, next_c) in enumerate(zip(
                    [{'end_time': 0}] + chapters, chapters, chapters[1:])):
                current_c['end_time'] = current_c['end_time'] or next_c['start_time']
                current_c['start_time'] = current_c['start_time'] or last_c['end_time']
                if None in current_c.values():
                    self.report_warning(f'Chapter {idx} contains broken data. Not extracting chapters')
                    return
            info['chapters'] = chapters
```

Code after:
            for idx, (last_c, current_c, next_c) in enumerate(zip(
                    [{'end_time': 0}] + chapters, chapters, chapters[1:])):
                current_c['end_time'] = current_c['end_time'] or next_c['start_time']
                current_c['start_time'] = current_c['start_time'] or last_c['end_time']
                if None in current_c.values():
                    self.report_warning(f'Chapter {idx} contains broken data. Not extracting chapters')
                    return
            if chapters:
                chapters[-1]['end_time'] = chapters[-1]['end_time'] or info['duration']
                info['chapters'] = chapters

        def extract_video_object(e):
            assert e['@type'] == 'VideoObject'
            author = e.get('author')
            info.update({
                'url': url_or_none(e.get('contentUrl')),
                'title': unescapeHTML(e.get('name')),
                'description': unescapeHTML(e.get('description')),
                'thumbnails': [{'url': url_or_none(url)}
                               for url in variadic(traverse_obj(e, 'thumbnailUrl', 'thumbnailURL'))],
                'duration': parse_duration(e.get('duration')),
                'timestamp': unified_timestamp(e.get('uploadDate')),
                # author can be an instance of 'Organization' or 'Person' types.
                # both types can have 'name' property(inherited from 'Thing' type). [1]
                # however some websites are using 'Text' type instead.
                # 1. https://schema.org/VideoObject
                'uploader': author.get('name') if isinstance(author, dict) else author if isinstance(author, compat_str) else None,
                'filesize': float_or_none(e.get('contentSize')),
                'tbr': int_or_none(e.get('bitrate')),
                'width': int_or_none(e.get('width')),
                'height': int_or_none(e.get('height')),
                'view_count': int_or_none(e.get('interactionCount')),
            })
            extract_interaction_statistic(e)
            extract_chapter_information(e)

        def traverse_json_ld(json_ld, at_top_level=True):
            for e in json_ld:
                if at_top_level and '@context' not in e:
                    continue
                if at_top_level and set(e.keys()) == {'@context', '@graph'}:
                    traverse_json_ld(variadic(e['@graph'], allowed_types=(dict,)), at_top_level=False)
                    break
                item_type = e.get('@type')
                if expected_type is not None and expected_type != item_type:
                    continue
                rating = traverse_obj(e, ('aggregateRating', 'ratingValue'), expected_type=float_or_none)
                if rating is not None:
                    info['average_rating'] = rating
                if item_type in ('TVEpisode', 'Episode'):
                    episode_name = unescapeHTML(e.get('name'))