27 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
d4cba7eb6c version 0.2.12 2024-03-31 04:44:03 +08:00
70cb453280 Set 'ios' client to bypass
absidue notes that blockage of the android client is collateral
damage due to YouTube's war with ReVanced. Switching to iOS should
keep us out of the line of fire for now:
https://github.com/yt-dlp/yt-dlp/issues/9554#issuecomment-2026828421
2024-03-31 04:43:11 +08:00
7a106331e7 README.md: update 2024-03-31 02:06:20 +08:00
8775e131af Temporal fix: all requests with ANDROID client get redirected to aQvGIIdgFDM video, hence the different "content not available"
Set YTMUSIC_ANDROID client instead, but it's just the matter of time before youtube updates that one too :(
2024-03-31 01:48:43 +08:00
1f16f7cb62 version 0.2.11 2024-03-30 10:14:08 +08:00
80b7f3cd00 Update user-agents and update android client parameters to fix blockage 2024-03-30 10:10:35 +08:00
8b79e067bc README.md: update 2024-03-11 10:30:09 +08:00
cda0627d5a version 0.2.10 2024-03-11 09:55:09 +08:00
ad40dd6d6b update requirements 2024-03-11 09:53:55 +08:00
b91d53dc6f Use response.headers instead of response.getheaders()
response.getheaders() will be deprecated by urllib3.
2024-03-11 09:47:35 +08:00
cda4fd1f26 version 0.2.9 2024-03-10 02:13:29 +08:00
ff2a2edaa5 generate_release: Fix wrong (32bit) MSVCR included for 64 bitInsert the 64 bit microsoft visual C runtime for 64 bit releases 2024-03-10 02:11:09 +08:00
38d8d5d4c5 av-merge: Retry more than once for timeouts 2024-03-10 02:08:23 +08:00
f010452abf Update android client version to fix 400 Bad Request 2024-03-10 02:02:42 +08:00
17 changed files with 221 additions and 204 deletions

View File

@@ -1,5 +1,3 @@
[![Build Status](https://drone.hgit.ga/api/badges/heckyel/yt-local/status.svg)](https://drone.hgit.ga/heckyel/yt-local)
# yt-local
Fork of [youtube-local](https://github.com/user234683/youtube-local)
@@ -153,7 +151,7 @@ For coding guidelines and an overview of the software architecture, see the [HAC
yt-local is not made to work in public mode, however there is an instance of yt-local in public mode but with less features
- <https://1cd1-93-95-230-133.ngrok-free.app/https://youtube.com>
- <https://m.fridu.us/https://youtube.com>
## License

View File

@@ -114,10 +114,12 @@ if bitness == '32':
visual_c_runtime_url = 'https://github.com/yuempek/vc-archive/raw/master/archives/vc15_(14.10.25017.0)_2017_x86.7z'
visual_c_runtime_sha256 = '2549eb4d2ce4cf3a87425ea01940f74368bf1cda378ef8a8a1f1a12ed59f1547'
visual_c_name = 'vc15_(14.10.25017.0)_2017_x86.7z'
visual_c_path_to_dlls = 'runtime_minimum/System'
else:
visual_c_runtime_url = 'https://github.com/yuempek/vc-archive/raw/master/archives/vc15_(14.10.25017.0)_2017_x64.7z'
visual_c_runtime_sha256 = '4f00b824c37e1017a93fccbd5775e6ee54f824b6786f5730d257a87a3d9ce921'
visual_c_name = 'vc15_(14.10.25017.0)_2017_x64.7z'
visual_c_path_to_dlls = 'runtime_minimum/System64'
download_if_not_exists('get-pip.py', get_pip_url)
@@ -198,7 +200,7 @@ with open('./python/python3' + major_release + '._pth', 'a', encoding='utf-8') a
f.write('..\n')'''
log('Inserting Microsoft C Runtime')
check_subp(subprocess.run([r'7z', '-y', 'e', '-opython', 'vc15_(14.10.25017.0)_2017_x86.7z', 'runtime_minimum/System']))
check_subp(subprocess.run([r'7z', '-y', 'e', '-opython', visual_c_name, visual_c_path_to_dlls]))
log('Installing dependencies')
wine_run(['./python/python.exe', '-I', '-m', 'pip', 'install', '--no-compile', '-r', './requirements.txt'])

View File

@@ -1,28 +1,21 @@
attrs==22.1.0
Brotli==1.0.9
cachetools==4.2.4
click==8.0.4
dataclasses==0.6
blinker==1.7.0
Brotli==1.1.0
cachetools==5.3.3
click==8.1.7
defusedxml==0.7.1
Flask==2.0.1
gevent==22.10.2
greenlet==2.0.1
importlib-metadata==4.6.4
iniconfig==1.1.1
itsdangerous==2.0.1
Jinja2==3.0.3
MarkupSafe==2.0.1
packaging==20.9
pluggy>=0.13.1
py==1.10.0
pyparsing==2.4.7
Flask==3.0.2
gevent==24.2.1
greenlet==3.0.3
iniconfig==2.0.0
itsdangerous==2.1.2
Jinja2==3.1.3
MarkupSafe==2.1.5
packaging==24.0
pluggy==1.4.0
PySocks==1.7.1
pytest==6.2.5
stem==1.8.0
toml==0.10.2
typing-extensions==3.10.0.2
urllib3==1.26.11
Werkzeug==2.1.1
zipp==3.5.1
zope.event==4.5.0
zope.interface==5.4.0
pytest==8.1.1
stem==1.8.2
urllib3==2.2.1
Werkzeug==3.0.1
zope.event==5.0
zope.interface==6.2

View File

@@ -1,20 +1,17 @@
Brotli==1.0.9
cachetools==4.2.4
click==8.0.4
dataclasses==0.6
blinker==1.7.0
Brotli==1.1.0
cachetools==5.3.3
click==8.1.7
defusedxml==0.7.1
Flask==2.0.1
gevent==22.10.2
greenlet==2.0.1
importlib-metadata==4.6.4
itsdangerous==2.0.1
Jinja2==3.0.3
MarkupSafe==2.0.1
Flask==3.0.2
gevent==24.2.1
greenlet==3.0.3
itsdangerous==2.1.2
Jinja2==3.1.3
MarkupSafe==2.1.5
PySocks==1.7.1
stem==1.8.0
typing-extensions==3.10.0.2
urllib3==1.26.11
Werkzeug==2.1.1
zipp==3.5.1
zope.event==4.5.0
zope.interface==5.4.0
stem==1.8.2
urllib3==2.2.1
Werkzeug==3.0.1
zope.event==5.0
zope.interface==6.2

View File

@@ -84,7 +84,7 @@ def proxy_site(env, start_response, video=False):
else:
response, cleanup_func = util.fetch_url_response(url, send_headers)
response_headers = response.getheaders()
response_headers = response.headers
if isinstance(response_headers, urllib3._collections.HTTPHeaderDict):
response_headers = response_headers.items()
if video:

View File

@@ -264,7 +264,7 @@ def get_channel_tab(channel_id, page="1", sort=3, tab='videos', view=1,
'hl': 'en',
'gl': 'US',
'clientName': 'WEB',
'clientVersion': '2.20180830',
'clientVersion': '2.20240327.00.00',
},
},
'continuation': ctoken,
@@ -371,7 +371,7 @@ def get_channel_search_json(channel_id, query, page):
'hl': 'en',
'gl': 'US',
'clientName': 'WEB',
'clientVersion': '2.20180830',
'clientVersion': '2.20240327.00.00',
},
},
'continuation': ctoken,

View File

@@ -53,7 +53,7 @@ def request_comments(ctoken, replies=False):
'hl': 'en',
'gl': 'US',
'clientName': 'MWEB',
'clientVersion': '2.20210804.02.00',
'clientVersion': '2.20240328.08.00',
},
},
'continuation': ctoken.replace('=', '%3D'),

View File

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

View File

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

View File

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

View File

@@ -569,7 +569,8 @@ function fetchRange(url, start, end, debugInfo) {
onFailure(e, 'Network error');
};
xhr.ontimeout = function (event){
onFailure(null, 'Timeout (15s)', maxRetries=1);
xhr.timeout += 5000;
onFailure(null, 'Timeout (15s)', maxRetries=5);
};
xhr.send();
});

View File

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

View File

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

View File

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

View File

@@ -336,7 +336,7 @@ def fetch_url(url, headers=(), timeout=15, report_text=None, data=None,
)
)
):
print(response.status, response.reason, response.getheaders())
print(response.status, response.reason, response.headers)
ip = re.search(
br'IP address: ((?:[\da-f]*:)+[\da-f]+|(?:\d+\.)+\d+)',
content)
@@ -395,22 +395,22 @@ def head(url, use_tor=False, report_text=None, max_redirects=10):
return response
mobile_user_agent = 'Mozilla/5.0 (Linux; Android 7.0; Redmi Note 4 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36'
mobile_user_agent = 'Mozilla/5.0 (Linux; Android 14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.80 Mobile Safari/537.36'
mobile_ua = (('User-Agent', mobile_user_agent),)
desktop_user_agent = 'Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0'
desktop_user_agent = 'Mozilla/5.0 (Windows NT 10.0; rv:124.0) Gecko/20100101 Firefox/124.0'
desktop_ua = (('User-Agent', desktop_user_agent),)
json_header = (('Content-Type', 'application/json'),)
desktop_xhr_headers = (
('Accept', '*/*'),
('Accept-Language', 'en-US,en;q=0.5'),
('X-YouTube-Client-Name', '1'),
('X-YouTube-Client-Version', '2.20180830'),
('X-YouTube-Client-Version', '2.20240327.00.00'),
) + desktop_ua
mobile_xhr_headers = (
('Accept', '*/*'),
('Accept-Language', 'en-US,en;q=0.5'),
('X-YouTube-Client-Name', '2'),
('X-YouTube-Client-Version', '2.20180830'),
('X-YouTube-Client-Name', '1'),
('X-YouTube-Client-Version', '2.20240328.08.00'),
) + mobile_ua
@@ -431,34 +431,29 @@ class RateLimitedQueue(gevent.queue.Queue):
gevent.queue.Queue.__init__(self)
def get(self):
self.lock.acquire() # blocks if another greenlet currently has the lock
if self.count_since_last_wait >= self.subsequent_bursts and self.surpassed_initial:
gevent.sleep(self.waiting_period)
self.count_since_last_wait = 0
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:
with self.lock: # blocks if another greenlet currently has the lock
if ((self.count_since_last_wait >= self.subsequent_bursts and self.surpassed_initial) or
(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.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):
@@ -674,21 +669,53 @@ INNERTUBE_CLIENTS = {
'hl': 'en',
'gl': 'US',
'clientName': 'ANDROID',
'clientVersion': '17.31.35',
'clientVersion': '19.15.35',
'osName': 'Android',
'osVersion': '12',
'androidSdkVersion': 31,
'userAgent': 'com.google.android.youtube/17.31.35 (Linux; U; Android 12) gzip'
},
# https://github.com/yt-dlp/yt-dlp/pull/575#issuecomment-887739287
#'thirdParty': {
# 'embedUrl': 'https://google.com', # Can be any valid URL
#}
'osVersion': '14',
'androidSdkVersion': 34,
'platform': 'MOBILE',
'userAgent': 'com.google.android.youtube/19.15.35 (Linux; U; Android 14; en_US; Google Pixel 6 Pro) gzip'
}
},
'INNERTUBE_CONTEXT_CLIENT_NAME': 3,
'REQUIRE_JS_PLAYER': False,
},
'ios': {
'INNERTUBE_API_KEY': 'AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc',
'INNERTUBE_CONTEXT': {
'client': {
'hl': 'en',
'gl': 'US',
'clientName': 'IOS',
'clientVersion': '19.12.3',
'deviceModel': 'iPhone14,3',
'userAgent': 'com.google.ios.youtube/19.12.3 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)'
}
},
'INNERTUBE_CONTEXT_CLIENT_NAME': 5,
'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)
# See: https://github.com/zerodytrash/YouTube-Internal-Clients
'tv_embedded': {
@@ -699,6 +726,7 @@ INNERTUBE_CLIENTS = {
'gl': 'US',
'clientName': 'TVHTML5_SIMPLY_EMBEDDED_PLAYER',
'clientVersion': '2.0',
'clientScreen': 'EMBED',
},
# https://github.com/yt-dlp/yt-dlp/pull/575#issuecomment-887739287
'thirdParty': {
@@ -715,7 +743,7 @@ INNERTUBE_CLIENTS = {
'INNERTUBE_CONTEXT': {
'client': {
'clientName': 'WEB',
'clientVersion': '2.20220801.00.00',
'clientVersion': '2.20240327.00.00',
'userAgent': desktop_user_agent,
}
},

View File

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

View File

@@ -2,6 +2,7 @@ import youtube
from youtube import yt_app
from youtube import util, comments, local_playlist, yt_data_extract
from youtube.util import time_utc_isoformat
from youtube.util import INNERTUBE_CLIENTS
import settings
from flask import request
@@ -343,7 +344,7 @@ def _add_to_error(info, key, additional_message):
def fetch_player_response(client, video_id):
return util.call_youtube_api(client, 'player', {
'videoId': video_id,
'params': 'CgIQBg',
'params': 'CgIIAdgDAQ==',
})
@@ -369,93 +370,83 @@ def fetch_watch_page_info(video_id, playlist_id, index):
return yt_data_extract.extract_watch_info_from_html(watch_page)
def extract_info(video_id, use_invidious, playlist_id=None, index=None):
tasks = (
# Get video metadata from here
gevent.spawn(fetch_watch_page_info, video_id, playlist_id, index),
for client in INNERTUBE_CLIENTS:
tasks = (
gevent.spawn(fetch_watch_page_info, video_id, playlist_id, index),
gevent.spawn(fetch_player_response, client, video_id) # Use client from INNERTUBE_CLIENTS
)
gevent.joinall(tasks)
util.check_gevent_exceptions(*tasks)
info, player_response = tasks[0].value, tasks[1].value
# Get video URLs by spoofing as android client because its urls don't
# require decryption
# The URLs returned with WEB for videos requiring decryption
# couldn't be decrypted with the base.js from the web page for some
# reason
# https://github.com/yt-dlp/yt-dlp/issues/574#issuecomment-887171136
# Update 4/26/23, these URLs will randomly start returning 403
# mid-playback and I'm not sure why
gevent.spawn(fetch_player_response, 'android', video_id)
)
gevent.joinall(tasks)
util.check_gevent_exceptions(*tasks)
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)
# signature decryption
decryption_error = decrypt_signatures(info, video_id)
if decryption_error:
decryption_error = 'Error decrypting url signatures: ' + decryption_error
info['playability_error'] = decryption_error
# 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)
# check if urls ready (non-live format) in former livestream
# urls not ready if all of them have no filesize
if info['was_live']:
info['urls_ready'] = False
for fmt in info['formats']:
if fmt['file_size'] is not None:
info['urls_ready'] = True
else:
info['urls_ready'] = True
# signature decryption
decryption_error = decrypt_signatures(info, video_id)
if decryption_error:
decryption_error = 'Error decrypting url signatures: ' + decryption_error
info['playability_error'] = decryption_error
# 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
# check if urls ready (non-live format) in former livestream
# urls not ready if all of them have no filesize
if info['was_live']:
info['urls_ready'] = False
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
if fmt['file_size'] is not None:
info['urls_ready'] = True
else:
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):