@@ -147,7 +147,8 @@ func TCPlaylist(c *gin.Context) {
147147 jobID := c .Param ("job" )
148148 token := c .Param ("token" )
149149 profile := c .Param ("profile" )
150- job , ok := transcode .Default ().Scheduler .Get (jobID )
150+ mgr := transcode .Default ()
151+ job , ok := mgr .Scheduler .Get (jobID )
151152 if ! ok {
152153 c .String (http .StatusNotFound , "job not found" )
153154 return
@@ -156,6 +157,13 @@ func TCPlaylist(c *gin.Context) {
156157 c .String (http .StatusForbidden , "bad token" )
157158 return
158159 }
160+ // 【关键】如果 job 之前因 idle 被 cancel,这里重新激活:复用 jobID/token 重新入队
161+ // 让 worker 再次启动 ffmpeg 转码,避免播放器看到 404 后无法恢复
162+ if st := job .GetStatus (); st == transcode .JobCancelled || st == transcode .JobFinished || st == transcode .JobFailed {
163+ if _ , ok := mgr .Scheduler .Reactivate (jobID ); ok {
164+ fmt .Printf ("[transcode] reactivate job %s on playlist request (was %s)\n " , jobID , st )
165+ }
166+ }
159167 job .Touch ()
160168 // 等首切片
161169 select {
@@ -193,7 +201,8 @@ func TCSegment(c *gin.Context) {
193201 token := c .Param ("token" )
194202 profile := c .Param ("profile" )
195203 segName := c .Param ("seg" )
196- job , ok := transcode .Default ().Scheduler .Get (jobID )
204+ mgr := transcode .Default ()
205+ job , ok := mgr .Scheduler .Get (jobID )
197206 if ! ok {
198207 c .String (http .StatusNotFound , "job not found" )
199208 return
@@ -202,17 +211,23 @@ func TCSegment(c *gin.Context) {
202211 c .String (http .StatusForbidden , "bad token" )
203212 return
204213 }
214+ // 【关键】job 之前因 idle 被 cancel 时,复用同一 jobID/token 重新入队启动转码。
215+ // 这样浏览器持有的旧 master.m3u8 + 切片 URL 仍然有效,暂停再继续也不会 404。
216+ if st := job .GetStatus (); st == transcode .JobCancelled || st == transcode .JobFinished || st == transcode .JobFailed {
217+ if _ , ok := mgr .Scheduler .Reactivate (jobID ); ok {
218+ fmt .Printf ("[transcode] reactivate job %s on segment request (was %s)\n " , jobID , st )
219+ }
220+ }
205221 // seg-N.ts
206222 name := strings .TrimSuffix (strings .TrimPrefix (segName , "seg-" ), ".ts" )
207223 seq , err := strconv .Atoi (name )
208224 if err != nil {
209225 c .String (http .StatusBadRequest , "bad segment name" )
210226 return
211227 }
212- cache := transcode . Default () .Cache
228+ cache := mgr .Cache
213229 // 【智能调度】触发对应 chunk 的 ffmpeg 启动(如已运行则只更新 LastAccess)
214230 // 这是用户拖动进度条到任意位置时能快速响应的关键
215- mgr := transcode .Default ()
216231 if mgr .Chunks != nil {
217232 chunkIdx := mgr .Chunks .EnsureChunkRunningForSeg (jobID , profile , seq )
218233 fmt .Printf ("[tc-segment] req seq=%d chunk=%d job=%s\n " , seq , chunkIdx , jobID )
0 commit comments