tdl
tdl copied to clipboard
[Feat] More/better name template functions
Proposal
Name templates currently have a rather limited functionality set. Following is a list of functions I personally believed to be useful:
-
Truncate
, whereSTRING
longer thanLEN
will be truncated. This allows more rich, longer templates to be used without worrying OS filename character limit. -
SafeString
, where unsafe characters (invalid filename characters) will be removed/replaced fromSTRING
. The guide's example usingReplace
works, but is a bit too long and is prone to accidental breakage (mistype, accidental character delete, etc.). -
Extension
andStem
, whereSTRING
will be parsed as a filename and the extension/non-extension part of the string will be returned. -
Pad
, whereNUMBER
will be padded toLEN
digits (e.g.{{ Pad 1 3 }}
will return a string001
)
Background
I'm using tdl
in a script to automatically backup my chat. The core logic of the script now looks something like this:
if tdl dl --continue --rewrite-ext --dir "$output_dir" "$cmd" "$cmd_arg" --skip-same \
--template '{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileCaption `\n` `_` `/` `_` `\\` `_` `:` `_` `*` `_` `?` `_` `<` `_` `>` `_` `|` `_` ` ` `_` }}_{{ replace .FileName `/` `_` `\` `_` `:` `_` `*` `_` `?` `_` `<` `_` `>` `_` `|` `_` ` ` `_` }}'; then
break
fi
# tdl failed. Try again with shorter name.
if tdl dl --continue --rewrite-ext --dir "$output_dir" "$cmd" "$cmd_arg" --skip-same \
--template '{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileCaption `\n` `_` `/` `_` `\\` `_` `:` `_` `*` `_` `?` `_` `<` `_` `>` `_` `|` `_` ` ` `_` }}'; then
break
fi
# Try again with even shorter name.
if tdl dl --continue --rewrite-ext --dir "$output_dir" "$cmd" "$cmd_arg" --skip-same \
--template '{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileName `/` `_` `\` `_` `:` `_` `*` `_` `?` `_` `<` `_` `>` `_` `|` `_` ` ` `_` }}'; then
break
fi
# Last attempt.
if tdl dl --continue --rewrite-ext --dir "$output_dir" "$cmd" "$cmd_arg" --skip-same \
--template '{{ .DialogID }}_{{ .MessageID }}'; then
break
fi
>&2 echo "Failed to download media"
return 1
Which is fairly long and isn't very elegant. I also have to download one file at a time because the whole batch will fail even if only one file is having a long name, and I want most of the file to have a descriptive name instead of plain DialogID_MessageID
.
Workarounds
I've made a small patch to add Truncate()
and Pad()
function because I need it desperately:
diff --git a/pkg/tplfunc/string.go b/pkg/tplfunc/string.go
index a4f9a93..514907d 100644
--- a/pkg/tplfunc/string.go
+++ b/pkg/tplfunc/string.go
@@ -3,6 +3,7 @@ package tplfunc
import (
"strings"
"text/template"
+ "fmt"
"github.com/iancoleman/strcase"
)
@@ -11,6 +12,7 @@ var String = []Func{
Repeat(), Replace(),
ToUpper(), ToLower(),
SnakeCase(), CamelCase(), KebabCase(),
+ Truncate(), Pad(),
}
func Repeat() Func {
@@ -64,3 +66,27 @@ func KebabCase() Func {
}
}
}
+
+func Truncate() Func {
+ return func(funcMap template.FuncMap) {
+ funcMap["truncate"] = func(s string, n int) string {
+ // Handle Unicode correctly - https://stackoverflow.com/a/76502408
+ trunc := ""
+ for index, val := range s {
+ if index >= n {
+ break
+ }
+ trunc += string(val)
+ }
+ return trunc
+ }
+ }
+}
+
+func Pad() Func {
+ return func(funcMap template.FuncMap) {
+ funcMap["pad"] = func(n, l int) string {
+ return fmt.Sprintf("%*d", l, n)
+ }
+ }
+}
SafeString
function is nice-to-have, but I think people should live OK without it :)
A possible solution to implement Truncate
outside tdl
using pure shell code is to write a jq
spaghetti code to find all media with longer-than-legal filenames. It's going to be a bit fragile and really complicated though. Please paste the code below if anyone have the dare to write it.