iyear / tdl

📥 A Telegram toolkit written in Golang
https://docs.iyear.me/tdl
GNU Affero General Public License v3.0
4.43k stars 436 forks source link

[Feat] More/better name template functions #676

Open hafeoz opened 4 months ago

hafeoz commented 4 months ago

Proposal

Name templates currently have a rather limited functionality set. Following is a list of functions I personally believed to be useful:

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.

hafeoz commented 2 months ago

Functions from https://github.com/Masterminds/sprig seem nice too.

iyear commented 1 month ago

I'm not sure if https://github.com/Masterminds/sprig can meet most text processing needs. If it can, I would prefer to use the sprig library directly to enhance template text functions.