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

Code before:
from ..networking.exceptions import HTTPError
from ..utils.traversal import traverse_obj
from ..utils import (
    ass_subtitles_timecode,
    bytes_to_intlist,
    bytes_to_long,
    ExtractorError,
    float_or_none,
    int_or_none,
    intlist_to_bytes,
    long_to_bytes,
    pkcs1pad,
    strip_or_none,
    str_or_none,
    try_get,
    unified_strdate,
    urlencode_postdata,
)


class ADNBaseIE(InfoExtractor):
    IE_DESC = 'Animation Digital Network'
    _NETRC_MACHINE = 'animationdigitalnetwork'
    _BASE = 'animationdigitalnetwork.fr'
    _API_BASE_URL = 'https://gw.api.' + _BASE + '/'
    _PLAYER_BASE_URL = _API_BASE_URL + 'player/'
    _HEADERS = {}
    _LOGIN_ERR_MESSAGE = 'Unable to log in'
    _RSA_KEY = (0x9B42B08905199A5CCE2026274399CA560ECB209EE9878A708B1C0812E1BB8CB5D1FB7441861147C1A1F2F3A0476DD63A9CAC20D3E983613346850AA6CB38F16DC7D720FD7D86FC6E5B3D5BBC72E14CD0BF9E869F2CEA2CCAD648F1DCE38F1FF916CEFB2D339B64AA0264372344BC775E265E8A852F88144AB0BD9AA06C1A4ABB, 65537)
    _POS_ALIGN_MAP = {
        'start': 1,
        'end': 3,
    }
    _LINE_ALIGN_MAP = {
        'middle': 8,
        'end': 4,
    }


class ADNIE(ADNBaseIE):
    _VALID_URL = r'https?://(?:www\.)?(?:animation|anime)digitalnetwork\.(?P<lang>fr|de)/video/[^/]+/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://animationdigitalnetwork.fr/video/fruits-basket/9841-episode-1-a-ce-soir',
        'md5': '1c9ef066ceb302c86f80c2b371615261',
        'info_dict': {
            'id': '9841',
            'ext': 'mp4',
            'title': 'Fruits Basket - Episode 1',
            'description': 'md5:14be2f72c3c96809b0ca424b0097d336',
            'series': 'Fruits Basket',
            'duration': 1437,

Review comment (suggestion):
```suggestion
    _API_BASE_URL = f'https://gw.api.{_BASE}/'
    _PLAYER_BASE_URL = f'{_API_BASE_URL}player/'
```

Code after:
from ..networking.exceptions import HTTPError
from ..utils import (
    ass_subtitles_timecode,
    bytes_to_intlist,
    bytes_to_long,
    ExtractorError,
    float_or_none,
    int_or_none,
    intlist_to_bytes,
    long_to_bytes,
    pkcs1pad,
    strip_or_none,
    str_or_none,
    try_get,
    unified_strdate,
    urlencode_postdata,
)
from ..utils.traversal import traverse_obj


class ADNBaseIE(InfoExtractor):
    IE_DESC = 'Animation Digital Network'
    _NETRC_MACHINE = 'animationdigitalnetwork'
    _BASE = 'animationdigitalnetwork.fr'
    _API_BASE_URL = f'https://gw.api.{_BASE}/'
    _PLAYER_BASE_URL = f'{_API_BASE_URL}player/'
    _HEADERS = {}
    _LOGIN_ERR_MESSAGE = 'Unable to log in'
    _RSA_KEY = (0x9B42B08905199A5CCE2026274399CA560ECB209EE9878A708B1C0812E1BB8CB5D1FB7441861147C1A1F2F3A0476DD63A9CAC20D3E983613346850AA6CB38F16DC7D720FD7D86FC6E5B3D5BBC72E14CD0BF9E869F2CEA2CCAD648F1DCE38F1FF916CEFB2D339B64AA0264372344BC775E265E8A852F88144AB0BD9AA06C1A4ABB, 65537)
    _POS_ALIGN_MAP = {
        'start': 1,
        'end': 3,
    }
    _LINE_ALIGN_MAP = {
        'middle': 8,
        'end': 4,
    }


class ADNIE(ADNBaseIE):
    _VALID_URL = r'https?://(?:www\.)?(?:animation|anime)digitalnetwork\.(?P<lang>fr|de)/video/[^/?#]+/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://animationdigitalnetwork.fr/video/fruits-basket/9841-episode-1-a-ce-soir',
        'md5': '1c9ef066ceb302c86f80c2b371615261',
        'info_dict': {
            'id': '9841',
            'ext': 'mp4',
            'title': 'Fruits Basket - Episode 1',
            'description': 'md5:14be2f72c3c96809b0ca424b0097d336',
            'series': 'Fruits Basket',
            'duration': 1437,