Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/local/util.go
1987 views
1
package local
2
3
import (
4
"bytes"
5
"encoding/json"
6
"fmt"
7
"io/fs"
8
"os"
9
"path/filepath"
10
"runtime"
11
"sort"
12
"strconv"
13
"strings"
14
15
"github.com/alist-org/alist/v3/internal/conf"
16
"github.com/alist-org/alist/v3/internal/model"
17
"github.com/alist-org/alist/v3/pkg/utils"
18
"github.com/disintegration/imaging"
19
ffmpeg "github.com/u2takey/ffmpeg-go"
20
)
21
22
func isLinkedDir(f fs.FileInfo, path string) bool {
23
if f.Mode()&os.ModeSymlink == os.ModeSymlink || (runtime.GOOS == "windows" && f.Mode()&os.ModeIrregular != 0) {
24
dst, err := os.Readlink(path)
25
if err != nil {
26
return false
27
}
28
if !filepath.IsAbs(dst) {
29
dst = filepath.Join(filepath.Dir(path), dst)
30
}
31
dst, err = filepath.Abs(dst)
32
if err != nil {
33
return false
34
}
35
stat, err := os.Stat(dst)
36
if err != nil {
37
return false
38
}
39
return stat.IsDir()
40
}
41
return false
42
}
43
44
// Get the snapshot of the video
45
func (d *Local) GetSnapshot(videoPath string) (imgData *bytes.Buffer, err error) {
46
// Run ffprobe to get the video duration
47
jsonOutput, err := ffmpeg.Probe(videoPath)
48
if err != nil {
49
return nil, err
50
}
51
// get format.duration from the json string
52
type probeFormat struct {
53
Duration string `json:"duration"`
54
}
55
type probeData struct {
56
Format probeFormat `json:"format"`
57
}
58
var probe probeData
59
err = json.Unmarshal([]byte(jsonOutput), &probe)
60
if err != nil {
61
return nil, err
62
}
63
totalDuration, err := strconv.ParseFloat(probe.Format.Duration, 64)
64
if err != nil {
65
return nil, err
66
}
67
68
var ss string
69
if d.videoThumbPosIsPercentage {
70
ss = fmt.Sprintf("%f", totalDuration*d.videoThumbPos)
71
} else {
72
// If the value is greater than the total duration, use the total duration
73
if d.videoThumbPos > totalDuration {
74
ss = fmt.Sprintf("%f", totalDuration)
75
} else {
76
ss = fmt.Sprintf("%f", d.videoThumbPos)
77
}
78
}
79
80
// Run ffmpeg to get the snapshot
81
srcBuf := bytes.NewBuffer(nil)
82
// If the remaining time from the seek point to the end of the video is less
83
// than the duration of a single frame, ffmpeg cannot extract any frames
84
// within the specified range and will exit with an error.
85
// The "noaccurate_seek" option prevents this error and would also speed up
86
// the seek process.
87
stream := ffmpeg.Input(videoPath, ffmpeg.KwArgs{"ss": ss, "noaccurate_seek": ""}).
88
Output("pipe:", ffmpeg.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}).
89
GlobalArgs("-loglevel", "error").Silent(true).
90
WithOutput(srcBuf, os.Stdout)
91
if err = stream.Run(); err != nil {
92
return nil, err
93
}
94
return srcBuf, nil
95
}
96
97
func readDir(dirname string) ([]fs.FileInfo, error) {
98
f, err := os.Open(dirname)
99
if err != nil {
100
return nil, err
101
}
102
list, err := f.Readdir(-1)
103
f.Close()
104
if err != nil {
105
return nil, err
106
}
107
sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
108
return list, nil
109
}
110
111
func (d *Local) getThumb(file model.Obj) (*bytes.Buffer, *string, error) {
112
fullPath := file.GetPath()
113
thumbPrefix := "alist_thumb_"
114
thumbName := thumbPrefix + utils.GetMD5EncodeStr(fullPath) + ".png"
115
if d.ThumbCacheFolder != "" {
116
// skip if the file is a thumbnail
117
if strings.HasPrefix(file.GetName(), thumbPrefix) {
118
return nil, &fullPath, nil
119
}
120
thumbPath := filepath.Join(d.ThumbCacheFolder, thumbName)
121
if utils.Exists(thumbPath) {
122
return nil, &thumbPath, nil
123
}
124
}
125
var srcBuf *bytes.Buffer
126
if utils.GetFileType(file.GetName()) == conf.VIDEO {
127
videoBuf, err := d.GetSnapshot(fullPath)
128
if err != nil {
129
return nil, nil, err
130
}
131
srcBuf = videoBuf
132
} else {
133
imgData, err := os.ReadFile(fullPath)
134
if err != nil {
135
return nil, nil, err
136
}
137
imgBuf := bytes.NewBuffer(imgData)
138
srcBuf = imgBuf
139
}
140
141
image, err := imaging.Decode(srcBuf, imaging.AutoOrientation(true))
142
if err != nil {
143
return nil, nil, err
144
}
145
thumbImg := imaging.Resize(image, 144, 0, imaging.Lanczos)
146
var buf bytes.Buffer
147
err = imaging.Encode(&buf, thumbImg, imaging.PNG)
148
if err != nil {
149
return nil, nil, err
150
}
151
if d.ThumbCacheFolder != "" {
152
err = os.WriteFile(filepath.Join(d.ThumbCacheFolder, thumbName), buf.Bytes(), 0666)
153
if err != nil {
154
return nil, nil, err
155
}
156
}
157
return &buf, nil, nil
158
}
159
160