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)
}
}