Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package you-get for openSUSE:Factory checked 
in at 2024-06-24 20:56:26
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/you-get (Old)
 and      /work/SRC/openSUSE:Factory/.you-get.new.18349 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "you-get"

Mon Jun 24 20:56:26 2024 rev:47 rq:1182973 version:0.4.1710

Changes:
--------
--- /work/SRC/openSUSE:Factory/you-get/you-get.changes  2024-05-22 
21:32:29.650016808 +0200
+++ /work/SRC/openSUSE:Factory/.you-get.new.18349/you-get.changes       
2024-06-24 20:57:56.842489257 +0200
@@ -1,0 +2,6 @@
+Mon Jun 24 08:08:54 UTC 2024 - Luigi Baldoni <aloi...@gmx.com>
+
+- Update to version 0.4.1710
+  * YouTube: Fix 403 error and throttling
+
+-------------------------------------------------------------------

Old:
----
  you-get-0.4.1700.tar.gz

New:
----
  you-get-0.4.1710.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ you-get.spec ++++++
--- /var/tmp/diff_new_pack.moGdY6/_old  2024-06-24 20:57:57.230503435 +0200
+++ /var/tmp/diff_new_pack.moGdY6/_new  2024-06-24 20:57:57.234503582 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           you-get
-Version:        0.4.1700
+Version:        0.4.1710
 Release:        0
 Summary:        Dumb downloader that scrapes the web
 License:        MIT

++++++ you-get-0.4.1700.tar.gz -> you-get-0.4.1710.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/you-get-0.4.1700/.github/workflows/python-package.yml 
new/you-get-0.4.1710/.github/workflows/python-package.yml
--- old/you-get-0.4.1700/.github/workflows/python-package.yml   2024-05-22 
01:58:47.000000000 +0200
+++ new/you-get-0.4.1710/.github/workflows/python-package.yml   2024-06-23 
21:04:52.000000000 +0200
@@ -26,7 +26,7 @@
     - name: Install dependencies
       run: |
         python -m pip install --upgrade pip setuptools
-        pip install flake8 pytest
+        pip install flake8
         if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
     - name: Lint with flake8
       run: |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/you-get-0.4.1700/.gitignore 
new/you-get-0.4.1710/.gitignore
--- old/you-get-0.4.1700/.gitignore     2024-05-22 01:58:47.000000000 +0200
+++ new/you-get-0.4.1710/.gitignore     2024-06-23 21:04:52.000000000 +0200
@@ -79,6 +79,7 @@
 *.ts
 *.webm
 *.xml
+*.json
 /.env
 /.idea
 *.m4a
@@ -88,5 +89,5 @@
 
 *.zip
 
+.emacs*
 .vscode
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/you-get-0.4.1700/Makefile 
new/you-get-0.4.1710/Makefile
--- old/you-get-0.4.1700/Makefile       2024-05-22 01:58:47.000000000 +0200
+++ new/you-get-0.4.1710/Makefile       2024-06-23 21:04:52.000000000 +0200
@@ -8,7 +8,7 @@
        @(cd src/; python3 -i -c 'import you_get; print("You-Get %s\n>>> import 
you_get" % you_get.version.__version__)')
 
 test:
-       $(SETUP) test
+       (cd src; python -m unittest discover -s ../tests)
 
 clean:
        zenity --question
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/you-get-0.4.1700/requirements.txt 
new/you-get-0.4.1710/requirements.txt
--- old/you-get-0.4.1700/requirements.txt       1970-01-01 01:00:00.000000000 
+0100
+++ new/you-get-0.4.1710/requirements.txt       2024-06-23 21:04:52.000000000 
+0200
@@ -0,0 +1,2 @@
+# runtime dependencies
+dukpy
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/you-get-0.4.1700/setup.py 
new/you-get-0.4.1710/setup.py
--- old/you-get-0.4.1700/setup.py       2024-05-22 01:58:47.000000000 +0200
+++ new/you-get-0.4.1710/setup.py       2024-06-23 21:04:52.000000000 +0200
@@ -56,7 +56,8 @@
 
     entry_points = {'console_scripts': proj_info['console_scripts']},
 
-    extras_require={
+    install_requires = ['dukpy'],
+    extras_require = {
         'socks': ['PySocks'],
     }
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/you-get-0.4.1700/src/you_get/extractors/youtube.py 
new/you-get-0.4.1710/src/you_get/extractors/youtube.py
--- old/you-get-0.4.1700/src/you_get/extractors/youtube.py      2024-05-22 
01:58:47.000000000 +0200
+++ new/you-get-0.4.1710/src/you_get/extractors/youtube.py      2024-06-23 
21:04:52.000000000 +0200
@@ -3,6 +3,13 @@
 from ..common import *
 from ..extractor import VideoExtractor
 
+try:
+    import dukpy
+except ImportError:
+    log.e('Please install dukpy in order to extract videos from YouTube:')
+    log.e('$ pip install dukpy')
+    exit(0)
+from urllib.parse import urlparse, parse_qs, urlencode
 from xml.dom.minidom import parseString
 
 class YouTube(VideoExtractor):
@@ -68,45 +75,32 @@
          'audio_encoding': 'AAC', 'audio_bitrate': '24'},
     ]
 
+    def dethrottle(js, url):
+        def n_to_n(js, n):
+            # Examples:
+            #   yma - 
https://www.youtube.com/s/player/84314bef/player_ias.vflset/en_US/base.js
+            #   Xka - 
https://www.youtube.com/s/player/dc0c6770/player_ias.vflset/sv_SE/base.js
+            f1 = match1(js, 
r'a\.set\("n",b\),[$\w]+\.length\|\|([$\w]+)\(""\)')
+            f1def = match1(js, r'\W%s=(function\(\w+\).+?\)});' % 
re.escape(f1))
+            n = dukpy.evaljs('(%s)("%s")' % (f1def, n))
+            return n
+
+        u = urlparse(url)
+        qs = parse_qs(u.query)
+        n = n_to_n(js, qs['n'][0])
+        qs['n'] = [n]
+        return u._replace(query=urlencode(qs, doseq=True)).geturl()
+
     def s_to_sig(js, s):
         # Examples:
-        # - https://www.youtube.com/yts/jsbin/player-da_DK-vflWlK-zq/base.js
-        # - https://www.youtube.com/yts/jsbin/player-vflvABTsY/da_DK/base.js
-        # - https://www.youtube.com/yts/jsbin/player-vfls4aurX/da_DK/base.js
-        # - 
https://www.youtube.com/yts/jsbin/player_ias-vfl_RGK2l/en_US/base.js
-        # - https://www.youtube.com/yts/jsbin/player-vflRjqq_w/da_DK/base.js
-        # - 
https://www.youtube.com/yts/jsbin/player_ias-vfl-jbnrr/da_DK/base.js
-        # - 
https://www.youtube.com/s/player/0b643cd1/player_ias.vflset/sv_SE/base.js
-        # - 
https://www.youtube.com/s/player/50e823fc/player_ias.vflset/sv_SE/base.js
-        # - 
https://www.youtube.com/s/player/3b5d5649/player_ias.vflset/sv_SE/base.js
-        # - 
https://www.youtube.com/s/player/dc0c6770/player_ias.vflset/sv_SE/base.js
-        def tr_js(code):
-            code = re.sub(r'function', r'def', code)
-            # add prefix '_sig_' to prevent namespace pollution
-            code = re.sub(r'(\W)([$\w][$\w][$\w]?)\(', r'\1_sig_\2(', code)
-            code = re.sub(r'\$', '_dollar', code)
-            code = re.sub(r'\{', r': ', code)
-            code = re.sub(r'\}', r'\n', code)
-            code = re.sub(r'var\s+', r'', code)
-            code = re.sub(r'(\w+).join\(""\)', r'"".join(\1)', code)
-            code = re.sub(r'(\w+).length', r'len(\1)', code)
-            code = re.sub(r'(\w+).slice\((\w+)\)', r'\1[\2:]', code)
-            code = re.sub(r'(\w+).splice\((\w+),(\w+)\)', r'del \1[\2:\2+\3]', 
code)
-            code = re.sub(r'(\w+).split\(""\)', r'list(\1)', code)
-            return code
-
-        js = js.replace('\n', ' ')
-        f1 = match1(js, r'\.set\(\w+\.sp,encodeURIComponent\(([$\w]+)') or \
-            match1(js, 
r'\.set\(\w+\.sp,\(0,window\.encodeURIComponent\)\(([$\w]+)') or \
-            match1(js, r'\.set\(\w+\.sp,([$\w]+)\(\w+\.s\)\)') or \
-            match1(js, r'"signature",([$\w]+)\(\w+\.\w+\)') or \
-            match1(js, r'=([$\w]+)\(decodeURIComponent\(')
-        f1def = match1(js, r'function %s(\(\w+\)\{[^\{]+\})' % re.escape(f1)) 
or \
-                match1(js, r'\W%s=function(\(\w+\)\{[^\{]+\})' % re.escape(f1))
-        f1def = re.sub(r'([$\w]+\.)([$\w]+\(\w+,\d+\))', r'\2', f1def)
+        #   BPa - 
https://www.youtube.com/s/player/84314bef/player_ias.vflset/en_US/base.js
+        #   Xva - 
https://www.youtube.com/s/player/dc0c6770/player_ias.vflset/sv_SE/base.js
+        js_code = ''
+        f1 = match1(js, r'=([$\w]+)\(decodeURIComponent\(')
+        f1def = match1(js, r'\W%s=function(\(\w+\)\{[^\{]+\})' % re.escape(f1))
+        f1def = re.sub(r'([$\w]+\.)([$\w]+\(\w+,\d+\))', r'\2', f1def)  # 
remove . prefix
         f1def = 'function %s%s' % (f1, f1def)
-        code = tr_js(f1def)
-        f2s = set(re.findall(r'([$\w]+)\(\w+,\d+\)', f1def))
+        f2s = set(re.findall(r'([$\w]+)\(\w+,\d+\)', f1def))  # find all 
invoked function names
         for f2 in f2s:
             f2e = re.escape(f2)
             f2def = re.search(r'[^$\w]%s:function\((\w+,\w+)\)(\{[^\{\}]+\})' 
% f2e, js)
@@ -115,13 +109,10 @@
             else:
                 f2def = re.search(r'[^$\w]%s:function\((\w+)\)(\{[^\{\}]+\})' 
% f2e, js)
                 f2def = 'function {}({},b){}'.format(f2e, f2def.group(1), 
f2def.group(2))
-            f2 = re.sub(r'\$', '_dollar', f2)  # replace dollar sign
-            code = code + 'global _sig_%s\n' % f2 + tr_js(f2def)
-
-        f1 = re.sub(r'\$', '_dollar', f1)  # replace dollar sign
-        code = code + '_sig=_sig_%s(s)' % f1
-        exec(code, globals(), locals())
-        return locals()['_sig']
+            js_code += f2def + ';'
+        js_code += f1def + ';%s("%s")' % (f1, s)
+        sig = dukpy.evaljs(js_code)
+        return sig
 
     def chunk_by_range(url, size):
         urls = []
@@ -196,188 +187,67 @@
         if re.search('\Wlist=', self.url) and not kwargs.get('playlist'):
             log.w('This video is from a playlist. (use --playlist to download 
all videos in the playlist.)')
 
-        # Get video info
-        # 'eurl' is a magic parameter that can bypass age restriction
-        # full form: 
'eurl=https%3A%2F%2Fyoutube.googleapis.com%2Fv%2F{VIDEO_ID}'
-        #video_info = 
parse.parse_qs(get_content('https://www.youtube.com/get_video_info?video_id={}&eurl=https%3A%2F%2Fy'.format(self.vid)))
-        #logging.debug('STATUS: %s' % video_info['status'][0])
-        video_info = {'status': ['ok'], 'use_cipher_signature': 'True'}
-
-        ytplayer_config = None
-        if 'status' not in video_info:
-            log.wtf('[Failed] Unknown status.', exit_code=None)
-            raise
-        elif video_info['status'] == ['ok']:
-            if 'use_cipher_signature' not in video_info or 
video_info['use_cipher_signature'] == ['False']:
-                self.title = 
parse.unquote_plus(json.loads(video_info["player_response"][0])["videoDetails"]["title"])
-                # Parse video page (for DASH)
-                video_page = get_content('https://www.youtube.com/watch?v=%s' 
% self.vid)
-                try:
-                    try:
-                        # Complete ytplayer_config
-                        ytplayer_config = 
json.loads(re.search('ytplayer.config\s*=\s*([^\n]+?});', video_page).group(1))
-
-                        # Workaround: get_video_info returns bad s. Why?
-                        if 'url_encoded_fmt_stream_map' not in 
ytplayer_config['args']:
-                            stream_list = 
json.loads(ytplayer_config['args']['player_response'])['streamingData']['formats']
-                        else:
-                            stream_list = 
ytplayer_config['args']['url_encoded_fmt_stream_map'].split(',')
-                        #stream_list = 
ytplayer_config['args']['adaptive_fmts'].split(',')
-
-                        if 'assets' in ytplayer_config:
-                            self.html5player = 'https://www.youtube.com' + 
ytplayer_config['assets']['js']
-                        elif re.search('([^"]*/base\.js)"', video_page):
-                            self.html5player = 'https://www.youtube.com' + 
re.search('([^"]*/base\.js)"', video_page).group(1)
-                            self.html5player = self.html5player.replace('\/', 
'/') # unescape URL
-                        else:
-                            self.html5player = None
-
-                    except:
-                        # ytplayer_config = 
{args:{raw_player_response:ytInitialPlayerResponse}}
-                        try:  # FIXME: we should extract 
ytInitialPlayerResponse more reliably
-                            ytInitialPlayerResponse = 
json.loads(re.search('ytInitialPlayerResponse\s*=\s*([^\n]+?});</script>', 
video_page).group(1))
-                        except:
-                            ytInitialPlayerResponse = 
json.loads(re.search('ytInitialPlayerResponse\s*=\s*([^\n]+?});', 
video_page).group(1))
-
-                        stream_list = 
ytInitialPlayerResponse['streamingData']['formats']
-                        #stream_list = 
ytInitialPlayerResponse['streamingData']['adaptiveFormats']
-
-                        if re.search('([^"]*/base\.js)"', video_page):
-                            self.html5player = 'https://www.youtube.com' + 
re.search('([^"]*/base\.js)"', video_page).group(1)
-                        else:
-                            self.html5player = None
-
-                except:
-                    if 'url_encoded_fmt_stream_map' not in video_info:
-                        stream_list = 
json.loads(video_info['player_response'][0])['streamingData']['formats']
-                    else:
-                        stream_list = 
video_info['url_encoded_fmt_stream_map'][0].split(',')
-
-                    if re.search('([^"]*/base\.js)"', video_page):
-                        self.html5player = 'https://www.youtube.com' + 
re.search('([^"]*/base\.js)"', video_page).group(1)
-                    else:
-                        self.html5player = None
-
-            else:
-                # Parse video page instead
-                video_page = get_content('https://www.youtube.com/watch?v=%s' 
% self.vid)
-
-                try:  # FIXME: we should extract ytInitialPlayerResponse more 
reliably
-                    ytInitialPlayerResponse = 
json.loads(re.search('ytInitialPlayerResponse\s*=\s*([^\n]+?});</script>', 
video_page).group(1))
-                except:
-                    ytInitialPlayerResponse = 
json.loads(re.search('ytInitialPlayerResponse\s*=\s*([^\n]+?});', 
video_page).group(1))
-
-                self.title = ytInitialPlayerResponse["videoDetails"]["title"]
-                if re.search('([^"]*/base\.js)"', video_page):
-                    self.html5player = 'https://www.youtube.com' + 
re.search('([^"]*/base\.js)"', video_page).group(1)
-                else:
-                    self.html5player = None
-
-                stream_list = 
ytInitialPlayerResponse['streamingData']['formats']
+        # Extract from video page
+        logging.debug('Extracting from the video page...')
+        video_page = get_content('https://www.youtube.com/watch?v=%s' % 
self.vid)
 
-        elif video_info['status'] == ['fail']:
-            logging.debug('ERRORCODE: %s' % video_info['errorcode'][0])
-            if video_info['errorcode'] == ['150']:
-                # FIXME: still relevant?
-                if cookies:
-                    # Load necessary cookies into headers (for age-restricted 
videos)
-                    consent, ssid, hsid, sid = 'YES', '', '', ''
-                    for cookie in cookies:
-                        if cookie.domain.endswith('.youtube.com'):
-                            if cookie.name == 'SSID':
-                                ssid = cookie.value
-                            elif cookie.name == 'HSID':
-                                hsid = cookie.value
-                            elif cookie.name == 'SID':
-                                sid = cookie.value
-                    cookie_str = 'CONSENT=%s; SSID=%s; HSID=%s; SID=%s' % 
(consent, ssid, hsid, sid)
-
-                    video_page = 
get_content('https://www.youtube.com/watch?v=%s' % self.vid,
-                                             headers={'Cookie': cookie_str})
-                else:
-                    video_page = 
get_content('https://www.youtube.com/watch?v=%s' % self.vid)
-
-                try:
-                    ytplayer_config = 
json.loads(re.search('ytplayer.config\s*=\s*([^\n]+});ytplayer', 
video_page).group(1))
-                except:
-                    msg = re.search('class="message">([^<]+)<', 
video_page).group(1)
-                    log.wtf('[Failed] Got message "%s". Try to login with 
--cookies.' % msg.strip())
-
-                if 'title' in ytplayer_config['args']:
-                    # 150 Restricted from playback on certain sites
-                    # Parse video page instead
-                    self.title = ytplayer_config['args']['title']
-                    self.html5player = 'https://www.youtube.com' + 
ytplayer_config['assets']['js']
-                    stream_list = 
ytplayer_config['args']['url_encoded_fmt_stream_map'].split(',')
-                else:
-                    log.wtf('[Error] The uploader has not made this video 
available in your country.', exit_code=None)
-                    raise
-                    #self.title = re.search('<meta name="title" 
content="([^"]+)"', video_page).group(1)
-                    #stream_list = []
-
-            elif video_info['errorcode'] == ['100']:
-                log.wtf('[Failed] This video does not exist.', exit_code=None) 
#int(video_info['errorcode'][0])
-                raise
-
-            else:
-                log.wtf('[Failed] %s' % video_info['reason'][0], 
exit_code=None) #int(video_info['errorcode'][0])
-                raise
-
-        else:
-            log.wtf('[Failed] Invalid status.', exit_code=None)
-            raise
-
-        # YouTube Live
-        if ytplayer_config and (ytplayer_config['args'].get('livestream') == 
'1' or ytplayer_config['args'].get('live_playback') == '1'):
-            if 'hlsvp' in ytplayer_config['args']:
-                hlsvp = ytplayer_config['args']['hlsvp']
-            else:
-                player_response= 
json.loads(ytplayer_config['args']['player_response'])
-                log.e('[Failed] %s' % 
player_response['playabilityStatus']['reason'], exit_code=1)
+        try:
+            jsUrl = re.search('([^"]*/base\.js)"', video_page).group(1)
+        except:
+            log.wtf('[Failed] Unable to find base.js on the video page')
+        self.html5player = 'https://www.youtube.com' + jsUrl
+        logging.debug('Retrieving the player code...')
+        self.js = get_content(self.html5player).replace('\n', ' ')
+
+        logging.debug('Loading ytInitialPlayerResponse...')
+        ytInitialPlayerResponse = 
json.loads(re.search('ytInitialPlayerResponse\s*=\s*([^\n]+?});(\n|</script>)', 
video_page).group(1))
+
+        # Get the video title
+        self.title = ytInitialPlayerResponse["videoDetails"]["title"]
+
+        # Check the status
+        playabilityStatus = ytInitialPlayerResponse['playabilityStatus']
+        status = playabilityStatus['status']
+        logging.debug('status: %s' % status)
+        if status != 'OK':
+            # If cookies are loaded, status should be OK
+            try:
+                subreason = 
playabilityStatus['errorScreen']['playerErrorMessageRenderer']['subreason']['runs'][0]['text']
+                log.e('[Error] %s (%s)' % (playabilityStatus['reason'], 
subreason))
+            except:
+                log.e('[Error] %s' % playabilityStatus['reason'])
+            if status == 'LOGIN_REQUIRED':
+                log.e('View the video from a browser and export the cookies, 
then use --cookies to load cookies.')
+            exit(1)
 
-            if 'info_only' in kwargs and kwargs['info_only']:
-                return
-            else:
-                download_url_ffmpeg(hlsvp, self.title, 'mp4')
-                exit(0)
+        stream_list = ytInitialPlayerResponse['streamingData']['formats']
 
         for stream in stream_list:
-            if isinstance(stream, str):
-                metadata = parse.parse_qs(stream)
-                stream_itag = metadata['itag'][0]
-                self.streams[stream_itag] = {
-                    'itag': metadata['itag'][0],
-                    'url': metadata['url'][0],
-                    'sig': metadata['sig'][0] if 'sig' in metadata else None,
-                    's': metadata['s'][0] if 's' in metadata else None,
-                    'quality': metadata['quality'][0] if 'quality' in metadata 
else None,
-                    #'quality': metadata['quality_label'][0] if 
'quality_label' in metadata else None,
-                    'type': metadata['type'][0],
-                    'mime': metadata['type'][0].split(';')[0],
-                    'container': 
mime_to_container(metadata['type'][0].split(';')[0]),
-                }
+            if 'signatureCipher' in stream:
+                logging.debug('Parsing signatureCipher for itag=%s...' % 
stream['itag'])
+                qs = parse_qs(stream['signatureCipher'])
+                #logging.debug(qs)
+                sp = qs['sp'][0]
+                sig = self.__class__.s_to_sig(self.js, qs['s'][0])
+                url = qs['url'][0] + '&{}={}'.format(sp, sig)
+            elif 'url' in stream:
+                url = stream['url']
             else:
-                stream_itag = str(stream['itag'])
-                self.streams[stream_itag] = {
-                    'itag': str(stream['itag']),
-                    'url': stream['url'] if 'url' in stream else None,
-                    'sig': None,
-                    's': None,
-                    'quality': stream['quality'],
-                    'type': stream['mimeType'],
-                    'mime': stream['mimeType'].split(';')[0],
-                    'container': 
mime_to_container(stream['mimeType'].split(';')[0]),
-                }
-                if 'signatureCipher' in stream:
-                    self.streams[stream_itag].update(dict([(_.split('=')[0], 
parse.unquote(_.split('=')[1]))
-                                                           for _ in 
stream['signatureCipher'].split('&')]))
+                log.wtf('No signatureCipher or url for itag=%s' % 
stream['itag'])
+            url = self.__class__.dethrottle(self.js, url)
+
+            self.streams[str(stream['itag'])] = {
+                'itag': str(stream['itag']),
+                'url': url,
+                'quality': stream['quality'],
+                'type': stream['mimeType'],
+                'mime': stream['mimeType'].split(';')[0],
+                'container': 
mime_to_container(stream['mimeType'].split(';')[0]),
+            }
 
-        # Prepare caption tracks
+        # FIXME: Prepare caption tracks
         try:
-            try:
-                caption_tracks = 
json.loads(ytplayer_config['args']['player_response'])['captions']['playerCaptionsTracklistRenderer']['captionTracks']
-            except:
-                caption_tracks = 
ytInitialPlayerResponse['captions']['playerCaptionsTracklistRenderer']['captionTracks']
+            caption_tracks = 
ytInitialPlayerResponse['captions']['playerCaptionsTracklistRenderer']['captionTracks']
             for ct in caption_tracks:
                 ttsurl, lang = ct['baseUrl'], ct['languageCode']
 
@@ -406,151 +276,62 @@
                 self.caption_tracks[lang] = srt
         except: pass
 
-        # Prepare DASH streams (NOTE: not every video has DASH streams!)
-        try:
-            dashmpd = ytplayer_config['args']['dashmpd']
-            dash_xml = parseString(get_content(dashmpd))
-            for aset in dash_xml.getElementsByTagName('AdaptationSet'):
-                mimeType = aset.getAttribute('mimeType')
-                if mimeType == 'audio/mp4':
-                    rep = aset.getElementsByTagName('Representation')[-1]
-                    burls = rep.getElementsByTagName('BaseURL')
-                    dash_mp4_a_url = burls[0].firstChild.nodeValue
-                    dash_mp4_a_size = burls[0].getAttribute('yt:contentLength')
-                    if not dash_mp4_a_size:
-                        try: dash_mp4_a_size = url_size(dash_mp4_a_url)
-                        except: continue
-                elif mimeType == 'audio/webm':
-                    rep = aset.getElementsByTagName('Representation')[-1]
-                    burls = rep.getElementsByTagName('BaseURL')
-                    dash_webm_a_url = burls[0].firstChild.nodeValue
-                    dash_webm_a_size = 
burls[0].getAttribute('yt:contentLength')
-                    if not dash_webm_a_size:
-                        try: dash_webm_a_size = url_size(dash_webm_a_url)
-                        except: continue
-                elif mimeType == 'video/mp4':
-                    for rep in aset.getElementsByTagName('Representation'):
-                        w = int(rep.getAttribute('width'))
-                        h = int(rep.getAttribute('height'))
-                        itag = rep.getAttribute('id')
-                        burls = rep.getElementsByTagName('BaseURL')
-                        dash_url = burls[0].firstChild.nodeValue
-                        dash_size = burls[0].getAttribute('yt:contentLength')
-                        if not dash_size:
-                            try: dash_size = url_size(dash_url)
-                            except: continue
-                        dash_urls = self.__class__.chunk_by_range(dash_url, 
int(dash_size))
-                        dash_mp4_a_urls = 
self.__class__.chunk_by_range(dash_mp4_a_url, int(dash_mp4_a_size))
-                        self.dash_streams[itag] = {
-                            'quality': '%sx%s' % (w, h),
-                            'itag': itag,
-                            'type': mimeType,
-                            'mime': mimeType,
-                            'container': 'mp4',
-                            'src': [dash_urls, dash_mp4_a_urls],
-                            'size': int(dash_size) + int(dash_mp4_a_size)
-                        }
-                elif mimeType == 'video/webm':
-                    for rep in aset.getElementsByTagName('Representation'):
-                        w = int(rep.getAttribute('width'))
-                        h = int(rep.getAttribute('height'))
-                        itag = rep.getAttribute('id')
-                        burls = rep.getElementsByTagName('BaseURL')
-                        dash_url = burls[0].firstChild.nodeValue
-                        dash_size = burls[0].getAttribute('yt:contentLength')
-                        if not dash_size:
-                            try: dash_size = url_size(dash_url)
-                            except: continue
-                        dash_urls = self.__class__.chunk_by_range(dash_url, 
int(dash_size))
-                        dash_webm_a_urls = 
self.__class__.chunk_by_range(dash_webm_a_url, int(dash_webm_a_size))
-                        self.dash_streams[itag] = {
-                            'quality': '%sx%s' % (w, h),
-                            'itag': itag,
-                            'type': mimeType,
-                            'mime': mimeType,
-                            'container': 'webm',
-                            'src': [dash_urls, dash_webm_a_urls],
-                            'size': int(dash_size) + int(dash_webm_a_size)
-                        }
-        except:
-            # VEVO
-            if not self.html5player: return
-            self.html5player = self.html5player.replace('\/', '/') # unescape 
URL (for age-restricted videos)
-            self.js = get_content(self.html5player)
+        # Prepare DASH streams
+        if 'adaptiveFormats' in ytInitialPlayerResponse['streamingData']:
+            streams = 
ytInitialPlayerResponse['streamingData']['adaptiveFormats']
+
+            # FIXME: dead code?
+            # streams without contentLength got broken urls, just remove them 
(#2767)
+            streams = [stream for stream in streams if 'contentLength' in 
stream]
+
+            for stream in streams:
+                stream['itag'] = str(stream['itag'])
+                if 'qualityLabel' in stream:
+                    stream['quality_label'] = stream['qualityLabel']
+                    del stream['qualityLabel']
+                if 'width' in stream:
+                    stream['size'] = '{}x{}'.format(stream['width'], 
stream['height'])
+                    del stream['width']
+                    del stream['height']
+                stream['type'] = stream['mimeType']
+                stream['clen'] = stream['contentLength']
+                stream['init'] = '{}-{}'.format(
+                    stream['initRange']['start'],
+                    stream['initRange']['end'])
+                stream['index'] = '{}-{}'.format(
+                    stream['indexRange']['start'],
+                    stream['indexRange']['end'])
+                del stream['mimeType']
+                del stream['contentLength']
+                del stream['initRange']
+                del stream['indexRange']
 
-            try:
-                # Video info from video page (not always available)
-                streams = [dict([(i.split('=')[0],
-                                  parse.unquote(i.split('=')[1]))
-                                 for i in afmt.split('&')])
-                           for afmt in 
ytplayer_config['args']['adaptive_fmts'].split(',')]
-            except:
-                if 'adaptive_fmts' in video_info:
-                    streams = [dict([(i.split('=')[0],
-                                      parse.unquote(i.split('=')[1]))
-                                     for i in afmt.split('&')])
-                               for afmt in 
video_info['adaptive_fmts'][0].split(',')]
+                if 'signatureCipher' in stream:
+                    logging.debug('Parsing signatureCipher for itag=%s...' % 
stream['itag'])
+                    qs = parse_qs(stream['signatureCipher'])
+                    #logging.debug(qs)
+                    sp = qs['sp'][0]
+                    sig = self.__class__.s_to_sig(self.js, qs['s'][0])
+                    url = qs['url'][0] + '&ratebypass=yes&{}={}'.format(sp, 
sig)
+                elif 'url' in stream:
+                    url = stream['url']
                 else:
-                    try:
-                        try:
-                            streams = 
json.loads(video_info['player_response'][0])['streamingData']['adaptiveFormats']
-                        except:
-                            streams = 
ytInitialPlayerResponse['streamingData']['adaptiveFormats']
-                    except:  # no DASH stream at all
-                        return
-
-                    # streams without contentLength got broken urls, just 
remove them (#2767)
-                    streams = [stream for stream in streams if 'contentLength' 
in stream]
-
-                    for stream in streams:
-                        stream['itag'] = str(stream['itag'])
-                        if 'qualityLabel' in stream:
-                            stream['quality_label'] = stream['qualityLabel']
-                            del stream['qualityLabel']
-                        if 'width' in stream:
-                            stream['size'] = '{}x{}'.format(stream['width'], 
stream['height'])
-                            del stream['width']
-                            del stream['height']
-                        stream['type'] = stream['mimeType']
-                        stream['clen'] = stream['contentLength']
-                        stream['init'] = '{}-{}'.format(
-                            stream['initRange']['start'],
-                            stream['initRange']['end'])
-                        stream['index'] = '{}-{}'.format(
-                            stream['indexRange']['start'],
-                            stream['indexRange']['end'])
-                        del stream['mimeType']
-                        del stream['contentLength']
-                        del stream['initRange']
-                        del stream['indexRange']
-                        if 'signatureCipher' in stream:
-                            stream.update(dict([(_.split('=')[0], 
parse.unquote(_.split('=')[1]))
-                                                for _ in 
stream['signatureCipher'].split('&')]))
-                            del stream['signatureCipher']
+                    log.wtf('No signatureCipher or url for itag=%s' % 
stream['itag'])
+                url = self.__class__.dethrottle(self.js, url)
+                stream['url'] = url
 
-            for stream in streams: # get over speed limiting
-                stream['url'] += '&ratebypass=yes'
             for stream in streams: # audio
                 if stream['type'].startswith('audio/mp4'):
                     dash_mp4_a_url = stream['url']
-                    if 's' in stream:
-                        sig = self.__class__.s_to_sig(self.js, stream['s'])
-                        dash_mp4_a_url += '&sig={}'.format(sig)
                     dash_mp4_a_size = stream['clen']
                 elif stream['type'].startswith('audio/webm'):
                     dash_webm_a_url = stream['url']
-                    if 's' in stream:
-                        sig = self.__class__.s_to_sig(self.js, stream['s'])
-                        dash_webm_a_url += '&sig={}'.format(sig)
                     dash_webm_a_size = stream['clen']
             for stream in streams: # video
                 if 'size' in stream:
                     if stream['type'].startswith('video/mp4'):
                         mimeType = 'video/mp4'
                         dash_url = stream['url']
-                        if 's' in stream:
-                            sig = self.__class__.s_to_sig(self.js, stream['s'])
-                            dash_url += '&sig={}'.format(sig)
                         dash_size = stream['clen']
                         itag = stream['itag']
                         dash_urls = self.__class__.chunk_by_range(dash_url, 
int(dash_size))
@@ -567,9 +348,6 @@
                     elif stream['type'].startswith('video/webm'):
                         mimeType = 'video/webm'
                         dash_url = stream['url']
-                        if 's' in stream:
-                            sig = self.__class__.s_to_sig(self.js, stream['s'])
-                            dash_url += '&sig={}'.format(sig)
                         dash_size = stream['clen']
                         itag = stream['itag']
                         audio_url = None
@@ -610,15 +388,6 @@
 
         if stream_id in self.streams:
             src = self.streams[stream_id]['url']
-            if self.streams[stream_id]['sig'] is not None:
-                sig = self.streams[stream_id]['sig']
-                src += '&sig={}'.format(sig)
-            elif self.streams[stream_id]['s'] is not None:
-                if not hasattr(self, 'js'):
-                    self.js = get_content(self.html5player)
-                s = self.streams[stream_id]['s']
-                sig = self.__class__.s_to_sig(self.js, s)
-                src += '&sig={}'.format(sig)
 
             self.streams[stream_id]['src'] = [src]
             self.streams[stream_id]['size'] = 
urls_size(self.streams[stream_id]['src'])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/you-get-0.4.1700/src/you_get/version.py 
new/you-get-0.4.1710/src/you_get/version.py
--- old/you-get-0.4.1700/src/you_get/version.py 2024-05-22 01:58:47.000000000 
+0200
+++ new/you-get-0.4.1710/src/you_get/version.py 2024-06-23 21:04:52.000000000 
+0200
@@ -1,4 +1,4 @@
 #!/usr/bin/env python
 
 script_name = 'you-get'
-__version__ = '0.4.1700'
+__version__ = '0.4.1710'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/you-get-0.4.1700/tests/test.py 
new/you-get-0.4.1710/tests/test.py
--- old/you-get-0.4.1700/tests/test.py  2024-05-22 01:58:47.000000000 +0200
+++ new/you-get-0.4.1710/tests/test.py  2024-06-23 21:04:52.000000000 +0200
@@ -36,15 +36,15 @@
         #    
'http://www.youtube.com/attribution_link?u=/watch?v%3DldAKIzq7bvs%26feature%3Dshare',
  # noqa
         #    info_only=True
         #)
-        #youtube.download(
-        #    'https://www.youtube.com/watch?v=Fpr4fQSh1cc', info_only=True
-        #)
+        youtube.download(
+            'https://www.youtube.com/watch?v=oRdxUFDoQe0', info_only=True
+        )
 
     def test_acfun(self):
         acfun.download('https://www.acfun.cn/v/ac44560432', info_only=True)
 
-    def test_bilibili(self):
-        bilibili.download('https://www.bilibili.com/video/BV1sL4y177sC', 
info_only=True)
+    #def test_bilibili(self):
+        #bilibili.download('https://www.bilibili.com/video/BV1sL4y177sC', 
info_only=True)
 
     #def test_soundcloud(self):
         ## single song

Reply via email to