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
|
package parser
import (
"bytes"
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"text/template"
)
var (
moviePattern = regexp.MustCompile(`(.*?)\.(\d{4})`)
episodePatterns = [4]*regexp.Regexp{
regexp.MustCompile(`^(?P<name>.+?)\.S(?P<season>\d{2})(?:E(?P<episode>\d{2}))?`), // S01, S01E04
regexp.MustCompile(`^(?P<name>.+?)\.E(?P<episode>\d{2})`), // E04
regexp.MustCompile(`^(?P<name>.+?)\.(?P<season>\d{1,2})x(?P<episode>\d{2})`), // 1x04, 01x04
regexp.MustCompile(`^(?P<name>.+?)\.Part\.?(?P<episode>\d{1,2})`), // Part4, Part11, Part.4, Part.11
}
)
type Parser func(s string) (Media, error)
type Media struct {
Release string
Name string
Year int
Season int
Episode int
}
func (m *Media) IsEmpty() bool {
return m.Name == ""
}
func (m *Media) ReplaceName(re *regexp.Regexp, repl string) {
m.Name = re.ReplaceAllString(m.Name, repl)
}
func (m *Media) Equal(o Media) bool {
if m.IsEmpty() {
return false
}
return m.Name == o.Name && m.Season == o.Season && m.Episode == o.Episode && m.Year == o.Year
}
func (m *Media) PathIn(dir *template.Template) (string, error) {
var b bytes.Buffer
if err := dir.Execute(&b, m); err != nil {
return "", err
}
path := b.String()
// When path has a trailing slash, the actual destination path will be a directory inside LocalPath (same
// behaviour as rsync)
if strings.HasSuffix(path, string(os.PathSeparator)) {
path = filepath.Join(path, m.Release)
}
return path, nil
}
func Default(s string) (Media, error) {
return Media{Release: s}, nil
}
func Movie(s string) (Media, error) {
matches := moviePattern.FindStringSubmatch(s)
if len(matches) < 3 {
return Media{}, fmt.Errorf("invalid input: %q", s)
}
name := matches[1]
year, err := strconv.Atoi(matches[2])
if err != nil {
return Media{}, fmt.Errorf("invalid input: %q: %s", s, err)
}
return Media{
Release: s,
Name: name,
Year: year,
}, nil
}
func Show(s string) (Media, error) {
for _, p := range episodePatterns {
matches := p.FindStringSubmatch(s)
if len(matches) == 0 {
continue
}
groupNames := p.SubexpNames()
var (
name string
season = 1
episode = 0
err error
)
for i, group := range matches {
if group == "" {
continue
}
switch groupNames[i] {
case "name":
name = group
case "season":
season, err = strconv.Atoi(group)
if err != nil {
return Media{}, fmt.Errorf("invalid input: %q: %s", s, err)
}
case "episode":
episode, err = strconv.Atoi(group)
if err != nil {
return Media{}, fmt.Errorf("invalid input: %q: %s", s, err)
}
}
}
return Media{
Release: s,
Name: name,
Season: season,
Episode: episode,
}, nil
}
return Media{}, fmt.Errorf("invalid input: %q", s)
}
|