-
Notifications
You must be signed in to change notification settings - Fork 2
/
source_tags.go
212 lines (180 loc) · 5.46 KB
/
source_tags.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
// Copyright (C) 2020 David Vogel
//
// This file is part of Galago.
//
// Galago is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// Galago is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Galago. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"sort"
"github.com/Dadido3/configdb/tree"
)
func init() {
registerSourceType("tags", CreateSourceTags)
}
// SourceTags represents a source that gets all images from a list of elements, and shows them grouped by tags.
// The elements are referenced by their internal path.
//
// Hidden children will not be included.
// To include hidden containers, you need to specify their path explicitly.
type SourceTags struct {
parent Element
index int
name, urlName string
internalPaths []string
hidden bool
home bool
}
// Compile time check if SourceTags implements Element.
var _ Element = (*SourceTags)(nil)
// CreateSourceTags returns a new instance of the source.
func CreateSourceTags(parent Element, index int, urlName string, c tree.Node) (Element, error) {
var name string
if err := c.Get(".Name", &name); err != nil {
return nil, fmt.Errorf("Configuration of source %q errornous: %w", urlName, err)
}
hidden, _ := c["Hidden"].(bool)
home, _ := c["Home"].(bool)
var paths []string
if err := c.Get(".InternalPaths", &paths); err != nil {
return nil, fmt.Errorf("Configuration of source %q errornous: %w", urlName, err)
}
return &SourceTags{
parent: parent,
index: index,
name: name,
urlName: urlName,
internalPaths: paths,
hidden: hidden,
home: home,
}, nil
}
// Clone returns a clone with the given parent and index set
func (s *SourceTags) Clone(parent Element, index int) Element {
clone := *s
clone.parent = parent
clone.index = index
return &clone
}
// Parent returns the parent element, duh.
func (s *SourceTags) Parent() Element {
return s.parent
}
// Index returns the index of the element in its parent children list.
func (s *SourceTags) Index() int {
return s.index
}
// Children returns the folders and images of a source.
func (s *SourceTags) Children() ([]Element, error) {
tags := map[string][]Element{} // List of tags with their elements
visited := map[Element]struct{}{s: {}} // To prevent duplicates and recursion
var recursive func(e Element) error
recursive = func(e Element) error {
// Check for duplicates and prevent recursion
if _, ok := visited[e]; ok {
return nil
}
visited[e] = struct{}{}
// If the element is an image, add itself to the correct tag entries
if img, ok := e.(Image); ok {
ce, err := img.CacheEntry()
if err != nil {
return err
}
for _, tag := range ce.Tags {
if _, ok := tags[tag]; !ok {
tags[tag] = []Element{}
}
tags[tag] = append(tags[tag], e)
}
}
// Check children
children, err := e.Children()
if err != nil {
return err
}
for _, child := range FilterNonHidden(children) {
if err := recursive(child); err != nil {
return err
}
}
return nil
}
// Check all given internal paths recursively
for _, internalPath := range s.internalPaths {
element, err := RootElement.Traverse(internalPath)
if err != nil {
log.Warnf("Internal path %q not found: %v", internalPath, err)
continue
}
if err := recursive(element); err != nil {
return nil, err
}
}
tagsSorted := []string{}
for tagName := range tags {
tagsSorted = append(tagsSorted, tagName)
}
sort.Strings(tagsSorted)
sort.SliceStable(tagsSorted, func(i, j int) bool {
return len(tags[tagsSorted[i]]) > len(tags[tagsSorted[j]])
})
// Create albums by tag list
elements := []Element{}
for _, tagName := range tagsSorted {
album := &Album{
parent: s,
name: tagName,
urlName: tagName,
index: len(elements),
}
for _, tagElement := range tags[tagName] {
album.children = append(album.children, tagElement.Clone(album, len(album.children)))
}
elements = append(elements, album)
}
return elements, nil
}
// Path returns the absolute path of the element, but not the filesystem path.
// For details see ElementPath.
func (s *SourceTags) Path() string {
return ElementPath(s)
}
// IsContainer returns whether an element can contain other elements or not.
func (s *SourceTags) IsContainer() bool {
return true
}
// IsHidden returns whether this element can be listed as child or not.
func (s *SourceTags) IsHidden() bool {
return s.hidden
}
// IsHome returns whether an element should be linked by the home button or not.
func (s *SourceTags) IsHome() bool {
return s.home
}
// Name returns the name that is shown to the user.
func (s *SourceTags) Name() string {
return s.name
}
// URLName returns the name/identifier that is used in URLs.
func (s *SourceTags) URLName() string {
return s.urlName
}
// Traverse the element's children with the given path.
func (s *SourceTags) Traverse(path string) (Element, error) {
return TraverseElements(s, path)
}
func (s *SourceTags) String() string {
return fmt.Sprintf("{SourceTags %q: %v}", s.Path(), s.internalPaths)
}