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

Code before:
                'id': 'qKfxu6fEpu',
                'ext': 'aac',
                'release_date': '20231129',
                'release_timestamp': 1701249480,
                'title': 'Roland TD-7 Demo.m4a',
                'channel_id': '64424fe73cd58cbcf8d8e131',
                'availability': 'subscriber_only',
                'uploader': 'The Trash Network',
                'duration': 114,
                'channel': 'The Drum Thing',
                'uploader_id': '61bc20c9a131fb692bf2a513',
                'channel_url': 'https://www.floatplane.com/channel/TheTrashNetwork/home/thedrumthing',
                'uploader_url': 'https://www.floatplane.com/channel/TheTrashNetwork/home',
            },
        }],
        'skip': 'requires subscription: "The Trash Network"',
        'params': {'skip_download': 'm3u8'},
    }]

    def _real_initialize(self):
        if not self._get_cookies(self._BASE_URL).get('sails.sid'):
            self.raise_login_required()


class FloatplaneChannelBaseIE(InfoExtractor):
    """Subclasses must set _ENTRY_IE_TYPE, _BASE_URL and _PAGE_SIZE"""

    def _fetch_page(self, display_id, creator_id, channel_id, page):
        query = {
            'id': creator_id,
            'limit': self._PAGE_SIZE,
            'fetchAfter': page * self._PAGE_SIZE,
        }
        if channel_id:
            query['channel'] = channel_id
        page_data = self._download_json(
            f'{self._BASE_URL}/api/v3/content/creator', display_id,
            query=query, note=f'Downloading page {page + 1}')
        for post in page_data or []:
            yield self.url_result(
                f'{self._BASE_URL}/post/{post["id"]}',
                self._ENTRY_IE_TYPE, id=post['id'], title=post.get('title'),
                release_timestamp=parse_iso8601(post.get('releaseDate')))

    def _real_extract(self, url):
        creator, channel = self._match_valid_url(url).group('id', 'channel')
        display_id = join_nonempty(creator, channel, delim='/')

        creator_data = self._download_json(
            f'{self._BASE_URL}/api/v3/creator/named',
            display_id, query={'creatorURL[0]': creator})[0]

Review comment (suggestion):
```suggestion
    """Subclasses must set _RESULT_IE, _BASE_URL and _PAGE_SIZE"""
```

Code after:
                'id': 'qKfxu6fEpu',
                'ext': 'aac',
                'release_date': '20231129',
                'release_timestamp': 1701249480,
                'title': 'Roland TD-7 Demo.m4a',
                'channel_id': '64424fe73cd58cbcf8d8e131',
                'availability': 'subscriber_only',
                'uploader': 'The Trash Network',
                'duration': 114,
                'channel': 'The Drum Thing',
                'uploader_id': '61bc20c9a131fb692bf2a513',
                'channel_url': 'https://www.floatplane.com/channel/TheTrashNetwork/home/thedrumthing',
                'uploader_url': 'https://www.floatplane.com/channel/TheTrashNetwork/home',
            },
        }],
        'skip': 'requires subscription: "The Trash Network"',
        'params': {'skip_download': 'm3u8'},
    }]

    def _real_initialize(self):
        if not self._get_cookies(self._BASE_URL).get('sails.sid'):
            self.raise_login_required()


class FloatplaneChannelBaseIE(InfoExtractor):
    """Subclasses must set _RESULT_IE, _BASE_URL and _PAGE_SIZE"""

    def _fetch_page(self, display_id, creator_id, channel_id, page):
        query = {
            'id': creator_id,
            'limit': self._PAGE_SIZE,
            'fetchAfter': page * self._PAGE_SIZE,
        }
        if channel_id:
            query['channel'] = channel_id
        page_data = self._download_json(
            f'{self._BASE_URL}/api/v3/content/creator', display_id,
            query=query, note=f'Downloading page {page + 1}')
        for post in page_data or []:
            yield self.url_result(
                f'{self._BASE_URL}/post/{post["id"]}',
                self._RESULT_IE, id=post['id'], title=post.get('title'),
                release_timestamp=parse_iso8601(post.get('releaseDate')))

    def _real_extract(self, url):
        creator, channel = self._match_valid_url(url).group('id', 'channel')
        display_id = join_nonempty(creator, channel, delim='/')

        creator_data = self._download_json(
            f'{self._BASE_URL}/api/v3/creator/named',
            display_id, query={'creatorURL[0]': creator})[0]