// Package idgen generates stable, opaque, URL-safe identifiers for domain // entities (users, folders, artists, albums, songs, ...). // // IDs are random and assigned once, then persisted. They are never derived // from filesystem position or recomputed on rescan: the scanner looks up // existing rows by natural key (e.g. a song's path within its music // folder) and only calls NewID for rows it has never seen before, so IDs // stay stable across restarts and normal rescans. package idgen import ( "crypto/rand" "encoding/base32" "strings" ) // enc is unpadded base32 using Crockford's alphabet-like lowercase set, // which is URL-safe and avoids ambiguous characters better than base64url // for anything a human might need to read back (log lines, debugging). var enc = base32.NewEncoding("0123456789abcdefghjkmnpqrstvwxyz").WithPadding(base32.NoPadding) // New returns a new opaque ID with the given short type prefix, e.g. // New("us") -> "us_1a2b3c4d5e6f7g8h9j0k1m2n". func New(prefix string) string { b := make([]byte, 16) if _, err := rand.Read(b); err != nil { panic(err) // crypto/rand failing means the system is broken } id := strings.ToLower(enc.EncodeToString(b)) if prefix == "" { return id } return prefix + "_" + id }