awemer/main.go

1716 lines
42 KiB
Go

package main
import (
"fmt"
"os"
"os/signal"
"errors"
"time"
"math/rand"
"bytes"
"io"
"bufio"
"sync"
"path/filepath"
"strconv"
"strings"
"io/ioutil"
"net/http"
"net/url"
"encoding/json"
)
var Os, Ip, DeviceId string;
var liveClient *http.Client
func DownloadAweme(filename string, uri string) (bool, error) {
var (
e error
f *os.File
rs *http.Response
rq *http.Request
)
_, e = os.Stat(filename)
if e == nil {
return true, nil
} else if !os.IsNotExist(e) {
return true, e
}
rq, e = http.NewRequest("GET", uri, nil)
rq.Header.Set("User-Agent", "okhttp")
rs, e = http.DefaultClient.Do(rq)
if e != nil {
return false, e
}
defer rs.Body.Close()
if rs.StatusCode != http.StatusOK {
return false, errors.New(rs.Status)
} else if rs.ContentLength == 0 {
return false, errors.New("Response body is empty.");
}
f, e = os.Create(filename)
if e != nil {
return false, e
}
_, e = io.Copy(f, rs.Body)
f.Close()
if e != nil {
_ = os.Remove(filename)
return false, e
}
return false, nil
}
func init() {
liveClient = createHTTPClient()
}
func createHTTPClient() *http.Client {
client := &http.Client{
Transport: &http.Transport{
MaxIdleConnsPerHost: 20,
},
Timeout: 0,//time.Duration(RequestTimeout) * time.Second,
}
return client
}
func DownloadStream(filename string, uri string) (bool, error) {
var (
e error
f *os.File
rs *http.Response
rq *http.Request
/*tr = &http.Transport{
IdleConnTimeout: 0,
}
client = &http.Client{Transport: tr}*/
)
/*_, e = os.Stat(filename)
if e == nil {
return true, nil
} else if !os.IsNotExist(e) {
return true, e
}*/
rq, e = http.NewRequest("GET", uri, nil)
rq.Header.Set("User-Agent", "okhttp")
fmt.Println(rq.Header)
//rq.Header.Set("Connection", "keep-alive")
fmt.Println(rq)
rs, e = liveClient.Do(rq)
//rs, e = http.DefaultClient.Do(rq)
if e != nil {
return false, e
//DownloadStream(filename, uri)
}
defer rs.Body.Close()
if rs.StatusCode != http.StatusOK {
return false, errors.New(rs.Status)
} else if rs.ContentLength == 0 {
return false, errors.New("Response body is empty.");
}
f, e = os.OpenFile(filename, os.O_RDWR | os.O_APPEND, 0700)
if e != nil {
if os.IsNotExist(e) {
f, e = os.Create(filename)
if e != nil {
return false, e
}
} else {
return false, e
}
}
/*f, e = os.Create(filename)
if e != nil {
return false, e
}*/
// 00 00 00 00 17 01 00 00 00 00 00 00 6a 06 64
_, e = io.Copy(f, rs.Body)
f.Close()
if e != nil {
_ = os.Remove(filename)
return false, e
}
return false, nil
}
func GenerateBase62(length int) string {
var (
i int
a []byte = make([]byte, length)
base64 string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
)
for i = 0; i < length; i++ {
a[i] = base64[rand.Intn(62)]
}
return string(a)
}
/*func GenerateDevice() {
var b []string = Devices[rand.Intn(len(Devices))];
Brand = b[0];
Model = b[rand.Intn(len(Devices) - 1) + 1];
}*/
func GenerateDeviceId() string {
var (
i int
a []byte = make([]byte, 19)//11 + rand.Intn(9)
)
a[0] = byte('6')//byte(rand.Intn(9) + 49)
for i = 1; i < len(a); i++ {
a[i] = byte(rand.Intn(10) + 48)
}
return string(a)
}
func GeneratePublicIp() string {
var i0, i1, i2 int
for i0 = 1 + rand.Intn(224); i0 == 10 || i0 == 127; i0 = 1 + rand.Intn(224) {}
switch i0 {
case 100:
for i1 = rand.Intn(256); i1 > 63 && i1 < 128; i1 = rand.Intn(256) {}
return strconv.Itoa(i0) + "." + strconv.Itoa(i1) + "." + strconv.Itoa(rand.Intn(256)) + "." + strconv.Itoa(rand.Intn(256))
case 169:
for i1 = rand.Intn(256); i1 == 254; i1 = rand.Intn(256) {}
return strconv.Itoa(i0) + "." + strconv.Itoa(i1) + "." + strconv.Itoa(rand.Intn(256)) + "." + strconv.Itoa(rand.Intn(256))
case 172:
for i1 = rand.Intn(256); i1 > 15 && i1 < 32; i1 = rand.Intn(256) {}
return strconv.Itoa(i0) + "." + strconv.Itoa(i1) + "." + strconv.Itoa(rand.Intn(256)) + "." + strconv.Itoa(rand.Intn(256))
case 192:
for i1 = rand.Intn(256); i1 == 168; i1 = rand.Intn(256) {}
switch i1 {
case 0:
for i2 = 1 + rand.Intn(256); i2 == 2; i2 = 1 + rand.Intn(256) {}
return strconv.Itoa(i0) + "." + strconv.Itoa(i1) + "." + strconv.Itoa(i2) + "." + strconv.Itoa(rand.Intn(256))
case 88:
for i2 = rand.Intn(256); i2 == 99; i2 = rand.Intn(256) {}
return strconv.Itoa(i0) + "." + strconv.Itoa(i1) + "." + strconv.Itoa(i2) + "." + strconv.Itoa(rand.Intn(256))
}
return strconv.Itoa(i0) + "." + strconv.Itoa(i1) + "." + strconv.Itoa(rand.Intn(256)) + "." + strconv.Itoa(rand.Intn(256))
case 198:
for i1 = rand.Intn(256); i1 == 18 || i1 == 19; i1 = rand.Intn(256) {}
if i1 == 51 {
for i2 = rand.Intn(256); i2 == 100; i2 = rand.Intn(256) {}
return strconv.Itoa(i0) + "." + strconv.Itoa(i1) + "." + strconv.Itoa(i2) + "." + strconv.Itoa(rand.Intn(256))
}
return strconv.Itoa(i0) + "." + strconv.Itoa(i1) + "." + strconv.Itoa(rand.Intn(256)) + "." + strconv.Itoa(rand.Intn(256))
case 203:
if i1 = rand.Intn(256); i1 == 0 {
for i2 = rand.Intn(256); i2 == 113; i2 = rand.Intn(256) {}
return strconv.Itoa(i0) + "." + strconv.Itoa(i1) + "." + strconv.Itoa(i2) + "." + strconv.Itoa(rand.Intn(256))
}
return strconv.Itoa(i0) + "." + strconv.Itoa(i1) + "." + strconv.Itoa(rand.Intn(256)) + "." + strconv.Itoa(rand.Intn(256))
}
return strconv.Itoa(i0) + "." + strconv.Itoa(rand.Intn(256)) + "." + strconv.Itoa(rand.Intn(256)) + "." + strconv.Itoa(rand.Intn(256))
}
/*func GenerateIp() string {
return strconv.Itoa(rand.Intn(256)) + "." + strconv.Itoa(rand.Intn(256)) + "." + strconv.Itoa(rand.Intn(256)) + "." + strconv.Itoa(rand.Intn(256))
}*/
func GetFlvStream(value string) string {
var i, e int
if i = strings.Index(value, "-hls-"); i > -1 {
if e = strings.Index(value[i + 5:], "/playlist.m3u8"); e > -1 {
return value[0:i] + "-flv-" + value[i + 5:e + i + 5] + ".flv"
}
}
return ""
}
func FindStream(value string) string {
var i, e int
i = strings.Index(value, "\"LiveUrl\"")
if i == -1 {
return ""
}
e = i + 9
for e < len(value) && (value[e] == ' ' || value[e] == '\n' || value[e] == '\r' || value[e] == '\t') {
e++
}
if e == len(value) || value[e] != ':' {
return ""
}
e++
for e < len(value) && (value[e] == ' ' || value[e] == '\n' || value[e] == '\r' || value[e] == '\t') {
e++
}
if e == len(value) || value[e] != '"' {
return ""
}
e++
i = e
for e < len(value) && value[e] != '"' {
e++
}
if e == len(value) {
return ""
}
return value[i:e]
}
func RequestStream(id int64) string {
var (
e error
t string
b []byte
rs *http.Response
rq *http.Request
)
rq, e = http.NewRequest("GET", "https://www.tiktok.com/share/live/" + strconv.FormatInt(id, 10), nil)
if e != nil {
return ""
}
rq.Header.Set("User-Agent", "okhttp")
rs, e = http.DefaultClient.Do(rq)
if e != nil {
return ""
}
defer rs.Body.Close()
if rs.StatusCode != http.StatusOK {
return ""
}
b, e = ioutil.ReadAll(rs.Body)
if e != nil {
return ""
}
t = FindStream(string(b))
if len(t) == 0 {
return ""
}
return t
}
func AwemeRequest(path string) ([]byte, error) {
var (
e error
b []byte
rs *http.Response
rq *http.Request
)
rq, e = http.NewRequest("GET", "http://api-t.tiktok.com" + path +
"&device_id=" + DeviceId +
//"&iid=0" +
"&version_code=140403" +
"&build_number=14.3.3" +
"&version_name=14.3.3" +
"&aid=1233" +
"&app_name=musical_ly" +
"&app_language=en" +
"&channel=googleplay" +
"&device_platform=android" +
"&device_brand=Google" +
"&device_type=Pixel%204%20XL" +
"&os_version=" + Os, nil)
if e != nil {
return nil, e
}
//rq.Header.Set("User-Agent", "okhttp")
rq.Header.Set("User-Agent", "com.zhiliaoapp.musically (Android " + Os + "; Pixel 4 XL)")
if len(Ip) > 0 {
rq.Header.Set("X-Forwarded-For", Ip)
}
rs, e = http.DefaultClient.Do(rq)
if e != nil {
return nil, e
}
defer rs.Body.Close()
if rs.StatusCode != http.StatusOK {
return nil, errors.New(rs.Status)
}
b, e = ioutil.ReadAll(rs.Body)
if e != nil {
return nil, e
}
return b, nil
}
func GetUserInfo(id string, retry int) (string, bool, int, int, int, int, string, string, string, int64, error) {
var (
e error
b []byte
d struct {
User struct {
UniqueId string "json:\"unique_id\""
Uid string "json:\"uid\""
AwemeCount int "json:\"aweme_count\""
FollowCount int "json:\"follower_count\""
FollowingCount int "json:\"following_count\""
Likes int "json:\"total_favorited\""
Ig string "json:\"ins_id\""
Yt string "json:\"youtube_channel_id\""
X string "json:\"signature\""
Secret int "json:\"secret\""
RoomId int64 "json:\"room_id\""
} "json:\"user\""
}
)
b, e = AwemeRequest("/aweme/v1/user/" +
"?user_id=" + id)
if e != nil {
return "", false, 0, 0, 0, 0, "", "", "", 0, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetUserInfo(id, retry + 1)
}
return "", false, 0, 0, 0, 0, "", "", "", 0, e
} else if len(d.User.UniqueId) == 0 {
return "", false, 0, 0, 0, 0, "", "", "", 0, errors.New("error: Invalid user id or user no longer exists")
}
return d.User.UniqueId, d.User.Secret != 0, d.User.AwemeCount, d.User.FollowCount, d.User.FollowingCount, d.User.Likes, d.User.Ig, d.User.Yt, d.User.X, d.User.RoomId, nil
}
func GetUserData(id string, retry int) (string, bool, int, int, int, int, string, string, string, int64, error) {
var (
e error
b []byte
d struct {
User struct {
UniqueId string "json:\"unique_id\""
Uid string "json:\"uid\""
ShortId string "json:\"short_id\""
Nickname string "json:\"nickname\""
Gender string "json:\"gender\""
Birthday string "json:\"birthday\""
IsVerified bool "json:\"is_verified\""
UserRate int "json:\"user_rate\""
AwemeCount int "json:\"aweme_count\""
FollowCount int "json:\"follower_count\""
FollowingCount int "json:\"following_count\""
Likes int "json:\"total_favorited\""
Ig string "json:\"ins_id\""
Yt string "json:\"youtube_channel_id\""
X string "json:\"signature\""
Secret int "json:\"secret\""
RoomId int64 "json:\"room_id\""
} "json:\"user\""
}
)
b, e = AwemeRequest("/aweme/v1/user/" +
"?user_id=" + id)
if e != nil {
return "", false, 0, 0, 0, 0, "", "", "", 0, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetUserInfo(id, retry + 1)
}
return "", false, 0, 0, 0, 0, "", "", "", 0, e
} else if len(d.User.UniqueId) == 0 {
return "", false, 0, 0, 0, 0, "", "", "", 0, errors.New("error: Invalid user id or user no longer exists")
}
return d.User.UniqueId, d.User.Secret != 0, d.User.AwemeCount, d.User.FollowCount, d.User.FollowingCount, d.User.Likes, d.User.Ig, d.User.Yt, d.User.X, d.User.RoomId, nil
}
func GetUserIdFromVideos(username string, related []string, retry int) (string, bool, int, int, int, int, string, string, string, int64, error) {
var (
i int
e error
b []byte
d struct {
AwemeList []struct {
Author struct {
UniqueId string "json:\"unique_id\""
Uid string "json:\"uid\""
AwemeCount int "json:\"aweme_count\""
FollowCount int "json:\"follower_count\""
FollowingCount int "json:\"following_count\""
Likes int "json:\"total_favorited\""
Ig string "json:\"ins_id\""
Yt string "json:\"youtube_channel_id\""
X string "json:\"signature\""
Secret int "json:\"secret\""
RoomId int64 "json:\"room_id\""
} "json:\"author\""
} "json:\"aweme_list\""
}
)
b, e = AwemeRequest("/aweme/v1/search/item/" +
"?keyword=" + url.QueryEscape(username) +
"&count=20" +
"&offset=0" +
"&source=video_search" +
"&is_pull_refresh=1" +
"&hot_search=0")
if e != nil {
return "", false, 0, 0, 0, 0, "", "", "", 0, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetUserIdFromVideos(username, related, retry + 1)
}
return "", false, 0, 0, 0, 0, "", "", "", 0, e
}
for i = 0; i < len(d.AwemeList); i++ {
if d.AwemeList[i].Author.UniqueId == username {
return GetUserInfo(d.AwemeList[i].Author.Uid, 0)
}
}
fmt.Println("User not found!")
if len(related) > 0 {
fmt.Println("Did you mean: ")
for i = 0; i < 10; i++ {
fmt.Println(related[i])
}
} else {
fmt.Println("(no related usernames found)")
}
return "", false, 0, 0, 0, 0, "", "", "", 0, errors.New("error: Username not found")
}
func GetUserId(username string, retry int) (string, bool, int, int, int, int, string, string, string, int64, error) {
var (
i int
e error
b []byte
related []string
d struct {
UserList []struct {
UserInfo struct {
UniqueId string "json:\"unique_id\""
Uid string "json:\"uid\""
AwemeCount int "json:\"aweme_count\""
FollowCount int "json:\"follower_count\""
FollowingCount int "json:\"following_count\""
Likes int "json:\"total_favorited\""
Ig string "json:\"ins_id\""
Yt string "json:\"youtube_channel_id\""
X string "json:\"signature\""
Secret int "json:\"secret\""
RoomId int64 "json:\"room_id\""
} "json:\"user_info\""
} "json:\"user_list\""
}
)
b, e = AwemeRequest("/aweme/v1/discover/search/" +
"?keyword=" + url.QueryEscape(username) +
"&count=20" +
"&cursor=0" +
"&type=1" +
"&search_source=discover" +
"&is_pull_refresh=1" +
"&hot_search=0")
if e != nil {
return "", false, 0, 0, 0, 0, "", "", "", 0, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetUserId(username, retry + 1)
}
return "", false, 0, 0, 0, 0, "", "", "", 0, e
}
for i = 0; i < len(d.UserList); i++ {
if d.UserList[i].UserInfo.UniqueId == username {
return d.UserList[i].UserInfo.Uid, d.UserList[i].UserInfo.Secret != 0, d.UserList[i].UserInfo.AwemeCount, d.UserList[i].UserInfo.FollowCount, d.UserList[i].UserInfo.FollowingCount, d.UserList[i].UserInfo.Likes, d.UserList[i].UserInfo.Ig, d.UserList[i].UserInfo.Yt, d.UserList[i].UserInfo.X, d.UserList[i].UserInfo.RoomId, nil
}
}
related = make([]string, 0)
for i = 0; i < len(d.UserList); i++ {
related = append(related, d.UserList[i].UserInfo.UniqueId + " (" + d.UserList[i].UserInfo.Uid + ")")
}
return GetUserIdFromVideos(username, related, 0)
}
func SearchUsers(username string, cursor *int, retry int) (users []string, more bool, _error error) {
var (
i int
e error
b []byte
d struct {
HasMore int "json:\"has_more\""
UserList []struct {
UserInfo struct {
UniqueId string "json:\"unique_id\""
Uid string "json:\"uid\""
AwemeCount int "json:\"aweme_count\""
FollowCount int "json:\"follower_count\""
FollowingCount int "json:\"following_count\""
Likes int "json:\"total_favorited\""
Ig string "json:\"ins_id\""
Yt string "json:\"youtube_channel_id\""
X string "json:\"signature\""
Secret int "json:\"secret\""
RoomId int64 "json:\"room_id\""
} "json:\"user_info\""
} "json:\"user_list\""
}
)
b, e = AwemeRequest("/aweme/v1/discover/search/" +
"?keyword=" + url.QueryEscape(username) +
"&count=20" +
"&cursor=" + strconv.Itoa(*cursor) +
"&type=1" +
"&search_source=discover" +
"&is_pull_refresh=1" +
"&hot_search=0")
if e != nil {
return nil, false, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return SearchUsers(username, cursor, retry + 1)
}
return nil, false, e
}
users = make([]string, 0)
for i = 0; i < len(d.UserList); i++ {
users = append(users, d.UserList[i].UserInfo.Uid + "\t" + d.UserList[i].UserInfo.UniqueId)
}
*cursor += 20
return users, d.HasMore != 0, nil
}
func GetUserVideos(id string, until int64, cursor *int64, retry int) (videos []string, more bool, _error error) {
var (
i int
e error
b []byte
d struct {
HasMore int "json:\"has_more\""
MaxCursor int64 "json:\"max_cursor\""
AwemeList []struct {
AwemeId string "json:\"aweme_id\""
CreateTime int64 "json:\"create_time\""
Desc string "json:\"desc\""
Video struct {
PlayAddr struct {
UrlList []string "json:\"url_list\""
} "json:\"play_addr\""
} "json:\"video\""
} "json:\"aweme_list\""
}
)
b, e = AwemeRequest("/aweme/v1/aweme/post/" +
"?user_id=" + id +
"&count=20" +
"&max_cursor=" + strconv.FormatInt(*cursor, 10))
if e != nil {
return nil, false, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetUserVideos(id, until, cursor, retry + 1)
}
return nil, false, e
}
videos = make([]string, 0)
//descs = make([]string, 0)
for i = 0; i < len(d.AwemeList); i++ {
if d.AwemeList[i].CreateTime < until {
return videos, false, nil
} else if len(d.AwemeList[i].Video.PlayAddr.UrlList) > 0 {
videos = append(videos, d.AwemeList[i].AwemeId, d.AwemeList[i].Video.PlayAddr.UrlList[0])
}
/*if len(d.AwemeList[i].Desc) > 0 {
descs = append(descs, d.AwemeList[i].Desc)
} else {
descs = append(descs, "")
}*/
}
*cursor = d.MaxCursor
return videos, d.HasMore != 0, nil
}
func GetUserFollows(id string, cursor *int64, retry int) (follows []string, more bool, _error error) {
var (
i int
e error
b []byte
d struct {
HasMore bool "json:\"has_more\""
MaxCursor int64 "json:\"min_time\""
UserList []struct {
UniqueId string "json:\"unique_id\""
Uid string "json:\"uid\""
} "json:\"followings\""
}
)
b, e = AwemeRequest("/aweme/v1/user/following/list/" +
"?user_id=" + id +
"&count=20" +
"&max_time=" + strconv.FormatInt(*cursor, 10))
if e != nil {
return nil, false, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetUserFollows(id, cursor, retry + 1)
}
return nil, false, e
}
follows = make([]string, 0)
for i = 0; i < len(d.UserList); i++ {
follows = append(follows, d.UserList[i].Uid + "\t" + d.UserList[i].UniqueId)
}
*cursor = d.MaxCursor
return follows, d.HasMore != false, nil
}
func GetUserFollowers(id string, cursor *int64, retry int) (followers []string, more bool, _error error) {
var (
i int
e error
b []byte
d struct {
HasMore bool "json:\"has_more\""
MaxCursor int64 "json:\"min_time\""
UserList []struct {
UniqueId string "json:\"unique_id\""
Uid string "json:\"uid\""
} "json:\"followers\""
}
)
b, e = AwemeRequest("/aweme/v1/user/follower/list/" +
"?user_id=" + id +
"&count=20" +
"&max_time=" + strconv.FormatInt(*cursor, 10))
if e != nil {
return nil, false, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetUserFollows(id, cursor, retry + 1)
}
return nil, false, e
}
followers = make([]string, 0)
//followersn = make([]string, 0)
for i = 0; i < len(d.UserList); i++ {
followers = append(followers, d.UserList[i].Uid + "\t" + d.UserList[i].UniqueId)
//followersn = append(followersn, d.UserList[i].UniqueId)
}
*cursor = d.MaxCursor
return followers, d.HasMore != false, nil
}
func GetUserFavorites(id string, cursor *int64, retry int) (videos []string, more bool, _error error) {
var (
i int
e error
b []byte
d struct {
HasMore int "json:\"has_more\""
MaxCursor int64 "json:\"max_cursor\""
AwemeList []struct {
AwemeId string "json:\"aweme_id\""
Video struct {
PlayAddr struct {
UrlList []string "json:\"url_list\""
} "json:\"play_addr\""
} "json:\"video\""
UserInfo struct {
UniqueId string "json:\"unique_id\""
Uid string "json:\"uid\""
} "json:\"author\""
} "json:\"aweme_list\""
}
)
b, e = AwemeRequest("/aweme/v1/aweme/favorite/" +
"?user_id=" + id +
"&count=20" +
"&max_cursor=" + strconv.FormatInt(*cursor, 10))
if e != nil {
return nil, false, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetUserFavorites(id, cursor, retry + 1)
}
return nil, false, e
}
videos = make([]string, 0)
for i = 0; i < len(d.AwemeList); i++ {
videos = append(videos, d.AwemeList[i].AwemeId + "\t" + d.AwemeList[i].UserInfo.Uid + "\t" + d.AwemeList[i].UserInfo.UniqueId + "\t" + d.AwemeList[i].Video.PlayAddr.UrlList[0])
}
*cursor = d.MaxCursor
return videos, d.HasMore != 0, nil
}
func GetHashtag(keyword string, retry int) (cid string, _error error) {
var (
e error
b []byte
d struct {
ChallengeList []struct {
ChallengeInfo struct {
Id string "json:\"cid\""
} "json:\"challenge_info\""
} "json:\"challenge_list\""
Match bool "json:\"is_match\""
}
)
b, e = AwemeRequest("/aweme/v1/challenge/search/" +
"?keyword=" + keyword +
"&count=20" +
"&cursor=0" +
"&search_source=challenge" +
"&is_pull_refresh=1" +
"&hot_search=0")
if e != nil {
return "", e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
GetHashtag(keyword, retry + 1)
}
return "", e
}
return d.ChallengeList[0].ChallengeInfo.Id, nil
}
func GetHashtagVideos(top bool, id string, cursor *int, retry int) (videos []string, more bool, _error error) {
var (
i int
e error
b []byte
d struct {
HasMore int "json:\"has_more\""
Cursor int "json:\"cursor\""
AwemeList []struct {
AwemeId string "json:\"aweme_id\""
Video struct {
PlayAddr struct {
UrlList []string "json:\"url_list\""
} "json:\"play_addr\""
} "json:\"video\""
UserInfo struct {
UniqueId string "json:\"unique_id\""
Uid string "json:\"uid\""
} "json:\"author\""
} "json:\"aweme_list\""
}
)
if top == true {
b, e = AwemeRequest("/aweme/v1/challenge/aweme/" +
"?ch_id=" + id +
"&count=20" +
"&cursor=" + strconv.Itoa(*cursor) +
"&query_type=0" +
"&type=5")
} else {
b, e = AwemeRequest("/aweme/v1/challenge/fresh/aweme/" +
"?ch_id=" + id +
"&count=20" +
"&cursor=" + strconv.Itoa(*cursor) +
"&query_type=0" +
"&type=5")
}
if e != nil {
return nil, false, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetHashtagVideos(top, id, cursor, retry + 1)
}
return nil, false, e
}
videos = make([]string, 0)
for i = 0; i < len(d.AwemeList); i++ {
videos = append(videos, d.AwemeList[i].AwemeId + "\t" + d.AwemeList[i].UserInfo.Uid + "\t" + d.AwemeList[i].UserInfo.UniqueId + "\t" + d.AwemeList[i].Video.PlayAddr.UrlList[0])
}
*cursor += 20
return videos, d.HasMore != 0, nil
}
func GetMusicVideos(top bool, id string, cursor *int, retry int) (videos []string, more bool, _error error) {
var (
i int
e error
b []byte
d struct {
HasMore int "json:\"has_more\""
Cursor int "json:\"cursor\""
AwemeList []struct {
AwemeId string "json:\"aweme_id\""
Video struct {
AwemeId string "json:\"aweme_id\""
PlayAddr struct {
UrlList []string "json:\"url_list\""
} "json:\"play_addr\""
} "json:\"video\""
UserInfo struct {
UniqueId string "json:\"unique_id\""
Uid string "json:\"uid\""
} "json:\"author\""
} "json:\"aweme_list\""
}
)
if top == true {
b, e = AwemeRequest("/aweme/v1/music/aweme/" +
"?music_id=" + id +
"&count=20" +
"&cursor=" + strconv.Itoa(*cursor) +
"&type=6")
} else {
b, e = AwemeRequest("/aweme/v1/music/fresh/aweme/" +
"?music_id=" + id +
"&count=20" +
"&cursor=" + strconv.Itoa(*cursor) +
"&type=6")
}
if e != nil {
return nil, false, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetMusicVideos(top, id, cursor, retry + 1)
}
return nil, false, e
}
videos = make([]string, 0)
for i = 0; i < len(d.AwemeList); i++ {
videos = append(videos, d.AwemeList[i].AwemeId + "\t" + d.AwemeList[i].UserInfo.Uid + "\t" + d.AwemeList[i].UserInfo.UniqueId + "\t" + d.AwemeList[i].Video.PlayAddr.UrlList[0])
}
*cursor += 20
return videos, d.HasMore != 0, nil
}
func GetVideos(ids string, retry int) (videos []string, _error error) {
var (
i int
e error
b []byte
d struct {
AwemeDetails []struct {
AwemeId string "json:\"aweme_id\""
Video struct {
PlayAddr struct {
UrlList []string "json:\"url_list\""
} "json:\"play_addr\""
} "json:\"video\""
} "json:\"aweme_details\""
}
)
b, e = AwemeRequest("/aweme/v1/multi/aweme/detail/" +
"?aweme_ids=[" + ids + "]")
if e != nil {
return nil, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetVideos(ids, retry + 1)
}
return nil, e
}
videos = make([]string, 0)
for i = 0; i < len(d.AwemeDetails); i++ {
if len(d.AwemeDetails[i].Video.PlayAddr.UrlList) > 0 {
videos = append(videos, d.AwemeDetails[i].AwemeId, d.AwemeDetails[i].Video.PlayAddr.UrlList[0])
}
}
return videos, nil
}
func GetVideoInfo(id string, retry int) (awemeid string, auth string, ctime int64, playurl string, dlurl string, mid string, mtitle string, mauthor string, desc string, com int, dl int, view int, sh int, isp bool, inr bool, ss bool, _error error) {
var (
e error
b []byte
d struct {
AwemeDetails struct {
AwemeId string "json:\"aweme_id\""
CreateTime int64 "json:\"create_time\""
Desc string "json:\"desc\""
Video struct {
PlayAddr struct {
UrlList []string "json:\"url_list\""
} "json:\"play_addr\""
DownloadAddr struct {
UrlList []string "json:\"url_list\""
} "json:\"download_addr\""
} "json:\"video\""
Music struct {
Uid string "json:\"mid\""
Title string "json:\"title\""
Author string "json:\"author\""
} "json:\"music\""
Stats struct {
Comments int "json:\"comment_count\""
Downloads int "json:\"download_count\""
Views int "json:\"play_count\""
Shares int "json:\"share_count\""
} "json:\"statistics\""
Status struct {
IsPrivate bool "json:\"is_private\""
InReviewing bool "json:\"in_reviewing\""
SelfSee bool "json:\"self_see\""
} "json:\"status\""
Author struct {
Uid string "json:\"uid\""
UniqueId string "json:\"unique_id\""
} "json:\"author\""
} "json:\"aweme_detail\""
}
)
b, e = AwemeRequest("/aweme/v1/aweme/detail/" +
"?aweme_id=" + id +
"&click_reason=0")
if e != nil {
return "", "", 0, "", "", "", "", "", "", 0, 0, 0, 0, false, false, false, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetVideoInfo(id, retry + 1)
}
return "", "", 0, "", "", "", "", "", "", 0, 0, 0, 0, false, false, false, e
}
return d.AwemeDetails.AwemeId, d.AwemeDetails.Author.Uid, d.AwemeDetails.CreateTime, d.AwemeDetails.Video.PlayAddr.UrlList[0], d.AwemeDetails.Video.DownloadAddr.UrlList[0], d.AwemeDetails.Music.Uid, d.AwemeDetails.Music.Title, d.AwemeDetails.Music.Author, d.AwemeDetails.Desc, d.AwemeDetails.Stats.Comments, d.AwemeDetails.Stats.Downloads, d.AwemeDetails.Stats.Views, d.AwemeDetails.Stats.Shares, d.AwemeDetails.Status.IsPrivate, d.AwemeDetails.Status.InReviewing, d.AwemeDetails.Status.SelfSee, nil
}
func GetVideoComments(id string, cursor *int, retry int) (comments []string, more bool, _error error) {
var (
i int
e error
b []byte
d struct {
HasMore int "json:\"has_more\""
Cursor int "json:\"cursor\""
Comments []struct {
Text string "json:\"text\""
CreateTime int64 "json:\"create_time\""
User struct {
Uid string "json:\"uid\""
UniqueId string "json:\"unique_id\""
} "json:\"user\""
} "json:\"comments\""
}
)
b, e = AwemeRequest("/aweme/v1/comment/list" +
"?aweme_id=" + id +
"&count=20" +
"&cursor=" + strconv.Itoa(*cursor) +
"&comment_style=2" +
"&digged_cid" +
"&insert_cids")
if e != nil {
return nil, false, e
}
e = json.Unmarshal(b, &d)
if e != nil {
if retry < 7 {
Ip = GeneratePublicIp()
return GetVideoComments(id, cursor, retry + 1)
}
return nil, false, e
}
comments = make([]string, 0)
for i = 0; i < len(d.Comments); i++ {
comments = append(comments, d.Comments[i].User.Uid + "\t" + d.Comments[i].User.UniqueId + "\t" + d.Comments[i].Text)
}
*cursor += 20
return comments, d.HasMore != 0, nil
}
func IsValidId(id string) bool {
var i, e int
e = len(id)
switch (e) {
case 0:
return false
case 1:
return id[0] >= '0' && id[0] <= '9'
default:
if e > 20 || id[0] < '1' || id[0] > '9' {
return false
}
}
for i = 1; i < e; i++ {
if id[i] < '0' || id[i] > '9' {
return false
}
}
return true
}
func EnsureDirectory(directory string) error {
var (
s os.FileInfo
e error
)
s, e = os.Stat(directory)
if e != nil {
if os.IsNotExist(e) {
e = os.Mkdir(directory, 0700)
if e != nil {
return e
}
} else {
return e
}
} else if !s.IsDir() {
return errors.New("error: File exists")
}
return nil
}
func EnsureIgnore(filename string) (*os.File, map[string]bool, error) {
var (
l string
e error
ig map[string]bool = make(map[string]bool)
f *os.File
r *bufio.Scanner
)
f, e = os.OpenFile(filename, os.O_RDWR | os.O_APPEND, 0700)
if e != nil {
if os.IsNotExist(e) {
f, e = os.Create(filename)
if e != nil {
return nil, nil, e
}
} else {
return nil, nil, e
}
} else {
r = bufio.NewScanner(f)
for r.Scan() {
l = r.Text()
if IsValidId(l) {
ig[l] = false
}
}
e = r.Err()
if e != nil {
f.Close()
return nil, nil, e
}
}
return f, ig, nil
}
func UpdateIgnore(file *os.File, ids []string) error {
var (
i, j int
e error
w *bufio.Writer = bufio.NewWriter(file)
)
for j = len(ids); i < j; i++ {
_, e = w.WriteString(ids[i])
if e != nil {
return e
}
e = w.WriteByte(byte('\n'))
if e != nil {
return e
}
}
w.Flush()
return nil
}
func EnsureIds(filename string, ignore map[string]bool) ([]string, error) {
var (
i int
o bool
l string
e error
ids []string = make([]string, 0)
f *os.File
r *bufio.Scanner
b bytes.Buffer
)
f, e = os.OpenFile(filename, os.O_RDONLY, 0700)
if e != nil {
return nil, e
}
defer f.Close()
r = bufio.NewScanner(f)
if len(ignore) > 0 {
for r.Scan() {
l = r.Text()
if !IsValidId(l) {
continue
} else if _, o = ignore[l]; o {
continue
}
b.WriteString(l)
if i == 19 {
ids = append(ids, b.String())
b.Reset()
i = 0
} else {
b.WriteByte(',')
i++
}
}
} else {
for r.Scan() {
l = r.Text()
if !IsValidId(l) {
continue
}
b.WriteString(l)
if i == 19 {
ids = append(ids, b.String())
b.Reset()
i = 0
} else {
b.WriteByte(',')
i++
}
}
}
e = r.Err()
if e != nil {
f.Close()
return nil, e
}
if b.Len() > 0 {
b.Truncate(b.Len() - 1)
ids = append(ids, b.String())
}
return ids, nil
}
func main() {
const (
DATE_FORMAT = "2006-01-02"
)
var (
HELP string = "usage:\n" +
filepath.Base(os.Args[0]) + " <name/id/file> [date/ping/video/followers/following/list/liked/music/hashtag/comments/live/search]\n" +
"arguments:\n" +
"name/id/file (@name, id, path) - name or id of a user, video, song, or hashtag, or a file with video ids\n" +
"using @name or user id:\n" +
" search (without '@') - find matching or similar usernames\n" +
" list - list a user's videos instead of downloading them\n" +
" date (YYYY-MM-DD) - date until which to download videos\n" +
" ping - ping a user\n" +
" user - alias for ping\n" +
" data - ping but output a tab-delimited one-liner\n" +
" following - list accounts followed by the user\n" +
" followers - list user's followers\n" +
" liked - list user's liked videos\n" +
" live [timeout (seconds)] - if a user is live, write the livestream data to a .flv file forever until program is manually terminated\n" +
"using video id:\n" +
" video - show video urls and data\n" +
" comments - show video comments\n" +
"using music id, #hashtag, or hashtag id:\n" +
"music/hashtag [top] - list recent or popular videos using song or hashtag\n" +
"examples:\n" +
" $ ./" + filepath.Base(os.Args[0]) + " @nyannyancosplay ping\n" +
" $ ./" + filepath.Base(os.Args[0]) + " \"#xyzcba\" top\n" +
" $ ./" + filepath.Base(os.Args[0]) + " <( ./" + filepath.Base(os.Args[0]) + " `./" + filepath.Base(os.Args[0]) + " $VIDEO_ID video | grep 'music id' | awk '{print $3}'` music | awk '{print $1}' )\n" +
"*usage over TOR is recommended\n" +
"*the file's name cannot begin with \"@\" or contain only digits (^(?:0|(:?[1-9][0-9]*))$)\n" +
"*the file has each video id on a new line like ignore.txt\n" +
"*video lists are output as tab-seperated data like shown below:\n" +
"6648394283958349583 4992348 user093092394 http://v16.muscdn.com/...\n" +
"(video id) (user id) (user name) (video url)\n" +
"*comment lists are output likewise like this:\n" +
"6693429093034095344 loomaster2020 open bob and vagene\n" +
"(user id) (user name) (text)"
top, isMusic, isVideo, isHashtag, isLiked, isList, isComments, isLive, isSearchUser, isUserData, o, m, ru, isFi, isFo, isp, inr, ss bool
i, j, y, nfo, nfi, nl, k, com, dl, view, sh int
c, u, ctime int64
d time.Time
id, n, g, yt, x, vid, vauth, vvid, playurl, durl, mid, mtitle, mauth, desc string
ig map[string]bool
ov, nv, v, tabFi, tabFo, tabHtv, tabMuv, tabLiv, tabCom, tabUserq []string
e error
f *os.File
l sync.Mutex
sc chan os.Signal = make(chan os.Signal, 1)
t chan bool = make(chan bool, 20)
)
switch (len(os.Args)) {
case 4:
if os.Args[3] == "top" {
top = true
/*} else if os.Args[2] == "watermark" {
watermark = true
}*/
}/* else if os.Args[2] == "quiet" {
d, e = time.Parse(DATE_FORMAT, os.Args[2])
if e != nil {
fmt.Println(HELP)
return
}
u = d.Unix()
}*/
fallthrough
case 3:
if os.Args[2] == "ping" {
o = true
} else if os.Args[2] == "user" {
o = true
} else if os.Args[2] == "search" {
isSearchUser = true
} else if os.Args[2] == "following" {
isFi = true
} else if os.Args[2] == "followers" {
isFo = true
} else if os.Args[2] == "hashtag" {
isHashtag = true
} else if os.Args[2] == "liked" {
isLiked = true
} else if os.Args[2] == "music" {
isMusic = true
} else if os.Args[2] == "video" {
isVideo = true
} else if os.Args[2] == "list" {
isList = true
} else if os.Args[2] == "comments" {
isComments = true
} else if os.Args[2] == "live" {
isLive = true
//} else if os.Args[2] == "quiet" {
// isQuiet == true
} else if os.Args[2] == "data" {
isUserData = true
o = true
//} else if os.Args[2] == "top" {
// top = true
} else {
d, e = time.Parse(DATE_FORMAT, os.Args[2])
if e != nil {
fmt.Println(HELP)
return
}
u = d.Unix()
}
fallthrough
case 2:
n = os.Args[1]
default:
fmt.Println(HELP)
return
}
rand.Seed(time.Now().UTC().UnixNano())
Os = GenerateBase62(16)
DeviceId = GenerateDeviceId()
if len(n) == 0 {
fmt.Println(HELP)
return
} else if n[0] == '@' {
if len(n) == 1 {
fmt.Println(HELP)
return
}
n = n[1:]
id, m, i, nfo, nfi, nl, g, yt, x, c, e = GetUserId(n, 0)
} else if n[0] == '#' {
if len(n) == 1 {
fmt.Println(HELP)
return
}
n = n[1:]
id, e = GetHashtag(n, 0)
ru = true
m = true
k = 0
for ru && m {
/*if top == true {
tabHtv, m, e = GetHashtagVideos(true, id, &k, 0)
} else {
tabHtv, m, e = GetHashtagVideos(false, id, &k, 0)
}
for i = 0; i < len(tabHtv); i++ {
fmt.Println(tabHtv[i])
}
}*/
tabHtv, m, e = GetHashtagVideos(false, id, &k, 0)
for i = 0; i < len(tabHtv); i++ {
fmt.Println(tabHtv[i])
}
}
return
} else if isHashtag {
ru = true
m = true
k = 0
for ru && m {
if top == true {
tabHtv, m, e = GetHashtagVideos(true, n, &k, 0)
} else {
tabHtv, m, e = GetHashtagVideos(false, n, &k, 0)
}
for i = 0; i < len(tabHtv); i++ {
fmt.Println(tabHtv[i])
}
}
return
} else if isLiked {
ru = true
m = true
c = 0
for ru && m {
tabLiv, m, e = GetUserFavorites(n, &c, 0)
for i = 0; i < len(tabLiv); i++ {
fmt.Println(tabLiv[i])
}
}
return
} else if isMusic {
ru = true
m = true
k = 0
for ru && m {
if top == true {
tabMuv, m, e = GetMusicVideos(true, n, &k, 0)
} else {
tabMuv, m, e = GetMusicVideos(false, n, &k, 0)
}
for i = 0; i < len(tabMuv); i++ {
fmt.Println(tabMuv[i])
}
}
return
} else if isSearchUser {
ru = true
m = true
k = 0
for ru && m {
tabUserq, m, e = SearchUsers(n, &k, 0)
for i = 0; i < len(tabUserq); i++ {
fmt.Println(tabUserq[i])
}
}
return
} else if isComments {
vid =n
ru = true
m = true
k = 0
for ru && m {
tabCom, m, e = GetVideoComments(vid, &k, 0)
for i = 0; i < len(tabCom); i++ {
fmt.Println(tabCom[i])
}
}
return
} else if isVideo {
vvid, vauth, ctime, playurl, durl, mid, mtitle, mauth, desc, com, dl, view, sh, isp, inr, ss, e = GetVideoInfo(n, 0)
fmt.Println("creator:\t", vauth)
fmt.Println("video id:\t", vvid)
fmt.Println("create time:\t", strconv.FormatInt(ctime, 10))
fmt.Println("views:\t\t", strconv.Itoa(view))
fmt.Println("comments:\t", strconv.Itoa(com))
fmt.Println("downloads:\t", strconv.Itoa(dl))
fmt.Println("shares:\t\t", strconv.Itoa(sh))
fmt.Println("description:\t", desc)
fmt.Println("music:\t\t", mtitle)
fmt.Println("music author:\t", mauth)
fmt.Println("music id:\t", mid)
if isp {
fmt.Println("private: yes")
}
if inr {
fmt.Println("in review: yes")
}
if ss {
fmt.Println("self see: yes")
}
fmt.Println("url:\n" + playurl)
fmt.Println("url (watermark):\n" + durl)
return
} else if IsValidId(n) {
id = n
n, m, i, nfo, nfi, nl, g, yt, x, c, e = GetUserInfo(id, 0)
} else {
if len(os.Args) == 2 {
e = EnsureDirectory("download")
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
f, ig, e = EnsureIgnore("download/ignore.txt")
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
defer f.Close()
ov, e = EnsureIds(n, ig)
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
e = os.Chdir("download")
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
nv = make([]string, 0)
signal.Notify(sc, os.Interrupt)
go func() {
<-sc
ru = false
}()
ru = true
defer func() {
for j > 0 {
<-t
j--
}
e = UpdateIgnore(f, nv)
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
}()
for ru && y < len(ov) {
v, e = GetVideos(ov[y], 0)
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
for i = 0; i < len(v); i += 2 {
go func(id string, uri string) {
var (
r bool
e error
)
r, e = DownloadAweme(id + ".mp4", uri)
if e != nil {
l.Lock()
fmt.Println(id, "failed")
fmt.Fprintln(os.Stderr, e.Error())
l.Unlock()
} else {
l.Lock()
nv = append(nv, id)
if !r {
fmt.Println(id, "is downloaded")
}
l.Unlock()
}
t <- true
}(v[i], v[i + 1])
j++
if j == cap(t) {
<-t
j--
}
}
y++
}
return
} else {
fmt.Println(HELP)
}
return
}
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
if isFi {
ru = true
m = true
for ru && m {
tabFi, m, e = GetUserFollows(id, &c, 0)
for i = 0; i < len(tabFi); i++ {
fmt.Println(tabFi[i])
}
}
return
}
if isFo {
ru = true
m = true
for ru && m {
tabFo, m, e = GetUserFollowers(id, &c, 0)
for i = 0; i < len(tabFo); i++ {
fmt.Println(tabFo[i])
}
}
return
}
if isList {
ru = true
m = true
for ru && m {
v, m, e = GetUserVideos(id, u, &c, 0)
for i = 0; i < len(v); i=i+2 {
fmt.Println(v[i] + "\t" + v[i + 1])
}
}
return
}
if isLive {
if c != 0 {
n = RequestStream(c)
if len(n) > 0 {
n = GetFlvStream(n)
if len(n) > 0 {
/*go func(id string, uri string) {
var (
r bool
e error
)
r, e = DownloadStream(id + ".flv", uri)
fmt.Println(e)
if e != nil {
l.Lock()
fmt.Println(id, "failed")
fmt.Fprintln(os.Stderr, e.Error())
l.Unlock()
}
if !r {
fmt.Println("livestream end")
}
}(strconv.FormatInt(c, 10), n)*/
fmt.Println("recording livestream to " + strconv.FormatInt(c, 10) + ".flv")
fmt.Println("press ctrl-c at any time to stop")
DownloadStream(strconv.FormatInt(c, 10) + ".flv", n)
}
} else {
fmt.Println("Could not get livestream URL.")
}
} else {
fmt.Println("User is not live.")
}
return
}
if isUserData {
fmt.Println(n + "\t" + id + "\t" + strconv.Itoa(i) + "\t" + strconv.Itoa(nl) + "\t" + strconv.Itoa(nfo) + " " + strconv.Itoa(nfi))
c = 0
} else {
fmt.Println("name:\t\t", n)
fmt.Println("id:\t\t", id)
if m {
fmt.Println("videos:\t\t ?")
} else {
fmt.Println("videos:\t\t", i)
}
fmt.Println("likes:\t\t", nl )
fmt.Println("followers:\t", nfo, nfi)
if len(g) > 0 {
fmt.Println("ig:\t\t", g)
}
if len(yt) > 0 {
fmt.Println("yt:\t\t", yt)
}
if len(x) > 0 {
fmt.Println("signature:\t", x)
}
if m {
fmt.Println("(private user)")
return
}
if i == 0 {
return
}
if c != 0 {
n = RequestStream(c)
if len(n) > 0 {
fmt.Println("live:\t\t", strconv.FormatInt(c, 10))
fmt.Println("hls stream:\t", n)
n = GetFlvStream(n)
if len(n) > 0 {
fmt.Println("flv stream:\t", n)
}
}
c = 0
}
}
if o {
return
}
e = EnsureDirectory(id)
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
e = os.Chdir(id)
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
f, ig, e = EnsureIgnore("ignore.txt")
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
defer f.Close()
nv = make([]string, 0)
signal.Notify(sc, os.Interrupt)
go func() {
<-sc
ru = false
}()
ru = true
m = true
defer func() {
for j > 0 {
<-t
j--
}
e = UpdateIgnore(f, nv)
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
}()
for ru && m {
v, m, e = GetUserVideos(id, u, &c, 0)
if e != nil {
fmt.Fprintln(os.Stderr, e.Error())
return
}
for i = 0; i < len(v); i += 2 {
if len(ig) > 0 {
_, o = ig[v[i]]
if o {
continue
}
}
go func(id string, uri string) {
var (
r bool
e error
)
r, e = DownloadAweme(id + ".mp4", uri)
if e != nil {
l.Lock()
fmt.Println(id, "failed")
fmt.Fprintln(os.Stderr, e.Error())
l.Unlock()
} else {
l.Lock()
nv = append(nv, id)
if !r {
fmt.Println(id, "is downloaded")
}
l.Unlock()
}
t <- true
}(v[i], v[i + 1])
j++
if j == cap(t) {
<-t
j--
}
}
}
}