Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
ed4b05d9b6 | |||
6f88b1cec6 | |||
03451fb8ae | |||
e45c3fd48b | |||
1153ac8f24 |
@ -53,7 +53,7 @@ def request_comments(ctoken, replies=False):
|
|||||||
'hl': 'en',
|
'hl': 'en',
|
||||||
'gl': 'US',
|
'gl': 'US',
|
||||||
'clientName': 'MWEB',
|
'clientName': 'MWEB',
|
||||||
'clientVersion': '2.20240328.08.00',
|
'clientVersion': '2.20210804.02.00',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'continuation': ctoken.replace('=', '%3D'),
|
'continuation': ctoken.replace('=', '%3D'),
|
||||||
@ -78,7 +78,7 @@ def single_comment_ctoken(video_id, comment_id):
|
|||||||
|
|
||||||
def post_process_comments_info(comments_info):
|
def post_process_comments_info(comments_info):
|
||||||
for comment in comments_info['comments']:
|
for comment in comments_info['comments']:
|
||||||
comment['author'] = strip_non_ascii(comment['author'])
|
comment['author'] = strip_non_ascii(comment['author']) if comment.get('author') else ""
|
||||||
comment['author_url'] = concat_or_none(
|
comment['author_url'] = concat_or_none(
|
||||||
'/', comment['author_url'])
|
'/', comment['author_url'])
|
||||||
comment['author_avatar'] = concat_or_none(
|
comment['author_avatar'] = concat_or_none(
|
||||||
@ -189,10 +189,10 @@ def video_comments(video_id, sort=0, offset=0, lc='', secret_key=''):
|
|||||||
comments_info['error'] += '\n\n' + e.error_message
|
comments_info['error'] += '\n\n' + e.error_message
|
||||||
comments_info['error'] += '\n\nExit node IP address: %s' % e.ip
|
comments_info['error'] += '\n\nExit node IP address: %s' % e.ip
|
||||||
else:
|
else:
|
||||||
comments_info['error'] = 'YouTube blocked the request. IP address: %s' % e.ip
|
comments_info['error'] = 'YouTube blocked the request. Error: %s' % str(e)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
comments_info['error'] = 'YouTube blocked the request. IP address: %s' % e.ip
|
comments_info['error'] = 'YouTube blocked the request. Error: %s' % str(e)
|
||||||
|
|
||||||
if comments_info.get('error'):
|
if comments_info.get('error'):
|
||||||
print('Error retrieving comments for ' + str(video_id) + ':\n' +
|
print('Error retrieving comments for ' + str(video_id) + ':\n' +
|
||||||
|
@ -5,8 +5,9 @@ function changeQuality(selection) {
|
|||||||
let videoPaused = video.paused;
|
let videoPaused = video.paused;
|
||||||
let videoSpeed = video.playbackRate;
|
let videoSpeed = video.playbackRate;
|
||||||
let srcInfo;
|
let srcInfo;
|
||||||
if (avMerge)
|
if (avMerge && typeof avMerge.close === 'function') {
|
||||||
avMerge.close();
|
avMerge.close();
|
||||||
|
}
|
||||||
if (selection.type == 'uni'){
|
if (selection.type == 'uni'){
|
||||||
srcInfo = data['uni_sources'][selection.index];
|
srcInfo = data['uni_sources'][selection.index];
|
||||||
video.src = srcInfo.url;
|
video.src = srcInfo.url;
|
||||||
|
@ -128,6 +128,29 @@ header {
|
|||||||
background-color: var(--buttom-hover);
|
background-color: var(--buttom-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.live-url-choices {
|
||||||
|
background-color: var(--thumb-background);
|
||||||
|
margin: 1rem 0;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playability-error {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 30vh;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playability-error > span {
|
||||||
|
display: flex;
|
||||||
|
background-color: var(--thumb-background);
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.playlist {
|
.playlist {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: 4px;
|
grid-gap: 4px;
|
||||||
@ -622,6 +645,9 @@ figure.sc-video {
|
|||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
.playability-error {
|
||||||
|
height: 60vh;
|
||||||
|
}
|
||||||
.playlist {
|
.playlist {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: 1px;
|
grid-gap: 1px;
|
||||||
|
@ -840,6 +840,8 @@ def call_youtube_api(client, api, data):
|
|||||||
|
|
||||||
def strip_non_ascii(string):
|
def strip_non_ascii(string):
|
||||||
''' Returns the string without non ASCII characters'''
|
''' Returns the string without non ASCII characters'''
|
||||||
|
if string is None:
|
||||||
|
return ""
|
||||||
stripped = (c for c in string if 0 < ord(c) < 127)
|
stripped = (c for c in string if 0 < ord(c) < 127)
|
||||||
return ''.join(stripped)
|
return ''.join(stripped)
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
__version__ = 'v0.3.1'
|
__version__ = 'v0.3.2'
|
||||||
|
@ -367,32 +367,42 @@ def fetch_watch_page_info(video_id, playlist_id, index):
|
|||||||
watch_page = watch_page.decode('utf-8')
|
watch_page = watch_page.decode('utf-8')
|
||||||
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):
|
||||||
|
primary_client = 'android_vr'
|
||||||
|
fallback_client = 'ios'
|
||||||
|
last_resort_client = 'tv_embedded'
|
||||||
|
|
||||||
tasks = (
|
tasks = (
|
||||||
# Get video metadata from here
|
# Get video metadata from here
|
||||||
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, 'android_vr', video_id)
|
gevent.spawn(fetch_player_response, primary_client, video_id)
|
||||||
)
|
)
|
||||||
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 = tasks[0].value or {}
|
||||||
|
player_response = tasks[1].value or {}
|
||||||
|
|
||||||
yt_data_extract.update_with_new_urls(info, player_response)
|
yt_data_extract.update_with_new_urls(info, player_response)
|
||||||
|
|
||||||
# Age restricted video, retry
|
# Fallback to 'ios' if no valid URLs are found
|
||||||
if info['age_restricted'] or info['player_urls_missing']:
|
if not info.get('formats') or info.get('player_urls_missing'):
|
||||||
if info['age_restricted']:
|
print(f"No URLs found in '{primary_client}', attempting with '{fallback_client}'.")
|
||||||
print('Age restricted video, retrying')
|
player_response = fetch_player_response(fallback_client, video_id) or {}
|
||||||
else:
|
yt_data_extract.update_with_new_urls(info, player_response)
|
||||||
print('Player urls missing, retrying')
|
|
||||||
player_response = fetch_player_response('tv_embedded', video_id)
|
# Final attempt with 'tv_embedded' if there are still no URLs
|
||||||
|
if not info.get('formats') or info.get('player_urls_missing'):
|
||||||
|
print(f"No URLs found in '{fallback_client}', attempting with '{last_resort_client}'")
|
||||||
|
player_response = fetch_player_response(last_resort_client, video_id) or {}
|
||||||
yt_data_extract.update_with_new_urls(info, player_response)
|
yt_data_extract.update_with_new_urls(info, player_response)
|
||||||
|
|
||||||
# signature decryption
|
# signature decryption
|
||||||
decryption_error = decrypt_signatures(info, video_id)
|
if info.get('formats'):
|
||||||
if decryption_error:
|
decryption_error = decrypt_signatures(info, video_id)
|
||||||
decryption_error = 'Error decrypting url signatures: ' + decryption_error
|
if decryption_error:
|
||||||
info['playability_error'] = decryption_error
|
info['playability_error'] = 'Error decrypting url signatures: ' + decryption_error
|
||||||
|
|
||||||
# check if urls ready (non-live format) in former livestream
|
# check if urls ready (non-live format) in former livestream
|
||||||
# urls not ready if all of them have no filesize
|
# urls not ready if all of them have no filesize
|
||||||
@ -406,21 +416,21 @@ def extract_info(video_id, use_invidious, playlist_id=None, index=None):
|
|||||||
|
|
||||||
# livestream urls
|
# livestream urls
|
||||||
# sometimes only the livestream urls work soon after the livestream is over
|
# sometimes only the livestream urls work soon after the livestream is over
|
||||||
if (info['hls_manifest_url']
|
info['hls_formats'] = []
|
||||||
and (info['live'] or not info['formats'] or not info['urls_ready'])
|
if info.get('hls_manifest_url') and (info.get('live') or not info.get('formats') or not info['urls_ready']):
|
||||||
):
|
try:
|
||||||
manifest = util.fetch_url(info['hls_manifest_url'],
|
manifest = util.fetch_url(info['hls_manifest_url'],
|
||||||
debug_name='hls_manifest.m3u8',
|
debug_name='hls_manifest.m3u8',
|
||||||
report_text='Fetched hls manifest'
|
report_text='Fetched hls manifest'
|
||||||
).decode('utf-8')
|
).decode('utf-8')
|
||||||
|
info['hls_formats'], err = yt_data_extract.extract_hls_formats(manifest)
|
||||||
info['hls_formats'], err = yt_data_extract.extract_hls_formats(manifest)
|
if not err:
|
||||||
if not err:
|
info['playability_error'] = None
|
||||||
info['playability_error'] = None
|
for fmt in info['hls_formats']:
|
||||||
for fmt in info['hls_formats']:
|
fmt['video_quality'] = video_quality_string(fmt)
|
||||||
fmt['video_quality'] = video_quality_string(fmt)
|
except Exception as e:
|
||||||
else:
|
print(f"Error obteniendo HLS manifest: {e}")
|
||||||
info['hls_formats'] = []
|
info['hls_formats'] = []
|
||||||
|
|
||||||
# check for 403. Unnecessary for tor video routing b/c ip address is same
|
# check for 403. Unnecessary for tor video routing b/c ip address is same
|
||||||
info['invidious_used'] = False
|
info['invidious_used'] = False
|
||||||
|
Loading…
x
Reference in New Issue
Block a user