Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

整理代码、实现了一些功能 #716

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion BBDown.Core/Entity/Entity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ public class Subtitle
{
public required string lan;
public required string url;
public required string path;
public required string type;
public string? cachePath;
}

public class Clip
Expand Down
12 changes: 6 additions & 6 deletions BBDown.Core/Util/SubUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace BBDown.Core.Util
public partial class SubUtil
{
//https://i0.hdslb.com/bfs/subtitle/subtitle_lan.json
public static (string, string) GetSubtitleCode(string key)
public static (string lan, string display) GetSubtitleCode(string key)
{
//zh-hans => zh-Hans
if (NonCapsRegex().Match(key) is { Success: true } result)
Expand Down Expand Up @@ -231,7 +231,7 @@ public static (string, string) GetSubtitleCode(string key)
{
url = url,
lan = lan,
path = $"{aid}/{aid}.{cid}.{lan}{(url.Contains(".json") ? ".srt" : ".ass")}"
type = (url.Contains(".json") ? ".srt" : ".ass")
};

//有空的URL 不合法
Expand Down Expand Up @@ -267,7 +267,7 @@ public static (string, string) GetSubtitleCode(string key)
{
url = url,
lan = lan,
path = $"{aid}/{aid}.{cid}.{lan}{(url.Contains(".json") ? ".srt" : ".ass")}"
type = url.Contains(".json") ? ".srt" : ".ass"
};

//有空的URL 不合法
Expand Down Expand Up @@ -300,7 +300,7 @@ public static (string, string) GetSubtitleCode(string key)
{
url = sub.GetProperty("subtitle_url").ToString(),
lan = lan,
path = $"{aid}/{aid}.{cid}.{lan}.srt"
type = "srt"
};
subtitles.Add(subtitle);
}
Expand Down Expand Up @@ -338,7 +338,7 @@ public static (string, string) GetSubtitleCode(string key)
{
url = sub.GetProperty("subtitle_url").ToString(),
lan = lan,
path = $"{aid}/{aid}.{cid}.{lan}.srt"
type = "srt"
};
subtitles.Add(subtitle);
}
Expand Down Expand Up @@ -392,7 +392,7 @@ public static (string, string) GetSubtitleCode(string key)
{
url = m.Groups[2].Value,
lan = m.Groups[1].Value,
path = $"{aid}/{aid}.{cid}.{m.Groups[1].Value}.srt"
type = "srt"
};
subtitles.Add(subtitle);
}
Expand Down
182 changes: 18 additions & 164 deletions BBDown/BBDownMuxer.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Diagnostics;
using System.Text;
using static BBDown.Core.Entity.Entity;
using static BBDown.BBDownUtil;
using static BBDown.Core.Util.SubUtil;
using static BBDown.Core.Logger;
using System.IO;
using BBDown.Core;
using BBDown.Mux;

namespace BBDown
{
partial class BBDownMuxer
{
public static string FFMPEG = "ffmpeg";
public static string MP4BOX = "mp4box";
public static string? FFMPEG;
public static string? MP4BOX;

public static IMuxer CreateMuxer(MyOption option)
{
if (option.UseMP4box)
{
if (MP4BOX == null)
throw new FileNotFoundException("Couldn't find mp4box");
return new MP4BoxMuxer(MP4BOX);
}
if (FFMPEG == null)
throw new FileNotFoundException("Couldn't find ffmpeg");
return new FFMPEGMuxer(FFMPEG);
}

private static int RunExe(string app, string parms, bool customBin = false)
{
Expand All @@ -36,162 +44,8 @@ private static int RunExe(string app, string parms, bool customBin = false)
p.WaitForExit();
p.Close();
p.Dispose();
return code;
}

private static string EscapeString(string str)
{
return string.IsNullOrEmpty(str) ? str : str.Replace("\"", "'").Replace("\\", "\\\\");
}

private static int MuxByMp4box(string videoPath, string audioPath, string outPath, string desc, string title, string author, string episodeId, string pic, string lang, List<Subtitle>? subs, bool audioOnly, bool videoOnly, List<ViewPoint>? points)
{
StringBuilder inputArg = new();
StringBuilder metaArg = new();
int nowId = 0;
inputArg.Append(" -inter 500 -noprog ");
if (!string.IsNullOrEmpty(videoPath))
{
inputArg.Append($" -add \"{videoPath}#trackID={(audioOnly && audioPath == "" ? "2" : "1")}:name=\" ");
nowId++;
}
if (!string.IsNullOrEmpty(audioPath))
{
inputArg.Append($" -add \"{audioPath}:lang={(lang == "" ? "und" : lang)}\" ");
nowId++;
}
if (points != null && points.Any())
{
var meta = GetMp4boxMetaString(points);
var metaFile = Path.Combine(Path.GetDirectoryName(string.IsNullOrEmpty(videoPath) ? audioPath : videoPath)!, "chapters");
File.WriteAllText(metaFile, meta);
inputArg.Append($" -chap \"{metaFile}\" ");
}
if (!string.IsNullOrEmpty(pic))
metaArg.Append($":cover=\"{pic}\"");
if (!string.IsNullOrEmpty(episodeId))
metaArg.Append($":album=\"{title}\":title=\"{episodeId}\"");
else
metaArg.Append($":title=\"{title}\"");
metaArg.Append($":comment=\"{desc}\"");
metaArg.Append($":artist=\"{author}\"");

if (subs != null)
{
for (int i = 0; i < subs.Count; i++)
{
if (File.Exists(subs[i].path) && File.ReadAllText(subs[i].path!) != "")
{
nowId++;
inputArg.Append($" -add \"{subs[i].path}#trackID=1:name=:hdlr=sbtl:lang={GetSubtitleCode(subs[i].lan).Item1}\" ");
inputArg.Append($" -udta {nowId}:type=name:str=\"{GetSubtitleCode(subs[i].lan).Item2}\" ");
}
}
}

//----分析完毕
var arguments = (Config.DEBUG_LOG ? " -verbose " : "") + inputArg.ToString() + (metaArg.ToString() == "" ? "" : " -itags tool=" + metaArg.ToString()) + $" -new -- \"{outPath}\"";
LogDebug("mp4box命令: {0}", arguments);
return RunExe(MP4BOX, arguments, MP4BOX != "mp4box");
}

public static int MuxAV(bool useMp4box, string videoPath, string audioPath, List<AudioMaterial> audioMaterial, string outPath, string desc = "", string title = "", string author = "", string episodeId = "", string pic = "", string lang = "", List<Subtitle>? subs = null, bool audioOnly = false, bool videoOnly = false, List<ViewPoint>? points = null, long pubTime = 0)
{
if (audioOnly && audioPath != "")
videoPath = "";
if (videoOnly)
audioPath = "";
desc = EscapeString(desc);
title = EscapeString(title);
episodeId = EscapeString(episodeId);

if (useMp4box)
{
return MuxByMp4box(videoPath, audioPath, outPath, desc, title, author, episodeId, pic, lang, subs, audioOnly, videoOnly, points);
}

if (outPath.Contains('/') && ! Directory.Exists(Path.GetDirectoryName(outPath)))
Directory.CreateDirectory(Path.GetDirectoryName(outPath)!);
//----分析并生成-i参数
StringBuilder inputArg = new();
StringBuilder metaArg = new();
byte inputCount = 0;
foreach (string path in new string[] { videoPath, audioPath })
{
if (!string.IsNullOrEmpty(path))
{
inputCount++;
inputArg.Append($"-i \"{path}\" ");
}
}

if (audioMaterial.Any())
{
byte audioCount = 0;
metaArg.Append("-metadata:s:a:0 title=\"原音频\" ");
foreach (var audio in audioMaterial)
{
inputCount++;
audioCount++;
inputArg.Append($"-i \"{audio.path}\" ");
if (!string.IsNullOrWhiteSpace(audio.title)) metaArg.Append($"-metadata:s:a:{audioCount} title=\"{audio.title}\" ");
if (!string.IsNullOrWhiteSpace(audio.personName)) metaArg.Append($"-metadata:s:a:{audioCount} artist=\"{audio.personName}\" ");
}
}

if (!string.IsNullOrEmpty(pic))
{
inputCount++;
inputArg.Append($"-i \"{pic}\" ");
}

if (subs != null)
{
for (int i = 0; i < subs.Count; i++)
{
if(File.Exists(subs[i].path) && File.ReadAllText(subs[i].path!) != "")
{
inputCount++;
inputArg.Append($"-i \"{subs[i].path}\" ");
metaArg.Append($"-metadata:s:s:{i} title=\"{GetSubtitleCode(subs[i].lan).Item2}\" -metadata:s:s:{i} language={GetSubtitleCode(subs[i].lan).Item1} ");
}
}
}

if (!string.IsNullOrEmpty(pic))
metaArg.Append($"-disposition:v:{(audioOnly ? "0" : "1")} attached_pic ");
// var inputCount = InputRegex().Matches(inputArg.ToString()).Count;

if (points != null && points.Any())
{
var meta = GetFFmpegMetaString(points);
var metaFile = Path.Combine(Path.GetDirectoryName(string.IsNullOrEmpty(videoPath) ? audioPath : videoPath)!, "chapters");
File.WriteAllText(metaFile, meta);
inputArg.Append($"-i \"{metaFile}\" -map_chapters {inputCount} ");
}

inputArg.Append(string.Concat(Enumerable.Range(0, inputCount).Select(i => $"-map {i} ")));

//----分析完毕
StringBuilder argsBuilder = new StringBuilder();
argsBuilder.Append($"-loglevel {(Config.DEBUG_LOG ? "verbose" : "warning")} -y ");
argsBuilder.Append(inputArg);
argsBuilder.Append(metaArg);
argsBuilder.Append($"-metadata title=\"{(episodeId == "" ? title : episodeId)}\" ");
if (lang != "") argsBuilder.Append($"-metadata:s:a:0 language={lang} ");
if (!string.IsNullOrWhiteSpace(desc)) argsBuilder.Append($"-metadata description=\"{desc}\" ");
if (!string.IsNullOrEmpty(author)) argsBuilder.Append($"-metadata artist=\"{author}\" ");
if (episodeId != "") argsBuilder.Append($"-metadata album=\"{title}\" ");
if (pubTime != 0) argsBuilder.Append($"-metadata creation_time=\"{(DateTimeOffset.FromUnixTimeSeconds(pubTime).ToString("yyyy-MM-ddTHH:mm:ss.ffffffZ"))}\" ");
argsBuilder.Append("-c copy ");
if (audioOnly && audioPath == "") argsBuilder.Append("-vn ");
if (subs != null) argsBuilder.Append("-c:s mov_text ");
argsBuilder.Append($"-movflags faststart -strict unofficial -strict -2 -f mp4 -- \"{outPath}\"");

string arguments = argsBuilder.ToString();

LogDebug("ffmpeg命令: {0}", arguments);
return RunExe(FFMPEG, arguments, FFMPEG != "ffmpeg");
return code;
}

public static void MergeFLV(string[] files, string outPath)
Expand Down
67 changes: 45 additions & 22 deletions BBDown/BBDownUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -491,28 +491,6 @@ public static async Task<List<ViewPoint>> FetchPointsAsync(string cid, string ai
return ponints;
}

/// <summary>
/// 生成metadata文件, 用于ffmpeg混流章节信息
/// </summary>
/// <param name="points"></param>
/// <returns></returns>
public static string GetFFmpegMetaString(List<ViewPoint> points)
{
StringBuilder sb = new();
sb.AppendLine(";FFMETADATA");
foreach (var p in points)
{
var time = 1000; //固定 1000
sb.AppendLine("[CHAPTER]");
sb.AppendLine($"TIMEBASE=1/{time}");
sb.AppendLine($"START={p.start * time}");
sb.AppendLine($"END={p.end * time}");
sb.AppendLine($"title={p.title}");
sb.AppendLine();
}
return sb.ToString();
}

/// <summary>
/// 生成metadata文件, 用于mp4box混流章节信息
/// </summary>
Expand Down Expand Up @@ -578,6 +556,51 @@ public static async Task<bool> CheckLogin(string cookie)
}
}

public static string FormatSavePath(string savePathFormat, string title, Video? videoTrack, Audio? audioTrack, Page p, int pagesCount, string apiType, long pubTime, Subtitle? subtitle)
{
var result = savePathFormat.Replace('\\', '/');
var regex = InfoRegex();
foreach (Match m in regex.Matches(result).Cast<Match>())
{
var key = m.Groups[1].Value;
var v = key switch
{
"videoTitle" => GetValidFileName(title, filterSlash: true).Trim().TrimEnd('.').Trim(),
"pageNumber" => p.index.ToString(),
"pageNumberWithZero" => p.index.ToString().PadLeft((int)Math.Log10(pagesCount) + 1, '0'),
"pageTitle" => GetValidFileName(p.title, filterSlash: true).Trim().TrimEnd('.').Trim(),
"bvid" => p.bvid,
"aid" => p.aid,
"cid" => p.cid,
"ownerName" => p.ownerName == null ? "" : GetValidFileName(p.ownerName, filterSlash: true).Trim().TrimEnd('.').Trim(),
"ownerMid" => p.ownerMid ?? "",
"dfn" => videoTrack?.dfn ?? "",
"res" => videoTrack?.res ?? "",
"fps" => videoTrack?.fps ?? "",
"videoCodecs" => videoTrack?.codecs ?? "",
"videoBandwidth" => videoTrack?.bandwith.ToString() ?? "",
"audioCodecs" => audioTrack?.codecs ?? "",
"audioBandwidth" => audioTrack?.bandwith.ToString() ?? "",
"publishDate" => FormatTimeStamp(pubTime, "yyyy-MM-dd_HH-mm-ss"),
"videoDate" => FormatTimeStamp(p.pubTime, "yyyy-MM-dd_HH-mm-ss"),
"apiType" => apiType,
"subtitleType" => subtitle?.type ?? string.Empty,
"subtitleLan" => subtitle?.lan ?? string.Empty,
_ => $"<{key}>"
};
result = result.Replace(m.Value, v);
}
return result;
}

public static string FormatTimeStamp(long ts, string format)
{
return ts == 0 ? "null" : DateTimeOffset.FromUnixTimeSeconds(ts).ToLocalTime().ToString(format);
}

[GeneratedRegex("<(\\w+?)>")]
private static partial Regex InfoRegex();

[GeneratedRegex("av(\\d+)")]
private static partial Regex AvRegex();
[GeneratedRegex("[Bb][Vv](\\w+)")]
Expand Down
11 changes: 11 additions & 0 deletions BBDown/CommandLineExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.CommandLine;
using System.CommandLine.Parsing;

namespace BBDown
{
public static class CommandLineExtension
{
public static T? GetValueForOptionNullable<T>(this ParseResult parseResult, Option<T> option) where T : struct
=> parseResult.HasOption(option) ? parseResult.GetValueForOption<T>(option) : null;
}
}
Loading