0.2.78
This commit is contained in:
@@ -7,8 +7,11 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// MediaRequest is one viewer's ask, as recorded. It carries no status: see the schema
|
||||
// comment on media_requests for why the state is derived per read rather than stored.
|
||||
// MediaRequest is one viewer's ask, as recorded.
|
||||
//
|
||||
// The status a card shows is not here: it is derived per read from the *arrs and the
|
||||
// library, for the reason the schema gives. The one exception is LastStatus, which is not
|
||||
// the card's status but the memory of it — the only way to notice that something changed.
|
||||
type MediaRequest struct {
|
||||
MediaType string `json:"mediaType"`
|
||||
ForeignID int `json:"foreignId"`
|
||||
@@ -16,6 +19,19 @@ type MediaRequest struct {
|
||||
Year int `json:"year,omitempty"`
|
||||
PosterURL string `json:"posterUrl,omitempty"`
|
||||
RequestedAt time.Time `json:"requestedAt"`
|
||||
// LastStatus is the state this request was in the last time anything looked, and it is
|
||||
// the only piece of request state that is stored. See the schema comment: an arrival is
|
||||
// a difference between two observations rather than a property of one, and the viewer is
|
||||
// told about it once.
|
||||
LastStatus string `json:"-"`
|
||||
}
|
||||
|
||||
// OwnedMediaRequest is a stored ask with the person who made it, which the per-viewer read
|
||||
// does not need to carry because it was asked for by user. The ready sweep looks at the
|
||||
// whole household in one query, so there it is the point.
|
||||
type OwnedMediaRequest struct {
|
||||
MediaRequest
|
||||
UserID string
|
||||
}
|
||||
|
||||
// RequestUsage is the operator-facing use of the request feature. A recorded request is
|
||||
@@ -59,16 +75,20 @@ func (s *Store) SaveMediaRequest(ctx context.Context, userID string, req MediaRe
|
||||
if userID == "" || req.ForeignID <= 0 {
|
||||
return fmt.Errorf("store: media request needs a user and a foreign id")
|
||||
}
|
||||
// last_status is deliberately absent from the UPDATE. Asking again is somebody saying
|
||||
// they still want it, not a reason to re-announce an arrival they were already told
|
||||
// about — and re-seeding it here would make a second press of Request the way to make
|
||||
// the gateway repeat itself.
|
||||
_, err := s.pool.Exec(ctx, `
|
||||
INSERT INTO media_requests
|
||||
(emby_user_id, media_type, foreign_id, title, year, poster_url, requested_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, now())
|
||||
(emby_user_id, media_type, foreign_id, title, year, poster_url, last_status, requested_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, now())
|
||||
ON CONFLICT (emby_user_id, media_type, foreign_id) DO UPDATE
|
||||
SET title = EXCLUDED.title,
|
||||
year = EXCLUDED.year,
|
||||
poster_url = EXCLUDED.poster_url,
|
||||
requested_at = now()`,
|
||||
userID, req.MediaType, req.ForeignID, req.Title, req.Year, req.PosterURL)
|
||||
userID, req.MediaType, req.ForeignID, req.Title, req.Year, req.PosterURL, req.LastStatus)
|
||||
if err != nil {
|
||||
return fmt.Errorf("store: save media request: %w", err)
|
||||
}
|
||||
@@ -78,7 +98,7 @@ func (s *Store) SaveMediaRequest(ctx context.Context, userID string, req MediaRe
|
||||
// MediaRequests returns one viewer's asks, most recent first.
|
||||
func (s *Store) MediaRequests(ctx context.Context, userID string) ([]MediaRequest, error) {
|
||||
rows, err := s.pool.Query(ctx, `
|
||||
SELECT media_type, foreign_id, title, year, poster_url, requested_at
|
||||
SELECT media_type, foreign_id, title, year, poster_url, last_status, requested_at
|
||||
FROM media_requests
|
||||
WHERE emby_user_id = $1
|
||||
ORDER BY requested_at DESC
|
||||
@@ -92,7 +112,8 @@ func (s *Store) MediaRequests(ctx context.Context, userID string) ([]MediaReques
|
||||
for rows.Next() {
|
||||
var req MediaRequest
|
||||
if err := rows.Scan(
|
||||
&req.MediaType, &req.ForeignID, &req.Title, &req.Year, &req.PosterURL, &req.RequestedAt,
|
||||
&req.MediaType, &req.ForeignID, &req.Title, &req.Year, &req.PosterURL,
|
||||
&req.LastStatus, &req.RequestedAt,
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("store: scan media request: %w", err)
|
||||
}
|
||||
@@ -118,3 +139,60 @@ func (s *Store) DeleteMediaRequest(
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllMediaRequests reads the whole household's asks, newest first, with the person attached.
|
||||
//
|
||||
// The per-viewer read above is what a page needs; this is what the ready sweep needs, and
|
||||
// the difference is worth one query rather than one per account: a household of six with
|
||||
// eighty requests between them is one read, and the sweep has to look at all of them anyway
|
||||
// because two people can be waiting for the same film.
|
||||
//
|
||||
// It is bounded like the per-viewer read. A sweep that fell behind on a household which had
|
||||
// been asking for things for two years must not become an unbounded query on a timer.
|
||||
func (s *Store) AllMediaRequests(ctx context.Context, limit int) ([]OwnedMediaRequest, error) {
|
||||
if limit <= 0 {
|
||||
limit = MediaRequestSweepLimit
|
||||
}
|
||||
rows, err := s.pool.Query(ctx, `
|
||||
SELECT emby_user_id, media_type, foreign_id, title, year, poster_url, last_status, requested_at
|
||||
FROM media_requests
|
||||
ORDER BY requested_at DESC
|
||||
LIMIT $1`, limit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("store: read all media requests: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
requests := []OwnedMediaRequest{}
|
||||
for rows.Next() {
|
||||
var req OwnedMediaRequest
|
||||
if err := rows.Scan(
|
||||
&req.UserID, &req.MediaType, &req.ForeignID, &req.Title, &req.Year,
|
||||
&req.PosterURL, &req.LastStatus, &req.RequestedAt,
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("store: scan media request: %w", err)
|
||||
}
|
||||
requests = append(requests, req)
|
||||
}
|
||||
return requests, rows.Err()
|
||||
}
|
||||
|
||||
// MediaRequestSweepLimit caps what one pass of the ready sweep will look at.
|
||||
const MediaRequestSweepLimit = 500
|
||||
|
||||
// SetMediaRequestStatus records what a request was last seen doing.
|
||||
//
|
||||
// Written only when the state actually moved, so a sweep over a household where nothing has
|
||||
// changed — which is almost every sweep — costs no writes at all.
|
||||
func (s *Store) SetMediaRequestStatus(
|
||||
ctx context.Context, userID, mediaType string, foreignID int, status string,
|
||||
) error {
|
||||
_, err := s.pool.Exec(ctx, `
|
||||
UPDATE media_requests SET last_status = $4
|
||||
WHERE emby_user_id = $1 AND media_type = $2 AND foreign_id = $3`,
|
||||
strings.TrimSpace(userID), mediaType, foreignID, status)
|
||||
if err != nil {
|
||||
return fmt.Errorf("store: set media request status: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user