同步上游 0921 版 zip(新增 emby 播放等 20 处变更)
This commit is contained in:
+22
-69
@@ -242,58 +242,16 @@ func (app *UIApp) handleEmbyLegacyStream(writer http.ResponseWriter, request *ht
|
||||
writer.Header().Set("Content-Type", "application/vnd.apple.mpegurl")
|
||||
return
|
||||
}
|
||||
sessionID := randomHex(24)
|
||||
if sessionID == "" {
|
||||
writeJSON(writer, http.StatusInternalServerError, map[string]string{"error": "无法创建播放会话"})
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(request.Context(), 90*time.Second)
|
||||
defer cancel()
|
||||
app.playbackMu.Lock()
|
||||
if len(app.playbacks) >= app.mediaResources().snapshot().MaxSessions {
|
||||
app.playbackMu.Unlock()
|
||||
writeJSON(writer, http.StatusTooManyRequests, map[string]string{"error": "同时播放数量已达上限,请稍后再试"})
|
||||
return
|
||||
}
|
||||
if app.playbacks == nil {
|
||||
app.playbacks = make(map[string]*playbackSession)
|
||||
}
|
||||
session := &playbackSession{accountID: request.URL.Query().Get("account"), dramaID: id, id: sessionID, run: 1, state: "opening", expires: time.Now().Add(playbackIdleTimeout), cancel: cancel}
|
||||
session.timer = time.AfterFunc(playbackIdleTimeout, func() { app.expirePlayback(sessionID) })
|
||||
app.playbacks[sessionID] = session
|
||||
app.playbackMu.Unlock()
|
||||
ready := false
|
||||
defer func() {
|
||||
if !ready {
|
||||
app.closePlayback(sessionID)
|
||||
}
|
||||
}()
|
||||
task, downloadID, err := app.embyTask(ctx, id, chapter)
|
||||
if err != nil {
|
||||
writeJSON(writer, http.StatusBadGateway, map[string]string{"error": "解析 Emby 分集失败:" + app.redactError(err)})
|
||||
return
|
||||
}
|
||||
cache := newPlaybackNative(app, context.Background(), task, downloadID, 0, 0, false)
|
||||
app.playbackMu.Lock()
|
||||
if app.playbacks[sessionID] != session || ctx.Err() != nil {
|
||||
app.playbackMu.Unlock()
|
||||
cache.Close()
|
||||
return
|
||||
}
|
||||
session.tasks = []Task{task}
|
||||
session.native = cache
|
||||
session.cancel = cache.Close
|
||||
app.playbackMu.Unlock()
|
||||
cache.start()
|
||||
if _, err = cache.segment(ctx, 0); err != nil {
|
||||
writeJSON(writer, http.StatusBadGateway, map[string]string{"error": "准备 Emby 播放失败:" + app.redactError(err)})
|
||||
return
|
||||
}
|
||||
_, duration, _ := cache.metadata()
|
||||
if duration <= 0 || duration > 24*60*60 || math.IsNaN(duration) || math.IsInf(duration, 0) {
|
||||
writeJSON(writer, http.StatusBadGateway, map[string]string{"error": "未取得有效播放时长"})
|
||||
session := app.acquireEmbyPlayback(writer, request, id, chapter, "", true)
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
defer app.releaseEmbyPlayback(session)
|
||||
app.serveEmbyPlaylist(writer, request, session)
|
||||
}
|
||||
|
||||
func (app *UIApp) serveEmbyPlaylist(writer http.ResponseWriter, request *http.Request, session *playbackSession) {
|
||||
id, chapter, sessionID, duration := session.dramaID, session.tasks[0].Chapter.ID, session.id, session.duration
|
||||
var playlist strings.Builder
|
||||
fmt.Fprintf(&playlist, "#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:%d\n#EXT-X-MEDIA-SEQUENCE:0\n#EXT-X-PLAYLIST-TYPE:VOD\n#EXT-X-INDEPENDENT-SEGMENTS\n", playbackNativeSegmentSeconds)
|
||||
for part, count := 0, int(math.Ceil(duration/playbackNativeSegmentSeconds)); part < count; part++ {
|
||||
@@ -304,17 +262,6 @@ func (app *UIApp) handleEmbyLegacyStream(writer http.ResponseWriter, request *ht
|
||||
fmt.Fprintf(&playlist, "#EXTINF:%.6f,\nsegment.ts?%s\n", math.Min(playbackNativeSegmentSeconds, duration-float64(part*playbackNativeSegmentSeconds)), query.Encode())
|
||||
}
|
||||
playlist.WriteString("#EXT-X-ENDLIST\n")
|
||||
app.playbackMu.Lock()
|
||||
if app.playbacks[sessionID] == session {
|
||||
session.state = "streaming"
|
||||
session.duration = duration
|
||||
app.touchPlaybackLocked(session)
|
||||
ready = true
|
||||
}
|
||||
app.playbackMu.Unlock()
|
||||
if !ready {
|
||||
return
|
||||
}
|
||||
writer.Header().Set("Content-Type", "application/vnd.apple.mpegurl")
|
||||
http.ServeContent(writer, request, "index.m3u8", time.Time{}, strings.NewReader(playlist.String()))
|
||||
}
|
||||
@@ -330,23 +277,29 @@ func (app *UIApp) handleEmbySegment(writer http.ResponseWriter, request *http.Re
|
||||
writeJSON(writer, http.StatusBadRequest, map[string]string{"error": "分片编号无效"})
|
||||
return
|
||||
}
|
||||
app.playbackMu.Lock()
|
||||
session := app.playbacks[query.Get("session")]
|
||||
if session == nil || session.native == nil || len(session.tasks) != 1 || session.tasks[0].DramaID != id || session.tasks[0].Chapter.ID != chapter {
|
||||
app.playbackMu.Unlock()
|
||||
writeJSON(writer, http.StatusGone, map[string]string{"error": "播放已过期,请重新播放"})
|
||||
if query.Get("session") == "" {
|
||||
app.writeEmbyPlaybackError(writer, request, http.StatusGone, "播放已过期,请重新播放")
|
||||
return
|
||||
}
|
||||
session := app.acquireEmbyPlayback(writer, request, id, chapter, query.Get("session"), true)
|
||||
if session == nil {
|
||||
return
|
||||
}
|
||||
defer app.releaseEmbyPlayback(session)
|
||||
cache := session.native
|
||||
app.touchPlaybackLocked(session)
|
||||
app.playbackMu.Unlock()
|
||||
if cache == nil {
|
||||
app.writeEmbyPlaybackError(writer, request, http.StatusGone, "播放格式已变化,请重新播放")
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(request.Context(), 90*time.Second)
|
||||
defer cancel()
|
||||
body, err := cache.segment(ctx, part)
|
||||
if err != nil {
|
||||
writeJSON(writer, http.StatusBadGateway, map[string]string{"error": "读取 Emby 分片失败:" + app.redactError(err)})
|
||||
app.writeEmbyPlaybackError(writer, request, http.StatusBadGateway, "读取 Emby 分片失败:"+app.redactError(err))
|
||||
return
|
||||
}
|
||||
active := &playbackRunContext{id: session.id, run: session.run, session: session, ctx: cache.ctx}
|
||||
writer = &playbackActivityWriter{ResponseWriter: writer, app: app, run: active}
|
||||
writer.Header().Set("Content-Type", "video/mp2t")
|
||||
http.ServeContent(writer, request, "segment.ts", time.Time{}, bytes.NewReader(body))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user