From db509ec0211749555e3e423b5ab6caa6e10982fc Mon Sep 17 00:00:00 2001 From: kanoshiou Date: Sun, 9 Aug 2026 00:28:27 +0800 Subject: [PATCH 1/2] feat(strm): add file size filtering for STRM generation - add ParseSize helper function in pkg/utils to parse human-readable byte sizes - add minFileSize setting field to STRM driver Addition struct - filter out files below minFileSize threshold during STRM generation --- drivers/strm/driver.go | 13 ++++++++++++ drivers/strm/meta.go | 1 + drivers/strm/util.go | 3 +++ pkg/utils/file.go | 46 ++++++++++++++++++++++++++++++++++++++++++ pkg/utils/file_test.go | 45 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+) diff --git a/drivers/strm/driver.go b/drivers/strm/driver.go index 1189fd9a7..8eff97aca 100644 --- a/drivers/strm/driver.go +++ b/drivers/strm/driver.go @@ -27,6 +27,7 @@ type Strm struct { supportSuffix map[string]struct{} downloadSuffix map[string]struct{} + minSizeBytes int64 } func (d *Strm) Config() driver.Config { @@ -120,6 +121,18 @@ func (d *Strm) Init(ctx context.Context) error { if len(d.SaveLocalMode) == 0 { d.SaveLocalMode = SaveLocalInsertMode } + d.MinFileSize = strings.TrimSpace(d.MinFileSize) + if d.MinFileSize != "" { + minSize, err := utils.ParseSize(d.MinFileSize) + if err != nil { + log.Warnf("failed to parse minFileSize %s: %v", d.MinFileSize, err) + d.minSizeBytes = 0 + } else { + d.minSizeBytes = minSize + } + } else { + d.minSizeBytes = 0 + } return nil } diff --git a/drivers/strm/meta.go b/drivers/strm/meta.go index 843ff7c66..d28f1c2d9 100644 --- a/drivers/strm/meta.go +++ b/drivers/strm/meta.go @@ -17,6 +17,7 @@ type Addition struct { PathPrefix string `json:"PathPrefix" type:"text" required:"false" default:"/d" help:"Path prefix"` DownloadFileTypes string `json:"downloadFileTypes" type:"text" default:"ass,srt,vtt,sub,strm" required:"false" help:"Files need to download with strm (usally subtitles)"` FilterFileTypes string `json:"filterFileTypes" type:"text" default:"mp4,mkv,flv,avi,wmv,ts,rmvb,webm,mp3,flac,aac,wav,ogg,m4a,wma,alac" required:"false" help:"Supports suffix name of strm file"` + MinFileSize string `json:"minFileSize" type:"text" required:"false" help:"Filter out files smaller than this size (e.g. 100MB, 10KB, 10G)"` EncodePath bool `json:"encodePath" default:"true" required:"true" help:"encode the path in the strm file"` WithoutUrl bool `json:"withoutUrl" default:"false" help:"strm file content without URL prefix"` WithSign bool `json:"withSign" default:"false"` diff --git a/drivers/strm/util.go b/drivers/strm/util.go index b9a40da5e..7dfc7dbee 100644 --- a/drivers/strm/util.go +++ b/drivers/strm/util.go @@ -73,6 +73,9 @@ func (d *Strm) convert2strmObjs(ctx context.Context, reqPath string, objs []mode if _, ok := d.downloadSuffix[ext]; ok { size = obj.GetSize() } else if _, ok := d.supportSuffix[ext]; ok { + if d.minSizeBytes > 0 && obj.GetSize() < d.minSizeBytes { + continue + } id = "strm" name = strings.TrimSuffix(name, sourceExt) + "strm" size = int64(len(d.getLink(ctx, path))) diff --git a/pkg/utils/file.go b/pkg/utils/file.go index 4fa58d397..539ac15c8 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -7,6 +7,7 @@ import ( "os" "path" "path/filepath" + "strconv" "strings" "github.com/OpenListTeam/OpenList/v4/internal/errs" @@ -202,3 +203,48 @@ func IsSystemFile(filename string) bool { return false } + +// ParseSize parses human-readable file size strings like "100MB", "10KB", "10G", "1.5GB", "500B", "500" into bytes. +func ParseSize(s string) (int64, error) { + s = strings.TrimSpace(s) + if s == "" { + return 0, nil + } + s = strings.ToUpper(s) + + i := 0 + for i < len(s) && ((s[i] >= '0' && s[i] <= '9') || s[i] == '.') { + i++ + } + numStr := strings.TrimSpace(s[:i]) + unitStr := strings.TrimSpace(s[i:]) + + if numStr == "" { + return 0, fmt.Errorf("invalid size format: %s", s) + } + + val, err := strconv.ParseFloat(numStr, 64) + if err != nil || val < 0 { + return 0, fmt.Errorf("invalid size value: %s", numStr) + } + + var multiplier float64 = 1 + switch unitStr { + case "", "B", "BYTE", "BYTES": + multiplier = 1 + case "K", "KB", "KIB": + multiplier = float64(KB) + case "M", "MB", "MIB": + multiplier = float64(MB) + case "G", "GB", "GIB": + multiplier = float64(GB) + case "T", "TB", "TIB": + multiplier = float64(TB) + case "P", "PB", "PIB": + multiplier = float64(1 << 50) + default: + return 0, fmt.Errorf("unknown unit in size format: %s", unitStr) + } + + return int64(val * multiplier), nil +} diff --git a/pkg/utils/file_test.go b/pkg/utils/file_test.go index bcfff7934..d2d881a8f 100644 --- a/pkg/utils/file_test.go +++ b/pkg/utils/file_test.go @@ -41,3 +41,48 @@ func TestIsSystemFile(t *testing.T) { }) } } + +func TestParseSize(t *testing.T) { + testCases := []struct { + input string + expected int64 + expectError bool + }{ + {"", 0, false}, + {" ", 0, false}, + {"0", 0, false}, + {"100", 100, false}, + {"500B", 500, false}, + {"10Kb", 10 * 1024, false}, + {"10KB", 10 * 1024, false}, + {"10KiB", 10 * 1024, false}, + {"100MB", 100 * 1024 * 1024, false}, + {"100mb", 100 * 1024 * 1024, false}, + {"10G", 10 * 1024 * 1024 * 1024, false}, + {"10GB", 10 * 1024 * 1024 * 1024, false}, + {"1.5GB", int64(1.5 * 1024 * 1024 * 1024), false}, + {"1TB", 1 * 1024 * 1024 * 1024 * 1024, false}, + {" 100 MB ", 100 * 1024 * 1024, false}, + {"invalid", 0, true}, + {"100XYZ", 0, true}, + {"-50MB", 0, true}, + } + + for _, tc := range testCases { + t.Run(tc.input, func(t *testing.T) { + res, err := ParseSize(tc.input) + if tc.expectError { + if err == nil { + t.Errorf("ParseSize(%q) expected error, got nil", tc.input) + } + } else { + if err != nil { + t.Errorf("ParseSize(%q) unexpected error: %v", tc.input, err) + } + if res != tc.expected { + t.Errorf("ParseSize(%q) = %d, want %d", tc.input, res, tc.expected) + } + } + }) + } +} From 1ccb8448b7264ae640ca7b8778f16bf4555ac1e1 Mon Sep 17 00:00:00 2001 From: kanoshiou Date: Tue, 11 Aug 2026 12:48:44 +0800 Subject: [PATCH 2/2] refactor(strm): use MB as default unit for minFileSize instead of ParseSize Replace string-based ParseSize auto-conversion with a simple int64 number field defaulting to MB, consistent with other drivers (Google Drive, Teldrive). Remove the now-unused ParseSize function and its tests. --- drivers/strm/driver.go | 13 +----------- drivers/strm/meta.go | 2 +- pkg/utils/file.go | 46 ------------------------------------------ pkg/utils/file_test.go | 45 ----------------------------------------- 4 files changed, 2 insertions(+), 104 deletions(-) diff --git a/drivers/strm/driver.go b/drivers/strm/driver.go index 8eff97aca..3d243c157 100644 --- a/drivers/strm/driver.go +++ b/drivers/strm/driver.go @@ -121,18 +121,7 @@ func (d *Strm) Init(ctx context.Context) error { if len(d.SaveLocalMode) == 0 { d.SaveLocalMode = SaveLocalInsertMode } - d.MinFileSize = strings.TrimSpace(d.MinFileSize) - if d.MinFileSize != "" { - minSize, err := utils.ParseSize(d.MinFileSize) - if err != nil { - log.Warnf("failed to parse minFileSize %s: %v", d.MinFileSize, err) - d.minSizeBytes = 0 - } else { - d.minSizeBytes = minSize - } - } else { - d.minSizeBytes = 0 - } + d.minSizeBytes = d.MinFileSize * 1024 * 1024 return nil } diff --git a/drivers/strm/meta.go b/drivers/strm/meta.go index d28f1c2d9..2e2e8a08e 100644 --- a/drivers/strm/meta.go +++ b/drivers/strm/meta.go @@ -17,7 +17,7 @@ type Addition struct { PathPrefix string `json:"PathPrefix" type:"text" required:"false" default:"/d" help:"Path prefix"` DownloadFileTypes string `json:"downloadFileTypes" type:"text" default:"ass,srt,vtt,sub,strm" required:"false" help:"Files need to download with strm (usally subtitles)"` FilterFileTypes string `json:"filterFileTypes" type:"text" default:"mp4,mkv,flv,avi,wmv,ts,rmvb,webm,mp3,flac,aac,wav,ogg,m4a,wma,alac" required:"false" help:"Supports suffix name of strm file"` - MinFileSize string `json:"minFileSize" type:"text" required:"false" help:"Filter out files smaller than this size (e.g. 100MB, 10KB, 10G)"` + MinFileSize int64 `json:"minFileSize" type:"number" required:"false" default:"0" help:"Filter out files smaller than this size (unit: MB, 0 to disable)"` EncodePath bool `json:"encodePath" default:"true" required:"true" help:"encode the path in the strm file"` WithoutUrl bool `json:"withoutUrl" default:"false" help:"strm file content without URL prefix"` WithSign bool `json:"withSign" default:"false"` diff --git a/pkg/utils/file.go b/pkg/utils/file.go index 539ac15c8..4fa58d397 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -7,7 +7,6 @@ import ( "os" "path" "path/filepath" - "strconv" "strings" "github.com/OpenListTeam/OpenList/v4/internal/errs" @@ -203,48 +202,3 @@ func IsSystemFile(filename string) bool { return false } - -// ParseSize parses human-readable file size strings like "100MB", "10KB", "10G", "1.5GB", "500B", "500" into bytes. -func ParseSize(s string) (int64, error) { - s = strings.TrimSpace(s) - if s == "" { - return 0, nil - } - s = strings.ToUpper(s) - - i := 0 - for i < len(s) && ((s[i] >= '0' && s[i] <= '9') || s[i] == '.') { - i++ - } - numStr := strings.TrimSpace(s[:i]) - unitStr := strings.TrimSpace(s[i:]) - - if numStr == "" { - return 0, fmt.Errorf("invalid size format: %s", s) - } - - val, err := strconv.ParseFloat(numStr, 64) - if err != nil || val < 0 { - return 0, fmt.Errorf("invalid size value: %s", numStr) - } - - var multiplier float64 = 1 - switch unitStr { - case "", "B", "BYTE", "BYTES": - multiplier = 1 - case "K", "KB", "KIB": - multiplier = float64(KB) - case "M", "MB", "MIB": - multiplier = float64(MB) - case "G", "GB", "GIB": - multiplier = float64(GB) - case "T", "TB", "TIB": - multiplier = float64(TB) - case "P", "PB", "PIB": - multiplier = float64(1 << 50) - default: - return 0, fmt.Errorf("unknown unit in size format: %s", unitStr) - } - - return int64(val * multiplier), nil -} diff --git a/pkg/utils/file_test.go b/pkg/utils/file_test.go index d2d881a8f..bcfff7934 100644 --- a/pkg/utils/file_test.go +++ b/pkg/utils/file_test.go @@ -41,48 +41,3 @@ func TestIsSystemFile(t *testing.T) { }) } } - -func TestParseSize(t *testing.T) { - testCases := []struct { - input string - expected int64 - expectError bool - }{ - {"", 0, false}, - {" ", 0, false}, - {"0", 0, false}, - {"100", 100, false}, - {"500B", 500, false}, - {"10Kb", 10 * 1024, false}, - {"10KB", 10 * 1024, false}, - {"10KiB", 10 * 1024, false}, - {"100MB", 100 * 1024 * 1024, false}, - {"100mb", 100 * 1024 * 1024, false}, - {"10G", 10 * 1024 * 1024 * 1024, false}, - {"10GB", 10 * 1024 * 1024 * 1024, false}, - {"1.5GB", int64(1.5 * 1024 * 1024 * 1024), false}, - {"1TB", 1 * 1024 * 1024 * 1024 * 1024, false}, - {" 100 MB ", 100 * 1024 * 1024, false}, - {"invalid", 0, true}, - {"100XYZ", 0, true}, - {"-50MB", 0, true}, - } - - for _, tc := range testCases { - t.Run(tc.input, func(t *testing.T) { - res, err := ParseSize(tc.input) - if tc.expectError { - if err == nil { - t.Errorf("ParseSize(%q) expected error, got nil", tc.input) - } - } else { - if err != nil { - t.Errorf("ParseSize(%q) unexpected error: %v", tc.input, err) - } - if res != tc.expected { - t.Errorf("ParseSize(%q) = %d, want %d", tc.input, res, tc.expected) - } - } - }) - } -}