refactor(progress): ProgressService required at construction; delete the 9 unreachable erase-fallbacks
Batch H1 step 3a. The ceiling census (exhaustive UpdateUniversalProgress
grep) found 9 nil-guarded `if progressSvc != nil { funnel } else { raw
erase-write }` fallbacks — the survey's "four" under-counted kobo.go,
which has FOUR sites (Markup ReadingSync, Markup last-read-place,
AnalyticsGettests, SyncFromServer), plus one each in media.go, queue.go
and koreader.go. All unreachable: the service is created unconditionally
at main.go:127 and wired into every handler and the queue processor; the
test harness wires them too.
The erasure pattern is now unrepresentable:
- NewMediaHandler / NewKOReaderHandler / NewKoboHandler / both
NewSyncQueueProcessor flavors take the ProgressService as a REQUIRED
constructor arg; the SetProgressService setters are gone.
- Every fallback body (raw UpdateUniversalProgress with explicit-NULL
pgtypes for viewport/scroll/panel/reading_mode/zoom and no
audio_offset_seconds/context_text) is deleted; each write funnels
through SaveProgress with the LWW seed, so fields the writer doesn't
know survive.
- Removed the now-dead koreader pg-type helpers (textPtrToPgText,
intPtrToPgInt4, int64PtrToPgInt8) and their self-tests.
Behavior deltas (stored-row content, no wire shape changes —
KOReader/Kobo response pins stayed green):
- KOReader fallback used to write explicit NULL viewports and drop the
context_text the koplugin sends; the funneled mainline already maps
book.ContextText.
- Queue flushes no longer overwrite chapter_progress with the book
percentage (bogus data; preserved via the seed).
- A web PUT without percentage no longer writes 0% via the deleted
fallback; percentage seeds from the stored row.
Contract suite: KOReader×4 + Kobo×3 pins green.
This commit is contained in:
1 parent
28bd323505
commit
e83c00f869
9 files changed
+163
-419
No files matched your search
+14
-59
@@ -180,11 +180,15 @@ type MediaHandler struct {
|
||||
annotationSvc *wsync.AnnotationService
|
||||
}
|
||||
|
||||
func NewMediaHandler(db *database.Queries, libraryService *services.LibraryService, worker ...*services.Worker) *MediaHandler {
|
||||
// NewMediaHandler requires the shared ProgressService — progress writes
|
||||
// must funnel through SaveProgress (the LWW-seeded canonical writer);
|
||||
// a nil service has nowhere to fall back to and must not be wired.
|
||||
func NewMediaHandler(db *database.Queries, libraryService *services.LibraryService, progressSvc *wsync.ProgressService, worker ...*services.Worker) *MediaHandler {
|
||||
mh := &MediaHandler{
|
||||
db: db,
|
||||
libraryService: libraryService,
|
||||
searchService: services.NewSearchService(db),
|
||||
progressSvc: progressSvc,
|
||||
}
|
||||
if len(worker) > 0 && worker[0] != nil {
|
||||
mh.worker = worker[0]
|
||||
@@ -192,10 +196,6 @@ func NewMediaHandler(db *database.Queries, libraryService *services.LibraryServi
|
||||
return mh
|
||||
}
|
||||
|
||||
func (mh *MediaHandler) SetProgressService(svc *wsync.ProgressService) {
|
||||
mh.progressSvc = svc
|
||||
}
|
||||
|
||||
func (mh *MediaHandler) SetAnnotationService(svc *wsync.AnnotationService) {
|
||||
mh.annotationSvc = svc
|
||||
}
|
||||
@@ -1281,66 +1281,21 @@ func (mh *MediaHandler) UpdateMediaReadingProgress(c *echo.Context) error {
|
||||
req.DeviceName = deviceLabel
|
||||
req.Broadcast = true
|
||||
|
||||
if mh.progressSvc != nil {
|
||||
if req.Percentage != nil && *req.Percentage < 0.005 {
|
||||
existing, err := mh.db.GetUniversalProgress(c.Request().Context(), database.GetUniversalProgressParams{
|
||||
MediaItemID: pgtype.UUID{Bytes: mediaUUID, Valid: true},
|
||||
UserID: pgtype.UUID{Bytes: userUUID, Valid: true},
|
||||
})
|
||||
if err == nil && existing.Percentage.Valid && existing.Percentage.Float64 > 0.01 {
|
||||
return c.JSON(http.StatusOK, map[string]string{"status": "ignored"})
|
||||
}
|
||||
if req.Percentage != nil && *req.Percentage < 0.005 {
|
||||
existing, err := mh.db.GetUniversalProgress(c.Request().Context(), database.GetUniversalProgressParams{
|
||||
MediaItemID: pgtype.UUID{Bytes: mediaUUID, Valid: true},
|
||||
UserID: pgtype.UUID{Bytes: userUUID, Valid: true},
|
||||
})
|
||||
if err == nil && existing.Percentage.Valid && existing.Percentage.Float64 > 0.01 {
|
||||
return c.JSON(http.StatusOK, map[string]string{"status": "ignored"})
|
||||
}
|
||||
|
||||
result, err := mh.progressSvc.SaveProgress(c.Request().Context(), req)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
percentage := 0.0
|
||||
if req.Percentage != nil {
|
||||
percentage = *req.Percentage
|
||||
}
|
||||
epubcfi := ""
|
||||
if req.Epubcfi != nil {
|
||||
epubcfi = *req.Epubcfi
|
||||
}
|
||||
currentPage := int32(0)
|
||||
if req.CurrentPage != nil {
|
||||
currentPage = int32(*req.CurrentPage)
|
||||
}
|
||||
totalPages := int32(0)
|
||||
if req.TotalPages != nil {
|
||||
totalPages = int32(*req.TotalPages)
|
||||
}
|
||||
|
||||
progress, err := mh.db.UpdateUniversalProgress(c.Request().Context(), database.UpdateUniversalProgressParams{
|
||||
MediaItemID: pgtype.UUID{Bytes: mediaUUID, Valid: true},
|
||||
UserID: pgtype.UUID{Bytes: userUUID, Valid: true},
|
||||
Percentage: pgtype.Float8{Float64: percentage, Valid: true},
|
||||
CharacterOffset: pgtype.Int8{Valid: false},
|
||||
Epubcfi: pgtype.Text{String: epubcfi, Valid: epubcfi != ""},
|
||||
Chapter: pgtype.Int4{Valid: false},
|
||||
ChapterProgress: pgtype.Float8{Valid: false},
|
||||
ViewportX: pgtype.Float8{Valid: false},
|
||||
ViewportY: pgtype.Float8{Valid: false},
|
||||
ZoomLevel: pgtype.Float8{Valid: false},
|
||||
ScrollPositionX: pgtype.Float8{Valid: false},
|
||||
ScrollPositionY: pgtype.Float8{Valid: false},
|
||||
PanelNumber: pgtype.Int4{Valid: false},
|
||||
ReadingMode: pgtype.Text{Valid: false},
|
||||
LastSyncDevice: pgtype.Text{String: "web", Valid: true},
|
||||
LastSyncSource: pgtype.Text{String: "web", Valid: true},
|
||||
CurrentPage: pgtype.Int4{Int32: currentPage, Valid: true},
|
||||
TotalPages: pgtype.Int4{Int32: totalPages, Valid: totalPages > 0},
|
||||
})
|
||||
result, err := mh.progressSvc.SaveProgress(c.Request().Context(), req)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, progress)
|
||||
return c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
// DeleteMediaReadingProgress handles DELETE /api/media-items/:id/progress
|
||||
|
||||
Reference in new issue
Block a user