13 Commits

Author SHA1 Message Date
0dc1747178 update version 0.2.16 2024-04-21 13:16:18 +08:00
8577164785 update client params 2024-04-21 13:14:08 +08:00
8af98968dd Refactoring code and reuse INNERTUBE_CLIENTS 2024-04-21 13:13:19 +08:00
8f00cbcdd6 update
update android_music client
2024-04-21 11:21:35 +08:00
af75551bc2 update
update android client
2024-04-21 11:18:42 +08:00
3a6cc1e44f version 0.2.15 2024-04-08 07:25:50 +08:00
7664b5f0ff normalize css 2024-04-08 07:12:03 +08:00
ec5d236cad fix color dark theme 2024-04-08 07:10:03 +08:00
d6b7a255d0 v0.2.14 2024-04-07 11:52:53 +08:00
22bc7324db css normalize 2024-04-07 11:50:53 +08:00
48e8f271e7 update styles to modern 2024-04-07 11:44:19 +08:00
9a0ad6070b version 0.2.13 2024-04-06 22:12:21 +08:00
6039589f24 Update android params
Discovered by LuanRT - https://github.com/LuanRT/YouTube.js/pull/624
2024-04-06 22:04:14 +08:00
9 changed files with 150 additions and 151 deletions

View File

@@ -256,7 +256,8 @@ hr {
padding-top: 6px; padding-top: 6px;
text-align: center; text-align: center;
white-space: nowrap; white-space: nowrap;
border: none; border: 1px solid;
border-color: var(--button-border);
border-radius: 0.2rem; border-radius: 0.2rem;
} }

View File

@@ -1,20 +1,22 @@
:root { :root {
--background: #212121; --background: #121113;
--text: #FFFFFF; --text: #FFFFFF;
--secondary-hover: #73828c; --secondary-hover: #222222;
--secondary-focus: #303030; --secondary-focus: #121113;
--secondary-inverse: #FFF; --secondary-inverse: #FFFFFF;
--primary-background: #242424; --primary-background: #242424;
--secondary-background: #424242; --secondary-background: #222222;
--thumb-background: #757575; --thumb-background: #222222;
--link: #00B0FF; --link: #00B0FF;
--link-visited: #40C4FF; --link-visited: #40C4FF;
--border-bg: #FFFFFF; --border-bg: #222222;
--buttom: #dcdcdb; --border-bg-settings: #000000;
--buttom-text: #415462; --border-bg-license: #000000;
--button-border: #91918c; --buttom: #121113;
--buttom-hover: #BBB; --buttom-text: #FFFFFF;
--search-text: #FFF; --button-border: #222222;
--time-background: #212121; --buttom-hover: #222222;
--time-text: #FFF; --search-text: #FFFFFF;
--time-background: #121113;
--time-text: #FFFFFF;
} }

View File

@@ -1,19 +1,21 @@
:root { :root {
--background: #2d3743; --background: #2D3743;
--text: #FFFFFF; --text: #FFFFFF;
--secondary-hover: #73828c; --secondary-hover: #73828C;
--secondary-focus: rgba(115, 130, 140, 0.125); --secondary-focus: rgba(115, 130, 140, 0.125);
--secondary-inverse: #FFFFFF; --secondary-inverse: #FFFFFF;
--primary-background: #2d3743; --primary-background: #2D3743;
--secondary-background: #102027; --secondary-background: #102027;
--thumb-background: #35404D; --thumb-background: #35404D;
--link: #22aaff; --link: #22AAFF;
--link-visited: #7755ff; --link-visited: #7755FF;
--border-bg: #FFFFFF; --border-bg: #FFFFFF;
--buttom: #DCDCDC; --border-bg-settings: #FFFFFF;
--buttom-text: #415462; --border-bg-license: #FFFFFF;
--button-border: #91918c; --buttom: #2D3743;
--buttom-hover: #BBBBBB; --buttom-text: #FFFFFF;
--button-border: #102027;
--buttom-hover: #102027;
--search-text: #FFFFFF; --search-text: #FFFFFF;
--time-background: #212121; --time-background: #212121;
--time-text: #FFFFFF; --time-text: #FFFFFF;

View File

@@ -181,7 +181,7 @@ label[for=options-toggle-cbox] {
.table td,.table th { .table td,.table th {
padding: 10px 10px; padding: 10px 10px;
border: 1px solid var(--secondary-background); border: 1px solid var(--border-bg-license);
text-align: center; text-align: center;
} }

View File

@@ -10,9 +10,11 @@
--link: #212121; --link: #212121;
--link-visited: #808080; --link-visited: #808080;
--border-bg: #212121; --border-bg: #212121;
--buttom: #DCDCDC; --border-bg-settings: #91918C;
--border-bg-license: #91918C;
--buttom: #FFFFFF;
--buttom-text: #212121; --buttom-text: #212121;
--button-border: #91918c; --button-border: #91918C;
--buttom-hover: #BBBBBB; --buttom-hover: #BBBBBB;
--search-text: #212121; --search-text: #212121;
--time-background: #212121; --time-background: #212121;

View File

@@ -155,7 +155,7 @@ label[for=options-toggle-cbox] {
} }
.settings-form > h2 { .settings-form > h2 {
border-bottom: 2px solid var(--border-bg); border-bottom: 2px solid var(--border-bg-settings);
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
} }

View File

@@ -431,34 +431,29 @@ class RateLimitedQueue(gevent.queue.Queue):
gevent.queue.Queue.__init__(self) gevent.queue.Queue.__init__(self)
def get(self): def get(self):
self.lock.acquire() # blocks if another greenlet currently has the lock with self.lock: # blocks if another greenlet currently has the lock
if self.count_since_last_wait >= self.subsequent_bursts and self.surpassed_initial: if ((self.count_since_last_wait >= self.subsequent_bursts and self.surpassed_initial) or
gevent.sleep(self.waiting_period) (self.count_since_last_wait >= self.initial_burst and not self.surpassed_initial)):
self.count_since_last_wait = 0 self.surpassed_initial = True
gevent.sleep(self.waiting_period)
elif self.count_since_last_wait >= self.initial_burst and not self.surpassed_initial:
self.surpassed_initial = True
gevent.sleep(self.waiting_period)
self.count_since_last_wait = 0
self.count_since_last_wait += 1
if not self.currently_empty and self.empty():
self.currently_empty = True
self.empty_start = time.monotonic()
item = gevent.queue.Queue.get(self) # blocks when nothing left
if self.currently_empty:
if time.monotonic() - self.empty_start >= self.waiting_period:
self.count_since_last_wait = 0 self.count_since_last_wait = 0
self.surpassed_initial = False
self.currently_empty = False self.count_since_last_wait += 1
self.lock.release() if not self.currently_empty and self.empty():
self.currently_empty = True
self.empty_start = time.monotonic()
return item item = gevent.queue.Queue.get(self) # blocks when nothing left
if self.currently_empty:
if time.monotonic() - self.empty_start >= self.waiting_period:
self.count_since_last_wait = 0
self.surpassed_initial = False
self.currently_empty = False
return item
def download_thumbnail(save_directory, video_id): def download_thumbnail(save_directory, video_id):
@@ -667,25 +662,6 @@ def to_valid_filename(name):
# https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/extractor/youtube.py#L72 # https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/extractor/youtube.py#L72
INNERTUBE_CLIENTS = { INNERTUBE_CLIENTS = {
'android_music': {
'INNERTUBE_API_KEY': 'AIzaSyAOghZGza2MQSZkY_zfZ370N-PUdXEo8AI',
'INNERTUBE_CONTEXT': {
'client': {
'hl': 'en',
'gl': 'US',
'clientName': 'ANDROID_MUSIC',
'clientVersion': '6.44.54',
'osName': 'Android',
'osVersion': '14',
'androidSdkVersion': 34,
'platform': 'MOBILE',
'userAgent': 'com.google.android.apps.youtube.music/6.44.54 (Linux; U; Android 14; US) gzip'
}
},
'INNERTUBE_CONTEXT_CLIENT_NAME': 21,
'REQUIRE_JS_PLAYER': False
},
'android': { 'android': {
'INNERTUBE_API_KEY': 'AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w', 'INNERTUBE_API_KEY': 'AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w',
'INNERTUBE_CONTEXT': { 'INNERTUBE_CONTEXT': {
@@ -693,17 +669,13 @@ INNERTUBE_CLIENTS = {
'hl': 'en', 'hl': 'en',
'gl': 'US', 'gl': 'US',
'clientName': 'ANDROID', 'clientName': 'ANDROID',
'clientVersion': '19.12.36', 'clientVersion': '19.15.35',
'osName': 'Android', 'osName': 'Android',
'osVersion': '14', 'osVersion': '14',
'androidSdkVersion': 34, 'androidSdkVersion': 34,
'platform': 'MOBILE', 'platform': 'MOBILE',
'userAgent': 'com.google.android.youtube/19.12.36 (Linux; U; Android 14; US) gzip' 'userAgent': 'com.google.android.youtube/19.15.35 (Linux; U; Android 14; en_US; Google Pixel 6 Pro) gzip'
}, }
# https://github.com/yt-dlp/yt-dlp/pull/575#issuecomment-887739287
#'thirdParty': {
# 'embedUrl': 'https://google.com', # Can be any valid URL
#}
}, },
'INNERTUBE_CONTEXT_CLIENT_NAME': 3, 'INNERTUBE_CONTEXT_CLIENT_NAME': 3,
'REQUIRE_JS_PLAYER': False, 'REQUIRE_JS_PLAYER': False,
@@ -725,6 +697,25 @@ INNERTUBE_CLIENTS = {
'REQUIRE_JS_PLAYER': False 'REQUIRE_JS_PLAYER': False
}, },
'android_music': {
'INNERTUBE_API_KEY': 'AIzaSyAOghZGza2MQSZkY_zfZ370N-PUdXEo8AI',
'INNERTUBE_CONTEXT': {
'client': {
'hl': 'en',
'gl': 'US',
'clientName': 'ANDROID_MUSIC',
'clientVersion': '6.48.51',
'osName': 'Android',
'osVersion': '14',
'androidSdkVersion': 34,
'platform': 'MOBILE',
'userAgent': 'com.google.android.apps.youtube.music/6.48.51 (Linux; U; Android 14; US) gzip'
}
},
'INNERTUBE_CONTEXT_CLIENT_NAME': 21,
'REQUIRE_JS_PLAYER': False
},
# This client can access age restricted videos (unless the uploader has disabled the 'allow embedding' option) # This client can access age restricted videos (unless the uploader has disabled the 'allow embedding' option)
# See: https://github.com/zerodytrash/YouTube-Internal-Clients # See: https://github.com/zerodytrash/YouTube-Internal-Clients
'tv_embedded': { 'tv_embedded': {

View File

@@ -1,3 +1,3 @@
from __future__ import unicode_literals from __future__ import unicode_literals
__version__ = '0.2.12' __version__ = '0.2.16'

View File

@@ -2,6 +2,7 @@ import youtube
from youtube import yt_app from youtube import yt_app
from youtube import util, comments, local_playlist, yt_data_extract from youtube import util, comments, local_playlist, yt_data_extract
from youtube.util import time_utc_isoformat from youtube.util import time_utc_isoformat
from youtube.util import INNERTUBE_CLIENTS
import settings import settings
from flask import request from flask import request
@@ -343,7 +344,7 @@ def _add_to_error(info, key, additional_message):
def fetch_player_response(client, video_id): def fetch_player_response(client, video_id):
return util.call_youtube_api(client, 'player', { return util.call_youtube_api(client, 'player', {
'videoId': video_id, 'videoId': video_id,
'params': 'CgIQBg', 'params': 'CgIIAdgDAQ==',
}) })
@@ -369,83 +370,83 @@ def fetch_watch_page_info(video_id, playlist_id, index):
return yt_data_extract.extract_watch_info_from_html(watch_page) return yt_data_extract.extract_watch_info_from_html(watch_page)
def extract_info(video_id, use_invidious, playlist_id=None, index=None): def extract_info(video_id, use_invidious, playlist_id=None, index=None):
tasks = ( for client in INNERTUBE_CLIENTS:
# Get video metadata from here tasks = (
gevent.spawn(fetch_watch_page_info, video_id, playlist_id, index), gevent.spawn(fetch_watch_page_info, video_id, playlist_id, index),
gevent.spawn(fetch_player_response, 'ios', video_id) gevent.spawn(fetch_player_response, client, video_id) # Use client from INNERTUBE_CLIENTS
) )
gevent.joinall(tasks) gevent.joinall(tasks)
util.check_gevent_exceptions(*tasks) util.check_gevent_exceptions(*tasks)
info, player_response = tasks[0].value, tasks[1].value info, player_response = tasks[0].value, tasks[1].value
yt_data_extract.update_with_new_urls(info, player_response)
# Age restricted video, retry
if info['age_restricted'] or info['player_urls_missing']:
if info['age_restricted']:
print('Age restricted video, retrying')
else:
print('Player urls missing, retrying')
player_response = fetch_player_response('tv_embedded', video_id)
yt_data_extract.update_with_new_urls(info, player_response) yt_data_extract.update_with_new_urls(info, player_response)
# signature decryption # Age restricted video, retry
decryption_error = decrypt_signatures(info, video_id) if info['age_restricted'] or info['player_urls_missing']:
if decryption_error: if info['age_restricted']:
decryption_error = 'Error decrypting url signatures: ' + decryption_error print('Age restricted video, retrying')
info['playability_error'] = decryption_error else:
print('Player urls missing, retrying')
player_response = fetch_player_response('tv_embedded', video_id)
yt_data_extract.update_with_new_urls(info, player_response)
# check if urls ready (non-live format) in former livestream # signature decryption
# urls not ready if all of them have no filesize decryption_error = decrypt_signatures(info, video_id)
if info['was_live']: if decryption_error:
info['urls_ready'] = False decryption_error = 'Error decrypting url signatures: ' + decryption_error
for fmt in info['formats']: info['playability_error'] = decryption_error
if fmt['file_size'] is not None:
info['urls_ready'] = True
else:
info['urls_ready'] = True
# livestream urls # check if urls ready (non-live format) in former livestream
# sometimes only the livestream urls work soon after the livestream is over # urls not ready if all of them have no filesize
if (info['hls_manifest_url'] if info['was_live']:
and (info['live'] or not info['formats'] or not info['urls_ready']) info['urls_ready'] = False
):
manifest = util.fetch_url(info['hls_manifest_url'],
debug_name='hls_manifest.m3u8',
report_text='Fetched hls manifest'
).decode('utf-8')
info['hls_formats'], err = yt_data_extract.extract_hls_formats(manifest)
if not err:
info['playability_error'] = None
for fmt in info['hls_formats']:
fmt['video_quality'] = video_quality_string(fmt)
else:
info['hls_formats'] = []
# check for 403. Unnecessary for tor video routing b/c ip address is same
info['invidious_used'] = False
info['invidious_reload_button'] = False
info['tor_bypass_used'] = False
if (settings.route_tor == 1
and info['formats'] and info['formats'][0]['url']):
try:
response = util.head(info['formats'][0]['url'],
report_text='Checked for URL access')
except urllib3.exceptions.HTTPError:
print('Error while checking for URL access:\n')
traceback.print_exc()
return info
if response.status == 403:
print('Access denied (403) for video urls.')
print('Routing video through Tor')
info['tor_bypass_used'] = True
for fmt in info['formats']: for fmt in info['formats']:
fmt['url'] += '&use_tor=1' if fmt['file_size'] is not None:
elif 300 <= response.status < 400: info['urls_ready'] = True
print('Error: exceeded max redirects while checking video URL') else:
return info info['urls_ready'] = True
# livestream urls
# sometimes only the livestream urls work soon after the livestream is over
if (info['hls_manifest_url']
and (info['live'] or not info['formats'] or not info['urls_ready'])
):
manifest = util.fetch_url(info['hls_manifest_url'],
debug_name='hls_manifest.m3u8',
report_text='Fetched hls manifest'
).decode('utf-8')
info['hls_formats'], err = yt_data_extract.extract_hls_formats(manifest)
if not err:
info['playability_error'] = None
for fmt in info['hls_formats']:
fmt['video_quality'] = video_quality_string(fmt)
else:
info['hls_formats'] = []
# check for 403. Unnecessary for tor video routing b/c ip address is same
info['invidious_used'] = False
info['invidious_reload_button'] = False
info['tor_bypass_used'] = False
if (settings.route_tor == 1
and info['formats'] and info['formats'][0]['url']):
try:
response = util.head(info['formats'][0]['url'],
report_text='Checked for URL access')
except urllib3.exceptions.HTTPError:
print('Error while checking for URL access:\n')
traceback.print_exc()
return info
if response.status == 403:
print('Access denied (403) for video urls.')
print('Routing video through Tor')
info['tor_bypass_used'] = True
for fmt in info['formats']:
fmt['url'] += '&use_tor=1'
elif 300 <= response.status < 400:
print('Error: exceeded max redirects while checking video URL')
return info
def video_quality_string(format): def video_quality_string(format):