Skip to content

Commit 66d0ae6

Browse files
authored
Merge pull request #541 from rdavydov/drops-2024-fix
fix #522
2 parents 63b8e77 + 94e81c9 commit 66d0ae6

File tree

2 files changed

+97
-10
lines changed

2 files changed

+97
-10
lines changed

TwitchChannelPointsMiner/classes/Twitch.py

+84-9
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
import re
1212
import string
1313
import time
14-
# from datetime import datetime
14+
# import json
15+
import requests
16+
import validators
1517
from pathlib import Path
1618
from secrets import choice, token_hex
17-
18-
# import json
19+
# from urllib.parse import quote
1920
# from base64 import urlsafe_b64decode
20-
21-
import requests
21+
# from datetime import datetime
2222

2323
from TwitchChannelPointsMiner.classes.entities.Campaign import Campaign
2424
from TwitchChannelPointsMiner.classes.entities.Drop import Drop
@@ -433,7 +433,8 @@ def send_minute_watched_events(self, streamers, priority, chunk_size=3):
433433
)
434434
> 30
435435
)
436-
and streamers[index].stream.minute_watched < 7 # fix #425
436+
# fix #425
437+
and streamers[index].stream.minute_watched < 7
437438
):
438439
streamers_watching.append(index)
439440
if len(streamers_watching) == 2:
@@ -467,14 +468,87 @@ def send_minute_watched_events(self, streamers, priority, chunk_size=3):
467468
streamers_watching = streamers_watching[:2]
468469

469470
for index in streamers_watching:
470-
next_iteration = time.time() + 60 / len(streamers_watching)
471+
# next_iteration = time.time() + 60 / len(streamers_watching)
472+
next_iteration = time.time() + 20 / len(streamers_watching)
471473

472474
try:
475+
####################################
476+
# Start of fix for 2024/5 API Change
477+
# Create the JSON data for the GraphQL request
478+
json_data = copy.deepcopy(
479+
GQLOperations.PlaybackAccessToken)
480+
json_data["variables"] = {
481+
"login": streamers[index].username,
482+
"isLive": True,
483+
"isVod": False,
484+
"vodID": "",
485+
# "playerType": "site"
486+
"playerType": "picture-by-picture"
487+
}
488+
489+
# Get signature and value using the post_gql_request method
490+
responsePlaybackAccessToken = self.post_gql_request(
491+
json_data)
492+
logger.debug(
493+
f"Sent PlaybackAccessToken request for {streamers[index]}"
494+
)
495+
signature = responsePlaybackAccessToken["data"]['streamPlaybackAccessToken']["signature"]
496+
value = responsePlaybackAccessToken["data"]['streamPlaybackAccessToken']["value"]
497+
if not signature or not value:
498+
continue
499+
500+
# encoded_value = quote(json.dumps(value))
501+
502+
# Construct the URL for the broadcast qualities
503+
RequestBroadcastQualitiesURL = f"https://usher.ttvnw.net/api/channel/hls/{streamers[index].username}.m3u8?sig={signature}&token={value}"
504+
505+
# Get list of video qualities
506+
responseBroadcastQualities = requests.get(RequestBroadcastQualitiesURL, headers={
507+
"User-Agent": self.user_agent}, timeout=20) # timeout=60
508+
logger.debug(
509+
f"Send RequestBroadcastQualitiesURL request for {streamers[index]} - Status code: {responseBroadcastQualities.status_code}"
510+
)
511+
if responseBroadcastQualities.status_code != 200:
512+
continue
513+
BroadcastQualities = responseBroadcastQualities.text
514+
515+
# Just takes the last line, which should be the URL for the lowest quality
516+
BroadcastLowestQualityURL = BroadcastQualities.split(
517+
"\n")[-1]
518+
if not validators.url(BroadcastLowestQualityURL):
519+
continue
520+
521+
# Get list of video URLs
522+
responseStreamURLList = requests.get(BroadcastLowestQualityURL, headers={
523+
"User-Agent": self.user_agent}, timeout=20) # timeout=60
524+
logger.debug(
525+
f"Send BroadcastLowestQualityURL request for {streamers[index]} - Status code: {responseStreamURLList.status_code}"
526+
)
527+
if responseStreamURLList.status_code != 200:
528+
continue
529+
StreamURLList = responseStreamURLList.text
530+
531+
# Just takes the last line, which should be the URL for the lowest quality
532+
StreamLowestQualityURL = StreamURLList.split("\n")[-2]
533+
if not validators.url(StreamLowestQualityURL):
534+
continue
535+
536+
# Perform a HEAD request to simulate watching the stream
537+
responseStreamLowestQualityURL = requests.head(StreamLowestQualityURL, headers={
538+
"User-Agent": self.user_agent}, timeout=20) # timeout=60
539+
logger.debug(
540+
f"Send StreamLowestQualityURL request for {streamers[index]} - Status code: {responseStreamLowestQualityURL.status_code}"
541+
)
542+
if responseStreamLowestQualityURL.status_code != 200:
543+
continue
544+
# End of fix for 2024/5 API Change
545+
##################################
473546
response = requests.post(
474547
streamers[index].stream.spade_url,
475548
data=streamers[index].stream.encode_payload(),
476549
headers={"User-Agent": self.user_agent},
477-
timeout=60,
550+
# timeout=60,
551+
timeout=20,
478552
)
479553
logger.debug(
480554
f"Send minute watched request for {streamers[index]} - Status code: {response.status_code}"
@@ -543,7 +617,8 @@ def send_minute_watched_events(self, streamers, priority, chunk_size=3):
543617
)
544618

545619
if streamers_watching == []:
546-
self.__chuncked_sleep(60, chunk_size=chunk_size)
620+
# self.__chuncked_sleep(60, chunk_size=chunk_size)
621+
self.__chuncked_sleep(20, chunk_size=chunk_size)
547622
except Exception:
548623
logger.error(
549624
"Exception raised in send minute watched", exc_info=True)

TwitchChannelPointsMiner/constants.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
# Twitch endpoints
2-
URL = "https://www.twitch.tv"
2+
URL = "https://www.twitch.tv" # Browser, Apps
3+
# URL = "https://m.twitch.tv" # Mobile Browser
4+
# URL = "https://android.tv.twitch.tv" # TV
35
IRC = "irc.chat.twitch.tv"
46
IRC_PORT = 6667
57
WEBSOCKET = "wss://pubsub-edge.twitch.tv/v1"
68
CLIENT_ID = "ue6666qo983tsx6so1t0vnawi233wa" # TV
79
# CLIENT_ID = "kimne78kx3ncx6brgo4mv6wki5h1ko" # Browser
10+
# CLIENT_ID = "r8s4dac0uhzifbpu9sjdiwzctle17ff" # Mobile Browser
811
# CLIENT_ID = "kd1unb4b3q4t58fwlpcbzcbnm76a8fp" # Android App
912
# CLIENT_ID = "851cqzxpb9bqu9z6galo155du" # iOS App
1013
DROP_ID = "c2542d6d-cd10-4532-919b-3d19f30a768b"
@@ -46,6 +49,15 @@ class GQLOperations:
4649
}
4750
},
4851
}
52+
PlaybackAccessToken = {
53+
"operationName": "PlaybackAccessToken",
54+
"extensions": {
55+
"persistedQuery": {
56+
"version": 1,
57+
"sha256Hash": "3093517e37e4f4cb48906155bcd894150aef92617939236d2508f3375ab732ce",
58+
}
59+
},
60+
}
4961
VideoPlayerStreamInfoOverlayChannel = {
5062
"operationName": "VideoPlayerStreamInfoOverlayChannel",
5163
"extensions": {

0 commit comments

Comments
 (0)