feat: save watch progress
This commit is contained in:
@@ -2,6 +2,7 @@ package playback
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
@@ -91,6 +92,7 @@ func (h *Handler) HandleWatchPage(w http.ResponseWriter, r *http.Request) {
|
||||
MalID: data.MalID,
|
||||
Title: data.Title,
|
||||
CurrentEpisode: data.CurrentEpisode,
|
||||
StartTimeSeconds: data.StartTimeSeconds,
|
||||
CurrentStatus: data.CurrentStatus,
|
||||
InitialMode: data.InitialMode,
|
||||
AvailableModes: data.AvailableModes,
|
||||
@@ -297,6 +299,67 @@ func (h *Handler) HandleProxyPreviewSprite(w http.ResponseWriter, r *http.Reques
|
||||
http.ServeFile(w, r, spritePath)
|
||||
}
|
||||
|
||||
func (h *Handler) HandleSaveProgress(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
user := middleware.GetUser(r.Context())
|
||||
if user == nil {
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
type saveProgressRequest struct {
|
||||
MalID int `json:"mal_id"`
|
||||
Episode int `json:"episode"`
|
||||
TimeSecond float64 `json:"time_seconds"`
|
||||
}
|
||||
|
||||
var payload saveProgressRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
||||
http.Error(w, "invalid payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if payload.MalID <= 0 || payload.Episode <= 0 {
|
||||
http.Error(w, "invalid payload", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
timeSeconds := payload.TimeSecond
|
||||
if timeSeconds < 0 || timeSeconds != timeSeconds {
|
||||
timeSeconds = 0
|
||||
}
|
||||
|
||||
if h.svc.db == nil {
|
||||
http.Error(w, "database unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := h.svc.db.GetWatchListEntry(r.Context(), database.GetWatchListEntryParams{
|
||||
UserID: user.ID,
|
||||
AnimeID: int64(payload.MalID),
|
||||
}); err != nil {
|
||||
http.Error(w, "watchlist entry not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.db.SaveWatchProgress(r.Context(), database.SaveWatchProgressParams{
|
||||
CurrentEpisode: sql.NullInt64{Int64: int64(payload.Episode), Valid: true},
|
||||
CurrentTimeSeconds: timeSeconds,
|
||||
UserID: user.ID,
|
||||
AnimeID: int64(payload.MalID),
|
||||
}); err != nil {
|
||||
log.Printf("save progress failed user_id=%s mal_id=%d err=%v", user.ID, payload.MalID, err)
|
||||
http.Error(w, "failed to save progress", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (h *Handler) proxyUpstream(w http.ResponseWriter, r *http.Request, targetURL string, referer string) {
|
||||
parsed, err := url.Parse(targetURL)
|
||||
if err != nil || (parsed.Scheme != "http" && parsed.Scheme != "https") {
|
||||
|
||||
@@ -104,6 +104,7 @@ func (s *Service) BuildWatchPageData(ctx context.Context, malID int, title strin
|
||||
segments := s.fetchSkipSegments(ctx, malID, normalizedEpisode)
|
||||
|
||||
currentStatus := ""
|
||||
startTimeSeconds := 0.0
|
||||
if userID != "" && s.db != nil {
|
||||
entry, err := s.db.GetWatchListEntry(ctx, database.GetWatchListEntryParams{
|
||||
UserID: userID,
|
||||
@@ -111,6 +112,9 @@ func (s *Service) BuildWatchPageData(ctx context.Context, malID int, title strin
|
||||
})
|
||||
if err == nil {
|
||||
currentStatus = entry.Status
|
||||
if entry.CurrentEpisode.Valid && strconv.FormatInt(entry.CurrentEpisode.Int64, 10) == normalizedEpisode && entry.CurrentTimeSeconds > 0 {
|
||||
startTimeSeconds = entry.CurrentTimeSeconds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,15 +127,16 @@ func (s *Service) BuildWatchPageData(ctx context.Context, malID int, title strin
|
||||
}
|
||||
|
||||
return WatchPageData{
|
||||
MalID: malID,
|
||||
Title: watchTitle,
|
||||
CurrentEpisode: normalizedEpisode,
|
||||
CurrentStatus: currentStatus,
|
||||
InitialMode: initialMode,
|
||||
AvailableModes: availableModes,
|
||||
ModeSources: modeSources,
|
||||
Episodes: episodes,
|
||||
Segments: segments,
|
||||
MalID: malID,
|
||||
Title: watchTitle,
|
||||
CurrentEpisode: normalizedEpisode,
|
||||
StartTimeSeconds: startTimeSeconds,
|
||||
CurrentStatus: currentStatus,
|
||||
InitialMode: initialMode,
|
||||
AvailableModes: availableModes,
|
||||
ModeSources: modeSources,
|
||||
Episodes: episodes,
|
||||
Segments: segments,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ type WatchPageData struct {
|
||||
MalID int
|
||||
Title string
|
||||
CurrentEpisode string
|
||||
StartTimeSeconds float64
|
||||
CurrentStatus string
|
||||
InitialMode string
|
||||
AvailableModes []string
|
||||
|
||||
@@ -71,6 +71,7 @@ func (s *Service) AddEntry(ctx context.Context, userID string, req AddRequest) e
|
||||
AnimeID: req.AnimeID,
|
||||
Status: req.Status,
|
||||
CurrentEpisode: sql.NullInt64{Int64: 0, Valid: false},
|
||||
CurrentTimeSeconds: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update watchlist: %w", err)
|
||||
@@ -165,6 +166,7 @@ func (s *Service) Import(ctx context.Context, userID string, export ExportData)
|
||||
AnimeID: entry.AnimeID,
|
||||
Status: entry.Status,
|
||||
CurrentEpisode: sql.NullInt64{Int64: 0, Valid: false},
|
||||
CurrentTimeSeconds: 0,
|
||||
})
|
||||
if err != nil {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user