We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Jamando: 'JamendoIE' object has no attribute '_VALID_URL_RE'
### Checklist

- [X] I'm reporting a broken site
- [X] I've verified that I'm running yt-dlp version **2021.12.01**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))
- [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](https://github.com/ytdl-org/youtube-dl#video-url-contains-an-ampersand-and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
- [X] I've searched the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues including closed ones. DO NOT post duplicates
- [X] I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
- [X] I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and I'm willing to share it if required

### Region

wordwide

### Description

```
yt-dlp https://www.jamendo.com/track/1885651/to-aurora
ERROR: 'JamendoIE' object has no attribute '_VALID_URL_RE'

yt-dlp https://www.jamendo.com/track/1848421/badly
ERROR: 'JamendoIE' object has no attribute '_VALID_URL_RE'
```

## Relevant lines
https://github.com/yt-dlp/yt-dlp/blob/1117579b9457f8fbf7a4d7433a92b67ac802bdea/yt_dlp/extractor/jamendo.py#L17

https://github.com/yt-dlp/yt-dlp/blob/1117579b9457f8fbf7a4d7433a92b67ac802bdea/yt_dlp/extractor/jamendo.py#L62

https://github.com/yt-dlp/yt-dlp/blob/ee8dd27a7351841e1de8cebf8311b69fbef09eab/yt_dlp/extractor/common.py#L463-L470

### Verbose log

```shell
yt-dlp -v https://www.jamendo.com/track/1848421/badly[debug] Command-line config: ['-v', 'https://www.jamendo.com/track/1848421/badly']
[debug] Encodings: locale UTF-8, fs utf-8, out utf-8, err utf-8, pref UTF-8
[debug] yt-dlp version 2021.12.01 [91f071af6]
[debug] Python version 3.9.7 (CPython 64bit) - Linux-5.10.79-1-MANJARO-x86_64-with-glibc2.33
[debug] exe versions: ffmpeg 4.4.1 (setts), ffprobe 4.4.1, rtmpdump 2.4
[debug] Optional libraries: Cryptodome, keyring, mutagen, sqlite, websockets
[debug] Proxy map: {}
[debug] [Jamendo] Extracting URL: https://www.jamendo.com/track/1848421/badly
ERROR: 'JamendoIE' object has no attribute '_VALID_URL_RE'
Traceback (most recent call last):
  File "/home/jaller94/.local/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 1329, in wrapper
    return func(self, *args, **kwargs)
  File "/home/jaller94/.local/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py", line 1398, in __extract_info
    ie_result = ie.extract(url)
  File "/home/jaller94/.local/lib/python3.9/site-packages/yt_dlp/extractor/common.py", line 597, in extract
    ie_result = self._real_extract(url)
  File "/home/jaller94/.local/lib/python3.9/site-packages/yt_dlp/extractor/jamendo.py", line 62, in _real_extract
    track_id, display_id = self._VALID_URL_RE.match(url).groups()
AttributeError: 'JamendoIE' object has no attribute '_VALID_URL_RE'
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `yt_dlp/extractor/jamendo.py`
Content:
```
1 # coding: utf-8
2 from __future__ import unicode_literals
3 
4 import hashlib
5 import random
6 
7 from ..compat import compat_str
8 from .common import InfoExtractor
9 from ..utils import (
10     clean_html,
11     int_or_none,
12     try_get,
13 )
14 
15 
16 class JamendoIE(InfoExtractor):
17     _VALID_URL = r'''(?x)
18                     https?://
19                         (?:
20                             licensing\.jamendo\.com/[^/]+|
21                             (?:www\.)?jamendo\.com
22                         )
23                         /track/(?P<id>[0-9]+)(?:/(?P<display_id>[^/?#&]+))?
24                     '''
25     _TESTS = [{
26         'url': 'https://www.jamendo.com/track/196219/stories-from-emona-i',
27         'md5': '6e9e82ed6db98678f171c25a8ed09ffd',
28         'info_dict': {
29             'id': '196219',
30             'display_id': 'stories-from-emona-i',
31             'ext': 'flac',
32             # 'title': 'Maya Filipič - Stories from Emona I',
33             'title': 'Stories from Emona I',
34             # 'artist': 'Maya Filipič',
35             'track': 'Stories from Emona I',
36             'duration': 210,
37             'thumbnail': r're:^https?://.*\.jpg',
38             'timestamp': 1217438117,
39             'upload_date': '20080730',
40             'license': 'by-nc-nd',
41             'view_count': int,
42             'like_count': int,
43             'average_rating': int,
44             'tags': ['piano', 'peaceful', 'newage', 'strings', 'upbeat'],
45         }
46     }, {
47         'url': 'https://licensing.jamendo.com/en/track/1496667/energetic-rock',
48         'only_matching': True,
49     }]
50 
51     def _call_api(self, resource, resource_id):
52         path = '/api/%ss' % resource
53         rand = compat_str(random.random())
54         return self._download_json(
55             'https://www.jamendo.com' + path, resource_id, query={
56                 'id[]': resource_id,
57             }, headers={
58                 'X-Jam-Call': '$%s*%s~' % (hashlib.sha1((path + rand).encode()).hexdigest(), rand)
59             })[0]
60 
61     def _real_extract(self, url):
62         track_id, display_id = self._VALID_URL_RE.match(url).groups()
63         # webpage = self._download_webpage(
64         #     'https://www.jamendo.com/track/' + track_id, track_id)
65         # models = self._parse_json(self._html_search_regex(
66         #     r"data-bundled-models='([^']+)",
67         #     webpage, 'bundled models'), track_id)
68         # track = models['track']['models'][0]
69         track = self._call_api('track', track_id)
70         title = track_name = track['name']
71         # get_model = lambda x: try_get(models, lambda y: y[x]['models'][0], dict) or {}
72         # artist = get_model('artist')
73         # artist_name = artist.get('name')
74         # if artist_name:
75         #     title = '%s - %s' % (artist_name, title)
76         # album = get_model('album')
77 
78         formats = [{
79             'url': 'https://%s.jamendo.com/?trackid=%s&format=%s&from=app-97dab294'
80                    % (sub_domain, track_id, format_id),
81             'format_id': format_id,
82             'ext': ext,
83             'quality': quality,
84         } for quality, (format_id, sub_domain, ext) in enumerate((
85             ('mp31', 'mp3l', 'mp3'),
86             ('mp32', 'mp3d', 'mp3'),
87             ('ogg1', 'ogg', 'ogg'),
88             ('flac', 'flac', 'flac'),
89         ))]
90         self._sort_formats(formats)
91 
92         urls = []
93         thumbnails = []
94         for covers in (track.get('cover') or {}).values():
95             for cover_id, cover_url in covers.items():
96                 if not cover_url or cover_url in urls:
97                     continue
98                 urls.append(cover_url)
99                 size = int_or_none(cover_id.lstrip('size'))
100                 thumbnails.append({
101                     'id': cover_id,
102                     'url': cover_url,
103                     'width': size,
104                     'height': size,
105                 })
106 
107         tags = []
108         for tag in (track.get('tags') or []):
109             tag_name = tag.get('name')
110             if not tag_name:
111                 continue
112             tags.append(tag_name)
113 
114         stats = track.get('stats') or {}
115         license = track.get('licenseCC') or []
116 
117         return {
118             'id': track_id,
119             'display_id': display_id,
120             'thumbnails': thumbnails,
121             'title': title,
122             'description': track.get('description'),
123             'duration': int_or_none(track.get('duration')),
124             # 'artist': artist_name,
125             'track': track_name,
126             # 'album': album.get('name'),
127             'formats': formats,
128             'license': '-'.join(license) if license else None,
129             'timestamp': int_or_none(track.get('dateCreated')),
130             'view_count': int_or_none(stats.get('listenedAll')),
131             'like_count': int_or_none(stats.get('favorited')),
132             'average_rating': int_or_none(stats.get('averageNote')),
133             'tags': tags,
134         }
135 
136 
137 class JamendoAlbumIE(JamendoIE):
138     _VALID_URL = r'https?://(?:www\.)?jamendo\.com/album/(?P<id>[0-9]+)'
139     _TESTS = [{
140         'url': 'https://www.jamendo.com/album/121486/duck-on-cover',
141         'info_dict': {
142             'id': '121486',
143             'title': 'Duck On Cover',
144             'description': 'md5:c2920eaeef07d7af5b96d7c64daf1239',
145         },
146         'playlist': [{
147             'md5': 'e1a2fcb42bda30dfac990212924149a8',
148             'info_dict': {
149                 'id': '1032333',
150                 'ext': 'flac',
151                 'title': 'Shearer - Warmachine',
152                 'artist': 'Shearer',
153                 'track': 'Warmachine',
154                 'timestamp': 1368089771,
155                 'upload_date': '20130509',
156             }
157         }, {
158             'md5': '1f358d7b2f98edfe90fd55dac0799d50',
159             'info_dict': {
160                 'id': '1032330',
161                 'ext': 'flac',
162                 'title': 'Shearer - Without Your Ghost',
163                 'artist': 'Shearer',
164                 'track': 'Without Your Ghost',
165                 'timestamp': 1368089771,
166                 'upload_date': '20130509',
167             }
168         }],
169         'params': {
170             'playlistend': 2
171         }
172     }]
173 
174     def _real_extract(self, url):
175         album_id = self._match_id(url)
176         album = self._call_api('album', album_id)
177         album_name = album.get('name')
178 
179         entries = []
180         for track in (album.get('tracks') or []):
181             track_id = track.get('id')
182             if not track_id:
183                 continue
184             track_id = compat_str(track_id)
185             entries.append({
186                 '_type': 'url_transparent',
187                 'url': 'https://www.jamendo.com/track/' + track_id,
188                 'ie_key': JamendoIE.ie_key(),
189                 'id': track_id,
190                 'album': album_name,
191             })
192 
193         return self.playlist_result(
194             entries, album_id, album_name,
195             clean_html(try_get(album, lambda x: x['description']['en'], compat_str)))
196
```
--- 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/yt_dlp/extractor/jamendo.py b/yt_dlp/extractor/jamendo.py
--- a/yt_dlp/extractor/jamendo.py
+++ b/yt_dlp/extractor/jamendo.py
@@ -59,7 +59,7 @@
             })[0]
 
     def _real_extract(self, url):
-        track_id, display_id = self._VALID_URL_RE.match(url).groups()
+        track_id, display_id = self._match_valid_url(url).groups()
         # webpage = self._download_webpage(
         #     'https://www.jamendo.com/track/' + track_id, track_id)
         # models = self._parse_json(self._html_search_regex(

{"golden_diff": "diff --git a/yt_dlp/extractor/jamendo.py b/yt_dlp/extractor/jamendo.py\n--- a/yt_dlp/extractor/jamendo.py\n+++ b/yt_dlp/extractor/jamendo.py\n@@ -59,7 +59,7 @@\n             })[0]\n \n     def _real_extract(self, url):\n-        track_id, display_id = self._VALID_URL_RE.match(url).groups()\n+        track_id, display_id = self._match_valid_url(url).groups()\n         # webpage = self._download_webpage(\n         #     'https://www.jamendo.com/track/' + track_id, track_id)\n         # models = self._parse_json(self._html_search_regex(\n", "issue": "Jamando: 'JamendoIE' object has no attribute '_VALID_URL_RE'\n### Checklist\n\n- [X] I'm reporting a broken site\n- [X] I've verified that I'm running yt-dlp version **2021.12.01**. ([update instructions](https://github.com/yt-dlp/yt-dlp#update))\n- [X] I've checked that all provided URLs are alive and playable in a browser\n- [X] I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/ytdl-org/youtube-dl#video-url-contains-an-ampersand-and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)\n- [X] I've searched the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues including closed ones. DO NOT post duplicates\n- [X] I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)\n- [X] I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and I'm willing to share it if required\n\n### Region\n\nwordwide\n\n### Description\n\n```\r\nyt-dlp https://www.jamendo.com/track/1885651/to-aurora\r\nERROR: 'JamendoIE' object has no attribute '_VALID_URL_RE'\r\n\r\nyt-dlp https://www.jamendo.com/track/1848421/badly\r\nERROR: 'JamendoIE' object has no attribute '_VALID_URL_RE'\r\n```\r\n\r\n## Relevant lines\r\nhttps://github.com/yt-dlp/yt-dlp/blob/1117579b9457f8fbf7a4d7433a92b67ac802bdea/yt_dlp/extractor/jamendo.py#L17\r\n\r\nhttps://github.com/yt-dlp/yt-dlp/blob/1117579b9457f8fbf7a4d7433a92b67ac802bdea/yt_dlp/extractor/jamendo.py#L62\r\n\r\nhttps://github.com/yt-dlp/yt-dlp/blob/ee8dd27a7351841e1de8cebf8311b69fbef09eab/yt_dlp/extractor/common.py#L463-L470\n\n### Verbose log\n\n```shell\nyt-dlp -v https://www.jamendo.com/track/1848421/badly[debug] Command-line config: ['-v', 'https://www.jamendo.com/track/1848421/badly']\r\n[debug] Encodings: locale UTF-8, fs utf-8, out utf-8, err utf-8, pref UTF-8\r\n[debug] yt-dlp version 2021.12.01 [91f071af6]\r\n[debug] Python version 3.9.7 (CPython 64bit) - Linux-5.10.79-1-MANJARO-x86_64-with-glibc2.33\r\n[debug] exe versions: ffmpeg 4.4.1 (setts), ffprobe 4.4.1, rtmpdump 2.4\r\n[debug] Optional libraries: Cryptodome, keyring, mutagen, sqlite, websockets\r\n[debug] Proxy map: {}\r\n[debug] [Jamendo] Extracting URL: https://www.jamendo.com/track/1848421/badly\r\nERROR: 'JamendoIE' object has no attribute '_VALID_URL_RE'\r\nTraceback (most recent call last):\r\n  File \"/home/jaller94/.local/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py\", line 1329, in wrapper\r\n    return func(self, *args, **kwargs)\r\n  File \"/home/jaller94/.local/lib/python3.9/site-packages/yt_dlp/YoutubeDL.py\", line 1398, in __extract_info\r\n    ie_result = ie.extract(url)\r\n  File \"/home/jaller94/.local/lib/python3.9/site-packages/yt_dlp/extractor/common.py\", line 597, in extract\r\n    ie_result = self._real_extract(url)\r\n  File \"/home/jaller94/.local/lib/python3.9/site-packages/yt_dlp/extractor/jamendo.py\", line 62, in _real_extract\r\n    track_id, display_id = self._VALID_URL_RE.match(url).groups()\r\nAttributeError: 'JamendoIE' object has no attribute '_VALID_URL_RE'\n```\n\n", "before_files": [{"content": "# coding: utf-8\nfrom __future__ import unicode_literals\n\nimport hashlib\nimport random\n\nfrom ..compat import compat_str\nfrom .common import InfoExtractor\nfrom ..utils import (\n    clean_html,\n    int_or_none,\n    try_get,\n)\n\n\nclass JamendoIE(InfoExtractor):\n    _VALID_URL = r'''(?x)\n                    https?://\n                        (?:\n                            licensing\\.jamendo\\.com/[^/]+|\n                            (?:www\\.)?jamendo\\.com\n                        )\n                        /track/(?P<id>[0-9]+)(?:/(?P<display_id>[^/?#&]+))?\n                    '''\n    _TESTS = [{\n        'url': 'https://www.jamendo.com/track/196219/stories-from-emona-i',\n        'md5': '6e9e82ed6db98678f171c25a8ed09ffd',\n        'info_dict': {\n            'id': '196219',\n            'display_id': 'stories-from-emona-i',\n            'ext': 'flac',\n            # 'title': 'Maya Filipi\u010d - Stories from Emona I',\n            'title': 'Stories from Emona I',\n            # 'artist': 'Maya Filipi\u010d',\n            'track': 'Stories from Emona I',\n            'duration': 210,\n            'thumbnail': r're:^https?://.*\\.jpg',\n            'timestamp': 1217438117,\n            'upload_date': '20080730',\n            'license': 'by-nc-nd',\n            'view_count': int,\n            'like_count': int,\n            'average_rating': int,\n            'tags': ['piano', 'peaceful', 'newage', 'strings', 'upbeat'],\n        }\n    }, {\n        'url': 'https://licensing.jamendo.com/en/track/1496667/energetic-rock',\n        'only_matching': True,\n    }]\n\n    def _call_api(self, resource, resource_id):\n        path = '/api/%ss' % resource\n        rand = compat_str(random.random())\n        return self._download_json(\n            'https://www.jamendo.com' + path, resource_id, query={\n                'id[]': resource_id,\n            }, headers={\n                'X-Jam-Call': '$%s*%s~' % (hashlib.sha1((path + rand).encode()).hexdigest(), rand)\n            })[0]\n\n    def _real_extract(self, url):\n        track_id, display_id = self._VALID_URL_RE.match(url).groups()\n        # webpage = self._download_webpage(\n        #     'https://www.jamendo.com/track/' + track_id, track_id)\n        # models = self._parse_json(self._html_search_regex(\n        #     r\"data-bundled-models='([^']+)\",\n        #     webpage, 'bundled models'), track_id)\n        # track = models['track']['models'][0]\n        track = self._call_api('track', track_id)\n        title = track_name = track['name']\n        # get_model = lambda x: try_get(models, lambda y: y[x]['models'][0], dict) or {}\n        # artist = get_model('artist')\n        # artist_name = artist.get('name')\n        # if artist_name:\n        #     title = '%s - %s' % (artist_name, title)\n        # album = get_model('album')\n\n        formats = [{\n            'url': 'https://%s.jamendo.com/?trackid=%s&format=%s&from=app-97dab294'\n                   % (sub_domain, track_id, format_id),\n            'format_id': format_id,\n            'ext': ext,\n            'quality': quality,\n        } for quality, (format_id, sub_domain, ext) in enumerate((\n            ('mp31', 'mp3l', 'mp3'),\n            ('mp32', 'mp3d', 'mp3'),\n            ('ogg1', 'ogg', 'ogg'),\n            ('flac', 'flac', 'flac'),\n        ))]\n        self._sort_formats(formats)\n\n        urls = []\n        thumbnails = []\n        for covers in (track.get('cover') or {}).values():\n            for cover_id, cover_url in covers.items():\n                if not cover_url or cover_url in urls:\n                    continue\n                urls.append(cover_url)\n                size = int_or_none(cover_id.lstrip('size'))\n                thumbnails.append({\n                    'id': cover_id,\n                    'url': cover_url,\n                    'width': size,\n                    'height': size,\n                })\n\n        tags = []\n        for tag in (track.get('tags') or []):\n            tag_name = tag.get('name')\n            if not tag_name:\n                continue\n            tags.append(tag_name)\n\n        stats = track.get('stats') or {}\n        license = track.get('licenseCC') or []\n\n        return {\n            'id': track_id,\n            'display_id': display_id,\n            'thumbnails': thumbnails,\n            'title': title,\n            'description': track.get('description'),\n            'duration': int_or_none(track.get('duration')),\n            # 'artist': artist_name,\n            'track': track_name,\n            # 'album': album.get('name'),\n            'formats': formats,\n            'license': '-'.join(license) if license else None,\n            'timestamp': int_or_none(track.get('dateCreated')),\n            'view_count': int_or_none(stats.get('listenedAll')),\n            'like_count': int_or_none(stats.get('favorited')),\n            'average_rating': int_or_none(stats.get('averageNote')),\n            'tags': tags,\n        }\n\n\nclass JamendoAlbumIE(JamendoIE):\n    _VALID_URL = r'https?://(?:www\\.)?jamendo\\.com/album/(?P<id>[0-9]+)'\n    _TESTS = [{\n        'url': 'https://www.jamendo.com/album/121486/duck-on-cover',\n        'info_dict': {\n            'id': '121486',\n            'title': 'Duck On Cover',\n            'description': 'md5:c2920eaeef07d7af5b96d7c64daf1239',\n        },\n        'playlist': [{\n            'md5': 'e1a2fcb42bda30dfac990212924149a8',\n            'info_dict': {\n                'id': '1032333',\n                'ext': 'flac',\n                'title': 'Shearer - Warmachine',\n                'artist': 'Shearer',\n                'track': 'Warmachine',\n                'timestamp': 1368089771,\n                'upload_date': '20130509',\n            }\n        }, {\n            'md5': '1f358d7b2f98edfe90fd55dac0799d50',\n            'info_dict': {\n                'id': '1032330',\n                'ext': 'flac',\n                'title': 'Shearer - Without Your Ghost',\n                'artist': 'Shearer',\n                'track': 'Without Your Ghost',\n                'timestamp': 1368089771,\n                'upload_date': '20130509',\n            }\n        }],\n        'params': {\n            'playlistend': 2\n        }\n    }]\n\n    def _real_extract(self, url):\n        album_id = self._match_id(url)\n        album = self._call_api('album', album_id)\n        album_name = album.get('name')\n\n        entries = []\n        for track in (album.get('tracks') or []):\n            track_id = track.get('id')\n            if not track_id:\n                continue\n            track_id = compat_str(track_id)\n            entries.append({\n                '_type': 'url_transparent',\n                'url': 'https://www.jamendo.com/track/' + track_id,\n                'ie_key': JamendoIE.ie_key(),\n                'id': track_id,\n                'album': album_name,\n            })\n\n        return self.playlist_result(\n            entries, album_id, album_name,\n            clean_html(try_get(album, lambda x: x['description']['en'], compat_str)))\n", "path": "yt_dlp/extractor/jamendo.py"}], "after_files": [{"content": "# coding: utf-8\nfrom __future__ import unicode_literals\n\nimport hashlib\nimport random\n\nfrom ..compat import compat_str\nfrom .common import InfoExtractor\nfrom ..utils import (\n    clean_html,\n    int_or_none,\n    try_get,\n)\n\n\nclass JamendoIE(InfoExtractor):\n    _VALID_URL = r'''(?x)\n                    https?://\n                        (?:\n                            licensing\\.jamendo\\.com/[^/]+|\n                            (?:www\\.)?jamendo\\.com\n                        )\n                        /track/(?P<id>[0-9]+)(?:/(?P<display_id>[^/?#&]+))?\n                    '''\n    _TESTS = [{\n        'url': 'https://www.jamendo.com/track/196219/stories-from-emona-i',\n        'md5': '6e9e82ed6db98678f171c25a8ed09ffd',\n        'info_dict': {\n            'id': '196219',\n            'display_id': 'stories-from-emona-i',\n            'ext': 'flac',\n            # 'title': 'Maya Filipi\u010d - Stories from Emona I',\n            'title': 'Stories from Emona I',\n            # 'artist': 'Maya Filipi\u010d',\n            'track': 'Stories from Emona I',\n            'duration': 210,\n            'thumbnail': r're:^https?://.*\\.jpg',\n            'timestamp': 1217438117,\n            'upload_date': '20080730',\n            'license': 'by-nc-nd',\n            'view_count': int,\n            'like_count': int,\n            'average_rating': int,\n            'tags': ['piano', 'peaceful', 'newage', 'strings', 'upbeat'],\n        }\n    }, {\n        'url': 'https://licensing.jamendo.com/en/track/1496667/energetic-rock',\n        'only_matching': True,\n    }]\n\n    def _call_api(self, resource, resource_id):\n        path = '/api/%ss' % resource\n        rand = compat_str(random.random())\n        return self._download_json(\n            'https://www.jamendo.com' + path, resource_id, query={\n                'id[]': resource_id,\n            }, headers={\n                'X-Jam-Call': '$%s*%s~' % (hashlib.sha1((path + rand).encode()).hexdigest(), rand)\n            })[0]\n\n    def _real_extract(self, url):\n        track_id, display_id = self._match_valid_url(url).groups()\n        # webpage = self._download_webpage(\n        #     'https://www.jamendo.com/track/' + track_id, track_id)\n        # models = self._parse_json(self._html_search_regex(\n        #     r\"data-bundled-models='([^']+)\",\n        #     webpage, 'bundled models'), track_id)\n        # track = models['track']['models'][0]\n        track = self._call_api('track', track_id)\n        title = track_name = track['name']\n        # get_model = lambda x: try_get(models, lambda y: y[x]['models'][0], dict) or {}\n        # artist = get_model('artist')\n        # artist_name = artist.get('name')\n        # if artist_name:\n        #     title = '%s - %s' % (artist_name, title)\n        # album = get_model('album')\n\n        formats = [{\n            'url': 'https://%s.jamendo.com/?trackid=%s&format=%s&from=app-97dab294'\n                   % (sub_domain, track_id, format_id),\n            'format_id': format_id,\n            'ext': ext,\n            'quality': quality,\n        } for quality, (format_id, sub_domain, ext) in enumerate((\n            ('mp31', 'mp3l', 'mp3'),\n            ('mp32', 'mp3d', 'mp3'),\n            ('ogg1', 'ogg', 'ogg'),\n            ('flac', 'flac', 'flac'),\n        ))]\n        self._sort_formats(formats)\n\n        urls = []\n        thumbnails = []\n        for covers in (track.get('cover') or {}).values():\n            for cover_id, cover_url in covers.items():\n                if not cover_url or cover_url in urls:\n                    continue\n                urls.append(cover_url)\n                size = int_or_none(cover_id.lstrip('size'))\n                thumbnails.append({\n                    'id': cover_id,\n                    'url': cover_url,\n                    'width': size,\n                    'height': size,\n                })\n\n        tags = []\n        for tag in (track.get('tags') or []):\n            tag_name = tag.get('name')\n            if not tag_name:\n                continue\n            tags.append(tag_name)\n\n        stats = track.get('stats') or {}\n        license = track.get('licenseCC') or []\n\n        return {\n            'id': track_id,\n            'display_id': display_id,\n            'thumbnails': thumbnails,\n            'title': title,\n            'description': track.get('description'),\n            'duration': int_or_none(track.get('duration')),\n            # 'artist': artist_name,\n            'track': track_name,\n            # 'album': album.get('name'),\n            'formats': formats,\n            'license': '-'.join(license) if license else None,\n            'timestamp': int_or_none(track.get('dateCreated')),\n            'view_count': int_or_none(stats.get('listenedAll')),\n            'like_count': int_or_none(stats.get('favorited')),\n            'average_rating': int_or_none(stats.get('averageNote')),\n            'tags': tags,\n        }\n\n\nclass JamendoAlbumIE(JamendoIE):\n    _VALID_URL = r'https?://(?:www\\.)?jamendo\\.com/album/(?P<id>[0-9]+)'\n    _TESTS = [{\n        'url': 'https://www.jamendo.com/album/121486/duck-on-cover',\n        'info_dict': {\n            'id': '121486',\n            'title': 'Duck On Cover',\n            'description': 'md5:c2920eaeef07d7af5b96d7c64daf1239',\n        },\n        'playlist': [{\n            'md5': 'e1a2fcb42bda30dfac990212924149a8',\n            'info_dict': {\n                'id': '1032333',\n                'ext': 'flac',\n                'title': 'Shearer - Warmachine',\n                'artist': 'Shearer',\n                'track': 'Warmachine',\n                'timestamp': 1368089771,\n                'upload_date': '20130509',\n            }\n        }, {\n            'md5': '1f358d7b2f98edfe90fd55dac0799d50',\n            'info_dict': {\n                'id': '1032330',\n                'ext': 'flac',\n                'title': 'Shearer - Without Your Ghost',\n                'artist': 'Shearer',\n                'track': 'Without Your Ghost',\n                'timestamp': 1368089771,\n                'upload_date': '20130509',\n            }\n        }],\n        'params': {\n            'playlistend': 2\n        }\n    }]\n\n    def _real_extract(self, url):\n        album_id = self._match_id(url)\n        album = self._call_api('album', album_id)\n        album_name = album.get('name')\n\n        entries = []\n        for track in (album.get('tracks') or []):\n            track_id = track.get('id')\n            if not track_id:\n                continue\n            track_id = compat_str(track_id)\n            entries.append({\n                '_type': 'url_transparent',\n                'url': 'https://www.jamendo.com/track/' + track_id,\n                'ie_key': JamendoIE.ie_key(),\n                'id': track_id,\n                'album': album_name,\n            })\n\n        return self.playlist_result(\n            entries, album_id, album_name,\n            clean_html(try_get(album, lambda x: x['description']['en'], compat_str)))\n", "path": "yt_dlp/extractor/jamendo.py"}]}