@@ -377,6 +377,18 @@ func (s *Service) PublishHistory(historyID uint, userID uint) error {
377377 // 注意:投稿后不修改UploadStatus,保持为2(已上传)
378378 db .Save (& history )
379379
380+ // 如果弹幕烧录功能已开启,标记此次一起投稿的弹幕版分P 为 appended_to_video=true。
381+ // 防止 AppendDanmakuBurnedPartsToApprovedVideos 定时任务在审核通过后再次追加已经包含在初始投稿里的弹幕版分P。
382+ if room .EnableDanmakuBurn {
383+ affected := db .Model (& models.RecordHistoryPart {}).Where (
384+ "history_id = ? AND is_temp_file = ? AND temp_file_type = ? AND upload = ?" ,
385+ historyID , true , "danmaku_burn" , true ,
386+ ).Update ("appended_to_video" , true )
387+ if affected .RowsAffected > 0 {
388+ log .Printf ("[投稿成功] 已标记 %d 个弹幕版分P 为 appended_to_video=true (history_id=%d)" , affected .RowsAffected , historyID )
389+ }
390+ }
391+
380392 log .Printf ("投稿成功: AV%d, BV%s" , avID , bvid )
381393
382394 // 兜底检测机制:使用新的API验证投稿是否真的成功
@@ -723,6 +735,14 @@ func (s *Service) AppendPartsToExisting(newHistoryID uint, existingHistory *mode
723735// UpdatePublishedVideoWithBurnedParts 回补更新已投稿视频,追加弹幕版分P
724736// 当弹幕版分P上传完成后,检查对应的历史记录是否已投稿,如果已投稿且没有弹幕版,则追加弹幕版分P
725737func (s * Service ) UpdatePublishedVideoWithBurnedParts (burnedPartID uint ) error {
738+ // 内存级防重入:防止 Path A(上传完成立即调用)与 Path B/C(定时任务发现 appended_to_video=false)并发调用
739+ // 导致同一弹幕版分P 在 B 站视频里出现两次
740+ if _ , loaded := s .appendingBurnedParts .LoadOrStore (burnedPartID , true ); loaded {
741+ log .Printf ("[回补弹幕版] 烧录版分P %d 正在追加中,跳过并发调用" , burnedPartID )
742+ return nil
743+ }
744+ defer s .appendingBurnedParts .Delete (burnedPartID )
745+
726746 db := database .GetDB ()
727747
728748 // 获取弹幕版分P
0 commit comments