├── .gitignore ├── README.md ├── alfred └── result.go ├── alfredworkflow └── ZiMuZu.alfredworkflow ├── client └── client.go ├── main.go ├── resource ├── alfred.go ├── list │ ├── alfred.go │ └── model.go ├── model.go ├── resource.go └── top │ ├── alfred.go │ └── model.go ├── screenshot ├── zmz_filter.png ├── zmz_search.png └── zmzh.png ├── search ├── alfred.go ├── model.go └── search.go └── utils └── utils.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ZiMuZu - Alfred Workflow 2 | 3 | *ZiMuZu.tv 字幕组* 4 | 5 | 6 | ## Usage 7 | 8 | + `zmz + keyword` 搜索 9 | + `zmzh` 24小时下载热门 10 | + `Enter`、`Cmd + Enter`复制电驴链接 11 | + `Alt + Enter` 复制磁力链接 12 | + `Ctrl + Enter`复制全季所有电驴链接 13 | + `Shift + Enter`复制全集所有磁力链接 14 | 15 | 16 | ## Screenshot 17 | 18 | ![热门](https://raw.githubusercontent.com/norlight/zimuzu-go/master/screenshot/zmzh.png) 19 | 20 | ![搜索](https://raw.githubusercontent.com/norlight/zimuzu-go/master/screenshot/zmz_search.png) 21 | 22 | ![筛选](https://raw.githubusercontent.com/norlight/zimuzu-go/master/screenshot/zmz_filter.png) 23 | -------------------------------------------------------------------------------- /alfred/result.go: -------------------------------------------------------------------------------- 1 | package alfred 2 | 3 | type AlfResulter interface { 4 | AlfResult() Result 5 | } 6 | type AlfItemer interface { 7 | AlfItem() Item 8 | } 9 | 10 | type Result struct { 11 | Variables map[string]string `json:"variables,omitempty"` 12 | Items []Item `json:"items"` 13 | } 14 | 15 | type Item struct { 16 | Title string `json:"title"` 17 | Valid bool `json:"valid,omitempty"` 18 | UID string `json:"uid,omitempty"` 19 | Type string `json:"type,omitempty"` 20 | Subtitle string `json:"subtitle,omitempty"` 21 | Arg string `json:"arg,omitempty"` 22 | Autocomplete string `json:"autocomplete,omitempty"` 23 | QuickLookURL string `json:"quicklookurl,omitempty"` 24 | Icon *Icon `json:"icon,omitempty"` 25 | Text *Text `json:"text,omitempty"` 26 | Mods map[string]*Mod `json:"mods,omitempty"` 27 | Variables map[string]string `json:"variables,omitempty"` //From Alfred 3.4.1 28 | } 29 | 30 | type Icon struct { 31 | Type string `json:"type,omitempty"` 32 | Path string `json:"path"` 33 | } 34 | 35 | type Text struct { 36 | Copy string `json:"copy,omitempty"` 37 | Largetype string `json:"largetype,omitempty"` 38 | } 39 | 40 | type Mod struct { 41 | Valid bool `json:"valid"` 42 | Arg string `json:"arg"` 43 | Subtitle string `json:"subtitle"` 44 | Icon *Icon `json:"icon,omitempty"` //From Alfred 3.4.1 45 | Variables map[string]string `json:"variables,omitempty"` //From Alfred 3.4.1 46 | } 47 | 48 | func NewResult() Result { 49 | return Result{ 50 | Items: make([]Item, 0), 51 | } 52 | } 53 | 54 | func (r *Result) Append(items ...Item) { 55 | r.Items = append(r.Items, items...) 56 | } 57 | -------------------------------------------------------------------------------- /alfredworkflow/ZiMuZu.alfredworkflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/norlight/zimuzu-go/8a1df9d90ce55246a18c694308e5969d4017c65f/alfredworkflow/ZiMuZu.alfredworkflow -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "net/url" 9 | "time" 10 | ) 11 | 12 | const ( 13 | Host = "http://api.ousns.net" 14 | ) 15 | 16 | type Client struct { 17 | cid string 18 | accesskey string 19 | platform int 20 | client *http.Client 21 | } 22 | 23 | func New(cid string, key string) (c Client) { 24 | c = Client{ 25 | cid: cid, 26 | accesskey: key, 27 | platform: 1, 28 | client: &http.Client{}, 29 | } 30 | return 31 | } 32 | 33 | func (c *Client) Get(p string, q string) (resp *http.Response, err error) { 34 | client := c.client 35 | u, err := url.Parse(Host) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | u.Path = p 41 | 42 | query, err := url.ParseQuery(q) 43 | if err != nil { 44 | return nil, err 45 | } 46 | query.Set("cid", c.cid) 47 | query.Set("accesskey", c.encryptKey()) 48 | query.Set("timestamp", fmt.Sprintf("%d", time.Now().Unix())) 49 | query.Set("client", fmt.Sprintf("%d", c.platform)) 50 | 51 | u.RawQuery = query.Encode() 52 | 53 | //log.Println(u.String()) 54 | r, err := http.NewRequest("POST", u.String(), nil) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | resp, err = client.Do(r) 60 | return 61 | } 62 | 63 | func (c *Client) encryptKey() string { 64 | str := fmt.Sprintf("%s$$%s&&%d", c.cid, c.accesskey, time.Now().Unix()) 65 | //m := md5.Sum([]byte(str)) 66 | m := md5.New() 67 | io.WriteString(m, str) 68 | md5 := m.Sum(nil) 69 | return fmt.Sprintf("%x", md5) 70 | } 71 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/norlight/zimuzu-go/client" 10 | "github.com/norlight/zimuzu-go/resource" 11 | "github.com/norlight/zimuzu-go/search" 12 | ) 13 | 14 | //填入你自己的 api key 15 | const ( 16 | cid = "" 17 | accesskey = "" 18 | ) 19 | 20 | func init() { 21 | log.SetPrefix("ZIMUZU: ") 22 | log.SetFlags(log.LstdFlags | log.Llongfile) 23 | log.SetOutput(os.Stdout) 24 | } 25 | 26 | func main() { 27 | if len(cid) == 0 || len(accesskey) == 0 { 28 | log.Println("configure your api key first.") 29 | os.Exit(1) 30 | } 31 | 32 | c := client.New(cid, accesskey) 33 | r := resource.New(&c) 34 | s := search.New(&c) 35 | 36 | var k string 37 | var chID string 38 | 39 | flag.StringVar(&k, "s", "", "search") 40 | flag.StringVar(&chID, "r", "", "fetch resource links") 41 | flag.Parse() 42 | if n := flag.NFlag(); n != 0 { 43 | if n > 1 { 44 | flag.Usage() 45 | os.Exit(1) 46 | } 47 | flag.Visit(func(f *flag.Flag) { 48 | switch f.Name { 49 | case "s": 50 | result, err := s.AlfSearch(k) 51 | if err != nil { 52 | panic(err) 53 | } 54 | fmt.Print(string(result)) 55 | os.Exit(0) 56 | 57 | case "r": 58 | result, err := r.AlfList(chID) 59 | if err != nil { 60 | panic(err) 61 | } 62 | fmt.Print(string(result)) 63 | os.Exit(0) 64 | } 65 | }) 66 | } 67 | 68 | if len(os.Args) != 2 { 69 | flag.Usage() 70 | os.Exit(1) 71 | } 72 | switch os.Args[1] { 73 | case "top": 74 | result, err := r.AlfTop() 75 | if err != nil { 76 | panic(err) 77 | } 78 | fmt.Print(string(result)) 79 | os.Exit(0) 80 | default: 81 | os.Exit(1) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /resource/alfred.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os" 9 | "regexp" 10 | "sort" 11 | "strconv" 12 | "sync" 13 | 14 | "github.com/norlight/zimuzu-go/alfred" 15 | "github.com/norlight/zimuzu-go/resource/list" 16 | "github.com/norlight/zimuzu-go/resource/top" 17 | ) 18 | 19 | func (l *List) AlfResult() (r alfred.Result) { 20 | for _, v := range l.Data.List { 21 | item := alfred.Item{ 22 | Title: v.CNName, 23 | Subtitle: fmt.Sprintf("%s%s", v.Itemupdate, v.Remark), 24 | } 25 | r.Append(item) 26 | } 27 | return 28 | } 29 | 30 | func (r *Resource) AlfFetchList() { 31 | resp, _ := r.FetchList("", "", "", "", "", "", "") 32 | body, _ := ioutil.ReadAll(resp.Body) 33 | defer resp.Body.Close() 34 | 35 | var info List 36 | json.Unmarshal(body, &info) 37 | //log.Println(string(body)) 38 | //log.Println(info) 39 | as := info.AlfResult() 40 | b, _ := json.Marshal(as) 41 | fmt.Print(string(b)) 42 | os.Exit(0) 43 | 44 | } 45 | 46 | func (r *Resource) AlfTop() (result []byte, err error) { 47 | resp, err := r.Top("", "11") 48 | if err != nil { 49 | return nil, err 50 | } 51 | body, err := ioutil.ReadAll(resp.Body) 52 | if err != nil { 53 | return nil, err 54 | } 55 | defer resp.Body.Close() 56 | 57 | var out top.TopOut 58 | if err := json.Unmarshal(body, &out); err != nil { 59 | return nil, err 60 | } 61 | as := out.AlfResult() 62 | result, err = json.Marshal(as) 63 | if err != nil { 64 | return nil, err 65 | } 66 | return 67 | } 68 | 69 | func (r *Resource) AlfList(chID string) (result []byte, err error) { 70 | channelRe, _ := regexp.Compile(`[a-zA-Z]+`) 71 | idRe, _ := regexp.Compile(`\d+`) 72 | channel := channelRe.FindString(chID) 73 | id := idRe.FindString(chID) 74 | switch channel { 75 | case "tv": 76 | return r.AlfTV(id) 77 | case "movie": 78 | return r.AlfMovie(id) 79 | default: 80 | return r.AlfTV(id) 81 | } 82 | } 83 | func (r *Resource) AlfTV(id string) (result []byte, err error) { 84 | resp, err := r.List(id) 85 | if err != nil { 86 | return nil, err 87 | } 88 | body, err := ioutil.ReadAll(resp.Body) 89 | if err != nil { 90 | return nil, err 91 | } 92 | defer resp.Body.Close() 93 | var out list.SEOut 94 | if err := json.Unmarshal(body, &out); err != nil { 95 | return nil, err 96 | } 97 | 98 | eps := make(chan []alfred.Item) 99 | var wg sync.WaitGroup 100 | for i := 0; i < len(out.Data); i++ { 101 | se := out.Data[i] 102 | var count int 103 | fmt.Sscanf(se.Episode, "%d", &count) 104 | 105 | for j := 0; j < count; j++ { 106 | wg.Add(1) 107 | go func(i, j int) { 108 | defer wg.Done() 109 | sStr := strconv.Itoa(i + 1) 110 | eStr := strconv.Itoa(j + 1) 111 | data, err := r.AlfItemList(id, sStr, eStr) 112 | if err != nil { 113 | log.Println(err) 114 | return 115 | } 116 | eps <- data 117 | }(i, j) 118 | } 119 | } 120 | 121 | go func() { 122 | wg.Wait() 123 | close(eps) 124 | }() 125 | 126 | var alfresult alfred.Result 127 | 128 | //信道传过来的为包含各集所有格式解析成AltItem的slice 129 | //取出来放到一块方便遍历处理 130 | allitem := make([]alfred.Item, 0) 131 | for items := range eps { 132 | allitem = append(allitem, items...) 133 | } 134 | sort.Sort(byEpisode(allitem)) 135 | 136 | //提取Item对应季度对应格式的所有链接 137 | for _, item := range allitem { 138 | format := item.Mods["ctrl"].Arg 139 | season := item.Mods["shift"].Arg 140 | 141 | var ed2ks string 142 | var magnets string 143 | 144 | for _, itemagain := range allitem { 145 | formatagain := itemagain.Mods["ctrl"].Arg 146 | seasonagain := itemagain.Mods["shift"].Arg 147 | if formatagain == format && seasonagain == season { 148 | ed2k := itemagain.Mods["cmd"].Arg 149 | magnet := itemagain.Mods["alt"].Arg 150 | ed2ks += fmt.Sprintln(ed2k) 151 | magnets += fmt.Sprintln(magnet) 152 | } 153 | } 154 | 155 | //更新AltItem,使按下相应键可以复制所有链接 156 | item.Mods["ctrl"] = &alfred.Mod{ 157 | Valid: true, 158 | Subtitle: fmt.Sprintf("复制全季[%s][电驴]链接", format), 159 | Arg: ed2ks, 160 | } 161 | item.Mods["shift"] = &alfred.Mod{ 162 | Valid: true, 163 | Subtitle: fmt.Sprintf("复制全季[%s][磁力]链接", format), 164 | Arg: magnets, 165 | } 166 | 167 | alfresult.Append(item) 168 | } 169 | 170 | result, err = json.Marshal(alfresult) 171 | return 172 | } 173 | 174 | func (r *Resource) AlfMovie(id string) (result []byte, err error) { 175 | resp, err := r.ItemList(id, "", "", "1") 176 | if err != nil { 177 | return nil, err 178 | } 179 | body, err := ioutil.ReadAll(resp.Body) 180 | if err != nil { 181 | return nil, err 182 | } 183 | defer resp.Body.Close() 184 | var out list.ItemListOut 185 | if err := json.Unmarshal(body, &out); err != nil { 186 | return nil, err 187 | } 188 | 189 | var alfresult alfred.Result 190 | for _, v := range out.Data { 191 | item := v.AlfItem() 192 | //将被用于临时存储的字段改回来 193 | item.Mods["shift"].Subtitle = item.Mods["ctrl"].Subtitle 194 | alfresult.Append(item) 195 | } 196 | result, err = json.Marshal(alfresult) 197 | return 198 | } 199 | 200 | func (r *Resource) AlfItemList(id, season, episode string) (slice []alfred.Item, err error) { 201 | resp, err := r.ItemList(id, season, episode, "1") 202 | if err != nil { 203 | return nil, err 204 | } 205 | body, err := ioutil.ReadAll(resp.Body) 206 | if err != nil { 207 | return nil, err 208 | } 209 | defer resp.Body.Close() 210 | var out list.ItemListOut 211 | if err := json.Unmarshal(body, &out); err != nil { 212 | return nil, err 213 | } 214 | for _, v := range out.Data { 215 | 216 | item := v.AlfItem() 217 | 218 | slice = append(slice, item) 219 | } 220 | return 221 | } 222 | 223 | //排序 224 | type byEpisode []alfred.Item 225 | 226 | func (x byEpisode) Len() int { 227 | return len(x) 228 | } 229 | func (x byEpisode) Less(i, j int) bool { 230 | itemi := x[i] 231 | itemj := x[j] 232 | eiStr := itemi.Mods["shift"].Subtitle 233 | ejStr := itemj.Mods["shift"].Subtitle 234 | ei, _ := strconv.Atoi(eiStr) 235 | ej, _ := strconv.Atoi(ejStr) 236 | return ei < ej 237 | } 238 | func (x byEpisode) Swap(i, j int) { 239 | x[i], x[j] = x[j], x[i] 240 | } 241 | -------------------------------------------------------------------------------- /resource/list/alfred.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | import ( 4 | "fmt" 5 | 6 | "strconv" 7 | 8 | "github.com/norlight/zimuzu-go/alfred" 9 | ) 10 | 11 | func (o *ItemListOut) AlfResult() (r alfred.Result) { 12 | for _, v := range o.Data { 13 | item := alfred.Item{ 14 | Title: v.Name, 15 | Subtitle: v.Size, 16 | } 17 | r.Append(item) 18 | } 19 | return 20 | } 21 | 22 | func (v *Item) AlfItem() (alf alfred.Item) { 23 | var title string 24 | var size string 25 | if s, _ := strconv.Atoi(v.Season); s == 0 { 26 | title = fmt.Sprintf("%s %s", v.Format, v.Name) 27 | } else { 28 | title = fmt.Sprintf("S%sE%s %s %s", v.Season, v.Episode, v.Format, v.Name) 29 | } 30 | if s, err := strconv.Atoi(v.Size); err == nil && s == 0 { 31 | size = "不提供下载" 32 | } else { 33 | size = v.Size 34 | } 35 | 36 | item := alfred.Item{ 37 | Title: title, 38 | Subtitle: size, 39 | Mods: make(map[string]*alfred.Mod), 40 | } 41 | for _, str := range []string{"cmd", "alt", "ctrl", "shift"} { 42 | item.Mods[str] = &alfred.Mod{ 43 | Valid: false, 44 | Subtitle: v.Size, 45 | Arg: "", 46 | } 47 | } 48 | 49 | //临时借用字段存储下格式和季度信息,传出去后再修改 50 | //等Alfred 3.4.1出来Item支持变量就不用这么麻烦了 51 | item.Mods["ctrl"] = &alfred.Mod{ 52 | Valid: false, 53 | Subtitle: v.Size, 54 | Arg: v.Format, 55 | } 56 | item.Mods["shift"] = &alfred.Mod{ 57 | Valid: false, 58 | Subtitle: v.Episode, 59 | Arg: v.Season, 60 | } 61 | for _, link := range v.Link { 62 | switch link.Way { 63 | case "1": 64 | item.Arg = link.Address 65 | item.Mods["cmd"] = &alfred.Mod{ 66 | Valid: true, 67 | Subtitle: "复制[电驴]链接到剪贴板", 68 | Arg: link.Address, 69 | } 70 | case "2": 71 | item.Mods["alt"] = &alfred.Mod{ 72 | Valid: true, 73 | Subtitle: "复制[磁力]链接到剪贴板", 74 | Arg: link.Address, 75 | } 76 | } 77 | } 78 | 79 | return item 80 | } 81 | -------------------------------------------------------------------------------- /resource/list/model.go: -------------------------------------------------------------------------------- 1 | package list 2 | 3 | type SEOut struct { 4 | Status int `json:"status"` 5 | Info string `json:"info"` 6 | Data []SE `json:"data"` 7 | } 8 | type SE struct { 9 | Season string `json:"season"` 10 | Episode string `json:"episode"` 11 | } 12 | 13 | type ItemListOut struct { 14 | Status int `json:"status"` 15 | Info string `json:"info"` 16 | Data []Item `json:"data"` 17 | } 18 | 19 | type Item struct { 20 | ID string `json:"id"` 21 | Name string `json:"name"` 22 | Format string `json:"format"` 23 | Season string `json:"season"` 24 | Episode string `json:"episode"` 25 | Size string `json:"size"` 26 | Link []ItemLink `json:"link"` 27 | } 28 | 29 | type ItemLink struct { 30 | Way string `json:"way"` 31 | Address string `json:"address"` 32 | } 33 | -------------------------------------------------------------------------------- /resource/model.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | //Subject 资源,剧、电影等 4 | type Subject struct { 5 | ID string `json:"id"` 6 | CNName string `json:"cnname"` 7 | ENName string `json:"enname"` 8 | Remark string `json:"remark"` 9 | Area string `json:"area"` 10 | Format string `json:"format"` 11 | Category string `json:"category"` 12 | Poster string `json:"poster"` 13 | Channel string `json:"channel"` 14 | Lang string `json:"lang"` 15 | PlayStatus string `json:"play_status"` 16 | Rank string `json:"rank"` 17 | Score string `json:"score"` 18 | Views string `json:"views"` 19 | Itemupdate string `json:"itemupdate"` 20 | PosterA string `json:"poster_a"` 21 | PosterB string `json:"poster_b"` 22 | PosterM string `json:"poster_m"` 23 | PosterS string `json:"poster_s"` 24 | } 25 | 26 | //List 资源列表 27 | type List struct { 28 | Status int `json:"status"` 29 | Info string `json:"info"` 30 | Data struct { 31 | Count string `json:"count"` 32 | List []Subject `json:"list"` 33 | } `json:"data"` 34 | } 35 | 36 | //Info 资源详情 37 | type Info struct { 38 | Status int `json:"status"` 39 | Info string `json:"info"` 40 | Data Subject `json:"data"` 41 | } 42 | 43 | //SeasonEpisodeInfo 资源季度信息列表 44 | type SeasonEpisodeInfo struct { 45 | Status int `json:"status"` 46 | Info string `json:"info"` 47 | Data []SeasonEpisode `json:"data"` 48 | } 49 | 50 | //SeasonEpisode 资源季度信息 51 | type SeasonEpisode struct { 52 | Season string `json:"season"` 53 | Episode string `json:"episode"` 54 | } 55 | 56 | //Item 单集下载信息 57 | type Item struct { 58 | ID string `json:"id"` 59 | Name string `json:"name"` 60 | Format string `json:"format"` 61 | Season string `json:"season"` 62 | Episode string `json:"episode"` 63 | Size string `json:"size"` 64 | Link []ItemLink `json:"link"` 65 | } 66 | 67 | //ItemLink 单集下载地址 68 | type ItemLink struct { 69 | Way string `json:"way"` 70 | Address string `json:"address"` 71 | } 72 | 73 | //ItemList 下载列表 74 | type ItemList struct { 75 | Status int `json:"status"` 76 | Info string `json:"info"` 77 | Data []struct { 78 | ID string `json:"id"` 79 | Name string `json:"name"` 80 | Format string `json:"format"` 81 | Season string `json:"season"` 82 | Episode string `json:"episode"` 83 | Size string `json:"size"` 84 | Link []struct { 85 | Way string `json:"way"` 86 | Address string `json:"address"` 87 | } `json:"link"` 88 | } `json:"data"` 89 | } 90 | -------------------------------------------------------------------------------- /resource/resource.go: -------------------------------------------------------------------------------- 1 | package resource 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | 7 | "github.com/norlight/zimuzu-go/client" 8 | ) 9 | 10 | type Resource struct { 11 | Client *client.Client 12 | } 13 | 14 | func New(c *client.Client) Resource { 15 | return Resource{c} 16 | } 17 | 18 | func (r *Resource) FetchList(channel, area, sort, year, category, limit, page string) (resp *http.Response, err error) { 19 | p := "/resource/fetchlist" 20 | 21 | q := url.Values{} 22 | q.Set("channel", channel) 23 | q.Set("area", area) 24 | q.Set("sort", sort) 25 | q.Set("year", year) 26 | q.Set("category", category) 27 | q.Set("limit", limit) 28 | q.Set("page", page) 29 | 30 | resp, err = r.Client.Get(p, q.Encode()) 31 | return 32 | } 33 | 34 | func (r *Resource) Top(channel string, limit string) (resp *http.Response, err error) { 35 | p := "/resource/top" 36 | 37 | q := url.Values{} 38 | q.Set("channel", channel) 39 | q.Set("limit", limit) 40 | 41 | resp, err = r.Client.Get(p, q.Encode()) 42 | return 43 | } 44 | 45 | func (r *Resource) List(id string) (resp *http.Response, err error) { 46 | p := "/resource/season_episode" 47 | q := url.Values{} 48 | q.Set("id", id) 49 | 50 | resp, err = r.Client.Get(p, q.Encode()) 51 | return 52 | } 53 | 54 | func (r *Resource) ItemList(id, season, episode, file string) (resp *http.Response, err error) { 55 | p := "/resource/itemlist_web" 56 | q := url.Values{} 57 | q.Set("id", id) 58 | q.Set("season", season) 59 | q.Set("episode", episode) 60 | q.Set("file", file) 61 | 62 | resp, err = r.Client.Get(p, q.Encode()) 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /resource/top/alfred.go: -------------------------------------------------------------------------------- 1 | package top 2 | 3 | import "github.com/norlight/zimuzu-go/alfred" 4 | import "fmt" 5 | 6 | func (o *TopOut) AlfResult() (r alfred.Result) { 7 | for i, v := range o.Data { 8 | var st string 9 | switch v.Channel { 10 | case "tv": 11 | switch v.Area { 12 | case "美国": 13 | st = "美剧" 14 | case "英国": 15 | st = "英剧" 16 | case "日本": 17 | st = "日剧" 18 | case "韩国": 19 | st = "韩剧" 20 | default: 21 | st = "影视剧" 22 | } 23 | 24 | case "movie": 25 | st = fmt.Sprintf("电影 [%s]", v.Area) 26 | case "openclass": 27 | st = "公开课" 28 | case "documentary": 29 | st = "纪录片" 30 | default: 31 | st = "影视剧" 32 | } 33 | 34 | item := alfred.Item{ 35 | Title: fmt.Sprintf("TOP%d《%s》%s", i+1, v.Cnname, v.PublishYear), 36 | Subtitle: fmt.Sprintf("%s [%s] [%s]", st, v.PlayStatus, v.Category), 37 | Arg: fmt.Sprintf("%s:%s", v.Channel, v.ID), 38 | Variables: map[string]string{"cnname": v.Cnname}, 39 | } 40 | r.Append(item) 41 | } 42 | 43 | return 44 | } 45 | -------------------------------------------------------------------------------- /resource/top/model.go: -------------------------------------------------------------------------------- 1 | package top 2 | 3 | type TopOut struct { 4 | Status int `json:"status"` 5 | Info string `json:"info"` 6 | Data []Resource `json:"data"` 7 | } 8 | 9 | type Resource struct { 10 | ID string `json:"id"` 11 | Cnname string `json:"cnname"` 12 | Channel string `json:"channel"` 13 | Area string `json:"area"` 14 | Category string `json:"category"` 15 | PublishYear string `json:"publish_year"` 16 | PlayStatus string `json:"play_status"` 17 | Poster string `json:"poster"` 18 | PosterB string `json:"poster_b"` 19 | PosterM string `json:"poster_m"` 20 | PosterS string `json:"poster_s"` 21 | } 22 | -------------------------------------------------------------------------------- /screenshot/zmz_filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/norlight/zimuzu-go/8a1df9d90ce55246a18c694308e5969d4017c65f/screenshot/zmz_filter.png -------------------------------------------------------------------------------- /screenshot/zmz_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/norlight/zimuzu-go/8a1df9d90ce55246a18c694308e5969d4017c65f/screenshot/zmz_search.png -------------------------------------------------------------------------------- /screenshot/zmzh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/norlight/zimuzu-go/8a1df9d90ce55246a18c694308e5969d4017c65f/screenshot/zmzh.png -------------------------------------------------------------------------------- /search/alfred.go: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | 7 | "fmt" 8 | "strconv" 9 | 10 | "github.com/norlight/zimuzu-go/alfred" 11 | "github.com/norlight/zimuzu-go/utils" 12 | ) 13 | 14 | func (o *SearchOut) AlfResult() (r alfred.Result) { 15 | for _, v := range o.Data.List { 16 | if v.Type == "resource" { 17 | unix, _ := strconv.Atoi(v.Uptime) 18 | s := fmt.Sprintf("更新:%s", utils.FormatUnix(int64(unix))) 19 | item := alfred.Item{ 20 | Title: v.Title, 21 | Subtitle: s, 22 | Arg: v.Itemid, 23 | } 24 | r.Append(item) 25 | } 26 | } 27 | return 28 | } 29 | 30 | func (s *Search) AlfSearch(k string) (result []byte, err error) { 31 | resp, err := s.Search(k) 32 | if err != nil { 33 | return nil, err 34 | } 35 | body, err := ioutil.ReadAll(resp.Body) 36 | if err != nil { 37 | return nil, err 38 | } 39 | defer resp.Body.Close() 40 | 41 | var out SearchOut 42 | if err := json.Unmarshal(body, &out); err != nil { 43 | return nil, err 44 | } 45 | as := out.AlfResult() 46 | result, err = json.Marshal(as) 47 | if err != nil { 48 | return nil, err 49 | } 50 | return 51 | } 52 | -------------------------------------------------------------------------------- /search/model.go: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | type SearchOut struct { 4 | Status int `json:"status"` 5 | Info string `json:"info"` 6 | Data ItemList `json:"data"` 7 | } 8 | 9 | type ItemList struct { 10 | Count int `json:"count"` 11 | List []Item `json:"list"` 12 | } 13 | type Item struct { 14 | Itemid string `json:"itemid"` 15 | Title string `json:"title"` 16 | Type string `json:"type"` 17 | Channel string `json:"channel"` 18 | Pubtime string `json:"pubtime"` 19 | Uptime string `json:"uptime"` 20 | } 21 | -------------------------------------------------------------------------------- /search/search.go: -------------------------------------------------------------------------------- 1 | package search 2 | 3 | import ( 4 | "net/http" 5 | "net/url" 6 | 7 | "github.com/norlight/zimuzu-go/client" 8 | ) 9 | 10 | type Search struct { 11 | Client *client.Client 12 | } 13 | 14 | func New(c *client.Client) Search { 15 | return Search{c} 16 | } 17 | 18 | func (s *Search) Search(k string) (resp *http.Response, err error) { 19 | p := "/search" 20 | q := url.Values{} 21 | q.Set("k", k) 22 | 23 | resp, err = s.Client.Get(p, q.Encode()) 24 | return 25 | } 26 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func FormatUnix(unix int64) (s string) { 9 | interval := time.Now().Unix() - unix 10 | switch { 11 | case interval < 60: 12 | return fmt.Sprintf("%d秒前", interval) 13 | case interval/60 < 60: 14 | return fmt.Sprintf("%d分钟前", interval/60) 15 | case interval/(60*60) < 24: 16 | return fmt.Sprintf("%d小时前", interval/(60*60)) 17 | case interval/(60*60*24) < 30: 18 | return fmt.Sprintf("%d天前", interval/(60*60*24)) 19 | default: 20 | t := time.Unix(unix, 0) 21 | return t.Format("2006-01-02 15:04:05") 22 | } 23 | } 24 | --------------------------------------------------------------------------------