#live #stream #tiktok #api #api-bindings #reverse-engineering #log-level

bin+lib tiktoklive

一个 Rust 库。使用它可以从 TikTok LIVE 实时接收直播事件,如评论和礼物,无需凭据。

14 个版本

0.0.18 2024 年 8 月 23 日
0.0.17 2024 年 8 月 23 日
0.0.15 2024 年 6 月 21 日
0.0.6 2024 年 5 月 24 日
0.0.5 2024 年 1 月 6 日

#900网络编程

MIT/Apache

2.5MB
47K SLoC

抖音直播 Rust

❤️❤️🎁 3 行代码连接抖音直播 🎁❤️❤️

简介

一个 Rust 库。使用它可以从 TikTok LIVE 实时接收直播事件,如评论和礼物,无需凭据。

加入支持 discord 并访问 #rust-support 频道以提问、贡献和提出想法。请随意提交具有缺失/新功能、修复等内容的拉取请求

您更喜欢其他编程语言吗?

注意: 这不是官方 API。它是一个逆向工程项目。

概述

入门

依赖

[dependencies]
tiktoklive = "0.0.18"
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 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
}

库错误表

您可以在事件上捕获错误

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);
        }
    }
}
错误类型 描述
RoomIDFieldMissing 缺少 Room ID 字段,请联系开发者
UserFieldMissing 缺少 User 字段
UserDataFieldMissing 缺少 User 数据字段
LiveDataFieldMissing 实时数据字段缺失
Json解析错误 解析JSON错误
用户消息字段缺失 用户消息字段缺失
参数错误 参数错误
用户状态字段缺失 用户状态字段缺失
直播状态字段缺失 直播状态字段缺失
标题字段缺失 标题字段缺失
用户数量字段缺失 用户数量字段缺失
统计数据字段缺失 统计数据字段缺失
点赞数字段缺失 点赞数缺失
总用户字段缺失 总用户字段缺失
直播室字段缺失 直播室字段缺失
开始时间字段缺失 开始时间字段缺失
用户未找到 用户未找到
主播未在线 主播直播流未在线!当前状态为主播离线
无效主播 WebSocket URL中的无效主播
WebSocket连接失败 连接WebSocket失败
推送帧解析错误 无法读取推送帧
网络直播响应解析错误 无法读取网络直播响应
确认包发送错误 无法发送确认包
HTTP请求失败 HTTP请求失败
URL签名失败 URL签名失败
未收到头部信息 未收到头部信息
字节解析错误 无法将字节解析为推送帧

贡献

欢迎您的改进!请随时在 问题拉取请求 中提出。

贡献者

Zmole Cristian

依赖

~27–42MB
~502K SLoC