diff --git a/cmd/dl.go b/cmd/dl.go index 1c10a9c..b42d7d4 100644 --- a/cmd/dl.go +++ b/cmd/dl.go @@ -3,7 +3,6 @@ package cmd import ( "context" "fmt" - "strings" "github.com/gotd/td/telegram" "github.com/spf13/cobra" @@ -48,14 +47,7 @@ func NewDownload() *cobra.Command { cmd.Flags().StringSliceVarP(&opts.URLs, "url", "u", []string{}, "telegram message links") cmd.Flags().StringSliceVarP(&opts.Files, file, "f", []string{}, "official client exported files") - // generate default replacer - builder := strings.Builder{} - chars := []string{`/`, `\`, `:`, `*`, `?`, `<`, `>`, `|`, ` `} - for _, c := range chars { - builder.WriteString(fmt.Sprintf("`%s` `_` ", c)) - } - t := fmt.Sprintf(`{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileName %s }}`, builder.String()) - cmd.Flags().String(consts.FlagDlTemplate, t, "download file name template") + cmd.Flags().String(consts.FlagDlTemplate, `{{ .DialogID }}_{{ .MessageID }}_{{ filenamify .FileName }}`, "download file name template") cmd.Flags().StringSliceVarP(&opts.Include, include, "i", []string{}, "include the specified file extensions, and only judge by file name, not file MIME. Example: -i mp4,mp3") cmd.Flags().StringSliceVarP(&opts.Exclude, exclude, "e", []string{}, "exclude the specified file extensions, and only judge by file name, not file MIME. Example: -e png,jpg") diff --git a/docs/content/en/guide/template.md b/docs/content/en/guide/template.md index 66d30f5..b140ad0 100644 --- a/docs/content/en/guide/template.md +++ b/docs/content/en/guide/template.md @@ -26,23 +26,24 @@ Template syntax is based on [Go's text/template](https://golang.org/pkg/text/tem ### Functions (beta) -| Func | Desc | Usage | Example | -|:------------:|:------------------------------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:| -| `repeat` | Repeat `STRING` `N` times | `repeat STRING N` | `{{ repeat "test" 3 }}` | -| `replace` | Perform replacement on `STRING` with `PAIRS` | `replace STRING PAIRS...` | `{{ replace "Test" "t" "T" "e" "E" }}` | -| `upper` | Convert `STRING` to uppercase | `upper STRING` | `{{ upper "Test" }}` | -| `lower` | Convert `STRING` to lowercase | `lower STRING` | `{{ lower "Test" }}` | -| `snakecase` | Convert `STRING` to snake_case | `snakecase STRING` | `{{ snakecase "Test" }}` | -| `camelcase` | Convert `STRING` to camelCase | `camelcase STRING` | `{{ camelcase "Test" }}` | -| `kebabcase` | Convert `STRING` to kebab-case | `kebabcase STRING` | `{{ kebabcase "Test" }}` | -| `rand` | Generate random number in range `MIN` to `MAX` | `rand MIN MAX` | `{{ rand 1 10 }}` | -| `now` | Get current timestamp | `now` | `{{ now }}` | +| Func | Desc | Usage | Example | +|:------------:|:------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------:|:-------------------------------------------------------------------------------------:| +| `repeat` | Repeat `STRING` `N` times | `repeat STRING N` | `{{ repeat "test" 3 }}` | +| `replace` | Perform replacement on `STRING` with `PAIRS` | `replace STRING PAIRS...` | `{{ replace "Test" "t" "T" "e" "E" }}` | +| `upper` | Convert `STRING` to uppercase | `upper STRING` | `{{ upper "Test" }}` | +| `lower` | Convert `STRING` to lowercase | `lower STRING` | `{{ lower "Test" }}` | +| `snakecase` | Convert `STRING` to snake_case | `snakecase STRING` | `{{ snakecase "Test" }}` | +| `camelcase` | Convert `STRING` to camelCase | `camelcase STRING` | `{{ camelcase "Test" }}` | +| `kebabcase` | Convert `STRING` to kebab-case | `kebabcase STRING` | `{{ kebabcase "Test" }}` | +| `rand` | Generate random number in range `MIN` to `MAX` | `rand MIN MAX` | `{{ rand 1 10 }}` | +| `now` | Get current timestamp | `now` | `{{ now }}` | | `formatDate` | Format `TIMESTAMP` with [format](https://golang.cafe/blog/golang-time-format-example.html)
Default: `20060102150405` | `formatDate TIMESTAMP`
`formatDate TIMESTAMP "format"` | `{{ formatDate 1600000000 }}`
`{{ formatDate 1600000000 "2006-01-02-15-04-05"}}` | +| `filenamify` | Convert `STRING` to a valid filename with the best effort. | `filenamify STRING` | `{{ filenamify .FileName }}` | ### Examples: ```gotemplate -{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileName `/` `_` `\` `_` `:` `_` `*` `_` `?` `_` `<` `_` `>` `_` `|` `_` ` ` `_` }} +{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileCaption `/` `_` `\` `_` `:` `_` }} {{ .FileName }}_{{ formatDate .DownloadDate }}_{{ .FileSize }} @@ -56,5 +57,5 @@ Template syntax is based on [Go's text/template](https://golang.org/pkg/text/tem ### Default: ```gotemplate -{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileName `/` `_` `\` `_` `:` `_` `*` `_` `?` `_` `<` `_` `>` `_` `|` `_` ` ` `_` }} +{{ .DialogID }}_{{ .MessageID }}_{{ filenamify .FileName }} ``` diff --git a/docs/content/zh/guide/template.md b/docs/content/zh/guide/template.md index 9f4124a..64ce290 100644 --- a/docs/content/zh/guide/template.md +++ b/docs/content/zh/guide/template.md @@ -38,11 +38,12 @@ bookToC: false | `rand` | 在范围 `MIN` 到 `MAX` 生成随机数 | `rand MIN MAX` | `{{ rand 1 10 }}` | | `now` | 获取当前时间戳 | `now` | `{{ now }}` | | `formatDate` | [格式化](https://zhuanlan.zhihu.com/p/145009400) `TIMESTAMP` 时间戳
(默认格式: `20060102150405`) | `formatDate TIMESTAMP`
`formatDate TIMESTAMP "format"` | `{{ formatDate 1600000000 }}`
`{{ formatDate 1600000000 "2006-01-02-15-04-05"}}` | +| `filenamify` | 尽可能将 `STRING` 转换为合法文件名 | `filenamify STRING` | `{{ filenamify .FileName }}` | ### 示例: ```gotemplate -{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileName `/` `_` `\` `_` `:` `_` `*` `_` `?` `_` `<` `_` `>` `_` `|` `_` ` ` `_` }} +{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileCaption `/` `_` `\` `_` `:` `_` }} {{ .FileName }}_{{ formatDate .DownloadDate }}_{{ .FileSize }} @@ -56,5 +57,5 @@ bookToC: false ### 默认: ```gotemplate -{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileName `/` `_` `\` `_` `:` `_` `*` `_` `?` `_` `<` `_` `>` `_` `|` `_` ` ` `_` }} +{{ .DialogID }}_{{ .MessageID }}_{{ filenamify .FileName }} ``` diff --git a/go.mod b/go.mod index 497e1b5..73ba1eb 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/beevik/ntp v1.4.3 github.com/expr-lang/expr v1.16.9 github.com/fatih/color v1.18.0 + github.com/flytam/filenamify v1.2.0 github.com/gabriel-vasile/mimetype v1.4.6 github.com/go-faster/errors v0.7.1 github.com/go-faster/jx v1.1.0 diff --git a/go.sum b/go.sum index fccb6a2..668ea27 100644 --- a/go.sum +++ b/go.sum @@ -22,6 +22,8 @@ github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40 github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/flytam/filenamify v1.2.0 h1:7RiSqXYR4cJftDQ5NuvljKMfd/ubKnW/j9C6iekChgI= +github.com/flytam/filenamify v1.2.0/go.mod h1:Dzf9kVycwcsBlr2ATg6uxjqiFgKGH+5SKFuhdeP5zu8= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= diff --git a/pkg/tplfunc/string.go b/pkg/tplfunc/string.go index a4f9a93..7a8a6d8 100644 --- a/pkg/tplfunc/string.go +++ b/pkg/tplfunc/string.go @@ -4,6 +4,7 @@ import ( "strings" "text/template" + "github.com/flytam/filenamify" "github.com/iancoleman/strcase" ) @@ -11,6 +12,7 @@ var String = []Func{ Repeat(), Replace(), ToUpper(), ToLower(), SnakeCase(), CamelCase(), KebabCase(), + Filenamify(), } func Repeat() Func { @@ -64,3 +66,15 @@ func KebabCase() Func { } } } + +func Filenamify() Func { + return func(funcMap template.FuncMap) { + funcMap["filenamify"] = func(s string) string { + res, err := filenamify.FilenamifyV2(s) + if err != nil || res == "" { + return "invalid-filename" + } + return res + } + } +}