feat(users): audio_resume_rewind_seconds column

Per-user resume rewind for the audiobook/podcast player: how many
seconds (0/5/10) before the saved position playback starts, both on
open-resume and again on every genuine unpause. Motivated by the car
startup case — exact resume loses the listener's place, and the
PlaybackRequestResolver path (phone UI, Android Auto, media buttons)
all funnel through one start position.

0 = exact resume (default), so the column is inert until the profile
page sets it. Follows the default_audio_speed / keep_screen_awake
precedent: idempotent ALTER in schema.sql (re-runs on every boot),
explicit GetUser SELECT, dedicated :exec update. Handler-side strict
allow-list {0,5,10} lands with the API slice.
This commit is contained in:
John O'Keefe committed 2026-11-01 17:16:35 -05:00
1 parent 0d613ba0e3
commit daffe499eb
5 files changed
+61 -29

No files matched your search

+33 -15
View File
@@ -11407,26 +11407,28 @@ SELECT
u.timezone,
u.default_audio_speed,
u.keep_screen_awake,
u.audio_resume_rewind_seconds,
(SELECT COUNT(*) FROM devices WHERE user_id = u.id) as device_count
FROM users u
WHERE u.id = $1
`
type GetUserRow struct {
ID pgtype.UUID `db:"id" json:"id"`
Email string `db:"email" json:"email"`
Username string `db:"username" json:"username"`
Theme pgtype.Text `db:"theme" json:"theme"`
FirstName pgtype.Text `db:"first_name" json:"first_name"`
LastName pgtype.Text `db:"last_name" json:"last_name"`
Role string `db:"role" json:"role"`
MaxDevices pgtype.Int4 `db:"max_devices" json:"max_devices"`
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
Timezone pgtype.Text `db:"timezone" json:"timezone"`
DefaultAudioSpeed float32 `db:"default_audio_speed" json:"default_audio_speed"`
KeepScreenAwake bool `db:"keep_screen_awake" json:"keep_screen_awake"`
DeviceCount int64 `db:"device_count" json:"device_count"`
ID pgtype.UUID `db:"id" json:"id"`
Email string `db:"email" json:"email"`
Username string `db:"username" json:"username"`
Theme pgtype.Text `db:"theme" json:"theme"`
FirstName pgtype.Text `db:"first_name" json:"first_name"`
LastName pgtype.Text `db:"last_name" json:"last_name"`
Role string `db:"role" json:"role"`
MaxDevices pgtype.Int4 `db:"max_devices" json:"max_devices"`
CreatedAt pgtype.Timestamptz `db:"created_at" json:"created_at"`
UpdatedAt pgtype.Timestamptz `db:"updated_at" json:"updated_at"`
Timezone pgtype.Text `db:"timezone" json:"timezone"`
DefaultAudioSpeed float32 `db:"default_audio_speed" json:"default_audio_speed"`
KeepScreenAwake bool `db:"keep_screen_awake" json:"keep_screen_awake"`
AudioResumeRewindSeconds int32 `db:"audio_resume_rewind_seconds" json:"audio_resume_rewind_seconds"`
DeviceCount int64 `db:"device_count" json:"device_count"`
}
func (q *Queries) GetUser(ctx context.Context, id pgtype.UUID) (GetUserRow, error) {
@@ -11446,6 +11448,7 @@ func (q *Queries) GetUser(ctx context.Context, id pgtype.UUID) (GetUserRow, erro
&i.Timezone,
&i.DefaultAudioSpeed,
&i.KeepScreenAwake,
&i.AudioResumeRewindSeconds,
&i.DeviceCount,
)
return i, err
@@ -20287,6 +20290,20 @@ func (q *Queries) UpdateUniversalProgress(ctx context.Context, arg UpdateUnivers
return i, err
}
const UpdateUserAudioResumeRewind = `-- name: UpdateUserAudioResumeRewind :exec
UPDATE users SET audio_resume_rewind_seconds = $2, updated_at = NOW() WHERE id = $1
`
type UpdateUserAudioResumeRewindParams struct {
ID pgtype.UUID `db:"id" json:"id"`
AudioResumeRewindSeconds int32 `db:"audio_resume_rewind_seconds" json:"audio_resume_rewind_seconds"`
}
func (q *Queries) UpdateUserAudioResumeRewind(ctx context.Context, arg UpdateUserAudioResumeRewindParams) error {
_, err := q.db.Exec(ctx, UpdateUserAudioResumeRewind, arg.ID, arg.AudioResumeRewindSeconds)
return err
}
const UpdateUserDefaultAudioSpeed = `-- name: UpdateUserDefaultAudioSpeed :exec
UPDATE users SET default_audio_speed = $2, updated_at = NOW() WHERE id = $1
`
@@ -20317,7 +20334,7 @@ func (q *Queries) UpdateUserKeepScreenAwake(ctx context.Context, arg UpdateUserK
const UpdateUserMaxDevices = `-- name: UpdateUserMaxDevices :one
UPDATE users SET max_devices = $2, updated_at = NOW() WHERE id = $1
RETURNING id, email, username, password_hash, first_name, last_name, role, theme, max_devices, created_at, timezone, updated_at, default_audio_speed, keep_screen_awake
RETURNING id, email, username, password_hash, first_name, last_name, role, theme, max_devices, created_at, timezone, updated_at, default_audio_speed, keep_screen_awake, audio_resume_rewind_seconds
`
type UpdateUserMaxDevicesParams struct {
@@ -20343,6 +20360,7 @@ func (q *Queries) UpdateUserMaxDevices(ctx context.Context, arg UpdateUserMaxDev
&i.UpdatedAt,
&i.DefaultAudioSpeed,
&i.KeepScreenAwake,
&i.AudioResumeRewindSeconds,
)
return i, err
}