-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
157 lines (127 loc) · 4.89 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import sys
import time
import uuid
import requests
from loguru import logger
import click
from typing import Any
import config
import creds
from helpers import utils
from helpers.protohelper import ProtoHelper
NoneType = type(None)
class ShitStar(object):
def __init__(self, title_id, refresh, search, manifest):
"""
Initialise the ShitStar class
:param title_id:
:param refresh:
:param search:
:param manifest:
"""
self.session = requests.Session()
self.title_id = title_id
self.refresh = refresh
self.search = search
self.manifest_type = manifest
if not ((self.title_id is not None) ^ (self.search is not None)):
logger.error("Please specify only either a Title ID or a search parameter!")
exit(0)
self.device_id = utils.get_device_id()
self.mobile_number = creds.PHONE_NUMBER
self.user_token = None
self.session.headers.update({
"User-Agent": config.USER_AGENT,
"Content-Type": config.PROTOBUF_CONTENT_TYPE,
"app_name": "android",
"X-Country-Code": "in",
"X-HS-App": config.APP_VALUE,
"X-HS-APP-ID": config.APP_VALUE_ID,
"X-HS-Client": config.HS_CLIENT,
"X-HS-Client-Targeting": config.HS_CLIENT_TARGETING.format(self.device_id),
"X-HS-Device-Id": self.device_id,
"X-HS-Platform": "android",
"X-HS-Schema-Version": config.HS_SCHEMA_VERSION
})
self.set_auth()
self.user_token = utils.get_creds().get('user_token')
self.session.headers.update({
"X-HS-Usertoken": self.user_token
})
if self.title_id is not None:
self.get_manifest()
else:
self.search_title()
def set_auth(self) -> None:
"""
Sets authentication credentials. If not cached,
logs in to the service.
:return: None
"""
if self.refresh:
logger.debug("Refreshing token..")
utils.dump_creds(self.login())
logger.info("Attempting to load credentials..")
credict = utils.get_creds()
if not credict.get("user_token"):
logger.error("Invalid credentials! Attempting to log in..")
utils.dump_creds(self.login())
logger.info(f"Saved token to {config.CREDFILE}")
def login(self) -> dict:
"""
Logs into Hotstar with phone number
and OTP.
:return: credentials
"""
s = self.session.post(url=config.OTP_ENDPOINT, params={
"action": "sendOtp"
}, headers={
"X-HS-Request-Id": str(uuid.uuid4()),
"hotstarauth": utils.get_hs_auth(),
"X-Hs-UserToken": utils.get_guest_token(self.session, self.device_id)
}, data=ProtoHelper.get_otp(self.mobile_number))
if "Enter OTP sent to" not in s.text: # TODO: figure out a way to do proper validation
logger.error("Some error occurred! Please debug.")
sys.exit(0)
s = self.session.post(url=config.OTP_VERIFY_ENDPOINT, params={
"action": "verifyOtp"
}, headers={
"X-HS-Request-Id": str(uuid.uuid4()),
"hotstarauth": utils.get_hs_auth(),
"X-Hs-UserToken": utils.get_guest_token(self.session, self.device_id)
}, data=ProtoHelper.verify_otp(self.mobile_number, input("Enter OTP: ")))
login_response = ProtoHelper.parse_success_widget(s.content)
cred_dict = {
"user_token": login_response,
"mobile_number": self.mobile_number,
"time": int(time.time())
}
return cred_dict
def search_title(self) -> dict:
s = self.session.get(url=config.SEARCH_URL, params={
"search_query": self.search,
})
# print(s.content.hex())
return {}
def get_manifest(self) -> None:
s = self.session.get(url=config.WATCH_URL, params={
"content_id": self.title_id,
"filters": "content_type=movie",
"client_capabilities": config.CLIENT_CAPABILITIES,
"drm_parameters": config.DRM_CAPABILITIES_WV
}, headers={
"app_name": "android",
"hotstarauth": utils.get_hs_auth()
})
# print(s.content)
@click.command()
@click.option('-r', '--refresh', is_flag=True, help='Refresh access token')
@click.option('-s', '--search', help='Search for a title')
@click.option('-m', '--manifest', type=click.Choice(['DASH', 'MSS', 'HLS']), required=True, help='Retrieve manifest type')
@click.option('-t', '--title-id', help="ID for the title")
@logger.catch
def main(title_id: str, manifest: str, refresh: bool = False, search: bool = False) -> None:
logger.info("Welcome to 💩⭐")
shitstar = ShitStar(title_id, refresh, search, manifest)
if __name__ == "__main__":
main()