Skip to content

Commit

Permalink
fix #477 & #508, video&audio ascending separation
Browse files Browse the repository at this point in the history
  • Loading branch information
My-Responsitories committed Jun 9, 2023
1 parent 92373f6 commit d88b2e9
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 52 deletions.
2 changes: 2 additions & 0 deletions BBDown.Core/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class Config
//BiliPlus Area
public static string AREA { get; set; } = "";

public static string WBI { get; set; } = "";

public static readonly Dictionary<string, string> qualitys = new() {
{"127","8K 超高清" }, {"126","杜比视界" }, {"125","HDR 真彩" }, {"120","4K 超清" }, {"116","1080P 高帧率" },
{"112","1080P 高码率" }, {"80","1080P 高清" }, {"74","720P 高帧率" },
Expand Down
17 changes: 4 additions & 13 deletions BBDown.Core/Fetcher/SpaceVideoFetcher.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using BBDown.Core.Entity;
using System;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using static BBDown.Core.Util.HTTPUtil;
Expand All @@ -10,24 +9,16 @@ namespace BBDown.Core.Fetcher
{
public class SpaceVideoFetcher : IFetcher
{
public static string WbiSign(string api, string wbi)
{
return $"{api}&w_rid=" + string.Concat(MD5.HashData(Encoding.UTF8.GetBytes(api + wbi)).Select(i => i.ToString("x2")).ToArray());
}

public async Task<VInfo> FetchAsync(string id)
{
id = id[4..];
string[] tmp = id.Split("|");
id = tmp[0];
var wbi = tmp[1];
// using the live API can bypass w_rid
string userInfoApi = $"https://api.live.bilibili.com/live_user/v1/Master/info?uid={id}";
string userName = GetValidFileName(JsonDocument.Parse(await GetWebSourceAsync(userInfoApi)).RootElement.GetProperty("data").GetProperty("info").GetProperty("uname").ToString(), ".", true);
List<string> urls = new();
int pageSize = 50;
int pageNumber = 1;
var api = WbiSign($"mid={id}&order=pubdate&pn={pageNumber}&ps={pageSize}&tid=0&wts={DateTimeOffset.Now.ToUnixTimeSeconds().ToString()}", wbi);
var api = Parser.WbiSign($"mid={id}&order=pubdate&pn={pageNumber}&ps={pageSize}&tid=0&wts={DateTimeOffset.Now.ToUnixTimeSeconds().ToString()}");
api = $"https://api.bilibili.com/x/space/wbi/arc/search?{api}";
string json = await GetWebSourceAsync(api);
var infoJson = JsonDocument.Parse(json);
Expand All @@ -41,7 +32,7 @@ public async Task<VInfo> FetchAsync(string id)
while (pageNumber < totalPage)
{
pageNumber++;
urls.AddRange(await GetVideosByPageAsync(pageNumber, pageSize, id, wbi));
urls.AddRange(await GetVideosByPageAsync(pageNumber, pageSize, id));
}
File.WriteAllText($"{userName}的投稿视频.txt", string.Join('\n', urls));
Log("目前下载器不支持下载用户的全部投稿视频,不过程序已经获取到了该用户的全部投稿视频地址,你可以自行使用批处理脚本等手段调用本程序进行批量下载。如在Windows系统你可以使用如下代码:");
Expand All @@ -53,10 +44,10 @@ public async Task<VInfo> FetchAsync(string id)
throw new Exception("暂不支持该功能");
}

static async Task<List<string>> GetVideosByPageAsync(int pageNumber, int pageSize, string mid, string wbi)
static async Task<List<string>> GetVideosByPageAsync(int pageNumber, int pageSize, string mid)
{
List<string> urls = new();
var api = WbiSign($"mid={mid}&order=pubdate&pn={pageNumber}&ps={pageSize}&tid=0&wts={DateTimeOffset.Now.ToUnixTimeSeconds().ToString()}", wbi);
var api = Parser.WbiSign($"mid={mid}&order=pubdate&pn={pageNumber}&ps={pageSize}&tid=0&wts={DateTimeOffset.Now.ToUnixTimeSeconds().ToString()}");
api = $"https://api.bilibili.com/x/space/wbi/arc/search?{api}";
string json = await GetWebSourceAsync(api);
var infoJson = JsonDocument.Parse(json);
Expand Down
36 changes: 27 additions & 9 deletions BBDown.Core/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ namespace BBDown.Core
{
public partial class Parser
{
public static string WbiSign(string api)
{
return $"{api}&w_rid=" + string.Concat(MD5.HashData(Encoding.UTF8.GetBytes(api + Config.WBI)).Select(i => i.ToString("x2")).ToArray());
}

private static async Task<string> GetPlayJsonAsync(bool onlyAvc, string aidOri, string aid, string cid, string epId, bool tvApi, bool intl, bool appApi, string qn = "0")
{
LogDebug("aid={0},cid={1},epId={2},tvApi={3},IntlApi={4},appApi={5},qn={6}", aid, cid, epId, tvApi, intl, appApi, qn);
Expand All @@ -24,13 +29,14 @@ private static async Task<string> GetPlayJsonAsync(bool onlyAvc, string aidOri,
if (appApi) return await AppHelper.DoReqAsync(aid, cid, epId, qn, bangumi, onlyAvc, Config.TOKEN);

string prefix = tvApi ? bangumi ? "api.snm0516.aisee.tv/pgc/player/api/playurltv" : "api.snm0516.aisee.tv/x/tv/ugc/playurl"
: bangumi ? $"{Config.HOST}/pgc/player/web/playurl" : $"{Config.HOST}/x/player/playurl";
string api = $"https://{prefix}?avid={aid}&cid={cid}&qn={qn}&type=&otype=json" + (tvApi ? "" : "&fourk=1") +
$"&fnver=0&fnval=4048" + (Config.AREA != "" ? Config.TOKEN != "" ? $"&access_key={Config.TOKEN}&area={Config.AREA}" : $"&area={Config.AREA}" : "") +
(tvApi ? "&device=android&platform=android" +
"&mobi_app=android_tv_yst&npcybs=0&force_host=2&build=102801" +
(Config.TOKEN != "" ? $"&access_key={Config.TOKEN}" : "") : "") +
(bangumi ? $"&module=bangumi&ep_id={epId}&fourk=1" + "&session=" : "");
: bangumi ? $"{Config.HOST}/pgc/player/web/playurl" : "api.bilibili.com/x/player/wbi/playurl";

string api = $"avid={aid}&cid={cid}&fnval=4048&fnver=0" +
(tvApi ? "" : "&fourk=1") + (Config.AREA != "" ? Config.TOKEN != "" ? $"&access_key={Config.TOKEN}&area={Config.AREA}" : $"&area={Config.AREA}" : "") +
(tvApi ? "&device=android&platform=android&mobi_app=android_tv_yst&npcybs=0&force_host=2&build=102801" +
(Config.TOKEN != "" ? $"&access_key={Config.TOKEN}" : "") : "") + $"&otype=json&qn={qn}" +
(bangumi ? $"&module=bangumi&ep_id={epId}&fourk=1&session=" : $"&try_look=1&wts={GetTimeStamp(true)}");
api = $"https://{prefix}?{(bangumi ? api : WbiSign(api))}";
if (tvApi && bangumi)
{
api = (Config.TOKEN != "" ? $"access_key={Config.TOKEN}&" : "") +
Expand Down Expand Up @@ -79,6 +85,12 @@ private static async Task<string> GetPlayJsonAsync(string aid, string cid, strin
return webJson;
}

public static string SkiPcdn(List<string> urlList, string baseUrl)
{
urlList.Add(baseUrl);
return urlList.FirstOrDefault(i => !PcdnRegex().IsMatch(i), urlList.First());
}

public static async Task<(string, List<Video>, List<Audio>, List<string>, List<string>)> ExtractTracksAsync(string aidOri, string aid, string cid, string epId, bool tvApi, bool intlApi, bool appApi, string qn = "0")
{
List<Video> videoTracks = new();
Expand Down Expand Up @@ -106,13 +118,15 @@ private static async Task<string> GetPlayJsonAsync(string aid, string cid, strin
if (dashVideo.GetProperty("base_url").ToString() != "")
{
var videoId = stream.GetProperty("stream_info").GetProperty("quality").ToString();
var urlList = new List<string>() { dashVideo.GetProperty("base_url").ToString() };
urlList.AddRange(dashVideo.GetProperty("backup_url").EnumerateArray().ToList().Select(i => i.ToString()));
Video v = new()
{
dur = pDur,
id = videoId,
dfn = Config.qualitys[videoId],
bandwith = Convert.ToInt64(dashVideo.GetProperty("bandwidth").ToString()) / 1000,
baseUrl = dashVideo.GetProperty("base_url").ToString(),
baseUrl = urlList.FirstOrDefault(i => !BaseUrlRegex().IsMatch(i), urlList.First()),
codecs = GetVideoCodec(dashVideo.GetProperty("codecid").ToString()),
size = dashVideo.TryGetProperty("size", out var sizeNode) ? Convert.ToDouble(sizeNode.ToString()) : 0
};
Expand All @@ -123,13 +137,15 @@ private static async Task<string> GetPlayJsonAsync(string aid, string cid, strin

foreach(var node in audio)
{
var urlList = new List<string>() { node.GetProperty("base_url").ToString() };
urlList.AddRange(node.GetProperty("backup_url").EnumerateArray().ToList().Select(i => i.ToString()));
Audio a = new()
{
id = node.GetProperty("id").ToString(),
dfn = node.GetProperty("id").ToString(),
dur = pDur,
bandwith = Convert.ToInt64(node.GetProperty("bandwidth").ToString()) / 1000,
baseUrl = node.GetProperty("base_url").ToString(),
baseUrl = urlList.FirstOrDefault(i => !BaseUrlRegex().IsMatch(i), urlList.First()),
codecs = "M4A"
};
if (!audioTracks.Contains(a)) audioTracks.Add(a);
Expand Down Expand Up @@ -393,5 +409,7 @@ private static string GetSign(string parms, bool isBiliPlus)
private static partial Regex PlayerJsonRegex();
[GeneratedRegex("http.*:\\d+")]
private static partial Regex BaseUrlRegex();
[GeneratedRegex("://.*:\\d+/")]
private static partial Regex PcdnRegex();
}
}
14 changes: 7 additions & 7 deletions BBDown/BBDownUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -801,23 +801,23 @@ public static string RSubString(string sub)
return sub[..sub.LastIndexOf(".")];
}

public static string GetMixinKey(string orig)
private static string GetMixinKey(string orig)
{
byte[] mixinKeyEncTab = new byte[]
{
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35,
27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13
};

var tmp = new StringBuilder();
var tmp = new StringBuilder(32);
foreach (var index in mixinKeyEncTab)
{
tmp.Append(orig[index]);
}
return tmp.ToString();
}

public static async Task<(bool, string)> CheckLogin(string cookie)
public static async Task<bool> CheckLogin(string cookie)
{
try
{
Expand All @@ -826,13 +826,13 @@ public static string GetMixinKey(string orig)
var json = JsonDocument.Parse(source).RootElement;
var is_login = json.GetProperty("data").GetProperty("isLogin").GetBoolean();
var wbi_img = json.GetProperty("data").GetProperty("wbi_img");
var wbi = GetMixinKey(RSubString(wbi_img.GetProperty("img_url").GetString()) + RSubString(wbi_img.GetProperty("sub_url").GetString()));
LogDebug($"wbi: {wbi}");
return (is_login, wbi);
Core.Config.WBI = GetMixinKey(RSubString(wbi_img.GetProperty("img_url").GetString()) + RSubString(wbi_img.GetProperty("sub_url").GetString()));
LogDebug("wbi: {0}", Core.Config.WBI);
return is_login;
}
catch (Exception)
{
return (false, "");
return false;
}
}

Expand Down
17 changes: 13 additions & 4 deletions BBDown/CommandLineInvoker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ internal class CommandLineInvoker
private readonly static Option<bool> ForceHttp = new(new string[] { "--force-http" }, "下载音视频时强制使用HTTP协议替换HTTPS(默认开启)");
private readonly static Option<bool> DownloadDanmaku = new(new string[] { "--download-danmaku", "-dd" }, "下载弹幕");
private readonly static Option<bool> SkipAi = new(new string[] { "--skip-ai" }, description: "跳过AI字幕下载");
private readonly static Option<bool> BandwithAscending = new(new string[] { "--bandwith-ascending" }, "比特率升序(最小体积优先)");
private readonly static Option<bool> VideoAscending = new(new string[] { "--video-ascending" }, "视频升序(最小体积优先)");
private readonly static Option<bool> AudioAscending = new(new string[] { "--audio-ascending" }, "音频升序(最小体积优先)");
private readonly static Option<bool> AllowPcdn = new(new string[] { "--allow-pcdn" }, "不替换PCDN域名, 仅在正常情况与--upos-host均无法下载时使用");
private readonly static Option<string> Language = new(new string[] { "--language" }, "设置混流的音频语言(代码), 如chi, jpn等");
private readonly static Option<string> Cookie = new(new string[] { "--cookie", "-c" }, "设置字符串cookie用以下载网页接口的会员内容");
private readonly static Option<string> AccessToken = new(new string[] { "--access-token", "-token" }, "设置access_token用以下载TV/APP接口的会员内容");
Expand All @@ -60,6 +62,7 @@ internal class CommandLineInvoker
private readonly static Option<bool> OnlyAv1 = new(new string[] { "--only-av1", "-av1" }, "只下载av1编码") { IsHidden = true };
private readonly static Option<bool> AddDfnSubfix = new(new string[] { "--add-dfn-subfix" }, "为文件加入清晰度后缀, 如XXX[1080P 高码率]") { IsHidden = true };
private readonly static Option<bool> NoPaddingPageNum = new(new string[] { "--no-padding-page-num" }, "不给分P序号补零") { IsHidden = true };
private readonly static Option<bool> BandwithAscending = new(new string[] { "--bandwith-ascending" }, "比特率升序(最小体积优先)") { IsHidden = true };


class MyOptionBinder : BinderBase<MyOption>
Expand Down Expand Up @@ -95,7 +98,9 @@ protected override MyOption GetBoundValue(BindingContext bindingContext)
if (bindingContext.ParseResult.HasOption(ForceHttp)) option.ForceHttp = bindingContext.ParseResult.GetValueForOption(ForceHttp)!;
if (bindingContext.ParseResult.HasOption(DownloadDanmaku)) option.DownloadDanmaku = bindingContext.ParseResult.GetValueForOption(DownloadDanmaku)!;
if (bindingContext.ParseResult.HasOption(SkipAi)) option.SkipAi = bindingContext.ParseResult.GetValueForOption(SkipAi)!;
if (bindingContext.ParseResult.HasOption(BandwithAscending)) option.BandwithAscending = bindingContext.ParseResult.GetValueForOption(BandwithAscending)!;
if (bindingContext.ParseResult.HasOption(VideoAscending)) option.VideoAscending = bindingContext.ParseResult.GetValueForOption(VideoAscending)!;
if (bindingContext.ParseResult.HasOption(AudioAscending)) option.AudioAscending = bindingContext.ParseResult.GetValueForOption(AudioAscending)!;
if (bindingContext.ParseResult.HasOption(AllowPcdn)) option.AllowPcdn = bindingContext.ParseResult.GetValueForOption(AllowPcdn)!;
if (bindingContext.ParseResult.HasOption(FilePattern)) option.FilePattern = bindingContext.ParseResult.GetValueForOption(FilePattern)!;
if (bindingContext.ParseResult.HasOption(MultiFilePattern)) option.MultiFilePattern = bindingContext.ParseResult.GetValueForOption(MultiFilePattern)!;
if (bindingContext.ParseResult.HasOption(SelectPage)) option.SelectPage = bindingContext.ParseResult.GetValueForOption(SelectPage)!;
Expand All @@ -119,6 +124,7 @@ protected override MyOption GetBoundValue(BindingContext bindingContext)
if (bindingContext.ParseResult.HasOption(OnlyAv1)) option.OnlyAv1 = bindingContext.ParseResult.GetValueForOption(OnlyAv1)!;
if (bindingContext.ParseResult.HasOption(AddDfnSubfix)) option.AddDfnSubfix = bindingContext.ParseResult.GetValueForOption(AddDfnSubfix)!;
if (bindingContext.ParseResult.HasOption(NoPaddingPageNum)) option.NoPaddingPageNum = bindingContext.ParseResult.GetValueForOption(NoPaddingPageNum)!;
if (bindingContext.ParseResult.HasOption(BandwithAscending)) option.BandwithAscending = bindingContext.ParseResult.GetValueForOption(BandwithAscending)!;
return option;
}
}
Expand Down Expand Up @@ -152,7 +158,9 @@ public static RootCommand GetRootCommand(Func<MyOption, Task> action)
ForceHttp,
DownloadDanmaku,
SkipAi,
BandwithAscending,
VideoAscending,
AudioAscending,
AllowPcdn,
FilePattern,
MultiFilePattern,
SelectPage,
Expand All @@ -175,7 +183,8 @@ public static RootCommand GetRootCommand(Func<MyOption, Task> action)
OnlyAvc,
OnlyAv1,
AddDfnSubfix,
NoPaddingPageNum
NoPaddingPageNum,
BandwithAscending
};

rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());
Expand Down
5 changes: 4 additions & 1 deletion BBDown/MyOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ internal class MyOption
public bool ForceHttp { get; set; } = true;
public bool DownloadDanmaku { get; set; } = false;
public bool SkipAi { get; set; } = true;
public bool BandwithAscending { get; set; } = false;
public bool VideoAscending { get; set; } = false;
public bool AudioAscending { get; set; } = false;
public bool AllowPcdn { get; set; } = false;
public string FilePattern { get; set; } = "";
public string MultiFilePattern { get; set; } = "";
public string SelectPage { get; set; } = "";
Expand All @@ -58,5 +60,6 @@ internal class MyOption
public bool OnlyAv1 { get; set; }
public bool AddDfnSubfix { get; set; }
public bool NoPaddingPageNum { get; set; }
public bool BandwithAscending { get; set; }
}
}
Loading

0 comments on commit d88b2e9

Please sign in to comment.