tiktok

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package tiktok is the library behind the tt command line: the HTTP client, the SSR blob parsing, the signed API calls, and the typed data models.

Two planes feed the records. The SSR plane reads the __UNIVERSAL_DATA_FOR_REHYDRATION__ JSON a logged-out page ships and needs no signing. The API plane calls www.tiktok.com/api/* with an X-Bogus signature and an msToken. The API plane sits behind a Web Application Firewall that scores the caller; when it wins, the client returns ErrWalled so the command layer can report it honestly instead of a silent empty result.

Index

Constants

View Source
const (
	KindUser    = "user"
	KindVideo   = "video"
	KindHashtag = "hashtag"
	KindSound   = "sound"
)

Node kinds carried by the discovery walk.

View Source
const DefaultUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"

DefaultUserAgent is a current desktop Chrome string. An honest, real User-Agent is both polite and the thing most likely to keep a session unblocked.

View Source
const Host = "https://www.tiktok.com"

Host is the web origin every request targets.

Variables

View Source
var ErrNotFound = errors.New("not found")

ErrNotFound means the page parsed but carried no record for the request, for example a handle that does not exist or a private profile with no items.

View Source
var ErrWalled = errors.New("tiktok served a WAF challenge (this surface needs a residential session)")

ErrWalled means the WAF served a challenge or a signed call came back empty. The surface needs a residential session.

Functions

func IsSecUID

func IsSecUID(s string) bool

IsSecUID reports whether s looks like a TikTok secUid rather than a handle. A secUid is a long opaque token starting with "MS4wLjAB".

func ParseHandle

func ParseHandle(in string) (string, error)

ParseHandle pulls a bare @handle out of "tiktok", "@tiktok", or a full profile url.

func ParseMusicID

func ParseMusicID(in string) (string, error)

ParseMusicID pulls a numeric sound id out of a /music/{slug}-{id} url, or accepts a bare numeric id.

func ParseVideoID

func ParseVideoID(in string) (string, error)

ParseVideoID pulls a numeric video id out of a /video/{id} or /photo/{id} url, or accepts a bare numeric id.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client talks to TikTok over HTTP.

func NewClient

func NewClient(cfg Config) *Client

NewClient builds a Client from cfg.

func (*Client) Comments

func (c *Client) Comments(ctx context.Context, videoID, author string, limit int) ([]Comment, error)

Comments pages the comment list under a video. author builds the url field.

func (*Client) GetAPI

func (c *Client) GetAPI(ctx context.Context, path, rawQuery string) ([]byte, error)

GetAPI signs an /api/{path} call, fetches it, and returns the JSON body. An empty body on a 200 is the WAF gating the call, so it maps to ErrWalled.

func (*Client) GetPage

func (c *Client) GetPage(ctx context.Context, url string) (string, error)

GetPage fetches an SSR page and returns its body. It detects the WAF challenge stub and returns ErrWalled for it.

func (*Client) HashtagByName

func (c *Client) HashtagByName(ctx context.Context, name string) (Hashtag, error)

HashtagByName fetches a tag page and returns its Hashtag record.

func (*Client) HashtagVideos

func (c *Client) HashtagVideos(ctx context.Context, challengeID string, limit int) ([]Video, error)

HashtagVideos pages the videos under a challenge id.

func (*Client) Posts

func (c *Client) Posts(ctx context.Context, handle string, limit int, cursor string) ([]Video, error)

Posts pages a user's videos through post/item_list. handle may be a @handle or a secUid. It returns up to limit videos (0 means one page).

func (*Client) RawUniversal

func (c *Client) RawUniversal(ctx context.Context, url string) ([]byte, error)

RawUniversal fetches a page and returns its rehydration blob as pretty JSON.

func (*Client) Related

func (c *Client) Related(ctx context.Context, videoID string, limit int) ([]Video, error)

Related pages the videos TikTok recommends alongside one video, through related/item_list. It returns up to limit videos (0 means one page). The discovery walk uses it as a video-to-video edge.

func (*Client) Replies

func (c *Client) Replies(ctx context.Context, videoID, commentID, author string, limit int) ([]Comment, error)

Replies pages the replies under one comment.

func (*Client) Search

func (c *Client) Search(ctx context.Context, keyword string, limit int) ([]SearchHit, error)

Search runs a mixed search and returns thin, normalized hits.

func (*Client) SoundByID

func (c *Client) SoundByID(ctx context.Context, slug, id string) (Sound, error)

SoundByID fetches a music page and returns its Sound record.

func (*Client) SoundVideos

func (c *Client) SoundVideos(ctx context.Context, musicID string, limit int) ([]Video, error)

SoundVideos pages the videos using a sound id.

func (*Client) Trending

func (c *Client) Trending(ctx context.Context, limit int) ([]Video, error)

Trending pages the logged-out recommend feed.

func (*Client) UserAgent

func (c *Client) UserAgent() string

UserAgent returns the configured User-Agent.

func (*Client) UserByHandle

func (c *Client) UserByHandle(ctx context.Context, handle string) (User, error)

UserByHandle fetches a profile page and returns its User record.

func (*Client) Users

func (c *Client) Users(ctx context.Context, keyword string, limit int) ([]User, error)

Users runs a user search and returns the matched profiles.

func (*Client) VideoByID

func (c *Client) VideoByID(ctx context.Context, author, id string) (Video, error)

VideoByID fetches a video page and returns its Video record. The author hint builds the canonical url when one is known; when empty the page supplies it.

type Comment

type Comment struct {
	ID         string `json:"id"`
	VideoID    string `json:"video_id"`
	Text       string `json:"text"`
	Author     string `json:"author"`
	AuthorID   string `json:"author_id"`
	AuthorNick string `json:"author_nick"`
	CreateTime int64  `json:"create_time"`
	DiggCount  int64  `json:"digg_count"`
	ReplyCount int64  `json:"reply_count"`
	ParentID   string `json:"parent_id"`
	URL        string `json:"url"`
}

Comment is one comment under a video, with its parent id for replies.

type Config

type Config struct {
	UserAgent string
	Rate      time.Duration
	Timeout   time.Duration
	Retries   int
}

Config holds the tunable client settings.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns polite defaults: a 600ms gap between requests, a 30s timeout, and five retries on transient errors.

type CrawlOptions

type CrawlOptions struct {
	Depth       int           // maximum hops from a seed
	MaxNodes    int           // stop after this many nodes are emitted
	MaxRequests int           // stop after this many source calls
	Deadline    time.Duration // wall-clock cap, 0 means off
	Fanout      int           // neighbors taken per list-bearing node
	CommentMine int           // commenters mined per video, 0 means off
	Decay       float64       // per-hop score decay applied to the frontier
	Now         func() time.Time
}

CrawlOptions bounds and tunes a walk. Zero values take sane defaults.

type Crawler

type Crawler struct {
	Src    Source
	Opts   CrawlOptions
	OnNode func(Node) // called as each node with data is emitted
	OnEdge func(Edge) // optional, called as each edge is walked
}

Crawler walks the public graph from seeds, scoring as it goes.

func (*Crawler) Run

func (c *Crawler) Run(ctx context.Context, seeds []Seed) (Summary, error)

Run walks from the seeds and returns the run summary. It streams emitted nodes through OnNode and edges through OnEdge as it goes.

type Edge

type Edge struct {
	FromID   string `json:"from_id"`
	FromKind string `json:"from_kind"`
	ToID     string `json:"to_id"`
	ToKind   string `json:"to_kind"`
	Type     string `json:"type"`
}

Edge records that expanding From led to To along Type.

type Hashtag

type Hashtag struct {
	ID         string `json:"id"`
	Title      string `json:"title"`
	Desc       string `json:"desc"`
	VideoCount int64  `json:"video_count"`
	ViewCount  int64  `json:"view_count"`
	Cover      string `json:"cover"`
	URL        string `json:"url"`
}

Hashtag is a challenge page header.

type Node

type Node struct {
	Kind    string
	ID      string
	Depth   int
	Via     string
	Score   float64
	User    *User
	Video   *Video
	Hashtag *Hashtag
	Sound   *Sound
}

Node is one reached graph node. Exactly one record pointer is set per Kind.

type SearchHit

type SearchHit struct {
	Type   string `json:"type"`
	ID     string `json:"id"`
	Title  string `json:"title"`
	Author string `json:"author"`
	URL    string `json:"url"`
}

SearchHit is a thin, normalized search result row.

type Seed

type Seed struct {
	Kind  SeedKind
	Value string
}

Seed is one starting point. Value is empty for SeedTrending.

type SeedKind

type SeedKind int

SeedKind is the kind of a starting point for a walk.

const (
	SeedUser SeedKind = iota
	SeedHashtag
	SeedSound
	SeedVideo
	SeedSearch
	SeedTrending
)

type Sound

type Sound struct {
	ID         string `json:"id"`
	Title      string `json:"title"`
	AuthorName string `json:"author_name"`
	Original   bool   `json:"original"`
	Duration   int64  `json:"duration"`
	PlayURL    string `json:"play_url"`
	Cover      string `json:"cover"`
	VideoCount int64  `json:"video_count"`
	URL        string `json:"url"`
}

Sound is a music page header.

type Source

type Source interface {
	VideoByID(ctx context.Context, author, id string) (Video, error)
	UserByHandle(ctx context.Context, handle string) (User, error)
	Posts(ctx context.Context, handle string, limit int, cursor string) ([]Video, error)
	HashtagByName(ctx context.Context, name string) (Hashtag, error)
	HashtagVideos(ctx context.Context, challengeID string, limit int) ([]Video, error)
	SoundByID(ctx context.Context, slug, id string) (Sound, error)
	SoundVideos(ctx context.Context, musicID string, limit int) ([]Video, error)
	Comments(ctx context.Context, videoID, author string, limit int) ([]Comment, error)
	Related(ctx context.Context, videoID string, limit int) ([]Video, error)
	Search(ctx context.Context, keyword string, limit int) ([]SearchHit, error)
	Trending(ctx context.Context, limit int) ([]Video, error)
}

Source is the slice of the client the walk depends on. *Client satisfies it. Tests inject a fake to run the walk offline and deterministically.

type Summary

type Summary struct {
	NodesByKind  map[string]int
	EdgesByType  map[string]int
	WalledEdges  map[string]int
	Errors       map[string]int
	Requests     int
	NodesEmitted int
	Walled       bool
	StopReason   string
}

Summary reports what a walk reached and why it stopped.

type User

type User struct {
	ID             string `json:"id"`
	UniqueID       string `json:"unique_id"`
	Nickname       string `json:"nickname"`
	SecUID         string `json:"sec_uid"`
	Signature      string `json:"signature"`
	Verified       bool   `json:"verified"`
	Private        bool   `json:"private"`
	Region         string `json:"region"`
	FollowerCount  int64  `json:"follower_count"`
	FollowingCount int64  `json:"following_count"`
	HeartCount     int64  `json:"heart_count"`
	VideoCount     int64  `json:"video_count"`
	FriendCount    int64  `json:"friend_count"`
	Avatar         string `json:"avatar"`
	URL            string `json:"url"`
}

User is a public profile.

type Video

type Video struct {
	ID           string   `json:"id"`
	Desc         string   `json:"desc"`
	CreateTime   int64    `json:"create_time"`
	Author       string   `json:"author"`
	AuthorID     string   `json:"author_id"`
	AuthorSecUID string   `json:"author_sec_uid"`
	MusicID      string   `json:"music_id"`
	MusicTitle   string   `json:"music_title"`
	MusicAuthor  string   `json:"music_author"`
	Challenges   []string `json:"challenges"`
	Duration     int64    `json:"duration"`
	Cover        string   `json:"cover"`
	PlayAddr     string   `json:"play_addr"`
	DownloadAddr string   `json:"download_addr"`
	Width        int64    `json:"width"`
	Height       int64    `json:"height"`
	DiggCount    int64    `json:"digg_count"`
	ShareCount   int64    `json:"share_count"`
	CommentCount int64    `json:"comment_count"`
	PlayCount    int64    `json:"play_count"`
	CollectCount int64    `json:"collect_count"`
	URL          string   `json:"url"`
}

Video is a single post with its author, sound, hashtags, and counters.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL