Repository: yt-dlp/yt-dlp

Issue:
[TwitCasting] No longer possible to download past broadcast (live still works)
### 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 broken 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)
- [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

Japan

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

Up till a few days ago, it was possible to download past broadcasts from twitcasting site. Now it no longer works. I could not find any issue logged for this yet so I guess it's pretty fresh.

### 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://twitcasting.tv/momokumopon/movie/750329313']
[debug] Encodings: locale UTF-8, fs utf-8, pref UTF-8, out utf-8, error utf-8, screen utf-8
[debug] yt-dlp version 2022.10.04 [4e0511f] (zip)
[debug] Python 3.10.6 (CPython 64bit) - Linux-4.4.0-19041-Microsoft-x86_64-with-glibc2.35 (glibc 2.35)
[debug] Checking exe version: ffmpeg -bsfs
[debug] Checking exe version: ffprobe -bsfs
[debug] exe versions: ffmpeg 4.4.2 (setts), ffprobe 4.4.2
[debug] Optional libraries: certifi-2020.06.20, secretstorage-3.3.1, sqlite3-2.6.0
[debug] Proxy map: {}
[debug] Loaded 1690 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] [TwitCasting] Extracting URL: https://twitcasting.tv/momokumopon/movie/750329313
[TwitCasting] 750329313: Downloading webpage
ERROR: [TwitCasting] 750329313: 750329313: Failed to parse JSON (caused by JSONDecodeError("Expecting value in '': line 1 column 1 (char 0)")); please report this issue on  https://github.com/yt-dlp/yt-dlp/issues?q= , filling out the appropriate issue template. Confirm you are on the latest version using  yt-dlp -U
  File "/usr/local/bin/yt-dlp/yt_dlp/extractor/common.py", line 672, in extract
    ie_result = self._real_extract(url)
  File "/usr/local/bin/yt-dlp/yt_dlp/extractor/twitcasting.py", line 101, in _real_extract
    video_js_data = try_get(
  File "/usr/local/bin/yt-dlp/yt_dlp/utils.py", line 3206, in try_get
    return try_call(*variadic(getter), args=(src,), expected_type=expected_type)
  File "/usr/local/bin/yt-dlp/yt_dlp/utils.py", line 3197, in try_call
    val = f(*args, **kwargs)
  File "/usr/local/bin/yt-dlp/yt_dlp/extractor/twitcasting.py", line 103, in <lambda>
    lambda x: self._parse_json(self._search_regex(
  File "/usr/local/bin/yt-dlp/yt_dlp/extractor/common.py", line 979, in _parse_json
    self.__print_error('Failed to parse JSON' if errnote is None else errnote, fatal, video_id, ve)
  File "/usr/local/bin/yt-dlp/yt_dlp/extractor/common.py", line 962, in __print_error
    raise ExtractorError(f'{video_id}: {errnote}', cause=err)

  File "/usr/local/bin/yt-dlp/yt_dlp/utils.py", line 598, in decode
    return super().decode(s)
  File "/usr/lib/python3.10/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.10/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/bin/yt-dlp/yt_dlp/extractor/common.py", line 976, in _parse_json
    return json.loads(
  File "/usr/lib/python3.10/json/__init__.py", line 359, in loads
    return cls(**kw).decode(s)
  File "/usr/local/bin/yt-dlp/yt_dlp/utils.py", line 601, in decode
    raise type(e)(f'{e.msg} in {s[e.pos-10:e.pos+10]!r}', s, e.pos)
json.decoder.JSONDecodeError: Expecting value in '': line 1 column 1 (char 0)
```



Discussion:
Looks like they've started something weird for the data we're extracting: I have an idea how to fix it, let me do that

```
 data-movie-playlist='==Qfd1nInBnauMXLx0SMlRWM5I2Yy8CXkFzLcFTZvwVMfhzNldWYtl2LcZHducmbpR3chNGdpdHduU2Zh1Wavw1MldWYtl2LcZHducmbpR3chNGdpdHduMDM3dWZnFWbp9CXvwlOwRHdoJiOiwmcVxWah5mYtVHa0JCL9JCTSV1ZlBXbtg3Lc52bpRXYjlGbwBXYiojIlBXe0JCLiETPtB3cmgDNhFTMhNGOwUzMyYjYjdTL1MWOxQDNxIWLxQDM5QjN3YjNx0SM0IDMyYzN2YTMtIjLw4yMxMTOyMDM1cjRyUidGJTJk9mduMGdGJTJ9s2P4U3Mt5iclR3ch12LcNHbo9CXy4CMuMTMzkjMzATN38CXz1WYlJHdz9CXxY3LcJjduQ2b25yY09CX2RnLn5Wa0NXYjRXa3RnL1MjMzkTMsR2Lc9CX6MHc0RHaiojIsJXdisnOiU2YyV3bzJCLwADMyETNzojIu9Wa0Fmc1RmIsAjOiUWbpRFdyFGdzJyebpjIyIye'
```
Interesting. Looks like a reversed b64 to me (actually it is I tried reversing and decoding). No idea why they'd do that :D

Resolving patch:
diff --git a/yt_dlp/extractor/twitcasting.py b/yt_dlp/extractor/twitcasting.py
--- a/yt_dlp/extractor/twitcasting.py
+++ b/yt_dlp/extractor/twitcasting.py
@@ -1,3 +1,4 @@
+import base64
 import itertools
 import re
 
@@ -74,6 +75,16 @@ class TwitCastingIE(InfoExtractor):
         'playlist_mincount': 2,
     }]
 
+    def _parse_data_movie_playlist(self, dmp, video_id):
+        # attempt 1: parse as JSON directly
+        try:
+            return self._parse_json(dmp, video_id)
+        except ExtractorError:
+            pass
+        # attempt 2: decode reversed base64
+        decoded = base64.b64decode(dmp[::-1])
+        return self._parse_json(decoded, video_id)
+
     def _real_extract(self, url):
         uploader_id, video_id = self._match_valid_url(url).groups()
 
@@ -100,7 +111,7 @@ def _real_extract(self, url):
 
         video_js_data = try_get(
             webpage,
-            lambda x: self._parse_json(self._search_regex(
+            lambda x: self._parse_data_movie_playlist(self._search_regex(
                 r'data-movie-playlist=\'([^\']+?)\'',
                 x, 'movie playlist', default=None), video_id)['2'], list)
 


Test patch: