Skip to content

Commit 2133561

Browse files
committed
feat: 🎉 init commit
0 parents  commit 2133561

File tree

8 files changed

+354
-0
lines changed

8 files changed

+354
-0
lines changed

.gitignore

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# If you prefer the allow list template instead of the deny list, see community template:
2+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3+
#
4+
# Binaries for programs and plugins
5+
*.exe
6+
*.exe~
7+
*.dll
8+
*.so
9+
*.dylib
10+
11+
# Test binary, built with `go test -c`
12+
*.test
13+
14+
# Output of the go coverage tool, specifically when used with LiteIDE
15+
*.out
16+
17+
# Dependency directories (remove the comment below to include it)
18+
# vendor/
19+
20+
# Go workspace file
21+
go.work
22+
go.work.sum
23+
24+
# env file
25+
.env

config.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
last_download_time: 1970-01-01T00:00:00Z # 最后下载时间,避免重复下载
2+
downloads:
3+
- name: name # 番剧名称
4+
path: path # 存储路径
5+
filter: filter # 正则表达式匹配

go.mod

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module go-ani-rss
2+
3+
go 1.22.2
4+
5+
require github.com/mmcdole/gofeed v1.3.0
6+
7+
require (
8+
github.com/PuerkitoBio/goquery v1.8.0 // indirect
9+
github.com/andybalholm/cascadia v1.3.1 // indirect
10+
github.com/docker/go-units v0.5.0 // indirect
11+
github.com/json-iterator/go v1.1.12 // indirect
12+
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
13+
github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23 // indirect
14+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
15+
github.com/modern-go/reflect2 v1.0.2 // indirect
16+
github.com/rivo/uniseg v0.4.7 // indirect
17+
github.com/schollz/progressbar v1.0.0 // indirect
18+
github.com/schollz/progressbar/v3 v3.14.6 // indirect
19+
golang.org/x/net v0.29.0 // indirect
20+
golang.org/x/sys v0.25.0 // indirect
21+
golang.org/x/term v0.24.0 // indirect
22+
golang.org/x/text v0.18.0 // indirect
23+
golang.org/x/tools v0.25.0 // indirect
24+
gopkg.in/yaml.v3 v3.0.1 // indirect
25+
)

go.sum

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
2+
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
3+
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
4+
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
5+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
8+
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
9+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
10+
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
11+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
12+
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
13+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
14+
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
15+
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
16+
github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
17+
github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE=
18+
github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23 h1:Zr92CAlFhy2gL+V1F+EyIuzbQNbSgP4xhTODZtrXUtk=
19+
github.com/mmcdole/goxpp v1.1.1-0.20240225020742-a0c311522b23/go.mod h1:v+25+lT2ViuQ7mVxcncQ8ch1URund48oH+jhjiwEgS8=
20+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
21+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
22+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
23+
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
24+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
25+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
26+
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
27+
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
28+
github.com/schollz/progressbar v1.0.0 h1:gbyFReLHDkZo8mxy/dLWMr+Mpb1MokGJ1FqCiqacjZM=
29+
github.com/schollz/progressbar v1.0.0/go.mod h1:/l9I7PC3L3erOuz54ghIRKUEFcosiWfLvJv+Eq26UMs=
30+
github.com/schollz/progressbar/v3 v3.14.6 h1:GyjwcWBAf+GFDMLziwerKvpuS7ZF+mNTAXIB2aspiZs=
31+
github.com/schollz/progressbar/v3 v3.14.6/go.mod h1:Nrzpuw3Nl0srLY0VlTvC4V6RL50pcEymjy6qyJAaLa0=
32+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
33+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
34+
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
35+
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
36+
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
37+
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
38+
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
39+
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
40+
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
41+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
42+
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
43+
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
44+
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
45+
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
46+
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
47+
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
48+
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
49+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
50+
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
51+
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
52+
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
53+
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
54+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
55+
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
56+
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
57+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
58+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
59+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"go-ani-rss/src/download"
6+
"go-ani-rss/src/settings"
7+
"time"
8+
9+
"github.com/mmcdole/gofeed"
10+
)
11+
12+
var (
13+
rssFeedUrl string
14+
feedProvider *gofeed.Parser
15+
)
16+
17+
func init() {
18+
rssFeedUrl = "https://api.ani.rip/ani-download.xml"
19+
20+
feedProvider = gofeed.NewParser()
21+
}
22+
23+
func main() {
24+
err := settings.ParserSettings()
25+
if err != nil {
26+
fmt.Println(err)
27+
}
28+
29+
for {
30+
feed, err := feedProvider.ParseURL(rssFeedUrl)
31+
if err != nil {
32+
fmt.Println(err)
33+
}
34+
35+
err = download.ProceedRssItems(feed)
36+
if err != nil {
37+
fmt.Println(err)
38+
}
39+
40+
time.Sleep(60 * time.Second)
41+
}
42+
}

src/download/download.go

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package download
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"net/http"
7+
"os"
8+
"path/filepath"
9+
"regexp"
10+
"strings"
11+
12+
"github.com/docker/go-units"
13+
"github.com/mmcdole/gofeed"
14+
"github.com/schollz/progressbar/v3"
15+
16+
"go-ani-rss/src/format"
17+
"go-ani-rss/src/settings"
18+
)
19+
20+
func downloadFileFromUrl(url string, path string, size int64) error {
21+
// create output file
22+
outputDir := filepath.Dir(path)
23+
err := os.MkdirAll(outputDir, 0755)
24+
if err != nil {
25+
return err
26+
}
27+
output, err := os.Create(path)
28+
if err != nil {
29+
return err
30+
}
31+
defer output.Close()
32+
33+
// make http request
34+
request, err := http.NewRequest("GET", url, nil)
35+
if err != nil {
36+
return err
37+
}
38+
response, err := http.DefaultClient.Do(request)
39+
if err != nil {
40+
return err
41+
}
42+
defer response.Body.Close()
43+
44+
// progress bar
45+
bar := progressbar.DefaultBytes(size, "downloading")
46+
47+
// write into target file
48+
io.Copy(io.MultiWriter(output, bar), response.Body)
49+
50+
return nil
51+
}
52+
53+
func proceedRssItem(item *gofeed.Item, itemConfig settings.ItemConfig) error {
54+
fmt.Printf("Started downloading %s\n", item.Title)
55+
56+
// convert file size
57+
size, err := units.FromHumanSize(strings.Replace(item.Extensions["anime"]["size"][0].Value, " ", "", -1))
58+
if err != nil {
59+
return nil
60+
}
61+
62+
// convert download path
63+
path, err := format.FormatPath(item.Title, itemConfig.Name, itemConfig.Path)
64+
if err != nil {
65+
return err
66+
}
67+
// download file
68+
err = downloadFileFromUrl(item.Link, path, size)
69+
if err != nil {
70+
fmt.Println(err)
71+
return nil
72+
}
73+
74+
fmt.Printf("\nDownloaded %s\n", item.Title)
75+
return nil
76+
}
77+
78+
func ProceedRssItems(feed *gofeed.Feed) error {
79+
for _, item := range feed.Items {
80+
for _, itemConfig := range settings.Config.ItemConfigs {
81+
reg, err := regexp.Compile(itemConfig.Filter)
82+
if err != nil {
83+
return err
84+
}
85+
86+
if reg.MatchString(item.Title) {
87+
if item.PublishedParsed.After(settings.Config.LastDownloadTime) {
88+
err = proceedRssItem(item, itemConfig)
89+
if err != nil {
90+
return err
91+
}
92+
}
93+
}
94+
}
95+
}
96+
97+
err := settings.SaveSettings()
98+
if err != nil {
99+
return err
100+
}
101+
102+
return nil
103+
}

src/format/format.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package format
2+
3+
import (
4+
"errors"
5+
"regexp"
6+
"strings"
7+
)
8+
9+
func getSeasonId(name string) string {
10+
chineseToDigit := map[string]string{"零": "00", "一": "01", "二": "02", "三": "03", "四": "04", "五": "05", "六": "06", "七": "07", "八": "08", "九": "09", "十": "10"}
11+
12+
const pattern = `第.季`
13+
reg, _ := regexp.Compile(pattern)
14+
15+
matches := reg.FindStringSubmatch(name)
16+
17+
if len(matches) == 2 && matches[0] == name {
18+
return chineseToDigit[matches[1]]
19+
}
20+
21+
return "01"
22+
}
23+
24+
func FormatPath(originName string, title string, format string) (string, error) {
25+
// compile regexp
26+
const pattern = `\[ANi\] (.*) - (\d{2}) [^\.]*\.(.*)`
27+
reg, _ := regexp.Compile(pattern)
28+
29+
matches := reg.FindStringSubmatch(originName)
30+
31+
if len(matches) == 4 && matches[0] == originName {
32+
name, seasonId, episodeId, extName := title, getSeasonId(matches[1]), matches[2], matches[3]
33+
34+
format = strings.ReplaceAll(format, `{name}`, name)
35+
format = strings.ReplaceAll(format, `{season}`, seasonId)
36+
format = strings.ReplaceAll(format, `{episode}`, episodeId)
37+
format = strings.ReplaceAll(format, `{ext}`, extName)
38+
39+
return format, nil
40+
}
41+
return "", errors.New("origin name is not valid")
42+
}

src/settings/settings.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package settings
2+
3+
import (
4+
"os"
5+
"time"
6+
7+
"gopkg.in/yaml.v3"
8+
)
9+
10+
type ItemConfig struct {
11+
Name string `yaml:"name"`
12+
Filter string `yaml:"filter"`
13+
Path string `yaml:"path"`
14+
}
15+
16+
type AppConfig struct {
17+
LastDownloadTime time.Time `yaml:"last_download_time"`
18+
ItemConfigs []ItemConfig `yaml:"downloads"`
19+
}
20+
21+
var (
22+
Config AppConfig
23+
)
24+
25+
func ParserSettings() error {
26+
data, err := os.ReadFile("config.yaml")
27+
if err != nil {
28+
return err
29+
}
30+
31+
err = yaml.Unmarshal(data, &Config)
32+
if err != nil {
33+
return err
34+
}
35+
36+
return nil
37+
}
38+
39+
func SaveSettings() error {
40+
Config.LastDownloadTime = time.Now()
41+
42+
data, err := yaml.Marshal(&Config)
43+
if err != nil {
44+
return err
45+
}
46+
47+
err = os.WriteFile("config.yaml", data, os.ModePerm)
48+
if err != nil {
49+
return err
50+
}
51+
52+
return nil
53+
}

0 commit comments

Comments
 (0)