package main import ( "flag" "fmt" "io/ioutil" "net/url" "os" "strings" "github.com/fatih/color" "github.com/rs/zerolog" "github.com/iawia002/annie/config" "github.com/iawia002/annie/downloader" "github.com/iawia002/annie/extractors/bcy" "github.com/iawia002/annie/extractors/bilibili" "github.com/iawia002/annie/extractors/douyin" "github.com/iawia002/annie/extractors/douyu" "github.com/iawia002/annie/extractors/facebook" "github.com/iawia002/annie/extractors/geekbang" "github.com/iawia002/annie/extractors/instagram" "github.com/iawia002/annie/extractors/iqiyi" "github.com/iawia002/annie/extractors/mgtv" "github.com/iawia002/annie/extractors/miaopai" "github.com/iawia002/annie/extractors/netease" "github.com/iawia002/annie/extractors/pixivision" "github.com/iawia002/annie/extractors/pornhub" "github.com/iawia002/annie/extractors/qq" "github.com/iawia002/annie/extractors/tangdou" "github.com/iawia002/annie/extractors/tiktok" "github.com/iawia002/annie/extractors/tumblr" "github.com/iawia002/annie/extractors/twitter" "github.com/iawia002/annie/extractors/udn" "github.com/iawia002/annie/extractors/universal" "github.com/iawia002/annie/extractors/vimeo" "github.com/iawia002/annie/extractors/weibo" "github.com/iawia002/annie/extractors/xvideos" "github.com/iawia002/annie/extractors/yinyuetai" "github.com/iawia002/annie/extractors/youku" "github.com/iawia002/annie/extractors/youtube" "github.com/iawia002/annie/utils" ) func init() { flag.BoolVar(&config.Debug, "d", false, "Debug mode") flag.BoolVar(&config.Version, "v", false, "Show version") flag.BoolVar(&config.InfoOnly, "i", false, "Information only") flag.StringVar(&config.Cookie, "c", "", "Cookie") flag.BoolVar(&config.Playlist, "p", false, "Download playlist") flag.StringVar(&config.Refer, "r", "", "Use specified Referrer") flag.StringVar(&config.Stream, "f", "", "Select specific stream to download") flag.StringVar(&config.OutputPath, "o", "", "Specify the output path") flag.StringVar(&config.OutputName, "O", "", "Specify the output file name") flag.BoolVar(&config.ExtractedData, "j", false, "Print extracted data") flag.IntVar(&config.ChunkSizeMB, "cs", 0, "HTTP chunk size for downloading (in MB)") flag.BoolVar(&config.UseAria2RPC, "aria2", false, "Use Aria2 RPC to download") flag.StringVar(&config.Aria2Token, "aria2token", "", "Aria2 RPC Token") flag.StringVar(&config.Aria2Addr, "aria2addr", "localhost:6800", "Aria2 Address") flag.StringVar(&config.Aria2Method, "aria2method", "http", "Aria2 Method") flag.IntVar( &config.ThreadNumber, "n", 10, "The number of download thread (only works for multiple-parts video)", ) flag.StringVar(&config.File, "F", "", "URLs file path") flag.IntVar(&config.ItemStart, "start", 1, "Define the starting item of a playlist or a file input") flag.IntVar(&config.ItemEnd, "end", 0, "Define the ending item of a playlist or a file input") flag.StringVar( &config.Items, "items", "", "Define wanted items from a file or playlist. Separated by commas like: 1,5,6,8-10", ) flag.BoolVar(&config.Caption, "C", false, "Download captions") flag.IntVar( &config.RetryTimes, "retry", 10, "How many times to retry when the download failed", ) // youku flag.StringVar(&config.YoukuCcode, "ccode", "0590", "Youku ccode") flag.StringVar( &config.YoukuCkey, "ckey", "7B19C0AB12633B22E7FE81271162026020570708D6CC189E4924503C49D243A0DE6CD84A766832C2C99898FC5ED31F3709BB3CDD82C96492E721BDD381735026", "Youku ckey", ) flag.StringVar(&config.YoukuPassword, "password", "", "Youku password") } func printError(url string, err error) { fmt.Printf( "Downloading %s error:\n%s\n", color.CyanString("%s", url), color.RedString("%v", err), ) } func download(videoURL string) bool { var ( domain string err error data []downloader.Data ) bilibiliShortLink := utils.MatchOneOf(videoURL, `^(av|ep)\d+`) if bilibiliShortLink != nil && len(bilibiliShortLink) > 1 { bilibiliURL := map[string]string{ "av": "https://www.bilibili.com/video/", "ep": "https://www.bilibili.com/bangumi/play/", } domain = "bilibili" videoURL = bilibiliURL[bilibiliShortLink[1]] + videoURL } else { u, err := url.ParseRequestURI(videoURL) if err != nil { printError(videoURL, err) return false } domain = utils.Domain(u.Host) } switch domain { case "douyin", "iesdouyin": data, err = douyin.Extract(videoURL) case "bilibili": data, err = bilibili.Extract(videoURL) case "bcy": data, err = bcy.Extract(videoURL) case "pixivision": data, err = pixivision.Extract(videoURL) case "youku": data, err = youku.Extract(videoURL) case "youtube", "youtu": // youtu.be data, err = youtube.Extract(videoURL) case "iqiyi": data, err = iqiyi.Extract(videoURL) case "mgtv": data, err = mgtv.Extract(videoURL) case "tangdou": data, err = tangdou.Extract(videoURL) case "tumblr": data, err = tumblr.Extract(videoURL) case "vimeo": data, err = vimeo.Extract(videoURL) case "facebook": data, err = facebook.Extract(videoURL) case "douyu": data, err = douyu.Extract(videoURL) case "miaopai": data, err = miaopai.Extract(videoURL) case "163": data, err = netease.Extract(videoURL) case "weibo": data, err = weibo.Extract(videoURL) case "instagram": data, err = instagram.Extract(videoURL) case "twitter": data, err = twitter.Extract(videoURL) case "qq": data, err = qq.Extract(videoURL) case "yinyuetai": data, err = yinyuetai.Extract(videoURL) case "geekbang": data, err = geekbang.Extract(videoURL) case "pornhub": data, err = pornhub.Extract(videoURL) case "xvideos": data, err = xvideos.Extract(videoURL) case "udn": data, err = udn.Extract(videoURL) case "tiktok": data, err = tiktok.Extract(videoURL) default: data, err = universal.Extract(videoURL) } if err != nil { // if this error occurs, it means that an error occurred before actually starting to extract data // (there is an error in the preparation step), and the data list is empty. printError(videoURL, err) return false } var isErr bool for _, item := range data { if item.Err != nil { // if this error occurs, the preparation step is normal, but the data extraction is wrong. // the data is an empty struct. printError(item.URL, item.Err) isErr = true continue } err = downloader.Download(item, videoURL, config.ChunkSizeMB) if err != nil { printError(item.URL, err) isErr = true } } return !isErr } func main() { flag.Parse() args := flag.Args() if config.Version { utils.PrintVersion() return } // introduced by "github.com/rylio/ytdl" zerolog.SetGlobalLevel(zerolog.InfoLevel) if config.Debug { utils.PrintVersion() zerolog.SetGlobalLevel(zerolog.DebugLevel) } if config.File != "" { file, err := os.Open(config.File) if err != nil { fmt.Printf("Error %v", err) return } defer file.Close() fileItems := utils.ParseInputFile(file) args = append(args, fileItems...) } if len(args) < 1 { fmt.Println("Too few arguments") fmt.Println("Usage: annie [args] URLs...") flag.PrintDefaults() return } if config.Cookie != "" { // If config.Cookie is a file path, convert it to a string to ensure // config.Cookie is always string if _, fileErr := os.Stat(config.Cookie); fileErr == nil { // Cookie is a file data, err := ioutil.ReadFile(config.Cookie) if err != nil { color.Red("%v", err) return } config.Cookie = string(data) } } var isErr bool for _, videoURL := range args { if !download(strings.TrimSpace(videoURL)) { isErr = true } } if isErr { os.Exit(1) } }