We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ADN] Can't download premium video after extractor fix
<!--

######################################################################
  WARNING!
  IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
######################################################################

-->


## Checklist

<!--
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
- Look through the README (http://yt-dl.org/readme) and FAQ (http://yt-dl.org/faq) for similar questions
- Search the bugtracker for similar questions: http://yt-dl.org/search-issues
- Finally, put x into all relevant boxes (like this [x])
-->

- [x] I'm asking a question
- [x] I've looked through the README and FAQ for similar questions
- [x] I've searched the bugtracker for similar questions including closed ones


## Question

<!--
Ask your question in an arbitrary form. Please make sure it's worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient.
-->

First of all I would like to thank the person who fixed the ADN extractor.

Everything works perfectly to download the free videos in 480p. But to download videos requiring a premium account or to download better quality I can't do it.

Before I used to use the command :

`youtube-dl.exe --cookies ./cookies.txt https://animedigitalnetwork.fr/video/my-hero-academia-saison-4/10271-episode-2-overhaul`

And it worked perfectly but after the extractor fix it doesn't work anymore I tried to re-download the cookies as indicated in the doc but it doesn't change anything I also tried to use `--username` and `--password` but it doesn't do anything either and it shows me in the both case:

`ERROR: This video is only available for paying users`

I think the problem comes from the cookies that don't have the same shape as before, there were 4 lines in my file but now there is only one.

Could someone help me?

Thank you.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `youtube_dl/extractor/adn.py`
Content:
```
1 # coding: utf-8
2 from __future__ import unicode_literals
3 
4 import base64
5 import binascii
6 import json
7 import os
8 import random
9 
10 from .common import InfoExtractor
11 from ..aes import aes_cbc_decrypt
12 from ..compat import (
13     compat_HTTPError,
14     compat_b64decode,
15     compat_ord,
16 )
17 from ..utils import (
18     bytes_to_intlist,
19     bytes_to_long,
20     ExtractorError,
21     float_or_none,
22     int_or_none,
23     intlist_to_bytes,
24     long_to_bytes,
25     pkcs1pad,
26     strip_or_none,
27     try_get,
28     unified_strdate,
29 )
30 
31 
32 class ADNIE(InfoExtractor):
33     IE_DESC = 'Anime Digital Network'
34     _VALID_URL = r'https?://(?:www\.)?animedigitalnetwork\.fr/video/[^/]+/(?P<id>\d+)'
35     _TEST = {
36         'url': 'http://animedigitalnetwork.fr/video/blue-exorcist-kyoto-saga/7778-episode-1-debut-des-hostilites',
37         'md5': '0319c99885ff5547565cacb4f3f9348d',
38         'info_dict': {
39             'id': '7778',
40             'ext': 'mp4',
41             'title': 'Blue Exorcist - Kyôto Saga - Episode 1',
42             'description': 'md5:2f7b5aa76edbc1a7a92cedcda8a528d5',
43             'series': 'Blue Exorcist - Kyôto Saga',
44             'duration': 1467,
45             'release_date': '20170106',
46             'comment_count': int,
47             'average_rating': float,
48             'season_number': 2,
49             'episode': 'Début des hostilités',
50             'episode_number': 1,
51         }
52     }
53 
54     _BASE_URL = 'http://animedigitalnetwork.fr'
55     _API_BASE_URL = 'https://gw.api.animedigitalnetwork.fr/'
56     _PLAYER_BASE_URL = _API_BASE_URL + 'player/'
57     _RSA_KEY = (0x9B42B08905199A5CCE2026274399CA560ECB209EE9878A708B1C0812E1BB8CB5D1FB7441861147C1A1F2F3A0476DD63A9CAC20D3E983613346850AA6CB38F16DC7D720FD7D86FC6E5B3D5BBC72E14CD0BF9E869F2CEA2CCAD648F1DCE38F1FF916CEFB2D339B64AA0264372344BC775E265E8A852F88144AB0BD9AA06C1A4ABB, 65537)
58     _POS_ALIGN_MAP = {
59         'start': 1,
60         'end': 3,
61     }
62     _LINE_ALIGN_MAP = {
63         'middle': 8,
64         'end': 4,
65     }
66 
67     @staticmethod
68     def _ass_subtitles_timecode(seconds):
69         return '%01d:%02d:%02d.%02d' % (seconds / 3600, (seconds % 3600) / 60, seconds % 60, (seconds % 1) * 100)
70 
71     def _get_subtitles(self, sub_url, video_id):
72         if not sub_url:
73             return None
74 
75         enc_subtitles = self._download_webpage(
76             sub_url, video_id, 'Downloading subtitles location', fatal=False) or '{}'
77         subtitle_location = (self._parse_json(enc_subtitles, video_id, fatal=False) or {}).get('location')
78         if subtitle_location:
79             enc_subtitles = self._download_webpage(
80                 subtitle_location, video_id, 'Downloading subtitles data',
81                 fatal=False, headers={'Origin': 'https://animedigitalnetwork.fr'})
82         if not enc_subtitles:
83             return None
84 
85         # http://animedigitalnetwork.fr/components/com_vodvideo/videojs/adn-vjs.min.js
86         dec_subtitles = intlist_to_bytes(aes_cbc_decrypt(
87             bytes_to_intlist(compat_b64decode(enc_subtitles[24:])),
88             bytes_to_intlist(binascii.unhexlify(self._K + 'ab9f52f5baae7c72')),
89             bytes_to_intlist(compat_b64decode(enc_subtitles[:24]))
90         ))
91         subtitles_json = self._parse_json(
92             dec_subtitles[:-compat_ord(dec_subtitles[-1])].decode(),
93             None, fatal=False)
94         if not subtitles_json:
95             return None
96 
97         subtitles = {}
98         for sub_lang, sub in subtitles_json.items():
99             ssa = '''[Script Info]
100 ScriptType:V4.00
101 [V4 Styles]
102 Format: Name,Fontname,Fontsize,PrimaryColour,SecondaryColour,TertiaryColour,BackColour,Bold,Italic,BorderStyle,Outline,Shadow,Alignment,MarginL,MarginR,MarginV,AlphaLevel,Encoding
103 Style: Default,Arial,18,16777215,16777215,16777215,0,-1,0,1,1,0,2,20,20,20,0,0
104 [Events]
105 Format: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text'''
106             for current in sub:
107                 start, end, text, line_align, position_align = (
108                     float_or_none(current.get('startTime')),
109                     float_or_none(current.get('endTime')),
110                     current.get('text'), current.get('lineAlign'),
111                     current.get('positionAlign'))
112                 if start is None or end is None or text is None:
113                     continue
114                 alignment = self._POS_ALIGN_MAP.get(position_align, 2) + self._LINE_ALIGN_MAP.get(line_align, 0)
115                 ssa += os.linesep + 'Dialogue: Marked=0,%s,%s,Default,,0,0,0,,%s%s' % (
116                     self._ass_subtitles_timecode(start),
117                     self._ass_subtitles_timecode(end),
118                     '{\\a%d}' % alignment if alignment != 2 else '',
119                     text.replace('\n', '\\N').replace('<i>', '{\\i1}').replace('</i>', '{\\i0}'))
120 
121             if sub_lang == 'vostf':
122                 sub_lang = 'fr'
123             subtitles.setdefault(sub_lang, []).extend([{
124                 'ext': 'json',
125                 'data': json.dumps(sub),
126             }, {
127                 'ext': 'ssa',
128                 'data': ssa,
129             }])
130         return subtitles
131 
132     def _real_extract(self, url):
133         video_id = self._match_id(url)
134         video_base_url = self._PLAYER_BASE_URL + 'video/%s/' % video_id
135         player = self._download_json(
136             video_base_url + 'configuration', video_id,
137             'Downloading player config JSON metadata')['player']
138         options = player['options']
139 
140         user = options['user']
141         if not user.get('hasAccess'):
142             raise ExtractorError(
143                 'This video is only available for paying users', expected=True)
144             # self.raise_login_required() # FIXME: Login is not implemented
145 
146         token = self._download_json(
147             user.get('refreshTokenUrl') or (self._PLAYER_BASE_URL + 'refresh/token'),
148             video_id, 'Downloading access token', headers={
149                 'x-player-refresh-token': user['refreshToken']
150             }, data=b'')['token']
151 
152         links_url = try_get(options, lambda x: x['video']['url']) or (video_base_url + 'link')
153         self._K = ''.join([random.choice('0123456789abcdef') for _ in range(16)])
154         message = bytes_to_intlist(json.dumps({
155             'k': self._K,
156             't': token,
157         }))
158 
159         # Sometimes authentication fails for no good reason, retry with
160         # a different random padding
161         links_data = None
162         for _ in range(3):
163             padded_message = intlist_to_bytes(pkcs1pad(message, 128))
164             n, e = self._RSA_KEY
165             encrypted_message = long_to_bytes(pow(bytes_to_long(padded_message), e, n))
166             authorization = base64.b64encode(encrypted_message).decode()
167 
168             try:
169                 links_data = self._download_json(
170                     links_url, video_id, 'Downloading links JSON metadata', headers={
171                         'X-Player-Token': authorization
172                     }, query={
173                         'freeWithAds': 'true',
174                         'adaptive': 'false',
175                         'withMetadata': 'true',
176                         'source': 'Web'
177                     })
178                 break
179             except ExtractorError as e:
180                 if not isinstance(e.cause, compat_HTTPError):
181                     raise e
182 
183                 if e.cause.code == 401:
184                     # This usually goes away with a different random pkcs1pad, so retry
185                     continue
186 
187                 error = self._parse_json(e.cause.read(), video_id)
188                 message = error.get('message')
189                 if e.cause.code == 403 and error.get('code') == 'player-bad-geolocation-country':
190                     self.raise_geo_restricted(msg=message)
191                 else:
192                     raise ExtractorError(message)
193         else:
194             raise ExtractorError('Giving up retrying')
195 
196         links = links_data.get('links') or {}
197         metas = links_data.get('metadata') or {}
198         sub_url = (links.get('subtitles') or {}).get('all')
199         video_info = links_data.get('video') or {}
200         title = metas['title']
201 
202         formats = []
203         for format_id, qualities in (links.get('streaming') or {}).items():
204             if not isinstance(qualities, dict):
205                 continue
206             for quality, load_balancer_url in qualities.items():
207                 load_balancer_data = self._download_json(
208                     load_balancer_url, video_id,
209                     'Downloading %s %s JSON metadata' % (format_id, quality),
210                     fatal=False) or {}
211                 m3u8_url = load_balancer_data.get('location')
212                 if not m3u8_url:
213                     continue
214                 m3u8_formats = self._extract_m3u8_formats(
215                     m3u8_url, video_id, 'mp4', 'm3u8_native',
216                     m3u8_id=format_id, fatal=False)
217                 if format_id == 'vf':
218                     for f in m3u8_formats:
219                         f['language'] = 'fr'
220                 formats.extend(m3u8_formats)
221         self._sort_formats(formats)
222 
223         video = (self._download_json(
224             self._API_BASE_URL + 'video/%s' % video_id, video_id,
225             'Downloading additional video metadata', fatal=False) or {}).get('video') or {}
226         show = video.get('show') or {}
227 
228         return {
229             'id': video_id,
230             'title': title,
231             'description': strip_or_none(metas.get('summary') or video.get('summary')),
232             'thumbnail': video_info.get('image') or player.get('image'),
233             'formats': formats,
234             'subtitles': self.extract_subtitles(sub_url, video_id),
235             'episode': metas.get('subtitle') or video.get('name'),
236             'episode_number': int_or_none(video.get('shortNumber')),
237             'series': show.get('title'),
238             'season_number': int_or_none(video.get('season')),
239             'duration': int_or_none(video_info.get('duration') or video.get('duration')),
240             'release_date': unified_strdate(video.get('releaseDate')),
241             'average_rating': float_or_none(video.get('rating') or metas.get('rating')),
242             'comment_count': int_or_none(video.get('commentsCount')),
243         }
244
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/youtube_dl/extractor/adn.py b/youtube_dl/extractor/adn.py
--- a/youtube_dl/extractor/adn.py
+++ b/youtube_dl/extractor/adn.py
@@ -26,6 +26,7 @@
     strip_or_none,
     try_get,
     unified_strdate,
+    urlencode_postdata,
 )
 
 
@@ -51,9 +52,11 @@
         }
     }
 
+    _NETRC_MACHINE = 'animedigitalnetwork'
     _BASE_URL = 'http://animedigitalnetwork.fr'
     _API_BASE_URL = 'https://gw.api.animedigitalnetwork.fr/'
     _PLAYER_BASE_URL = _API_BASE_URL + 'player/'
+    _HEADERS = {}
     _RSA_KEY = (0x9B42B08905199A5CCE2026274399CA560ECB209EE9878A708B1C0812E1BB8CB5D1FB7441861147C1A1F2F3A0476DD63A9CAC20D3E983613346850AA6CB38F16DC7D720FD7D86FC6E5B3D5BBC72E14CD0BF9E869F2CEA2CCAD648F1DCE38F1FF916CEFB2D339B64AA0264372344BC775E265E8A852F88144AB0BD9AA06C1A4ABB, 65537)
     _POS_ALIGN_MAP = {
         'start': 1,
@@ -129,19 +132,32 @@
             }])
         return subtitles
 
+    def _real_initialize(self):
+        username, password = self._get_login_info()
+        if username:
+            access_token = (self._download_json(
+                self._API_BASE_URL + 'authentication/login', None,
+                'Logging in', errnote='Unable to log in', fatal=False,
+                data=urlencode_postdata({
+                    'password': password,
+                    'rememberMe': False,
+                    'source': 'Web',
+                    'username': username,
+                })) or {}).get('accessToken')
+            if access_token:
+                self._HEADERS = {'authorization': 'Bearer ' + access_token}
+
     def _real_extract(self, url):
         video_id = self._match_id(url)
         video_base_url = self._PLAYER_BASE_URL + 'video/%s/' % video_id
         player = self._download_json(
             video_base_url + 'configuration', video_id,
-            'Downloading player config JSON metadata')['player']
+            'Downloading player config JSON metadata', headers=self._HEADERS)['player']
         options = player['options']
 
         user = options['user']
         if not user.get('hasAccess'):
-            raise ExtractorError(
-                'This video is only available for paying users', expected=True)
-            # self.raise_login_required() # FIXME: Login is not implemented
+            self.raise_login_required()
 
         token = self._download_json(
             user.get('refreshTokenUrl') or (self._PLAYER_BASE_URL + 'refresh/token'),

{"golden_diff": "diff --git a/youtube_dl/extractor/adn.py b/youtube_dl/extractor/adn.py\n--- a/youtube_dl/extractor/adn.py\n+++ b/youtube_dl/extractor/adn.py\n@@ -26,6 +26,7 @@\n     strip_or_none,\n     try_get,\n     unified_strdate,\n+    urlencode_postdata,\n )\n \n \n@@ -51,9 +52,11 @@\n         }\n     }\n \n+    _NETRC_MACHINE = 'animedigitalnetwork'\n     _BASE_URL = 'http://animedigitalnetwork.fr'\n     _API_BASE_URL = 'https://gw.api.animedigitalnetwork.fr/'\n     _PLAYER_BASE_URL = _API_BASE_URL + 'player/'\n+    _HEADERS = {}\n     _RSA_KEY = (0x9B42B08905199A5CCE2026274399CA560ECB209EE9878A708B1C0812E1BB8CB5D1FB7441861147C1A1F2F3A0476DD63A9CAC20D3E983613346850AA6CB38F16DC7D720FD7D86FC6E5B3D5BBC72E14CD0BF9E869F2CEA2CCAD648F1DCE38F1FF916CEFB2D339B64AA0264372344BC775E265E8A852F88144AB0BD9AA06C1A4ABB, 65537)\n     _POS_ALIGN_MAP = {\n         'start': 1,\n@@ -129,19 +132,32 @@\n             }])\n         return subtitles\n \n+    def _real_initialize(self):\n+        username, password = self._get_login_info()\n+        if username:\n+            access_token = (self._download_json(\n+                self._API_BASE_URL + 'authentication/login', None,\n+                'Logging in', errnote='Unable to log in', fatal=False,\n+                data=urlencode_postdata({\n+                    'password': password,\n+                    'rememberMe': False,\n+                    'source': 'Web',\n+                    'username': username,\n+                })) or {}).get('accessToken')\n+            if access_token:\n+                self._HEADERS = {'authorization': 'Bearer ' + access_token}\n+\n     def _real_extract(self, url):\n         video_id = self._match_id(url)\n         video_base_url = self._PLAYER_BASE_URL + 'video/%s/' % video_id\n         player = self._download_json(\n             video_base_url + 'configuration', video_id,\n-            'Downloading player config JSON metadata')['player']\n+            'Downloading player config JSON metadata', headers=self._HEADERS)['player']\n         options = player['options']\n \n         user = options['user']\n         if not user.get('hasAccess'):\n-            raise ExtractorError(\n-                'This video is only available for paying users', expected=True)\n-            # self.raise_login_required() # FIXME: Login is not implemented\n+            self.raise_login_required()\n \n         token = self._download_json(\n             user.get('refreshTokenUrl') or (self._PLAYER_BASE_URL + 'refresh/token'),\n", "issue": "[ADN] Can't download premium video after extractor fix\n<!--\r\n\r\n######################################################################\r\n  WARNING!\r\n  IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE\r\n######################################################################\r\n\r\n-->\r\n\r\n\r\n## Checklist\r\n\r\n<!--\r\nCarefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:\r\n- Look through the README (http://yt-dl.org/readme) and FAQ (http://yt-dl.org/faq) for similar questions\r\n- Search the bugtracker for similar questions: http://yt-dl.org/search-issues\r\n- Finally, put x into all relevant boxes (like this [x])\r\n-->\r\n\r\n- [x] I'm asking a question\r\n- [x] I've looked through the README and FAQ for similar questions\r\n- [x] I've searched the bugtracker for similar questions including closed ones\r\n\r\n\r\n## Question\r\n\r\n<!--\r\nAsk your question in an arbitrary form. Please make sure it's worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient.\r\n-->\r\n\r\nFirst of all I would like to thank the person who fixed the ADN extractor.\r\n\r\nEverything works perfectly to download the free videos in 480p. But to download videos requiring a premium account or to download better quality I can't do it.\r\n\r\nBefore I used to use the command :\r\n\r\n`youtube-dl.exe --cookies ./cookies.txt https://animedigitalnetwork.fr/video/my-hero-academia-saison-4/10271-episode-2-overhaul`\r\n\r\nAnd it worked perfectly but after the extractor fix it doesn't work anymore I tried to re-download the cookies as indicated in the doc but it doesn't change anything I also tried to use `--username` and `--password` but it doesn't do anything either and it shows me in the both case:\r\n\r\n`ERROR: This video is only available for paying users`\r\n\r\nI think the problem comes from the cookies that don't have the same shape as before, there were 4 lines in my file but now there is only one.\r\n\r\nCould someone help me?\r\n\r\nThank you.\n", "before_files": [{"content": "# coding: utf-8\nfrom __future__ import unicode_literals\n\nimport base64\nimport binascii\nimport json\nimport os\nimport random\n\nfrom .common import InfoExtractor\nfrom ..aes import aes_cbc_decrypt\nfrom ..compat import (\n    compat_HTTPError,\n    compat_b64decode,\n    compat_ord,\n)\nfrom ..utils import (\n    bytes_to_intlist,\n    bytes_to_long,\n    ExtractorError,\n    float_or_none,\n    int_or_none,\n    intlist_to_bytes,\n    long_to_bytes,\n    pkcs1pad,\n    strip_or_none,\n    try_get,\n    unified_strdate,\n)\n\n\nclass ADNIE(InfoExtractor):\n    IE_DESC = 'Anime Digital Network'\n    _VALID_URL = r'https?://(?:www\\.)?animedigitalnetwork\\.fr/video/[^/]+/(?P<id>\\d+)'\n    _TEST = {\n        'url': 'http://animedigitalnetwork.fr/video/blue-exorcist-kyoto-saga/7778-episode-1-debut-des-hostilites',\n        'md5': '0319c99885ff5547565cacb4f3f9348d',\n        'info_dict': {\n            'id': '7778',\n            'ext': 'mp4',\n            'title': 'Blue Exorcist - Ky\u00f4to Saga - Episode 1',\n            'description': 'md5:2f7b5aa76edbc1a7a92cedcda8a528d5',\n            'series': 'Blue Exorcist - Ky\u00f4to Saga',\n            'duration': 1467,\n            'release_date': '20170106',\n            'comment_count': int,\n            'average_rating': float,\n            'season_number': 2,\n            'episode': 'D\u00e9but des hostilit\u00e9s',\n            'episode_number': 1,\n        }\n    }\n\n    _BASE_URL = 'http://animedigitalnetwork.fr'\n    _API_BASE_URL = 'https://gw.api.animedigitalnetwork.fr/'\n    _PLAYER_BASE_URL = _API_BASE_URL + 'player/'\n    _RSA_KEY = (0x9B42B08905199A5CCE2026274399CA560ECB209EE9878A708B1C0812E1BB8CB5D1FB7441861147C1A1F2F3A0476DD63A9CAC20D3E983613346850AA6CB38F16DC7D720FD7D86FC6E5B3D5BBC72E14CD0BF9E869F2CEA2CCAD648F1DCE38F1FF916CEFB2D339B64AA0264372344BC775E265E8A852F88144AB0BD9AA06C1A4ABB, 65537)\n    _POS_ALIGN_MAP = {\n        'start': 1,\n        'end': 3,\n    }\n    _LINE_ALIGN_MAP = {\n        'middle': 8,\n        'end': 4,\n    }\n\n    @staticmethod\n    def _ass_subtitles_timecode(seconds):\n        return '%01d:%02d:%02d.%02d' % (seconds / 3600, (seconds % 3600) / 60, seconds % 60, (seconds % 1) * 100)\n\n    def _get_subtitles(self, sub_url, video_id):\n        if not sub_url:\n            return None\n\n        enc_subtitles = self._download_webpage(\n            sub_url, video_id, 'Downloading subtitles location', fatal=False) or '{}'\n        subtitle_location = (self._parse_json(enc_subtitles, video_id, fatal=False) or {}).get('location')\n        if subtitle_location:\n            enc_subtitles = self._download_webpage(\n                subtitle_location, video_id, 'Downloading subtitles data',\n                fatal=False, headers={'Origin': 'https://animedigitalnetwork.fr'})\n        if not enc_subtitles:\n            return None\n\n        # http://animedigitalnetwork.fr/components/com_vodvideo/videojs/adn-vjs.min.js\n        dec_subtitles = intlist_to_bytes(aes_cbc_decrypt(\n            bytes_to_intlist(compat_b64decode(enc_subtitles[24:])),\n            bytes_to_intlist(binascii.unhexlify(self._K + 'ab9f52f5baae7c72')),\n            bytes_to_intlist(compat_b64decode(enc_subtitles[:24]))\n        ))\n        subtitles_json = self._parse_json(\n            dec_subtitles[:-compat_ord(dec_subtitles[-1])].decode(),\n            None, fatal=False)\n        if not subtitles_json:\n            return None\n\n        subtitles = {}\n        for sub_lang, sub in subtitles_json.items():\n            ssa = '''[Script Info]\nScriptType:V4.00\n[V4 Styles]\nFormat: Name,Fontname,Fontsize,PrimaryColour,SecondaryColour,TertiaryColour,BackColour,Bold,Italic,BorderStyle,Outline,Shadow,Alignment,MarginL,MarginR,MarginV,AlphaLevel,Encoding\nStyle: Default,Arial,18,16777215,16777215,16777215,0,-1,0,1,1,0,2,20,20,20,0,0\n[Events]\nFormat: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text'''\n            for current in sub:\n                start, end, text, line_align, position_align = (\n                    float_or_none(current.get('startTime')),\n                    float_or_none(current.get('endTime')),\n                    current.get('text'), current.get('lineAlign'),\n                    current.get('positionAlign'))\n                if start is None or end is None or text is None:\n                    continue\n                alignment = self._POS_ALIGN_MAP.get(position_align, 2) + self._LINE_ALIGN_MAP.get(line_align, 0)\n                ssa += os.linesep + 'Dialogue: Marked=0,%s,%s,Default,,0,0,0,,%s%s' % (\n                    self._ass_subtitles_timecode(start),\n                    self._ass_subtitles_timecode(end),\n                    '{\\\\a%d}' % alignment if alignment != 2 else '',\n                    text.replace('\\n', '\\\\N').replace('<i>', '{\\\\i1}').replace('</i>', '{\\\\i0}'))\n\n            if sub_lang == 'vostf':\n                sub_lang = 'fr'\n            subtitles.setdefault(sub_lang, []).extend([{\n                'ext': 'json',\n                'data': json.dumps(sub),\n            }, {\n                'ext': 'ssa',\n                'data': ssa,\n            }])\n        return subtitles\n\n    def _real_extract(self, url):\n        video_id = self._match_id(url)\n        video_base_url = self._PLAYER_BASE_URL + 'video/%s/' % video_id\n        player = self._download_json(\n            video_base_url + 'configuration', video_id,\n            'Downloading player config JSON metadata')['player']\n        options = player['options']\n\n        user = options['user']\n        if not user.get('hasAccess'):\n            raise ExtractorError(\n                'This video is only available for paying users', expected=True)\n            # self.raise_login_required() # FIXME: Login is not implemented\n\n        token = self._download_json(\n            user.get('refreshTokenUrl') or (self._PLAYER_BASE_URL + 'refresh/token'),\n            video_id, 'Downloading access token', headers={\n                'x-player-refresh-token': user['refreshToken']\n            }, data=b'')['token']\n\n        links_url = try_get(options, lambda x: x['video']['url']) or (video_base_url + 'link')\n        self._K = ''.join([random.choice('0123456789abcdef') for _ in range(16)])\n        message = bytes_to_intlist(json.dumps({\n            'k': self._K,\n            't': token,\n        }))\n\n        # Sometimes authentication fails for no good reason, retry with\n        # a different random padding\n        links_data = None\n        for _ in range(3):\n            padded_message = intlist_to_bytes(pkcs1pad(message, 128))\n            n, e = self._RSA_KEY\n            encrypted_message = long_to_bytes(pow(bytes_to_long(padded_message), e, n))\n            authorization = base64.b64encode(encrypted_message).decode()\n\n            try:\n                links_data = self._download_json(\n                    links_url, video_id, 'Downloading links JSON metadata', headers={\n                        'X-Player-Token': authorization\n                    }, query={\n                        'freeWithAds': 'true',\n                        'adaptive': 'false',\n                        'withMetadata': 'true',\n                        'source': 'Web'\n                    })\n                break\n            except ExtractorError as e:\n                if not isinstance(e.cause, compat_HTTPError):\n                    raise e\n\n                if e.cause.code == 401:\n                    # This usually goes away with a different random pkcs1pad, so retry\n                    continue\n\n                error = self._parse_json(e.cause.read(), video_id)\n                message = error.get('message')\n                if e.cause.code == 403 and error.get('code') == 'player-bad-geolocation-country':\n                    self.raise_geo_restricted(msg=message)\n                else:\n                    raise ExtractorError(message)\n        else:\n            raise ExtractorError('Giving up retrying')\n\n        links = links_data.get('links') or {}\n        metas = links_data.get('metadata') or {}\n        sub_url = (links.get('subtitles') or {}).get('all')\n        video_info = links_data.get('video') or {}\n        title = metas['title']\n\n        formats = []\n        for format_id, qualities in (links.get('streaming') or {}).items():\n            if not isinstance(qualities, dict):\n                continue\n            for quality, load_balancer_url in qualities.items():\n                load_balancer_data = self._download_json(\n                    load_balancer_url, video_id,\n                    'Downloading %s %s JSON metadata' % (format_id, quality),\n                    fatal=False) or {}\n                m3u8_url = load_balancer_data.get('location')\n                if not m3u8_url:\n                    continue\n                m3u8_formats = self._extract_m3u8_formats(\n                    m3u8_url, video_id, 'mp4', 'm3u8_native',\n                    m3u8_id=format_id, fatal=False)\n                if format_id == 'vf':\n                    for f in m3u8_formats:\n                        f['language'] = 'fr'\n                formats.extend(m3u8_formats)\n        self._sort_formats(formats)\n\n        video = (self._download_json(\n            self._API_BASE_URL + 'video/%s' % video_id, video_id,\n            'Downloading additional video metadata', fatal=False) or {}).get('video') or {}\n        show = video.get('show') or {}\n\n        return {\n            'id': video_id,\n            'title': title,\n            'description': strip_or_none(metas.get('summary') or video.get('summary')),\n            'thumbnail': video_info.get('image') or player.get('image'),\n            'formats': formats,\n            'subtitles': self.extract_subtitles(sub_url, video_id),\n            'episode': metas.get('subtitle') or video.get('name'),\n            'episode_number': int_or_none(video.get('shortNumber')),\n            'series': show.get('title'),\n            'season_number': int_or_none(video.get('season')),\n            'duration': int_or_none(video_info.get('duration') or video.get('duration')),\n            'release_date': unified_strdate(video.get('releaseDate')),\n            'average_rating': float_or_none(video.get('rating') or metas.get('rating')),\n            'comment_count': int_or_none(video.get('commentsCount')),\n        }\n", "path": "youtube_dl/extractor/adn.py"}], "after_files": [{"content": "# coding: utf-8\nfrom __future__ import unicode_literals\n\nimport base64\nimport binascii\nimport json\nimport os\nimport random\n\nfrom .common import InfoExtractor\nfrom ..aes import aes_cbc_decrypt\nfrom ..compat import (\n    compat_HTTPError,\n    compat_b64decode,\n    compat_ord,\n)\nfrom ..utils import (\n    bytes_to_intlist,\n    bytes_to_long,\n    ExtractorError,\n    float_or_none,\n    int_or_none,\n    intlist_to_bytes,\n    long_to_bytes,\n    pkcs1pad,\n    strip_or_none,\n    try_get,\n    unified_strdate,\n    urlencode_postdata,\n)\n\n\nclass ADNIE(InfoExtractor):\n    IE_DESC = 'Anime Digital Network'\n    _VALID_URL = r'https?://(?:www\\.)?animedigitalnetwork\\.fr/video/[^/]+/(?P<id>\\d+)'\n    _TEST = {\n        'url': 'http://animedigitalnetwork.fr/video/blue-exorcist-kyoto-saga/7778-episode-1-debut-des-hostilites',\n        'md5': '0319c99885ff5547565cacb4f3f9348d',\n        'info_dict': {\n            'id': '7778',\n            'ext': 'mp4',\n            'title': 'Blue Exorcist - Ky\u00f4to Saga - Episode 1',\n            'description': 'md5:2f7b5aa76edbc1a7a92cedcda8a528d5',\n            'series': 'Blue Exorcist - Ky\u00f4to Saga',\n            'duration': 1467,\n            'release_date': '20170106',\n            'comment_count': int,\n            'average_rating': float,\n            'season_number': 2,\n            'episode': 'D\u00e9but des hostilit\u00e9s',\n            'episode_number': 1,\n        }\n    }\n\n    _NETRC_MACHINE = 'animedigitalnetwork'\n    _BASE_URL = 'http://animedigitalnetwork.fr'\n    _API_BASE_URL = 'https://gw.api.animedigitalnetwork.fr/'\n    _PLAYER_BASE_URL = _API_BASE_URL + 'player/'\n    _HEADERS = {}\n    _RSA_KEY = (0x9B42B08905199A5CCE2026274399CA560ECB209EE9878A708B1C0812E1BB8CB5D1FB7441861147C1A1F2F3A0476DD63A9CAC20D3E983613346850AA6CB38F16DC7D720FD7D86FC6E5B3D5BBC72E14CD0BF9E869F2CEA2CCAD648F1DCE38F1FF916CEFB2D339B64AA0264372344BC775E265E8A852F88144AB0BD9AA06C1A4ABB, 65537)\n    _POS_ALIGN_MAP = {\n        'start': 1,\n        'end': 3,\n    }\n    _LINE_ALIGN_MAP = {\n        'middle': 8,\n        'end': 4,\n    }\n\n    @staticmethod\n    def _ass_subtitles_timecode(seconds):\n        return '%01d:%02d:%02d.%02d' % (seconds / 3600, (seconds % 3600) / 60, seconds % 60, (seconds % 1) * 100)\n\n    def _get_subtitles(self, sub_url, video_id):\n        if not sub_url:\n            return None\n\n        enc_subtitles = self._download_webpage(\n            sub_url, video_id, 'Downloading subtitles location', fatal=False) or '{}'\n        subtitle_location = (self._parse_json(enc_subtitles, video_id, fatal=False) or {}).get('location')\n        if subtitle_location:\n            enc_subtitles = self._download_webpage(\n                subtitle_location, video_id, 'Downloading subtitles data',\n                fatal=False, headers={'Origin': 'https://animedigitalnetwork.fr'})\n        if not enc_subtitles:\n            return None\n\n        # http://animedigitalnetwork.fr/components/com_vodvideo/videojs/adn-vjs.min.js\n        dec_subtitles = intlist_to_bytes(aes_cbc_decrypt(\n            bytes_to_intlist(compat_b64decode(enc_subtitles[24:])),\n            bytes_to_intlist(binascii.unhexlify(self._K + 'ab9f52f5baae7c72')),\n            bytes_to_intlist(compat_b64decode(enc_subtitles[:24]))\n        ))\n        subtitles_json = self._parse_json(\n            dec_subtitles[:-compat_ord(dec_subtitles[-1])].decode(),\n            None, fatal=False)\n        if not subtitles_json:\n            return None\n\n        subtitles = {}\n        for sub_lang, sub in subtitles_json.items():\n            ssa = '''[Script Info]\nScriptType:V4.00\n[V4 Styles]\nFormat: Name,Fontname,Fontsize,PrimaryColour,SecondaryColour,TertiaryColour,BackColour,Bold,Italic,BorderStyle,Outline,Shadow,Alignment,MarginL,MarginR,MarginV,AlphaLevel,Encoding\nStyle: Default,Arial,18,16777215,16777215,16777215,0,-1,0,1,1,0,2,20,20,20,0,0\n[Events]\nFormat: Marked,Start,End,Style,Name,MarginL,MarginR,MarginV,Effect,Text'''\n            for current in sub:\n                start, end, text, line_align, position_align = (\n                    float_or_none(current.get('startTime')),\n                    float_or_none(current.get('endTime')),\n                    current.get('text'), current.get('lineAlign'),\n                    current.get('positionAlign'))\n                if start is None or end is None or text is None:\n                    continue\n                alignment = self._POS_ALIGN_MAP.get(position_align, 2) + self._LINE_ALIGN_MAP.get(line_align, 0)\n                ssa += os.linesep + 'Dialogue: Marked=0,%s,%s,Default,,0,0,0,,%s%s' % (\n                    self._ass_subtitles_timecode(start),\n                    self._ass_subtitles_timecode(end),\n                    '{\\\\a%d}' % alignment if alignment != 2 else '',\n                    text.replace('\\n', '\\\\N').replace('<i>', '{\\\\i1}').replace('</i>', '{\\\\i0}'))\n\n            if sub_lang == 'vostf':\n                sub_lang = 'fr'\n            subtitles.setdefault(sub_lang, []).extend([{\n                'ext': 'json',\n                'data': json.dumps(sub),\n            }, {\n                'ext': 'ssa',\n                'data': ssa,\n            }])\n        return subtitles\n\n    def _real_initialize(self):\n        username, password = self._get_login_info()\n        if username:\n            access_token = (self._download_json(\n                self._API_BASE_URL + 'authentication/login', None,\n                'Logging in', errnote='Unable to log in', fatal=False,\n                data=urlencode_postdata({\n                    'password': password,\n                    'rememberMe': False,\n                    'source': 'Web',\n                    'username': username,\n                })) or {}).get('accessToken')\n            if access_token:\n                self._HEADERS = {'authorization': 'Bearer ' + access_token}\n\n    def _real_extract(self, url):\n        video_id = self._match_id(url)\n        video_base_url = self._PLAYER_BASE_URL + 'video/%s/' % video_id\n        player = self._download_json(\n            video_base_url + 'configuration', video_id,\n            'Downloading player config JSON metadata', headers=self._HEADERS)['player']\n        options = player['options']\n\n        user = options['user']\n        if not user.get('hasAccess'):\n            self.raise_login_required()\n\n        token = self._download_json(\n            user.get('refreshTokenUrl') or (self._PLAYER_BASE_URL + 'refresh/token'),\n            video_id, 'Downloading access token', headers={\n                'x-player-refresh-token': user['refreshToken']\n            }, data=b'')['token']\n\n        links_url = try_get(options, lambda x: x['video']['url']) or (video_base_url + 'link')\n        self._K = ''.join([random.choice('0123456789abcdef') for _ in range(16)])\n        message = bytes_to_intlist(json.dumps({\n            'k': self._K,\n            't': token,\n        }))\n\n        # Sometimes authentication fails for no good reason, retry with\n        # a different random padding\n        links_data = None\n        for _ in range(3):\n            padded_message = intlist_to_bytes(pkcs1pad(message, 128))\n            n, e = self._RSA_KEY\n            encrypted_message = long_to_bytes(pow(bytes_to_long(padded_message), e, n))\n            authorization = base64.b64encode(encrypted_message).decode()\n\n            try:\n                links_data = self._download_json(\n                    links_url, video_id, 'Downloading links JSON metadata', headers={\n                        'X-Player-Token': authorization\n                    }, query={\n                        'freeWithAds': 'true',\n                        'adaptive': 'false',\n                        'withMetadata': 'true',\n                        'source': 'Web'\n                    })\n                break\n            except ExtractorError as e:\n                if not isinstance(e.cause, compat_HTTPError):\n                    raise e\n\n                if e.cause.code == 401:\n                    # This usually goes away with a different random pkcs1pad, so retry\n                    continue\n\n                error = self._parse_json(e.cause.read(), video_id)\n                message = error.get('message')\n                if e.cause.code == 403 and error.get('code') == 'player-bad-geolocation-country':\n                    self.raise_geo_restricted(msg=message)\n                else:\n                    raise ExtractorError(message)\n        else:\n            raise ExtractorError('Giving up retrying')\n\n        links = links_data.get('links') or {}\n        metas = links_data.get('metadata') or {}\n        sub_url = (links.get('subtitles') or {}).get('all')\n        video_info = links_data.get('video') or {}\n        title = metas['title']\n\n        formats = []\n        for format_id, qualities in (links.get('streaming') or {}).items():\n            if not isinstance(qualities, dict):\n                continue\n            for quality, load_balancer_url in qualities.items():\n                load_balancer_data = self._download_json(\n                    load_balancer_url, video_id,\n                    'Downloading %s %s JSON metadata' % (format_id, quality),\n                    fatal=False) or {}\n                m3u8_url = load_balancer_data.get('location')\n                if not m3u8_url:\n                    continue\n                m3u8_formats = self._extract_m3u8_formats(\n                    m3u8_url, video_id, 'mp4', 'm3u8_native',\n                    m3u8_id=format_id, fatal=False)\n                if format_id == 'vf':\n                    for f in m3u8_formats:\n                        f['language'] = 'fr'\n                formats.extend(m3u8_formats)\n        self._sort_formats(formats)\n\n        video = (self._download_json(\n            self._API_BASE_URL + 'video/%s' % video_id, video_id,\n            'Downloading additional video metadata', fatal=False) or {}).get('video') or {}\n        show = video.get('show') or {}\n\n        return {\n            'id': video_id,\n            'title': title,\n            'description': strip_or_none(metas.get('summary') or video.get('summary')),\n            'thumbnail': video_info.get('image') or player.get('image'),\n            'formats': formats,\n            'subtitles': self.extract_subtitles(sub_url, video_id),\n            'episode': metas.get('subtitle') or video.get('name'),\n            'episode_number': int_or_none(video.get('shortNumber')),\n            'series': show.get('title'),\n            'season_number': int_or_none(video.get('season')),\n            'duration': int_or_none(video_info.get('duration') or video.get('duration')),\n            'release_date': unified_strdate(video.get('releaseDate')),\n            'average_rating': float_or_none(video.get('rating') or metas.get('rating')),\n            'comment_count': int_or_none(video.get('commentsCount')),\n        }\n", "path": "youtube_dl/extractor/adn.py"}]}