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,12 +431,9 @@ 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:
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
@@ -456,8 +453,6 @@ class RateLimitedQueue(gevent.queue.Queue):
self.currently_empty = False
self.lock.release()
return item
@@ -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,20 +370,10 @@ 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):
for client in INNERTUBE_CLIENTS:
tasks = (
# Get video metadata from here
gevent.spawn(fetch_watch_page_info, video_id, playlist_id, index),
# 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.spawn(fetch_player_response, client, video_id) # Use client from INNERTUBE_CLIENTS
)
gevent.joinall(tasks)
util.check_gevent_exceptions(*tasks)