Repository: ytdl-org/youtube-dl

Issue:
[YouTube] Unable to decode n-parameter with player 9c6dfc4a 
_Originally posted by @LaurentVB in https://github.com/ytdl-org/youtube-dl/issues/32905#issuecomment-2637984269_

>I just updated to the latest version (2025.01.23), full of hope 😅 and I'm still getting 403 errors. 
Forcing `-f 18` does not seem to help whereas I vaguely remember it being a valid workaround in the past.
>
>Here's my full verbose log:

```console
~ $ youtube-dl --extract-audio --audio-format wav -v https://www.youtube.com/watch?v=bCLyEGNi448
[debug] System config: []
[debug] User config: []
[debug] Custom config: []
[debug] Command-line args: [u'--extract-audio', u'--audio-format', u'wav', u'-v', u'https://www.youtube.com/watch?v=bCLyEGNi448']
[debug] Encodings: locale UTF-8, fs utf-8, out UTF-8, pref UTF-8
[debug] youtube-dl version 2025.01.23 [5975d7bb9] (single file build)
[debug] ** This version was built from the latest master code at https://github.com/ytdl-org/youtube-dl.
[debug] ** For support, visit the main site.
[debug] Python 2.7.16 (CPython x86_64 64bit) - Darwin-20.6.0-x86_64-i386-64bit - LibreSSL 2.8.3
[debug] exe versions: ffmpeg 4.3.1, ffprobe 4.3.1, rtmpdump 2.4
[debug] Proxy map: {}
[youtube] bCLyEGNi448: Downloading webpage
[youtube] bCLyEGNi448: Downloading TVHTML5 API JSON
[debug] [youtube] Extracting signature function js_9c6dfc4a_105
WARNING: [youtube] bCLyEGNi448: Unable to decode n-parameter: expect download to be blocked or throttled (Signature function returned an exception; please report this issue on https://github.com/ytdl-org/youtube-dl/issues , using the appropriate issue template. Make sure you are using the latest version; type  youtube-dl -U  to update. Be sure to call youtube-dl with the --verbose option and include the complete output. Traceback (most recent call last):
  File "/usr/local/bin/youtube-dl/youtube_dl/extractor/youtube.py", line 1763, in _decrypt_nsig
    ret = extract_nsig(jsi, func_code)(n)
  File "/usr/local/bin/youtube-dl/youtube_dl/extractor/youtube.py", line 1852, in extract_nsig
    raise JSInterpreter.Exception('Signature function returned an exception')
Exception: Signature function returned an exception; please report this issue on https://github.com/ytdl-org/youtube-dl/issues , using the appropriate issue template. Make sure you are using the latest version; type  youtube-dl -U  to update. Be sure to call youtube-dl with the --verbose option and include the complete output.
)
[debug] [youtube] Extracting signature function js_9c6dfc4a_109
WARNING: [youtube] bCLyEGNi448: Unable to decode n-parameter: expect download to be blocked or throttled (Signature function returned an exception; please report this issue on https://github.com/ytdl-org/youtube-dl/issues , using the appropriate issue template. Make sure you are using the latest version; type  youtube-dl -U  to update. Be sure to call youtube-dl with the --verbose option and include the complete output. Traceback (most recent call last):
  File "/usr/local/bin/youtube-dl/youtube_dl/extractor/youtube.py", line 1763, in _decrypt_nsig
    ret = extract_nsig(jsi, func_code)(n)
  File "/usr/local/bin/youtube-dl/youtube_dl/extractor/youtube.py", line 1852, in extract_nsig
    raise JSInterpreter.Exception('Signature function returned an exception')
Exception: Signature function returned an exception; please report this issue on https://github.com/ytdl-org/youtube-dl/issues , using the appropriate issue template. Make sure you are using the latest version; type  youtube-dl -U  to update. Be sure to call youtube-dl with the --verbose option and include the complete output.
)
[debug] Invoking downloader on u'https://rr8---sn-cxab5jvh-cg0ee.googlevideo.com/videoplayback?rms=au%2Cau&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cxpc%2Cbui%2Cspc%2Cvprv%2Csvpuc%2Cmime%2Cns%2Crqh%2Cgir%2Cclen%2Cdur%2Clmt&fexp=51326932%2C51371294&ei=iMyjZ7GqFfiD6dsPwf6umAw&ip=2a02%3Aa03f%3A617f%3A7d00%3Ae1a1%3A2fd4%3Afd8d%3Ad366&clen=2598445&spc=9kzgDT1aVwu1mUC__l5VxX2G_Jn9lt9z--pCYnH9y_yAy3Ov-OHe5EyUJQN-a0o&id=o-AMmE0foQ9eJEjuRMGpK_aRiVOsqhUM4lmRT_Q0X5dUzj&keepalive=yes&svpuc=1&gir=yes&xpc=EgVo2aDSNQ%3D%3D&requiressl=yes&source=youtube&mv=m&sig=AJfQdSswRAIgEx-0CM7oj5T5vWIiLg9npwK2IJLFcdmJZwgC-8QKkkkCIClQ3KhhZMv330aQ2pR3jFqMyvDplkW6nJQigM2RjnTh&dur=163.561&ns=H4cwZyaW04pLoKQVAeOk-fEQ&initcwndbps=2538750&vprv=1&lsig=AGluJ3MwRQIgMpYPN3L75qLl9xOu2MLXrLjmzBKnB9JFR1LZ0ukTvd0CIQCyfCR40zjYIizl2Rwgcrot6KgS62AL3JYQ4gALGIhRiQ%3D%3D&lsparams=met%2Cmh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Crms%2Cinitcwndbps&lmt=1534780148317850&c=TVHTML5&lmw=1&met=1738787976%2C&bui=AY2Et-Py-k85ZInMkLGeKPrluk5iQxa9OR9Z18WnNZ3dGFZqTrQKc6sy-1pEiByP9XDZxTmz2ZyX-0pa&mime=audio%2Fmp4&fvip=5&rqh=1&itag=140&sefc=1&mm=31%2C29&mn=sn-cxab5jvh-cg0ee%2Csn-5hneknee&mh=dZ&n=jbu7ylIosQHyJyJV&mt=1738787575&expire=1738809576&pl=42&ms=au%2Crdu&mvi=8'
[dashsegments] Total fragments: 1
[download] Destination: Sonia Gary - Oh ! John-bCLyEGNi448.m4a
[download] Got server HTTP error: HTTP Error 403: Forbidden. Retrying fragment 1 (attempt 1 of 10)...
[download] Got server HTTP error: HTTP Error 403: Forbidden. Retrying fragment 1 (attempt 2 of 10)...
[download] Got server HTTP error: HTTP Error 403: Forbidden. Retrying fragment 1 (attempt 3 of 10)...
[download] Got server HTTP error: HTTP Error 403: Forbidden. Retrying fragment 1 (attempt 4 of 10)...
[download] Got server HTTP error: HTTP Error 403: Forbidden. Retrying fragment 1 (attempt 5 of 10)...
[download] Got server HTTP error: HTTP Error 403: Forbidden. Retrying fragment 1 (attempt 6 of 10)...
[download] Got server HTTP error: HTTP Error 403: Forbidden. Retrying fragment 1 (attempt 7 of 10)...
[download] Got server HTTP error: HTTP Error 403: Forbidden. Retrying fragment 1 (attempt 8 of 10)...
[download] Got server HTTP error: HTTP Error 403: Forbidden. Retrying fragment 1 (attempt 9 of 10)...
[download] Got server HTTP error: HTTP Error 403: Forbidden. Retrying fragment 1 (attempt 10 of 10)...
ERROR: giving up after 10 fragment retries
Traceback (most recent call last):
  File "/usr/local/bin/youtube-dl/youtube_dl/downloader/dash.py", line 53, in real_download
    success, frag_content = self._download_fragment(ctx, fragment_url, info_dict, headers)
  File "/usr/local/bin/youtube-dl/youtube_dl/downloader/fragment.py", line 111, in _download_fragment
    success = ctx['dl'].download(fragment_filename, fragment_info_dict)
  File "/usr/local/bin/youtube-dl/youtube_dl/downloader/common.py", line 380, in download
    return self.real_download(filename, info_dict)
  File "/usr/local/bin/youtube-dl/youtube_dl/downloader/http.py", line 349, in real_download
    establish_connection()
  File "/usr/local/bin/youtube-dl/youtube_dl/downloader/http.py", line 116, in establish_connection
    raise err
HTTPError: HTTP Error 403: Forbidden
```

>It looks like the code errors out when computing the signature, which then causes the fail download.
Any suggestion or workaround I could try?
Cheers
>
>Laurent

Discussion:
@dirkf: _Originally posted by @dirkf in https://github.com/ytdl-org/youtube-dl/issues/32905#issuecomment-2638081997_

>403 is now expected when nsig processing fails. This is a new issue related to player 9c6dfc4a; I can reproduce it with the same test video. Using yt-dl, there isn't any work-around at the moment.@Vangelis66: > This is a new issue related to player `9c6dfc4a`
> ...
> Using `yt-dl`, there **isn't any work-around** at the moment.

... I haven't been following closely _downstream_, but _they_ seem to handle **new YT-player** `9c6dfc4a` without problems: 
```console
yt-dlp "https://www.youtube.com/watch?v=VLCQV30im1A" -o "PASL2 Avance 007.mp4" =>

[youtube] Extracting URL: https://www.youtube.com/watch?v=VLCQV30im1A
[youtube] VLCQV30im1A: Downloading webpage
[youtube] VLCQV30im1A: Downloading tv player API JSON
[youtube] VLCQV30im1A: Downloading ios player API JSON
[youtube] VLCQV30im1A: Downloading player 9c6dfc4a
[youtube] VLCQV30im1A: Downloading m3u8 information
[info] VLCQV30im1A: Downloading 1 format(s): 399+140
[download] Destination: PASL2 Avance 007.f399.mp4
[download] 100% of    3.32MiB in 00:00:00 at 4.44MiB/s
[download] Destination: PASL2 Avance 007.f140.m4a
[download] 100% of  317.61KiB in 00:00:00 at 1.73MiB/s
[Merger] Merging formats into "PASL2 Avance 007.mp4"
Deleting original file PASL2 Avance 007.f399.mp4 (pass -k to keep)
Deleting original file PASL2 Avance 007.f140.m4a (pass -k to keep)
```
Can "we" backport their logic or is it a case of them using **py>=3.9** ? 
(the **py3.4**-based _latest_ Windows nightly `yt-dl` executable fails here, as reported 😞 ... )@dirkf: The test is actually processing the nsig as shown with a verbose log:
```console
...
[debug] yt-dlp version stable@2024.12.23 from yt-dlp/yt-dlp [65cf46cdd] (zip)
[youtube] VLCQV30im1A: Downloading player 9c6dfc4a
[debug] Saving youtube-nsig.9c6dfc4a to cache
[debug] [youtube] Decrypted nsig udNo95y49I-mTqZg => LoK4Pd7-YoUqdg
...
```
It looks like a problem with operator precedence in some expression with * and <<. @dirkf: No, just a number type issue. JS numeric values are floats even when they don't appear to be, but the evaluation rules involve weird conversions so that this sort of thing happens:
```js
"abc" << String
/*
0
*/
```
Which minute of [Brendan's busy week](https://thenewstack.io/brendan-eich-on-creating-javascript-in-10-days-and-what-hed-do-differently-today/) was spent on that?

Then:
```console
$ python -m youtube_dl -vF 'https://www.youtube.com/watch?v=bCLyEGNi448'
[debug] System config: [u'--prefer-ffmpeg']
[debug] User config: []
[debug] Custom config: []
[debug] Command-line args: [u'-vF', u'https://www.youtube.com/watch?v=bCLyEGNi448']
[debug] Encodings: locale UTF-8, fs UTF-8, out UTF-8, pref UTF-8
[debug] youtube-dl version 2021.12.17
[debug] Git HEAD: c866c8384
[debug] Python 2.7.18 (CPython i686 32bit) - Linux-4.4.0-210-generic-i686-with-Ubuntu-16.04-xenial - OpenSSL 1.1.1w  11 Sep 2023 - glibc 2.15
[debug] exe versions: avconv 4.3, avprobe 4.3, ffmpeg 4.3, ffprobe 4.3
[debug] Proxy map: {}
[youtube] bCLyEGNi448: Downloading webpage
[youtube] bCLyEGNi448: Downloading TVHTML5 API JSON
[debug] [youtube] Extracting signature function js_9c6dfc4a_109
[debug] [youtube] Decrypted nsig 8QJgTwT48DwRSS52 => jj95dz27cWdT9g
[debug] [youtube] Decrypted nsig aaWU1_cpDwAcmJQY => AQHG7VmHAV8EUg
[debug] [youtube] Extracting signature function js_9c6dfc4a_105
[info] Available formats for bCLyEGNi448:
format code  extension  resolution note
140          m4a        audio only audio_quality_medium  127k , m4a_dash container, mp4a.40.2@127k (44100Hz), 2.48MiB
160          mp4        256x144    144p   12k , mp4_dash container, avc1.4d400b@  12k, 6fps, video only, 244.97KiB
134          mp4        640x360    360p   42k , mp4_dash container, avc1.4d4016@  42k, 6fps, video only, 838.70KiB
243          webm       640x360    360p   87k , webm_dash container, vp9@  87k, 6fps, video only, 1.71MiB
136          mp4        1280x720   720p  132k , mp4_dash container, avc1.4d401f@ 132k, 6fps, video only, 2.58MiB
137          mp4        1920x1080  1080p  222k , mp4_dash container, avc1.640028@ 222k, 6fps, video only, 4.33MiB
18           mp4        640x360    360p  168k , avc1.42001E, 6fps, mp4a.40.2 (44100Hz) (best)
$
```
@dirkf: Also, _yt-dlp_ has the same issue, but some other difference causes the expression `operator.lshift(zeroise(0), zeroise(None))` to be executed rather than the failing (for both slightly different definitions of `zeroise()`) `operator.lshift(zeroise(0.0), zeroise(None))`.

Without going through the ECMA specification of `<<`, or even MDN, this is my proposed solution:
```diff
 def _js_bit_op(op):
 
     def zeroise(x):
-        return 0 if x in (None, JS_Undefined, _NaN, _Infinity) else int_or_none(x, default=0)
+        return int_or_none(x, default=0)
 
     @wraps_op(op)
     def wrapped(a, b):
```
Or with `@partial_application` (and showing the _yt-dlp_ code):
```diff
 def _js_bit_op(op):
 
-    def zeroise(x):
-         if x in (None, JS_Undefined):
-             return 0
-         with contextlib.suppress(TypeError):
-             if math.isnan(x):  # NB: NaN cannot be checked by membership
-                 return 0
-         return x
+    zeroise = int_or_none(default=0)
 
     @wraps_op(op)
     def wrapped(a, b):
```@dirkf: Finally (after MDN):
```py
    def zeroise(x, is_shift=False):
        if isinstance(x, compat_integer_types):
            return (x % 32) if is_shift else (x & 0xffffffff)
        try:
            x = float(x)
            if is_shift:
                x = int(x % 32)
            elif x < 0:
                x = -compat_int(-x % 0xffffffff)
            else:
                x = compat_int(x % 0xffffffff)
        except (ValueError, TypeError):
            # also here for int(NaN), including float('inf') % 32
            x = 0
        return x
```
`compat_int` is `long` for Py2, or `int`.@Vangelis66: ... As ever, many thanks for your prompt fixes 🥇 🫶 !
On the **Windows** side of things, I have applied locally #33049 and can confirm it works as planned:

```console
yt-dl -vF "VLCQV30im1A" => 

[debug] System config: []
[debug] User config: []
[debug] Custom config: []
[debug] Command-line args: ['--ffmpeg-location', '.\\FFmpeg', '--external-downloader-args', '-v 8 -stats', '-vF', 'VLCQV30im1A']
[debug] Encodings: locale cp1253, fs mbcs, out cp737, pref cp1253
[debug] youtube-dl version 2025.02.06 [8ba28b761] (single file build)
[debug] ** This version was built from the latest master code at https://github.com/ytdl-org/youtube-dl.
[debug] ** For support, visit the main site.
[debug] Python 3.4.4 (CPython x86 32bit) - Windows-Vista-6.0.6003-SP2 - OpenSSL 1.0.2d 9 Jul 2015
[debug] exe versions: ffmpeg n6.1-dev-2615-N-112499-ga76b409, ffprobe n6.1-dev-2615-N-112499-ga76b409, phantomjs 2.1.1, rtmpdump 2.4
[debug] Proxy map: {}
[youtube] VLCQV30im1A: Downloading webpage
[youtube] VLCQV30im1A: Downloading TVHTML5 API JSON
[youtube] VLCQV30im1A: Downloading player 9c6dfc4a
[debug] [youtube] Decrypted nsig 2lAInJwR1ATBRP8a => IwSiXXTj-ehyAQ
[debug] [youtube] Decrypted nsig VNx02E9AtZ9unIpZ => H56i2Wrr8DvOtg
[info] Available formats for VLCQV30im1A:
format code  extension  resolution note
140          m4a        audio only audio_quality_medium  129k , m4a_dash container, mp4a.40.2 (44100Hz), 317.61KiB
140-drc      m4a        audio only audio_quality_medium  129k , m4a_dash container, mp4a.40.2 (44100Hz), 317.61KiB
394          mp4        256x144    144p   65k , mp4_dash container, av01.0.00M.08, 30fps, video only, 160.35KiB
160          mp4        256x144    144p  103k , mp4_dash container, avc1.4d400c, 30fps, video only, 252.77KiB
395          mp4        426x240    240p  108k , mp4_dash container, av01.0.00M.08, 30fps, video only, 265.70KiB
396          mp4        640x360    360p  193k , mp4_dash container, av01.0.01M.08, 30fps, video only, 472.34KiB
243          webm       640x360    360p  256k , webm_dash container, vp9, 30fps, video only, 625.67KiB
134          mp4        640x360    360p  429k , mp4_dash container, avc1.4d401e, 30fps, video only, 1.02MiB
397          mp4        854x480    480p  331k , mp4_dash container, av01.0.04M.08, 30fps, video only, 807.70KiB
398          mp4        1280x720   720p  626k , mp4_dash container, av01.0.05M.08, 30fps, video only, 1.49MiB
136          mp4        1280x720   720p 1518k , mp4_dash container, avc1.64001f, 30fps, video only, 3.62MiB
399          mp4        1920x1080  1080p 1395k , mp4_dash container, av01.0.08M.08, 30fps, video only, 3.32MiB
137          mp4        1920x1080  1080p 3433k , mp4_dash container, avc1.640028, 30fps, video only, 8.18MiB
18           mp4        640x360    360p  558k , avc1.42001E, 30fps, mp4a.40.2 (44100Hz) (best)
```
As for the other YT "nastiness" I hadn't realised it existed till now, i.e.

> **403 is now expected when nsig processing fails**.

I searched "around" and found below references: 

https://github.com/ytdl-org/youtube-dl/issues/32815#issuecomment-2176754694
> it seems like incorrect/missing nsig is no longer punished by throttling, but by 403.

https://github.com/yt-dlp/yt-dlp/pull/10223
> Failing to decode the nsig param now results in HTTP Error 403 instead of download speed throttling.

... so I presume this is now FINAL, with no remedy in sight (getting **throttled** instead of **403**'d) 😢 ; and I had thought to myself that **Google** couldn't get more evil :rage: , surely; of course they can...@dirkf: Those are indeed the references. You can think of it as throttling [with extreme prejudice](https://en.wiktionary.org/wiki/terminate_with_extreme_prejudice)!

Solution patch:
diff --git a/youtube_dl/casefold.py b/youtube_dl/casefold.py
index ad9c66f8e..712b2e7fa 100644
--- a/youtube_dl/casefold.py
+++ b/youtube_dl/casefold.py
@@ -10,9 +10,10 @@ from .compat import (
 # https://github.com/unicode-org/icu/blob/main/icu4c/source/data/unidata/CaseFolding.txt
 # In case newly foldable Unicode characters are defined, paste the new version
 # of the text inside the ''' marks.
-# The text is expected to have only blank lines andlines with 1st character #,
+# The text is expected to have only blank lines and lines with 1st character #,
 # all ignored, and fold definitions like this:
-# `from_hex_code; space_separated_to_hex_code_list; comment`
+# `from_hex_code; status; space_separated_to_hex_code_list; comment`
+# Only `status` C/F are used.
 
 _map_str = '''
 # CaseFolding-15.0.0.txt
@@ -1657,11 +1658,6 @@ _map = dict(
 del _map_str
 
 
-def casefold(s):
+def _casefold(s):
     assert isinstance(s, compat_str)
     return ''.join((_map.get(c, c) for c in s))
-
-
-__all__ = [
-    'casefold',
-]
diff --git a/youtube_dl/compat.py b/youtube_dl/compat.py
index ed1a33cf2..26b655fb6 100644
--- a/youtube_dl/compat.py
+++ b/youtube_dl/compat.py
@@ -44,7 +44,7 @@ try:
     compat_str.casefold
     compat_casefold = lambda s: s.casefold()
 except AttributeError:
-    from .casefold import casefold as compat_casefold
+    from .casefold import _casefold as compat_casefold
 
 try:
     import collections.abc as compat_collections_abc
@@ -3116,17 +3116,21 @@ else:
     compat_kwargs = lambda kwargs: kwargs
 
 
+# compat_numeric_types
 try:
     compat_numeric_types = (int, float, long, complex)
 except NameError:  # Python 3
     compat_numeric_types = (int, float, complex)
 
 
+# compat_integer_types
 try:
     compat_integer_types = (int, long)
 except NameError:  # Python 3
     compat_integer_types = (int, )
 
+# compat_int
+compat_int = compat_integer_types[-1]
 
 if sys.version_info < (2, 7):
     def compat_socket_create_connection(address, timeout, source_address=None):
@@ -3532,6 +3536,7 @@ __all__ = [
     'compat_http_client',
     'compat_http_server',
     'compat_input',
+    'compat_int',
     'compat_integer_types',
     'compat_itertools_count',
     'compat_itertools_zip_longest',
diff --git a/youtube_dl/jsinterp.py b/youtube_dl/jsinterp.py
index 7835187f5..2859bc734 100644
--- a/youtube_dl/jsinterp.py
+++ b/youtube_dl/jsinterp.py
@@ -24,6 +24,8 @@ from .compat import (
     compat_collections_chain_map as ChainMap,
     compat_contextlib_suppress,
     compat_filter as filter,
+    compat_int,
+    compat_integer_types,
     compat_itertools_zip_longest as zip_longest,
     compat_map as map,
     compat_numeric_types,
@@ -70,14 +72,27 @@ class JS_Undefined(object):
     pass
 
 
-def _js_bit_op(op):
+def _js_bit_op(op, is_shift=False):
 
-    def zeroise(x):
-        return 0 if x in (None, JS_Undefined, _NaN, _Infinity) else x
+    def zeroise(x, is_shift_arg=False):
+        if isinstance(x, compat_integer_types):
+            return (x % 32) if is_shift_arg else (x & 0xffffffff)
+        try:
+            x = float(x)
+            if is_shift_arg:
+                x = int(x % 32)
+            elif x < 0:
+                x = -compat_int(-x % 0xffffffff)
+            else:
+                x = compat_int(x % 0xffffffff)
+        except (ValueError, TypeError):
+            # also here for int(NaN), including float('inf') % 32
+            x = 0
+        return x
 
     @wraps_op(op)
     def wrapped(a, b):
-        return op(zeroise(a), zeroise(b)) & 0xffffffff
+        return op(zeroise(a), zeroise(b, is_shift)) & 0xffffffff
 
     return wrapped
 
@@ -253,8 +268,8 @@ def _js_typeof(expr):
 # avoid dict to maintain order
 # definition None => Defined in JSInterpreter._operator
 _OPERATORS = (
-    ('>>', _js_bit_op(operator.rshift)),
-    ('<<', _js_bit_op(operator.lshift)),
+    ('>>', _js_bit_op(operator.rshift, True)),
+    ('<<', _js_bit_op(operator.lshift, True)),
     ('+', _js_add),
     ('-', _js_arith_op(operator.sub)),
     ('*', _js_arith_op(operator.mul)),


Test patch:
diff --git a/test/test_jsinterp.py b/test/test_jsinterp.py
index 12e7b9b94..6c34bc896 100644
--- a/test/test_jsinterp.py
+++ b/test/test_jsinterp.py
@@ -459,6 +459,10 @@ class TestJSInterpreter(unittest.TestCase):
         self._test('function f(){return undefined >> 5}', 0)
         self._test('function f(){return 42 << NaN}', 42)
         self._test('function f(){return 42 << Infinity}', 42)
+        self._test('function f(){return 0.0 << null}', 0)
+        self._test('function f(){return NaN << 42}', 0)
+        self._test('function f(){return "21.9" << 1}', 42)
+        self._test('function f(){return 21 << 4294967297}', 42)
 
     def test_negative(self):
         self._test('function f(){return 2    *    -2.0    ;}', -4)
diff --git a/test/test_youtube_signature.py b/test/test_youtube_signature.py
index fcbc9d7a8..67ef75fde 100644
--- a/test/test_youtube_signature.py
+++ b/test/test_youtube_signature.py
@@ -219,6 +219,10 @@ _NSIG_TESTS = [
         'https://www.youtube.com/s/player/2f1832d2/player_ias.vflset/en_US/base.js',
         'YWt1qdbe8SAfkoPHW5d', 'RrRjWQOJmBiP',
     ),
+    (
+        'https://www.youtube.com/s/player/9c6dfc4a/player_ias.vflset/en_US/base.js',
+        'jbu7ylIosQHyJyJV', 'uwI0ESiynAmhNg',
+    ),
 ]