mirror of
https://github.com/nilaoda/BBDown.git
synced 2026-10-23 08:18:39 +08:00
增加TV端番剧接口
This commit is contained in:
+14
-1
@@ -13,8 +13,21 @@ namespace BBDown
|
||||
string prefix = tvApi ? (bangumi ? "api.snm0516.aisee.tv/pgc/player/api/playurltv" : "api.snm0516.aisee.tv/x/tv/ugc/playurl")
|
||||
: (bangumi ? "api.bilibili.com/pgc/player/web/playurl" : "api.bilibili.com/x/player/playurl");
|
||||
string api = $"https://{prefix}?avid={aid}&cid={cid}&qn={qn}&type=&otype=json" + (tvApi ? "" : "&fourk=1") +
|
||||
"&fnver=0&fnval=16" + (tvApi ? "&device=android&platform=android&mobi_app=android_tv_yst&npcybs=0&force_host=0&build=102801" : "") +
|
||||
"&fnver=0&fnval=16" + (tvApi ? "&device=android&platform=android" +
|
||||
"&mobi_app=android_tv_yst&npcybs=0&force_host=0&build=102801" +
|
||||
(Program.TOKEN != "" ? $"&access_key={GetQueryString("access_token", Program.TOKEN)}" : "") : "") +
|
||||
(bangumi ? $"&module=bangumi&ep_id={epId}&fourk=1" + "&session=" : "");
|
||||
if (tvApi && bangumi)
|
||||
{
|
||||
api = (Program.TOKEN != "" ? $"access_key={GetQueryString("access_token", Program.TOKEN)}&" : "") +
|
||||
$"aid={aid}&appkey=4409e2ce8ffd12b8&build=102801" +
|
||||
$"&cid={cid}&device=android&ep_id={epId}&expire=0" +
|
||||
$"&fnval=16&fnver=0&fourk=1" +
|
||||
$"&mid=0&mobi_app=android_tv_yst" +
|
||||
$"&module=bangumi&npcybs=0&otype=json&platform=android" +
|
||||
$"&qn={qn}&ts={GetTimeStamp(true)}";
|
||||
api = $"https://{prefix}?" + api + (bangumi ? $"&sign={GetSign(api)}" : "");
|
||||
}
|
||||
//Console.WriteLine(api);
|
||||
string webJson = GetWebSource(api);
|
||||
//以下情况从网页源代码尝试解析
|
||||
|
||||
+83
-3
@@ -6,13 +6,16 @@ using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Cache;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using static BBDown.BBDownEntity;
|
||||
|
||||
namespace BBDown
|
||||
@@ -182,7 +185,8 @@ namespace BBDown
|
||||
{
|
||||
long totalLength = -1;
|
||||
WebClient client = new WebClient();
|
||||
client.Headers.Add("Referer", "https://www.bilibili.com");
|
||||
if (!url.Contains("platform=android_tv_yst"))
|
||||
client.Headers.Add("Referer", "https://www.bilibili.com");
|
||||
client.Headers.Add("User-Agent", "Mozilla/5.0");
|
||||
client.Headers.Add("Cookie", Program.COOKIE);
|
||||
client.Credentials = CredentialCache.DefaultCredentials;
|
||||
@@ -228,7 +232,8 @@ namespace BBDown
|
||||
request.AllowAutoRedirect = true;
|
||||
request.KeepAlive = false;
|
||||
request.Method = "GET";
|
||||
request.Referer = "https://www.bilibili.com";
|
||||
if (!url.Contains("platform=android_tv_yst"))
|
||||
request.Referer = "https://www.bilibili.com";
|
||||
request.UserAgent = "Mozilla/5.0";
|
||||
request.Headers.Add("Cookie", Program.COOKIE);
|
||||
if (clip.to != -1)
|
||||
@@ -358,7 +363,8 @@ namespace BBDown
|
||||
private static long GetFileSize(string url)
|
||||
{
|
||||
WebClient webClient = new WebClient();
|
||||
webClient.Headers.Add("Referer", "https://www.bilibili.com");
|
||||
if (!url.Contains("platform=android_tv_yst"))
|
||||
webClient.Headers.Add("Referer", "https://www.bilibili.com");
|
||||
webClient.Headers.Add("User-Agent", "Mozilla/5.0");
|
||||
webClient.OpenRead(url);
|
||||
long totalSizeBytes = Convert.ToInt64(webClient.ResponseHeaders["Content-Length"]);
|
||||
@@ -441,5 +447,79 @@ namespace BBDown
|
||||
//这个参数可以没有 所以此处就不写具体实现了
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static string GetSign(string parms)
|
||||
{
|
||||
string toEncode = parms + "59b43e04ad6965f34319062b478f83dd";
|
||||
MD5 md5 = MD5.Create();
|
||||
byte[] bs = Encoding.UTF8.GetBytes(toEncode);
|
||||
byte[] hs = md5.ComputeHash(bs);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (byte b in hs)
|
||||
{
|
||||
sb.Append(b.ToString("x2"));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string GetTimeStamp(bool bflag)
|
||||
{
|
||||
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
||||
string ret = string.Empty;
|
||||
if (bflag)
|
||||
ret = Convert.ToInt64(ts.TotalSeconds).ToString();
|
||||
else
|
||||
ret = Convert.ToInt64(ts.TotalMilliseconds).ToString();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings
|
||||
private static Random random = new Random();
|
||||
public static string GetRandomString(int length)
|
||||
{
|
||||
const string chars = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789";
|
||||
return new string(Enumerable.Repeat(chars, length)
|
||||
.Select(s => s[random.Next(s.Length)]).ToArray());
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/a/45088333
|
||||
public static string ToQueryString(NameValueCollection nameValueCollection)
|
||||
{
|
||||
NameValueCollection httpValueCollection = HttpUtility.ParseQueryString(String.Empty);
|
||||
httpValueCollection.Add(nameValueCollection);
|
||||
return httpValueCollection.ToString();
|
||||
}
|
||||
|
||||
public static NameValueCollection GetTVLoginParms()
|
||||
{
|
||||
NameValueCollection sb = new NameValueCollection();
|
||||
DateTime now = DateTime.Now;
|
||||
string deviceId = GetRandomString(20);
|
||||
string buvid = GetRandomString(37);
|
||||
string fingerprint = $"{now.ToString("yyyyMMddHHmmssfff")}{GetRandomString(45)}";
|
||||
sb.Add("appkey","4409e2ce8ffd12b8");
|
||||
sb.Add("auth_code", "");
|
||||
sb.Add("bili_local_id", deviceId);
|
||||
sb.Add("build", "102801");
|
||||
sb.Add("buvid", buvid);
|
||||
sb.Add("channel", "master");
|
||||
sb.Add("device", "OnePlus");
|
||||
sb.Add($"device_id", deviceId);
|
||||
sb.Add("device_name", "OnePlus7TPro");
|
||||
sb.Add("device_platform", "Android10OnePlusHD1910");
|
||||
sb.Add($"fingerprint", fingerprint);
|
||||
sb.Add($"guid", buvid);
|
||||
sb.Add($"local_fingerprint", fingerprint);
|
||||
sb.Add($"local_id", buvid);
|
||||
sb.Add("mobi_app", "android_tv_yst");
|
||||
sb.Add("networkstate", "wifi");
|
||||
sb.Add("platform", "android");
|
||||
sb.Add("sys_ver", "29");
|
||||
sb.Add($"ts", GetTimeStamp(true));
|
||||
sb.Add($"sign", GetSign(ToQueryString(sb)));
|
||||
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+77
-6
@@ -16,12 +16,14 @@ using static BBDown.BBDownUtil;
|
||||
using static BBDown.BBDownParser;
|
||||
using static BBDown.BBDownLogger;
|
||||
using static BBDown.BBDownMuxer;
|
||||
using System.Text;
|
||||
|
||||
namespace BBDown
|
||||
{
|
||||
class Program
|
||||
{
|
||||
public static string COOKIE = "";
|
||||
public static string TOKEN = "";
|
||||
static Dictionary<string, string> qualitys = new Dictionary<string, string>() {
|
||||
{"120","超清 4K" }, {"116","高清 1080P60" },{"112","高清 1080P+" },
|
||||
{"80","高清 1080P" }, {"74","高清 720P60" },{"64","高清 720P" },
|
||||
@@ -49,6 +51,7 @@ namespace BBDown
|
||||
public bool MultiThread { get; set; }
|
||||
public string SelectPage { get; set; }
|
||||
public string Cookie { get; set; }
|
||||
public string AccessToken { get; set; }
|
||||
}
|
||||
|
||||
public static int Main(params string[] args)
|
||||
@@ -83,16 +86,24 @@ namespace BBDown
|
||||
"选择指定分p或分p范围"),
|
||||
new Option<string>(
|
||||
new string[]{ "--cookie" ,"-c"},
|
||||
"设置字符串cookie用以下载网页接口的会员内容")
|
||||
"设置字符串cookie用以下载网页接口的会员内容"),
|
||||
new Option<string>(
|
||||
new string[]{ "--access-token" ,"-a"},
|
||||
"设置access_token用以下载TV接口的会员内容")
|
||||
};
|
||||
|
||||
Command loginCommand = new Command(
|
||||
"login",
|
||||
"通过APP扫描二维码以登录您的账号");
|
||||
"通过APP扫描二维码以登录您的WEB账号");
|
||||
rootCommand.AddCommand(loginCommand);
|
||||
Command loginTVCommand = new Command(
|
||||
"logintv",
|
||||
"通过APP扫描二维码以登录您的TV账号");
|
||||
rootCommand.AddCommand(loginTVCommand);
|
||||
rootCommand.Description = "BBDown是一个免费且便捷高效的哔哩哔哩下载/解析软件.";
|
||||
rootCommand.TreatUnmatchedTokensAsErrors = true;
|
||||
|
||||
//WEB登录
|
||||
loginCommand.Handler = CommandHandler.Create(delegate
|
||||
{
|
||||
try
|
||||
@@ -147,6 +158,60 @@ namespace BBDown
|
||||
catch (Exception e) { LogError(e.Message); }
|
||||
});
|
||||
|
||||
//TV登录
|
||||
loginTVCommand.Handler = CommandHandler.Create(delegate
|
||||
{
|
||||
try
|
||||
{
|
||||
string loginUrl = "https://passport.snm0516.aisee.tv/x/passport-tv-login/qrcode/auth_code";
|
||||
string pollUrl = "https://passport.bilibili.com/x/passport-tv-login/qrcode/poll";
|
||||
var parms = GetTVLoginParms();
|
||||
Log("获取登录地址...");
|
||||
WebClient webClient = new WebClient();
|
||||
byte[] responseArray = webClient.UploadValues(loginUrl, parms);
|
||||
string web = Encoding.UTF8.GetString(responseArray);
|
||||
string url = JObject.Parse(web)["data"]["url"].ToString();
|
||||
string authCode = JObject.Parse(web)["data"]["auth_code"].ToString();
|
||||
Log("生成二维码...");
|
||||
QRCodeGenerator qrGenerator = new QRCodeGenerator();
|
||||
QRCodeData qrCodeData = qrGenerator.CreateQrCode(url, QRCodeGenerator.ECCLevel.Q);
|
||||
QRCode qrCode = new QRCode(qrCodeData);
|
||||
Bitmap qrCodeImage = qrCode.GetGraphic(7);
|
||||
qrCodeImage.Save("qrcode.png", System.Drawing.Imaging.ImageFormat.Png);
|
||||
Log("生成二维码成功:qrcode.png, 请打开并扫描");
|
||||
parms.Set("auth_code", authCode);
|
||||
parms.Set("ts", GetTimeStamp(true));
|
||||
parms.Remove("sign");
|
||||
parms.Add("sign", GetSign(ToQueryString(parms)));
|
||||
while (true)
|
||||
{
|
||||
Thread.Sleep(1000);
|
||||
responseArray = webClient.UploadValues(pollUrl, parms);
|
||||
web = Encoding.UTF8.GetString(responseArray);
|
||||
string code = JObject.Parse(web)["code"].ToString();
|
||||
if (code == "86038")
|
||||
{
|
||||
LogColor("二维码已过期, 请重新执行登录指令.");
|
||||
break;
|
||||
}
|
||||
else if (code == "86039") //等待扫码
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
string cc = JObject.Parse(web)["data"]["access_token"].ToString();
|
||||
Log("登录成功: AccessToken=" + cc);
|
||||
//导出cookie
|
||||
File.WriteAllText(Path.Combine(AppContext.BaseDirectory, "BBDownTV.data"), "access_token=" + cc);
|
||||
File.Delete("qrcode.png");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) { LogError(e.Message); }
|
||||
});
|
||||
|
||||
rootCommand.Handler = CommandHandler.Create<MyOption>(async (myOption) =>
|
||||
{
|
||||
//Console.WriteLine(myOption.ToString());
|
||||
@@ -160,7 +225,7 @@ namespace BBDown
|
||||
{
|
||||
Console.BackgroundColor = ConsoleColor.DarkBlue;
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.Write("BBDown version 20200810[RC2], Bilibili Downloader.\r\n");
|
||||
Console.Write("BBDown version 20200816[RC3], Bilibili Downloader.\r\n");
|
||||
Console.ResetColor();
|
||||
Console.Write("请注意:这是一个测试版本,任何BUG请前往以下网址反馈:\r\n" +
|
||||
"https://github.com/nilaoda/BBDown/issues\r\n");
|
||||
@@ -177,18 +242,24 @@ namespace BBDown
|
||||
string selectPage = myOption.SelectPage;
|
||||
string aid = "";
|
||||
COOKIE = myOption.Cookie;
|
||||
TOKEN = myOption.AccessToken.Replace("access_token=", "");
|
||||
List<string> selectedPages = null;
|
||||
if (!string.IsNullOrEmpty(GetQueryString("p", input)))
|
||||
{
|
||||
selectedPages = new List<string>();
|
||||
selectedPages.Add(GetQueryString("p", input));
|
||||
}
|
||||
|
||||
if (File.Exists(Path.Combine(AppContext.BaseDirectory, "BBDown.data")))
|
||||
|
||||
if (File.Exists(Path.Combine(AppContext.BaseDirectory, "BBDown.data")) && !tvApi)
|
||||
{
|
||||
Log("加载本地cookie...");
|
||||
COOKIE = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "BBDown.data"));
|
||||
}
|
||||
if (File.Exists(Path.Combine(AppContext.BaseDirectory, "BBDownTV.data")) && tvApi)
|
||||
{
|
||||
Log("加载本地token...");
|
||||
TOKEN = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "BBDownTV.data"));
|
||||
}
|
||||
Log("获取aid...");
|
||||
aid = await GetAvIdAsync(input);
|
||||
Log("获取aid结束: " + aid);
|
||||
@@ -312,7 +383,7 @@ namespace BBDown
|
||||
JArray video = null;
|
||||
|
||||
//此处代码简直灾难,后续优化吧
|
||||
if (webJson.Contains("\"dash\":")) //dash
|
||||
if (webJson.Contains("\"dash\":[")) //dash
|
||||
{
|
||||
string nodeName = "data";
|
||||
if (webJson.Contains("\"result\":{"))
|
||||
|
||||
@@ -17,23 +17,25 @@ Arguments:
|
||||
<url> 视频地址 或 av|bv|BV|ep|ss
|
||||
|
||||
Options:
|
||||
-tv, --use-tv-api 使用TV端解析模式(不支持版权内容)
|
||||
-hevc, --only-hevc 下载hevc编码
|
||||
-info, --only-show-info 仅解析不下载
|
||||
-hs, --hide-streams 不要显示所有可用音视频流
|
||||
-ia, --interactive 交互式选择清晰度
|
||||
-mt, --multi-thread 使用多线程下载
|
||||
-p, --select-page <select-page> 选择指定分p或分p范围
|
||||
-c, --cookie <cookie> 设置字符串cookie用以下载网页接口的会员内容
|
||||
--version Show version information
|
||||
-?, -h, --help Show help and usage information
|
||||
-tv, --use-tv-api 使用TV端解析模式(不支持版权内容)
|
||||
-hevc, --only-hevc 下载hevc编码
|
||||
-info, --only-show-info 仅解析不下载
|
||||
-hs, --hide-streams 不要显示所有可用音视频流
|
||||
-ia, --interactive 交互式选择清晰度
|
||||
-mt, --multi-thread 使用多线程下载
|
||||
-p, --select-page <select-page> 选择指定分p或分p范围
|
||||
-c, --cookie <cookie> 设置字符串cookie用以下载网页接口的会员内容
|
||||
-a, --access-token <access-token> 设置access_token用以下载TV接口的会员内容
|
||||
--version Show version information
|
||||
-?, -h, --help Show help and usage information
|
||||
|
||||
Commands:
|
||||
login 通过APP扫描二维码以登录您的账号
|
||||
login 通过APP扫描二维码以登录您的WEB账号
|
||||
logintv 通过APP扫描二维码以登录您的TV账号
|
||||
```
|
||||
|
||||
# 功能
|
||||
- [x] 番剧下载(Web)
|
||||
- [x] 番剧下载(Web|TV)
|
||||
- [x] 普通内容下载(Web|TV) `(TV接口可以下载部分UP主的无水印内容)`
|
||||
- [x] 多分P自动下载
|
||||
- [x] 选择指定分P进行下载
|
||||
@@ -48,6 +50,9 @@ Commands:
|
||||
- [ ] 其他的懒得写了
|
||||
|
||||
# 更新日志
|
||||
* 2020年8月16日 0:04
|
||||
支持TV版番剧下载
|
||||
支持TV二维码登录
|
||||
* 2020年8月10日 20:19
|
||||
修复严重BUG
|
||||
* 2020年8月9日 0:16
|
||||
|
||||
Reference in New Issue
Block a user