Login light
package main

import (
    //输出
    . "fmt"
    //读取配置文件
    "github.com/BurntSushi/toml"
    //解析参数
    "flag"
    //数组排序
    "sort"
    //时间
    "time"
    //解析json
    "encoding/json"
    //md5
    "crypto/md5"
    "encoding/hex"

    //网络库
    "net/http"
    //io 工具包 写文件
    "io/ioutil"
    "os"
    //跨平台路径库
    // "path/filepath"
)

// 配置文件结构体
type Main struct {
    Base `toml:"Base"`
    UP   `toml:"UP"`
}
type Base struct {
    SingleLimit int    `toml:"SingleLimit"`
    ShowCount   int    `toml:"ShowCount"`
    SleepSec    int    `toml:"SleepSec"`
    JSONFile    string `toml:"JSONFile"`
    // StaticDir   string `toml:"StaticDir"`
}
type UP struct {
    List []int64 `toml:"List"`
}

//json结果结构体
type BilibiliJson struct {
    UpCount     int64             `json:"up_count"`
    UpListMd5   string            `json:"up_list_md5"`
    LastTimeStr string            `json:"last_time_str"`
    LastTime    int64             `json:"last_time"`
    UpImg       map[string]string `json:"up_img"`
    List        []AvList          `json:"list"`
}

//json结果集中的视频信息结构体
type AvList struct {
    Title       string `json:"title"`
    Description string `json:"description"`
    Author      string `json:"author"`
    Mid         int64  `json:"mid"`
    Pic         string `json:"pic"`
    Created     int64  `json:"created"`
    Length      string `json:"length"`
    Aid         int64  `json:"aid"`
    Bvid        string `json:"bvid"`
}

//视频信息结构体数组
type VlistArr []AvList

func (I VlistArr) Len() int {
    return len(I)
}
func (I VlistArr) Less(i, j int) bool {
    return I[i].Created > I[j].Created
}
func (I VlistArr) Swap(i, j int) {
    I[i], I[j] = I[j], I[i]
}

var appPath = flag.String("conf", "E:\\goStudy\\biliGet\\bili.toml", "need conf path")
var action = flag.String("action", "json", "need action")

// var appPath = flag.String("conf", "E:\\goStudy\\src\\App\\biliGet\\bili.toml","need conf path")
var mainConfig *Main

func main() {
    //获取配置信息
    ReadConfig()
    Printf("config : %+v", *mainConfig)
    Println()
    UpdateJson()
    // if "avData" == *action {
    //     UpdateJson()
    // } else if "headData" == *action{
    //     updateHeadJson()
    // } else {
    //     Println("wrong action.")
    // }
}
func ReadConfig() {
    flag.Parse()
    Printf("Config path: %s", *appPath)
    Println()
    //这里如果用 := 那就是局部变量 如果用 = 就是使用了外部定义的全局变量 !
    mainConfig, _ = ReadTomlConf(*appPath)
}

//读取当前json文件
func ReadDataJson() (BilibiliJson BilibiliJson) {
    var (
        err      error
        fp       *os.File
        fcontent []byte
    )
    if fp, err = os.Open(mainConfig.Base.JSONFile); err != nil {
        Println("open error ", err)
        return
    }
    if fcontent, err = ioutil.ReadAll(fp); err != nil {
        Println("ReadAll error ", err)
        return
    }
    // Println(string(fcontent))
    // Printf("&BilibiliJson:%v\n", &BilibiliJson);
    // Printf("BilibiliJson:%v\n", BilibiliJson);
    // Printf("BilibiliJson:%v\n", *&BilibiliJson);
    if err = json.Unmarshal(fcontent, &BilibiliJson); err != nil {
        Println("json.Unmarshal error", err)
        return
    }
    return
}

//循环每一个up主的id更新json信息
func UpdateJson() {
    var (
        avList VlistArr
    )
    for i, mid := range mainConfig.UP.List {
        Printf("pos:%d, mid:%d\n", i+1, mid)
        //获取json地址
        url := Sprintf("https://api.bilibili.com/x/space/arc/search?mid=%d&ps=%d&pn=1&order=pubdate", mid, mainConfig.Base.SingleLimit)
        //获取json内容 []byte
        body := Get(url)
        //sleep
        time.Sleep(time.Duration(mainConfig.Base.SleepSec) * time.Second)
        //获取json结构对应的 map[string]interface{}
        jsonDataMap := JsonDecode(body)
        //获取json的结构体数据
        jsonDataStruct := VlistParse(jsonDataMap)
        for _, item := range jsonDataStruct {
            item.Mid = mid
            avList = append(avList, item)
            // Printf("title:%s,Created:%d\n", item.Title, item.Created)
        }
        // Printf("%+v", avList) // 打印数组
    }
    Printf("av count:%d", len(avList))
    Println()

    //排序
    sort.Sort(avList)
    //用切片截取前 mainConfig.Base.ShowCount 个
    if len(avList) > mainConfig.Base.ShowCount {
        avList = avList[:mainConfig.Base.ShowCount]
    }
    upJsonData, _ := json.Marshal(mainConfig.UP.List)
    //组装json结构
    retJson := BilibiliJson{
        UpCount:     int64(len(mainConfig.UP.List)),
        UpListMd5:   string(md5Encode(upJsonData)),
        LastTimeStr: string(time.Now().Format("2006-01-02 15:04:05")),
        LastTime:    int64(time.Now().Unix()),
        UpImg:       map[string]string{},
        List:        avList,
    }
    //如果文件存在
    if checkFileIsExist(mainConfig.Base.JSONFile) {
        oldBilibiliJson := ReadDataJson()
        // Printf("%+v", BilibiliJson)
        // 如果uplist MD5变化,则更新head头像
        if retJson.UpListMd5 != oldBilibiliJson.UpListMd5 {
            //更新头像
            updateHeadJson(&retJson)
        } else {
            retJson.UpImg = oldBilibiliJson.UpImg
        }
    } else {
        updateHeadJson(&retJson)
    }
    //更新封面
    // updateCover(&retJson)
    //更新json文件
    retJsonByte, _ := json.Marshal(retJson)
    //写入文件(字节数组)
    err2 := ioutil.WriteFile(mainConfig.Base.JSONFile, retJsonByte, 0666)
    check(err2)
}

//更新up主头像数组
func updateHeadJson(retJson *BilibiliJson) {
    Println("updateHeadJson.")
    for _, mid := range mainConfig.UP.List {
        upDataMap := JsonDecode(Get(Sprintf("https://api.bilibili.com/x/space/acc/info?mid=%d", mid)))
        faceUrl := upDataMap["data"].(map[string]interface{})["face"].(string)
        retJson.UpImg[Sprintf("%d", mid)] = faceUrl[5:]
    }
}

//更新头像
// func updateHead() {
//     //目录不存在则创建
//     if !checkDirIsExist(mainConfig.Base.StaticDir) {
//         err := os.Mkdir(mainConfig.Base.StaticDir, os.ModePerm)
//         check(err)
//     }
//     for _, mid := range mainConfig.UP.List {
//         headpath := filepath.Join(mainConfig.Base.StaticDir, Sprintf("head_%d.jpg", mid))
//         if !checkFileIsExist(headpath) {
//             Printf("headpath:%s\n", headpath)
//             upDataMap := JsonDecode(Get(Sprintf("https://api.bilibili.com/x/space/acc/info?mid=%d", mid)))
//             faceUrl := upDataMap["data"].(map[string]interface{})["face"].(string)
//             ioutil.WriteFile(headpath, Get(faceUrl), 0666)
//         } else {
//             Printf("exist headpath:%s\n", headpath)
//         }
//     }
// }
//更新封面
// func updateCover(retJSon *BilibiliJson) {
//     //目录不存在则创建
//     if !checkDirIsExist(mainConfig.Base.StaticDir) {
//         err := os.Mkdir(mainConfig.Base.StaticDir, os.ModePerm)
//         check(err)
//     }
//     for _, avItem := range retJSon.List {
//         coverpath := filepath.Join(mainConfig.Base.StaticDir, Sprintf("cover_av%d.jpg", avItem.Aid))
//         if !checkFileIsExist(coverpath) {
//             Printf("coverpath:%s\n", coverpath)
//             ioutil.WriteFile(coverpath, Get(Sprintf("http:%s@160w_100h_100Q_1c.webp", avItem.Pic)), 0666)
//         } else {
//             Printf("exist coverpath:%s\n", coverpath)
//         }
//     }
// }
//读取配置文件 返回结构体
func ReadTomlConf(fname string) (p *Main, err error) {
    var (
        fp       *os.File
        fcontent []byte
    )
    p = new(Main)
    if fp, err = os.Open(fname); err != nil {
        Println("open error ", err)
        return
    }
    if fcontent, err = ioutil.ReadAll(fp); err != nil {
        Println("ReadAll error ", err)
        return
    }
    //    Println(string(fcontent))
    if err = toml.Unmarshal(fcontent, p); err != nil {
        Println("toml.Unmarshal error", err)
        return
    }
    return
}

//获取一个链接的网页内容
func Get(url string) []byte {
    resp, err := http.Get(url)
    if nil != err {
        Println("http error", err)
        return nil
    }
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        Println("read error", err)
        return nil
    }
    return body
}

//解析一个json
func JsonDecode(data []byte) map[string]interface{} {
    m := make(map[string]interface{})
    json.Unmarshal(data, &m)
    return m
}

//获取vlist结构体数组
func VlistParse(m map[string]interface{}) VlistArr {
    vlist := m["data"].(map[string]interface{})["list"].(map[string]interface{})["vlist"].([]interface{})
    //根据数组长度初始化结构体数组
    var vDataList = make(VlistArr, len(vlist))
    for i, item := range vlist {
        // Printf("%s=>%s\n",i,d)
        // Println(d.(map[string]interface{})["author"])
        // Println(i)
        tmp := item.(map[string]interface{})
        vDataList[i] = AvList{
            tmp["title"].(string),
            tmp["description"].(string),
            tmp["author"].(string),
            int64(tmp["mid"].(float64)),
            tmp["pic"].(string),
            int64(tmp["created"].(float64)),
            tmp["length"].(string),
            int64(tmp["aid"].(float64)),
            tmp["bvid"].(string),
        }
    }
    return vDataList
}

//md5 crypt
func md5Encode(s []byte) string {
    m := md5.Sum(s)
    return hex.EncodeToString(m[:])
}

//文件是否存在
func checkFileIsExist(filename string) bool {
    var exist = true
    if _, err := os.Stat(filename); os.IsNotExist(err) {
        exist = false
    }
    return exist
}

// 判断目录是否存在
// func checkDirIsExist(fileAddr string)bool{
//     s,err := os.Stat(fileAddr)
//     if err != nil{
//         Println(err)
//         return false
//     }
//     return s.IsDir()
// }
//异常
func check(e error) {
    if e != nil {
        panic(e)
    }
}