diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.cs b/src/N_m3u8DL-RE.Common/Resource/ResString.cs index 39983ae2..85f1702d 100644 --- a/src/N_m3u8DL-RE.Common/Resource/ResString.cs +++ b/src/N_m3u8DL-RE.Common/Resource/ResString.cs @@ -97,6 +97,7 @@ public static class ResString public static string liveLimit => GetText("liveLimit"); public static string realTimeDecMessage => GetText("realTimeDecMessage"); public static string liveLimitReached => GetText("liveLimitReached"); + public static string liveFinished => GetText("liveFinished"); public static string saveName => GetText("saveName"); public static string taskStartAt => GetText("taskStartAt"); public static string namedPipeCreated => GetText("namedPipeCreated"); diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs index 7b3c2590..70a7abe6 100644 --- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs +++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs @@ -754,6 +754,12 @@ internal static class StaticText zhTW: "到達直播錄製上限,即將停止錄製", enUS: "Live recording limit reached, will stop recording soon" ), + ["liveFinished"] = new TextContainer + ( + zhCN: "直播已结束,即将停止录制", + zhTW: "直播已結束,即將停止錄製", + enUS: "Livestream seems to have ended, will stop recording" + ), ["saveName"] = new TextContainer ( zhCN: "保存文件名: ", diff --git a/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs b/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs index 3e7668bb..ae88b337 100644 --- a/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs +++ b/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs @@ -547,10 +547,14 @@ public async Task FetchPlayListAsync(List lists) var newPlaylist = await ParseListAsync(); if (lists[i].Playlist?.MediaInit != null) + { lists[i].Playlist!.MediaParts = newPlaylist.MediaParts; // 不更新init + lists[i].Playlist!.IsLive = newPlaylist.IsLive; + } else + { lists[i].Playlist = newPlaylist; - + } if (lists[i].MediaType == MediaType.SUBTITLES) { var a = lists[i].Playlist!.MediaParts.Any(p => p.MediaSegments.Any(m => m.Url.Contains(".ttml"))); @@ -569,4 +573,4 @@ public async Task RefreshPlayListAsync(List streamSpecs) { await FetchPlayListAsync(streamSpecs); } -} \ No newline at end of file +} diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs index 65112ddd..825d2e1a 100644 --- a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs +++ b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs @@ -36,6 +36,7 @@ internal class SimpleLiveRecordManager2 ConcurrentDictionary>> BlockDic = new(); // 各流的Block ConcurrentDictionary SamePathDic = new(); // 各流是否allSamePath ConcurrentDictionary RecordLimitReachedDic = new(); // 各流是否达到上限 + ConcurrentDictionary LiveFinishedDic = new(); // 各流是否已完成 ConcurrentDictionary LastFileNameDic = new(); // 上次下载的文件名 ConcurrentDictionary MaxIndexDic = new(); // 最大Index ConcurrentDictionary DateTimeDic = new(); // 上次下载的dateTime @@ -644,7 +645,6 @@ private async Task PlayListProduceAsync(Dictionary dic while (!STOP_FLAG) { if (WAIT_SEC == 0) continue; - // 1. MPD 所有URL相同 单次请求即可获得所有轨道的信息 // 2. M3U8 所有URL不同 才需要多次请求 await Parallel.ForEachAsync(dic, async (dic, _) => @@ -653,7 +653,7 @@ await Parallel.ForEachAsync(dic, async (dic, _) => var task = dic.Value; // 达到上限时 不需要刷新了 - if (RecordLimitReachedDic[task.Id]) + if (RecordLimitReachedDic[task.Id] || LiveFinishedDic[task.Id]) return; var allHasDatetime = streamSpec.Playlist!.MediaParts[0].MediaSegments.All(s => s.DateTime != null); @@ -679,10 +679,16 @@ await Parallel.ForEachAsync(dic, async (dic, _) => // 累加已获取到的时长 RefreshedDurDic[task.Id] += (int)newList.Sum(s => s.Duration); } + else if (!streamSpec.Playlist!.IsLive && BlockDic[task.Id].Count == 0) + { + LiveFinishedDic[task.Id] = true; + BlockDic[task.Id].Complete(); + } if (!STOP_FLAG && RefreshedDurDic[task.Id] >= DownloaderConfig.MyOptions.LiveRecordLimit?.TotalSeconds) { RecordLimitReachedDic[task.Id] = true; + BlockDic[task.Id].Complete(); } // 检测时长限制 @@ -692,6 +698,13 @@ await Parallel.ForEachAsync(dic, async (dic, _) => STOP_FLAG = true; CancellationTokenSource.Cancel(); } + + // 检测直播是否已结束 + if (!STOP_FLAG && LiveFinishedDic.Values.All(x => x)) + { + Logger.WarnMarkUp($"[darkorange3_1]{ResString.liveFinished}[/]"); + STOP_FLAG = true; + } }); try @@ -822,6 +835,7 @@ await progress.StartAsync(async ctx => } LastFileNameDic[task.Id] = ""; RecordLimitReachedDic[task.Id] = false; + LiveFinishedDic[task.Id] = false; DateTimeDic[task.Id] = 0L; RecordedDurDic[task.Id] = 0; RefreshedDurDic[task.Id] = 0; @@ -918,4 +932,4 @@ await Parallel.ForEachAsync(dic, options, async (kp, _) => return success; } -} \ No newline at end of file +}