A Rust library. Use it to receive live stream events such as comments and gifts in realtime from TikTok LIVE No credentials are required.
Join the support discord and visit the #rust-support channel for questions, contributions and ideas. Feel free to make pull requests with missing/new features, fixes, etc
Do you prefer other programming languages?
- JavaTikTokLiveJava
- NodeTikTok-Live-Connector by @zerodytrash
- PythonTikTokLive by @isaackogan
- C#TikTokLiveSharp by @frankvHoof93
NOTE: This is not an official API. It's a reverse engineering project.
If you don't have a signing server you can obtain a free API key from EulerStream
[dependencies]
tiktoklive = "0.0.19"tokio = { version = "1.35.1", features = ["full"] }
serde_json = "1.0"log = "0.4"env_logger = "0.10.1"use env_logger::{Builder,Env};// Importing the logger builder and environment configurationuse log::LevelFilter;// Importing log level filteruse log::{error, warn};use std::time::Duration;// Importing Duration for timeout settingsuse tiktoklive::{// Importing necessary modules and structs from tiktoklive crate
core::live_client::TikTokLiveClient,
data::live_common::{ClientData,StreamData,TikTokLiveSettings},
errors::LibError,
generated::events::TikTokLiveEvent,TikTokLive,};use tokio::signal;// Importing signal handling from tokio#[tokio::main]// Main function is asynchronous and uses tokio runtimeasyncfnmain(){init_logger("info");// Initialize logger with "info" levellet user_name = "tragdate";let client = create_client(user_name);// Create a client for the given username// Spawn a new asynchronous task to connect the clientlet handle = tokio::spawn(asyncmove{// Attempt to connect the clientifletErr(e) = client.connect().await{match e {// Match on the error typeLibError::LiveStatusFieldMissing => {// Specific error casewarn!("Failed to get live status (probably needs authenticated client): {}",
e
);let auth_client = create_client_with_cookies(user_name);// Create an authenticated clientifletErr(e) = auth_client.connect().await{// Attempt to connect the authenticated clienterror!("Error connecting to TikTok Live after retry: {}", e);}}LibError::HeaderNotReceived => {error!("Error connecting to TikTok Live: {}", e);}
_ => {// General error caseerror!("Error connecting to TikTok Live: {}", e);}}}});
signal::ctrl_c().await.expect("Failed to listen for Ctrl+C");// Wait for Ctrl+C signal to gracefully shut down
handle.await.expect("The spawned task has panicked");// Await the spawned task to ensure it completes}fnhandle_event(client:&TikTokLiveClient,event:&TikTokLiveEvent){match event {TikTokLiveEvent::OnConnected(..) => {// This is an EXPERIMENTAL and UNSTABLE feature// Get room info from the clientlet room_info = client.get_room_info();// // Parse the room infolet client_data:ClientData = serde_json::from_str(room_info).unwrap();// // Parse the stream datalet stream_data:StreamData = serde_json::from_str(&client_data
.data.stream_url.live_core_sdk_data.unwrap().pull_data.stream_data,).unwrap();// Get the video URL for the low definition stream with fallback to the high definition stream in a flv formatlet video_url = stream_data
.data.ld.map(|ld| ld.main.flv).or_else(|| stream_data.data.sd.map(|sd| sd.main.flv)).or_else(|| stream_data.data.origin.map(|origin| origin.main.flv)).expect("None of the stream types set");println!("room info: {}", video_url);}// Match on the event typeTikTokLiveEvent::OnMember(join_event) => {// Handle member join eventprintln!("user: {} joined", join_event.raw_data.user.nickname);}TikTokLiveEvent::OnChat(chat_event) => {// Handle chat eventprintln!("user: {} -> {}",
chat_event.raw_data.user.nickname, chat_event.raw_data.content
);}TikTokLiveEvent::OnGift(gift_event) => {// Handle gift eventlet nick = &gift_event.raw_data.user.nickname;let gift_name = &gift_event.raw_data.gift.name;let gifts_amount = gift_event.raw_data.gift.combo;println!("user: {} sends gift: {} x {}",
nick, gift_name, gifts_amount
);}TikTokLiveEvent::OnLike(like_event) => {// Handle like eventlet nick = &like_event.raw_data.user.nickname;println!("user: {} likes", nick);}
_ => {}// Ignore other events}}// Function to initialize the logger with a default log levelfninit_logger(default_level:&str){let env = Env::default().filter_or("LOG_LEVEL", default_level);// Set default log level from environment or use provided levelBuilder::from_env(env)// Build the logger from environment settings.filter_module("tiktoklive",LevelFilter::Debug)// Set log level for tiktoklive module.init();// Initialize the logger}// Function to configure the TikTok live settingsfnconfigure(settings:&mutTikTokLiveSettings){
settings.http_data.time_out = Duration::from_secs(12);// Set HTTP timeout to 12 seconds
settings.sign_api_key = "".to_string();// Provide your own api key here}// Function to configure the TikTok live settings with cookies for authenticationfnconfigure_with_cookies(settings:&mutTikTokLiveSettings){
settings.http_data.time_out = Duration::from_secs(12);// Set HTTP timeout to 12 seconds
settings.sign_api_key = "".to_string();// Provide your own api key herelet contents = "";// Placeholder for cookies
settings
.http_data.headers.insert("Cookie".to_string(), contents.to_string());// Insert cookies into HTTP headers}// Function to create a TikTok live client for the given usernamefncreate_client(user_name:&str) -> TikTokLiveClient{TikTokLive::new_client(user_name)// Create a new client.configure(configure)// Configure the client.on_event(handle_event)// Set the event handler.build()// Build the client}// Function to create a TikTok live client with cookies for the given usernamefncreate_client_with_cookies(user_name:&str) -> TikTokLiveClient{TikTokLive::new_client(user_name)// Create a new client.configure(configure_with_cookies)// Configure the client with cookies.on_event(handle_event)// Set the event handler.build()// Build the client}You can catch errors on events with
use tiktoklive::LibError;ifletErr(e) = client.connect().await{match e {LibError::UserFieldMissing => {println!("User field is missing");}
_ => {eprintln!("Error connecting to TikTok Live: {}", e);}}}| Error type | Description |
|---|---|
| RoomIDFieldMissing | Room ID field is missing, contact developer |
| UserFieldMissing | User field is missing |
| UserDataFieldMissing | User data field is missing |
| LiveDataFieldMissing | Live data field is missing |
| JsonParseError | Error parsing JSON |
| UserMessageFieldMissing | User message field is missing |
| ParamsError | Params error |
| UserStatusFieldMissing | User status field is missing |
| LiveStatusFieldMissing | Live status field is missing |
| TitleFieldMissing | Title field is missing |
| UserCountFieldMissing | User count field is missing |
| StatsFieldMissing | Stats field is missing |
| LikeCountFieldMissing | Like count is missing |
| TotalUserFieldMissing | Total user field is missing |
| LiveRoomFieldMissing | Live room field is missing |
| StartTimeFieldMissing | Start time field is missing |
| UserNotFound | User not found |
| HostNotOnline | Live stream for host is not online!, current status HostOffline |
| InvalidHost | Invalid host in WebSocket URL |
| WebSocketConnectFailed | Failed to connect to WebSocket |
| PushFrameParseError | Unable to read push frame |
| WebcastResponseParseError | Unable to read webcast response |
| AckPacketSendError | Unable to send ack packet |
| HttpRequestFailed | HTTP request failed |
| UrlSigningFailed | URL signing failed |
| HeaderNotReceived | Header was not received |
| BytesParseError | Unable to parse bytes to Push Frame |
Your improvements are welcome! Feel free to open an issue or pull request.
