Skip to content

Commit

Permalink
增加TV端番剧接口
Browse files Browse the repository at this point in the history
  • Loading branch information
nilaoda committed Aug 16, 2020
1 parent 3abb602 commit fbab2e9
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 22 deletions.
15 changes: 14 additions & 1 deletion BBDown/BBDownParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,21 @@ public static string GetPlayJson(string aid, string cid, string epId, bool tvApi
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);
//以下情况从网页源代码尝试解析
Expand Down
86 changes: 83 additions & 3 deletions BBDown/BBDownUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
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
Expand Down Expand Up @@ -182,7 +185,8 @@ public static async Task DownloadFile(string url, string path)
{
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;
Expand Down Expand Up @@ -228,7 +232,8 @@ public static void MultiThreadDownloadFile(string url, string path)
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)
Expand Down Expand Up @@ -358,7 +363,8 @@ public static string[] GetFiles(string dir, string ext)
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"]);
Expand Down Expand Up @@ -441,5 +447,79 @@ public static string GetSession(string buvid3)
//这个参数可以没有 所以此处就不写具体实现了
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;
}
}
}
83 changes: 77 additions & 6 deletions BBDown/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
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" },
Expand Down Expand Up @@ -49,6 +51,7 @@ class MyOption
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)
Expand Down Expand Up @@ -83,16 +86,24 @@ public static int Main(params string[] args)
"选择指定分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
Expand Down Expand Up @@ -147,6 +158,60 @@ public static int Main(params string[] args)
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());
Expand All @@ -160,7 +225,7 @@ private static async Task DoWorkAsync(MyOption myOption)
{
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");
Expand All @@ -177,18 +242,24 @@ private static async Task DoWorkAsync(MyOption myOption)
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);
Expand Down Expand Up @@ -312,7 +383,7 @@ private static async Task DoWorkAsync(MyOption myOption)
JArray video = null;

//此处代码简直灾难,后续优化吧
if (webJson.Contains("\"dash\":")) //dash
if (webJson.Contains("\"dash\":[")) //dash
{
string nodeName = "data";
if (webJson.Contains("\"result\":{"))
Expand Down
29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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进行下载
Expand All @@ -48,6 +50,9 @@ Commands:
- [ ] 其他的懒得写了

# 更新日志
* 2020年8月16日 0:04
支持TV版番剧下载
支持TV二维码登录
* 2020年8月10日 20:19
修复严重BUG
* 2020年8月9日 0:16
Expand Down

0 comments on commit fbab2e9

Please sign in to comment.