支持手动设置直播刷新间隔
This commit is contained in:
parent
c0c821da63
commit
21e70674f5
|
@ -11,6 +11,7 @@ namespace N_m3u8DL_RE.Common.Entity
|
|||
public long Index { get; set; }
|
||||
public double Duration { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public DateTime? DateTime { get; set; }
|
||||
|
||||
public long? StartRange { get; set; }
|
||||
public long? StopRange { get => (StartRange != null && ExpectLength != null) ? StartRange + ExpectLength - 1 : null; }
|
||||
|
|
|
@ -64,6 +64,7 @@ namespace N_m3u8DL_RE.Common.Resource
|
|||
public static string cmd_customProxy { get => GetText("cmd_customProxy"); }
|
||||
public static string cmd_liveKeepSegments { get => GetText("cmd_liveKeepSegments"); }
|
||||
public static string cmd_liveRecordLimit { get => GetText("cmd_liveRecordLimit"); }
|
||||
public static string cmd_liveWaitTime { get => GetText("cmd_liveWaitTime"); }
|
||||
public static string cmd_liveRealTimeMerge { get => GetText("cmd_liveRealTimeMerge"); }
|
||||
public static string cmd_livePerformAsVod { get => GetText("cmd_livePerformAsVod"); }
|
||||
public static string cmd_muxAfterDone { get => GetText("cmd_muxAfterDone"); }
|
||||
|
|
|
@ -262,6 +262,12 @@ namespace N_m3u8DL_RE.Common.Resource
|
|||
zhTW: "以點播方式下載直播流",
|
||||
enUS: "Download live streams as vod"
|
||||
),
|
||||
["cmd_liveWaitTime"] = new TextContainer
|
||||
(
|
||||
zhCN: "手动设置直播列表刷新间隔",
|
||||
zhTW: "手動設置直播列表刷新間隔",
|
||||
enUS: "Manually set the live playlist refresh interval"
|
||||
),
|
||||
["cmd_customHLSMethod"] = new TextContainer
|
||||
(
|
||||
zhCN: "指定HLS加密方式 (AES_128|AES_128_ECB|CENC|CHACHA20|NONE|SAMPLE_AES|SAMPLE_AES_CTR|UNKNOWN)",
|
||||
|
|
|
@ -295,7 +295,7 @@ namespace N_m3u8DL_RE.Parser.Extractor
|
|||
//program date time
|
||||
else if (line.StartsWith(HLSTags.ext_x_program_date_time))
|
||||
{
|
||||
//
|
||||
segment.DateTime = DateTime.Parse(ParserUtil.GetAttribute(line));
|
||||
}
|
||||
//解析不连续标记,需要单独合并(timestamp不同)
|
||||
else if (line.StartsWith(HLSTags.ext_x_discontinuity))
|
||||
|
|
|
@ -70,6 +70,7 @@ namespace N_m3u8DL_RE.CommandLine
|
|||
private readonly static Option<bool> LiveRealTimeMerge = new(new string[] { "--live-real-time-merge" }, description: ResString.cmd_liveRealTimeMerge, getDefaultValue: () => false);
|
||||
private readonly static Option<bool> LiveKeepSegments = new(new string[] { "--live-keep-segments" }, description: ResString.cmd_liveKeepSegments, getDefaultValue: () => true);
|
||||
private readonly static Option<TimeSpan?> LiveRecordLimit = new(new string[] { "--live-record-limit" }, description: ResString.cmd_liveRecordLimit, parseArgument: ParseLiveLimit) { ArgumentHelpName = "HH:mm:ss" };
|
||||
private readonly static Option<int?> LiveWaitTime = new(new string[] { "--live-wait-time" }, description: ResString.cmd_liveRecordLimit) { ArgumentHelpName = "SEC" };
|
||||
|
||||
|
||||
//复杂命令行如下
|
||||
|
@ -374,6 +375,7 @@ namespace N_m3u8DL_RE.CommandLine
|
|||
LivePerformAsVod = bindingContext.ParseResult.GetValueForOption(LivePerformAsVod),
|
||||
UseSystemProxy = bindingContext.ParseResult.GetValueForOption(UseSystemProxy),
|
||||
CustomProxy = bindingContext.ParseResult.GetValueForOption(CustomProxy),
|
||||
LiveWaitTime = bindingContext.ParseResult.GetValueForOption(LiveWaitTime),
|
||||
};
|
||||
|
||||
if (bindingContext.ParseResult.HasOption(CustomHLSMethod)) option.CustomHLSMethod = bindingContext.ParseResult.GetValueForOption(CustomHLSMethod);
|
||||
|
@ -439,7 +441,7 @@ namespace N_m3u8DL_RE.CommandLine
|
|||
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption,
|
||||
MuxAfterDone,
|
||||
CustomHLSMethod, CustomHLSKey, CustomHLSIv, UseSystemProxy, CustomProxy,
|
||||
LivePerformAsVod, LiveRealTimeMerge, LiveKeepSegments, LiveRecordLimit,
|
||||
LivePerformAsVod, LiveRealTimeMerge, LiveKeepSegments, LiveRecordLimit, LiveWaitTime,
|
||||
MuxImports, VideoFilter, AudioFilter, SubtitleFilter, MoreHelp
|
||||
};
|
||||
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
||||
|
|
|
@ -196,6 +196,10 @@ namespace N_m3u8DL_RE.CommandLine
|
|||
/// See: <see cref="CommandInvoker.CustomProxy"/>.
|
||||
/// </summary>
|
||||
public WebProxy? CustomProxy { get; set; }
|
||||
/// <summary>
|
||||
/// See: <see cref="CommandInvoker.LiveWaitTime"/>.
|
||||
/// </summary>
|
||||
public int? LiveWaitTime { get; set; }
|
||||
public bool MuxKeepFiles { get; set; }
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
|||
int WAIT_SEC = 0; //刷新间隔
|
||||
ConcurrentDictionary<int, int> RecordingDurDic = new(); //已录制时长
|
||||
ConcurrentDictionary<string, string> LastUrlDic = new(); //上次下载的url
|
||||
ConcurrentDictionary<string, long> DateTimeDic = new(); //上次下载的dateTime
|
||||
CancellationTokenSource CancellationTokenSource = new(); //取消Wait
|
||||
|
||||
public SimpleLiveRecordManager2(DownloaderConfig downloaderConfig, List<StreamSpec> selectedSteams, StreamExtractor streamExtractor)
|
||||
|
@ -84,6 +85,39 @@ namespace N_m3u8DL_RE.DownloadManager
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取时间戳
|
||||
/// </summary>
|
||||
/// <param name="dateTime"></param>
|
||||
/// <returns></returns>
|
||||
private long GetUnixTimestamp(DateTime dateTime)
|
||||
{
|
||||
return new DateTimeOffset(dateTime.ToUniversalTime()).ToUnixTimeSeconds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取分段文件夹
|
||||
/// </summary>
|
||||
/// <param name="segment"></param>
|
||||
/// <param name="allHasDatetime"></param>
|
||||
/// <returns></returns>
|
||||
private string GetSegmentName(MediaSegment segment, bool allHasDatetime)
|
||||
{
|
||||
bool hls = StreamExtractor.ExtractorType == ExtractorType.HLS;
|
||||
|
||||
string name = OtherUtil.GetFileNameFromInput(segment.Url, false);
|
||||
if (hls && allHasDatetime)
|
||||
{
|
||||
name = GetUnixTimestamp(segment.DateTime!.Value).ToString();
|
||||
}
|
||||
else if (hls && segment.Index > 10)
|
||||
{
|
||||
name = segment.Index.ToString();
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private void ChangeSpecInfo(StreamSpec streamSpec, List<Mediainfo> mediainfos, ref bool useAACFilter)
|
||||
{
|
||||
if (!DownloaderConfig.MyOptions.BinaryMerge && mediainfos.Any(m => m.DolbyVison == true))
|
||||
|
@ -125,7 +159,6 @@ namespace N_m3u8DL_RE.DownloadManager
|
|||
var readInfo = false; //是否读取过
|
||||
bool useAACFilter = false; //ffmpeg合并flag
|
||||
bool initDownloaded = false; //是否下载过init文件
|
||||
bool hls = StreamExtractor.ExtractorType == ExtractorType.HLS;
|
||||
ConcurrentDictionary<MediaSegment, DownloadResult?> FileDic = new();
|
||||
List<Mediainfo> mediaInfos = new();
|
||||
FileStream? fileOutputStream = null;
|
||||
|
@ -205,8 +238,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
|||
}
|
||||
}
|
||||
|
||||
//计算填零个数
|
||||
var pad = "0".PadLeft(segments.Count().ToString().Length, '0');
|
||||
var allHasDatetime = segments.All(s => s.DateTime != null);
|
||||
|
||||
//下载第一个分片
|
||||
if (!readInfo)
|
||||
|
@ -214,7 +246,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
|||
var seg = segments.First();
|
||||
segments = segments.Skip(1);
|
||||
//获取文件名
|
||||
var filename = hls && seg.Index > 10 ? seg.Index.ToString(pad) : OtherUtil.GetFileNameFromInput(seg.Url, false);
|
||||
var filename = GetSegmentName(seg, allHasDatetime);
|
||||
var index = seg.Index;
|
||||
var path = Path.Combine(tmpDir, filename + $".{streamSpec.Extension ?? "clip"}.tmp");
|
||||
var result = await Downloader.DownloadSegmentAsync(seg, path, speedContainer, headers);
|
||||
|
@ -262,7 +294,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
|||
await Parallel.ForEachAsync(segments, options, async (seg, _) =>
|
||||
{
|
||||
//获取文件名
|
||||
var filename = hls && seg.Index > 10 ? seg.Index.ToString(pad) : OtherUtil.GetFileNameFromInput(seg.Url, false);
|
||||
var filename = GetSegmentName(seg, allHasDatetime);
|
||||
var index = seg.Index;
|
||||
var path = Path.Combine(tmpDir, filename + $".{streamSpec.Extension ?? "clip"}.tmp");
|
||||
var result = await Downloader.DownloadSegmentAsync(seg, path, speedContainer, headers);
|
||||
|
@ -505,7 +537,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
|||
if (WAIT_SEC != 0)
|
||||
{
|
||||
//过滤不需要下载的片段
|
||||
FilterMediaSegments(streamSpec, LastUrlDic[streamSpec.ToShortString()]);
|
||||
FilterMediaSegments(streamSpec, LastUrlDic[streamSpec.ToShortString()], DateTimeDic[streamSpec.ToShortString()]);
|
||||
var newList = streamSpec.Playlist!.MediaParts[0].MediaSegments;
|
||||
if (newList.Count > 0)
|
||||
{
|
||||
|
@ -513,6 +545,9 @@ namespace N_m3u8DL_RE.DownloadManager
|
|||
await target.SendAsync(newList);
|
||||
//更新最新链接
|
||||
LastUrlDic[streamSpec.ToShortString()] = GetPath(newList.Last().Url);
|
||||
//尝试更新时间戳
|
||||
var dt = newList.Last().DateTime;
|
||||
DateTimeDic[streamSpec.ToShortString()] = dt != null ? GetUnixTimestamp(dt.Value) : 0L;
|
||||
task.MaxValue += newList.Count;
|
||||
}
|
||||
try
|
||||
|
@ -532,11 +567,22 @@ namespace N_m3u8DL_RE.DownloadManager
|
|||
target.Complete();
|
||||
}
|
||||
|
||||
private void FilterMediaSegments(StreamSpec streamSpec, string lastUrl)
|
||||
private void FilterMediaSegments(StreamSpec streamSpec, string lastUrl, long dateTime)
|
||||
{
|
||||
if (string.IsNullOrEmpty(lastUrl)) return;
|
||||
if (string.IsNullOrEmpty(lastUrl) && dateTime == 0) return;
|
||||
|
||||
var index = -1;
|
||||
|
||||
//优先使用dateTime判断
|
||||
if (dateTime != 0 && streamSpec.Playlist!.MediaParts[0].MediaSegments.All(s => s.DateTime != null))
|
||||
{
|
||||
index = streamSpec.Playlist!.MediaParts[0].MediaSegments.FindIndex(s => GetUnixTimestamp(s.DateTime!.Value) == dateTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
index = streamSpec.Playlist!.MediaParts[0].MediaSegments.FindIndex(s => GetPath(s.Url) == lastUrl);
|
||||
}
|
||||
|
||||
var index = streamSpec.Playlist!.MediaParts[0].MediaSegments.FindIndex(s => GetPath(s.Url) == lastUrl);
|
||||
if (index > -1)
|
||||
{
|
||||
streamSpec.Playlist!.MediaParts[0].MediaSegments = streamSpec.Playlist!.MediaParts[0].MediaSegments.Skip(index + 1).ToList();
|
||||
|
@ -566,11 +612,16 @@ namespace N_m3u8DL_RE.DownloadManager
|
|||
foreach (var item in SelectedSteams)
|
||||
{
|
||||
LastUrlDic[item.ToShortString()] = "";
|
||||
DateTimeDic[item.ToShortString()] = 0L;
|
||||
}
|
||||
//设置等待时间
|
||||
if (WAIT_SEC == 0)
|
||||
{
|
||||
WAIT_SEC = (int)(SelectedSteams.Min(s => s.Playlist!.MediaParts[0].MediaSegments.Sum(s => s.Duration)) / 2);
|
||||
WAIT_SEC -= 2; //再提前两秒吧 留出冗余
|
||||
if (DownloaderConfig.MyOptions.LiveWaitTime != null)
|
||||
WAIT_SEC = DownloaderConfig.MyOptions.LiveWaitTime.Value;
|
||||
if (WAIT_SEC <= 0) WAIT_SEC = 1;
|
||||
Logger.WarnMarkUp($"set refresh interval to {WAIT_SEC} seconds");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue