Skip to content

Commit a17b3dc

Browse files
feat(strm_driver): add strm driver (#410)
* feat(strm_driver): add strm driver * chore(strm_driver): get api_url from context * 优化代码 * chore(strm_driver): update package name --------- Co-authored-by: j2rong4cn <j2rong@qq.com>
1 parent 022614f commit a17b3dc

File tree

6 files changed

+377
-1
lines changed

6 files changed

+377
-1
lines changed

drivers/all.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import (
5757
_ "github.com/OpenListTeam/OpenList/v4/drivers/seafile"
5858
_ "github.com/OpenListTeam/OpenList/v4/drivers/sftp"
5959
_ "github.com/OpenListTeam/OpenList/v4/drivers/smb"
60+
_ "github.com/OpenListTeam/OpenList/v4/drivers/strm"
6061
_ "github.com/OpenListTeam/OpenList/v4/drivers/teambition"
6162
_ "github.com/OpenListTeam/OpenList/v4/drivers/terabox"
6263
_ "github.com/OpenListTeam/OpenList/v4/drivers/thunder"

drivers/strm/driver.go

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package strm
2+
3+
import (
4+
"context"
5+
"errors"
6+
"strings"
7+
8+
"github.com/OpenListTeam/OpenList/v4/internal/driver"
9+
"github.com/OpenListTeam/OpenList/v4/internal/errs"
10+
"github.com/OpenListTeam/OpenList/v4/internal/fs"
11+
"github.com/OpenListTeam/OpenList/v4/internal/model"
12+
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
13+
)
14+
15+
type Strm struct {
16+
model.Storage
17+
Addition
18+
pathMap map[string][]string
19+
autoFlatten bool
20+
oneKey string
21+
}
22+
23+
func (d *Strm) Config() driver.Config {
24+
return config
25+
}
26+
27+
func (d *Strm) GetAddition() driver.Additional {
28+
return &d.Addition
29+
}
30+
31+
func (d *Strm) Init(ctx context.Context) error {
32+
if d.Paths == "" {
33+
return errors.New("paths is required")
34+
}
35+
d.pathMap = make(map[string][]string)
36+
for _, path := range strings.Split(d.Paths, "\n") {
37+
path = strings.TrimSpace(path)
38+
if path == "" {
39+
continue
40+
}
41+
k, v := getPair(path)
42+
d.pathMap[k] = append(d.pathMap[k], v)
43+
}
44+
if len(d.pathMap) == 1 {
45+
for k := range d.pathMap {
46+
d.oneKey = k
47+
}
48+
d.autoFlatten = true
49+
} else {
50+
d.oneKey = ""
51+
d.autoFlatten = false
52+
}
53+
54+
if d.FilterFileTypes != "" {
55+
types := strings.Split(d.FilterFileTypes, ",")
56+
for _, ext := range types {
57+
ext = strings.ToLower(strings.TrimSpace(ext))
58+
if ext != "" {
59+
supportSuffix[ext] = struct{}{}
60+
}
61+
}
62+
}
63+
return nil
64+
}
65+
66+
func (d *Strm) Drop(ctx context.Context) error {
67+
d.pathMap = nil
68+
return nil
69+
}
70+
71+
func (d *Strm) Get(ctx context.Context, path string) (model.Obj, error) {
72+
if utils.PathEqual(path, "/") {
73+
return &model.Object{
74+
Name: "Root",
75+
IsFolder: true,
76+
Path: "/",
77+
}, nil
78+
}
79+
root, sub := d.getRootAndPath(path)
80+
dsts, ok := d.pathMap[root]
81+
if !ok {
82+
return nil, errs.ObjectNotFound
83+
}
84+
for _, dst := range dsts {
85+
obj, err := d.get(ctx, path, dst, sub)
86+
if err == nil {
87+
return obj, nil
88+
}
89+
}
90+
return nil, errs.ObjectNotFound
91+
}
92+
93+
func (d *Strm) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
94+
path := dir.GetPath()
95+
if utils.PathEqual(path, "/") && !d.autoFlatten {
96+
return d.listRoot(), nil
97+
}
98+
root, sub := d.getRootAndPath(path)
99+
dsts, ok := d.pathMap[root]
100+
if !ok {
101+
return nil, errs.ObjectNotFound
102+
}
103+
var objs []model.Obj
104+
fsArgs := &fs.ListArgs{NoLog: true, Refresh: args.Refresh}
105+
for _, dst := range dsts {
106+
tmp, err := d.list(ctx, dst, sub, fsArgs)
107+
if err == nil {
108+
objs = append(objs, tmp...)
109+
}
110+
}
111+
return objs, nil
112+
}
113+
114+
func (d *Strm) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
115+
root, sub := d.getRootAndPath(file.GetPath())
116+
dsts, ok := d.pathMap[root]
117+
if !ok {
118+
return nil, errs.ObjectNotFound
119+
}
120+
for _, dst := range dsts {
121+
link, err := d.link(ctx, dst, sub)
122+
if err == nil {
123+
return link, nil
124+
}
125+
}
126+
return nil, errs.ObjectNotFound
127+
}
128+
129+
func (d *Strm) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
130+
return errors.New("strm Driver cannot make dir")
131+
}
132+
133+
func (d *Strm) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
134+
return errors.New("strm Driver cannot move file")
135+
}
136+
137+
func (d *Strm) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
138+
return errors.New("strm Driver cannot rename file")
139+
}
140+
141+
func (d *Strm) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
142+
return errors.New("strm Driver cannot copy file")
143+
}
144+
145+
func (d *Strm) Remove(ctx context.Context, obj model.Obj) error {
146+
return errors.New("strm Driver cannot remove file")
147+
}
148+
149+
func (d *Strm) Put(ctx context.Context, dstDir model.Obj, s model.FileStreamer, up driver.UpdateProgress) error {
150+
return errors.New("strm Driver cannot put file")
151+
}
152+
153+
func (d *Strm) PutURL(ctx context.Context, dstDir model.Obj, name, url string) error {
154+
return errors.New("strm Driver cannot put file")
155+
}
156+
157+
func (d *Strm) GetArchiveMeta(ctx context.Context, obj model.Obj, args model.ArchiveArgs) (model.ArchiveMeta, error) {
158+
return nil, errs.NotImplement
159+
}
160+
161+
func (d *Strm) ListArchive(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) ([]model.Obj, error) {
162+
return nil, errs.NotImplement
163+
}
164+
165+
func (d *Strm) Extract(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) (*model.Link, error) {
166+
return nil, errs.NotImplement
167+
}
168+
169+
func (d *Strm) ArchiveDecompress(ctx context.Context, srcObj, dstDir model.Obj, args model.ArchiveDecompressArgs) error {
170+
return errs.NotImplement
171+
}
172+
173+
var _ driver.Driver = (*Strm)(nil)

drivers/strm/meta.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package strm
2+
3+
import (
4+
"github.com/OpenListTeam/OpenList/v4/internal/driver"
5+
"github.com/OpenListTeam/OpenList/v4/internal/op"
6+
)
7+
8+
type Addition struct {
9+
Paths string `json:"paths" required:"true" type:"text"`
10+
ProtectSameName bool `json:"protect_same_name" default:"true" required:"false" help:"Protects same-name files from Delete or Rename"`
11+
SiteUrl string `json:"siteUrl" type:"text" required:"false" help:"The prefix URL of the strm file"`
12+
FilterFileTypes string `json:"filterFileTypes" type:"text" default:"strm" required:"false" help:"Supports suffix name of strm file"`
13+
}
14+
15+
var config = driver.Config{
16+
Name: "Strm",
17+
LocalSort: true,
18+
NoCache: true,
19+
NoUpload: true,
20+
DefaultRoot: "/",
21+
OnlyLocal: true,
22+
OnlyProxy: true,
23+
}
24+
25+
func init() {
26+
op.RegisterDriver(func() driver.Driver {
27+
return &Strm{}
28+
})
29+
}

drivers/strm/types.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package strm
2+
3+
var supportSuffix = map[string]struct{}{
4+
// video
5+
"mp4": {},
6+
"mkv": {},
7+
"flv": {},
8+
"avi": {},
9+
"wmv": {},
10+
"ts": {},
11+
"rmvb": {},
12+
"webm": {},
13+
// audio
14+
"mp3": {},
15+
"flac": {},
16+
"aac": {},
17+
"wav": {},
18+
"ogg": {},
19+
"m4a": {},
20+
"wma": {},
21+
"alac": {},
22+
}

drivers/strm/util.go

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package strm
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
stdpath "path"
8+
"strings"
9+
10+
"github.com/OpenListTeam/OpenList/v4/internal/fs"
11+
"github.com/OpenListTeam/OpenList/v4/internal/model"
12+
"github.com/OpenListTeam/OpenList/v4/internal/sign"
13+
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
14+
"github.com/OpenListTeam/OpenList/v4/server/common"
15+
)
16+
17+
func (d *Strm) listRoot() []model.Obj {
18+
var objs []model.Obj
19+
for k := range d.pathMap {
20+
obj := model.Object{
21+
Name: k,
22+
IsFolder: true,
23+
Modified: d.Modified,
24+
}
25+
objs = append(objs, &obj)
26+
}
27+
return objs
28+
}
29+
30+
// do others that not defined in Driver interface
31+
func getPair(path string) (string, string) {
32+
//path = strings.TrimSpace(path)
33+
if strings.Contains(path, ":") {
34+
pair := strings.SplitN(path, ":", 2)
35+
if !strings.Contains(pair[0], "/") {
36+
return pair[0], pair[1]
37+
}
38+
}
39+
return stdpath.Base(path), path
40+
}
41+
42+
func (d *Strm) getRootAndPath(path string) (string, string) {
43+
if d.autoFlatten {
44+
return d.oneKey, path
45+
}
46+
path = strings.TrimPrefix(path, "/")
47+
parts := strings.SplitN(path, "/", 2)
48+
if len(parts) == 1 {
49+
return parts[0], ""
50+
}
51+
return parts[0], parts[1]
52+
}
53+
54+
func (d *Strm) get(ctx context.Context, path string, dst, sub string) (model.Obj, error) {
55+
reqPath := stdpath.Join(dst, sub)
56+
obj, err := fs.Get(ctx, reqPath, &fs.GetArgs{NoLog: true})
57+
if err != nil {
58+
return nil, err
59+
}
60+
size := int64(0)
61+
if !obj.IsDir() {
62+
if utils.Ext(obj.GetName()) == "strm" {
63+
size = obj.GetSize()
64+
} else {
65+
file := stdpath.Join(reqPath, obj.GetName())
66+
size = int64(len(d.getLink(ctx, file)))
67+
}
68+
}
69+
return &model.Object{
70+
Path: path,
71+
Name: obj.GetName(),
72+
Size: size,
73+
Modified: obj.ModTime(),
74+
IsFolder: obj.IsDir(),
75+
HashInfo: obj.GetHash(),
76+
}, nil
77+
}
78+
79+
func (d *Strm) list(ctx context.Context, dst, sub string, args *fs.ListArgs) ([]model.Obj, error) {
80+
reqPath := stdpath.Join(dst, sub)
81+
objs, err := fs.List(ctx, reqPath, args)
82+
if err != nil {
83+
return nil, err
84+
}
85+
86+
var validObjs []model.Obj
87+
for _, obj := range objs {
88+
if !obj.IsDir() {
89+
ext := strings.ToLower(utils.Ext(obj.GetName()))
90+
if _, ok := supportSuffix[ext]; !ok {
91+
continue
92+
}
93+
}
94+
validObjs = append(validObjs, obj)
95+
}
96+
return utils.SliceConvert(validObjs, func(obj model.Obj) (model.Obj, error) {
97+
name := obj.GetName()
98+
size := int64(0)
99+
if !obj.IsDir() {
100+
ext := utils.Ext(name)
101+
name = strings.TrimSuffix(name, ext) + "strm"
102+
if ext == "strm" {
103+
size = obj.GetSize()
104+
} else {
105+
file := stdpath.Join(reqPath, obj.GetName())
106+
size = int64(len(d.getLink(ctx, file)))
107+
}
108+
}
109+
objRes := model.Object{
110+
Name: name,
111+
Size: size,
112+
Modified: obj.ModTime(),
113+
IsFolder: obj.IsDir(),
114+
Path: stdpath.Join(sub, obj.GetName()),
115+
}
116+
thumb, ok := model.GetThumb(obj)
117+
if !ok {
118+
return &objRes, nil
119+
}
120+
return &model.ObjThumb{
121+
Object: objRes,
122+
Thumbnail: model.Thumbnail{
123+
Thumbnail: thumb,
124+
},
125+
}, nil
126+
})
127+
}
128+
129+
func (d *Strm) link(ctx context.Context, dst, sub string) (*model.Link, error) {
130+
reqPath := stdpath.Join(dst, sub)
131+
_, err := fs.Get(ctx, reqPath, &fs.GetArgs{NoLog: true})
132+
if err != nil {
133+
return nil, err
134+
}
135+
return &model.Link{
136+
MFile: model.NewNopMFile(strings.NewReader(d.getLink(ctx, reqPath))),
137+
}, nil
138+
}
139+
140+
func (d *Strm) getLink(ctx context.Context, path string) string {
141+
apiUrl := d.SiteUrl
142+
if len(apiUrl) > 0 {
143+
apiUrl = strings.TrimSuffix(apiUrl, "/")
144+
} else {
145+
apiUrl = common.GetApiUrl(ctx)
146+
}
147+
return fmt.Sprintf("%s/d%s?sign=%s",
148+
apiUrl,
149+
utils.EncodePath(path, true),
150+
sign.Sign(path))
151+
}

internal/bootstrap/data/setting.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func InitialSettings() []model.SettingItem {
111111
{Key: "home_container", Value: "max_980px", Type: conf.TypeSelect, Options: "max_980px,hope_container", Group: model.STYLE},
112112
{Key: "settings_layout", Value: "list", Type: conf.TypeSelect, Options: "list,responsive", Group: model.STYLE},
113113
// preview settings
114-
{Key: conf.TextTypes, Value: "txt,htm,html,xml,java,properties,sql,js,md,json,conf,ini,vue,php,py,bat,gitignore,yml,go,sh,c,cpp,h,hpp,tsx,vtt,srt,ass,rs,lrc", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
114+
{Key: conf.TextTypes, Value: "txt,htm,html,xml,java,properties,sql,js,md,json,conf,ini,vue,php,py,bat,gitignore,yml,go,sh,c,cpp,h,hpp,tsx,vtt,srt,ass,rs,lrc,strm", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
115115
{Key: conf.AudioTypes, Value: "mp3,flac,ogg,m4a,wav,opus,wma", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
116116
{Key: conf.VideoTypes, Value: "mp4,mkv,avi,mov,rmvb,webm,flv,m3u8", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},
117117
{Key: conf.ImageTypes, Value: "jpg,tiff,jpeg,png,gif,bmp,svg,ico,swf,webp,avif", Type: conf.TypeText, Group: model.PREVIEW, Flag: model.PRIVATE},

0 commit comments

Comments
 (0)