登録番組の定期確認を追加(24 時間ごと、予告の除外、確認エラーの記録)
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
@@ -66,6 +67,95 @@ func (s *Store) QueueUnseenEpisodes(ctx context.Context, seriesID string, episod
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// ListDueSubscriptions は有効で、cutoff 以前に確認された(または未確認の)番組を返す。
|
||||
func (s *Store) ListDueSubscriptions(ctx context.Context, cutoff time.Time) ([]Subscription, error) {
|
||||
rows, err := s.pool.Query(ctx, `SELECT series_id, series_title, enabled, created_at, last_checked_at,
|
||||
last_check_error_at, last_check_error, 0::bigint
|
||||
FROM subscriptions
|
||||
WHERE enabled AND (last_checked_at IS NULL OR last_checked_at <= $1)
|
||||
ORDER BY last_checked_at ASC NULLS FIRST, created_at, series_id`, cutoff)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var result []Subscription
|
||||
for rows.Next() {
|
||||
var sub Subscription
|
||||
if err := rows.Scan(&sub.SeriesID, &sub.SeriesTitle, &sub.Enabled, &sub.CreatedAt,
|
||||
&sub.LastCheckedAt, &sub.LastCheckErrorAt, &sub.LastCheckError, &sub.JobCount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, sub)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
// CompleteSubscriptionCheck は全公開回を既知にし、しきい値を超える未確認回だけを投入する。
|
||||
// 最終確認日時の更新と前回エラーの消去も同じ取引で行う。
|
||||
func (s *Store) CompleteSubscriptionCheck(ctx context.Context, seriesID string, episodes []Episode, previewMaxSeconds int, checkedAt time.Time) (int, error) {
|
||||
if previewMaxSeconds < 0 {
|
||||
return 0, errors.New("preview threshold must not be negative")
|
||||
}
|
||||
tx, err := s.pool.Begin(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer tx.Rollback(ctx)
|
||||
var enabled bool
|
||||
err = tx.QueryRow(ctx, `SELECT enabled FROM subscriptions WHERE series_id=$1 FOR UPDATE`, seriesID).Scan(&enabled)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return 0, ErrInvalidTransition
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !enabled {
|
||||
return 0, ErrSubscriptionPaused
|
||||
}
|
||||
queued := 0
|
||||
for _, ep := range episodes {
|
||||
if ep.ID == "" || ep.SeriesID != seriesID || ep.DurationSeconds < 0 {
|
||||
return 0, fmt.Errorf("invalid episode identity for series %s", seriesID)
|
||||
}
|
||||
var inserted string
|
||||
err := tx.QueryRow(ctx, `INSERT INTO known_episodes (series_id, episode_id)
|
||||
VALUES ($1,$2) ON CONFLICT DO NOTHING RETURNING episode_id`, seriesID, ep.ID).Scan(&inserted)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if ep.DurationSeconds <= previewMaxSeconds {
|
||||
continue
|
||||
}
|
||||
tag, err := tx.Exec(ctx, `INSERT INTO download_jobs
|
||||
(episode_id, series_id, series_title, title, broadcaster_name, duration_seconds, end_at, thumbnail_path, source)
|
||||
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'subscription')
|
||||
ON CONFLICT (episode_id) DO NOTHING`, ep.ID, seriesID, ep.SeriesTitle, ep.Title,
|
||||
ep.BroadcasterName, ep.DurationSeconds, ep.EndAt, ep.ThumbnailPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
queued += int(tag.RowsAffected())
|
||||
}
|
||||
if _, err := tx.Exec(ctx, `UPDATE subscriptions SET last_checked_at=$2,
|
||||
last_check_error_at=NULL, last_check_error=NULL WHERE series_id=$1`, seriesID, checkedAt); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return queued, nil
|
||||
}
|
||||
|
||||
// RecordSubscriptionCheckFailure は取得失敗を記録し、最終確認日時は変更しない。
|
||||
func (s *Store) RecordSubscriptionCheckFailure(ctx context.Context, seriesID string, failedAt time.Time, message string) error {
|
||||
tag, err := s.pool.Exec(ctx, `UPDATE subscriptions SET last_check_error_at=$2, last_check_error=$3
|
||||
WHERE series_id=$1 AND enabled`, seriesID, failedAt, message)
|
||||
return transitionResult(tag.RowsAffected(), err)
|
||||
}
|
||||
|
||||
func addKnownAndMaybeQueue(ctx context.Context, tx pgx.Tx, seriesID string, episodes []Episode, queue bool) (int, error) {
|
||||
count := 0
|
||||
for _, ep := range episodes {
|
||||
|
||||
Reference in New Issue
Block a user