We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[cammodels] ExtractorError: Unable to find manifest URL root


- [x] I'm reporting a broken site support
- [x] I've verified that I'm running youtube-dl version **2019.10.16**
- [x] I've checked that all provided URLs are alive and playable in a browser
- [x] I've checked that all URLs and arguments with special characters are properly quoted or escaped
- [x] I've searched the bugtracker for similar issues including closed ones


## Verbose log
LATEST VERSION (broken):
```
# ./youtube-dl -vg https://www.cammodels.com/cam/agnesss
[debug] System config: []
[debug] User config: []
[debug] Custom config: []
[debug] Command-line args: [u'-vg', u'https://www.cammodels.com/cam/agnesss']
[debug] Encodings: locale UTF-8, fs UTF-8, out UTF-8, pref UTF-8
[debug] youtube-dl version 2019.10.16
[debug] Python version 2.7.16 (CPython) - Linux-5.3.6-050306-generic-x86_64-with-Ubuntu-19.04-disco
[debug] exe versions: ffmpeg 4.1.3, ffprobe 4.1.3, rtmpdump 2.4
[debug] Proxy map: {}
ERROR: Unable to find manifest URL root; please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; type  youtube-dl -U  to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.
Traceback (most recent call last):
  File "./youtube-dl/youtube_dl/YoutubeDL.py", line 796, in extract_info
    ie_result = ie.extract(url)
  File "./youtube-dl/youtube_dl/extractor/common.py", line 530, in extract
    ie_result = self._real_extract(url)
  File "./youtube-dl/youtube_dl/extractor/cammodels.py", line 43, in _real_extract
    raise ExtractorError(error, expected=expected)
ExtractorError: Unable to find manifest URL root; please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; type  youtube-dl -U  to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.
```
LAST KNOWN WORKING:
```
# ./youtube-dl-lastok -vg https://www.cammodels.com/cam/agnesss
[debug] System config: []
[debug] User config: []
[debug] Custom config: []
[debug] Command-line args: [u'-vg', u'https://www.cammodels.com/cam/agnesss']
[debug] Encodings: locale UTF-8, fs UTF-8, out UTF-8, pref UTF-8
[debug] youtube-dl version 2019.06.27
[debug] Python version 2.7.16 (CPython) - Linux-5.3.6-050306-generic-x86_64-with-Ubuntu-19.04-disco
[debug] exe versions: ffmpeg 4.1.3, ffprobe 4.1.3, rtmpdump 2.4
[debug] Proxy map: {}
[debug] Default format spec: bestvideo+bestaudio/best
rtmp://sea1c-edge-33.naiadsystems.com:1936/live/01ead87f-66cc-465c-9cca-5f91e2505708_2000_1280x720_56
```


## Description
* You need to use a model that is online: change the name in the URL accordingly
* One could make it work again by reverting the changes in utils.py ("random_user_agent"):
```
utils.py, line 1669:
   > 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0',
   < 'User-Agent': random_user_agent(),
 
```
* not sure if OS updates or web site changes made a difference because it has been working with a version of youtube-dl  _2019.06.27 < ok < 2019.09.28_ in September where utils.py was already the newer version
* no difference in using python2 or python3

Thanks for your work.

--- 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/cammodels.py`
Content:
```
1 # coding: utf-8
2 from __future__ import unicode_literals
3 
4 from .common import InfoExtractor
5 from ..utils import (
6     ExtractorError,
7     int_or_none,
8     url_or_none,
9 )
10 
11 
12 class CamModelsIE(InfoExtractor):
13     _VALID_URL = r'https?://(?:www\.)?cammodels\.com/cam/(?P<id>[^/?#&]+)'
14     _TESTS = [{
15         'url': 'https://www.cammodels.com/cam/AutumnKnight/',
16         'only_matching': True,
17         'age_limit': 18
18     }]
19 
20     def _real_extract(self, url):
21         user_id = self._match_id(url)
22 
23         webpage = self._download_webpage(
24             url, user_id, headers=self.geo_verification_headers())
25 
26         manifest_root = self._html_search_regex(
27             r'manifestUrlRoot=([^&\']+)', webpage, 'manifest', default=None)
28 
29         if not manifest_root:
30             ERRORS = (
31                 ("I'm offline, but let's stay connected", 'This user is currently offline'),
32                 ('in a private show', 'This user is in a private show'),
33                 ('is currently performing LIVE', 'This model is currently performing live'),
34             )
35             for pattern, message in ERRORS:
36                 if pattern in webpage:
37                     error = message
38                     expected = True
39                     break
40             else:
41                 error = 'Unable to find manifest URL root'
42                 expected = False
43             raise ExtractorError(error, expected=expected)
44 
45         manifest = self._download_json(
46             '%s%s.json' % (manifest_root, user_id), user_id)
47 
48         formats = []
49         for format_id, format_dict in manifest['formats'].items():
50             if not isinstance(format_dict, dict):
51                 continue
52             encodings = format_dict.get('encodings')
53             if not isinstance(encodings, list):
54                 continue
55             vcodec = format_dict.get('videoCodec')
56             acodec = format_dict.get('audioCodec')
57             for media in encodings:
58                 if not isinstance(media, dict):
59                     continue
60                 media_url = url_or_none(media.get('location'))
61                 if not media_url:
62                     continue
63 
64                 format_id_list = [format_id]
65                 height = int_or_none(media.get('videoHeight'))
66                 if height is not None:
67                     format_id_list.append('%dp' % height)
68                 f = {
69                     'url': media_url,
70                     'format_id': '-'.join(format_id_list),
71                     'width': int_or_none(media.get('videoWidth')),
72                     'height': height,
73                     'vbr': int_or_none(media.get('videoKbps')),
74                     'abr': int_or_none(media.get('audioKbps')),
75                     'fps': int_or_none(media.get('fps')),
76                     'vcodec': vcodec,
77                     'acodec': acodec,
78                 }
79                 if 'rtmp' in format_id:
80                     f['ext'] = 'flv'
81                 elif 'hls' in format_id:
82                     f.update({
83                         'ext': 'mp4',
84                         # hls skips fragments, preferring rtmp
85                         'preference': -1,
86                     })
87                 else:
88                     continue
89                 formats.append(f)
90         self._sort_formats(formats)
91 
92         return {
93             'id': user_id,
94             'title': self._live_title(user_id),
95             'is_live': True,
96             'formats': formats,
97             'age_limit': 18
98         }
99
```
--- 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/cammodels.py b/youtube_dl/extractor/cammodels.py
--- a/youtube_dl/extractor/cammodels.py
+++ b/youtube_dl/extractor/cammodels.py
@@ -3,7 +3,6 @@
 
 from .common import InfoExtractor
 from ..utils import (
-    ExtractorError,
     int_or_none,
     url_or_none,
 )
@@ -20,32 +19,11 @@
     def _real_extract(self, url):
         user_id = self._match_id(url)
 
-        webpage = self._download_webpage(
-            url, user_id, headers=self.geo_verification_headers())
-
-        manifest_root = self._html_search_regex(
-            r'manifestUrlRoot=([^&\']+)', webpage, 'manifest', default=None)
-
-        if not manifest_root:
-            ERRORS = (
-                ("I'm offline, but let's stay connected", 'This user is currently offline'),
-                ('in a private show', 'This user is in a private show'),
-                ('is currently performing LIVE', 'This model is currently performing live'),
-            )
-            for pattern, message in ERRORS:
-                if pattern in webpage:
-                    error = message
-                    expected = True
-                    break
-            else:
-                error = 'Unable to find manifest URL root'
-                expected = False
-            raise ExtractorError(error, expected=expected)
-
         manifest = self._download_json(
-            '%s%s.json' % (manifest_root, user_id), user_id)
+            'https://manifest-server.naiadsystems.com/live/s:%s.json' % user_id, user_id)
 
         formats = []
+        thumbnails = []
         for format_id, format_dict in manifest['formats'].items():
             if not isinstance(format_dict, dict):
                 continue
@@ -85,6 +63,13 @@
                         'preference': -1,
                     })
                 else:
+                    if format_id == 'jpeg':
+                        thumbnails.append({
+                            'url': f['url'],
+                            'width': f['width'],
+                            'height': f['height'],
+                            'format_id': f['format_id'],
+                        })
                     continue
                 formats.append(f)
         self._sort_formats(formats)
@@ -92,6 +77,7 @@
         return {
             'id': user_id,
             'title': self._live_title(user_id),
+            'thumbnails': thumbnails,
             'is_live': True,
             'formats': formats,
             'age_limit': 18

{"golden_diff": "diff --git a/youtube_dl/extractor/cammodels.py b/youtube_dl/extractor/cammodels.py\n--- a/youtube_dl/extractor/cammodels.py\n+++ b/youtube_dl/extractor/cammodels.py\n@@ -3,7 +3,6 @@\n \n from .common import InfoExtractor\n from ..utils import (\n-    ExtractorError,\n     int_or_none,\n     url_or_none,\n )\n@@ -20,32 +19,11 @@\n     def _real_extract(self, url):\n         user_id = self._match_id(url)\n \n-        webpage = self._download_webpage(\n-            url, user_id, headers=self.geo_verification_headers())\n-\n-        manifest_root = self._html_search_regex(\n-            r'manifestUrlRoot=([^&\\']+)', webpage, 'manifest', default=None)\n-\n-        if not manifest_root:\n-            ERRORS = (\n-                (\"I'm offline, but let's stay connected\", 'This user is currently offline'),\n-                ('in a private show', 'This user is in a private show'),\n-                ('is currently performing LIVE', 'This model is currently performing live'),\n-            )\n-            for pattern, message in ERRORS:\n-                if pattern in webpage:\n-                    error = message\n-                    expected = True\n-                    break\n-            else:\n-                error = 'Unable to find manifest URL root'\n-                expected = False\n-            raise ExtractorError(error, expected=expected)\n-\n         manifest = self._download_json(\n-            '%s%s.json' % (manifest_root, user_id), user_id)\n+            'https://manifest-server.naiadsystems.com/live/s:%s.json' % user_id, user_id)\n \n         formats = []\n+        thumbnails = []\n         for format_id, format_dict in manifest['formats'].items():\n             if not isinstance(format_dict, dict):\n                 continue\n@@ -85,6 +63,13 @@\n                         'preference': -1,\n                     })\n                 else:\n+                    if format_id == 'jpeg':\n+                        thumbnails.append({\n+                            'url': f['url'],\n+                            'width': f['width'],\n+                            'height': f['height'],\n+                            'format_id': f['format_id'],\n+                        })\n                     continue\n                 formats.append(f)\n         self._sort_formats(formats)\n@@ -92,6 +77,7 @@\n         return {\n             'id': user_id,\n             'title': self._live_title(user_id),\n+            'thumbnails': thumbnails,\n             'is_live': True,\n             'formats': formats,\n             'age_limit': 18\n", "issue": "[cammodels] ExtractorError: Unable to find manifest URL root\n\r\n\r\n- [x] I'm reporting a broken site support\r\n- [x] I've verified that I'm running youtube-dl version **2019.10.16**\r\n- [x] I've checked that all provided URLs are alive and playable in a browser\r\n- [x] I've checked that all URLs and arguments with special characters are properly quoted or escaped\r\n- [x] I've searched the bugtracker for similar issues including closed ones\r\n\r\n\r\n## Verbose log\r\nLATEST VERSION (broken):\r\n```\r\n# ./youtube-dl -vg https://www.cammodels.com/cam/agnesss\r\n[debug] System config: []\r\n[debug] User config: []\r\n[debug] Custom config: []\r\n[debug] Command-line args: [u'-vg', u'https://www.cammodels.com/cam/agnesss']\r\n[debug] Encodings: locale UTF-8, fs UTF-8, out UTF-8, pref UTF-8\r\n[debug] youtube-dl version 2019.10.16\r\n[debug] Python version 2.7.16 (CPython) - Linux-5.3.6-050306-generic-x86_64-with-Ubuntu-19.04-disco\r\n[debug] exe versions: ffmpeg 4.1.3, ffprobe 4.1.3, rtmpdump 2.4\r\n[debug] Proxy map: {}\r\nERROR: Unable to find manifest URL root; please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; type  youtube-dl -U  to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.\r\nTraceback (most recent call last):\r\n  File \"./youtube-dl/youtube_dl/YoutubeDL.py\", line 796, in extract_info\r\n    ie_result = ie.extract(url)\r\n  File \"./youtube-dl/youtube_dl/extractor/common.py\", line 530, in extract\r\n    ie_result = self._real_extract(url)\r\n  File \"./youtube-dl/youtube_dl/extractor/cammodels.py\", line 43, in _real_extract\r\n    raise ExtractorError(error, expected=expected)\r\nExtractorError: Unable to find manifest URL root; please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; type  youtube-dl -U  to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.\r\n```\r\nLAST KNOWN WORKING:\r\n```\r\n# ./youtube-dl-lastok -vg https://www.cammodels.com/cam/agnesss\r\n[debug] System config: []\r\n[debug] User config: []\r\n[debug] Custom config: []\r\n[debug] Command-line args: [u'-vg', u'https://www.cammodels.com/cam/agnesss']\r\n[debug] Encodings: locale UTF-8, fs UTF-8, out UTF-8, pref UTF-8\r\n[debug] youtube-dl version 2019.06.27\r\n[debug] Python version 2.7.16 (CPython) - Linux-5.3.6-050306-generic-x86_64-with-Ubuntu-19.04-disco\r\n[debug] exe versions: ffmpeg 4.1.3, ffprobe 4.1.3, rtmpdump 2.4\r\n[debug] Proxy map: {}\r\n[debug] Default format spec: bestvideo+bestaudio/best\r\nrtmp://sea1c-edge-33.naiadsystems.com:1936/live/01ead87f-66cc-465c-9cca-5f91e2505708_2000_1280x720_56\r\n```\r\n\r\n\r\n## Description\r\n* You need to use a model that is online: change the name in the URL accordingly\r\n* One could make it work again by reverting the changes in utils.py (\"random_user_agent\"):\r\n```\r\nutils.py, line 1669:\r\n   > 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0',\r\n   < 'User-Agent': random_user_agent(),\r\n \r\n```\r\n* not sure if OS updates or web site changes made a difference because it has been working with a version of youtube-dl  _2019.06.27 < ok < 2019.09.28_ in September where utils.py was already the newer version\r\n* no difference in using python2 or python3\r\n\r\nThanks for your work.\n", "before_files": [{"content": "# coding: utf-8\nfrom __future__ import unicode_literals\n\nfrom .common import InfoExtractor\nfrom ..utils import (\n    ExtractorError,\n    int_or_none,\n    url_or_none,\n)\n\n\nclass CamModelsIE(InfoExtractor):\n    _VALID_URL = r'https?://(?:www\\.)?cammodels\\.com/cam/(?P<id>[^/?#&]+)'\n    _TESTS = [{\n        'url': 'https://www.cammodels.com/cam/AutumnKnight/',\n        'only_matching': True,\n        'age_limit': 18\n    }]\n\n    def _real_extract(self, url):\n        user_id = self._match_id(url)\n\n        webpage = self._download_webpage(\n            url, user_id, headers=self.geo_verification_headers())\n\n        manifest_root = self._html_search_regex(\n            r'manifestUrlRoot=([^&\\']+)', webpage, 'manifest', default=None)\n\n        if not manifest_root:\n            ERRORS = (\n                (\"I'm offline, but let's stay connected\", 'This user is currently offline'),\n                ('in a private show', 'This user is in a private show'),\n                ('is currently performing LIVE', 'This model is currently performing live'),\n            )\n            for pattern, message in ERRORS:\n                if pattern in webpage:\n                    error = message\n                    expected = True\n                    break\n            else:\n                error = 'Unable to find manifest URL root'\n                expected = False\n            raise ExtractorError(error, expected=expected)\n\n        manifest = self._download_json(\n            '%s%s.json' % (manifest_root, user_id), user_id)\n\n        formats = []\n        for format_id, format_dict in manifest['formats'].items():\n            if not isinstance(format_dict, dict):\n                continue\n            encodings = format_dict.get('encodings')\n            if not isinstance(encodings, list):\n                continue\n            vcodec = format_dict.get('videoCodec')\n            acodec = format_dict.get('audioCodec')\n            for media in encodings:\n                if not isinstance(media, dict):\n                    continue\n                media_url = url_or_none(media.get('location'))\n                if not media_url:\n                    continue\n\n                format_id_list = [format_id]\n                height = int_or_none(media.get('videoHeight'))\n                if height is not None:\n                    format_id_list.append('%dp' % height)\n                f = {\n                    'url': media_url,\n                    'format_id': '-'.join(format_id_list),\n                    'width': int_or_none(media.get('videoWidth')),\n                    'height': height,\n                    'vbr': int_or_none(media.get('videoKbps')),\n                    'abr': int_or_none(media.get('audioKbps')),\n                    'fps': int_or_none(media.get('fps')),\n                    'vcodec': vcodec,\n                    'acodec': acodec,\n                }\n                if 'rtmp' in format_id:\n                    f['ext'] = 'flv'\n                elif 'hls' in format_id:\n                    f.update({\n                        'ext': 'mp4',\n                        # hls skips fragments, preferring rtmp\n                        'preference': -1,\n                    })\n                else:\n                    continue\n                formats.append(f)\n        self._sort_formats(formats)\n\n        return {\n            'id': user_id,\n            'title': self._live_title(user_id),\n            'is_live': True,\n            'formats': formats,\n            'age_limit': 18\n        }\n", "path": "youtube_dl/extractor/cammodels.py"}], "after_files": [{"content": "# coding: utf-8\nfrom __future__ import unicode_literals\n\nfrom .common import InfoExtractor\nfrom ..utils import (\n    int_or_none,\n    url_or_none,\n)\n\n\nclass CamModelsIE(InfoExtractor):\n    _VALID_URL = r'https?://(?:www\\.)?cammodels\\.com/cam/(?P<id>[^/?#&]+)'\n    _TESTS = [{\n        'url': 'https://www.cammodels.com/cam/AutumnKnight/',\n        'only_matching': True,\n        'age_limit': 18\n    }]\n\n    def _real_extract(self, url):\n        user_id = self._match_id(url)\n\n        manifest = self._download_json(\n            'https://manifest-server.naiadsystems.com/live/s:%s.json' % user_id, user_id)\n\n        formats = []\n        thumbnails = []\n        for format_id, format_dict in manifest['formats'].items():\n            if not isinstance(format_dict, dict):\n                continue\n            encodings = format_dict.get('encodings')\n            if not isinstance(encodings, list):\n                continue\n            vcodec = format_dict.get('videoCodec')\n            acodec = format_dict.get('audioCodec')\n            for media in encodings:\n                if not isinstance(media, dict):\n                    continue\n                media_url = url_or_none(media.get('location'))\n                if not media_url:\n                    continue\n\n                format_id_list = [format_id]\n                height = int_or_none(media.get('videoHeight'))\n                if height is not None:\n                    format_id_list.append('%dp' % height)\n                f = {\n                    'url': media_url,\n                    'format_id': '-'.join(format_id_list),\n                    'width': int_or_none(media.get('videoWidth')),\n                    'height': height,\n                    'vbr': int_or_none(media.get('videoKbps')),\n                    'abr': int_or_none(media.get('audioKbps')),\n                    'fps': int_or_none(media.get('fps')),\n                    'vcodec': vcodec,\n                    'acodec': acodec,\n                }\n                if 'rtmp' in format_id:\n                    f['ext'] = 'flv'\n                elif 'hls' in format_id:\n                    f.update({\n                        'ext': 'mp4',\n                        # hls skips fragments, preferring rtmp\n                        'preference': -1,\n                    })\n                else:\n                    if format_id == 'jpeg':\n                        thumbnails.append({\n                            'url': f['url'],\n                            'width': f['width'],\n                            'height': f['height'],\n                            'format_id': f['format_id'],\n                        })\n                    continue\n                formats.append(f)\n        self._sort_formats(formats)\n\n        return {\n            'id': user_id,\n            'title': self._live_title(user_id),\n            'thumbnails': thumbnails,\n            'is_live': True,\n            'formats': formats,\n            'age_limit': 18\n        }\n", "path": "youtube_dl/extractor/cammodels.py"}]}