Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decoding Error, appears sometimes, doesn't autofix. #26920

Closed
HelpMeGame opened this issue Oct 18, 2020 · 29 comments
Closed

Decoding Error, appears sometimes, doesn't autofix. #26920

HelpMeGame opened this issue Oct 18, 2020 · 29 comments

Comments

@HelpMeGame
Copy link

@HelpMeGame HelpMeGame commented Oct 18, 2020

Checklist

  • I'm reporting a broken site support issue
  • I've verified that I'm running youtube-dl version 2020.09.20
  • I've checked that all provided URLs are alive and playable in a browser
  • I've checked that all URLs and arguments with special characters are properly quoted or escaped
  • I've searched the bugtracker for similar bug reports including closed ones
  • I've read bugs section in FAQ

Verbose log

[debug] Encodings: locale cp1252, fs utf-8, out UTF-8, pref cp1252
[debug] youtube-dl version 2020.09.20
[debug] Python version 3.7.8 (CPython) - Windows-10-10.0.19041-SP0
[youtube:search] query "iron man 3 song": Downloading page 1
[debug] exe versions: none
[debug] Proxy map: {}
ERROR: query "iron man 3 song": Failed to parse JSON  caused by JSONDecodeError('Expecting value: line 1 column 1 (char 0)')); please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; see  https://yt-dl.org/update  on how to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.

Description

I posted a question on this yesterday and it got flagged as a duplicate - the original answer is here. This issue doesn't automatically fix like it did for that user. Instead, it seems to come and go with no pattern, so I don't have a good way to replicate it. Some songs will work at one point in time, then they won't a couple minutes later.

I get the issue when attempting to extract the data from the video. Here is a snippet of the code I am using:

ydlOps = {
    'format': 'bestaudio/best',
    'outtmpl': './%(title)s.webm',
    'noplaylist': True,
    'extractaudio': True,
    'audioformat': 'webm',
    'default_search': 'ytsearch1',
    'quite': True,
    'verbose':True,
    'version': True
}
        with youtube_dl.YoutubeDL(ydlOps) as downloader:
            songData = downloader.extract_info(url, download=download)
@SoHectic
Copy link

@SoHectic SoHectic commented Oct 18, 2020

Really hope that there is a fix for this, I am having the same issues, I am using youtube-dl for a discord bot to play some music but each search seems to throw this error randomly. It happens on both searches as well as with links and sometimes it works for songs then the same song will not work 2 min later, but recently it seems to have almost stopped working entirely

@ghost
Copy link

@ghost ghost commented Oct 18, 2020

same issue. Is it linked to the new Youtube sign-in policy?

ERROR: query "heads will rock a-trak remix yeah yeah yeahs": Failed to parse JSON (caused by JSONDecodeError('Expecting value: line 1 column 1 (char 0)')); please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; see https://yt-dl.org/update on how to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.

youtube-dl --version
2020.09.20

@HelpMeGame
Copy link
Author

@HelpMeGame HelpMeGame commented Oct 18, 2020

That’s possible... I’ll try using the sign in options and see if that helps

@pukkandan
Copy link

@pukkandan pukkandan commented Oct 18, 2020

For me, the search feature works when and only when I dont use cookies.

@HelpMeGame
Copy link
Author

@HelpMeGame HelpMeGame commented Oct 18, 2020

I haven’t been, and I assume it doesn’t create its own when it’s doing multiple searches, which is why there’s no apparent correlation with what doesn’t work for a couple minutes and what does

@pukkandan
Copy link

@pukkandan pukkandan commented Oct 18, 2020

I haven’t been, and I assume it doesn’t create its own when it’s doing multiple searches

I didnt quite understand what you meant. Can you elaborate?

@ghost
Copy link

@ghost ghost commented Oct 18, 2020

[debug] System config: []
[debug] User config: []
[debug] Custom config: []
[debug] Command-line args: ['-C', '-v', '-x', '-f', 'm4a', '-o', '~/Music/%(title)s.%(ext)s', 'ytsearch1:heads will rock a-trak remix yeah yeah yeahs']
[debug] Encodings: locale UTF-8, fs utf-8, out UTF-8, pref UTF-8
[debug] youtube-dl version 2020.09.20
[debug] Python version 3.7.3 (CPython) - Linux-5.7.0-0.bpo.2-amd64-x86_64-with-debian-10.6
[debug] exe versions: ffmpeg 4.1.6-1, ffprobe 4.1.6-1
[debug] Proxy map: {}
[youtube:search] query "heads will rock a-trak remix yeah yeah yeahs": Downloading page 1
ERROR: query "heads will rock a-trak remix yeah yeah yeahs": Failed to parse JSON  (caused by JSONDecodeError('Expecting value: line 1 column 1 (char 0)')); please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; see  https://yt-dl.org/update  on how to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py", line 904, in _parse_json
    return json.loads(json_string)
  File "/usr/lib/python3.7/json/__init__.py", line 348, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.7/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.7/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py", line 904, in _parse_json
    return json.loads(json_string)
  File "/usr/lib/python3.7/json/__init__.py", line 348, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.7/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.7/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.7/dist-packages/youtube_dl/YoutubeDL.py", line 797, in extract_info
    ie_result = ie.extract(url)
  File "/usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py", line 532, in extract
    ie_result = self._real_extract(url)
  File "/usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py", line 3014, in _real_extract
    return self._get_n_results(query, n)
  File "/usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/youtube.py", line 3204, in _get_n_results
    query={'spf': 'navigate'})
  File "/usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py", line 897, in _download_json
    expected_status=expected_status)
  File "/usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py", line 881, in _download_json_handle
    fatal=fatal), urlh
  File "/usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py", line 908, in _parse_json
    raise ExtractorError(errmsg, cause=ve)
youtube_dl.utils.ExtractorError: query "heads will rock a-trak remix yeah yeah yeahs": Failed to parse JSON  (caused by JSONDecodeError('Expecting value: line 1 column 1 (char 0)')); please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; see  https://yt-dl.org/update  on how to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.
@HelpMeGame
Copy link
Author

@HelpMeGame HelpMeGame commented Oct 18, 2020

@pukkandan Sure, what I mean is that I assume youtube-dl doesn’t automatically create cookies to make your search results change, and instead just tries to find the video without cookies.
I also haven’t been doing anything with the cookies when I’ve been searching.

@ghost
Copy link

@ghost ghost commented Oct 18, 2020

This is the json_string when it fails
debug.txt

--- /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py	2020-10-18 19:23:49.465637087 +0200
+++ /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py	2020-10-18 19:20:47.937090816 +0200
@@ -901,6 +901,7 @@
         if transform_source:
             json_string = transform_source(json_string)
         try:
+            print('DEBUG: ' + json_string)
             return json.loads(json_string)
         except ValueError as ve:
             errmsg = '%s: Failed to parse JSON ' % video_id
@ghost
Copy link

@ghost ghost commented Oct 18, 2020

The difference between my web browser (Firefox) and the debug.txt above
using

~/.bashrc
gdiff() { git diff --unified=0 --color=always --word-diff=color --word-diff-regex=. -- "$@" | sed -r 's/(\x1b)\[31m/\1\[1;33m/g; s/(\x1b)\[32m/\1\[1;35m/g;' | tail -n +5; }

image

@ghost
Copy link

@ghost ghost commented Oct 18, 2020

So the problem is that since a couple days (weeks?), Youtube returns randomly (1/3 tries) the HTML instead of the JSON
when it is working you get this valid JSON debug2.txt

Maybe workaround could be to retry 2-3 times if you don't get a JSON. Content without {"title": or a content with <html

@HelpMeGame
Copy link
Author

@HelpMeGame HelpMeGame commented Oct 18, 2020

That's a possible fix I think, but it seems to fail multiple times in a row.
Wonder what changed that's supplying HTML instead of JSON

@ghost
Copy link

@ghost ghost commented Oct 18, 2020

FYI

Same random issue with Chrome User-Agent like blocked by YouTube Proxy
So it's not a matter of User-Agent or special headers.

Cookies matters: A good value of VISITOR_INFO1_LIVE will make it work 100%

--- /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py	2020-10-18 19:23:49.465637087 +0200
+++ /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py	2020-10-23 14:56:18.538975849 +0200
@@ -617,6 +617,29 @@
             if 'X-Forwarded-For' not in headers:
                 headers['X-Forwarded-For'] = self._x_forwarded_for_ip
 
+        headers['Accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9'
+        headers['Accept-Language'] = 'en-US,en;q=0.5'
+        headers['Content-Type'] = 'application/json'
+        headers['DNT'] = '1'
+        headers['Sec-GPC'] = '1'
+        headers['Sec-Fetch-Dest'] = 'document'
+        headers['Sec-Fetch-Mode'] = 'navigate'
+        headers['Sec-Fetch-Site'] = 'same-origin'
+        headers['Sec-Fetch-User'] = '?1'
+        headers['TE'] = 'Trailers'
+        headers['Upgrade-Insecure-Requests'] = '1'
+        headers['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'
+        headers['Access-Control-Request-Headers'] = 'x-goog-visitor-id'
+        headers['Access-Control-Request-Hethod'] = 'GET'
+        headers['Origin'] = 'https://www.youtube.com'
+        headers['Referer'] = url_or_request
+        headers['Pragma'] = 'no-cache'
+        headers['Cache-Control'] = 'no-cache'
+
+        print('DEBUG: url: %s' % url_or_request)
+        print('DEBUG: headers: %s' % json.dumps(headers))
+        print('DEBUG: query: %s' % json.dumps(query))
+
         if isinstance(url_or_request, compat_urllib_request.Request):
             url_or_request = update_Request(
                 url_or_request, data=data, headers=headers, query=query)
@ghost
Copy link

@ghost ghost commented Oct 18, 2020

You can easily reproduce it with -g

youtube-dl -g -v -x -f m4a -o '~/Music/%(title)s.%(ext)s' "ytsearch1:heads will rock a-trak remix yeah yeah yeahs" 2>&1 | grep https
https://r2---sn-nfpnnjvh-1gik.googlevideo.com/videoplayback?expire=1603071657&ei=SZqMX-PIEdKv7gOsk42wCQ&ip=x.x.x.x&id=o-AAnkJnDH9AQOem3FnyycNJ-bzzcRqN2gDcLsyKiPrGN5&itag=140&source=youtube&requiressl=yes&mh=0R&mm=31%2C29&mn=sn-nfpnnjvh-1gik%2Csn-1gi7znes&ms=au%2Crdu&mv=m&mvi=2&pl=21&gcr=ch&initcwndbps=1166250&vprv=1&mime=audio%2Fmp4&gir=yes&clen=6215211&dur=383.893&lmt=1577389889691526&mt=1603049926&fvip=2&keepalive=yes&fexp=23915654&c=WEB&txp=5531432&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cgcr%2Cvprv%2Cmime%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIgIYKbxZrVnoPfF5iuy6dQpcAehCWiXUCrG_9UXmQe6rsCIQCBf4l6a_pDKbj--yCpm2lGD4VgkYgdE_NygHGrE7gSFw%3D%3D&sig=AOq0QJ8wRQIhAIusxv3_gwWxIonb7hKpFbTNu2bt9HdECDrne_SsghQGAiAS7diMkejRCi65XOW8qxXebcol4jWRwouil3Fw_nx_gA==&ratebypass=yes
DEBUG: https://www.youtube.com/results?search_query=heads+will+rock+a-trak+remix+yeah+yeah+yeahs
DEBUG: https://www.youtube.com/watch?v=TDyG4YNUXcI&gl=US&hl=en&has_verified=1&bpctr=9999999999

youtube-dl -g -v -x -f m4a -o '~/Music/%(title)s.%(ext)s' "ytsearch1:heads will rock a-trak remix yeah yeah yeahs" 2>&1 | grep https
https://r2---sn-nfpnnjvh-1gik.googlevideo.com/videoplayback?expire=1603071666&ei=UpqMX8v4NIrF1gKtoYTABg&ip=x.x.x.x&id=o-AHUHm33PbotktjymNCNcx7L7l4PUWcwTh5MkwvOzVcsh&itag=140&source=youtube&requiressl=yes&mh=0R&mm=31%2C29&mn=sn-nfpnnjvh-1gik%2Csn-1gi7znes&ms=au%2Crdu&mv=m&mvi=2&pl=21&gcr=ch&initcwndbps=1166250&vprv=1&mime=audio%2Fmp4&gir=yes&clen=6215211&dur=383.893&lmt=1577389889691526&mt=1603049926&fvip=2&keepalive=yes&fexp=23915654&c=WEB&txp=5531432&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cgcr%2Cvprv%2Cmime%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhAIDayOMfUOkwbYirliUrmuZ4CKQOCYvKr5KNssR8NYp2AiEAnKQdC-lSfw2oqcaBS1oyTVbMLIq8SQo-Rs2tO6RU5jE%3D&sig=AOq0QJ8wRQIhAKeKdQrbndiJOdMRSAv4IgbcUxclnPo5rNApFrFuHzKaAiAza50yqSGtpzIo2XdVsqkSJWWHQDpgAKXKJyCPeeM3-g==&ratebypass=yes
DEBUG: https://www.youtube.com/results?search_query=heads+will+rock+a-trak+remix+yeah+yeah+yeahs
DEBUG: https://www.youtube.com/watch?v=TDyG4YNUXcI&gl=US&hl=en&has_verified=1&bpctr=9999999999

youtube-dl -g -v -x -f m4a -o '~/Music/%(title)s.%(ext)s' "ytsearch1:heads will rock a-trak remix yeah yeah yeahs" 2>&1 | grep https
https://r2---sn-nfpnnjvh-1gik.googlevideo.com/videoplayback?expire=1603071671&ei=VpqMX72gO4zNgAfC-7_oBw&ip=x.x.x.x&id=o-AOf-fNdbyodkPwfSccJUwBx35A_TTKOi_iIRSEDzip8o&itag=140&source=youtube&requiressl=yes&mh=0R&mm=31%2C29&mn=sn-nfpnnjvh-1gik%2Csn-1gi7znes&ms=au%2Crdu&mv=m&mvi=2&pl=21&gcr=ch&initcwndbps=1166250&vprv=1&mime=audio%2Fmp4&gir=yes&clen=6215211&dur=383.893&lmt=1577389889691526&mt=1603049926&fvip=2&keepalive=yes&fexp=23915654&c=WEB&txp=5531432&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cgcr%2Cvprv%2Cmime%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRAIgNAASB6c2yuSdG47JXpIaxyKBgDvY9VX41ei4_V_N1H0CIC1-2zPrRjQWkm1lwqGEBs_v3XaglTiGfi2nGKP1ACb_&sig=AOq0QJ8wRgIhAMjQr9LVUiBObBlAb9uZb99MEvilP8K7DRFHgvdKWDxFAiEApESzroxx_fhBnMbqH4RhhTX1KVpainYoOUUuxy4iKpU=&ratebypass=yes
DEBUG: https://www.youtube.com/results?search_query=heads+will+rock+a-trak+remix+yeah+yeah+yeahs
DEBUG: https://www.youtube.com/watch?v=TDyG4YNUXcI&gl=US&hl=en&has_verified=1&bpctr=9999999999

youtube-dl -g -v -x -f m4a -o '~/Music/%(title)s.%(ext)s' "ytsearch1:heads will rock a-trak remix yeah yeah yeahs" 2>&1 | grep https
https://r2---sn-nfpnnjvh-1gik.googlevideo.com/videoplayback?expire=1603071674&ei=WpqMX8nBFZargAfb3ajYDg&ip=x.x.x.x&id=o-AOGWleVaG97NlhDQsLYgdORVulxdWsZUDyaLz6OM-V6x&itag=140&source=youtube&requiressl=yes&mh=0R&mm=31%2C29&mn=sn-nfpnnjvh-1gik%2Csn-1gi7znes&ms=au%2Crdu&mv=m&mvi=2&pl=21&gcr=ch&initcwndbps=1228750&vprv=1&mime=audio%2Fmp4&gir=yes&clen=6215211&dur=383.893&lmt=1577389889691526&mt=1603050044&fvip=2&keepalive=yes&fexp=23915654&c=WEB&txp=5531432&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cgcr%2Cvprv%2Cmime%2Cgir%2Cclen%2Cdur%2Clmt&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIgASZsEQuf3Fndenixvqt4qQmfQ9ihvdITWnW9LlJrdSICIQC7lP2CGE_16N5imW-oUjNo3vWR5pWCOn4uJp7AahcqOg%3D%3D&sig=AOq0QJ8wRgIhAMe8ZWgRl3yhvTz7x3zVohGtuYNSHsK9LuWVU3sEX4qMAiEAz4J0zov05GAF6wU2qJqb9Jz-sb0p_siPVuW7X5gtToA=&ratebypass=yes
DEBUG: https://www.youtube.com/results?search_query=heads+will+rock+a-trak+remix+yeah+yeah+yeahs
DEBUG: https://www.youtube.com/watch?v=TDyG4YNUXcI&gl=US&hl=en&has_verified=1&bpctr=9999999999
DEBUG: https://www.youtube.com/embed/TDyG4YNUXcI

youtube-dl -g -v -x -f m4a -o '~/Music/%(title)s.%(ext)s' "ytsearch1:heads will rock a-trak remix yeah yeah yeahs" 2>&1 | grep https
ERROR: query "heads will rock a-trak remix yeah yeah yeahs": Failed to parse JSON (caused by JSONDecodeError('Expecting value: line 1 column 1 (char 0)')); please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; see https://yt-dl.org/update on how to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.
youtube_dl.utils.ExtractorError: query "heads will rock a-trak remix yeah yeah yeahs": Failed to parse JSON (caused by JSONDecodeError('Expecting value: line 1 column 1 (char 0)')); please report this issue on https://yt-dl.org/bug . Make sure you are using the latest version; see https://yt-dl.org/update on how to update. Be sure to call youtube-dl with the --verbose flag and include its complete output.
DEBUG: https://www.youtube.com/results?search_query=heads+will+rock+a-trak+remix+yeah+yeah+yeahs

--- /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py	2020-10-18 19:23:49.465637087 +0200
+++ /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py	2020-10-18 21:43:39.055238080 +0200
@@ -617,6 +617,8 @@
             if 'X-Forwarded-For' not in headers:
                 headers['X-Forwarded-For'] = self._x_forwarded_for_ip
 
+        print('DEBUG: ' + url_or_request)
+
         if isinstance(url_or_request, compat_urllib_request.Request):
             url_or_request = update_Request(
                 url_or_request, data=data, headers=headers, query=query)
@Xenophloxic
Copy link

@Xenophloxic Xenophloxic commented Oct 22, 2020

This error happens to me every time I use a string query. URLs work, but queries don't. I hope YTDL fixes this fast, as I love using YTDL.

Jcodeerd added a commit to Jcodeerd/MusicBot that referenced this issue Oct 22, 2020
Add guide for Ubuntu 20.04.

Yes:
I've tested this out on my Ubuntu 20.04, and I got no errors (only that (temporary?) youtube-dl error ytdl-org/youtube-dl#26920 )

I'm not 100% sure whether I should add that part 'bout the installation of python3.7, but I added it because this whole page uses the master branch. You may remove that part if that's necessary.
@ArranMcAloon
Copy link

@ArranMcAloon ArranMcAloon commented Oct 22, 2020

I get the same error, although, it is more often than not at this time.

Just as @Xenophloxic says direct links do work, although, the default-search option of 'auto or 'ytsearch' will always result in this error 99/100 times.

@SoHectic
Copy link

@SoHectic SoHectic commented Oct 22, 2020

I have tested with both a string query as well as using a direct link and I get this error 99% of the time, not sure of any fixes though.

@ghost
Copy link

@ghost ghost commented Oct 22, 2020

From my understanding, if you get VISITOR_INFO1_LIVE cookie right in "youtube-dl --cookies cookies", it will work.

  • When it fails, you will have CONSENT cookie defined, GPS and s_gl cookies missing
  • When it works, get VISITOR_INFO1_LIVE line and create a new empty cookiefile with it and it will continue to work

Working

# Netscape HTTP Cookie File
# This file is generated by youtube-dl.  Do not edit.

.youtube.com	TRUE	/	FALSE	1603405441	GPS	1
.youtube.com	TRUE	/	FALSE	1608587880	PREF	f1=50000000&f6=8&hl=en
.youtube.com	TRUE	/	TRUE	1618954366	VISITOR_INFO1_LIVE	xxxxxxxxxxx
.youtube.com	TRUE	/	TRUE	0	YSC	xxxxxxxxxxx
.youtube.com	TRUE	/	FALSE	0	s_gl	1d69aac621b2f9c0a25dade722d6e24bcwIAAABVUw==

Also Working if the same VISITOR_INFO1_LIVE and it will auto add GPS, PREF, YSC, s_gl the next run
and it will continue to work ...

# Netscape HTTP Cookie File
# This file is generated by youtube-dl.  Do not edit.

.youtube.com	TRUE	/	TRUE	1618954366	VISITOR_INFO1_LIVE	xxxxxxxxxxx

Not Working

# Netscape HTTP Cookie File
# This file is generated by youtube-dl.  Do not edit.

.youtube.com	TRUE	/	FALSE	2145916800	CONSENT	xxxxxxxxx
.youtube.com	TRUE	/	FALSE	1608588114	PREF	f1=50000000&f6=8&hl=en
.youtube.com	TRUE	/	TRUE	1618956114	VISITOR_INFO1_LIVE	xxxxxxxxxxx
.youtube.com	TRUE	/	TRUE	0	YSC	xxxxxxxxxxx
@SoHectic
Copy link

@SoHectic SoHectic commented Oct 22, 2020

From my understanding, if you get VISITOR_INFO1_LIVE cookie right in "youtube-dl --cookies cookies", it will work almost everytime

Is there a way to do this in python through the ydl_opts list?

@ghost
Copy link

@ghost ghost commented Oct 22, 2020

If it works once with youtube-dl --cookies cookiefile
extract VISITOR_INFO1_LIVE value from cookiefile file

edit /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/youtube.py (sudo vi)
search for def _get_n_results(self, query, n):
# add this line but replace xxxxxxxxxx with the working value
self._set_cookie('.youtube.com', 'VISITOR_INFO1_LIVE', 'xxxxxxxxxx')
youtube-dl will continue to work everytime without "--cookies cookiefile"

Now I need to understand, where to get VISITOR_INFO1_LIVE right
and probably clear() all cookies except VISITOR_INFO1_LIVE and PREF

@Xenophloxic
Copy link

@Xenophloxic Xenophloxic commented Oct 23, 2020

I've tried over 300 times for a cookie that works. Any tips to help find one?

@ghost
Copy link

@ghost ghost commented Oct 23, 2020

I've tried over 300 times for a cookie that works. Any tips to help find one?

You need youtube-dl to work once with --cookies cookiefile then copy VISITOR_INFO1_LIVE

rm -f cookiefile; youtube-dl --cookies cookiefile -v -x -f m4a -o '~/Music/%(title)s.%(ext)s' 'ytsearch1:heads will rock a-trak remix yeah yeah yeahs'; grep VISITOR_INFO1_LIVE cookiefile

N.B. Before enforcing a VISITOR_INFO1_LIVE with a good value, I had random success like 1/3, 2/10, 0/7, ...
the more your try in a row the more you will get bad downloads like a DDoS protection. So you should
try to wait 2-3 minutes before retrying the above until a single one works, you get VISITOR_INFO1_LIVE value
implement it in the code and stop using --cookie (but you can also continue using it as long as you keep
the magic VISITOR_INFO1_LIVE).

You can also try to modify CONSENT from cookiefile to match your browser

.youtube.com    TRUE    /       FALSE   0       CONSENT YES+xxxxx+V13

When you have a single youtube-dl with completed download, you can remove --cookies cookiefile from youtube-dl
and apply a patch like this to enforce VISITOR_INFO1_LIVE. It's a ugly hack but it's working for me all the time
for couple of hours debugging... as soon as VISITOR_INFO1_LIVE is set to a magic value, it will work for all downloads

--- /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/youtube.py	2020-10-06 20:50:09.554406404 +0200
+++ /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/youtube.py	2020-10-23 03:49:17.357860513 +0200
@@ -3196,6 +3196,20 @@
         url_query.update(self._EXTRA_QUERY_ARGS)
         result_url = 'https://www.youtube.com/results?' + compat_urllib_parse_urlencode(url_query)
 
+        visitor_info1_live = self._get_cookies(result_url).get('VISITOR_INFO1_LIVE')
+        print('DEBUG: visitor_info1_live1: %s' % visitor_info1_live)
+
+        visitor_info1_live = self._get_cookies('https://www.youtube.com/').get('VISITOR_INFO1_LIVE')
+        print('DEBUG: visitor_info1_live2: %s' % visitor_info1_live)
+
+        visitor_info1_live = self._get_cookies('https://www.youtube.com').get('VISITOR_INFO1_LIVE')
+        print('DEBUG: visitor_info1_live3: %s' % visitor_info1_live)
+
+        #self._downloader.cookiejar.clear('.youtube.com')
+
+        self._set_cookie('.youtube.com', 'VISITOR_INFO1_LIVE', 'ToGMI3_Nn_I')
+        #self._set_cookie('.youtube.com', 'VISITOR_INFO1_LIVE', 'xxxxxxxxxxx')
+
         for pagenum in itertools.count(1):
             data = self._download_json(
                 result_url, video_id='query "%s"' % query,
--- /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py	2020-10-18 19:23:49.465637087 +0200
+++ /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/common.py	2020-10-23 03:44:56.548444691 +0200
@@ -617,6 +617,10 @@
             if 'X-Forwarded-For' not in headers:
                 headers['X-Forwarded-For'] = self._x_forwarded_for_ip
 
+        print('DEBUG: url: %s' % url_or_request)
+        print('DEBUG: headers: %s' % json.dumps(headers))
+        print('DEBUG: query: %s' % json.dumps(query))
+
         if isinstance(url_or_request, compat_urllib_request.Request):
             url_or_request = update_Request(
                 url_or_request, data=data, headers=headers, query=query)

P.S. I did not manage to get self._get_cookies(url).get('VISITOR_INFO1_LIVE')
I tried from different locations in both youtube.py and common.py but I don't get
what is returning this Set-Cookie: VISITOR_INFO1_LIVE when you use --cookie
My idea is to get this VISITOR_INFO1_LIVE cookie from a fetched url w/o --cookie

youtube-dl -v -x -f m4a -o '~/Music/%(title)s.%(ext)s' 'ytsearch1:heads will rock a-trak remix yeah yeah yeahs'
[debug] System config: []
[debug] User config: []
[debug] Custom config: []
[debug] Command-line args: ['-v', '-x', '-f', 'm4a', '-o', '~/Music/%(title)s.%(ext)s', 'ytsearch1:heads will rock a-trak remix yeah yeah yeahs']
[debug] Encodings: locale UTF-8, fs utf-8, out UTF-8, pref UTF-8
[debug] youtube-dl version 2020.09.20
[debug] Python version 3.7.3 (CPython) - Linux-5.7.0-0.bpo.2-amd64-x86_64-with-debian-10.6
[debug] exe versions: ffmpeg 4.1.6-1, ffprobe 4.1.6-1
[debug] Proxy map: {}
DEBUG: visitor_info1_live1: None
DEBUG: visitor_info1_live2: None
DEBUG: visitor_info1_live3: None
[youtube:search] query "heads will rock a-trak remix yeah yeah yeahs": Downloading page 1
DEBUG: url: https://www.youtube.com/results?search_query=heads+will+rock+a-trak+remix+yeah+yeah+yeahs
DEBUG: headers: {}
DEBUG: query: {"spf": "navigate", "disable_polymer": "true"}
[download] Downloading playlist: heads will rock a-trak remix yeah yeah yeahs
[youtube:search] playlist heads will rock a-trak remix yeah yeah yeahs: Collected 1 video ids (downloading 1 of them)
[download] Downloading video 1 of 1
[youtube] umAL-6-j1Ew: Downloading webpage
DEBUG: url: https://www.youtube.com/watch?v=umAL-6-j1Ew&gl=US&hl=en&has_verified=1&bpctr=9999999999
DEBUG: headers: {}
DEBUG: query: {"disable_polymer": "true"}
[youtube] umAL-6-j1Ew: Downloading embed webpage
DEBUG: url: https://www.youtube.com/embed/umAL-6-j1Ew
DEBUG: headers: {}
DEBUG: query: {"disable_polymer": "true"}
[youtube] {18} signature length 104, html5 player 5799986b
[...]
[download] Finished downloading playlist: heads will rock a-trak remix yeah yeah yeahs

**Using a valid VISITOR_INFO1_LIVE is the only thing that make it work all the time
You can also test it with -g to get the URL. I am able to get 100% (with the above patch and a good VISITOR_INFO1_LIVE) **

youtube-dl -g -v -x -f m4a -o '~/Music/%(title)s.%(ext)s' "ytsearch1:heads will rock a-trak remix yeah yeah yeahs"

What did work

  • using --cookie cookefile with a valid VISITOR_INFO1_LIVE of a previous completed download (then keep on using it as is)
  • using --cookie cookefile with a valid VISITOR_INFO1_LIVE of a previous completed download and removing other Cookies
  • hardcoding VISITOR_INFO1_LIVE in the code (patch above) after finding it in cookefile and stop using --cookie

What did not work

  • Using a VISITOR_INFO1_LIVE value from your web browser
  • Using a VISITOR_INFO1_LIVE value from cookiefile when CONSENT cookie is defined and GPS and s_gl cookies missing
  • All the following crazy stuff failed :
  • Add more headers to query={'spf': 'navigate'}) has no effect (a valid VISITOR_INFO1_LIVE will always pass)
@@ -3192,6 +3232,7 @@
 
         url_query = {
             'search_query': query.encode('utf-8'),
+            'gl': 'US'.encode('utf-8'),
         }
         url_query.update(self._EXTRA_QUERY_ARGS)
         result_url = 'https://www.youtube.com/results?' + compat_urllib_parse_urlencode(url_query)
@@ -3201,7 +3242,27 @@
                 result_url, video_id='query "%s"' % query,
                 note='Downloading page %s' % pagenum,
                 errnote='Unable to download API page',
-                query={'spf': 'navigate'})
+                query={'spf': 'navigate'},
+                headers={
+                    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
+                    'Accept-Language': 'en-US,en;q=0.5',
+                    'Content-Type': 'application/json',
+                    'DNT': '1',
+                    'Sec-GPC': '1',
+                    'TE': 'Trailers',
+                    'Upgrade-Insecure-Requests': '1',
+                    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
+                    'Access-Control-Request-Headers': 'x-goog-visitor-id',
+                    'Access-Control-Request-Hethod': 'GET',
+                    'Origin': 'https://www.youtube.com',
+                    'Referer': result_url,
+                    'Pragma': 'no-cache',
+                    'Sec-Fetch-Dest': 'document',
+                    'Sec-Fetch-Mode': 'navigate',
+                    'Sec-Fetch-Site': 'same-origin',
+                    'Sec-Fetch-User': '?1',
+                },
+            )
             html_content = data[1]['body']['content']
 
             if 'class="search-message' in html_content:
  • I tried implementing retries almost everywhere in the stack (based on the backtrace) with sleep and random sleeps
        for _ in range(5):
            try:
                print('DEBUG: Tries ' + str(_))
                res = self._download_webpage_handle(
                    url_or_request, video_id, note, errnote, fatal=fatal,
                    encoding=encoding, data=data, headers=headers, query=query,
                    expected_status=expected_status)
                if res is False:
                    return res
                json_string, urlh = res
                return self._parse_json(
                    json_string, video_id, transform_source=transform_source,
                    fatal=fatal), urlh
            except ExtractorError as e:
                if json_string.__contains__('<html') == True:
                    self._sleep(5, video_id)
                    continue
                raise

        for _ in range(5):
            try:
                print('DEBUG: Tries ' + str(_))
                res = self._download_json_handle(
                    url_or_request, video_id, note=note, errnote=errnote,
                    transform_source=transform_source, fatal=fatal, encoding=encoding,
                    data=data, headers=headers, query=query,
                    expected_status=expected_status)
                return res if res is False else res[0]
            except ExtractorError as e:
                """if json_string.__contains__('<html') == True:"""
                self._sleep(5, video_id)
                continue
                raise

            for _ in range(5):
                try:
                    print('DEBUG: Tries ' + str(_))
                    data = self._download_json(
                        result_url, video_id='query "%s"' % query,
                        note='Downloading page %s' % pagenum,
                        errnote='Unable to download API page',
                        query={'spf': 'navigate'})
                    break
                except ExtractorError as e:
                    #if json_string.__contains__('<html') == True:
                    self._sleep(5, 'query "%s"' % query)
                    continue
                    raise

        try:
            for _ in range(7):
                try:
                    print('DEBUG: Tries ' + str(_))
                    self.initialize()
                    ie_result = self._real_extract(url)
                    if self._x_forwarded_for_ip:
                        ie_result['__x_forwarded_for_ip'] = self._x_forwarded_for_ip
                    return ie_result
                except GeoRestrictedError as e:
                    if self.__maybe_fake_ip_and_retry(e.countries):
                        continue
                    raise
                except ExtractorError as e:
                    time.sleep(random.randint(1,10))
                    continue
                    raise
@Xenophloxic
Copy link

@Xenophloxic Xenophloxic commented Oct 23, 2020

Wow, thanks! This fixed ytdl locally, now its time to do this with my server.

@ghost
Copy link

@ghost ghost commented Oct 23, 2020

Maybe someone can help me?

I am able to get VISITOR_INFO1_LIVE from https://www.youtube.com but it's not working
you really need VISITOR_INFO1_LIVE that worked in the past to get constant 100%

New ideas:

  • maybe you need VISITOR_INFO1_LIVE from a different URL than https://www.youtube.com (later in stage)
  • maybe it will not give you a valid VISITOR_INFO1_LIVE If you retry too many times
  • smell like a DDoS protection from YouTube Frontend Proxy based on VISITOR_INFO1_LIVE exchange (Set-Cookie/Cookie)
  • check if VISITOR_INFO1_LIVE is working and store it for later use

This is my new patch

--- /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/youtube.py	2020-10-06 20:50:09.554406404 +0200
+++ /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/youtube.py	2020-10-23 13:39:58.273988869 +0200
@@ -8,6 +8,7 @@
 import os.path
 import random
 import re
+import sys
 import time
 import traceback
 
@@ -75,12 +76,6 @@
         'x-youtube-client-version': '1.20200609.04.02',
     }
 
-    def _set_language(self):
-        self._set_cookie(
-            '.youtube.com', 'PREF', 'f1=50000000&f6=8&hl=en',
-            # YouTube sets the expire time to about two months
-            expire_time=time.time() + 2 * 30 * 24 * 3600)
-
     def _ids_to_results(self, ids):
         return [
             self.url_result(vid_id, 'Youtube', video_id=vid_id)
@@ -276,10 +271,59 @@
         return super(YoutubeBaseInfoExtractor, self)._download_webpage_handle(
             *args, **compat_kwargs(kwargs))
 
+    def _get_cookie_handle(self, url_handle, cookie):
+        """
+        Get cookie from Set-Cookie header
+        """
+        for header, cookies in url_handle.headers.items():
+            if header.lower() != 'set-cookie':
+                continue
+            if sys.version_info[0] >= 3:
+                cookies = cookies.encode('iso-8859-1')
+            cookies = cookies.decode('utf-8')
+            cookie_value = re.search(
+                r'%s=(.+?);.*?\b[Dd]omain=(.+?)(?:[,;]|$)' % cookie, cookies)
+            if cookie_value:
+                value, domain = cookie_value.groups()
+                return value
+                #self._set_cookie(domain, cookie, value)
+                #break
+
+    def _set_youtube_cookies(self, url='https://www.youtube.com/'):
+        youtube_page, url_handle = self._download_webpage_handle(url, None, 'Get cookies', fatal=False)
+        if youtube_page is False:
+            return False
+
+        ## This get a dynamic VISITOR_INFO1_LIVE most of the time not valid (the one from --cookies cookefile after a working download is still valid)
+        #visitor_info1_live = self._get_cookie_handle(url_handle, 'VISITOR_INFO1_LIVE')
+        #if visitor_info1_live:
+        #  print('DEBUG: visitor_info1_live: ' + visitor_info1_live)
+        #  self._set_cookie('.youtube.com', 'VISITOR_INFO1_LIVE', visitor_info1_live, expire_time=time.time() + 180 * 24 * 3600)
+
+        #for cookie in ('VISITOR_INFO1_LIVE', 'YSC', 'GPS'):
+        for cookie in ('VISITOR_INFO1_LIVE', 'YSC'):
+            value = self._get_cookie_handle(url_handle, cookie)
+            if value:
+                print('DEBUG: Cookie: %s=%s' % (cookie, value))
+                self._set_cookie('.youtube.com', cookie, value, expire_time=time.time() + 180 * 24 * 3600)
+
+        # replace f4=xxxxxxx&gl=xx with PREF from your web browser
+        # self._set_cookie('.youtube.com', 'PREF', 'f4=xxxxxxx&gl=xx')
+        self._set_cookie('.youtube.com', 'PREF', 'f4=4000000&gl=US', expire_time=time.time() + 180 * 24 * 3600)
+
+        # replace YES+xxxxx+V13 with CONSENT from your web browser
+        # self._set_cookie('.youtube.com', 'CONSENT', 'YES+xxxxx+V13')
+        self._set_cookie('.youtube.com', 'CONSENT', 'YES+CH.de+V13')
+
+        # replace xxxxxxxxxx with a working VISITOR_INFO1_LIVE taken from youtube-dl --cookie cookiefile (a completed download)
+        # self._set_cookie('.youtube.com', 'VISITOR_INFO1_LIVE', 'xxxxxxxxxx')
+        # N.B. The following line is a workaround and should be commented in the future when you will get a valid VISITOR_INFO1_LIVE from the above code
+        self._set_cookie('.youtube.com', 'VISITOR_INFO1_LIVE', 'ToGMI3_Nn_I', expire_time=time.time() + 180 * 24 * 3600)
+
     def _real_initialize(self):
         if self._downloader is None:
             return
-        self._set_language()
+        self._set_youtube_cookies()
         if not self._login():
             return
 

You can use this to test it

youtube-dl -g -v -x -f m4a -o '~/Music/%(title)s.%(ext)s' "ytsearch1:heads will rock a-trak remix yeah yeah yeahs"
# Tune code for a valid VISITOR_INFO1_LIVE and retry until "Failed to parse JSON" is gone

If you want to try to get a valid VISITOR_INFO1_LIVE wait a couple of minutes then try several times
until you get no error and the url from googlevideo.com. Add it to the code above instead of 'ToGMI3_Nn_I'

touch cookiefile; \
sed -ri '/VISITOR_INFO1_LIVE/d' cookiefile; \
cat cookiefile; \
youtube-dl --cookies cookiefile -g -v -x -f m4a -o '~/Music/%(title)s.%(ext)s' "ytsearch1:heads will rock a-trak remix yeah yeah yeahs"; \
awk '/VISITOR_INFO1_LIVE/ { printf "%s=%s\n", $6, $7 }' cookiefile
@freddeh
Copy link

@freddeh freddeh commented Oct 23, 2020

You might got lucky with that cookie.. I tried it at least 30 times with waiting in between, but no luck yet.

@ghost
Copy link

@ghost ghost commented Oct 23, 2020

Fun https://twitter.com/search?q=VISITOR_INFO1_LIVE&src=typed_query&f=live

https://twitter.com/LeoWattenberg/status/1306864495722139649
VISITOR_INFO1_LIVE cookie, certain settings of the PREF cookie

Maybe Google ban youtube-dl by the special cookie
'PREF', 'f1=50000000&f6=8&hl=en'
on my browser, I have
'PREF', 'f4=4000000&gl=US'

lynx -mime_header https://www.youtube.com | grep VISITOR_INFO1_LIVE
Set-Cookie: VISITOR_INFO1_LIVE=xxxxxxxxxxx; path=/; domain=.youtube.com; secure; expires=Wed, 21-Apr-2021 10:23:16 GMT; httponly; samesite=None

This is about 180 days (180.5 days) but VISITOR_INFO1_LIVE from lynx does not work in youtube-dl
so maybe the issue is the hardcoded PREF

@freddeh
Copy link

@freddeh freddeh commented Oct 23, 2020

Maybe Google ban youtube-dl by the special cookie
'PREF', 'f1=50000000&f6=8&hl=en'

FYI: I just checked on my Mac; wiped all cookies, went to youtube.com and did not have the cookie PREF at all, only after signing into my account it appeared with content "al=de".

Tried to change the hardcoded PREF in youtube.py, but unfortunately does not change anything for me.

@ghost
Copy link

@ghost ghost commented Oct 23, 2020

Another tips to try to get a valid VISITOR_INFO1_LIVE youtube-dl --cookies cookiefile
wait a couple minutes and try the name of another song ("ytsearch1:Younotus and Lahos Nacho") or use a shorter one
and use the original youtube-dl (which does not include the above code).

When you have it, apply the following code and use a valid VISITOR_INFO1_LIVE code instead of ToGMI3_Nn_I
(from your last working download using youtube-dl --cookies cookiefile)

--- /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/youtube.py-orig	2020-10-06 20:50:09.554406404 +0200
+++ /usr/local/lib/python3.7/dist-packages/youtube_dl/extractor/youtube.py-new	2020-10-23 13:48:22.204708619 +0200
@@ -75,12 +75,6 @@
         'x-youtube-client-version': '1.20200609.04.02',
     }
 
-    def _set_language(self):
-        self._set_cookie(
-            '.youtube.com', 'PREF', 'f1=50000000&f6=8&hl=en',
-            # YouTube sets the expire time to about two months
-            expire_time=time.time() + 2 * 30 * 24 * 3600)
-
     def _ids_to_results(self, ids):
         return [
             self.url_result(vid_id, 'Youtube', video_id=vid_id)
@@ -276,10 +270,14 @@
         return super(YoutubeBaseInfoExtractor, self)._download_webpage_handle(
             *args, **compat_kwargs(kwargs))
 
+    def _set_youtube_cookies(self, url='https://www.youtube.com/'):
+        self._set_cookie('.youtube.com', 'PREF', 'f1=50000000&f6=8&hl=en', expire_time=time.time() + 180 * 24 * 3600)
+        self._set_cookie('.youtube.com', 'VISITOR_INFO1_LIVE', 'ToGMI3_Nn_I', expire_time=time.time() + 180 * 24 * 3600)
+
     def _real_initialize(self):
         if self._downloader is None:
             return
-        self._set_language()
+        self._set_youtube_cookies()
         if not self._login():
             return
 

I have close to 12h 100% working everytime when I enable this line and 0% or random when not.

@ghost
Copy link

@ghost ghost commented Oct 23, 2020

I will do a break as I have a VISITOR_INFO1_LIVE working for me all the time.

Nothing else matters [than a valid VISITOR_INFO1_LIVE]

Magic VISITOR_INFO1_LIVE, voodoo VISITOR_INFO1_LIVE
The voodoo, who do, what you don't dare do VISITOR_INFO1_LIVE
Voodoo, the voodoo, who do, what you don't dare do VISITOR_INFO1_LIVE
The voodoo, who do, what you don't dare do VISITOR_INFO1_LIVE
The voodoo, the voodoo, who do, what you don't dare do VISITOR_INFO1_LIVE
The voodoo, who do, what you don't dare do VISITOR_INFO1_LIVE

Magic VISITOR_INFO1_LIVE, magic VISITOR_INFO1_LIVE, voodoo VISITOR_INFO1_LIVE, magic VISITOR_INFO1_LIVE
Magic VISITOR_INFO1_LIVE, magic VISITOR_INFO1_LIVE, voodoo VISITOR_INFO1_LIVE, magic VISITOR_INFO1_LIVE
Magic VISITOR_INFO1_LIVE, magic VISITOR_INFO1_LIVE, voodoo VISITOR_INFO1_LIVE, magic VISITOR_INFO1_LIVE
Magic VISITOR_INFO1_LIVE, magic VISITOR_INFO1_LIVE, voodoo VISITOR_INFO1_LIVE, voodoo

;-)

@ytdl-org ytdl-org locked as off topic and limited conversation to collaborators Oct 23, 2020
@dstftw dstftw closed this in 416da57 Oct 23, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants
You can’t perform that action at this time.