初始提交:短剧库源码(含转码优化 ultrafast)
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type playbackHistoryProgress struct {
|
||||
Run uint64 `json:"run"`
|
||||
Sequence uint64 `json:"sequence"`
|
||||
Episode int `json:"episode"`
|
||||
Position float64 `json:"position"`
|
||||
Duration float64 `json:"duration"`
|
||||
Completed bool `json:"completed"`
|
||||
}
|
||||
|
||||
type playbackHistoryRun struct {
|
||||
episode int
|
||||
duration float64
|
||||
}
|
||||
|
||||
func (app *UIApp) handlePlaybackHistory(writer http.ResponseWriter, request *http.Request) {
|
||||
if !playbackRequestAllowed(writer, request, http.MethodGet) {
|
||||
return
|
||||
}
|
||||
viewer := requestViewer(writer, request)
|
||||
if viewer == nil {
|
||||
return
|
||||
}
|
||||
entries, err := viewer.playbackHistory().list()
|
||||
if err != nil {
|
||||
writeJSON(writer, http.StatusInternalServerError, map[string]string{"error": "无法读取观看记录:" + publicError(err).Error()})
|
||||
return
|
||||
}
|
||||
visible := make([]playbackHistoryEntry, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
if dramaAllowed(request.Context(), entry.DramaID, entry.Source) {
|
||||
visible = append(visible, entry)
|
||||
}
|
||||
}
|
||||
writeJSON(writer, http.StatusOK, map[string]any{"data": visible, "limit": playbackHistoryLimit})
|
||||
}
|
||||
|
||||
func (app *UIApp) handlePlaybackHistoryRemove(writer http.ResponseWriter, request *http.Request) {
|
||||
var input struct {
|
||||
DramaID string `json:"dramaId"`
|
||||
All bool `json:"all"`
|
||||
}
|
||||
if !readPlaybackRequest(writer, request, &input) {
|
||||
return
|
||||
}
|
||||
id, _, valid := playbackHistoryIdentity(input.DramaID)
|
||||
if input.All && input.DramaID != "" || !input.All && !valid {
|
||||
writeJSON(writer, http.StatusBadRequest, map[string]string{"error": "请选择要删除的观看记录"})
|
||||
return
|
||||
}
|
||||
viewer := requestViewer(writer, request)
|
||||
if viewer == nil {
|
||||
return
|
||||
}
|
||||
if !input.All && !app.requireDramaSources(writer, request, []string{id}) {
|
||||
return
|
||||
}
|
||||
if err := viewer.playbackHistory().removeForSources(request.Context(), id, input.All); err != nil {
|
||||
writeJSON(writer, http.StatusInternalServerError, map[string]string{"error": "删除观看记录未能保存:" + publicError(err).Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(writer, http.StatusOK, map[string]bool{"ok": true})
|
||||
}
|
||||
|
||||
func (app *UIApp) handlePlaybackProgress(writer http.ResponseWriter, request *http.Request) {
|
||||
var input struct {
|
||||
Session string `json:"session"`
|
||||
Progress playbackHistoryProgress `json:"progress"`
|
||||
}
|
||||
if !readPlaybackRequest(writer, request, &input) {
|
||||
return
|
||||
}
|
||||
if !app.requirePlaybackOwner(writer, request, input.Session) {
|
||||
return
|
||||
}
|
||||
entry, saved, err := app.recordPlaybackProgress(contextViewer(request.Context()), input.Session, input.Progress)
|
||||
if err != nil {
|
||||
writeJSON(writer, http.StatusBadRequest, map[string]string{"error": publicError(err).Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(writer, http.StatusOK, map[string]any{"saved": saved, "entry": entry})
|
||||
}
|
||||
|
||||
func (app *UIApp) recordPlaybackProgress(viewer *viewerRecords, id string, progress playbackHistoryProgress) (playbackHistoryEntry, bool, error) {
|
||||
if progress.Run == 0 || progress.Sequence == 0 || !validPlaybackHistoryTime(progress.Position) || !validPlaybackHistoryTime(progress.Duration) {
|
||||
return playbackHistoryEntry{}, false, errors.New("观看进度无效")
|
||||
}
|
||||
if viewer == nil {
|
||||
return playbackHistoryEntry{}, false, errors.New("浏览器身份无效")
|
||||
}
|
||||
store := viewer.playbackHistory()
|
||||
app.playbackMu.Lock()
|
||||
session := app.playbacks[id]
|
||||
if session == nil || progress.Episode < 1 || progress.Episode > len(session.tasks) {
|
||||
app.playbackMu.Unlock()
|
||||
return playbackHistoryEntry{}, false, nil
|
||||
}
|
||||
if session.viewer != viewer {
|
||||
app.playbackMu.Unlock()
|
||||
return playbackHistoryEntry{}, false, errors.New("播放会话已过期,请重新打开本剧")
|
||||
}
|
||||
run, known := session.historyRuns[progress.Run]
|
||||
if !known || run.episode != progress.Episode || progress.Sequence <= session.historySequence {
|
||||
app.playbackMu.Unlock()
|
||||
return playbackHistoryEntry{}, false, nil
|
||||
}
|
||||
task := session.tasks[progress.Episode-1]
|
||||
dramaID, source, valid := playbackHistoryIdentity(task.DramaID)
|
||||
if !valid {
|
||||
app.playbackMu.Unlock()
|
||||
return playbackHistoryEntry{}, false, errors.New("观看记录的站源或剧集 ID 无效")
|
||||
}
|
||||
duration := progress.Duration
|
||||
if run.duration > 0 && validPlaybackHistoryTime(run.duration) {
|
||||
duration = run.duration
|
||||
}
|
||||
position := progress.Position
|
||||
if duration > 0 {
|
||||
if position > duration+2 {
|
||||
app.playbackMu.Unlock()
|
||||
return playbackHistoryEntry{}, false, errors.New("观看位置超过分集时长")
|
||||
}
|
||||
position = math.Min(position, duration)
|
||||
}
|
||||
completed := progress.Completed && duration > 0 && position >= math.Max(0, duration-2)
|
||||
index := task.Index
|
||||
if index < 1 {
|
||||
index = progress.Episode
|
||||
}
|
||||
total := task.Total
|
||||
if total < index {
|
||||
total = index
|
||||
}
|
||||
if total < len(session.tasks) {
|
||||
total = len(session.tasks)
|
||||
}
|
||||
entry := playbackHistoryEntry{DramaID: dramaID, Source: source, Title: task.DramaTitle, ChapterID: task.Chapter.ID, Episode: task.Chapter.EpisodeString(index), Index: index, Total: total, Position: math.Round(position*1000) / 1000, Duration: duration, Completed: completed, Mode: "online", WatchedAt: time.Now(), sessionID: id, sessionOpened: session.openedAt}
|
||||
if len(session.downloadIDs) >= progress.Episode {
|
||||
entry.Mode, entry.TaskID = "collection", session.downloadIDs[progress.Episode-1]
|
||||
}
|
||||
saved, err := store.record(entry, session.openedAt)
|
||||
if err == nil {
|
||||
session.historySequence = progress.Sequence
|
||||
app.touchPlaybackLocked(session)
|
||||
}
|
||||
app.playbackMu.Unlock()
|
||||
if err == nil && saved {
|
||||
err = store.flush()
|
||||
}
|
||||
return entry, saved && err == nil, err
|
||||
}
|
||||
|
||||
func playbackHistoryResume(entry playbackHistoryEntry, tasks []Task, fallback int) (int, float64, bool, string) {
|
||||
matched := -1
|
||||
for index, task := range tasks {
|
||||
if entry.ChapterID != "" && entry.ChapterID == task.Chapter.ID {
|
||||
matched = index
|
||||
break
|
||||
}
|
||||
}
|
||||
if matched < 0 {
|
||||
for index, task := range tasks {
|
||||
if task.Chapter.EpisodeString(task.Index) == entry.Episode {
|
||||
matched = index
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if matched < 0 {
|
||||
return fallback, 0, false, "当前选集中没有上次观看的分集,请手动选集"
|
||||
}
|
||||
if entry.Completed {
|
||||
if matched+1 < len(tasks) {
|
||||
return matched + 2, 0, false, "已接续到下一集"
|
||||
}
|
||||
message := "已看至最新,可选集重看"
|
||||
if entry.Index < entry.Total {
|
||||
message = "已看完此合集当前可播放的分集,可更新合集或从剧库继续观看"
|
||||
}
|
||||
return matched + 1, 0, true, message
|
||||
}
|
||||
position := entry.Position
|
||||
if entry.Duration > 0 && position >= entry.Duration {
|
||||
position = math.Max(0, entry.Duration-1)
|
||||
}
|
||||
return matched + 1, position, false, "已恢复上次观看进度"
|
||||
}
|
||||
Reference in New Issue
Block a user