Skip to content

Commit

Permalink
Fix remote watch history not updating #1008
Browse files Browse the repository at this point in the history
(cherry picked from commit 5b10047)

# Conflicts:
#	resources/lib/youtube_plugin/youtube/client/request_client.py
#	resources/lib/youtube_plugin/youtube/helper/stream_info.py
  • Loading branch information
MoojMidge committed Dec 4, 2024
1 parent a186542 commit 2180ae6
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 60 deletions.
45 changes: 34 additions & 11 deletions resources/lib/youtube_plugin/youtube/client/request_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ class YouTubeRequestClient(BaseRequestsClass):
},
'ios': {
'_id': 5,
'_auth_type': False,
'_os': {
'major': '17',
'minor': '5',
Expand Down Expand Up @@ -261,6 +262,7 @@ class YouTubeRequestClient(BaseRequestsClass):
},
'_common': {
'_access_token': None,
'_access_token_tv': None,
'json': {
'contentCheckOk': True,
'context': {
Expand All @@ -286,13 +288,11 @@ class YouTubeRequestClient(BaseRequestsClass):
'videoId': None,
},
'headers': {
'Origin': 'https://www.youtube.com',
'Referer': 'https://www.youtube.com/watch?v={json[videoId]}',
'Accept-Encoding': 'gzip, deflate',
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.5',
'Authorization': 'Bearer {_access_token}',
'Authorization': None,
},
'params': {
'key': ValueError,
Expand Down Expand Up @@ -369,31 +369,53 @@ def json_traverse(cls, json_data, path, default=None):
def build_client(cls, client_name=None, data=None):
templates = {}

client = None
base_client = None
if client_name:
client = cls.CLIENTS.get(client_name)
if client and client.get('_disabled'):
base_client = cls.CLIENTS.get(client_name)
if base_client and base_client.get('_disabled'):
return None
if not client:
client = YouTubeRequestClient.CLIENTS['web']
client = client.copy()
if not base_client:
base_client = YouTubeRequestClient.CLIENTS['web']
base_client = base_client.copy()

if data:
client = merge_dicts(client, data)
client = merge_dicts(base_client, data)
client = merge_dicts(cls.CLIENTS['_common'], client, templates)
client['_name'] = client_name
if base_client.get('_auth_required'):
client['_auth_required'] = True

for values, template_id, template in templates.values():
if template_id in values:
values[template_id] = template.format(**client)

has_auth = False
try:
params = client['params']
if client.get('_access_token'):
auth_required = client.get('_auth_required')
auth_requested = client.get('_auth_requested')
auth_type = client.get('_auth_type')
if auth_type == 'tv' and auth_requested != 'personal':
auth_token = client.get('_access_token_tv')
elif auth_type is not False:
auth_token = client.get('_access_token')
else:
auth_token = None

if auth_token and (auth_required or auth_requested):
headers = client['headers']
if 'Authorization' in headers:
headers = headers.copy()
headers['Authorization'] = 'Bearer {0}'.format(auth_token)
client['headers'] = headers
has_auth = True

if 'key' in params:
params = params.copy()
del params['key']
client['params'] = params
elif auth_required:
return None
else:
headers = client['headers']
if 'Authorization' in headers:
Expand All @@ -407,5 +429,6 @@ def build_client(cls, client_name=None, data=None):
client['params'] = params
except KeyError:
pass
client['_has_auth'] = has_auth

return client
54 changes: 28 additions & 26 deletions resources/lib/youtube_plugin/youtube/client/youtube.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,24 @@ class YouTube(LoginClient):
'Host': 'www.youtube.com',
},
},
'watch_history': {
'_auth_required': True,
'_auth_type': 'personal',
'_video_id': None,
'headers': {
'Host': 's.youtube.com',
'Referer': 'https://www.youtube.com/watch?v={_video_id}',
},
'params': {
'referrer': 'https://accounts.google.com/',
'ns': 'yt',
'el': 'detailpage',
'ver': '2',
'fs': '0',
'volume': '100',
'muted': '0',
},
},
'_common': {
'_access_token': None,
'_access_token_tv': None,
Expand Down Expand Up @@ -155,29 +173,13 @@ def update_watch_history(self, context, video_id, url, status=None):
et=et,
state=state))

headers = {
'Host': 's.youtube.com',
'Connection': 'keep-alive',
'Accept-Encoding': 'gzip, deflate',
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.5',
'DNT': '1',
'Referer': 'https://www.youtube.com/watch?v=' + video_id,
'User-Agent': ('Mozilla/5.0 (Linux; Android 10; SM-G981B)'
' AppleWebKit/537.36 (KHTML, like Gecko)'
' Chrome/80.0.3987.162 Mobile Safari/537.36'),
}
params = {
'docid': video_id,
'referrer': 'https://accounts.google.com/',
'ns': 'yt',
'el': 'detailpage',
'ver': '2',
'fs': '0',
'volume': '100',
'muted': '0',
client_data = {
'_video_id': video_id,
'url': url,
'error_title': 'Failed to update watch history',
}

params = {}
if cmt is not None:
params['cmt'] = format(cmt, '.3f')
if st is not None:
Expand All @@ -186,11 +188,11 @@ def update_watch_history(self, context, video_id, url, status=None):
params['et'] = format(et, '.3f')
if state is not None:
params['state'] = state
if self._access_token:
params['access_token'] = self._access_token

self.request(url, params=params, headers=headers,
error_msg='Failed to update watch history')
self.api_request(client='watch_history',
client_data=client_data,
params=params,
no_content=True)

def get_streams(self,
context,
Expand Down
62 changes: 39 additions & 23 deletions resources/lib/youtube_plugin/youtube/helper/stream_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,8 @@ def __init__(self,
self._calculate_n = True
self._cipher = None

self._selected_client = None
self._auth_client = {}
self._selected_client = {}
self._client_groups = {
'custom': clients if clients else (),
# Access "premium" streams, HLS and DASH
Expand Down Expand Up @@ -1357,6 +1358,7 @@ def load_stream_info(self, video_id):
audio_only = self._audio_only
ask_for_quality = self._ask_for_quality
use_mpd = self._use_mpd
use_remote_history = settings.use_remote_history()

client_name = None
_client = None
Expand Down Expand Up @@ -1394,20 +1396,24 @@ def load_stream_info(self, video_id):
}
abort = False

client_data = {'json': {'videoId': video_id}}
access_token = self._access_token
auth = False
has_access_token = bool(self._access_token)
client_data = {
'json': {
'videoId': video_id,
},
'_auth_required': False,
'_auth_requested': 'personal' if use_remote_history else False,
'_access_token': self._access_token,
}

for name, clients in self._client_groups.items():
if not clients:
continue
if name == 'mpd' and not use_mpd:
if name == 'mpd' and not (use_mpd or use_remote_history):
continue
if name == 'ask' and use_mpd and not ask_for_quality:
continue

status = None

restart = False
while 1:
for client_name in clients:
Expand All @@ -1424,7 +1430,7 @@ def load_stream_info(self, video_id):
error_hook_kwargs={
'video_id': video_id,
'client': client_name,
'auth': bool(_client.get('_access_token')),
'auth': _client.get('_has_auth', False),
},
**_client
) or {}
Expand Down Expand Up @@ -1469,14 +1475,13 @@ def load_stream_info(self, video_id):
reason=reason or 'UNKNOWN',
video_id=video_id,
client=_client['_name'],
auth=auth,
auth=_client.get('_has_auth', False),
)
)
compare_reason = reason.lower()
if any(why in compare_reason for why in reauth_reasons):
if access_token and not auth:
auth = True
client_data['_access_token'] = access_token
if has_access_token:
client_data['_auth_required'] = True
restart = True
break
if any(why in compare_reason for why in retry_reasons):
Expand Down Expand Up @@ -1510,14 +1515,19 @@ def load_stream_info(self, video_id):
.format(
video_id=video_id,
client=client_name,
auth=bool(_client.get('_access_token')),
auth=_client.get('_has_auth', False),
)
)
if not self._selected_client:
client = self._selected_client = _client.copy()
result = _result
video_details = result.get('videoDetails', {})
playability = result.get('playabilityStatus', {})
self._selected_client = {
'client': _client.copy(),
'result': _result,
}
if not self._auth_client and _client.get('_has_auth'):
self._auth_client = {
'client': _client.copy(),
'result': _result,
}

_streaming_data = _result.get('streamingData', {})
if audio_only or ask_for_quality or not use_mpd:
Expand Down Expand Up @@ -1549,6 +1559,9 @@ def load_stream_info(self, video_id):
reason = self._get_error_details(playability)
raise YouTubeException(reason or 'UNKNOWN')

client = self._selected_client['client']
result = self._selected_client['result']

if 'Authorization' in client['headers']:
del client['headers']['Authorization']
# Make a set of URL-quoted headers to be sent to Kodi when requesting
Expand All @@ -1557,8 +1570,7 @@ def load_stream_info(self, video_id):
# curl_headers = self._make_curl_headers(headers, cookies)
curl_headers = self._prepare_headers(client['headers'])

microformat = (result.get('microformat', {})
.get('playerMicroformatRenderer', {}))
video_details = result.get('videoDetails', {})
is_live = video_details.get('isLiveContent', False)
if is_live:
is_live = video_details.get('isLive', False)
Expand All @@ -1568,6 +1580,8 @@ def load_stream_info(self, video_id):
live_dvr = False
thumb_suffix = ''

microformat = (result.get('microformat', {})
.get('playerMicroformatRenderer', {}))
meta_info = {
'id': video_id,
'title': unescape(video_details.get('title', '')
Expand Down Expand Up @@ -1597,12 +1611,14 @@ def load_stream_info(self, video_id):
'subtitles': None,
}

if settings.use_remote_history():
if use_remote_history and self._auth_client:
playback_stats = {
'playback_url': 'videostatsPlaybackUrl',
'watchtime_url': 'videostatsWatchtimeUrl',
}
playback_tracking = result.get('playbackTracking', {})
playback_tracking = (self._auth_client
.get('result', {})
.get('playbackTracking', {}))
cpn = self._generate_cpn()

for key, url_key in playback_stats.items():
Expand Down Expand Up @@ -1637,7 +1653,7 @@ def load_stream_info(self, video_id):
'',
'',
)) + '||R{{SSM}}|R',
'token': access_token,
'token': self._access_token,
}
break
else:
Expand Down Expand Up @@ -1699,7 +1715,7 @@ def load_stream_info(self, video_id):
error_hook_kwargs={
'video_id': video_id,
'client': client_name,
'auth': bool(caption_client.get('_access_token')),
'auth': _client.get('_has_auth', False),
},
**caption_client
)
Expand Down

0 comments on commit 2180ae6

Please sign in to comment.