package jellyfin import ( "fmt" "strconv" "strings" ) // Jellyfin identifies every object with an opaque 32-character lowercase hex // id (a GUID with the dashes removed). Vellum's ids are int64, and both // libraries and items must appear as Jellyfin ids — including as a ParentId, // which a client hands back verbatim. Rendering both as plain decimal would // make ParentId ambiguous between a library and an item, so libraries get a // leading 'f' marker. A positive int64 rendered as 32 hex digits always starts // with 0-7, so 'f' can never collide with an item id. // // The mapping is stable and deterministic: the same Vellum row always yields // the same Jellyfin id, which clients rely on for caching and favourites. // itemID renders an item's Jellyfin id. func itemID(id int64) string { return fmt.Sprintf("%032x", uint64(id)) } // libraryID renders a library's Jellyfin id. The leading 'f' namespaces it // away from item ids. func libraryID(id int64) string { return fmt.Sprintf("f%031x", uint64(id)) } // decodeID reverses itemID/libraryID. isLibrary reports which namespace the id // came from; ok is false for anything that is not one of our 32-char hex ids. // Input is lowercased first, because clients sometimes normalise case. func decodeID(s string) (id int64, isLibrary, ok bool) { s = strings.ToLower(strings.TrimSpace(s)) if len(s) != 32 { return 0, false, false } if s[0] == 'f' { n, err := strconv.ParseUint(s[1:], 16, 64) if err != nil { return 0, false, false } return int64(n), true, true } n, err := strconv.ParseUint(s, 16, 64) if err != nil { return 0, false, false } // Item ids are positive int64; a value with the top bit set is malformed. if n > 1<<63-1 { return 0, false, false } return int64(n), false, true }