Repository: yt-dlp/yt-dlp

Issue:
`InfoExtractor._json_ld` fails under certain conditions
### DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE

- [X] I understand that I will be **blocked** if I remove or skip any mandatory\* field

### Checklist

- [X] I'm reporting a bug unrelated to a specific site
- [X] I've verified that I'm running yt-dlp version **2022.10.04** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
- [X] I've checked that all provided URLs are playable in a browser with the same IP and same login details
- [X] I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/yt-dlp/yt-dlp/wiki/FAQ#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)

### Provide a description that is worded well enough to be understood

`InfoExtractor._json_ld` fails under certain conditions.
Inside `traverse_json_ld` `e` is assumed a dict eventhough it does not always have to be.

### Provide verbose output that clearly demonstrates the problem

- [X] Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
- [X] Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below

### Complete Verbose Output

```shell
[debug] Command-line config: ['-vU', 'https://www.autoweek.nl/autotests/artikel/porsche-911-gt3-rs-rij-impressie-2/']
[debug] Encodings: locale cp1252, fs utf-8, pref cp1252, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version 2022.10.04 [4e0511f27] (source)
[debug] Lazy loading extractors is disabled
[debug] Plugins: ['SamplePluginIE', 'SamplePluginPP']
[debug] Python 3.10.5 (CPython 64bit) - Windows-10-10.0.22000-SP0
[debug] Checking exe version: ffmpeg -bsfs
[debug] Checking exe version: ffprobe -bsfs
[debug] exe versions: ffmpeg n5.1-1-g6d83c5f876-20220727 (setts), ffprobe n5.1-1-g6d83c5f876-20220727
[debug] Optional libraries: Cryptodome-3.14.1, brotli-1.0.9, certifi-2022.09.14, mutagen-1.45.1, sqlite3-2.6.0, websockets-10.3
[debug] Proxy map: {}
[debug] Loaded 1691 extractors
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
Latest version: 2022.10.04, Current version: 2022.10.04
yt-dlp is up to date (2022.10.04)
[debug] [generic] Extracting URL: https://www.autoweek.nl/autotests/artikel/porsche-911-gt3-rs-rij-impressie-2/
[generic] porsche-911-gt3-rs-rij-impressie-2: Downloading webpage
[redirect] Following redirect to https://myprivacy.dpgmedia.nl/consent?siteKey=ujm6mv0jrqiz5syr&callbackUrl=https%3A%2F%2Fwww.autoweek.nl%2Fprivacygate-confirm%3FredirectUri%3D%252Fautotests%252Fartikel%252Fporsche-911-gt3-rs-rij-impressie-2%252F
[debug] [generic] Extracting URL: https://myprivacy.dpgmedia.nl/consent?siteKey=ujm6mv0jrqiz5syr&callbackUrl=https%3A%2F%2Fwww.autoweek.nl%2Fprivacygate-confirm%3FredirectUri%3D%252Fautotests%252Fartikel%252Fporsche-911-gt3-rs-rij-impressie-2%252F
[generic] consent?siteKey=ujm6mv0jrqiz5syr&callbackUrl=https://www.autoweek: Downloading webpage
[redirect] Following redirect to https://www.autoweek.nl/autotests/artikel/porsche-911-gt3-rs-rij-impressie-2/
[debug] [generic] Extracting URL: https://www.autoweek.nl/autotests/artikel/porsche-911-gt3-rs-rij-impressie-2/
[generic] porsche-911-gt3-rs-rij-impressie-2: Downloading webpage
WARNING: [generic] Falling back on generic information extractor
[generic] porsche-911-gt3-rs-rij-impressie-2: Extracting information
[debug] Looking for Brightcove embeds
[debug] Looking for embeds
ERROR: 'list' object has no attribute 'get'
Traceback (most recent call last):
  File "D:\Documents\GitHub\Grub4K\yt-dlp\yt_dlp\YoutubeDL.py", line 1477, in wrapper
    return func(self, *args, **kwargs)
  File "D:\Documents\GitHub\Grub4K\yt-dlp\yt_dlp\YoutubeDL.py", line 1553, in __extract_info
    ie_result = ie.extract(url)
  File "D:\Documents\GitHub\Grub4K\yt-dlp\yt_dlp\extractor\common.py", line 672, in extract
    ie_result = self._real_extract(url)
  File "D:\Documents\GitHub\Grub4K\yt-dlp\yt_dlp\extractor\generic.py", line 2897, in _real_extract
    json_ld = self._search_json_ld(webpage, video_id, default={})
  File "D:\Documents\GitHub\Grub4K\yt-dlp\yt_dlp\extractor\common.py", line 1450, in _search_json_ld
    info = self._json_ld(
  File "D:\Documents\GitHub\Grub4K\yt-dlp\yt_dlp\extractor\common.py", line 1631, in _json_ld
    traverse_json_ld(json_ld)
  File "D:\Documents\GitHub\Grub4K\yt-dlp\yt_dlp\extractor\common.py", line 1576, in traverse_json_ld
    traverse_json_ld(variadic(e['@graph'], allowed_types=(dict,)), at_top_level=False)
  File "D:\Documents\GitHub\Grub4K\yt-dlp\yt_dlp\extractor\common.py", line 1624, in traverse_json_ld
    video = e.get('video')
AttributeError: 'list' object has no attribute 'get'
```



Discussion:

Resolving patch:
diff --git a/yt_dlp/extractor/common.py b/yt_dlp/extractor/common.py
--- a/yt_dlp/extractor/common.py
+++ b/yt_dlp/extractor/common.py
@@ -1466,10 +1466,6 @@ def _json_ld(self, json_ld, video_id, fatal=True, expected_type=None):
         if not json_ld:
             return {}
         info = {}
-        if not isinstance(json_ld, (list, tuple, dict)):
-            return info
-        if isinstance(json_ld, dict):
-            json_ld = [json_ld]
 
         INTERACTION_TYPE_MAP = {
             'CommentAction': 'comment',
@@ -1569,11 +1565,13 @@ def extract_video_object(e):
             extract_chapter_information(e)
 
         def traverse_json_ld(json_ld, at_top_level=True):
-            for e in json_ld:
+            for e in variadic(json_ld):
+                if not isinstance(e, dict):
+                    continue
                 if at_top_level and '@context' not in e:
                     continue
                 if at_top_level and set(e.keys()) == {'@context', '@graph'}:
-                    traverse_json_ld(variadic(e['@graph'], allowed_types=(dict,)), at_top_level=False)
+                    traverse_json_ld(e['@graph'], at_top_level=False)
                     break
                 if expected_type is not None and not is_type(e, expected_type):
                     continue
@@ -1628,8 +1626,8 @@ def traverse_json_ld(json_ld, at_top_level=True):
                     continue
                 else:
                     break
-        traverse_json_ld(json_ld)
 
+        traverse_json_ld(json_ld)
         return filter_dict(info)
 
     def _search_nextjs_data(self, webpage, video_id, *, transform_source=None, fatal=True, **kw):
diff --git a/yt_dlp/extractor/generic.py b/yt_dlp/extractor/generic.py
--- a/yt_dlp/extractor/generic.py
+++ b/yt_dlp/extractor/generic.py
@@ -2463,6 +2463,21 @@ class GenericIE(InfoExtractor):
                 'duration': 111.0,
             }
         },
+        {
+            'note': 'JSON LD with unexpected data type',
+            'url': 'https://www.autoweek.nl/autotests/artikel/porsche-911-gt3-rs-rij-impressie-2/',
+            'info_dict': {
+                'id': 'porsche-911-gt3-rs-rij-impressie-2',
+                'ext': 'mp4',
+                'title': 'Test: Porsche 911 GT3 RS',
+                'description': 'Je ziet het niet, maar het is er wel. Downforce, hebben we het dan over. En in de nieuwe Porsche 911 GT3 RS is er zelfs heel veel downforce.',
+                'timestamp': 1664920902,
+                'upload_date': '20221004',
+                'thumbnail': r're:^https://media.autoweek.nl/m/.+\.jpg$',
+                'age_limit': 0,
+                'direct': True,
+            }
+        }
     ]
 
     def report_following_redirect(self, new_url):


Test patch: