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
|
package media
import (
"errors"
"fmt"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
"strings"
"sync"
)
// Library manages importing and retrieving video data.
type Library struct {
mu sync.RWMutex
Paths map[string]*Path
Videos map[string]*Video
}
// NewLibrary returns new instance of Library.
func NewLibrary() *Library {
lib := &Library{
Paths: make(map[string]*Path),
Videos: make(map[string]*Video),
}
return lib
}
// AddPath adds a media path to the library.
func (lib *Library) AddPath(p *Path) error {
lib.mu.Lock()
defer lib.mu.Unlock()
// make sure new path doesn't collide with existing ones
for _, p2 := range lib.Paths {
if p.Path == p2.Path {
return errors.New("media: duplicate library path")
}
if p.Prefix == p2.Prefix {
return errors.New("media: duplicate library prefix")
}
}
if err := os.MkdirAll(p.Path, 0755); err != nil {
return fmt.Errorf("error creating library path %s: %w", p.Path, err)
}
lib.Paths[p.Path] = p
return nil
}
// Import adds all valid videos from a given path.
func (lib *Library) Import(p *Path) error {
files, err := ioutil.ReadDir(p.Path)
if err != nil {
return err
}
for _, info := range files {
if strings.ContainsAny(info.Name(), "#") {
// ignore resized videos e.g: #240p.mp4
continue
}
err = lib.Add(path.Join(p.Path, info.Name()))
if err != nil {
// Ignore files that can't be parsed
continue
}
}
return nil
}
// Add adds a single video from a given file path.
func (lib *Library) Add(fp string) error {
lib.mu.Lock()
defer lib.mu.Unlock()
fp = filepath.ToSlash(fp)
d := path.Dir(fp)
p, ok := lib.Paths[d]
if !ok {
return errors.New("media: path not found")
}
n := path.Base(fp)
v, err := ParseVideo(p, n)
if err != nil {
return err
}
lib.Videos[v.ID] = v
log.Println("Added:", v.Path)
return nil
}
// Remove removes a single video from a given file path.
func (lib *Library) Remove(fp string) {
lib.mu.Lock()
defer lib.mu.Unlock()
fp = filepath.ToSlash(fp)
d := path.Dir(fp)
p, ok := lib.Paths[d]
if !ok {
return
}
n := path.Base(fp)
// ID is name without extension
idx := strings.LastIndex(n, ".")
if idx == -1 {
idx = len(n)
}
id := n[:idx]
if len(p.Prefix) > 0 {
id = path.Join(p.Prefix, id)
}
v, ok := lib.Videos[id]
if ok {
delete(lib.Videos, id)
log.Println("Removed:", v.Path)
}
}
// Playlist returns a sorted Playlist of all videos.
func (lib *Library) Playlist() Playlist {
lib.mu.RLock()
defer lib.mu.RUnlock()
pl := make(Playlist, len(lib.Videos))
i := 0
for _, v := range lib.Videos {
pl[i] = v
i++
}
By(SortByTimestamp).Sort(pl)
return pl
}
|