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

Code before:
            'like_count': int,
            'dislike_count': int,
            'comment_count': int,
            'uploader': 'LinusTechTips',
            'uploader_id': '59f94c0bdd241b70349eb72b',
            'uploader_url': 'https://www.floatplane.com/channel/linustechtips/home',
            'channel': 'GameLinked',
            'channel_id': '649dbade3540dbc3945eeda7',
            'channel_url': 'https://www.floatplane.com/channel/linustechtips/home/gamelinked',
            'availability': 'subscriber_only',
        },
        'params': {'skip_download': 'm3u8'},
    }]

    def _real_initialize(self):
        if not self._get_cookies('https://www.floatplane.com').get('sails.sid'):
            self.raise_login_required()

    def _real_extract(self, url):
        post_id = self._match_id(url)

        post_data = self._download_json(
            'https://www.floatplane.com/api/v3/content/post', post_id, query={'id': post_id},
            note='Downloading post data', errnote='Unable to download post data')

        if True not in traverse_obj(post_data, ('metadata', ('hasVideo', 'hasAudio'))):
            raise ExtractorError('Post does not contain a video or audio track', expected=True)

        items = []
        for media in traverse_obj(post_data, (('videoAttachments', 'audioAttachments'), ...)):
            media_id = media['id']
            media_typ = media.get('type') or 'video'

            metadata = self._download_json(
                f'https://www.floatplane.com/api/v3/content/{media_typ}', media_id, query={'id': media_id},
                note=f'Downloading {media_typ} metadata')

            stream = self._download_json(
                'https://www.floatplane.com/api/v2/cdn/delivery', media_id,
                query={
                    'type': 'vod' if media_typ == 'video' else 'aod',
                    'guid': metadata['guid']
                },
                note=f'Downloading {media_typ} stream data')

            def format_path(params):
                path = traverse_obj(stream, ('resource', 'uri', {str}))
                for i, val in (params or {}).items():
                    path = path.replace(f'{{qualityLevelParams.{i}}}', val)
                return path


Review comment (suggestion):
```suggestion
        if not any(traverse_obj(post_data, ('metadata', ('hasVideo', 'hasAudio')))):
```

Code after:
            'description': 'md5:80d612dcabf41b17487afcbe303ec57d',
            'thumbnail': r're:^https?://.*\.jpe?g$',
            'release_timestamp': 1700622000,
            'release_date': '20231122',
            'duration': 513,
            'like_count': int,
            'dislike_count': int,
            'comment_count': int,
            'uploader': 'LinusTechTips',
            'uploader_id': '59f94c0bdd241b70349eb72b',
            'uploader_url': 'https://www.floatplane.com/channel/linustechtips/home',
            'channel': 'GameLinked',
            'channel_id': '649dbade3540dbc3945eeda7',
            'channel_url': 'https://www.floatplane.com/channel/linustechtips/home/gamelinked',
            'availability': 'subscriber_only',
        },
        'params': {'skip_download': 'm3u8'},
    }]

    def _real_initialize(self):
        if not self._get_cookies('https://www.floatplane.com').get('sails.sid'):
            self.raise_login_required()

    def _real_extract(self, url):
        post_id = self._match_id(url)

        post_data = self._download_json(
            'https://www.floatplane.com/api/v3/content/post', post_id, query={'id': post_id},
            note='Downloading post data', errnote='Unable to download post data')

        if not any(traverse_obj(post_data, ('metadata', ('hasVideo', 'hasAudio')))):
            raise ExtractorError('Post does not contain a video or audio track', expected=True)

        items = []
        for media in traverse_obj(post_data, (('videoAttachments', 'audioAttachments'), ...)):
            media_id = media['id']
            media_typ = media.get('type') or 'video'

            metadata = self._download_json(
                f'https://www.floatplane.com/api/v3/content/{media_typ}', media_id, query={'id': media_id},
                note=f'Downloading {media_typ} metadata')

            stream = self._download_json(
                'https://www.floatplane.com/api/v2/cdn/delivery', media_id, query={
                    'type': 'vod' if media_typ == 'video' else 'aod',
                    'guid': metadata['guid']
                }, note=f'Downloading {media_typ} stream data')

            path_template = traverse_obj(stream, ('resource', 'uri', {str}))

            def format_path(params):