diff --git a/db/migrations/20260720015443_uniform_canonical_ids.go b/db/migrations/20260720015443_uniform_canonical_ids.go index cde0b584c..69d0907c1 100644 --- a/db/migrations/20260720015443_uniform_canonical_ids.go +++ b/db/migrations/20260720015443_uniform_canonical_ids.go @@ -26,13 +26,13 @@ func canonicalID(s string) string { return s } sum := md5.Sum([]byte(s)) - return id.Encode128(sum) + return id.Encode(sum) case 32: b, err := hex.DecodeString(s) if err != nil { return s } - return id.Encode128([16]byte(b)) + return id.Encode([16]byte(b)) case 36: if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { return s @@ -41,7 +41,7 @@ func canonicalID(s string) string { if err != nil { return s } - return id.Encode128([16]byte(b)) + return id.Encode([16]byte(b)) } return s } diff --git a/model/id/id.go b/model/id/id.go index 43332cff8..2885bc6ad 100644 --- a/model/id/id.go +++ b/model/id/id.go @@ -10,17 +10,17 @@ import ( func NewRandom() string { var b [16]byte - _, _ = rand.Read(b[:]) // never fails since Go 1.24 - return Encode128(b) + _, _ = rand.Read(b[:]) + return Encode(b) } -// Encode128 renders a 16-byte value as the canonical 22-char zero-padded base62 id. -func Encode128(b [16]byte) string { +// Encode renders a 16-byte value as the canonical 22-char zero-padded base62 id. +func Encode(b [16]byte) string { return fmt.Sprintf("%022s", new(big.Int).SetBytes(b[:]).Text(62)) } -// Decode128 is the exact inverse of Encode128. -func Decode128(s string) ([]byte, error) { +// Decode is the exact inverse of Encode. +func Decode(s string) ([]byte, error) { if len(s) != 22 { return nil, fmt.Errorf("invalid id length %d", len(s)) } @@ -40,7 +40,7 @@ func NewHash(data ...string) string { hash.Write([]byte(d)) hash.Write([]byte(string('\u200b'))) } - return Encode128([16]byte(hash.Sum(nil))) + return Encode([16]byte(hash.Sum(nil))) } func NewTagID(name, value string) string { diff --git a/model/id/id_test.go b/model/id/id_test.go index 6ee9a345d..407560a75 100644 --- a/model/id/id_test.go +++ b/model/id/id_test.go @@ -6,30 +6,30 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("Encode128/Decode128", func() { +var _ = Describe("Encode/Decode", func() { It("encodes 16 bytes as 22-char zero-padded base62", func() { - Expect(id.Encode128([16]byte{})).To(Equal("0000000000000000000000")) + Expect(id.Encode([16]byte{})).To(Equal("0000000000000000000000")) allFF := [16]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} - Expect(id.Encode128(allFF)).To(Equal("7N42dgm5tFLK9N8MT7fHC7")) + Expect(id.Encode(allFF)).To(Equal("7N42dgm5tFLK9N8MT7fHC7")) }) It("round-trips arbitrary 16-byte values", func() { b := [16]byte{0xe3, 0xb7, 0xfc, 0x2a, 0xe9, 0x44, 0x7b, 0xbe, 0xc3, 0x7a, 0x13, 0xbf, 0x91, 0x6e, 0x3c, 0xf6} - s := id.Encode128(b) + s := id.Encode(b) Expect(s).To(Equal("6VHl3uR4kss6sUPKA8Cwnk")) - Expect(id.Decode128(s)).To(Equal(b[:])) + Expect(id.Decode(s)).To(Equal(b[:])) }) It("rejects invalid input", func() { - _, err := id.Decode128("short") + _, err := id.Decode("short") Expect(err).To(HaveOccurred()) - _, err = id.Decode128("!!!!!!!!!!!!!!!!!!!!!!") // 22 chars, not base62 + _, err = id.Decode("!!!!!!!!!!!!!!!!!!!!!!") // 22 chars, not base62 Expect(err).To(HaveOccurred()) - _, err = id.Decode128("-000000000000000000001") // sign is not part of the alphabet + _, err = id.Decode("-000000000000000000001") // sign is not part of the alphabet Expect(err).To(HaveOccurred()) - _, err = id.Decode128("zzzzzzzzzzzzzzzzzzzzzz") // > 2^128 + _, err = id.Decode("zzzzzzzzzzzzzzzzzzzzzz") // > 2^128 Expect(err).To(HaveOccurred()) }) }) @@ -40,7 +40,7 @@ var _ = Describe("NewRandom", func() { for range 1000 { s := id.NewRandom() Expect(s).To(HaveLen(22)) - _, err := id.Decode128(s) + _, err := id.Decode(s) Expect(err).ToNot(HaveOccurred(), "id %q must decode to 128 bits", s) seen[s] = struct{}{} } @@ -58,7 +58,7 @@ var _ = Describe("NewHash", func() { It("always emits 22 decodable chars", func() { h := id.NewHash("anything", "at", "all") Expect(h).To(HaveLen(22)) - _, err := id.Decode128(h) + _, err := id.Decode(h) Expect(err).ToNot(HaveOccurred()) }) }) diff --git a/model/metadata/legacy_ids.go b/model/metadata/legacy_ids.go index 0c1d5825a..7156c4f50 100644 --- a/model/metadata/legacy_ids.go +++ b/model/metadata/legacy_ids.go @@ -21,7 +21,7 @@ func legacyTrackID(mf model.MediaFile, prependLibId bool) string { key = fmt.Sprintf("%d\\%s", mf.LibraryID, key) } sum := md5.Sum([]byte(key)) - return id.Encode128(sum) + return id.Encode(sum) } func legacyAlbumID(mf model.MediaFile, md Metadata, prependLibId bool) string { @@ -36,7 +36,7 @@ func legacyAlbumID(mf model.MediaFile, md Metadata, prependLibId bool) string { albumPath = fmt.Sprintf("%d\\%s", mf.LibraryID, albumPath) } sum := md5.Sum([]byte(albumPath)) - return id.Encode128(sum) + return id.Encode(sum) } func legacyMapAlbumArtistName(md Metadata) string { diff --git a/model/metadata/persistent_ids_test.go b/model/metadata/persistent_ids_test.go index 57a5b1308..8e38bbd42 100644 --- a/model/metadata/persistent_ids_test.go +++ b/model/metadata/persistent_ids_test.go @@ -225,12 +225,12 @@ var _ = Describe("getPID", func() { }) It("prepends the library id for a non-default library", func() { mf := model.MediaFile{Path: "/music/a.mp3", LibraryID: 2} - // id.Encode128(md5.Sum([]byte("2\\/music/a.mp3"))) + // id.Encode(md5.Sum([]byte("2\\/music/a.mp3"))) Expect(legacyTrackID(mf, true)).To(Equal("4EK5DHQBMeFuDHw6S3iooO")) }) It("emits a canonical album id (golden)", func() { mf := model.MediaFile{LibraryID: 1} - // id.Encode128(md5.Sum([]byte("[unknown artist]\\[unknown album]"))) + // id.Encode(md5.Sum([]byte("[unknown artist]\\[unknown album]"))) Expect(legacyAlbumID(mf, Metadata{}, false)).To(Equal("6xBmxSAUFJSQuW7UvwCq8X")) }) Context("track_legacy", func() {