├── InstagramLive.py ├── README.md └── chat_server.py /InstagramLive.py: -------------------------------------------------------------------------------- 1 | from getpass import getpass 2 | from InstagramAPI import InstagramAPI 3 | import json 4 | import threading 5 | import time 6 | from InstagramAPI.exceptions import SentryBlockException 7 | import os 8 | import chat_server 9 | from http.server import HTTPServer 10 | 11 | 12 | class Main: 13 | 14 | def __init__(self): 15 | IG_NAME = input('Username: ') 16 | IG_PASS = getpass('Password: ') 17 | self.api = InstagramAPI(IG_NAME, IG_PASS) 18 | self.api.USER_AGENT = self.api.USER_AGENT.replace('10.26.0', '10.34.0') 19 | self.broadcast_id = 0 20 | self.chat_thread = threading.Thread(target=self.chat_job, daemon=True) 21 | self.isRunning = True 22 | 23 | def chat_job(self): 24 | server = HTTPServer(('', 4132), chat_server.ChatServer) 25 | 26 | while self.isRunning: 27 | server.handle_request() 28 | time.sleep(1) 29 | a = self.SendRequest(f'live/{self.broadcast_id}/get_comment/', last=False) 30 | try: 31 | if a['comments']: 32 | for comment in a['comments']: 33 | try: 34 | if comment['user']['username'] == self.api.username: 35 | chatter = 'Me' 36 | else: 37 | chatter = comment['user']['username'] 38 | comment_user = {'username': chatter, 'user_id': comment['user_id']} 39 | timestamp = time.gmtime(comment['created_at_utc']) 40 | comment_time = time.strftime('%H:%M:%S', timestamp) 41 | comment_text = comment['text'] 42 | comment_id = comment['pk'] 43 | chat = { 44 | 'id': comment_id, 45 | 'user': comment_user, 46 | 'text': comment_text, 47 | 'time': comment_time 48 | } 49 | except Exception as e: 50 | print(e) 51 | 52 | if chat in chat_server.GARBAGE: 53 | pass 54 | else: 55 | chat_server.GARBAGE.append(chat) 56 | 57 | except Exception as e: 58 | pass 59 | print('Comment Thread Stopped') 60 | 61 | def create_live(self, msg=''): 62 | data = json.dumps({ 63 | '_uuid': self.api.uuid, 64 | '_csrftoken': self.api.token, 65 | 'preview_height': 1280, 66 | 'preview_width': 720, 67 | 'broadcast_message': msg 68 | }) 69 | self.api.SendRequest('live/create/', self.api.generateSignature(data)) 70 | broadcast_id, live_url, stream_key = (self.api.LastJson['broadcast_id'], self.api.LastJson['upload_url'][:43], self.api.LastJson['upload_url'][43:]) 71 | 72 | print(f'[*]Broadcast ID: {broadcast_id}\n') 73 | print(f'[*]Live Upload URL: {live_url}\n') 74 | print(f'[*]Stream Key:\n{stream_key}\n\n') 75 | 76 | self.broadcast_id = broadcast_id 77 | 78 | return True 79 | 80 | def start_live(self): 81 | notify = True 82 | 83 | data = json.dumps({ 84 | '_uuid': self.api.uuid, 85 | '_csrftoken': self.api.token, 86 | 'should_send_notifications': notify 87 | }) 88 | self.api.SendRequest(f'live/{self.broadcast_id}/start/', self.api.generateSignature(data)) 89 | return True 90 | 91 | def end_live(self): 92 | data = json.dumps({ 93 | '_uid': self.api.username_id, 94 | '_uuid': self.api.uuid, 95 | '_csrftoken': self.api.token, 96 | 'end_after_copyright_warning': False 97 | }) 98 | 99 | return self.api.SendRequest(f'live/{self.broadcast_id}/end_broadcast/', self.api.generateSignature(data)) 100 | 101 | def save_post_live(self): 102 | trial = 3 103 | posted = bool 104 | while trial: 105 | trial -= 1 106 | data = json.dumps({ 107 | '_uid': self.api.username_id, 108 | '_uuid': self.api.uuid, 109 | '_csrftoken': self.api.token 110 | }) 111 | self.api.SendRequest(f'live/{self.broadcast_id}/add_to_post_live', self.api.generateSignature(data)) 112 | 113 | if self.api.LastResponse.status_code == 200: 114 | print('Live Posted to Story!') 115 | posted = True 116 | break 117 | if not posted: 118 | print('Vailed to post live to story!') 119 | 120 | def delete_post_live(self): 121 | data = json.dumps({ 122 | '_uid': self.api.username_id, 123 | '_uuid': self.api.uuid, 124 | '_csrftoken': self.api.token 125 | }) 126 | return self.api.SendRequest(f'live/{self.broadcast_id}/delete_post_live', self.api.generateSignature(data)) 127 | 128 | def pin_comment(self, comment_id): 129 | data = json.dumps({ 130 | 'offset_to_video_start': 0, 131 | 'comment_id': comment_id, 132 | '_uid': self.api.username_id, 133 | '_uuid': self.api.uuid, 134 | '_csrftoken': self.api.token 135 | }) 136 | return self.api.SendRequest(f'live/{self.broadcast_id}/pin_comment', self.api.generateSignature(data)) 137 | 138 | def unpin_comment(self, comment_id): 139 | data = json.dumps({ 140 | 'offset_to_video_start': 0, 141 | 'comment_id': comment_id, 142 | '_uid': self.api.username_id, 143 | '_uuid': self.api.uuid, 144 | '_csrftoken': self.api.token 145 | }) 146 | return self.api.SendRequest(f'live/{self.broadcast_id}/unpin_comment', self.api.generateSignature(data)) 147 | 148 | def SendRequest(self, endpoint, post=None, login=False, last=True): 149 | verify = False # don't show request warning 150 | if not last: 151 | if (not self.api.isLoggedIn and not login): 152 | raise Exception("Not logged in!\n") 153 | 154 | self.api.s.headers.update({'Connection': 'close', 155 | 'Accept': '*/*', 156 | 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 157 | 'Cookie2': '$Version=1', 158 | 'Accept-Language': 'en-US', 159 | 'User-Agent': self.api.USER_AGENT}) 160 | 161 | while True: 162 | try: 163 | if (post is not None): 164 | response = self.api.s.post(self.api.API_URL + endpoint, data=post, verify=verify) 165 | else: 166 | response = self.api.s.get(self.api.API_URL + endpoint, verify=verify) 167 | break 168 | except Exception as e: 169 | print('Except on SendRequest (wait 60 sec and resend): ' + str(e)) 170 | time.sleep(60) 171 | 172 | if response.status_code == 200: 173 | self.api.LastResponse = response 174 | self.api.LastJson = json.loads(response.text) 175 | else: 176 | print("Request return " + str(response.status_code) + " error!") 177 | # for debugging 178 | try: 179 | self.api.LastResponse = response 180 | self.api.LastJson = json.loads(response.text) 181 | print(self.api.LastJson) 182 | if 'error_type' in self.api.LastJson and self.api.LastJson['error_type'] == 'sentry_block': 183 | raise SentryBlockException(self.api.LastJson['message']) 184 | except SentryBlockException: 185 | raise 186 | except: 187 | pass 188 | return self.api.LastJson 189 | else: 190 | return self.api.SendRequest(endpoint, post=None, login=False) 191 | 192 | def live_info(self): 193 | _json = self.SendRequest(f'live/{self.broadcast_id}/info/', last=False) 194 | dash_url = _json['dash_playback_url'] 195 | viewer_count = _json['viewer_count'] 196 | _id = _json['id'] 197 | owner = _json['broadcast_owner']['username'] 198 | status = _json['broadcast_status'] 199 | 200 | print(f'[*]ID: {_id}') 201 | print(f'[*]Broadcast Owner: {owner}') 202 | print(f'[*]Dash URL: {dash_url}') 203 | print(f'[*]Viewer Count: {viewer_count}') 204 | print(f'[*]Status: {status}') 205 | 206 | def send_comment(self, msg): 207 | data = json.dumps({ 208 | 'idempotence_token': self.api.generateUUID(True), 209 | 'comment_text': msg, 210 | 'live_or_vod': 1, 211 | 'offset_to_video_start': 0 212 | }) 213 | 214 | _json = self.SendRequest(f'live/{self.broadcast_id}/comment/', self.api.generateSignature(data), last=False) 215 | if _json['status'] == 'ok': 216 | return True 217 | 218 | def exit(self): 219 | self.end_live() 220 | print('Save Live replay to story ? ') 221 | save = input('command> ') 222 | if save == 'y': 223 | self.save_post_live() 224 | else: 225 | self.delete_post_live() 226 | print('Exiting...') 227 | self.isRunning = False 228 | self.chat_thread.join() 229 | print('All process Stopped') 230 | time.sleep(1) 231 | print('Cleaning cache...') 232 | 233 | def wave(self, userid): 234 | 235 | data = json.dumps({ 236 | '_uid': self.api.username_id, 237 | '_uuid': self.api.uuid, 238 | '_csrftoken': self.api.token, 239 | 'viewer_id': userid 240 | }) 241 | 242 | _json = self.SendRequest(f'live/{self.broadcast_id}/wave/', self.api.generateSignature(data), last=False) 243 | if _json: 244 | return True 245 | 246 | def get_viewer_list(self): 247 | _json = self.SendRequest(f'live/{self.broadcast_id}/get_viewer_list/', last=False) 248 | users = [] 249 | ids = [] 250 | for user in _json['users']: 251 | users.append(f"{user['username']}") 252 | ids.append(f"{user['pk']}") 253 | 254 | return users, ids 255 | 256 | def run(self): 257 | print('[!]logging in...') 258 | if self.api.login(): 259 | print('[*]logged in!!') 260 | print('[!]Generating stream upload_url and keys...\n') 261 | self.create_live() 262 | print('Now Open Broadcast Software and start streaming on given url and key') 263 | input('Press Enter after your Broadcast Software started streaming\n') 264 | self.start_live() 265 | self.chat_thread.start() 266 | print("Chat Server Started") 267 | 268 | while self.isRunning: 269 | cmd = input('command> ') 270 | 271 | if cmd == 'stop': 272 | self.exit() 273 | 274 | elif cmd == 'wave': 275 | users, ids = self.get_viewer_list() 276 | for i in range(len(users)): 277 | print(f'{i+1}. {users[i]}') 278 | print('Type number according to user e.g 1.') 279 | while True: 280 | cmd = input('number> ') 281 | 282 | if cmd == 'back': 283 | break 284 | try: 285 | userid = int(cmd) - 1 286 | self.wave(ids[userid]) 287 | break 288 | except: 289 | print('Please type number e.g 1') 290 | 291 | elif cmd == 'info': 292 | self.live_info() 293 | 294 | elif cmd == 'viewers': 295 | users, ids = self.get_viewer_list() 296 | print(users) 297 | 298 | elif cmd[:4] == 'chat': 299 | to_send = cmd[5:] 300 | if to_send: 301 | self.send_comment(to_send) 302 | else: 303 | print('usage: chat ') 304 | 305 | else: 306 | print('Available commands:\n\t"stop"\n\t"info"\n\t"viewers"\n\t"chat"\n\t"wave"') 307 | 308 | 309 | if __name__ == '__main__': 310 | main = Main() 311 | main.run() 312 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #[DEPRECATED] 2 | 3 | # InstagramLive-Python 4 | This Repo is based on [mgp25's Instagram-API](https://github.com/mgp25/Instagram-API) and use [LevPasha's Instagram-API python module](https://github.com/LevPasha/Instagram-API-python) 5 | 6 | # Current Feature: 7 | 1. Get & send chat 8 | 2. Wave 9 | 3. Get Viewer List 10 | 4. Get Broadcast Info 11 | 12 | # How To: 13 | * Make sure you have installed InstagramAPI python from source 14 | * Run InstagramLive.py script. 15 | 16 | $python3 InstagramLive.py 17 | * Login 18 | * Input given stream upload URL and stream keys to your Broadcast Software and start streaming. 19 | * Test using 'info' command 20 | 21 | command> info 22 | [*]ID: 17949131626266654 23 | [*]Broadcast Owner: 3.5mm_jack 24 | [*]Dash URL: https://instagram.fsub8-1.fna.fbcdn.net/hvideo-atn1/v/rZ4kf4qOxlQ2zFHx4V2Ng/live-dash/dash-hd/17949131626266654.mpd 25 | [*]Viewer Count: 0.0 26 | [*]Status: active 27 | 28 | * http://localhost:4132 to view chat 29 | 30 | 31 | # To-DO: 32 | 1. I Dont Know 33 | -------------------------------------------------------------------------------- /chat_server.py: -------------------------------------------------------------------------------- 1 | from http.server import BaseHTTPRequestHandler 2 | import json 3 | 4 | hostName = "" 5 | hostPort = 80 6 | 7 | PAGE = """ 8 | 9 | 10 | 11 | 12 | 51 | 52 | 53 | 54 | 77 |
78 |
79 |

Live Chat

80 |
81 |
82 |
83 |
84 | 85 | 86 | """ 87 | 88 | GARBAGE = [] 89 | BIASA = bool 90 | response = None 91 | 92 | class ChatServer(BaseHTTPRequestHandler): 93 | 94 | def do_GET(self): 95 | global BIASA 96 | global GARBAGE 97 | global response 98 | 99 | if self.path == '/': 100 | response = bytes(PAGE, "utf-8") 101 | types = 'text/html' 102 | BIASA = True 103 | 104 | elif self.path == '/chat': 105 | garbage = GARBAGE 106 | if len(garbage): 107 | this = garbage[len(garbage) - 1] 108 | else: 109 | this = GARBAGE 110 | response = bytes(json.dumps(this), "utf-8") 111 | types = 'application/json' 112 | BIASA = True 113 | elif self.path == '/Fonts/OpenSans-Regular.ttf': 114 | types = 'application/octet-stream' 115 | else: 116 | BIASA = False 117 | 118 | self.send_response(200) 119 | if BIASA: 120 | self.send_header('Content-Type', types) 121 | if types == 'application/json': 122 | self.send_header('Content-Length', str(len(response))) 123 | self.end_headers() 124 | self.wfile.write(response) 125 | else: 126 | self.send_header('Content-Type', 'text/plain') 127 | self.end_headers() 128 | self.wfile.write(b'no') 129 | 130 | def do_POST(self): 131 | self.send_response(200) 132 | 133 | def log_message(self, format, *args): 134 | return 135 | --------------------------------------------------------------------------------