tiktok

package
v0.2.1 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: 22 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 BaseIdentity added in v0.2.0

func BaseIdentity() kit.Identity

BaseIdentity is the help and version identity shared by the standalone binary and any host that links the package.

func Defaults added in v0.2.0

func Defaults(c *kit.Config)

Defaults seeds the framework baseline from the tiktok defaults, so an unset --rate/--retries/--timeout keeps the library's own pacing.

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 MapErr added in v0.2.0

func MapErr(err error) error

mapErr converts a library error into the kit error kind that carries the right exit code, so a host renders the same walled and not-found outcomes the standalone binary does. A WAF challenge is exit 4 (needs a residential session), a missing record is exit 6.

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.

func Register added in v0.2.0

func Register(app *kit.App)

Register is the convenience a host or the binary calls without naming the zero-value Domain.

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" kit:"id"`
	VideoID    string `json:"video_id"`
	Text       string `json:"text" kit:"body" table:"text,truncate"`
	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 Domain added in v0.2.0

type Domain struct{}

Domain is the TikTok driver. It carries no state; the per-run client is built by the factory Register hands kit.

func (Domain) Classify added in v0.2.0

func (Domain) Classify(input string) (uriType, id string, err error)

Classify turns any accepted input into the canonical (type, id), so `ant resolve` and `ant url` need no network. A bare handle, a /video/ link, a /tag/ link, and a /music/ link each map to their resource type.

func (Domain) Info added in v0.2.0

func (Domain) Info() kit.DomainInfo

Info describes the scheme, the hostnames a pasted link is matched against, and the identity a host reuses for help and version.

func (Domain) Locate added in v0.2.0

func (Domain) Locate(uriType, id string) (string, error)

Locate is the inverse: the live https URL for a (type, id), built without a fetch.

func (Domain) Register added in v0.2.0

func (Domain) Register(app *kit.App)

Register installs the client factory and every TikTok operation onto app. It is the single point both surfaces go through: cli.NewApp calls it for the standalone binary, and a host calls Domain.Register for tiktok:// URIs.

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" kit:"id"`
	Desc       string `json:"desc" kit:"body" table:"desc,truncate"`
	VideoCount int64  `json:"video_count"`
	ViewCount  int64  `json:"view_count"`
	Cover      string `json:"cover"`
	URL        string `json:"url"`
}

Hashtag is a challenge page header, addressed by its name.

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" kit:"id"`
	Title  string `json:"title" table:"title,truncate"`
	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 Session added in v0.2.0

type Session struct {
	Client *Client
	Quiet  bool
}

Session is the per-run client kit injects into every operation. It pairs the HTTP client with the resolved quiet flag, so an operation can pace its own stderr progress without reaching for a global.

func (*Session) Progressf added in v0.2.0

func (s *Session) Progressf(format string, args ...any)

progressf prints a one-line progress note to stderr unless the run is quiet.

type Sound

type Sound struct {
	ID         string `json:"id" kit:"id"`
	Title      string `json:"title" table:"title,truncate"`
	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, addressed by its numeric id.

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" kit:"id"`
	Nickname       string `json:"nickname"`
	SecUID         string `json:"sec_uid"`
	Signature      string `json:"signature" kit:"body" table:"signature,truncate"`
	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, addressed by its @handle.

type Video

type Video struct {
	ID           string   `json:"id" kit:"id"`
	Desc         string   `json:"desc" kit:"body" table:"desc,truncate"`
	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" table:"music_title,truncate"`
	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