-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
257 lines (217 loc) · 7.33 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/url"
"os"
"path/filepath"
"regexp"
"slices"
"strings"
"github.com/hekmon/transmissionrpc/v3"
)
type Config struct {
Username string `json:"username"`
Password string `json:"password"`
Host string `json:"host"`
Port int `json:"port"`
SeasonPathRegex string `json:"seasonPathRegex"`
EpisodeRegex []string `json:"episodeRegex"`
Ext []string `json:"ext"`
ExtSubs []string `json:"extSubs"`
LangRegex string `json:"langRegex"`
}
func main() {
// 获取可执行文件的路径
executablePath, err := os.Executable()
if err != nil {
log.Fatal("获取可执行文件路径失败:", err)
}
// 尝试从二进制文件所在的目录读取配置文件
configFilePath := filepath.Join(filepath.Dir(executablePath), "config.json")
if _, err := os.Stat(configFilePath); os.IsNotExist(err) {
// 如果二进制目录中没有配置文件,则尝试从当前工作目录读取
log.Printf("二进制目录中的配置文件不存在,尝试从当前工作目录读取")
configFilePath = "config.json"
} else if err != nil {
log.Fatal("检查配置文件时发生错误:", err)
}
configFile, err := os.ReadFile(configFilePath)
if err != nil {
log.Fatal("读取配置文件失败:", err)
}
var config Config
if err := json.Unmarshal(configFile, &config); err != nil {
log.Fatal("解析配置文件失败:", err)
}
// 尝试编译正则表达式
seasonPathRegex := regexp.MustCompile(config.SeasonPathRegex)
langRegex := regexp.MustCompile(config.LangRegex)
// 检查正则表达式是否有效
if seasonPathRegex == nil {
log.Fatal("季度路径正则表达式无效")
}
if len(config.EpisodeRegex) == 0 {
log.Fatal("集数正则表达式无效")
}
if langRegex == nil {
log.Fatal("多国语言正则表达式无效")
}
// 构建 Transmission RPC URL
endpoint, err := url.Parse(fmt.Sprintf("http://%s:%s@%s:%d/transmission/rpc",
config.Username,
config.Password,
config.Host,
config.Port))
if err != nil {
log.Fatal(err)
}
client, err := transmissionrpc.New(endpoint, nil)
if err != nil {
log.Fatal(err)
}
// 获取所有种子
torrents, err := client.TorrentGet(context.Background(), []string{"id", "hashString", "name", "labels", "downloadDir"}, nil)
if err != nil {
log.Fatal(err)
}
// 处理每个种子
for _, torrent := range torrents {
log.Printf("正在处理种子: %s", *torrent.Name)
// 检查是否包含 collection 标签
hasCollectionLabel := false
labelIndex := -1
for i, label := range torrent.Labels {
if strings.Contains(strings.ToLower(label), "collection") {
hasCollectionLabel = true
labelIndex = i
log.Printf("找到 collection 标签: %s", label)
break
}
}
if !hasCollectionLabel {
// log.Printf("跳过未标记 collection 的种子: %s", *torrent.Name)
continue
}
// 获取种子的详细信息,包括文件列表
torrentInfo, err := client.TorrentGet(context.Background(),
[]string{"id", "name", "files", "downloadDir", "wanted", "priorities"},
[]int64{*torrent.ID})
if err != nil {
log.Printf("获取种子详细信息失败: %v", err)
continue
}
if len(torrentInfo) == 0 {
log.Printf("未找到种子信息")
continue
}
currentTorrent := torrentInfo[0]
bgmName := *torrent.Name // 动漫名称
seasonNum := "01" // 默认为第一季
// 处理手动指定季数
if seasonMatch := seasonPathRegex.FindStringSubmatch(bgmName); seasonMatch != nil {
seasonNum = fmt.Sprintf("%02s", seasonMatch[1])
bgmName = strings.Replace(bgmName, seasonMatch[0], "", 1)
// 再去掉前后空格
bgmName = strings.TrimSpace(bgmName)
}
// 添加一个标志来跟踪是否所有文件都成功重命名
allFilesRenamed := true
// 处理种子中的每个文件
for i, file := range currentTorrent.Files {
// 检查文件是否被选择下载
if !currentTorrent.Wanted[i] {
// log.Printf("跳过未选择下载的文件: %s", file.Name)
continue
}
oldPath := file.Name // 文件相对下载目录的路径
oldPath = strings.ReplaceAll(oldPath, "\\", "/") // 确保使用正斜杠
oldBaseName := filepath.Base(oldPath) // 文件名
ext := filepath.Ext(oldPath) // 文件扩展名
log.Printf("处理文件: %s", oldPath)
// 如果文件路径包含的/超过两个,则跳过
if strings.Count(oldPath, "/") > 2 {
log.Printf("跳过多层文件夹: %s", oldPath)
continue
}
// 为每个文件提取季度信息
if seasonMatch := seasonPathRegex.FindStringSubmatch(oldPath); seasonMatch != nil {
seasonNum = fmt.Sprintf("%02s", seasonMatch[1])
// log.Printf("从文件路径中提取到季度: %s", seasonNum)
} else {
if strings.Count(oldPath, "/") > 1 {
log.Printf("未找到季度,跳过文件夹: %s", oldPath)
continue
}
}
// 如果文件不是视频文件或者字幕文件, 则跳过
if !slices.Contains(config.Ext, ext) && !slices.Contains(config.ExtSubs, ext) {
log.Printf("跳过非视频或字幕文件: %s", oldPath)
continue
}
// 从文件名中提取集数
var episodeNum string
for _, regex := range config.EpisodeRegex { // 遍历正则表达式数组
episodeMatch := regexp.MustCompile(regex).FindStringSubmatch(oldBaseName)
if len(episodeMatch) > 0 {
episodeNum = episodeMatch[1] // 使用捕获的集数
break // 找到后退出循环
}
}
if episodeNum == "" {
log.Printf("无法从文件名提取集数: %s", oldBaseName)
continue
}
// log.Printf("从文件名 %s 中提取到集数: %s", oldBaseName, episodeNum)
// 构建新文件名时保持相同的目录结构
// 如果文件是字幕文件, 则需要保留多语言后缀
lang := ""
if slices.Contains(config.ExtSubs, ext) {
oldBaseNameWithoutExt := oldBaseName[:len(oldBaseName)-len(ext)] // 去掉扩展名
if langMatch := langRegex.FindStringSubmatch(oldBaseNameWithoutExt); langMatch != nil {
lang = langMatch[0]
}
}
newBaseName := fmt.Sprintf("%s S%sE%s%s%s",
bgmName,
seasonNum,
fmt.Sprintf("%02s", episodeNum),
lang,
ext,
)
// 检查文件名是否相同
if oldBaseName == newBaseName {
log.Printf("文件名相同,跳过重命名")
continue
}
log.Printf("重命名文件: %s -> %s", oldBaseName, newBaseName)
// 只传入文件名进行重命名
// 这里前面传递的是路径,后面传递的是文件名
err = client.TorrentRenamePath(context.Background(), *currentTorrent.ID, oldPath, newBaseName)
if err != nil {
log.Printf("重命名文件失败: %v", err)
allFilesRenamed = false
continue
}
log.Printf("重命名文件成功")
}
// 只有当所有文件都成功重命名后才更新标签
if allFilesRenamed {
// 更新标签
newLabels := make([]string, len(torrent.Labels))
copy(newLabels, torrent.Labels)
newLabels[labelIndex] = "rename"
payload := transmissionrpc.TorrentSetPayload{
IDs: []int64{*torrent.ID},
Labels: newLabels,
}
if err := client.TorrentSet(context.Background(), payload); err != nil {
log.Printf("更新种子标签失败: %v", err)
continue
}
log.Printf("已更新种子 %s 的标签", *torrent.Name)
}
}
}