Giter Club home page Giter Club logo

tiktokliverust's Introduction

TikTok Live Rust

❤️❤️🎁 Connect to TikTok live in 3 lines 🎁❤️❤️

Introduction

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?

NOTE: This is not an official API. It's a reverse engineering project.

Overview

Getting started

Dependencies

[dependencies]
tiktoklive = "0.0.15"
tokio = { version = "1.35.1", features = ["full"] }
serde_json = "1.0"
log = "0.4"
env_logger = "0.10.1"

Usage example

use env_logger::{Builder, Env}; // Importing the logger builder and environment configuration
use log::LevelFilter; // Importing log level filter
use log::{error, warn};
use std::time::Duration; // Importing Duration for timeout settings
use 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 runtime
async fn main() {
    init_logger("info"); // Initialize logger with "info" level
    let user_name = "tragdate";

    let client = create_client(user_name); // Create a client for the given username

    // Spawn a new asynchronous task to connect the client
    let handle = tokio::spawn(async move {
        // Attempt to connect the client
        if let Err(e) = client.connect().await {
            match e {
                // Match on the error type
                LibError::LiveStatusFieldMissing => {
                    // Specific error case
                    warn!(
                        "Failed to get live status (probably needs authenticated client): {}",
                        e
                    );
                    let auth_client = create_client_with_cookies(user_name); // Create an authenticated client
                    if let Err(e) = auth_client.connect().await {
                        // Attempt to connect the authenticated client
                        error!("Error connecting to TikTok Live after retry: {}", e);
                    }
                }
                LibError::HeaderNotReceived => {
                    error!("Error connecting to TikTok Live: {}", e);
                }

                _ => {
                    // General error case
                    error!("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
}

fn handle_event(client: &TikTokLiveClient, event: &TikTokLiveEvent) {
    match event {
        TikTokLiveEvent::OnConnected(..) => {
            // This is an EXPERIMENTAL and UNSTABLE feature
            // Get room info from the client
            let room_info = client.get_room_info();
            // // Parse the room info
            let client_data: ClientData = serde_json::from_str(room_info).unwrap();
            // // Parse the stream data
            let 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 format
            let 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 type
        TikTokLiveEvent::OnMember(join_event) => {
            // Handle member join event
            println!("user: {} joined", join_event.raw_data.user.nickname);
        }
        TikTokLiveEvent::OnChat(chat_event) => {
            // Handle chat event
            println!(
                "user: {} -> {}",
                chat_event.raw_data.user.nickname, chat_event.raw_data.content
            );
        }
        TikTokLiveEvent::OnGift(gift_event) => {
            // Handle gift event
            let 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 event
            let nick = &like_event.raw_data.user.nickname;
            println!("user: {} likes", nick);
        }
        _ => {} // Ignore other events
    }
}

// Function to initialize the logger with a default log level
fn init_logger(default_level: &str) {
    let env = Env::default().filter_or("LOG_LEVEL", default_level); // Set default log level from environment or use provided level
    Builder::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 settings
fn configure(settings: &mut TikTokLiveSettings) {
    settings.http_data.time_out = Duration::from_secs(12); // Set HTTP timeout to 12 seconds
}

// Function to configure the TikTok live settings with cookies for authentication
fn configure_with_cookies(settings: &mut TikTokLiveSettings) {
    settings.http_data.time_out = Duration::from_secs(12); // Set HTTP timeout to 12 seconds
    let 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 username
fn create_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 username
fn create_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
}

Library errors table

You can catch errors on events with

use tiktoklive::LibError;

if let Err(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

Contributing

Your improvements are welcome! Feel free to open an issue or pull request.

Contributors

Zmole Cristian

tiktokliverust's People

Contributors

actions-user avatar jwdeveloper avatar zmolecristian avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

tiktokliverust's Issues

get all users currently inside the live, instead of just the top viewers

more a question then an issue:

is it possible to get all users currently inside the live, instead of just the top viewers?

the roomUser event currently only gives the total count, and an array of topViewers.
I want to get a list of all viewers to do some statistics on how long a user stayed after joining. But for that
I need a list of all the users as there is a big discrepancy between top viewers and all viewers.

in one live there were 16 top viewers bis over 100 viewers.

Compiling error while using as lib.

error[E0425]: cannot find value `VERSION_3_3_0` in crate `protobuf'

const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_3_0;
                                                ^^^^^^^^^^^^^ help: a constant with a similar name exists: `VERSION_3_4_0`

Compiling error while using protobuf

error[E0425]: cannot find value `VERSION_3_4_0` in crate `protobuf`
const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_4_0;
   |                                                 ^^^^^^^^^^^^^ help: a constant with a similar name exists: `VERSION_3_5_0`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.