A CD rip logchecker, used for analyzing the generated logs for any problems that would potentially indicate a non-perfect rip was produced. Of course, just because a log doesn't score a perfect 100% does not mean that the produced rip isn't bit perfect, it's just less likely.
This project is a pure Go rewrite of the original PHP Logchecker.
Unlike the original PHP version which required external Python scripts to validate EAC and XLD checksums, this Go version has all checksum verification built-in via native Go libraries (github.com/Nirzak/eac-logchecker and github.com/Nirzak/xld-logchecker).
- Go 1.25.0+ (Only for build and development. No runtime required for prebuilt binaries)
Download from github releases : https://github.com/Nirzak/logchecker-go/releases
Install via go install:
go install github.com/Nirzak/logchecker-go/cmd/logchecker@latestAlternatively, you can build it from source:
git clone https://github.com/Nirzak/logchecker-go.git
cd logchecker-go
go build -o logchecker cmd/logchecker/main.go$ logchecker version
Logchecker 1.14.4
Usage:
logchecker analyze [--html] [--no_text] [--ids] <file> [out_file] [details_json]
logchecker analyse (alias of analyze)
logchecker decode <file>
logchecker translate [-l lang] <file>
logchecker version
Main usage is through the analyze command, e.g.:
$ logchecker analyze path/to/file.log
Ripper : EAC
Version : 1.0 beta 3
Language: en
Score : 59
Checksum: checksum_ok
Details :
Could not verify gap handling (-10 points)
Could not verify id3 tag setting (-1 point)
Range rip detected (-30 points)
analyze --ids prints the disc identifiers (AccurateRip, MusicBrainz, CTDB,
FreeDB/CDDB) with their lookup URLs, then exits without dumping the log text.
The AccurateRip ID is taken from the log when embedded (dBpoweramp); the rest
are computed from the parsed TOC. Lines are omitted when the log has no TOC.
For FreeDB/CDDB, the tool additionally queries the gnudb database online to resolve the disc to an authoritative entry (the calculated CDDB ID and the database's stored ID can differ in the track-count byte). On a match the gnudb ID is used for the URL; if the lookup fails — no internet, timeout, or no match — it falls back to the calculated ID. The fallback is automatic; the command never blocks beyond a 15-second network timeout and always prints a usable URL.
$ logchecker analyze --ids path/to/file.log
Ripper : EAC
AccurateRip : 011-00164217-00beabe7-9d0bf70b
AR URL : http://www.accuraterip.com/accuraterip/7/1/2/dBAR-011-00164217-00beabe7-9d0bf70b.bin
MusicBrainz : lXlcau748FItWbittqBtRd3VYPU-
MB URL : https://musicbrainz.org/cdtoc/attach?toc=...&tracks=11&id=...
CTDB : ReQ9rI.mtYl1ofdDtzJxoEbiZUU-
CTDB URL : https://db.cuetools.net/ui/?tocid=ReQ9rI.mtYl1ofdDtzJxoEbiZUU-
FreeDB/CDDB : 9d0bf70b
gnudb : matched 9d0bf789 — Various Artists / Aashiqui 2
FreeDB URL: https://gnudb.org/cd/9d0bf789
When offline or unmatched the last two lines instead read:
gnudb : no match; using calculated ID
FreeDB URL: https://gnudb.org/cd/9d0bf70b
go get github.com/Nirzak/logchecker-gopackage main
import (
"fmt"
"github.com/Nirzak/logchecker-go/logchecker"
)
func main() {
lc := logchecker.New()
// Load and parse the file
err := lc.NewFile("/path/to/log/file.log")
if err != nil {
panic(err)
}
lc.Parse()
// Output results
fmt.Printf("Ripper : %s\n", lc.GetRipper())
fmt.Printf("Version : %s\n", lc.GetRipperVersion())
fmt.Printf("Score : %d\n", lc.GetScore())
fmt.Printf("Checksum : %s\n", lc.GetChecksumState())
fmt.Printf("\nDetails:\n")
for _, detail := range lc.GetDetails() {
fmt.Printf(" %s\n", detail)
}
}lc.Parse() // Run analysis
lc.GetRipper() // string: "EAC" | "XLD" | "whipper" | "dBpoweramp" | "unknown"
lc.GetRipperVersion() // string
lc.GetScore() // int 0–100
lc.GetChecksumState() // "checksum_ok" | "checksum_invalid" | "checksum_missing"
lc.GetDetails() // []string — human-readable list of deductions / notices
lc.GetLanguage() // string language code, e.g. "en", "ru"
lc.GetLog() // string — HTML-annotated log text (span-tagged)
lc.IsCombinedLog() // bool — true when the file holds multiple rip sessions
// Disc identifiers
lc.GetTOC() // *toc.TOC — parsed Table of Contents, or nil if absent
lc.GetAccurateRipID() // string — AccurateRip ID (embedded if present, else computed)
// Control
lc.ValidateChecksum(false) // Disable external checksum validationThe value returned by GetTOC() exposes the disc-ID computations (all pure, no
network I/O):
t := lc.GetTOC()
if t != nil {
t.MusicBrainzDiscID() // string
t.MusicBrainzLookupURL()
t.FreeDBDiscID() // string (8-char hex CDDB ID)
t.FreeDBLookupURL()
t.CTDBDiscID() // string (CUETools Database TOC ID)
t.CTDBLookupURL()
t.AccurateRipID() // string "NNN-ID1-ID2-CDDB"
t.AccurateRipURL() // string — AccurateRip .bin lookup URL
}The core logchecker library performs no network I/O. To verify a disc against
the AccurateRip database, use the separate accuraterip package, passing the
TOC from GetTOC():
import "github.com/Nirzak/logchecker-go/accuraterip"
res, err := accuraterip.Lookup(lc.GetTOC()) // or LookupWithContext(ctx, toc)
if err != nil {
// network / parse error
}
switch res.Status {
case accuraterip.StatusFound: // res.Pressings holds per-track Confidence/CRCv1/CRCv2
case accuraterip.StatusNotFound: // disc absent from the database
case accuraterip.StatusError:
}The locally-computed FreeDB/CDDB ID and gnudb's stored ID can differ in the
track-count byte. The gnudb package resolves a TOC to gnudb's authoritative
entry over its CDDB CGI protocol, falling back to the calculated ID on any
failure (no internet, timeout, or no match). Like accuraterip, it performs
network I/O and is kept out of the pure core — call it explicitly:
import "github.com/Nirzak/logchecker-go/gnudb"
res, err := gnudb.Resolve(lc.GetTOC()) // or ResolveWithContext(ctx, toc)
if err != nil {
// offline / timeout / http error — res still holds the calculated fallback
}
res.DiscID // gnudb ID if matched, else the calculated CDDB ID
res.URL // https://gnudb.org/cd/<DiscID>
res.Matched // true if gnudb returned an entry
res.Title // "Artist / Album" from the match, if anyResolve never blocks beyond a 15-second timeout, and res is always usable
even on error. The CLI's --ids flag uses this internally.
For the pure, offline calculated URL (no network), use GetTOC().FreeDBLookupURL().
To run the test suite:
go test -v ./...