why? using regex on every single message the bot receives, even simple patterns, can be very harmful for your cpu lol
184 lines
4.7 KiB
Go
184 lines
4.7 KiB
Go
package tiktok
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"regexp"
|
|
|
|
"govd/enums"
|
|
"govd/models"
|
|
"govd/util"
|
|
)
|
|
|
|
const (
|
|
apiHostname = "api16-normal-c-useast1a.tiktokv.com"
|
|
installationID = "7127307272354596614"
|
|
appName = "musical_ly"
|
|
appID = "1233"
|
|
appVersion = "37.1.4"
|
|
manifestAppVersion = "2023508030"
|
|
packageID = "com.zhiliaoapp.musically/" + manifestAppVersion
|
|
appUserAgent = packageID + " (Linux; U; Android 13; en_US; Pixel 7; Build/TD1A.220804.031; Cronet/58.0.2991.0)"
|
|
)
|
|
|
|
var (
|
|
httpSession = util.GetHTTPSession()
|
|
baseHost = []string{
|
|
"tiktok.com",
|
|
"vxtiktok.com",
|
|
"vm.tiktok.com",
|
|
"vt.tiktok.com",
|
|
"vt.vxtiktok.com",
|
|
"vm.vxtiktok.com",
|
|
"m.tiktok.com",
|
|
"m.vxtiktok.com",
|
|
}
|
|
)
|
|
|
|
var VMExtractor = &models.Extractor{
|
|
Name: "TikTok VM",
|
|
CodeName: "tiktokvm",
|
|
Type: enums.ExtractorTypeSingle,
|
|
Category: enums.ExtractorCategorySocial,
|
|
URLPattern: regexp.MustCompile(`https:\/\/((?:vm|vt|www)\.)?(vx)?tiktok\.com\/(?:t\/)?(?P<id>[a-zA-Z0-9]+)`),
|
|
Host: baseHost,
|
|
IsRedirect: true,
|
|
|
|
Run: func(ctx *models.DownloadContext) (*models.ExtractorResponse, error) {
|
|
location, err := util.GetLocationURL(ctx.MatchedContentURL, "")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get url location: %w", err)
|
|
}
|
|
return &models.ExtractorResponse{
|
|
URL: location,
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
var Extractor = &models.Extractor{
|
|
Name: "TikTok",
|
|
CodeName: "tiktok",
|
|
Type: enums.ExtractorTypeSingle,
|
|
Category: enums.ExtractorCategorySocial,
|
|
URLPattern: regexp.MustCompile(`https?:\/\/((www|m)\.)?(vx)?tiktok\.com\/((?:embed|@[\w\.-]+)\/)?(v(ideo)?|p(hoto)?)\/(?P<id>[0-9]+)`),
|
|
Host: baseHost,
|
|
|
|
Run: func(ctx *models.DownloadContext) (*models.ExtractorResponse, error) {
|
|
mediaList, err := MediaListFromAPI(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get media: %w", err)
|
|
}
|
|
return &models.ExtractorResponse{
|
|
MediaList: mediaList,
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
func MediaListFromAPI(ctx *models.DownloadContext) ([]*models.Media, error) {
|
|
var mediaList []*models.Media
|
|
|
|
details, err := GetVideoAPI(ctx.MatchedContentID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get from api: %w", err)
|
|
}
|
|
caption := details.Desc
|
|
isImageSlide := details.ImagePostInfo != nil
|
|
if !isImageSlide {
|
|
media := ctx.Extractor.NewMedia(
|
|
ctx.MatchedContentID,
|
|
ctx.MatchedContentURL,
|
|
)
|
|
media.SetCaption(caption)
|
|
video := details.Video
|
|
|
|
// generic PlayAddr
|
|
if video.PlayAddr != nil {
|
|
format, err := ParsePlayAddr(video, video.PlayAddr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse playaddr: %w", err)
|
|
}
|
|
media.AddFormat(format)
|
|
}
|
|
// hevc PlayAddr
|
|
if video.PlayAddrBytevc1 != nil {
|
|
format, err := ParsePlayAddr(video, video.PlayAddrBytevc1)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse playaddr: %w", err)
|
|
}
|
|
media.AddFormat(format)
|
|
}
|
|
// h264 PlayAddr
|
|
if video.PlayAddrH264 != nil {
|
|
format, err := ParsePlayAddr(video, video.PlayAddrH264)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse playaddr: %w", err)
|
|
}
|
|
media.AddFormat(format)
|
|
}
|
|
mediaList = append(mediaList, media)
|
|
} else {
|
|
images := details.ImagePostInfo.Images
|
|
for _, image := range images {
|
|
media := ctx.Extractor.NewMedia(
|
|
ctx.MatchedContentID,
|
|
ctx.MatchedContentURL,
|
|
)
|
|
media.SetCaption(caption)
|
|
media.AddFormat(&models.MediaFormat{
|
|
FormatID: "image",
|
|
Type: enums.MediaTypePhoto,
|
|
URL: image.DisplayImage.URLList,
|
|
})
|
|
mediaList = append(mediaList, media)
|
|
}
|
|
}
|
|
return mediaList, nil
|
|
}
|
|
|
|
func GetVideoAPI(awemeID string) (*AwemeDetails, error) {
|
|
apiURL := fmt.Sprintf(
|
|
"https://%s/aweme/v1/multi/aweme/detail/",
|
|
apiHostname,
|
|
)
|
|
queryParams, err := BuildAPIQuery()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to build api query: %w", err)
|
|
}
|
|
postData := BuildPostData(awemeID)
|
|
|
|
req, err := http.NewRequest(
|
|
http.MethodPost,
|
|
apiURL,
|
|
postData,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create request: %w", err)
|
|
}
|
|
req.URL.RawQuery = queryParams.Encode()
|
|
req.Header.Set("User-Agent", appUserAgent)
|
|
req.Header.Set("Accept", "application/json")
|
|
req.Header.Set("X-Argus", "")
|
|
|
|
resp, err := httpSession.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to send request: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read response body: %w", err)
|
|
}
|
|
|
|
var data *Response
|
|
err = json.Unmarshal(body, &data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
|
|
}
|
|
videoData, err := FindVideoData(data, awemeID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to find video data: %w", err)
|
|
}
|
|
return videoData, nil
|
|
}
|