We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Prefer ffmpeg HLS decoder for v.redd.it
### Make sure you are using the *latest* version: run `youtube-dl --version` and ensure your version is *2017.11.15*. If it's not, read [this FAQ entry](https://github.com/rg3/youtube-dl/blob/master/README.md#how-do-i-update-youtube-dl) and update. Issues with outdated version will be rejected.
- [x] I've **verified** and **I assure** that I'm running youtube-dl **2017.11.15**

### Before submitting an *issue* make sure you have:
- [x] At least skimmed through the [README](https://github.com/rg3/youtube-dl/blob/master/README.md), **most notably** the [FAQ](https://github.com/rg3/youtube-dl#faq) and [BUGS](https://github.com/rg3/youtube-dl#bugs) sections
- [x] [Searched](https://github.com/rg3/youtube-dl/search?type=Issues) the bugtracker for similar issues including closed ones

### What is the purpose of your *issue*?
- [x] Bug report (encountered problems with youtube-dl)
- [ ] Site support request (request for adding support for a new site)
- [ ] Feature request (request for a new functionality)
- [ ] Question
- [ ] Other

---

### Description of your *issue*, suggested solution and other information

Site v.reddit.com should prefer ffmpeg HLS downloader instead of native. Using the native downloader results in some video corruption and artifacting, e.g. visible within the first few seconds of this source: `youtube-dl https://v.redd.it/poqkxthgcpxz`

--- 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/downloader/hls.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import re
4 import binascii
5 try:
6     from Crypto.Cipher import AES
7     can_decrypt_frag = True
8 except ImportError:
9     can_decrypt_frag = False
10 
11 from .fragment import FragmentFD
12 from .external import FFmpegFD
13 
14 from ..compat import (
15     compat_urllib_error,
16     compat_urlparse,
17     compat_struct_pack,
18 )
19 from ..utils import (
20     parse_m3u8_attributes,
21     update_url_query,
22 )
23 
24 
25 class HlsFD(FragmentFD):
26     """ A limited implementation that does not require ffmpeg """
27 
28     FD_NAME = 'hlsnative'
29 
30     @staticmethod
31     def can_download(manifest, info_dict):
32         UNSUPPORTED_FEATURES = (
33             r'#EXT-X-KEY:METHOD=(?!NONE|AES-128)',  # encrypted streams [1]
34             # r'#EXT-X-BYTERANGE',  # playlists composed of byte ranges of media files [2]
35 
36             # Live streams heuristic does not always work (e.g. geo restricted to Germany
37             # http://hls-geo.daserste.de/i/videoportal/Film/c_620000/622873/format,716451,716457,716450,716458,716459,.mp4.csmil/index_4_av.m3u8?null=0)
38             # r'#EXT-X-MEDIA-SEQUENCE:(?!0$)',  # live streams [3]
39 
40             # This heuristic also is not correct since segments may not be appended as well.
41             # Twitch vods of finished streams have EXT-X-PLAYLIST-TYPE:EVENT despite
42             # no segments will definitely be appended to the end of the playlist.
43             # r'#EXT-X-PLAYLIST-TYPE:EVENT',  # media segments may be appended to the end of
44             #                                 # event media playlists [4]
45 
46             # 1. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.4
47             # 2. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.2
48             # 3. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.2
49             # 4. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.5
50         )
51         check_results = [not re.search(feature, manifest) for feature in UNSUPPORTED_FEATURES]
52         is_aes128_enc = '#EXT-X-KEY:METHOD=AES-128' in manifest
53         check_results.append(can_decrypt_frag or not is_aes128_enc)
54         check_results.append(not (is_aes128_enc and r'#EXT-X-BYTERANGE' in manifest))
55         check_results.append(not info_dict.get('is_live'))
56         return all(check_results)
57 
58     def real_download(self, filename, info_dict):
59         man_url = info_dict['url']
60         self.to_screen('[%s] Downloading m3u8 manifest' % self.FD_NAME)
61 
62         urlh = self.ydl.urlopen(self._prepare_url(info_dict, man_url))
63         man_url = urlh.geturl()
64         s = urlh.read().decode('utf-8', 'ignore')
65 
66         if not self.can_download(s, info_dict):
67             if info_dict.get('extra_param_to_segment_url') or info_dict.get('_decryption_key_url'):
68                 self.report_error('pycrypto not found. Please install it.')
69                 return False
70             self.report_warning(
71                 'hlsnative has detected features it does not support, '
72                 'extraction will be delegated to ffmpeg')
73             fd = FFmpegFD(self.ydl, self.params)
74             for ph in self._progress_hooks:
75                 fd.add_progress_hook(ph)
76             return fd.real_download(filename, info_dict)
77 
78         def is_ad_fragment_start(s):
79             return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=ad' in s
80                     or s.startswith('#UPLYNK-SEGMENT') and s.endswith(',ad'))
81 
82         def is_ad_fragment_end(s):
83             return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=master' in s
84                     or s.startswith('#UPLYNK-SEGMENT') and s.endswith(',segment'))
85 
86         media_frags = 0
87         ad_frags = 0
88         ad_frag_next = False
89         for line in s.splitlines():
90             line = line.strip()
91             if not line:
92                 continue
93             if line.startswith('#'):
94                 if is_ad_fragment_start(line):
95                     ad_frag_next = True
96                 elif is_ad_fragment_end(line):
97                     ad_frag_next = False
98                 continue
99             if ad_frag_next:
100                 ad_frags += 1
101                 continue
102             media_frags += 1
103 
104         ctx = {
105             'filename': filename,
106             'total_frags': media_frags,
107             'ad_frags': ad_frags,
108         }
109 
110         self._prepare_and_start_frag_download(ctx)
111 
112         fragment_retries = self.params.get('fragment_retries', 0)
113         skip_unavailable_fragments = self.params.get('skip_unavailable_fragments', True)
114         test = self.params.get('test', False)
115 
116         extra_query = None
117         extra_param_to_segment_url = info_dict.get('extra_param_to_segment_url')
118         if extra_param_to_segment_url:
119             extra_query = compat_urlparse.parse_qs(extra_param_to_segment_url)
120         i = 0
121         media_sequence = 0
122         decrypt_info = {'METHOD': 'NONE'}
123         byte_range = {}
124         frag_index = 0
125         ad_frag_next = False
126         for line in s.splitlines():
127             line = line.strip()
128             if line:
129                 if not line.startswith('#'):
130                     if ad_frag_next:
131                         continue
132                     frag_index += 1
133                     if frag_index <= ctx['fragment_index']:
134                         continue
135                     frag_url = (
136                         line
137                         if re.match(r'^https?://', line)
138                         else compat_urlparse.urljoin(man_url, line))
139                     if extra_query:
140                         frag_url = update_url_query(frag_url, extra_query)
141                     count = 0
142                     headers = info_dict.get('http_headers', {})
143                     if byte_range:
144                         headers['Range'] = 'bytes=%d-%d' % (byte_range['start'], byte_range['end'])
145                     while count <= fragment_retries:
146                         try:
147                             success, frag_content = self._download_fragment(
148                                 ctx, frag_url, info_dict, headers)
149                             if not success:
150                                 return False
151                             break
152                         except compat_urllib_error.HTTPError as err:
153                             # Unavailable (possibly temporary) fragments may be served.
154                             # First we try to retry then either skip or abort.
155                             # See https://github.com/ytdl-org/youtube-dl/issues/10165,
156                             # https://github.com/ytdl-org/youtube-dl/issues/10448).
157                             count += 1
158                             if count <= fragment_retries:
159                                 self.report_retry_fragment(err, frag_index, count, fragment_retries)
160                     if count > fragment_retries:
161                         if skip_unavailable_fragments:
162                             i += 1
163                             media_sequence += 1
164                             self.report_skip_fragment(frag_index)
165                             continue
166                         self.report_error(
167                             'giving up after %s fragment retries' % fragment_retries)
168                         return False
169                     if decrypt_info['METHOD'] == 'AES-128':
170                         iv = decrypt_info.get('IV') or compat_struct_pack('>8xq', media_sequence)
171                         decrypt_info['KEY'] = decrypt_info.get('KEY') or self.ydl.urlopen(
172                             self._prepare_url(info_dict, info_dict.get('_decryption_key_url') or decrypt_info['URI'])).read()
173                         frag_content = AES.new(
174                             decrypt_info['KEY'], AES.MODE_CBC, iv).decrypt(frag_content)
175                     self._append_fragment(ctx, frag_content)
176                     # We only download the first fragment during the test
177                     if test:
178                         break
179                     i += 1
180                     media_sequence += 1
181                 elif line.startswith('#EXT-X-KEY'):
182                     decrypt_url = decrypt_info.get('URI')
183                     decrypt_info = parse_m3u8_attributes(line[11:])
184                     if decrypt_info['METHOD'] == 'AES-128':
185                         if 'IV' in decrypt_info:
186                             decrypt_info['IV'] = binascii.unhexlify(decrypt_info['IV'][2:].zfill(32))
187                         if not re.match(r'^https?://', decrypt_info['URI']):
188                             decrypt_info['URI'] = compat_urlparse.urljoin(
189                                 man_url, decrypt_info['URI'])
190                         if extra_query:
191                             decrypt_info['URI'] = update_url_query(decrypt_info['URI'], extra_query)
192                         if decrypt_url != decrypt_info['URI']:
193                             decrypt_info['KEY'] = None
194                 elif line.startswith('#EXT-X-MEDIA-SEQUENCE'):
195                     media_sequence = int(line[22:])
196                 elif line.startswith('#EXT-X-BYTERANGE'):
197                     splitted_byte_range = line[17:].split('@')
198                     sub_range_start = int(splitted_byte_range[1]) if len(splitted_byte_range) == 2 else byte_range['end']
199                     byte_range = {
200                         'start': sub_range_start,
201                         'end': sub_range_start + int(splitted_byte_range[0]),
202                     }
203                 elif is_ad_fragment_start(line):
204                     ad_frag_next = True
205                 elif is_ad_fragment_end(line):
206                     ad_frag_next = False
207 
208         self._finish_frag_download(ctx)
209 
210         return True
211
```
--- 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/downloader/hls.py b/youtube_dl/downloader/hls.py
--- a/youtube_dl/downloader/hls.py
+++ b/youtube_dl/downloader/hls.py
@@ -141,7 +141,7 @@
                     count = 0
                     headers = info_dict.get('http_headers', {})
                     if byte_range:
-                        headers['Range'] = 'bytes=%d-%d' % (byte_range['start'], byte_range['end'])
+                        headers['Range'] = 'bytes=%d-%d' % (byte_range['start'], byte_range['end'] - 1)
                     while count <= fragment_retries:
                         try:
                             success, frag_content = self._download_fragment(

{"golden_diff": "diff --git a/youtube_dl/downloader/hls.py b/youtube_dl/downloader/hls.py\n--- a/youtube_dl/downloader/hls.py\n+++ b/youtube_dl/downloader/hls.py\n@@ -141,7 +141,7 @@\n                     count = 0\n                     headers = info_dict.get('http_headers', {})\n                     if byte_range:\n-                        headers['Range'] = 'bytes=%d-%d' % (byte_range['start'], byte_range['end'])\n+                        headers['Range'] = 'bytes=%d-%d' % (byte_range['start'], byte_range['end'] - 1)\n                     while count <= fragment_retries:\n                         try:\n                             success, frag_content = self._download_fragment(\n", "issue": "Prefer ffmpeg HLS decoder for v.redd.it\n### Make sure you are using the *latest* version: run `youtube-dl --version` and ensure your version is *2017.11.15*. If it's not, read [this FAQ entry](https://github.com/rg3/youtube-dl/blob/master/README.md#how-do-i-update-youtube-dl) and update. Issues with outdated version will be rejected.\r\n- [x] I've **verified** and **I assure** that I'm running youtube-dl **2017.11.15**\r\n\r\n### Before submitting an *issue* make sure you have:\r\n- [x] At least skimmed through the [README](https://github.com/rg3/youtube-dl/blob/master/README.md), **most notably** the [FAQ](https://github.com/rg3/youtube-dl#faq) and [BUGS](https://github.com/rg3/youtube-dl#bugs) sections\r\n- [x] [Searched](https://github.com/rg3/youtube-dl/search?type=Issues) the bugtracker for similar issues including closed ones\r\n\r\n### What is the purpose of your *issue*?\r\n- [x] Bug report (encountered problems with youtube-dl)\r\n- [ ] Site support request (request for adding support for a new site)\r\n- [ ] Feature request (request for a new functionality)\r\n- [ ] Question\r\n- [ ] Other\r\n\r\n---\r\n\r\n### Description of your *issue*, suggested solution and other information\r\n\r\nSite v.reddit.com should prefer ffmpeg HLS downloader instead of native. Using the native downloader results in some video corruption and artifacting, e.g. visible within the first few seconds of this source: `youtube-dl https://v.redd.it/poqkxthgcpxz`\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nimport re\nimport binascii\ntry:\n    from Crypto.Cipher import AES\n    can_decrypt_frag = True\nexcept ImportError:\n    can_decrypt_frag = False\n\nfrom .fragment import FragmentFD\nfrom .external import FFmpegFD\n\nfrom ..compat import (\n    compat_urllib_error,\n    compat_urlparse,\n    compat_struct_pack,\n)\nfrom ..utils import (\n    parse_m3u8_attributes,\n    update_url_query,\n)\n\n\nclass HlsFD(FragmentFD):\n    \"\"\" A limited implementation that does not require ffmpeg \"\"\"\n\n    FD_NAME = 'hlsnative'\n\n    @staticmethod\n    def can_download(manifest, info_dict):\n        UNSUPPORTED_FEATURES = (\n            r'#EXT-X-KEY:METHOD=(?!NONE|AES-128)',  # encrypted streams [1]\n            # r'#EXT-X-BYTERANGE',  # playlists composed of byte ranges of media files [2]\n\n            # Live streams heuristic does not always work (e.g. geo restricted to Germany\n            # http://hls-geo.daserste.de/i/videoportal/Film/c_620000/622873/format,716451,716457,716450,716458,716459,.mp4.csmil/index_4_av.m3u8?null=0)\n            # r'#EXT-X-MEDIA-SEQUENCE:(?!0$)',  # live streams [3]\n\n            # This heuristic also is not correct since segments may not be appended as well.\n            # Twitch vods of finished streams have EXT-X-PLAYLIST-TYPE:EVENT despite\n            # no segments will definitely be appended to the end of the playlist.\n            # r'#EXT-X-PLAYLIST-TYPE:EVENT',  # media segments may be appended to the end of\n            #                                 # event media playlists [4]\n\n            # 1. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.4\n            # 2. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.2\n            # 3. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.2\n            # 4. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.5\n        )\n        check_results = [not re.search(feature, manifest) for feature in UNSUPPORTED_FEATURES]\n        is_aes128_enc = '#EXT-X-KEY:METHOD=AES-128' in manifest\n        check_results.append(can_decrypt_frag or not is_aes128_enc)\n        check_results.append(not (is_aes128_enc and r'#EXT-X-BYTERANGE' in manifest))\n        check_results.append(not info_dict.get('is_live'))\n        return all(check_results)\n\n    def real_download(self, filename, info_dict):\n        man_url = info_dict['url']\n        self.to_screen('[%s] Downloading m3u8 manifest' % self.FD_NAME)\n\n        urlh = self.ydl.urlopen(self._prepare_url(info_dict, man_url))\n        man_url = urlh.geturl()\n        s = urlh.read().decode('utf-8', 'ignore')\n\n        if not self.can_download(s, info_dict):\n            if info_dict.get('extra_param_to_segment_url') or info_dict.get('_decryption_key_url'):\n                self.report_error('pycrypto not found. Please install it.')\n                return False\n            self.report_warning(\n                'hlsnative has detected features it does not support, '\n                'extraction will be delegated to ffmpeg')\n            fd = FFmpegFD(self.ydl, self.params)\n            for ph in self._progress_hooks:\n                fd.add_progress_hook(ph)\n            return fd.real_download(filename, info_dict)\n\n        def is_ad_fragment_start(s):\n            return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=ad' in s\n                    or s.startswith('#UPLYNK-SEGMENT') and s.endswith(',ad'))\n\n        def is_ad_fragment_end(s):\n            return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=master' in s\n                    or s.startswith('#UPLYNK-SEGMENT') and s.endswith(',segment'))\n\n        media_frags = 0\n        ad_frags = 0\n        ad_frag_next = False\n        for line in s.splitlines():\n            line = line.strip()\n            if not line:\n                continue\n            if line.startswith('#'):\n                if is_ad_fragment_start(line):\n                    ad_frag_next = True\n                elif is_ad_fragment_end(line):\n                    ad_frag_next = False\n                continue\n            if ad_frag_next:\n                ad_frags += 1\n                continue\n            media_frags += 1\n\n        ctx = {\n            'filename': filename,\n            'total_frags': media_frags,\n            'ad_frags': ad_frags,\n        }\n\n        self._prepare_and_start_frag_download(ctx)\n\n        fragment_retries = self.params.get('fragment_retries', 0)\n        skip_unavailable_fragments = self.params.get('skip_unavailable_fragments', True)\n        test = self.params.get('test', False)\n\n        extra_query = None\n        extra_param_to_segment_url = info_dict.get('extra_param_to_segment_url')\n        if extra_param_to_segment_url:\n            extra_query = compat_urlparse.parse_qs(extra_param_to_segment_url)\n        i = 0\n        media_sequence = 0\n        decrypt_info = {'METHOD': 'NONE'}\n        byte_range = {}\n        frag_index = 0\n        ad_frag_next = False\n        for line in s.splitlines():\n            line = line.strip()\n            if line:\n                if not line.startswith('#'):\n                    if ad_frag_next:\n                        continue\n                    frag_index += 1\n                    if frag_index <= ctx['fragment_index']:\n                        continue\n                    frag_url = (\n                        line\n                        if re.match(r'^https?://', line)\n                        else compat_urlparse.urljoin(man_url, line))\n                    if extra_query:\n                        frag_url = update_url_query(frag_url, extra_query)\n                    count = 0\n                    headers = info_dict.get('http_headers', {})\n                    if byte_range:\n                        headers['Range'] = 'bytes=%d-%d' % (byte_range['start'], byte_range['end'])\n                    while count <= fragment_retries:\n                        try:\n                            success, frag_content = self._download_fragment(\n                                ctx, frag_url, info_dict, headers)\n                            if not success:\n                                return False\n                            break\n                        except compat_urllib_error.HTTPError as err:\n                            # Unavailable (possibly temporary) fragments may be served.\n                            # First we try to retry then either skip or abort.\n                            # See https://github.com/ytdl-org/youtube-dl/issues/10165,\n                            # https://github.com/ytdl-org/youtube-dl/issues/10448).\n                            count += 1\n                            if count <= fragment_retries:\n                                self.report_retry_fragment(err, frag_index, count, fragment_retries)\n                    if count > fragment_retries:\n                        if skip_unavailable_fragments:\n                            i += 1\n                            media_sequence += 1\n                            self.report_skip_fragment(frag_index)\n                            continue\n                        self.report_error(\n                            'giving up after %s fragment retries' % fragment_retries)\n                        return False\n                    if decrypt_info['METHOD'] == 'AES-128':\n                        iv = decrypt_info.get('IV') or compat_struct_pack('>8xq', media_sequence)\n                        decrypt_info['KEY'] = decrypt_info.get('KEY') or self.ydl.urlopen(\n                            self._prepare_url(info_dict, info_dict.get('_decryption_key_url') or decrypt_info['URI'])).read()\n                        frag_content = AES.new(\n                            decrypt_info['KEY'], AES.MODE_CBC, iv).decrypt(frag_content)\n                    self._append_fragment(ctx, frag_content)\n                    # We only download the first fragment during the test\n                    if test:\n                        break\n                    i += 1\n                    media_sequence += 1\n                elif line.startswith('#EXT-X-KEY'):\n                    decrypt_url = decrypt_info.get('URI')\n                    decrypt_info = parse_m3u8_attributes(line[11:])\n                    if decrypt_info['METHOD'] == 'AES-128':\n                        if 'IV' in decrypt_info:\n                            decrypt_info['IV'] = binascii.unhexlify(decrypt_info['IV'][2:].zfill(32))\n                        if not re.match(r'^https?://', decrypt_info['URI']):\n                            decrypt_info['URI'] = compat_urlparse.urljoin(\n                                man_url, decrypt_info['URI'])\n                        if extra_query:\n                            decrypt_info['URI'] = update_url_query(decrypt_info['URI'], extra_query)\n                        if decrypt_url != decrypt_info['URI']:\n                            decrypt_info['KEY'] = None\n                elif line.startswith('#EXT-X-MEDIA-SEQUENCE'):\n                    media_sequence = int(line[22:])\n                elif line.startswith('#EXT-X-BYTERANGE'):\n                    splitted_byte_range = line[17:].split('@')\n                    sub_range_start = int(splitted_byte_range[1]) if len(splitted_byte_range) == 2 else byte_range['end']\n                    byte_range = {\n                        'start': sub_range_start,\n                        'end': sub_range_start + int(splitted_byte_range[0]),\n                    }\n                elif is_ad_fragment_start(line):\n                    ad_frag_next = True\n                elif is_ad_fragment_end(line):\n                    ad_frag_next = False\n\n        self._finish_frag_download(ctx)\n\n        return True\n", "path": "youtube_dl/downloader/hls.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nimport re\nimport binascii\ntry:\n    from Crypto.Cipher import AES\n    can_decrypt_frag = True\nexcept ImportError:\n    can_decrypt_frag = False\n\nfrom .fragment import FragmentFD\nfrom .external import FFmpegFD\n\nfrom ..compat import (\n    compat_urllib_error,\n    compat_urlparse,\n    compat_struct_pack,\n)\nfrom ..utils import (\n    parse_m3u8_attributes,\n    update_url_query,\n)\n\n\nclass HlsFD(FragmentFD):\n    \"\"\" A limited implementation that does not require ffmpeg \"\"\"\n\n    FD_NAME = 'hlsnative'\n\n    @staticmethod\n    def can_download(manifest, info_dict):\n        UNSUPPORTED_FEATURES = (\n            r'#EXT-X-KEY:METHOD=(?!NONE|AES-128)',  # encrypted streams [1]\n            # r'#EXT-X-BYTERANGE',  # playlists composed of byte ranges of media files [2]\n\n            # Live streams heuristic does not always work (e.g. geo restricted to Germany\n            # http://hls-geo.daserste.de/i/videoportal/Film/c_620000/622873/format,716451,716457,716450,716458,716459,.mp4.csmil/index_4_av.m3u8?null=0)\n            # r'#EXT-X-MEDIA-SEQUENCE:(?!0$)',  # live streams [3]\n\n            # This heuristic also is not correct since segments may not be appended as well.\n            # Twitch vods of finished streams have EXT-X-PLAYLIST-TYPE:EVENT despite\n            # no segments will definitely be appended to the end of the playlist.\n            # r'#EXT-X-PLAYLIST-TYPE:EVENT',  # media segments may be appended to the end of\n            #                                 # event media playlists [4]\n\n            # 1. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.4\n            # 2. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.2\n            # 3. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.2\n            # 4. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.5\n        )\n        check_results = [not re.search(feature, manifest) for feature in UNSUPPORTED_FEATURES]\n        is_aes128_enc = '#EXT-X-KEY:METHOD=AES-128' in manifest\n        check_results.append(can_decrypt_frag or not is_aes128_enc)\n        check_results.append(not (is_aes128_enc and r'#EXT-X-BYTERANGE' in manifest))\n        check_results.append(not info_dict.get('is_live'))\n        return all(check_results)\n\n    def real_download(self, filename, info_dict):\n        man_url = info_dict['url']\n        self.to_screen('[%s] Downloading m3u8 manifest' % self.FD_NAME)\n\n        urlh = self.ydl.urlopen(self._prepare_url(info_dict, man_url))\n        man_url = urlh.geturl()\n        s = urlh.read().decode('utf-8', 'ignore')\n\n        if not self.can_download(s, info_dict):\n            if info_dict.get('extra_param_to_segment_url') or info_dict.get('_decryption_key_url'):\n                self.report_error('pycrypto not found. Please install it.')\n                return False\n            self.report_warning(\n                'hlsnative has detected features it does not support, '\n                'extraction will be delegated to ffmpeg')\n            fd = FFmpegFD(self.ydl, self.params)\n            for ph in self._progress_hooks:\n                fd.add_progress_hook(ph)\n            return fd.real_download(filename, info_dict)\n\n        def is_ad_fragment_start(s):\n            return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=ad' in s\n                    or s.startswith('#UPLYNK-SEGMENT') and s.endswith(',ad'))\n\n        def is_ad_fragment_end(s):\n            return (s.startswith('#ANVATO-SEGMENT-INFO') and 'type=master' in s\n                    or s.startswith('#UPLYNK-SEGMENT') and s.endswith(',segment'))\n\n        media_frags = 0\n        ad_frags = 0\n        ad_frag_next = False\n        for line in s.splitlines():\n            line = line.strip()\n            if not line:\n                continue\n            if line.startswith('#'):\n                if is_ad_fragment_start(line):\n                    ad_frag_next = True\n                elif is_ad_fragment_end(line):\n                    ad_frag_next = False\n                continue\n            if ad_frag_next:\n                ad_frags += 1\n                continue\n            media_frags += 1\n\n        ctx = {\n            'filename': filename,\n            'total_frags': media_frags,\n            'ad_frags': ad_frags,\n        }\n\n        self._prepare_and_start_frag_download(ctx)\n\n        fragment_retries = self.params.get('fragment_retries', 0)\n        skip_unavailable_fragments = self.params.get('skip_unavailable_fragments', True)\n        test = self.params.get('test', False)\n\n        extra_query = None\n        extra_param_to_segment_url = info_dict.get('extra_param_to_segment_url')\n        if extra_param_to_segment_url:\n            extra_query = compat_urlparse.parse_qs(extra_param_to_segment_url)\n        i = 0\n        media_sequence = 0\n        decrypt_info = {'METHOD': 'NONE'}\n        byte_range = {}\n        frag_index = 0\n        ad_frag_next = False\n        for line in s.splitlines():\n            line = line.strip()\n            if line:\n                if not line.startswith('#'):\n                    if ad_frag_next:\n                        continue\n                    frag_index += 1\n                    if frag_index <= ctx['fragment_index']:\n                        continue\n                    frag_url = (\n                        line\n                        if re.match(r'^https?://', line)\n                        else compat_urlparse.urljoin(man_url, line))\n                    if extra_query:\n                        frag_url = update_url_query(frag_url, extra_query)\n                    count = 0\n                    headers = info_dict.get('http_headers', {})\n                    if byte_range:\n                        headers['Range'] = 'bytes=%d-%d' % (byte_range['start'], byte_range['end'] - 1)\n                    while count <= fragment_retries:\n                        try:\n                            success, frag_content = self._download_fragment(\n                                ctx, frag_url, info_dict, headers)\n                            if not success:\n                                return False\n                            break\n                        except compat_urllib_error.HTTPError as err:\n                            # Unavailable (possibly temporary) fragments may be served.\n                            # First we try to retry then either skip or abort.\n                            # See https://github.com/ytdl-org/youtube-dl/issues/10165,\n                            # https://github.com/ytdl-org/youtube-dl/issues/10448).\n                            count += 1\n                            if count <= fragment_retries:\n                                self.report_retry_fragment(err, frag_index, count, fragment_retries)\n                    if count > fragment_retries:\n                        if skip_unavailable_fragments:\n                            i += 1\n                            media_sequence += 1\n                            self.report_skip_fragment(frag_index)\n                            continue\n                        self.report_error(\n                            'giving up after %s fragment retries' % fragment_retries)\n                        return False\n                    if decrypt_info['METHOD'] == 'AES-128':\n                        iv = decrypt_info.get('IV') or compat_struct_pack('>8xq', media_sequence)\n                        decrypt_info['KEY'] = decrypt_info.get('KEY') or self.ydl.urlopen(\n                            self._prepare_url(info_dict, info_dict.get('_decryption_key_url') or decrypt_info['URI'])).read()\n                        frag_content = AES.new(\n                            decrypt_info['KEY'], AES.MODE_CBC, iv).decrypt(frag_content)\n                    self._append_fragment(ctx, frag_content)\n                    # We only download the first fragment during the test\n                    if test:\n                        break\n                    i += 1\n                    media_sequence += 1\n                elif line.startswith('#EXT-X-KEY'):\n                    decrypt_url = decrypt_info.get('URI')\n                    decrypt_info = parse_m3u8_attributes(line[11:])\n                    if decrypt_info['METHOD'] == 'AES-128':\n                        if 'IV' in decrypt_info:\n                            decrypt_info['IV'] = binascii.unhexlify(decrypt_info['IV'][2:].zfill(32))\n                        if not re.match(r'^https?://', decrypt_info['URI']):\n                            decrypt_info['URI'] = compat_urlparse.urljoin(\n                                man_url, decrypt_info['URI'])\n                        if extra_query:\n                            decrypt_info['URI'] = update_url_query(decrypt_info['URI'], extra_query)\n                        if decrypt_url != decrypt_info['URI']:\n                            decrypt_info['KEY'] = None\n                elif line.startswith('#EXT-X-MEDIA-SEQUENCE'):\n                    media_sequence = int(line[22:])\n                elif line.startswith('#EXT-X-BYTERANGE'):\n                    splitted_byte_range = line[17:].split('@')\n                    sub_range_start = int(splitted_byte_range[1]) if len(splitted_byte_range) == 2 else byte_range['end']\n                    byte_range = {\n                        'start': sub_range_start,\n                        'end': sub_range_start + int(splitted_byte_range[0]),\n                    }\n                elif is_ad_fragment_start(line):\n                    ad_frag_next = True\n                elif is_ad_fragment_end(line):\n                    ad_frag_next = False\n\n        self._finish_frag_download(ctx)\n\n        return True\n", "path": "youtube_dl/downloader/hls.py"}]}