-
Notifications
You must be signed in to change notification settings - Fork 0
/
iteminfo.go
126 lines (114 loc) · 3.6 KB
/
iteminfo.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
// Copyright 2019 by Chris Palmer (https://noncombatant.org)
// SPDX-License-Identifier: GPL-3.0
package main
import (
"id3"
"net/url"
"path/filepath"
"regexp"
"strings"
)
type ItemInfo struct {
Pathname string `json:"pathname"`
Album string `json:"album"`
Artist string `json:"artist"`
Name string `json:"name"`
Disc string `json:"disc"`
Track string `json:"track"`
Year string `json:"year"`
Genre string `json:"genre"`
NormalizedPathname string `json:"-"`
NormalizedAlbum string `json:"-"`
NormalizedArtist string `json:"-"`
NormalizedName string `json:"-"`
NormalizedDisc string `json:"-"`
NormalizedTrack string `json:"-"`
NormalizedYear string `json:"-"`
NormalizedGenre string `json:"-"`
ModTime string `json:"-"`
File *id3.File `json:"-"`
}
type ItemInfos []ItemInfo
// This terrible hack is an alternative to separately `url.PathEscape`ing each
// pathname component and then re-joining them. That would be conceptually
// better but this is expedient.
func pathnameEscape(pathname string) string {
// `PathEscape` uses capital hex, hence "%2F".
return strings.ReplaceAll(url.PathEscape(pathname), "%2F", "/")
}
var (
discTrackAndNameMatcher = regexp.MustCompile(`^\s*(\d*)?-?(\d*)?\s+(.*)$`)
)
func getDiscTrackAndNameFromBasename(basename string) (string, string, string) {
submatches := discTrackAndNameMatcher.FindSubmatch([]byte(basename))
if len(submatches) != 4 {
return "", "", basename
}
if len(submatches[1]) > 0 && len(submatches[2]) == 0 {
return "", string(submatches[1]), string(submatches[3])
}
return string(submatches[1]), string(submatches[2]), string(submatches[3])
}
// Sets fields of `i` from `i.Pathname`, assuming the format:
//
// ".../AC_DC/Back In Black/1-01 Hells Bells.m4a"
// performer/album/disc#-track# name
func (i *ItemInfo) fillMetadataFromPathname() {
parts := strings.Split(i.Pathname, string(filepath.Separator))
length := len(parts)
if length > 2 {
i.Artist = parts[length-3]
}
if length > 1 {
i.Album = parts[length-2]
}
if length > 0 {
i.Disc, i.Track, i.Name = getDiscTrackAndNameFromBasename(parts[length-1])
i.Name = removeBasenameExtension(i.Name)
}
}
func (i *ItemInfo) fillMetadata() {
i.fillMetadataFromPathname()
if i.File != nil {
i.File.Album = strings.TrimSpace(i.File.Album)
if i.File.Album != "" {
i.Album = i.File.Album
}
i.File.Artist = strings.TrimSpace(i.File.Artist)
if i.File.Artist != "" {
i.Artist = i.File.Artist
}
i.File.Name = strings.TrimSpace(i.File.Name)
if i.File.Name != "" {
i.Name = i.File.Name
}
i.File.Disc = strings.TrimSpace(i.File.Disc)
if i.File.Disc != "" {
i.Disc = i.File.Disc
}
i.File.Track = strings.TrimSpace(i.File.Track)
if i.File.Track != "" {
i.Track = i.File.Track
}
i.File.Year = strings.TrimSpace(i.File.Year)
if i.File.Year != "" {
i.Year = i.File.Year
}
i.File.Genre = strings.TrimSpace(i.File.Genre)
if i.File.Genre != "" {
i.Genre = i.File.Genre
}
}
i.Pathname = pathnameEscape(i.Pathname)
i.normalize()
}
func (i *ItemInfo) normalize() {
i.NormalizedPathname = normalizeStringForSearch(i.Pathname)
i.NormalizedAlbum = normalizeStringForSearch(i.Album)
i.NormalizedArtist = normalizeStringForSearch(i.Artist)
i.NormalizedName = normalizeStringForSearch(i.Name)
i.NormalizedDisc = extractDigits(i.Disc)
i.NormalizedTrack = extractDigits(i.Track)
i.NormalizedYear = extractDigits(i.Year)
i.NormalizedGenre = normalizeStringForSearch(i.Genre)
}