package douyin import ( "bytes" "encoding/json" ) const routerDataMarker = "window._ROUTER_DATA" // ParseShareTargetHTML extracts only the author identity needed to add a // competitor. It intentionally ignores the work's playback and engagement data. func ParseShareTargetHTML(body []byte, expectedWorkKey string) (TargetProfile, bool) { if len(body) == 0 || len(body) > 2<<20 || !workKeyPattern.MatchString(expectedWorkKey) { return TargetProfile{}, false } root, ok := extractRouterData(body) if !ok { return TargetProfile{}, false } loaderData, ok := root["loaderData"].(map[string]any) if !ok { return TargetProfile{}, false } page, ok := loaderData["video_(id)/page"].(map[string]any) if !ok { return TargetProfile{}, false } videoInfo, ok := page["videoInfoRes"].(map[string]any) if !ok { return TargetProfile{}, false } items, ok := videoInfo["item_list"].([]any) if !ok || len(items) == 0 { return TargetProfile{}, false } item, ok := items[0].(map[string]any) if !ok { return TargetProfile{}, false } if workKey, ok := stringValue(item["aweme_id"]); ok && workKey != expectedWorkKey { return TargetProfile{}, false } author, ok := item["author"].(map[string]any) if !ok { return TargetProfile{}, false } return parseShareAuthor(author) } func extractRouterData(body []byte) (map[string]any, bool) { marker := []byte(routerDataMarker) for offset := 0; ; { index := bytes.Index(body[offset:], marker) if index < 0 { return nil, false } index += offset + len(marker) equals := bytes.IndexByte(body[index:], '=') if equals < 0 { return nil, false } start := index + equals + 1 for start < len(body) && (body[start] == ' ' || body[start] == '\n' || body[start] == '\r' || body[start] == '\t') { start++ } if start >= len(body) || body[start] != '{' { offset = index continue } end, ok := jsonObjectEnd(body, start) if !ok { return nil, false } var root map[string]any if json.Unmarshal(body[start:end], &root) == nil { return root, true } offset = end } } func jsonObjectEnd(body []byte, start int) (int, bool) { depth := 0 inString := false escaped := false for index := start; index < len(body); index++ { char := body[index] if inString { if escaped { escaped = false } else if char == '\\' { escaped = true } else if char == '"' { inString = false } continue } switch char { case '"': inString = true case '{': depth++ case '}': depth-- if depth == 0 { return index + 1, true } } } return 0, false } func parseShareAuthor(author map[string]any) (TargetProfile, bool) { secUID, ok := stringValue(author["sec_uid"]) if !ok || !keyPattern.MatchString(secUID) { return TargetProfile{}, false } uid := stringOrEmpty(author["uid"]) uniqueID := stringOrEmpty(author["unique_id"]) if uid != "" && !keyPattern.MatchString(uid) || uniqueID != "" && !keyPattern.MatchString(uniqueID) { return TargetProfile{}, false } return TargetProfile{ UID: uid, SecUID: secUID, UniqueID: uniqueID, Nickname: stringOrEmpty(author["nickname"]), AvatarURL: avatarURL(author), }, true } func stringValue(value any) (string, bool) { result, ok := value.(string) return result, ok && result != "" } func stringOrEmpty(value any) string { result, _ := value.(string) return result } func avatarURL(author map[string]any) string { for _, key := range []string{"avatar_thumb", "avatar_larger", "avatar_medium", "avatar"} { value := author[key] if direct, ok := value.(string); ok && direct != "" { return direct } container, ok := value.(map[string]any) if !ok { continue } urls, ok := container["url_list"].([]any) if !ok || len(urls) == 0 { continue } if result, ok := urls[0].(string); ok { return result } } return "" }