music
最近很少用 deepin
在线或者使用客户段听音乐了,喜欢下载到本地听那些熟悉的歌曲。
很多歌不是这个平台没版权就是那个要付费。也是顾虑到网易云9级用户的面子,不经常去其他平台听歌,在 GitHub
上有个项目,项目地址:https://github.com/nondanee/UnblockNeteaseMusic。可以只使用网易云的客户端听大部分平台的音乐,对于没有版权的歌曲会使用其他平台的 api
来播放。用着总算有个解决方案了。但是在 linux
客户端上,网易云程序没有设置代理的功能,而linux
上又要弄证书什么的,一个客户端就懒的折腾了,主要是折腾了几天没有很好的解决方案,就一直搁置了。
也是经常使用 you-get
的缘故,会经常把电影或视频下载下来,同时也支持很多国内视频与音乐平台,you-get 项目地址:https://github.com/soimort/you-get
you-get
使用起来也比较简单,将链接放进去就好了,但是国内如果不使用代理,速度十分的慢。关于代理的使用
you-get -x 127.0.0.1:7890 'https://www.youtube.com/watch?v=jNQXAC9IVRw'
对于视频来说每次找到一个视频,复制链接,下载。还算可以接受,但是对于音乐,只有短短的几分钟,每次要下新的歌曲都要进行打开浏览器搜索,复制链接。干脆自己写一个程序。
由于是打算做命令行程序,就是用 Go
来写吧,一个小程序,就不使用 go mod
来做了,直接一个 main
搞定。此项目地址:https://github.com/ayuayue/kugou
本来打算通过命令行来查询歌曲,返回歌曲的链接,然后配合 you-get
来进行下载,这样就多了一步,类似于使用管道符的方式。
又打算直接通过 go
来调用 you-get
来下载,但是这样前提需要安装 you-get
并且代理不好在程序中配置,只好配置临时或全局的终端代理。
最后打算直接在程序中完成歌曲的搜索,歌词及歌曲下载。这样就不依赖于外部的东西。
对于歌曲下载api
的获取,使用 you-get
是通过 -d
参数,开启调试。会显示出请求的地址以及音乐的地址,由此可以的到歌曲详情的 api
Api
- 查询歌曲 http://msearchcdn.kugou.com/api/v3/search/song eg: http://msearchcdn.kugou.com/api/v3/search/song?keyword=花海 参数 值类型 keyword 字符串
- 歌曲详情 https://www.kugou.com/yy/index.php?r=play/getdata eg: https://www.kugou.com/yy/index.php?r=play/getdata?hash=xxxx&album_id=xxx&mid=123 参数 值类型 hash 字符串 album_id 字符串 mid 123
有了这两个 API
剩下的就简单了。
开始编码
代码语言:javascript复制package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
)
type Req struct {
Status int `json:"status"`
Error string `json:"error"`
Data Data `json:"data"`
Errcode int `json:"errcode"`
}
type Data struct {
Info []Info `json:"info"`
}
type Info struct {
Hash string `json:"hash"`
AlbumID string `json:"album_id"`
SingerName string `josn:"singername"`
SongName string `json:"songname"`
}
type Song struct {
Status int `json:"status"`
ErrCode int `json:"err_code"`
Data SongData `json:"data"`
}
type SongData struct {
AudioName string `json:"audio_name"`
Lyrics string `json:"lyrics"`
PlayURL string `json:"play_url"`
}
var SearchApi = "http://msearchcdn.kugou.com/api/v3/search/song"
var SongApi = "https://www.kugou.com/yy/index.php?r=play"
func query() Req {
var name string
flag.StringVar(&name, "name", "", "歌曲名")
flag.Parse()
//fmt.Println(name)
client, err := http.NewRequest(http.MethodGet, SearchApi, nil)
if err != nil {
panic("请求失败")
}
params := make(url.Values)
params.Add("keyword", name)
client.URL.RawQuery = params.Encode()
//fmt.Println(client)
req, err := http.DefaultClient.Do(client)
defer func() {
req.Body.Close()
}()
if req.StatusCode != 200 {
panic("返回错误响应")
}
body, _ := ioutil.ReadAll(req.Body)
r := Req{}
json.Unmarshal(body, &r)
//fmt.Printf("%#v",r)
return r
}
func (r *Req) List() {
if r.Status != 1 {
panic(r.Error)
}
songs := r.Data.Info
for k, song := range songs {
fmt.Println(k 1, song.SongName, "--", song.SingerName)
}
}
func (r *Req) GetLink(num int) []string {
songApi := "https://www.kugou.com/song"
songs := r.Data.Info
hash := songs[num-1].Hash
album_id := songs[num-1].AlbumID
fmt.Printf("在线播放链接:%s/#hash=%s&album_id=%sn", songApi, hash, album_id)
return []string{
hash, album_id,
}
}
func (s *Song) Download() {
if s.Status != 1 {
panic("获取歌曲信息失败")
}
lyrics := s.Data.Lyrics
fmt.Println("开始下载歌词")
lrcFileName := fmt.Sprintf("%s.lrc", s.Data.AudioName)
songName := fmt.Sprintf("%s.mp3", s.Data.AudioName)
lf, err := os.Create(lrcFileName)
if err != nil {
panic(err)
}
defer func() { _ = lf.Close() }()
_, err = lf.Write([]byte(lyrics))
if err != nil {
panic(err)
}
fmt.Println("下载歌词完成")
fmt.Println("开始下载歌曲")
req, err := http.Get(s.Data.PlayURL)
if err != nil {
panic("请求失败")
}
defer func() {
req.Body.Close()
}()
if req.StatusCode != 200 {
panic("返回错误响应")
}
sf, err := os.Create(songName)
if err != nil {
panic(err)
}
defer func() { _ = sf.Close() }()
_, err = io.Copy(sf, req.Body)
if err != nil {
panic(err)
}
}
func main() {
var num int
req := query()
req.List()
fmt.Println("请输入要选择的歌曲链接的序号:")
fmt.Scanln(&num)
params := req.GetLink(num)
song := GetSong(params)
song.Download()
}
func GetSong(params []string) Song {
url := fmt.Sprintf("%s/getdata&hash=%s&album_id=%s&mid=123", SongApi, params[0], params[1])
req, err := http.Get(url)
if err != nil {
panic("请求失败")
}
defer func() {
req.Body.Close()
}()
if req.StatusCode != 200 {
panic("返回错误响应")
}
body, _ := ioutil.ReadAll(req.Body)
song := Song{}
json.Unmarshal(body, &song)
return song
}
编译(我本地是 linux 环境)
代码语言:javascript复制# linux
go build -o path ./main.go #path 路径
# windows
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o path main.go