Skip to content

Commit

Permalink
feat: Add support for importing token from browser without needing St…
Browse files Browse the repository at this point in the history
…reamlabs to be installed
  • Loading branch information
Loukious committed Aug 9, 2024
1 parent 4e855b0 commit 3229acc
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 52 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ dist/*
*/__pycache__/*
.env
__pycache__/*
cookies.json
.venv/*
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,27 @@ This application is a simple tool that generates a TikTok Live Stream Key for OB


## Features
- Generate TikTok Live Stream Key
- Generate TikTok Live Stream Key using Streamlabs API

## Requirements
- Streamlabs TikTok LIVE access. You can request access [here](https://tiktok.com/falcon/live_g/live_access_pc_apply/result/index.html?id=GL6399433079641606942&lang=en-US)
- Streamlabs account
- TikTok account
- Streamlabs installed on your computer and you are logged in with your TikTok account in Streamlabs
- Streamlabs installed on your computer and you are logged in with your TikTok account in Streamlabs (optional)

## Download
- Download the latest release from [here](https://github.com/Loukious/StreamLabsTikTokStreamKeyGenerator/releases/latest)
- Download the latest release from [here](../../releases/latest)

## Usage
1. Run the application.
2. click on the "Load token" button.
3.
1. If you have Streamlabs installed on your computer and you are logged in with your TikTok account in Streamlabs, the application will automatically load the token.
2. If you don't have Streamlabs installed on your computer or you are not logged in with your TikTok account in Streamlabs, you will need to login with your TikTok account in the browser that will open.

4. Select stream title and category.
5. Click on "Save Config" button to save the token, title and category.
6. Click on the "Go Live" button.


## Screenshots

Expand All @@ -26,8 +37,13 @@ This application is a simple tool that generates a TikTok Live Stream Key for OB

The script will output:
- **Base stream URL:** The URL needed to connect to the TikTok live stream.
- **Stream key for OBS Studio integration:** Stream key that that you can use in OBS Studio to stream to TikTok.
- **Stream key for OBS Studio (or any other streaming app):** Stream key that that you can use in OBS Studio to stream to TikTok.

## FAQ
### I'm getting a `Maximum number of attempts reached. Try again later.` error. What should I do?
This error sometimes occurs when TikTok detects selenium. You can use this [extension](https://chromewebstore.google.com/detail/export-cookie-json-file-f/nmckokihipjgplolmcmjakknndddifde) to export your cookies and import them into the script.
1. Start by installing the above extension in your browser.
2. Log into TikTok in the browser (if not already logged in), then export the cookies using the extension (while being on TikTok's website).
3. After that, place the file in the same directory as the script and rename it to `cookies.json` then start the app.
### Do I need to have 1k followers to get Streamlabs TikTok LIVE access?
No, you can request access even if you have less than 1k followers.
39 changes: 39 additions & 0 deletions Stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@


import requests


class Stream:
def __init__(self, token):
self.s = requests.session()
self.s.headers.update({
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) StreamlabsDesktop/1.16.7 Chrome/114.0.5735.289 Electron/25.9.3 Safari/537.36",
"authorization": f"Bearer {token}"
})

def search(self, game):
if not game:
return []
url = f"https://streamlabs.com/api/v5/slobs/tiktok/info?category={game}"
info = self.s.get(url).json()
info["categories"].append({"full_name": "Other", "game_mask_id": ""})
return info["categories"]

def start(self, title, category):
url = "https://streamlabs.com/api/v5/slobs/tiktok/stream/start"
files=(
('title', (None, title)),
('device_platform', (None, 'win32')),
('category', (None, category)),
)
response = self.s.post(url, files=files).json()
try:
self.id = response["id"]
return response["rtmp"], response["key"]
except KeyError:
return None, None

def end(self):
url = f"https://streamlabs.com/api/v5/slobs/tiktok/stream/{self.id}/end"
response = self.s.post(url).json()
return response["success"]
85 changes: 39 additions & 46 deletions StreamLabsTikTokStreamKeyGenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,37 @@
import tkinter as tk
from tkinter import messagebox
import webbrowser
import requests


class Stream:
def __init__(self, token):
self.s = requests.session()
self.s.headers.update({
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) StreamlabsDesktop/1.16.7 Chrome/114.0.5735.289 Electron/25.9.3 Safari/537.36",
"authorization": f"Bearer {token}"
})

def search(self, game):
if not game:
return []
url = f"https://streamlabs.com/api/v5/slobs/tiktok/info?category={game}"
info = self.s.get(url).json()
info["categories"].append({"full_name": "Other", "game_mask_id": ""})
return info["categories"]

def start(self, title, category):
url = "https://streamlabs.com/api/v5/slobs/tiktok/stream/start"
files=(
('title', (None, title)),
('device_platform', (None, 'win32')),
('category', (None, category)),
)
response = self.s.post(url, files=files).json()
try:
self.id = response["id"]
return response["rtmp"], response["key"]
except KeyError:
return None, None
from Stream import Stream
from TokenRetriever import TokenRetriever

def end(self):
url = f"https://streamlabs.com/api/v5/slobs/tiktok/stream/{self.id}/end"
response = self.s.post(url).json()
return response["success"]

def load_config():
"""Load entry values from a JSON file."""
try:
with open("config.json", "r") as file:
data = json.load(file)

token_entry.delete(0, tk.END)
token_entry.insert(0, data.get("token", ""))

stream_title_entry.config(state=tk.NORMAL)
stream_title_entry.delete(0, tk.END)
stream_title_entry.insert(0, data.get("title", ""))
stream_title_entry.config(state=tk.DISABLED)


game_category_entry.config(state=tk.NORMAL)
game_category_entry.delete(0, tk.END)
game_category_entry.insert(0, data.get("game", ""))
game_category_entry.config(state=tk.DISABLED)


if token_entry.get():
global stream
stream = Stream(token_entry.get())
go_live_button.config(state=tk.NORMAL)
else:
stream_title_entry.config(state=tk.DISABLED)
game_category_entry.config(state=tk.DISABLED)


if stream:
fetch_game_mask_id(data.get("game", ""))
Expand All @@ -75,7 +53,8 @@ def save_config():
"""Save entry values to a JSON file."""
data = {
"title": stream_title_entry.get(),
"game": game_category_entry.get()
"game": game_category_entry.get(),
"token": token_entry.get()
}
with open("config.json", "w") as file:
json.dump(data, file)
Expand Down Expand Up @@ -117,13 +96,31 @@ def load_token():
except Exception as e:
messagebox.showerror("Error", f"Error reading file {file}: {e}")

messagebox.showinfo("API Token", "No API Token found. Make sure you have logged into Streamlabs with your TikTok account.")
messagebox.showinfo("API Token", "No API Token found locally. A webpage will now open to allow you to login into your TikTok account.")
return None

def fetch_online_token():
retriever = TokenRetriever()
token = retriever.retrieve_token()
if token:
token_entry.delete(0, tk.END)
token_entry.insert(0, token)
token_entry.config(show='*')
global stream
stream = Stream(token)
stream_title_entry.config(state=tk.NORMAL)
game_category_entry.config(state=tk.NORMAL)
go_live_button.config(state=tk.NORMAL)
fetch_game_mask_id(game_category_entry.get())
else:
messagebox.showerror("Error", "Failed to obtain token online.")

def populate_token():
global stream
token = load_token()
if token:
if not token:
fetch_online_token() # If no local token, try fetching online
else:
token_entry.delete(0, tk.END)
token_entry.insert(0, token)
token_entry.config(show='*')
Expand All @@ -143,10 +140,7 @@ def toggle_token_visibility():
toggle_button.config(text='Hide Token')

def go_live():
game_mask_id = getattr(game_category_entry, 'game_mask_id', None)
if not game_mask_id:
messagebox.showerror("Error", "Game category is invalid or not selected.")
return
game_mask_id = getattr(game_category_entry, 'game_mask_id', "")

stream_url, stream_key = stream.start(stream_title_entry.get(), game_mask_id)

Expand Down Expand Up @@ -351,5 +345,4 @@ def on_motion(event):
if __name__ == "__main__":
stream = None
load_config()
populate_token()
root.mainloop()
88 changes: 88 additions & 0 deletions TokenRetriever.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import gzip
import seleniumwire.undetected_chromedriver as uc
import json
import requests
from urllib.parse import urlparse, parse_qs
import hashlib
import os
import base64

class TokenRetriever:
CLIENT_KEY = "awdjaq9ide8ofrtz"
REDIRECT_URI = "https://streamlabs.com/tiktok/auth"
STATE = ""
SCOPE = "user.info.basic,live.room.info,live.room.manage,user.info.profile,user.info.stats"
STREAMLABS_API_URL = "https://streamlabs.com/api/v5/auth/data"

def __init__(self, cookies_file='cookies.json'):
self.code_verifier = self.generate_code_verifier()
self.code_challenge = self.generate_code_challenge(self.code_verifier)
self.streamlabs_auth_url = (
f"https://streamlabs.com/m/login?force_verify=1&external=mobile&skip_splash=1&tiktok&code_challenge={self.code_challenge}"
)
self.cookies_file = cookies_file
self.driver = None

@staticmethod
def generate_code_verifier():
return base64.urlsafe_b64encode(os.urandom(64)).decode('utf-8').rstrip('=')

@staticmethod
def generate_code_challenge(code_verifier):
sha256 = hashlib.sha256()
sha256.update(code_verifier.encode('utf-8'))
return base64.urlsafe_b64encode(sha256.digest()).decode('utf-8').rstrip('=')

def load_cookies(self, driver):
if os.path.exists(self.cookies_file):
with open(self.cookies_file, 'r') as f:
cookies = json.load(f)
for cookie in cookies:
driver.add_cookie(cookie)

def retrieve_token(self):
self.driver = uc.Chrome()
self.driver.get("https://www.tiktok.com") # Load a page first before setting cookies
self.load_cookies(self.driver)
self.driver.get(self.streamlabs_auth_url)

try:
request = self.driver.wait_for_request('https://www.tiktok.com/passport/open/web/auth/v2/', timeout=600)
if request:
decompressed_body = gzip.decompress(request.response.body)
response_body = decompressed_body.decode('utf-8')
data = json.loads(response_body)

redirect_url = data.get('redirect_url')
if redirect_url:
with requests.session() as s:
s.get(redirect_url)
parsed_url = urlparse(redirect_url)
auth_code = parse_qs(parsed_url.query).get('code', [None])[0]
else:
print("No redirect_url found in the response.")
return None
else:
print("No request intercepted or timeout reached.")
return None

finally:
try:
self.driver.close()
except Exception as e:
print(f"Error closing browser: {e}")

if auth_code:
try:
import time
token_request_url = f"{self.STREAMLABS_API_URL}?code_verifier={self.code_verifier}&code={auth_code}"
time.sleep(3) # Wait a few seconds before sending the request
response = requests.get(token_request_url).json()
if response["success"]:
return response["data"]["oauth_token"]
except:
print("Failed to obtain token.")
return None

print("Failed to obtain authorization code.")
return None
5 changes: 4 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
requests>=2.31.0
pyinstaller>=6.6.0
setuptools>=69.5.1
setuptools>=69.5.1
selenium-wire>=5.1.0
undetected_chromedriver>=3.5.5
blinker==1.7.0

0 comments on commit 3229acc

Please sign in to comment.