[
  {
    "path": ".gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n*.test\n*.prof\n\n*.iml\n\n.idea\n\ntest.go\n\nbilibili-service\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM golang:onbuild\n\nMAINTAINER whiteblue0616@gmail.com\n\nADD . $GOPATH/src/github.com/whiteblue/bilibili-go\n\nWORKDIR $GOPATH/src/github.com/whiteblue/bilibili-go\n\n\n# RUN go get -u github.com/go-playground/log \\\n#    && go get -u github.com/gin-gonic/gin \\\n#    && go get -u github.com/gin-gonic/contrib/gzip \\\n#    && go get -u github.com/valyala/fasthttp\n\nRUN go get github.com/tools/godep\n\nEXPOSE 8080\n\nCMD [\"./run.sh\"]\n\n"
  },
  {
    "path": "Godeps/Godeps.json",
    "content": "{\n\t\"ImportPath\": \"github.com/whiteblue/bilibili-go\",\n\t\"GoVersion\": \"go1.7\",\n\t\"GodepVersion\": \"v75\",\n\t\"Deps\": [\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/anacrolix/missinggo\",\n\t\t\t\"Rev\": \"03b41562f79c09a2bccb139f2a6282dcd14d6ce6\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/anacrolix/sync\",\n\t\t\t\"Rev\": \"812602587b72df6a2a4f6e30536adc75394a374b\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/gin-gonic/contrib/gzip\",\n\t\t\t\"Rev\": \"547e518040cfb96576b507d2f26779ab9c6fc829\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/gin-gonic/gin\",\n\t\t\t\"Comment\": \"v1.0rc1-268-gf931d1e\",\n\t\t\t\"Rev\": \"f931d1ea80ae95a6fc739213cdd9399bd2967fb6\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/gin-gonic/gin/binding\",\n\t\t\t\"Comment\": \"v1.0rc1-268-gf931d1e\",\n\t\t\t\"Rev\": \"f931d1ea80ae95a6fc739213cdd9399bd2967fb6\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/gin-gonic/gin/render\",\n\t\t\t\"Comment\": \"v1.0rc1-268-gf931d1e\",\n\t\t\t\"Rev\": \"f931d1ea80ae95a6fc739213cdd9399bd2967fb6\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/go-playground/log\",\n\t\t\t\"Comment\": \"v4.0.1\",\n\t\t\t\"Rev\": \"700a09cca964de69c81ed1d7fa3bb9b94af1fd5f\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/go-playground/log/handlers/console\",\n\t\t\t\"Comment\": \"v4.0.1\",\n\t\t\t\"Rev\": \"700a09cca964de69c81ed1d7fa3bb9b94af1fd5f\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/golang/protobuf/proto\",\n\t\t\t\"Rev\": \"1f49d83d9aa00e6ce4fc8258c71cc7786aec968a\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/klauspost/compress/flate\",\n\t\t\t\"Comment\": \"v1.1-2-ge3b7981\",\n\t\t\t\"Rev\": \"e3b7981a12dd3cab49afa1d3a50e715846f23732\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/klauspost/compress/gzip\",\n\t\t\t\"Comment\": \"v1.1-2-ge3b7981\",\n\t\t\t\"Rev\": \"e3b7981a12dd3cab49afa1d3a50e715846f23732\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/klauspost/compress/zlib\",\n\t\t\t\"Comment\": \"v1.1-2-ge3b7981\",\n\t\t\t\"Rev\": \"e3b7981a12dd3cab49afa1d3a50e715846f23732\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/klauspost/cpuid\",\n\t\t\t\"Comment\": \"v1.0\",\n\t\t\t\"Rev\": \"09cded8978dc9e80714c4d85b0322337b0a1e5e0\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/klauspost/crc32\",\n\t\t\t\"Comment\": \"v1.0-2-gcb6bfca\",\n\t\t\t\"Rev\": \"cb6bfca970f6908083f26f39a79009d608efd5cd\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/manucorporat/sse\",\n\t\t\t\"Rev\": \"ee05b128a739a0fb76c7ebd3ae4810c1de808d6d\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/valyala/bytebufferpool\",\n\t\t\t\"Rev\": \"e746df99fe4a3986f4d4f79e13c1e0117ce9c2f7\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/valyala/fasthttp\",\n\t\t\t\"Comment\": \"v20160617-69-g6cd438b\",\n\t\t\t\"Rev\": \"6cd438ba896c48caa4194fb7381d91eae33b9461\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/valyala/fasthttp/fasthttputil\",\n\t\t\t\"Comment\": \"v20160617-69-g6cd438b\",\n\t\t\t\"Rev\": \"6cd438ba896c48caa4194fb7381d91eae33b9461\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"github.com/valyala/fasthttp/stackless\",\n\t\t\t\"Comment\": \"v20160617-69-g6cd438b\",\n\t\t\t\"Rev\": \"6cd438ba896c48caa4194fb7381d91eae33b9461\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"golang.org/x/net/context\",\n\t\t\t\"Rev\": \"6250b412798208e6c90b03b7c4f226de5aa299e2\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"gopkg.in/go-playground/validator.v8\",\n\t\t\t\"Comment\": \"v8.18.1\",\n\t\t\t\"Rev\": \"5f57d2222ad794d0dffb07e664ea05e2ee07d60c\"\n\t\t},\n\t\t{\n\t\t\t\"ImportPath\": \"gopkg.in/yaml.v2\",\n\t\t\t\"Rev\": \"e4d366fc3c7938e2958e662b4258c7a89e1f0e3e\"\n\t\t}\n\t]\n}\n"
  },
  {
    "path": "Godeps/Readme",
    "content": "This directory tree is generated automatically by godep.\n\nPlease do not edit.\n\nSee https://github.com/tools/godep for more information.\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 WhiteBlue\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# bilibili-sdk-go\n\nBiliBili Open API & SDK written in Go\n\n\n\n## Open API\n\nDocs: [docs](docs/api_doc.md)\n\n* api.bilibilih5.club\n* api.prprpr.me/bilibili ( support by [DIYgod](https://github.com/DIYgod))\n\nDeploy:\n\n* ```docker build -t bilibili-go```\n* ```docekr run -d -p 80:8080 bilibili-go```\n\n\n## Progress\n\n* Rank\n\t* ```SortRank``` (order by danmu/comment/hot)\n* Video\n\t* ```VideoInfo```\n\t* ```VideoLin``` (mp4/flv)\n* User\n\t* ```UserInfo```\n\t* ```UserVideos```\n* Special\n\t* ```SpecialInfo```\n\t* ```SpecialVideos```\n* Bangumi\n\t* ```BangumiList```\n\t* ```BangumiRecommend```\n* Others\n\t* ```Search```(search user/video/bangumi)\n\n\n\n## Install\n\n```\ngo get github.com/WhiteBlue/bilibili-go\n```\n\n## Usage\n\n```\nimport \"github.com/whiteblue/bilibili-go/client\"\n\nc := client.NewClient(\"APPKEY\", \"SECRET\")\nback, err := c.Bangumi.GetWeekList(\"2\")\n\nif err != nil {\n    log.Error(err)\n    return\n}\n\nlog.Info(result)\n\n```\n\n## Related Projects\n\n* [BiliBili-Html5](http://bilibilih5.club)\n\n\n\n## License\n\nMIT License\n\nCopyright (c) 2016 Castaway Consulting LLC\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n\n"
  },
  {
    "path": "client/bangumi.go",
    "content": "package client\n\nimport (\n\t\"encoding/json\"\n)\n\ntype bangumiElement struct {\n\tTitle        string `json:\"title\"`\n\tArea         string `json:\"area\"`\n\tAreaLimit    int    `json:\"arealimit\"`\n\tAttention    int    `json:\"attention\"`\n\tBangumiId    int    `json:\"bangumi_id\"`\n\tBgmCount     string `json:\"bgmcount\"`\n\tCover        string `json:\"cover\"`\n\tSquareCover  string `json:\"square_cover\"`\n\tDanmakuCount int    `json:\"danmaku_count\"`\n\tFavorites    int    `json:\"favorites\"`\n\tIsFinish     int    `json:\"is_finish\"`\n\tLastUpdate   string `json:\"lastupdate_at\"`\n\tNew          bool   `json:\"new\"`\n\tPlayCount    int    `json:\"play_count\"`\n\tSeasonId     int    `json:\"season_id\"`\n\tSpId         int    `json:\"spid\"`\n\tUrl          string `json:\"url\"`\n\tViewRank     int    `json:\"viewRank\"`\n\tWeekday      int    `json:\"weekday\"`\n}\n\ntype banner struct {\n\tTitle    string `json:\"title\"`\n\tLink     string `json:\"link\"`\n\tImg      string `json:\"img\"`\n\tSImg     string `json:\"simg\"`\n\tAid      int    `json:\"aid\"`\n\tType     string `json:\"type\"`\n\tPlatform int    `json:\"platform\"`\n\tPid      int    `json:\"pid\"`\n}\n\ntype recommendBangumiVideo struct {\n\tAid         string `json:\"aid\"`\n\tTitle       string `json:\"title\"`\n\tSubtitle    string `json:\"subtitle\"`\n\tPlay        int    `json:\"play\"`\n\tReview      int    `json:\"review\"`\n\tVideoReview int    `json:\"video_review\"`\n\tFavorites   int    `json:\"favorites\"`\n\tMid         int    `json:\"mid\"`\n\tAuthor      string `json:\"author\"`\n\tDescription string `json:\"description\"`\n\tCreate      string `json:\"create\"`\n\tPic         string `json:\"pic\"`\n\tCoins       int    `json:\"coins\"`\n\tDuration    string `json:\"duration\"`\n}\n\ntype bangumiActor struct {\n\tActor string `json:\"actor\"`\n\tRole  string `json:\"role\"`\n}\n\ntype bangumiVideo struct {\n\tAid        string `json:\"av_id\"`\n\tCoins      int    `json:\"coins\"`\n\tCover      string `json:\"cover\"`\n\tDanmaku    string `json:\"danmaku\"`\n\tIndex      string `json:\"index\"`\n\tTitle      string `json:\"index_title\"`\n\tUpdateTime string `json:\"update_time\"`\n}\n\ntype bangumiSeason struct {\n\tCover        string `json:\"cover\"`\n\tIsFinish     string `json:\"is_finish\"`\n\tSeasonId     string `json:\"season_id\"`\n\tSeasonStatus int    `json:\"season_status\"`\n\tTitle        string `json:\"title\"`\n\tTotalCount   string `json:\"total_count\"`\n}\n\ntype bangumiInfoResponse struct {\n\tActors       []bangumiActor  `json:\"actor\"`\n\tAlias        string          `json:\"alias\"`\n\tArea         string          `json:\"area\"`\n\tBangumiId    string          `json:\"bangumi_id\"`\n\tBangumiTitle string          `json:\"bangumi_title\"`\n\tBrief        string          `json:\"brief\"`\n\tCoins        string          `json:\"coins\"`\n\tCopyRight    string          `json:\"copyright\"`\n\tCover        string          `json:\"cover\"`\n\tDanmakuCount string          `json:\"danmaku_count\"`\n\tEpisodes     []bangumiVideo  `json:\"episodes\"`\n\tEvaluate     string          `json:\"evaluate\"`\n\tFavorites    string          `json:\"favorites\"`\n\tIsFinish     string          `json:\"is_finish\"`\n\tJpTitle      string          `json:\"jp_title\"`\n\tPlayCount    string          `json:\"play_count\"`\n\tPubTime      string          `json:\"pub_time\"`\n\tSeasonId     string          `json:\"season_id\"`\n\tSeasonStatus int             `json:\"season_status\"`\n\tSeasonTitle  string          `json:\"season_title\"`\n\tSeasons      []bangumiSeason `json:\"seasons\"`\n\tSquareCover  string          `json:\"squareCover\"`\n\tStaff        string          `json:\"staff\"`\n\tTitle        string          `json:\"title\"`\n\tTotalCount   string          `json:\"total_count\"`\n}\n\ntype weekBangumiResponse struct {\n\tCount string           `json:\"count\"`\n\tList  []bangumiElement `json:\"list\"`\n}\n\ntype bangumiIndexResponse struct {\n\tBanners    []banner                `json:\"banners\"`\n\tRecommends []recommendBangumiVideo `json:\"recommends\"`\n}\n\ntype BangumiService struct {\n\tBaseService\n}\n\nfunc (b *BangumiService) GetWeekList(bType string) (*weekBangumiResponse, error) {\n\tretBody, err := b.doRequest(\"http://app.bilibili.com/bangumi/timeline_v2\", map[string]string{\n\t\t\"_device\":  \"iphone\",\n\t\t\"btype\":    bType,\n\t\t\"platform\": \"ios\",\n\t\t\"type\":     \"json\",\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar ret weekBangumiResponse\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret, nil\n}\n\nfunc (b *BangumiService) GetIndex() (*bangumiIndexResponse, error) {\n\tretBody, err := b.doRequest(\"http://app.bilibili.com/api/region_ios/13.json\", map[string]string{\n\t\t\"platform\": \"ios\",\n\t\t\"device\":   \"phone\",\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ret struct {\n\t\tContent bangumiIndexResponse `json:\"result\"`\n\t}\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret.Content, nil\n}\n\nfunc (b *BangumiService) GetBangumiInfo(seasonId string) (*bangumiInfoResponse, error) {\n\tretBody, err := b.doRequest(\"http://bangumi.bilibili.com/api/season_v4\", map[string]string{\n\t\t\"platform\":  \"ios\",\n\t\t\"build\":     \"3940\",\n\t\t\"season_id\": seasonId,\n\t\t\"type\":      \"bangumi\",\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ret struct {\n\t\tContent bangumiInfoResponse `json:\"result\"`\n\t}\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret.Content, nil\n}\n"
  },
  {
    "path": "client/base.go",
    "content": "package client\n\nimport (\n\t\"encoding/json\"\n)\n\ntype BaseParam struct {\n\tAppkey string\n\tSecret string\n}\n\ntype BaseService struct {\n\tParams BaseParam\n\tClient HttpClient\n}\n\ntype apiResponse struct {\n\tCode    int    `json:\"code\"`\n\tMessage string `json:\"message\"`\n\tError   string `json:\"error\"`\n}\n\ntype ApiError struct {\n\tMsg string\n}\n\nfunc (a *ApiError) Error() string {\n\treturn a.Msg\n}\n\nfunc (b *BaseService) doRequest(url string, params map[string]string) ([]byte, error) {\n\tparams[\"appkey\"] = b.Params.Appkey\n\t//generate bilibili sign code\n\tquery, sign := EncodeSign(params, b.Params.Secret)\n\treqUrl := url + \"?\" + query + \"&sign=\" + sign\n\tretByte, err := b.Client.Get(reqUrl)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar badRet apiResponse\n\terr = json.Unmarshal(retByte, &badRet)\n\tif err != nil {\n\t\treturn nil, &ApiError{Msg: \"api encode error\"}\n\t}\n\n\tif badRet.Code == 0 {\n\t\t//api return success\n\t\treturn retByte, nil\n\t}\n\n\t//api return error\n\treturn nil, &ApiError{Msg: badRet.Message + badRet.Error}\n}\n"
  },
  {
    "path": "client/cli.go",
    "content": "package client\n\ntype BCli struct {\n\tRank    RankService\n\tBangumi BangumiService\n\tVideo   VideoService\n\tSpecial SpecialService\n\tUser    UserService\n\tOthers  OthersService\n}\n\nfunc NewClient(appkey, secret string) *BCli {\n\tparams := BaseParam{\n\t\tAppkey: appkey,\n\t\tSecret: secret,\n\t}\n\n\tclient := NewHttpClient()\n\n\tbase := BaseService{params, client}\n\treturn &BCli{\n\t\tRank:    RankService{base},\n\t\tBangumi: BangumiService{base},\n\t\tVideo:   VideoService{base},\n\t\tSpecial: SpecialService{base},\n\t\tUser:    UserService{base},\n\t\tOthers:  OthersService{base},\n\t}\n}\n"
  },
  {
    "path": "client/others.go",
    "content": "package client\n\nimport (\n\t\"encoding/json\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n)\n\ntype videoTypeInfoElement struct {\n\tTid   int    `json:\"tid\"`\n\tName  string `json:\"name\"`\n\tCount int    `json:\"count\"`\n}\n\ntype searchItem struct {\n\tTitle            string `json:\"title\"`\n\tCover            string `json:\"cover\"`\n\tUri              string `json:\"uri\"`\n\tParams           string `json:\"param\"`\n\tGoto             string `json:\"goto\"`\n\tDesc             string `json:\"desc\"`\n\n\tMovieActors      string `json:\"actors\"`\n\tMovieStaff       string `json:\"staff\"`\n\tMoviePubDate     string `json:\"screen_date\"`\n\tMovieArea        string `json:\"area\"`\n\tMovieLength      int    `json:\"length\"`\n\n\tVideoPlay        int    `json:\"play\"`\n\tVideoDanmaku     int    `json:\"danmaku\"`\n\tVideoAuthor      string `json:\"author\"`\n\tVideoDuration    string `json:\"duration\"`\n\n\tSeasonTotalCount int    `json:\"total_count\"`\n\tSeasonDesc       string `json:\"cat_desc\"`\n\n\tUserFans         int `json:\"fans\"`\n\tUserSign         string `json:\"sign\"`\n}\n\ntype searchItems struct {\n\tSeasons []searchItem `json:\"season\"`\n\tMovies  []searchItem  `json:\"movie\"`\n\tVides   []searchItem  `json:\"archive\"`\n}\n\ntype searchNavItem struct {\n\tName  string `json:\"name\"`\n\tTotal int    `json:\"total\"`\n\tPages int    `json:\"pages\"`\n\tType  int    `json:\"type\"`\n}\n\ntype searchResponse struct {\n\tPage  int             `json:\"page\"`\n\tNavs  []searchNavItem `json:\"nav\"`\n\tItems searchItems     `json:\"items\"`\n}\n\ntype searchByTypeResponse struct {\n\tAllPage int `json:\"pages\"`\n\tItems   []searchItem `json:\"items\"`\n}\n\ntype BannerElement struct {\n\tId     int `json:\"id\"`\n\tName   string `json:\"name\"`\n\tPic    string `json:\"pic\"`\n\tUrl    string `json:\"url\"`\n\tPosNum int `json:\"pos_num\"`\n}\n\ntype liveBanner struct {\n\tTitle  string `json:\"title\"`\n\tImg    string `json:\"img\"`\n\tRemark string `json:\"remark\"`\n\tLink   string `json:\"link\"`\n}\n\ntype liveElement struct {\n\tUser          struct {\n\t\t\t      Face string `json:\"face\"`\n\t\t\t      Mid  int    `json:\"mid\"`\n\t\t\t      Name string `json:\"name\"`\n\t\t      } `json:\"owner\"`\n\tCover         struct {\n\t\t\t      Src string `json:\"src\"`\n\t\t      } `json:\"cover\"`\n\tTitle         string `json:\"title\"`\n\tRoomId        int    `json:\"room_id\"`\n\tOnline        int    `json:\"online\"`\n\tArea          string `json:\"area\"`\n\tAreaId        int    `json:\"area_id\"`\n\tPlayUrl       string `json:\"playurl\"`\n\tAcceptQuality string `json:\"accept_quality\"`\n}\n\ntype liveAppIndexResponse struct {\n\tBanners    []liveBanner `json:\"banner\"`\n\tPartitions []struct {\n\t\tPartition struct {\n\t\t\t\t  Id      int    `json:\"id\"`\n\t\t\t\t  Name    string `json:\"name\"`\n\t\t\t\t  Area    string `json:\"area\"`\n\t\t\t\t  SubIcon struct {\n\t\t\t\t\t\t  Src string `json:\"src\"`\n\t\t\t\t\t  } `json:\"sub_icon\"`\n\t\t\t  } `json:\"partition\"`\n\t\tLives     []liveElement `json:\"lives\"`\n\t} `json:\"partitions\"`\n\tRecommend  struct {\n\t\t\t   Lives      []liveElement `json:\"lives\"`\n\t\t\t   BannerData []liveElement `json:\"banner_data\"`\n\t\t   } `json:\"recommend_data\"`\n}\n\ntype OthersService struct {\n\tBaseService\n}\n\n/*\n\torder:\n\t\t\"totalrank\"\n\t\t\"click\"\n\t\t\"pubdate\"\n\t\t\"dm\"\n\n\tsearchType:\n\t\t\"all\"\n*/\nfunc (o *OthersService) Search(keyword string, page, pageSize int, order string) (*searchResponse, error) {\n\t//url raw encode\n\tkeywordEncode := strings.Replace(url.QueryEscape(keyword), \"+\", \"%20\", -1)\n\tretBody, err := o.doRequest(\"http://app.bilibili.com/x/v2/search\", map[string]string{\n\t\t\"keyword\":     keywordEncode,\n\t\t\"pn\":        strconv.Itoa(page),\n\t\t\"ps\":    strconv.Itoa(pageSize),\n\t\t\"device\":      \"phone\",\n\t\t\"main_ver\":    \"v3\",\n\t\t\"order\":       order,\n\t\t\"platform\":    \"ios\",\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ret struct {\n\t\tData searchResponse `json:\"data\"`\n\t}\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret.Data, nil\n}\n\nfunc (o *OthersService) SearchByType(keyword string, page, pageSize int, searchType int) (*searchByTypeResponse, error) {\n\t//url raw encode\n\tkeywordEncode := strings.Replace(url.QueryEscape(keyword), \"+\", \"%20\", -1)\n\tretBody, err := o.doRequest(\"http://app.bilibili.com/x/v2/search/type\", map[string]string{\n\t\t\"keyword\":     keywordEncode,\n\t\t\"pn\":        strconv.Itoa(page),\n\t\t\"ps\":    strconv.Itoa(pageSize),\n\t\t\"mobi_app\":      \"iphone\",\n\t\t\"platform\":    \"ios\",\n\t\t\"device\":       \"phone\",\n\t\t\"type\": strconv.Itoa(searchType),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ret struct {\n\t\tData searchByTypeResponse `json:\"data\"`\n\t}\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret.Data, nil\n}\n\nfunc (o *OthersService) AppIndex() (*liveAppIndexResponse, error) {\n\tretBody, err := o.doRequest(\"http://live.bilibili.com/AppIndex/home\", map[string]string{\n\t\t\"device\":    \"phone\",\n\t\t\"platform\":  \"ios\",\n\t\t\"scale\":     \"2\",\n\t\t\"actionKey\": \"appkey\",\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ret struct {\n\t\tData liveAppIndexResponse `json:\"data\"`\n\t}\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret.Data, nil\n}\n\nfunc (o *OthersService) IndexBanner() ([]BannerElement, error) {\n\tretBody, err := o.doRequest(\"http://api.bilibili.com/x/web-show/res/loc\", map[string]string{\n\t\t\"jsonp\":\"jsonp\",\n\t\t\"pf\":\"0\",\n\t\t\"id\":\"23\",\n\t})\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ret struct {\n\t\tData []BannerElement `json:\"data\"`\n\t}\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn ret.Data, nil\n}\n"
  },
  {
    "path": "client/rank.go",
    "content": "package client\n\nimport (\n\t\"encoding/json\"\n\t\"strconv\"\n)\n\ntype RankService struct {\n\tBaseService\n}\n\ntype RankVideoElement struct {\n\tTitle     string `json:\"title\"`\n\tCover     string `json:\"cover\"`\n\tUri       string `json:\"uri\"`\n\tParam     string `json:\"param\"`\n\tGoto      string `json:\"goto\"`\n\tName      string `json:\"name\"`\n\tPlay      int `json:\"play\"`\n\tReply     int `json:\"reply\"`\n\tFavourite int `json:\"favourite\"`\n\tDanmaku   int `json:\"danmaku\"`\n}\n\n/*\n\torder:\n\t\t\"view\",\n\t\t\"senddate\",\n\t\t\"reply\",\n\t\t\"danmaku\",\n\t\t\"favorite\",\n*/\nfunc (r *RankService) SortRank(tid, page, pageSize int, order string) ([]RankVideoElement, error) {\n\tretBody, err := r.doRequest(\"http://app.bilibili.com/x/v2/region/show/child/list\", map[string]string{\n\t\t\"build\":    \"4040\",\n\t\t\"device\":      \"phone\",\n\t\t\"mobi_app\":      \"iphone\",\n\t\t\"platform\":      \"ios\",\n\t\t\"order\":    order,\n\t\t\"pn\":     strconv.Itoa(page),\n\t\t\"ps\": strconv.Itoa(pageSize),\n\t\t\"rid\":      strconv.Itoa(tid),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar ret struct {\n\t\tList []RankVideoElement `json:\"data\"`\n\t}\n\n\t//delete the 'num' key (mdzz)\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn ret.List, nil\n}\n"
  },
  {
    "path": "client/special.go",
    "content": "package client\n\nimport (\n\t\"encoding/json\"\n\t\"strconv\"\n)\n\ntype specialVideoElement struct {\n\tAid   int    `json:\"aid\"`\n\tCid   int    `json:\"cid\"`\n\tCover string `json:\"cover\"`\n\tTitle string `json:\"title\"`\n\tClick int    `json:\"click\"`\n\tPage  int    `json:\"page\"`\n}\n\ntype specialVideosResponse struct {\n\tCount   int                   `json:\"count\"`\n\tResults int                   `json:\"results\"`\n\tList    []specialVideoElement `json:\"list\"`\n}\n\ntype specialInfoResponse struct {\n\tSpId         int    `json:\"spid\"`\n\tTitle        string `json:\"title\"`\n\tCreateAt     string `json:\"create_at\"`\n\tUpdateAt     string `json:\"lastupdate_at\"`\n\tAlias        string `json:\"alias\"`\n\tCover        string `json:\"cover\"`\n\tIsBangumi    int    `json:\"isbangumi\"`\n\tIsBangumiEnd int    `json:\"isbangumi_end\"`\n\tBangumiDate  string `json:\"bangumi_date\"`\n\tDescription  string `json:\"description\"`\n\tView         int    `json:\"view\"`\n\tVideoView    int    `json:\"video_view\"`\n\tFavourite    int    `json:\"favourite\"`\n\tAttention    int    `json:\"attention\"`\n}\n\ntype SpecialService struct {\n\tBaseService\n}\n\nfunc (s *SpecialService) GetSpecialInfo(spid int) (*specialInfoResponse, error) {\n\tretBody, err := s.doRequest(\"http://api.bilibili.cn/sp\", map[string]string{\n\t\t\"spid\": strconv.Itoa(spid),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ret specialInfoResponse\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret, nil\n}\n\n/*\n\tisBangumi:\n\t\tthe result is \"bangumi\" or other videos\n*/\nfunc (s *SpecialService) GetSpecialVideos(spid int, isBangumi bool) (*specialVideosResponse, error) {\n\tretType := 0\n\tif isBangumi {\n\t\tretType = 1\n\t}\n\tretBody, err := s.doRequest(\"http://api.bilibili.com/spview\", map[string]string{\n\t\t\"spid\":    strconv.Itoa(spid),\n\t\t\"bangumi\": strconv.Itoa(retType),\n\t\t\"type\":    \"json\",\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ret specialVideosResponse\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret, nil\n}\n"
  },
  {
    "path": "client/user.go",
    "content": "package client\n\nimport (\n\t\"encoding/json\"\n\t\"strconv\"\n)\n\ntype userVideosResponse struct {\n\tList      []UserVideoElement              `json:\"vlist\"`\n\tTypeIndex map[string]videoTypeInfoElement `json:\"tlist\"`\n}\n\ntype UserVideoElement struct {\n\tAid         int    `json:\"aid\"`\n\tCopyright   string `json:\"copyright\"`\n\tTypeId      int    `json:\"typeid\"`\n\tTitle       string `json:\"title\"`\n\tSubtitle    string `json:\"subtitle\"`\n\tPlay        int    `json:\"play\"`\n\tReview      int    `json:\"review\"`\n\tVideoReview int    `json:\"video_review\"`\n\tFavorites   int    `json:\"favorites\"`\n\tMid         int    `json:\"mid\"`\n\tAuthor      string `json:\"author\"`\n\tDescription string `json:\"description\"`\n\tCreated     string `json:\"created\"`\n\tPic         string `json:\"pic\"`\n\tComment     int    `json:\"comment\"`\n\tLength      string `json:\"length\"`\n}\n\ntype userInfoResponse struct {\n\tMid          int     `json:\"mid\"`\n\tName         string  `json:\"name\"`\n\tSex          string  `json:\"sex\"`\n\tRank         int     `json:\"rank\"`\n\tFace         string  `json:\"face\"`\n\tCoins        float32 `json:\"coins\"`\n\tRegTime      int     `json:\"regtime\"`\n\tBirthday     string  `json:\"birthday\"`\n\tPlace        string  `json:\"place\"`\n\tDescription  string  `json:\"description\"`\n\tAttentions   []int   `json:\"attentions\"`\n\tFansNum      int     `json:\"fans\"`\n\tFriendNum    int     `json:\"friend\"`\n\tAttentionNum int     `json:\"attention\"`\n\tSign         string  `json:\"sign\"`\n}\n\ntype UserVideoResponse struct {\n}\n\ntype UserService struct {\n\tBaseService\n}\n\nfunc (u *UserService) GetUserInfo(mid int) (*userInfoResponse, error) {\n\tretBody, err := u.doRequest(\"http://api.bilibili.cn/userinfo\", map[string]string{\n\t\t\"mid\": strconv.Itoa(mid),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ret userInfoResponse\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret, nil\n}\n\nfunc (u *UserService) GetUserVideos(mid, page, pageSize int) (*userVideosResponse, error) {\n\tretBody, err := u.doRequest(\"http://space.bilibili.com/ajax/member/getSubmitVideos\", map[string]string{\n\t\t\"mid\":      strconv.Itoa(mid),\n\t\t\"page\":     strconv.Itoa(page),\n\t\t\"pagesize\": strconv.Itoa(pageSize),\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ret struct {\n\t\tData userVideosResponse `json:\"data\"`\n\t}\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret.Data, nil\n}\n"
  },
  {
    "path": "client/utils.go",
    "content": "package client\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"github.com/valyala/fasthttp\"\n\t\"time\"\n\t\"sort\"\n\t\"strings\"\n\t\"fmt\"\n\t\"sync\"\n\t\"errors\"\n)\n\nfunc EncodeSign(params map[string]string, secret string) (string, string) {\n\tqueryString := httpBuildQuery(params)\n\treturn queryString, Md5(queryString + secret)\n}\n\nfunc Md5(formal string) string {\n\th := md5.New()\n\th.Write([]byte(formal))\n\treturn hex.EncodeToString(h.Sum(nil))\n}\n\nconst (\n\tHTTP_TIMEOUT     = 2\n\tHTTP_BUFFER_SIZE = 2 * 1024\n)\n\nvar (\n\tbufPool = &sync.Pool{New: func() interface{} {\n\t\treturn make([]byte, HTTP_BUFFER_SIZE)\n\t}}\n\t//transport = http.Transport{\n\t//\tDial: func(network, addr string) (net.Conn, error) {\n\t//\t\tdeadline := time.Now().Add((HTTP_TIMEOUT + 2) * time.Second)\n\t//\t\tc, err := net.DialTimeout(network, addr, HTTP_TIMEOUT*time.Second)\n\t//\t\tif err != nil {\n\t//\t\t\treturn nil, err\n\t//\t\t}\n\t//\t\tc.SetDeadline(deadline)\n\t//\t\treturn c, nil\n\t//\t},\n\t//\tDisableKeepAlives: true,\n\t//}\n)\n\ntype HttpClient struct {\n\tclient *fasthttp.Client\n}\n\nfunc NewHttpClient() HttpClient {\n\treturn HttpClient{\n\t\tclient: &fasthttp.Client{ReadTimeout: HTTP_TIMEOUT * time.Second, WriteTimeout: HTTP_TIMEOUT * time.Second},\n\t}\n}\n\n//map to query string & sort by key\nfunc httpBuildQuery(params map[string]string) string {\n\tlist := make([]string, 0, len(params))\n\tbuffer := make([]string, 0, len(params))\n\tfor key := range params {\n\t\tlist = append(list, key)\n\t}\n\tsort.Strings(list)\n\tfor _, key := range list {\n\t\tvalue := params[key]\n\t\tbuffer = append(buffer, key)\n\t\tbuffer = append(buffer, \"=\")\n\t\tbuffer = append(buffer, value)\n\t\tbuffer = append(buffer, \"&\")\n\t}\n\tbuffer = buffer[:len(buffer) - 1]\n\treturn strings.Join(buffer, \"\")\n}\n\nfunc (b *HttpClient) Get(url string) ([]byte, error) {\n\n\tbuf, _ := bufPool.Get().([]byte)\n\tdefer bufPool.Put(buf)\n\n\tcode, body, err := b.client.Get(buf, url)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif code != 200 {\n\t\treturn nil, errors.New(fmt.Sprintf(\"server return code %d\", code))\n\t}\n\n\treturn body, nil\n}\n"
  },
  {
    "path": "client/video.go",
    "content": "package client\n\nimport (\n\t\"encoding/json\"\n\t\"strconv\"\n)\n\ntype videoElement struct {\n\tAid         string `json:\"aid\"`\n\tMid         int    `json:\"mid\"`\n\tCopyright   string `json:\"copyright\"`\n\tTypeId      int    `json:\"typeid\"`\n\tTypeName    string `json:\"typename\"`\n\tTitle       string `json:\"title\"`\n\tSubTitle    string `json:\"subtitle\"`\n\tPlay        int    `json:\"play\"`\n\tReview      int    `json:\"review\"`\n\tVideoReview int    `json:\"video_review\"`\n\tFavorites   int    `json:\"favorites\"`\n\tAuthor      string `json:\"author\"`\n\tDescription string `json:\"description\"`\n\tCreate      string `json:\"create\"`\n\tPic         string `json:\"pic\"`\n\tCredit      int    `json:\"credit\"`\n\tCoins       int    `json:\"coins\"`\n\tDuration    string `json:\"duration\"`\n\tComment     int    `json:\"comment\"`\n\tBadGePay    bool   `json:\"badgepay\"`\n}\n\ntype videoMidInfo struct {\n\tPage int    `json:\"page\"`\n\tType string `json:\"type\"`\n\tPart string `json:\"part\"`\n\tCid  int    `json:\"cid\"`\n\tVid  int    `json:\"vid\"`\n}\n\ntype videoInfoResponse struct {\n\tTid         int                     `json:\"tid\"`\n\tTypeName    string                  `json:\"typename\"`\n\tArcType     string                  `json:\"arctype\"`\n\tPlay        string                  `json:\"play\"`\n\tReview      string                  `json:\"review\"`\n\tVideoReview string                  `json:\"video_review\"`\n\tFavorites   string                  `json:\"favorites\"`\n\tTitle       string                  `json:\"title\"`\n\tDescription string                  `json:\"description\"`\n\tTag         string                  `json:\"tag\"`\n\tPic         string                  `json:\"pic\"`\n\tAuthor      string                  `json:\"author\"`\n\tMid         string                  `json:\"mid\"`\n\tAuthorFace  string                  `json:\"face\"`\n\tPages       int                     `json:\"pages\"`\n\tCreatedAt   string                  `json:\"created_at\"`\n\tCoins       string                  `json:\"coins\"`\n\tPartList    map[string]videoMidInfo `json:\"list\"`\n}\n\ntype videoDurl struct {\n\tLength    int      `json:\"length\"`\n\tSize      int      `json:\"size\"`\n\tUrl       string   `json:\"url\"`\n\tBackupUrl []string `json:\"backup_url\"`\n}\n\ntype videoPathResponse struct {\n\tresult        string      `json:\"result\"`\n\tFormat        string      `json:\"format\"`\n\tTimeLength    int         `json:\"timelength\"`\n\tAcceptFormat  string      `json:\"accept_format\"`\n\tAcceptQuality []int       `json:\"accept_quality\"`\n\tList          []videoDurl `json:\"durl\"`\n}\n\ntype VideoService struct {\n\tBaseService\n}\n\nfunc (v *VideoService) GetVideoInfo(aid int) (*videoInfoResponse, error) {\n\tretBody, err := v.doRequest(\"http://api.bilibili.com/view\", map[string]string{\n\t\t\"batch\":      \"1\",\n\t\t\"check_area\": \"1\",\n\t\t\"id\":         strconv.Itoa(aid),\n\t\t\"platform\":   \"ios\",\n\t\t\"type\":       \"json\",\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar ret videoInfoResponse\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret, nil\n}\n\n/**\nvideoType:\n\t\"flv\"\n\t\"hdmp4\"\n\t\"mp4\"\n\nquality:\n\t1,2,3\n\n*/\nfunc (v *VideoService) GetVideoPartPath(cid int, quality int) (*videoPathResponse, error) {\n\tquery, sign := EncodeSign(map[string]string{\n\t\t\"cid\":            strconv.Itoa(cid),\n\t\t\"from\":           \"miniplay\",\n\t\t\"player\":         \"1\",\n\t\t\"otype\":          \"json\",\n\t\t\"type\":           \"mp4\",\n\t\t\"quality\":        strconv.Itoa(quality),\n\t\t\"appkey\":         \"f3bb208b3d081dc8\",\n\t}, \"1c15888dc316e05a15fdd0a02ed6584f\")\n\n\turl := \"http://interface.bilibili.com/playurl?&\" + query + \"&sign=\" + sign\n\n\tretBody, err := v.Client.Get(url)\n\n\tvar ret videoPathResponse\n\n\tjson.Unmarshal(retBody, &ret)\n\n\treturn &ret, err\n}\n"
  },
  {
    "path": "conf.example.json",
    "content": "{\n  \"debug\": true,\n  \"appkey\": \"\",\n  \"secret\": \"\"\n}"
  },
  {
    "path": "conf.json",
    "content": "{\n  \"debug\": false,\n  \"appkey\": \"4ebafd7c4951b366\",\n  \"secret\": \"8cb98205e9b2ad3669aad0fce12a4c13\",\n  \"private\": false,\n  \"allow_host\": \"http://bilibilih5.club\"\n}\n"
  },
  {
    "path": "deploy.sh",
    "content": "#!/bin/bash\n\nIMAGENAME=\"bilibili-go\"\nTAGENAME=\"whiteblue/bilibili-go\"\n\nsudo -l || ( echo \"Error: scripts need run with 'sudo'\" && exit -1 )\n\n\n( cid=`sudo docker ps -a |grep $IMAGENAME | awk '{printf $1\" \"}'`\n\nif [ \"$cid\" !=  \"\" ];then\n\nsudo docker rm -f $cid && echo \"delete container $cid\"\n\nfi )\n\n( iid=`sudo docker images | grep $IMAGENAME | awk '{printf $3\" \"}'`\n\nif [ \"$iid\" !=  \"\" ];then\n\nsudo docker rmi $iid && echo \"delete image $iid\"\n\nfi )\n\n\nsudo docker build -t $TAGENAME . || ( echo \"Build image failed.....\" && exit -1 )\n\necho \"build success\"\n\nsudo docker run -d --name $IMAGENAME -p 80:8080 $TAGENAME\n\necho \"run container success\"\n\nexit 0\n"
  },
  {
    "path": "docs/api_doc.md",
    "content": "## 接口地址\n\nURL : ```http://bilibili-service.daoapp.io```\n\n基于DaoCloud免费容器\n\n\n## 接口文档\n\n* 数据格式: ```application/json```\n* 请求方式: ```post/get```\n\n\n#### 基本状态码(HTTP)约定:\n\n\t500: 服务器错误（API返回异常）\n\t404: 请求的资源不可得\n\t200: 成功\n\t400: 参数异常\n\n\n#### 错误返回格式:\n\n\t{\n    \"code\": \"PARAM_ERROR\",\n    \"message\": \"request valitdate error\"\n\t}\n\n\n#### 分类(sort)约定:\n        '1' => '动画',\n        '3' => '音乐',\n        '4' => '游戏',\n        '5' => '娱乐',\n        '11' => '电视剧'\n        '13' => '番剧',\n        '23' => '电影',\n        '36' => '科技',\n        '119' => '鬼畜',\n        '129' => '舞蹈',\n\n\n### 1. 首页内容获取\n---\n取得主要分类下的前10个热门视频\n\n* URL: ```/allrank```\n* 请求方式: GET\n* 示例:\n\t*  ```curl -X GET -H \"Content-Type: application/json\" -H \"Cache-Control: no-cache\" \"http://bilibili-service.daoapp.io/allrank\"```\n\n* 参数: 无\n\n\n成功返回:\n\n```\n[\n  {\n    \"sort_name\": \"动画\",\n    \"videos\": [\n      {\n        \"aid\": \"5697545\",\n        \"mid\": 28965086,\n        \"copyright\": \"Original\",\n        \"typeid\": 27,\n        \"typename\": \"综合\",\n        \"title\": \"【妈的智障】笑到胃疼的动画片段（第七期）片尾洗澡＆足控福利\",\n        \"subtitle\": \"\",\n        \"play\": 1398924,\n        \"review\": 3435,\n        \"video_review\": 24134,\n        \"favorites\": 40908,\n        \"author\": \"噗汪汪\",\n        \"description\": \"看你们谁还敢说我短！嗯？不知道怎么抽奖的请看上期视频。番名按照顺序分别是：银魂、超元气三姐妹、男子高中生的日常、妄想学生会、潜行吧奈亚子、濑户的花嫁、我们大家的河合庄和悠哉日常大王，片尾是银魂OAD。谢谢支持！\",\n        \"create\": \"2016-08-07 19:12\",\n        \"pic\": \"http://i2.hdslb.com/bfs/archive/7b2aced4ba0e5924b12fcf5a0ad36d8c2728b73d.jpg_320x200.jpg\",\n        \"credit\": 0,\n        \"coins\": 6264,\n        \"duration\": \"24:28\",\n        \"comment\": 24134,\n        \"badgepay\": false\n      },\n      ...\n]\n```\n\n### 2. 分类排行获取\n---\n各分类下的排行\n\n* URL: ```/sort/{tid}```\n\t* ```tid```为分类id(例如“动画”=>13)\n* 请求方式: GET\n* 示例: \t\n\t* ```curl -X GET -H \"Content-Type: application/json\" -H \"Cache-Control: no-cache\" \"http://bilibili-service.daoapp.io/sort/13\"```\n\n* 参数:\n\n\t* ```page```: 页码\n\t* ```count```: 分页容量\n\t* ```order```: 排序方式(new,hot)\n\n\n成功返回:\n\n```\n{\n  \"name\": \"番剧\",\n  \"list\": {\n    \"0\": {\n      \"aid\": \"5698105\",\n      \"mid\": 21453565,\n      \"copyright\": \"Copy\",\n      \"typeid\": 33,\n      \"typename\": \"连载动画\",\n      \"title\": \"【4月】Re：从零开始的异世界生活 19\",\n      \"subtitle\": \"\",\n      \"play\": 2493341,\n      \"review\": 42346,\n      \"video_review\": 191673,\n      \"favorites\": 2157,\n      \"author\": \"TV-TOKYO\",\n      \"description\": \"#19 白鲸攻略战\",\n      \"create\": \"2016-08-07 19:46\",\n      \"pic\": \"http://i1.hdslb.com/bfs/archive/a0656101763a68a4bcb3fe603496037c253e106d.jpg_320x200.jpg\",\n      \"credit\": 0,\n      \"coins\": 15908,\n      \"duration\": \"24:35\",\n      \"comment\": 191673,\n      \"badgepay\": false\n    },\n    \"1\":{...},\n    ...\n    }\n}\n```\n\n### 3. 视频信息获取\n---\n各分类下的排行\n\n* URL: ```/view/{aid}```\n \t* aid => av号\n* 请求方式: GET\n* 示例:\n\t* ```curl -X GET -H \"Content-Type: application/json\" -H \"Cache-Control: no-cache\" \"http://bilibili-service.daoapp.io/view/5698105\"```\n\n* 参数: 无\n\n\n成功返回:\n\n```\n{\n  \"tid\": 33,\n  \"typename\": \"连载动画\",\n  \"arctype\": \"Copy\",\n  \"play\": \"2493735\",\n  \"review\": \"42400\",\n  \"video_review\": \"191632\",\n  \"favorites\": \"2159\",\n  \"title\": \"【4月】Re：从零开始的异世界生活 19\",\n  \"description\": \"#19 白鲸攻略战\",\n  \"tag\": \"TV动画,BILIBILI正版,RE：从零开始的异世界生活,从零开始的异世界生活\",\n  \"pic\": \"http://i0.hdslb.com/bfs/archive/a0656101763a68a4bcb3fe603496037c253e106d.jpg\",\n  \"author\": \"TV-TOKYO\",\n  \"mid\": \"21453565\",\n  \"face\": \"http://i0.hdslb.com/bfs/face/69ef6861067d6ef637b7c73b77d71c3414996745.jpg\",\n  \"pages\": 1,\n  \"created_at\": \"2016-08-08 01:05\",\n  \"coins\": \"15911\",\n  \"list\": {\n    \"0\": {\n      \"page\": 1,\n      \"type\": \"vupload\",\n      \"part\": \"ReZERO_19\",\n      \"cid\": 9253164,\n      \"vid\": 0\n    }\n  }\n}\n```\n\n\n### 4. 视频地址解析\n---\nmp4/flv视频源取得，（注意某些老视频没有mp4源）\n\n* URL: ```/video/{cid}```\n* 请求方式: GET\n* 示例:\n\t* ```curl -X GET -H \"Cache-Control: no-cache\" \"http://bilibili-service.daoapp.io/video/9253164?quality=2\"```\n* 参数:\n\t* ```quality```:清晰度(1~2，根据视频有不同)\n\t* ```type```: 格式(mp4/flv)\n\n\n成功返回:\n\n```\n{\n  \"format\": \"hdmp4\",\n  \"timelength\": 1476160,\n  \"accept_format\": \"mp4,hdmp4\",\n  \"accept_quality\": [\n    2,\n    1\n  ],\n  \"durl\": [\n    {\n      \"length\": 1476160,\n      \"size\": 206950377,\n      \"url\": \"http://cn-tj1-cu.acgvideo.com/vg123/3/2e/9253164-1-hd.mp4?expires=1471021200&ssig=1NyYrtPpFZmm4zHVClIHzA&oi=2067479167&rate=0\",\n      \"backup_url\": [\n        \"http://cn-sddz2-cu.acgvideo.com/vg1/3/6d/9253164-1-hd.mp4?expires=1471021200&ssig=actJTZUGft5yN6fSQGL9Kw&oi=2067479167&rate=0\",\n        \"http://cn-sdjn-cu-v-01.acgvideo.com/vg6/e/d5/9253164-1-hd.mp4?expires=1471021200&ssig=7I1sNpH6szF5CtNP4bwfXA&oi=2067479167&rate=0\"\n      ]\n    }\n  ]\n}\n```\n\n\n### 5. 番剧更新列表\n---\n目前B站版权二次元新番\n\n* URL: ```/bangumi```\n* 请求方式: GET\n* 示例:\n\t* ```curl -X GET -H \"Cache-Control: no-cache\" \"http://bilibili-service.daoapp.io/bangumi\"```\n\n* 参数: 无\n\n\n成功返回:\n\n```\n{\n  \"0\": [\n    {\n      \"area\": \"日本\",\n      \"arealimit\": 0,\n      \"attention\": 471585,\n      \"bangumi_id\": 1070,\n      \"bgmcount\": \"24\",\n      \"brief\": null,\n      \"cover\": \"http://i1.hdslb.com/u_user/e6835e74ce9d6f63bc44d4f42dfc82e4.jpg\",\n      \"danmaku_count\": 439806,\n      \"favorites\": 471585,\n      \"is_finish\": 0,\n      \"lastupdate\": 1451152800,\n      \"lastupdate_at\": \"2015-12-27 02:00:00\",\n      \"new\": true,\n      \"play_count\": 9646746,\n      \"pub_time\": \"\",\n      \"season_id\": 2760,\n      \"spid\": 56749,\n      \"square_cover\": \"http://i0.hdslb.com/sp/1e/1e21c6a6e17f5419eb1e10fadc53e6eb.jpg\",\n      \"title\": \"终结的炽天使 第二季\",\n      \"url\": \"/bangumi/i/2760/\",\n      \"weekday\": 0\n    },\n    ...\n ],\n ...\n}\n```\n\n\n### 6. 专题信息查看\n---\n例如番剧专题\n\n* URL:\n    * ```/spinfo/{spid}```\n* 请求方式: GET\n* 示例:\n\t* ```curl -X GET -H \"Cache-Control: no-cache\" \"http://bilibili-service.daoapp.io/spinfo/56749\"```\n\n* 参数: 无\n\n\n成功返回:\n\n```\n{\n    \"alias\": \"终わりのセラフ\",\n    \"alias_spid\": 41465,\n    \"attention\": 367438,\n    \"bangumi_date\": \"2015-10-01\",\n    \"count\": 40,\n    \"cover\": \"http://i0.hdslb.com/sp/1e/1e21c6a6e17f5419eb1e10fadc53e6eb.jpg\",\n    \"create_at\": \"2014-12-12 21:19\",\n    \"description\": \"电视动画《终结的炽天使》改编自日本轻小说家镜贵也原作、漫画家山本大和作画的同名漫画。\\r\\n2014年8月28日，发表了《终结的炽天使》电视动画化的决定。\\r\\n2014年12月20日，在日本千叶县幕张展览馆开幕的“Jump Festa 2015”会场上，宣布电视动画《终结的炽天使》会被分割成两个季度播出。\\r\\n第1期的播送时间为2015年4月4日－6月20日。\\r\\n第2期则是同年的10月至12月。\",\n    \"favourite\": 172114,\n    \"isbangumi\": 1,\n    \"isbangumi_end\": 1,\n    \"lastupdate\": 1450364026,\n    \"lastupdate_at\": \"2015-12-17 22:53\",\n    \"pubdate\": 1418390386,\n    \"season\": [\n        {\n            \"default\": false,\n            \"index_cover\": \"http://i2.hdslb.com/sp/5c/5c7dbad52d522b6a5cbc8fe383ed92fe.jpg\",\n            \"last_episode\": null,\n            \"season_id\": 2052,\n            \"season_name\": \"第一季\",\n            \"video_view\": 826087\n        },\n        {\n            \"default\": false,\n            \"index_cover\": \"http://i1.hdslb.com/sp/87/87d650e53a8d50302a369365063c45a4.jpg\",\n            \"last_episode\": null,\n            \"season_id\": 2053,\n            \"season_name\": \"第二季\",\n            \"video_view\": 4314354\n        }\n    ],\n    \"season_id\": 2053,\n    \"spid\": 56749,\n    \"title\": \"终结的炽天使 第二季\",\n    \"video_view\": 28723627,\n    \"view\": 1350787\n}\n```\n\n### 7. 专题视频获取\n---\n取得专题下的所有视频\n\n* URL: ```/spvideos/{spid}```\n* 请求方式: GET\n* 示例:\n\t* ```curl -X GET -H \"Cache-Control: no-cache\" \"http://bilibili-service.daoapp.io/spvideos/56749?bangumi=0\"```\n\n* 参数:\n\t* ```bangumi```: 取得番剧视频:1，其他视频:0\n\n\n成功返回:\n\n```\n{\n    \"code\": 0,\n    \"count\": 17,\n    \"list\": [\n        {\n            \"aid\": 2330598,\n            \"cid\": 3638258,\n            \"click\": 498347,\n            \"cover\": \"http://i2.hdslb.com/video/51/512fc7fce5bb04a42fe116eb5500af20.jpg\",\n            \"from\": \"vupload\",\n            \"page\": 0,\n            \"title\": \"「终结的炽天使」OP ED专辑\"\n        },\n        {\n            \"aid\": 2425245,\n            \"cid\": 3796297,\n            \"click\": 101788,\n            \"cover\": \"http://i0.hdslb.com/video/4c/4cf6be151a858c200e4aa5ac07c2ccd1.jpg\",\n            \"from\": \"vupload\",\n            \"page\": 0,\n            \"title\": \"让我们的炽天使燃起来吧Answer is near【MAD】\"\n        },\n\t\t\t\t...\n    ],\n    \"results\": 17,\n    \"spid\": 56749\n}\n```\n\n\n### 8. 全站搜索\n---\n\n* URL: ```/search```\n* 请求方式: POST\n* 示例:\n\t* ```curl -X POST -H \"Cache-Control: no-cache\" -H \"Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW\" -F \"content=Fate\" \"http://bilibili-service.daoapp.io/search\"```\n\n* 参数:\n\t* ```content```: 搜索内容\n\t* ```page```: 页码\n\t* ```count```: 分页大小\n\n成功返回:\n\n```\n{\n  \"page\": 1,\n  \"pagesize\": 20,\n  \"pageinfo\": {\n    \"bangumi\": {\n      \"total\": 11,\n      \"numResults\": 11,\n      \"pages\": 4\n    },\n    \"movie\": {\n      \"total\": 2,\n      \"numResults\": 2,\n      \"pages\": 1\n    },\n    \"pgc\": {\n      \"total\": 2,\n      \"numResults\": 2,\n      \"pages\": 1\n    },\n    \"special\": {\n      \"total\": 27,\n      \"numResults\": 24,\n      \"pages\": 8\n    },\n    \"topic\": {\n      \"total\": 10,\n      \"numResults\": 10,\n      \"pages\": 4\n    },\n    \"tvplay\": {\n      \"total\": 0,\n      \"numResults\": 0,\n      \"pages\": 1\n    },\n    \"upuser\": {\n      \"total\": 86,\n      \"numResults\": 86,\n      \"pages\": 29\n    },\n    \"video\": {\n      \"total\": 24742,\n      \"numResults\": 999,\n      \"pages\": 50\n    }\n  },\n  \"result\": {\n    \"video\": [\n      {\n        \"aid\": \"4912937\",\n        \"mid\": 777536,\n        \"copyright\": \"\",\n        \"typeid\": 0,\n        \"typename\": \"综合\",\n        \"title\": \"【灵魂配音】10分钟演完fate stay night UBW\",\n        \"subtitle\": \"\",\n        \"play\": 1121120,\n        \"review\": 6922,\n        \"video_review\": 18656,\n        \"favorites\": 32058,\n        \"author\": \"LexBurner\",\n        \"description\": \"自制 试水作，感谢新月冰冰配以及小鹤儿的帮忙，希望以后参与的人能越来越多，做的越来越好玩，这次还不是很到位，以后继续努力啦\\r\\nlex的零食铺：http://lexzhils.taobao.com\\r\\nlex的新浪微博：http://weibo.com/lexburner\\r\\n新月冰冰视频空间：http://space.bilibili.com/3295/#!/index\\r\\n小鹤儿视频空间：http://space.bilibili.com/6719190/#!/index\",\n        \"create\": \"\",\n        \"pic\": \"http://i0.hdslb.com/bfs/archive/7edf866255ae2d9a8f31c176c0873769d6451243.jpg_320x200.jpg\",\n        \"credit\": 0,\n        \"coins\": 0,\n        \"duration\": \"10:33\",\n        \"comment\": 0,\n        \"badgepay\": false\n      },\n      ...\n      ]\n  }   \n}\n```\n\n### 9. 用户信息\n---\n* URL: ```/user/{mid}```\n* 请求方式: GET\n* 示例:\n\t* ```curl -X GET -H \"Cache-Control: no-cache\" \"http://bilibili-service.daoapp.io/user/116683\"```\n\n* 参数: 无\n\n成功返回:\n\n```\n{\n  \"mid\": 116683,\n  \"name\": \"=咬人猫=\",\n  \"sex\": \"女\",\n  \"rank\": 10000,\n  \"face\": \"http://i1.hdslb.com/bfs/face/8fad84a4470f3d894d8f0dc95555ab8f2cb10a83.jpg\",\n  \"coins\": 63211.8,\n  \"regtime\": 1301718879,\n  \"birthday\": \"0000-00-00\",\n  \"place\": \"\",\n  \"description\": \"bilibili 知名舞见\",\n  \"attentions\": [\n    179628,\n    271126,\n    622863,\n    5055,\n    433715,\n    6870383,\n    4350178,\n    8084905\n  ],\n  \"fans\": 627933,\n  \"friend\": 8,\n  \"attention\": 8,\n  \"sign\": \"面瘫女仆酱~小粗腿~事业线什么的！！吐槽你就输了！喵~\"\n}\n```\n\n### 10. 用户视频\n---\n* URL: ```/uservideos/{mid}```\n* 请求方式: GET\n* 示例:\n\t* ```curl -X GET -H \"Cache-Control: no-cache\" \"http://bilibili-service.daoapp.io/uservideos/116683\"```\n\n* 参数:\n\t* ```page```: 页码\n\t* ```count```: 分页容量\n\n\n成功返回:\n\n```\n{\n  \"vlist\": [\n    {\n      \"aid\": 5682645,\n      \"copyright\": \"Original\",\n      \"typeid\": 154,\n      \"title\": \"【咬人猫】落花情❤ o(*≧▽≦)ツ\",\n      \"subtitle\": \"\",\n      \"play\": 752435,\n      \"review\": 6402,\n      \"video_review\": 9549,\n      \"favorites\": 25734,\n      \"mid\": 116683,\n      \"author\": \"=咬人猫=\",\n      \"description\": \"也许是有史以来最忐忑的一次投稿，这次尝试的风格对我来说挑战很大，完全没有这类舞蹈的基础，所以当时这支舞挖坑填了一段时间因为实在是能力有限，被我搁置了很久～后来因为确实是太喜欢这种感觉的舞蹈了，又重新鼓起劲去学，也尝试了不同的服装版本录制了很多次，非常想完成这一支舞，虽然还有很多地方不够好，也希望大家多多包涵，谢谢大家的支持和等待～\\n服装：七秀萝莉定国套\\n舞蹈歌曲：七朵组合（一代）的作品《落花情》。\",\n      \"created\": \"2016-08-06 20:20:45\",\n      \"pic\": \"http://i0.hdslb.com/bfs/archive/a96729ac9e0a14544d1fcdb1471d23cf7ac1e61b.jpg\",\n      \"comment\": 9549,\n      \"length\": \"03:46\"\n    },\n    ...\n   ]\n}\n```\n\n\n\n### 11. 新番推荐\n---\n\n* URL: ```/bangumiindex```\n* 请求方式: GET\n* 示例:\n\t* ```curl -X GET -H \"Cache-Control: no-cache\" \"http://bilibili-service.daoapp.io/bangumiindex\"```\n\n参数: 无\n\n\n成功返回:\n\n```\n{\n  \"banners\": [\n    {\n      \"title\": \"美术社大有问题\",\n      \"link\": \"http://www.bilibili.com/bangumi/i/5043/\",\n      \"img\": \"http://i0.hdslb.com/bfs/archive/c69c196266bfe6fb52e05d5752f4687e501d83e8.jpg\",\n      \"simg\": \"\",\n      \"aid\": 0,\n      \"type\": \"link\",\n      \"platform\": 0,\n      \"pid\": 0\n    },\n    {\n      \"title\": \"魔法战争\",\n      \"link\": \"http://www.bilibili.com/bangumi/i/4367/\",\n      \"img\": \"http://i0.hdslb.com/bfs/archive/3324a9fc99275ef22d0364bd3221e009020d9567.jpg\",\n      \"simg\": \"\",\n      \"aid\": 0,\n      \"type\": \"link\",\n      \"platform\": 0,\n      \"pid\": 0\n    },\n    {\n      \"title\": \"月歌\",\n      \"link\": \"http://bangumi.bilibili.com/anime/5038\",\n      \"img\": \"http://i0.hdslb.com/bfs/archive/dfbc9098218c7deffed53dd9c14619d88ac8f180.jpg\",\n      \"simg\": \"\",\n      \"aid\": 0,\n      \"type\": \"link\",\n      \"platform\": 0,\n      \"pid\": 0\n    },\n    {\n      \"title\": \"灵能百分百\",\n      \"link\": \"http://bangumi.bilibili.com/anime/5058\",\n      \"img\": \"http://i0.hdslb.com/bfs/archive/35e322a660aa83ada2a6ae94923c27510f40fd26.jpg\",\n      \"simg\": \"\",\n      \"aid\": 0,\n      \"type\": \"link\",\n      \"platform\": 0,\n      \"pid\": 0\n    }\n  ],\n  \"recommends\": [\n    {\n      \"aid\": \"5753187\",\n      \"title\": \"【蒼氏甜品坊】初恋怪兽「…这是我的广播、要怎么办呢？」\",\n      \"subtitle\": \"\",\n      \"play\": 631,\n      \"review\": 0,\n      \"video_review\": 82,\n      \"favorites\": 139,\n      \"mid\": 628114,\n      \"author\": \"祈妹\",\n      \"description\": \"TV动画初恋怪兽的应援广播，配信日为每周五，主持人为苍井翔太。\\n一个与动画内容相比在污和hentai的层面毫不逊色的广播节目。\\n这是甜品坊第一次做广播，由于人手、经验和水平的不足不能保证翻译完全准确，有错误的地方欢迎指正~\",\n      \"create\": \"2016-08-10 22:24\",\n      \"pic\": \"http://i0.hdslb.com/bfs/archive/8770dd5682edb0170b5d3bfe08b8763aff9f7e4d.jpg_320x200.jpg\",\n      \"coins\": 40,\n      \"duration\": \"128:36\"\n    },\n    ...\n  ]\n}\n```\n"
  },
  {
    "path": "main.go",
    "content": "package main\n\nimport (\n\t\"github.com/go-playground/log\"\n\t\"github.com/whiteblue/bilibili-go/service\"\n)\n\nfunc main() {\n\tapp, err := service.NewApplication(\"conf.json\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tapp.Router.Run(\":8080\")\n}\n"
  },
  {
    "path": "run.sh",
    "content": "#!/bin/sh\n\n\ngodep go build && ./bilibili-go\n"
  },
  {
    "path": "service/application.go",
    "content": "package service\n\nimport (\n\t\"github.com/gin-gonic/contrib/gzip\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/go-playground/log\"\n\t\"github.com/go-playground/log/handlers/console\"\n\t\"github.com/whiteblue/bilibili-go/client\"\n\t\"time\"\n)\n\nconst (\n\tINDEX_CACHE = \"index\"\n\tALL_RANK_CACHE = \"all_rank\"\n\tBANGUMI_CACHE = \"bangumi\"\n\tBANGUMI_LIST_CACHE = \"bangumi_list\"\n\tSORT_TOP_CACHE = \"sort-\"\n\tLIVE_INDEX_CACHE = \"live_index\"\n\tINDEX_BANNER_CACHE = \"index_banner\"\n)\n\nvar (\n\tProdLevels = []log.Level{\n\t\tlog.InfoLevel,\n\t\tlog.NoticeLevel,\n\t\tlog.WarnLevel,\n\t\tlog.ErrorLevel,\n\t\tlog.PanicLevel,\n\t\tlog.AlertLevel,\n\t\tlog.FatalLevel,\n\t}\n)\n\ntype BiliBiliApplication struct {\n\tRouter *gin.Engine\n\tCorn   *CornService\n\tConf   *Config\n\tClient *client.BCli\n\tCache  *CacheManager\n}\n\nfunc NewApplication(configFile string) (*BiliBiliApplication, error) {\n\tconf, err := ReadConfigFromFile(configFile)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcLog := console.New()\n\n\tif conf.Debug {\n\t\tlog.RegisterHandler(cLog, log.AllLevels...)\n\t\tgin.SetMode(gin.DebugMode)\n\t} else {\n\t\tlog.RegisterHandler(cLog, ProdLevels...)\n\t\tgin.SetMode(gin.ReleaseMode)\n\t}\n\n\tlog.Info(\"conform config file\")\n\n\tr := gin.New()\n\n\t//use gzip\n\tr.Use(gin.Recovery())\n\tr.Use(gzip.Gzip(gzip.BestCompression))\n\n\t//corn service\n\tcorn := NewCornService()\n\n\t//bilibili client\n\tcli := client.NewClient(conf.Appkey, conf.Secret)\n\n\tcache := NewCacheManager()\n\n\tapp := &BiliBiliApplication{Router: r, Corn: corn, Conf: conf, Client: cli, Cache: cache}\n\n\tConformRoute(app)\n\n\tlog.Info(\"conform route\")\n\n\tconformTask(app)\n\tcorn.Start()\n\n\tlog.Info(\"conform task\")\n\n\tlog.Info(\"init complete, start listen...\")\n\n\treturn app, nil\n}\n\nfunc conformTask(app *BiliBiliApplication) {\n\tapp.Corn.RegisterTask(&IndexInfoTask{CornTask: CornTask{Name: \"index_info\", Duration: 2 * time.Hour}, app: app})\n\tapp.Corn.RegisterTask(&BangumiInfoTask{CornTask: CornTask{Name: \"bangumi_info\", Duration: 6 * time.Hour}, app: app})\n\tapp.Corn.RegisterTask(&BangumiListTask{CornTask: CornTask{Name: \"bangumi_list\", Duration: 6 * time.Hour}, app: app})\n\tapp.Corn.RegisterTask(&TopRankTask{CornTask: CornTask{Name: \"top_rank\", Duration: 2 * time.Hour}, app: app})\n\tapp.Corn.RegisterTask(&LiveIndexTask{CornTask: CornTask{Name: \"alive_index\", Duration: 2 * time.Hour}, app: app})\n\tapp.Corn.RegisterTask(&BannerTask{CornTask: CornTask{Name: \"index_banner\", Duration: 6 * time.Hour}, app: app})\n}\n"
  },
  {
    "path": "service/cache.go",
    "content": "package service\n\nimport \"sync\"\n\ntype CacheManager struct {\n\tcacheMap map[string]interface{}\n\tlock     *sync.RWMutex\n}\n\nfunc (c *CacheManager) GetCache(key string) interface{} {\n\tc.lock.RLock()\n\tdefer c.lock.RUnlock()\n\telem, ok := c.cacheMap[key]\n\tif !ok {\n\t\treturn nil\n\t}\n\treturn elem\n}\n\nfunc (c *CacheManager) SetCache(key string, value interface{}) {\n\tc.lock.Lock()\n\tdefer c.lock.Unlock()\n\tc.cacheMap[key] = value\n}\n\nfunc NewCacheManager() *CacheManager {\n\treturn &CacheManager{cacheMap: make(map[string]interface{}), lock: &sync.RWMutex{}}\n}\n"
  },
  {
    "path": "service/config.go",
    "content": "package service\n\nimport (\n\t\"encoding/json\"\n\t\"io/ioutil\"\n\t\"os\"\n)\n\ntype Config struct {\n\tDebug     bool   `json:\"debug\"`\n\tAppkey    string `json:\"appkey\"`\n\tSecret    string `json:\"secret\"`\n\tAllowHost string `json:\"allow_host\"`\n\tIsPrivate bool   `json:\"private\"`\n}\n\nfunc ReadConfigFromFile(filename string) (*Config, error) {\n\tif _, err := os.Stat(filename); os.IsNotExist(err) {\n\t\treturn nil, err\n\t}\n\tbytes, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar conf Config\n\terr = json.Unmarshal(bytes, &conf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &conf, nil\n}\n"
  },
  {
    "path": "service/corn.go",
    "content": "package service\n\nimport (\n\t\"github.com/anacrolix/sync\"\n\t\"github.com/go-playground/log\"\n\t\"time\"\n)\n\ntype CornTaskImpl interface {\n\tRun() error\n\tSuccess()\n\tFailure(error)\n\tGetName() string\n\tGetDuration() time.Duration\n\tGetLastRun() time.Time\n\tSyncLastRunTime()\n}\n\ntype CornTask struct {\n\tName     string\n\tDuration time.Duration\n\tLastRun  time.Time\n}\n\nfunc (t *CornTask) Run() error {\n\treturn nil\n}\n\nfunc (t *CornTask) Success() {\n\n}\n\nfunc (t *CornTask) Failure(err error) {\n\n}\n\nfunc (t *CornTask) GetName() string {\n\treturn t.Name\n}\n\nfunc (t *CornTask) GetDuration() time.Duration {\n\treturn t.Duration\n}\n\nfunc (t *CornTask) GetLastRun() time.Time {\n\treturn t.LastRun\n}\n\nfunc (t *CornTask) SyncLastRunTime() {\n\tt.LastRun = time.Now()\n}\n\n//execute task\nfunc exec(f CornTaskImpl) {\n\tlog.Info(\"invoke task, taskname: \", f.GetName())\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tlog.Error(r)\n\t\t}\n\t}()\n\n\tif err := f.Run(); err != nil {\n\t\tf.Failure(err)\n\t} else {\n\t\tf.Success()\n\t}\n\tlog.Info(\"run task end, taskname: \", f.GetName())\n}\n\ntype CornService struct {\n\tticker *time.Ticker\n\ttasks  []CornTaskImpl\n\tlock   sync.Mutex\n\tdone   chan struct{}\n}\n\nfunc (c *CornService) RegisterTask(task CornTaskImpl) {\n\ttask.SyncLastRunTime()\n\texec(task)\n\tc.tasks = append(c.tasks, task)\n}\n\nfunc (c *CornService) syncTaskList(nowTime time.Time) {\n\tfor _, task := range c.tasks {\n\t\t//Unix timestamp => duration\n\t\tbetween := time.Duration(nowTime.Unix()-task.GetLastRun().Unix()) * time.Second\n\t\tif between >= task.GetDuration() {\n\t\t\ttask.SyncLastRunTime()\n\t\t\texec(task)\n\t\t}\n\t}\n}\n\nfunc (c *CornService) loop() {\n\tfor {\n\t\tselect {\n\t\tcase <-c.done:\n\t\t\tlog.Info(\"corn loop stopped....\")\n\t\t\treturn\n\t\tcase nowTime := <-c.ticker.C:\n\t\t\tgo c.syncTaskList(nowTime)\n\t\t}\n\t}\n}\n\nfunc (c *CornService) Start() {\n\tgo c.loop()\n}\n\nfunc (c *CornService) Stop() {\n\tc.ticker.Stop()\n\tclose(c.done)\n}\n\nfunc NewCornService() *CornService {\n\treturn &CornService{\n\t\tticker: time.NewTicker(time.Minute),\n\t\ttasks:  []CornTaskImpl{},\n\t\tlock:   sync.Mutex{},\n\t\tdone:   make(chan struct{}, 1),\n\t}\n}\n"
  },
  {
    "path": "service/corn_tasks.go",
    "content": "package service\n\nimport (\n\t\"strconv\"\n)\n\nvar (\n\t_INDEX_SORTS = []int{\n\t\t24, 33, 31, 20, 17, 36, 119,\n\t}\n)\n\ntype SortRankInfo struct {\n\tSortId int        `json:\"sort_id\"`\n\tVideos []interface{} `json:\"videos\"`\n}\n\ntype IndexInfoTask struct {\n\tCornTask\n\tapp *BiliBiliApplication\n}\n\nfunc (i *IndexInfoTask) Run() error {\n\tretInfo := make([]SortRankInfo, 0, len(_INDEX_SORTS))\n\tfor _, sortId := range _INDEX_SORTS {\n\t\tback, err := i.app.Client.Rank.SortRank(sortId, 1, 10, \"view\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvideos := make([]interface{}, 0, len(back))\n\n\t\tfor _, v := range (back) {\n\t\t\tvideos = append(videos, v)\n\t\t}\n\n\t\tsortRank := SortRankInfo{SortId: sortId, Videos: videos}\n\n\t\tretInfo = append(retInfo, sortRank)\n\n\t\tsortCacheName := SORT_TOP_CACHE + strconv.Itoa(sortId)\n\n\t\ti.app.Cache.SetCache(sortCacheName, sortRank)\n\n\t}\n\ti.app.Cache.SetCache(INDEX_CACHE, retInfo)\n\treturn nil\n}\n\ntype BangumiInfoTask struct {\n\tCornTask\n\tapp *BiliBiliApplication\n}\n\nfunc (i *BangumiInfoTask) Run() error {\n\tret, err := i.app.Client.Bangumi.GetIndex()\n\tif err != nil {\n\t\treturn err\n\t}\n\ti.app.Cache.SetCache(BANGUMI_CACHE, ret)\n\treturn nil\n}\n\ntype BangumiListTask struct {\n\tCornTask\n\tapp *BiliBiliApplication\n}\n\nfunc (i *BangumiListTask) Run() error {\n\tret, err := i.app.Client.Bangumi.GetWeekList(\"2\")\n\tif err != nil {\n\t\treturn err\n\t}\n\ti.app.Cache.SetCache(BANGUMI_LIST_CACHE, ret)\n\treturn nil\n}\n\ntype TopRankTask struct {\n\tCornTask\n\tapp *BiliBiliApplication\n}\n\nfunc (i *TopRankTask) Run() error {\n\tret, err := i.app.Client.Rank.SortRank(0, 1, 8, \"hot\")\n\tif err != nil {\n\t\treturn err\n\t}\n\ti.app.Cache.SetCache(ALL_RANK_CACHE, ret)\n\treturn nil\n}\n\ntype LiveIndexTask struct {\n\tCornTask\n\tapp *BiliBiliApplication\n}\n\nfunc (i *LiveIndexTask) Run() error {\n\tret, err := i.app.Client.Others.AppIndex()\n\tif err != nil {\n\t\treturn err\n\t}\n\ti.app.Cache.SetCache(LIVE_INDEX_CACHE, ret)\n\treturn nil\n}\n\ntype BannerTask struct {\n\tCornTask\n\tapp *BiliBiliApplication\n}\n\nfunc (b *BannerTask) Run() error {\n\tret, err := b.app.Client.Others.IndexBanner()\n\tif err != nil {\n\t\treturn err\n\t}\n\tb.app.Cache.SetCache(INDEX_BANNER_CACHE, ret)\n\treturn nil\n}\n"
  },
  {
    "path": "service/router.go",
    "content": "package service\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc MakeFailedJsonMap(code string, message string) map[string]string {\n\treturn map[string]string{\n\t\t\"code\":    code,\n\t\t\"message\": message,\n\t}\n}\n\nfunc ConformRoute(app *BiliBiliApplication) {\n\tallowOrigin := \"*\"\n\n\tif app.Conf.IsPrivate {\n\t\tallowOrigin = app.Conf.AllowHost\n\t}\n\n\tapp.Router.Use(func(c *gin.Context) {\n\t\tc.Header(\"Access-Control-Allow-Origin\", allowOrigin)\n\t\tc.Header(\"Access-Control-Allow-Headers\", \"Content-Type\")\n\t\tc.Header(\"Access-Control-Max-Age\", \"7200\")\n\t})\n\n\tapp.Router.GET(\"/\", func(c *gin.Context) {\n\t\tc.JSON(200, gin.H{\"message\": \"BiliBili-Service 2.1\"})\n\t})\n\n\tapp.Router.GET(\"/allrank\", func(c *gin.Context) {\n\t\tback := app.Cache.GetCache(INDEX_CACHE)\n\t\tc.JSON(200, back)\n\t})\n\n\tapp.Router.GET(\"/toprank\", func(c *gin.Context) {\n\t\tback := app.Cache.GetCache(ALL_RANK_CACHE)\n\t\tc.JSON(200, back)\n\t})\n\n\tapp.Router.GET(\"/view/:aid\", func(c *gin.Context) {\n\t\taid := c.Param(\"aid\")\n\t\taidNum, err := strconv.Atoi(aid)\n\t\tif err != nil {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM ERROR\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\tlist, err := app.Client.Video.GetVideoInfo(aidNum)\n\t\tif err != nil {\n\t\t\tc.JSON(404, MakeFailedJsonMap(\"VIDEO_NOT_FOUND\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\tc.JSON(200, list)\n\t})\n\n\tapp.Router.GET(\"/video/:cid\", func(c *gin.Context) {\n\t\tcid := c.Param(\"cid\")\n\t\tquality := c.DefaultQuery(\"quality\", \"1\")\n\t\tcidNum, err := strconv.Atoi(cid)\n\t\tif err != nil {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM ERROR\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\tqualityNum, err := strconv.Atoi(quality)\n\t\tif err != nil {\n\t\t\tqualityNum = 1\n\t\t}\n\t\tlist, err := app.Client.Video.GetVideoPartPath(cidNum, qualityNum)\n\t\tif err != nil {\n\t\t\tc.JSON(404, MakeFailedJsonMap(\"VIDEO_NOT_FOUND\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\tc.JSON(200, list)\n\t})\n\n\tapp.Router.GET(\"/user/:mid\", func(c *gin.Context) {\n\t\tmid := c.Param(\"mid\")\n\t\tmidNum, err := strconv.Atoi(mid)\n\t\tif err != nil {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM ERROR\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\tlist, err := app.Client.User.GetUserInfo(midNum)\n\t\tif err != nil {\n\t\t\tc.JSON(404, MakeFailedJsonMap(\"USER_NOT_FOUND\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\tc.JSON(200, list)\n\t})\n\n\tapp.Router.GET(\"/uservideos/:mid\", func(c *gin.Context) {\n\t\tmid := c.Param(\"mid\")\n\t\tpage := c.DefaultQuery(\"page\", \"1\")\n\t\tpageSize := c.DefaultQuery(\"page_size\", \"20\")\n\n\t\tmidNum, err := strconv.Atoi(mid)\n\t\tpageNum, err := strconv.Atoi(page)\n\t\tpageSizeNum, err := strconv.Atoi(pageSize)\n\n\t\tif err != nil {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM ERROR\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\tlist, err := app.Client.User.GetUserVideos(midNum, pageNum, pageSizeNum)\n\t\tif err != nil {\n\t\t\tc.JSON(404, MakeFailedJsonMap(\"USER_NOT_FOUND\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\tc.JSON(200, list)\n\t})\n\n\tapp.Router.GET(\"/search\", func(c *gin.Context) {\n\t\tcontent := c.Query(\"content\")\n\t\tpage := c.DefaultQuery(\"page\", \"1\")\n\t\tpageSize := c.DefaultQuery(\"page_size\", \"20\")\n\t\torder := c.DefaultQuery(\"order\", \"totalrank\")\n\n\t\tvar err error\n\t\tpageNum, err := strconv.Atoi(page)\n\t\tpageSizeNum, err := strconv.Atoi(pageSize)\n\n\t\tif err != nil {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM_ERROR\", \"\"))\n\t\t\treturn\n\t\t}\n\n\t\tif strings.TrimSpace(content) == \"\" {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM 'content' is '' or not set\", \"\"))\n\t\t\treturn\n\t\t}\n\n\t\tlist, err := app.Client.Others.Search(content, pageNum, pageSizeNum, order)\n\t\tif err != nil {\n\t\t\tc.JSON(500, MakeFailedJsonMap(\"API_RETURN_ERROR\", err.Error()))\n\t\t\treturn\n\t\t}\n\n\t\tc.JSON(200, list)\n\t})\n\n\t//type :\n\t//\tbangumi => 1\n\t//\tuser =>2\n\t//\tmovie=>3\n\t//\tsp=>4\n\n\tapp.Router.GET(\"/searchbytype\", func(c *gin.Context) {\n\t\tcontent := c.Query(\"content\")\n\t\tpage := c.DefaultQuery(\"page\", \"1\")\n\t\tpageSize := c.DefaultQuery(\"page_size\", \"20\")\n\t\tsearchType := c.Query(\"type\")\n\n\t\tvar err error\n\t\tpageNum, err := strconv.Atoi(page)\n\t\tpageSizeNum, err := strconv.Atoi(pageSize)\n\n\t\tif err != nil {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM_ERROR\", \"\"))\n\t\t\treturn\n\t\t}\n\n\t\tif strings.TrimSpace(content) == \"\" {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM 'content' is '' or not set\", \"\"))\n\t\t\treturn\n\t\t}\n\n\t\tif strings.TrimSpace(searchType) == \"\" {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM 'type' is '' or not set\", \"\"))\n\t\t\treturn\n\t\t}\n\n\t\ttypeInt := 1\n\t\tswitch searchType {\n\t\tcase \"user\":typeInt = 2\n\t\tcase \"movie\":typeInt = 3\n\t\tcase \"sp\":typeInt = 4\n\t\t}\n\n\t\tlist, err := app.Client.Others.SearchByType(content, pageNum, pageSizeNum, typeInt)\n\t\tif err != nil {\n\t\t\tc.JSON(500, MakeFailedJsonMap(\"API_RETURN_ERROR\", err.Error()))\n\t\t\treturn\n\t\t}\n\n\t\tc.JSON(200, list)\n\t})\n\n\tapp.Router.GET(\"/top/:tid\", func(c *gin.Context) {\n\t\ttid := c.Param(\"tid\")\n\t\tvar err error\n\t\ttidNum, err := strconv.Atoi(tid)\n\t\tif err != nil {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM_ERROR\", err.Error()))\n\t\t\treturn\n\t\t}\n\n\t\tcacheName := SORT_TOP_CACHE + strconv.Itoa(tidNum)\n\t\ttarget := app.Cache.GetCache(cacheName)\n\n\t\tif target == nil {\n\t\t\tc.JSON(404, MakeFailedJsonMap(\"SORT_NOT_FOUND\", \"\"))\n\t\t\treturn\n\t\t}\n\n\t\tc.JSON(200, target)\n\t})\n\n\tapp.Router.GET(\"/sort/:tid\", func(c *gin.Context) {\n\t\tpage := c.DefaultQuery(\"page\", \"1\")\n\t\tpageSize := c.DefaultQuery(\"count\", \"20\")\n\t\ttid := c.Param(\"tid\")\n\t\torder := c.DefaultQuery(\"order\", \"hot\")\n\n\t\tvar err error\n\t\ttidNum, err := strconv.Atoi(tid)\n\t\tpageNum, err := strconv.Atoi(page)\n\t\tpageSizeNum, err := strconv.Atoi(pageSize)\n\n\t\tif err != nil {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM_ERROR\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\tlist, err := app.Client.Rank.SortRank(tidNum, pageNum, pageSizeNum, order)\n\t\tif err != nil {\n\t\t\tc.JSON(500, MakeFailedJsonMap(\"API_RETURN_ERROR\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\tc.JSON(200, list)\n\t})\n\n\tapp.Router.GET(\"/spinfo/:spid\", func(c *gin.Context) {\n\t\tspid := c.Param(\"spid\")\n\t\tspidNum, err := strconv.Atoi(spid)\n\n\t\tif err != nil {\n\t\t\tc.JSON(400, MakeFailedJsonMap(\"PARAM_ERROR\", \"\"))\n\t\t}\n\n\t\tlist, err := app.Client.Special.GetSpecialInfo(spidNum)\n\t\tif err != nil {\n\t\t\tc.JSON(500, MakeFailedJsonMap(\"API_RETURN_ERROR\", err.Error()))\n\t\t\treturn\n\t\t}\n\t\tc.JSON(200, list)\n\t})\n\n\tapp.Router.GET(\"/bangumi\", func(c *gin.Context) {\n\t\tback := app.Cache.GetCache(BANGUMI_LIST_CACHE)\n\n\t\tc.JSON(200, back)\n\t})\n\n\tapp.Router.GET(\"/bangumiinfo/:seasonid\", func(c *gin.Context) {\n\t\tseasonId := c.Param(\"seasonid\")\n\n\t\tback, err := app.Client.Bangumi.GetBangumiInfo(seasonId)\n\n\t\tif err != nil {\n\t\t\tc.JSON(500, MakeFailedJsonMap(\"API_RETURN_ERROR\", err.Error()))\n\t\t\treturn\n\t\t}\n\n\t\tc.JSON(200, back)\n\t})\n\n\tapp.Router.GET(\"/bangumiindex\", func(c *gin.Context) {\n\t\tback := app.Cache.GetCache(BANGUMI_CACHE)\n\n\t\tc.JSON(200, back)\n\t})\n\n\tapp.Router.GET(\"/liveindex\", func(c *gin.Context) {\n\t\tback := app.Cache.GetCache(LIVE_INDEX_CACHE)\n\n\t\tc.JSON(200, back)\n\t})\n\n\tapp.Router.GET(\"/banner\", func(c *gin.Context) {\n\t\tback := app.Cache.GetCache(INDEX_BANNER_CACHE)\n\n\t\tc.JSON(200, back)\n\t})\n}\n"
  },
  {
    "path": "test/api_test.go",
    "content": "package test\n\nimport (\n\t\"encoding/json\"\n\t\"github.com/whiteblue/bilibili-go/client\"\n\t\"os\"\n\t\"strconv\"\n\t\"testing\"\n)\n\nconst (\n\tAPPKEY = \"4ebafd7c4951b366\"\n\tSECRET = \"8cb98205e9b2ad3669aad0fce12a4c13\"\n)\n\nfunc TestApiSortRank(t *testing.T) {\n\tc := client.NewClient(APPKEY, SECRET)\n\tback, err := c.Rank.SortRank(1, 1, 10, \"hot\")\n\tif err != nil {\n\t\tt.Error(err.Error())\n\t\tt.Fail()\n\t} else {\n\t\tlength := len(back.List)\n\t\tif length == 0 {\n\t\t\tt.Error(\"return length is 0\")\n\t\t}\n\t\tfor i := 0; i < length; i++ {\n\t\t\tindex := strconv.Itoa(i)\n\t\t\tif back.List[index].Title == \"\" {\n\t\t\t\tt.Error(\"api return nil\")\n\t\t\t}\n\t\t\tt.Log(back.List[index])\n\t\t}\n\t}\n}\n\nfunc TestWeekBangumi(t *testing.T) {\n\tc := client.NewClient(APPKEY, SECRET)\n\tback, err := c.Bangumi.GetWeekList(\"2\")\n\tif err != nil {\n\t\tt.Error(err.Error())\n\t\tt.Failed()\n\t} else {\n\t\tif len(back.List) == 0 {\n\t\t\tt.Error(\"return length is 0\")\n\t\t}\n\t\tfor _, element := range back.List {\n\t\t\tif element.Title == \"\" {\n\t\t\t\tt.Error(\"api return nil\")\n\t\t\t}\n\t\t\tt.Log(element)\n\t\t}\n\t}\n}\n\nfunc TestBangumiIndex(t *testing.T) {\n\tc := client.NewClient(APPKEY, SECRET)\n\tback, err := c.Bangumi.GetIndex()\n\tif err != nil {\n\t\tt.Error(err.Error())\n\t\tt.Failed()\n\t} else {\n\t\tif len(back.Banners) == 0 {\n\t\t\tt.Error(\"return banner length is 0\")\n\t\t}\n\t\tfor _, banner := range back.Banners {\n\t\t\tif banner.Title == \"\" {\n\t\t\t\tt.Error(\"api return nil\")\n\t\t\t}\n\t\t\tt.Log(banner.Title)\n\t\t}\n\t\tif len(back.Recommends) == 0 {\n\t\t\tt.Error(\"return recommends length is 0\")\n\t\t}\n\t\tfor _, ele := range back.Recommends {\n\t\t\tif ele.Title == \"\" {\n\t\t\t\tt.Error(\"api return nil\")\n\t\t\t}\n\t\t\tt.Log(ele)\n\t\t}\n\n\t\tf, err := os.Create(\"test.json\")\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tdefer f.Close()\n\n\t\tjsonByte, _ := json.Marshal(back)\n\n\t\tf.WriteString(string(jsonByte))\n\n\t}\n}\n\nfunc TestVideoInfo(t *testing.T) {\n\tc := client.NewClient(APPKEY, SECRET)\n\tback, err := c.Video.GetVideoInfo(5495647)\n\tif err != nil {\n\t\tt.Error(err.Error())\n\t\tt.Failed()\n\t} else {\n\t\tif back.Title == \"\" {\n\t\t\tt.Error(\"return title is nil\")\n\t\t}\n\t\tif len(back.PartList) == 0 {\n\t\t\tt.Error(\"return partlist length is 0\")\n\t\t}\n\t\tt.Log(back)\n\t}\n}\n\nfunc TestVideoPath(t *testing.T) {\n\tc := client.NewClient(APPKEY, SECRET)\n\tback, err := c.Video.GetVideoPartPath(8932442, 1, \"mp4\")\n\tif err != nil {\n\t\tt.Error(err.Error())\n\t\tt.Failed()\n\t} else {\n\t\tif len(back.List) == 0 {\n\t\t\tt.Error(\"return list length is 0\")\n\t\t}\n\t\tfor _, dUrl := range back.List {\n\t\t\tt.Log(dUrl)\n\t\t}\n\t}\n}\n\nfunc TestSearch(t *testing.T) {\n\tc := client.NewClient(APPKEY, SECRET)\n\tback, err := c.Others.Search(\"fate\", 1, 10, \"totalrank\", \"all\")\n\tif err != nil {\n\t\tt.Error(err.Error())\n\t\tt.Failed()\n\t} else {\n\t\tif len(back.PageInfo) == 0 {\n\t\t\tt.Error(\"return list length is 0\")\n\t\t}\n\n\t\tfor _, video := range back.Result.Videos {\n\t\t\tt.Log(video)\n\t\t}\n\t}\n}\n\nfunc TestSpInfo(t *testing.T) {\n\tc := client.NewClient(APPKEY, SECRET)\n\tback, err := c.Special.GetSpecialInfo(158)\n\tif err != nil {\n\t\tt.Error(err.Error())\n\t\tt.Failed()\n\t} else {\n\t\tif back.Title == \"\" {\n\t\t\tt.Error(\"return title is nil\")\n\t\t}\n\t\tt.Log(back)\n\t}\n}\n\nfunc TestSpVideos(t *testing.T) {\n\tc := client.NewClient(APPKEY, SECRET)\n\tback, err := c.Special.GetSpecialVideos(158, true)\n\tif err != nil {\n\t\tt.Error(err.Error())\n\t\tt.Failed()\n\t} else {\n\t\tif len(back.List) == 0 {\n\t\t\tt.Error(\"return list is nil\")\n\t\t}\n\t\tfor _, ele := range back.List {\n\t\t\tt.Log(ele)\n\t\t}\n\t}\n}\n\nfunc TestUserInfo(t *testing.T) {\n\tc := client.NewClient(APPKEY, SECRET)\n\tback, err := c.User.GetUserInfo(591635)\n\tif err != nil {\n\t\tt.Error(err.Error())\n\t\tt.Failed()\n\t} else {\n\t\tif back.Name == \"\" {\n\t\t\tt.Error(\"api return user name nil\")\n\t\t}\n\t\tt.Log(back)\n\t}\n}\n\nfunc TestUserVideos(t *testing.T) {\n\tc := client.NewClient(APPKEY, SECRET)\n\tback, err := c.User.GetUserVideos(591635, 1, 10)\n\tif err != nil {\n\t\tt.Error(err.Error())\n\t\tt.Failed()\n\t} else {\n\t\tif len(back.List) == 0 {\n\t\t\tt.Error(\"api return empty list\")\n\t\t}\n\t\tif len(back.TypeIndex) == 0 {\n\t\t\tt.Error(\"type list is empty\")\n\t\t}\n\t\tt.Log(back)\n\t}\n}\n\nfunc TestAppIndex(t *testing.T) {\n\tc := client.NewClient(APPKEY, SECRET)\n\t_, err := c.Others.AppIndex()\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\n}\n"
  }
]