-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
winterssy
committed
May 30, 2019
0 parents
commit 900cd5d
Showing
29 changed files
with
2,427 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Binaries for programs and plugins | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
|
||
# Test binary, built with `go test -c` | ||
*.test | ||
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
*.out | ||
|
||
# Editor directories and files | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
language: go | ||
|
||
go: | ||
- 1.12.x | ||
|
||
# Note: If you're working inside $GOPATH/src, go modules are disabled by default and can be enabled by setting GO111MODULE=on. | ||
env: | ||
GO111MODULE=on | ||
|
||
install: | ||
- go mod download | ||
|
||
script: | ||
- go test -v ./... | ||
- GOOS=windows GOARCH=amd64 go build -o music-get.exe | ||
- GOOS=linux GOARCH=amd64 go build -o music-get | ||
|
||
before_deploy: | ||
- zip music-get_windows_amd64.zip music-get.exe README.md | ||
- zip music-get_linux_amd64.zip music-get README.md | ||
|
||
deploy: | ||
provider: releases | ||
api_key: $GITHUB_TOKEN | ||
file: | ||
- music-get_windows_amd64.zip | ||
- music-get_linux_amd64.zip | ||
skip_cleanup: true | ||
on: | ||
tags: true |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Music-Get | ||
|
||
[](https://travis-ci.org/winterssy/music-get) | ||
[](https://github.com/winterssy/music-get/blob/master/LICENSE) | ||
|
||
[网易云音乐](https://music.163.com) | [QQ音乐](https://y.qq.com) 下载助手,支持一键下载单曲/专辑/歌单以及歌手热门歌曲,并自动更新音乐标签。 | ||
|
||
>本项目仅供学习研究使用。如侵犯你的权益,请 [联系作者](mailto:[email protected]) 删除。 | ||
## 如何使用? | ||
|
||
直接将音乐地址作为命令行参数传入即可,如: | ||
|
||
- 下载单曲: | ||
``` | ||
$ music-get https://music.163.com/#/song?id=553310243 | ||
$ music-get https://y.qq.com/n/yqq/song/002Zkt5S2z8JZx.html | ||
``` | ||
|
||
- 下载专辑: | ||
``` | ||
$ music-get https://music.163.com/#/album?id=38373053 | ||
$ music-get https://y.qq.com/n/yqq/song/002Zkt5S2z8JZx.html | ||
``` | ||
|
||
- 下载歌单: | ||
``` | ||
$ music-get https://music.163.com/#/playlist?id=156934569 | ||
$ music-get https://y.qq.com/n/yqq/album/002fRO0N4FftzY.html | ||
``` | ||
|
||
- 下载歌手热门歌曲: | ||
``` | ||
$ music-get https://music.163.com/#/artist?id=13193 | ||
$ music-get https://y.qq.com/n/yqq/singer/000Sp0Bz4JXH0o.html | ||
``` | ||
|
||
命令选项: | ||
- `-br`:优先下载音质,可选128/192/320,默认128。 | ||
- `-o`:下载保存目录,默认为 `/home/用户名/Music-Get` (Windows为 `C:\\Users\\用户名\\Music-Get` )。 | ||
- `-n`:并发下载任务数,最大值16,默认1,即单任务下载。 | ||
- `-h`:获取命令帮助。 | ||
|
||
**注:** 命令选项必须先于其它命令行参数输入。 | ||
|
||
## License | ||
|
||
GPLv3. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
package common | ||
|
||
import ( | ||
"errors" | ||
"github.com/bogem/id3v2" | ||
"github.com/winterssy/music-get/utils" | ||
"github.com/winterssy/music-get/utils/logger" | ||
"gopkg.in/cheggaaa/pb.v1" | ||
"io" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"sync" | ||
"time" | ||
) | ||
|
||
const ( | ||
NeteaseMusic = 1000 + iota | ||
TencentMusic | ||
) | ||
|
||
const ( | ||
DownloadSuccess = 2000 + iota | ||
DownloadNoCopyrightError | ||
DownloadBuildPathError | ||
DownloadHTTPRequestError | ||
DownloadBuildFileError | ||
DownloadFileTransferError | ||
) | ||
|
||
type Tag struct { | ||
Title string | ||
Artist string | ||
Album string | ||
Year string | ||
Track string | ||
CoverImage string | ||
} | ||
|
||
type MP3 struct { | ||
FileName string | ||
SavePath string | ||
Playable bool | ||
DownloadUrl string | ||
Tag Tag | ||
Origin int | ||
} | ||
|
||
type DownloadTask struct { | ||
MP3 *MP3 | ||
Status int | ||
} | ||
|
||
func (m *MP3) UpdateTag(wg *sync.WaitGroup) { | ||
var err error | ||
defer func() { | ||
if err != nil { | ||
logger.Error.Printf("Update music tag error: %s: %s", m.FileName, err.Error()) | ||
} else { | ||
logger.Info.Printf("Music tag updated: %s", m.FileName) | ||
} | ||
wg.Done() | ||
}() | ||
|
||
file := filepath.Join(m.SavePath, m.FileName) | ||
resp, err := Request("GET", m.Tag.CoverImage, nil, nil, NeteaseMusic) | ||
if err != nil { | ||
return | ||
} | ||
defer resp.Body.Close() | ||
|
||
data, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return | ||
} | ||
|
||
tag, err := id3v2.Open(file, id3v2.Options{Parse: true}) | ||
if err != nil { | ||
return | ||
} | ||
defer tag.Close() | ||
|
||
tag.SetDefaultEncoding(id3v2.EncodingUTF8) | ||
pic := id3v2.PictureFrame{ | ||
Encoding: id3v2.EncodingUTF8, | ||
MimeType: "image/jpg", | ||
PictureType: id3v2.PTOther, | ||
Picture: data, | ||
} | ||
tag.AddAttachedPicture(pic) | ||
tag.SetTitle(m.Tag.Title) | ||
tag.SetArtist(m.Tag.Artist) | ||
tag.SetAlbum(m.Tag.Album) | ||
tag.SetYear(m.Tag.Year) | ||
textFrame := id3v2.TextFrame{ | ||
Encoding: id3v2.EncodingUTF8, | ||
Text: m.Tag.Track, | ||
} | ||
tag.AddFrame(tag.CommonID("Track number/Position in set"), textFrame) | ||
|
||
err = tag.Save() | ||
return | ||
} | ||
|
||
func (m *MP3) SingleDownload() error { | ||
m.SavePath = filepath.Join(MP3DownloadDir, m.SavePath) | ||
if err := utils.BuildPathIfNotExist(m.SavePath); err != nil { | ||
return err | ||
} | ||
|
||
resp, err := Request("GET", m.DownloadUrl, nil, nil, m.Origin) | ||
if err != nil { | ||
return err | ||
} | ||
defer resp.Body.Close() | ||
|
||
fPath := filepath.Join(m.SavePath, m.FileName) | ||
f, err := os.Create(fPath) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
|
||
bar := pb.New(int(resp.ContentLength)).SetUnits(pb.U_BYTES).SetRefreshRate(100 * time.Millisecond) | ||
bar.ShowSpeed = true | ||
bar.Start() | ||
reader := bar.NewProxyReader(resp.Body) | ||
n, err := io.Copy(f, reader) | ||
if err != nil { | ||
return err | ||
} | ||
if n != resp.ContentLength { | ||
return errors.New("file transfer interrupted") | ||
} | ||
|
||
bar.Finish() | ||
return nil | ||
} | ||
|
||
func (m *MP3) ConcurrentDownload(taskList chan DownloadTask, taskQueue chan struct{}, wg *sync.WaitGroup) { | ||
var err error | ||
task := DownloadTask{ | ||
MP3: m, | ||
} | ||
|
||
defer func() { | ||
if err != nil { | ||
logger.Error.Printf("Download error: %s: %s", m.FileName, err.Error()) | ||
} | ||
wg.Done() | ||
taskList <- task | ||
<-taskQueue | ||
}() | ||
|
||
if !m.Playable { | ||
logger.Info.Printf("Ignore no coypright music: %s", m.Tag.Title) | ||
task.Status = DownloadNoCopyrightError | ||
return | ||
} | ||
|
||
logger.Info.Printf("Downloading: %s", m.FileName) | ||
m.SavePath = filepath.Join(MP3DownloadDir, m.SavePath) | ||
if err = utils.BuildPathIfNotExist(m.SavePath); err != nil { | ||
task.Status = DownloadBuildPathError | ||
return | ||
} | ||
|
||
resp, err := Request("GET", m.DownloadUrl, nil, nil, m.Origin) | ||
if err != nil { | ||
task.Status = DownloadHTTPRequestError | ||
return | ||
} | ||
defer resp.Body.Close() | ||
|
||
fPath := filepath.Join(m.SavePath, m.FileName) | ||
f, err := os.Create(fPath) | ||
if err != nil { | ||
task.Status = DownloadBuildFileError | ||
return | ||
} | ||
defer f.Close() | ||
|
||
n, err := io.Copy(f, resp.Body) | ||
if err != nil { | ||
task.Status = DownloadFileTransferError | ||
return | ||
} | ||
if n != resp.ContentLength { | ||
task.Status = DownloadFileTransferError | ||
return | ||
} | ||
|
||
task.Status = DownloadSuccess | ||
logger.Info.Printf("Download complete: %s", m.FileName) | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package common | ||
|
||
import ( | ||
"flag" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
const ( | ||
MaxConcurrentDownloadTasksNumber = 16 | ||
) | ||
|
||
var ( | ||
MP3DownloadDir string | ||
MP3DownloadBr int | ||
MP3ConcurrentDownloadTasksNumber int | ||
) | ||
|
||
func init() { | ||
homedir, err := os.UserHomeDir() | ||
if err != nil { | ||
homedir = "." | ||
} | ||
downloadDir := filepath.Join(homedir, "Music-Get") | ||
flag.StringVar(&MP3DownloadDir, "o", downloadDir, "MP3 download directory") | ||
flag.IntVar(&MP3DownloadBr, "br", 128, "MP3 prior download bit rate, 128|192|320") | ||
flag.IntVar(&MP3ConcurrentDownloadTasksNumber, "n", 1, "MP3 concurrent download tasks number, max 16") | ||
flag.Parse() | ||
} |
Oops, something went wrong.