Initial commit

This commit is contained in:
net909
2023-09-28 16:21:06 +08:00
parent 498772eb04
commit ba7be004c0
640 changed files with 87745 additions and 0 deletions
+477
View File
@@ -0,0 +1,477 @@
<?php
namespace app\lib;
use Exception;
class BilibiliHelper
{
private $cookie;
private $token;
private $mixinKey;
public static $qualitys = ['127'=>'8K 超高清', '126'=>'杜比视界', '125'=>'HDR 真彩', '120'=>'4K 超清', '116'=>'1080P 高帧率', '112'=>'1080P 高码率', '80'=>'1080P 高清', '74'=>'720P 高帧率', '64'=>'720P 高清', '48'=>'720P 高清', '32'=>'480P 清晰', '16'=>'360P 流畅', '6'=>'240P 极速'];
public static $qualitys_audio = ['30216'=>'64K', '30232'=>'132K', '30280'=>'192K'];
public function __construct($cookie = null, $token = null)
{
$this->cookie = $cookie; //For WEB
$this->token = $token; //For APP/TV
}
//获取登录信息
public function login_info()
{
$url = 'https://api.bilibili.com/x/web-interface/nav';
$ret = $this->curl($url, null, $this->cookie);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取登录状态失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
return true;
}elseif($arr['code'] == -101){
throw new Exception('COOKIE已失效');
}else{
throw new Exception('获取登录状态失败 '.$arr['message']);
}
}
//获取用户上传视频信息
public function ugc_video_info($querystring){
$url = 'https://api.bilibili.com/x/web-interface/view?'.$querystring;
$ret = $this->curl($url, null, $this->cookie);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取视频信息失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
return $arr['data'];
}else{
throw new Exception('获取视频信息失败:'.$arr['message']);
}
}
//获取正版视频信息
public function pgc_video_info($ep_id){
$url = 'https://api.bilibili.com/pgc/view/web/season?ep_id='.$ep_id;
$ret = $this->curl($url, null, $this->cookie);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取视频信息失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
if(!isset($arr['result']['episodes'])) throw new Exception('获取视频信息失败,返回内容错误');
$data = null;
foreach($arr['result']['episodes'] as $row){
if($ep_id == $row['id']){
$data = $row;
}
}
if(empty($data))throw new Exception('获取视频信息失败,未找到对应视频信息');
return $data;
}else{
throw new Exception('获取视频信息失败:'.$arr['message']);
}
}
//获取正版视频信息
public function pgc_video_info_by_ssid($season_id){
$url = 'https://api.bilibili.com/pgc/view/web/season?season_id='.$season_id;
$ret = $this->curl($url, null, $this->cookie);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取视频信息失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
if(!isset($arr['result']['episodes'])) throw new Exception('获取视频信息失败,返回内容错误');
$data = $arr['result']['episodes'][0];
if(empty($data))throw new Exception('获取视频信息失败,未找到对应视频信息');
return $data;
}else{
throw new Exception('获取视频信息失败:'.$arr['message']);
}
}
//获取课堂视频信息
public function pugv_video_info($ep_id){
$url = 'https://api.bilibili.com/pugv/view/web/season?ep_id='.$ep_id;
$ret = $this->curl($url, null, $this->cookie);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取视频信息失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
if(!isset($arr['data']['episodes'])) throw new Exception('获取视频信息失败,返回内容错误');
$data = null;
foreach($arr['data']['episodes'] as $row){
if($ep_id == $row['id']){
$data = $row;
}
}
if(empty($data))throw new Exception('获取视频信息失败,未找到对应视频信息');
return $data;
}else{
throw new Exception('获取视频信息失败:'.$arr['message']);
}
}
//获取视频弹幕
public function get_video_comment($cid){
$danmu_xml = $this->curl('https://comment.bilibili.com/'.$cid.'.xml');
if(!$danmu_xml){
return msg('error','获取弹幕内容失败');
}
$dom = new \DOMDocument();
$dom->loadXML($danmu_xml);
$result = $this->getArray($dom->documentElement);
return isset($result['d']) ? $result['d'] : [];
}
//用户上传视频解析(支持外链)
public function get_video_url($aid, $cid){
$param = [
'avid' => $aid,
'cid' => $cid,
'qn' => '120',
'otype' => 'json',
'fourk' => '1',
'fnver' => '0',
'fnval' => '128',
'player' => '3',
'platform' => 'html5',
'high_quality' => '1',
];
$url = 'https://api.bilibili.com/x/player/playurl?'.http_build_query($param);
$ret = $this->curl($url, null, $this->cookie);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取视频下载链接失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
if(!isset($arr['data']['durl'])) throw new Exception('获取视频下载链接失败,返回内容错误');
$url = $arr['data']['durl'][0]['url'];
$size = $arr['data']['durl'][0]['size'];
$quality = $arr['data']['support_formats'][0]['new_description'];
return ['url'=>$url, 'size'=>$size, 'quality'=>$quality, 'format'=>$arr['data']['format'], 'codec'=>$this->get_codec($arr['data']['video_codecid'])];
}else{
throw new Exception('获取视频下载链接失败 '.$arr['message']);
}
}
//用户上传视频解析
public function ugc_video_parse($aid, $cid){
$param = [
'avid' => $aid,
'cid' => $cid,
'qn' => '0',
'type' => '',
'otype' => 'json',
'fourk' => '1',
'fnver' => '0',
'fnval' => '4048',
];
$url = 'https://api.bilibili.com/x/player/playurl?'.http_build_query($param);
$ret = $this->curl($url, null, $this->cookie);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取视频下载链接失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
if(!isset($arr['data']['dash'])) throw new Exception('获取视频下载链接失败,返回内容错误');
return $this->video_data_handle($arr['data']);
}else{
throw new Exception('获取视频下载链接失败 '.$arr['message']);
}
}
//用户上传视频解析(TV接口)
public function ugc_video_parse_tv($aid, $cid){
$param = [
'avid' => $aid,
'cid' => $cid,
'qn' => '0',
'type' => '',
'otype' => 'json',
'fnver' => '0',
'fnval' => '4048',
'device' => 'android',
'platform' => 'android',
'mobi_app' => 'android_tv_yst',
'npcybs' => '0',
'force_host' => '2',
'build' => '102801',
];
if($this->token){
$param['access_key'] = $this->token;
}
$url = 'https://api.snm0516.aisee.tv/x/tv/ugc/playurl?'.http_build_query($param);
$ret = $this->curl($url);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取视频下载链接失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
if(!isset($arr['dash'])) throw new Exception('获取视频下载链接失败,返回内容错误');
return $this->video_data_handle($arr);
}else{
throw new Exception('获取视频下载链接失败 '.$arr['message']);
}
}
//正版视频解析
public function pgc_video_parse($aid, $cid, $epid, $is_cheese=false){
$param = [
'avid' => $aid,
'cid' => $cid,
'qn' => '0',
'type' => '',
'otype' => 'json',
'fourk' => '1',
'fnver' => '0',
'fnval' => '4048',
'module' => 'bangumi',
'ep_id' => $epid,
'session' => ''
];
$url = 'https://api.bilibili.com/pgc/player/web/playurl?'.http_build_query($param);
if($is_cheese){
$url = str_replace('/pgc/','/pugv/',$url);
}
$ret = $this->curl($url, null, $this->cookie);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取视频下载链接失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
if(!isset($arr['result']['dash'])) throw new Exception('获取视频下载链接失败,返回内容错误');
return $this->video_data_handle($arr['result']);
}elseif($arr['code'] == -10403 && !$is_cheese){
$url = 'https://www.bilibili.com/bangumi/play/ep'.$epid;
$ret = $this->curl($url, null, $this->cookie.';CURRENT_FNVAL=4048;');
preg_match('!window\.__playinfo__=([\s\S]*?)<\/script>!',$ret,$match);
if(isset($match[1])){
$arr = json_decode($match[1], true);
}else{
throw new Exception('获取视频下载链接失败 '.$arr['message']);
}
}else{
throw new Exception('获取视频下载链接失败 '.$arr['message']);
}
}
//正版视频解析(TV接口)
public function pgc_video_parse_tv($aid, $cid, $epid, $is_cheese=false){
$param = [
'appkey' => '4409e2ce8ffd12b8',
'aid' => $aid,
'cid' => $cid,
'qn' => '0',
'module' => 'bangumi',
'ep_id' => $epid,
'expire' => '0',
'fnval' => '80',
'fnver' => '0',
'fourk' => '1',
'mid' => '0',
'otype' => 'json',
'device' => 'android',
'platform' => 'android',
'mobi_app' => 'android_tv_yst',
'npcybs' => '0',
'build' => '102801',
'ts' => time()
];
if($this->token){
$param['access_key'] = $this->token;
}
$param['sign'] = $this->tv_get_sign($param);
$url = 'https://api.snm0516.aisee.tv/pgc/player/api/playurltv?'.http_build_query($param);
if($is_cheese){
$url = str_replace('/pgc/','/pugv/',$url);
}
$ret = $this->curl($url);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取视频下载链接失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
if(!isset($arr['dash'])) throw new Exception('获取视频下载链接失败,返回内容错误');
return $this->video_data_handle($arr);
}else{
throw new Exception('获取视频下载链接失败 '.$arr['message']);
}
}
private function video_data_handle($data){
$video = [];
$audio = [];
$timelength = round($data['timelength']/1000);
if($data['dash']['video']){
foreach($data['dash']['video'] as $row){
if(preg_match('!://(.*:\\d+)/!',$row['base_url'],$match)){ //替换PCDN
$row['base_url'] = str_replace($match[1], 'upos-sz-mirrorcoso1.bilivideo.com', $row['base_url']);
}
$size = round($timelength * $row['bandwidth'] / 8);
$video[] = ['url'=>$row['base_url'], 'quality'=>self::$qualitys[$row['id']], 'bandwidth'=>round($row['bandwidth']/1000), 'size' => $size, 'codec'=>$this->get_codec($row['codecid']), 'ratio'=>$row['width'].'×'.$row['height'], 'fps'=>$row['frame_rate']];
}
}
if($data['dash']['audio']){
foreach($data['dash']['audio'] as $row){
$size = round($timelength * $row['bandwidth'] / 8);
$audio[] = ['url'=>$row['base_url'], 'quality'=>self::$qualitys_audio[$row['id']], 'bandwidth'=>round($row['bandwidth']/1000), 'size' => $size, 'codec'=>str_replace(['mp4a.40.2','ec-3'], ['M4A', 'AC3'], $row['codecs'])];
}
}
return ['video'=>$video, 'audio'=>$audio];
}
//获取音乐信息
public function get_audio_info($sid){
$url = 'https://www.bilibili.com/audio/music-service-c/web/song/info?sid='.$sid;
$ret = $this->curl($url, null, $this->cookie);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取音乐信息失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
return $arr['data'];
}else{
throw new Exception('获取音乐信息失败:'.$arr['message']);
}
}
//音乐解析
public function get_audio_url($sid){
$url = 'https://www.bilibili.com/audio/music-service-c/web/url?sid='.$sid.'&privilege=2&quality=2';
$ret = $this->curl($url, null, $this->cookie);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('获取音乐下载链接失败');
}elseif(isset($arr['code']) && $arr['code'] == 0){
if(!isset($arr['data']['cdns'])) throw new Exception('获取音乐下载链接失败,返回内容错误');
$url = $arr['data']['cdns'][0];
$size = $arr['data']['size'];
return ['url'=>$url, 'size'=>$size, 'quality'=>'MP3192K'];
}else{
throw new Exception('获取音乐下载链接失败:'.$arr['message']);
}
}
private function curl($url,$data=null,$cookie=null,$referer=null){
$ch=curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
$httpheader[] = "Accept: application/json";
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
$httpheader[] = "Accept-Encoding: gzip,deflate,sdch";
$httpheader[] = "Connection: keep-alive";
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
if($data){
if(is_array($data)) $data=http_build_query($data);
curl_setopt($ch, CURLOPT_POSTFIELDS,$data);
curl_setopt($ch, CURLOPT_POST,1);
}
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_REFERER, $referer?$referer:'https://www.bilibili.com/');
if($cookie){
curl_setopt($ch,CURLOPT_COOKIE, $cookie);
}
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.44');
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
$ret=curl_exec($ch);
curl_close($ch);
return $ret;
}
private function get_codec($codecid){
switch($codecid){
case 13:
return 'AV1';break;
case 12:
return 'HEVC';break;
case 7:
return 'AVC';break;
default:
return 'UNKNOWN';break;
}
}
private function tv_get_sign($param){
$key = '59b43e04ad6965f34319062b478f83dd';
ksort($param);
$signstr = http_build_query($param);
return md5($signstr.$key);
}
private function getArray($node) {
$array = false;
if ($node->hasAttributes()) {
foreach ($node->attributes as $attr) {
$array[$attr->nodeName] = $attr->nodeValue;
}
}
if ($node->hasChildNodes()) {
if ($node->childNodes->length == 1) {
$array[$node->firstChild->nodeName] = $this->getArray($node->firstChild);
} else {
foreach ($node->childNodes as $childNode) {
if ($childNode->nodeType != XML_TEXT_NODE) {
$array[$childNode->nodeName][] = $this->getArray($childNode);
}
}
}
} else {
return $node->nodeValue;
}
return $array;
}
private function encWbi($params){
$mixin_key = $this->getMixinKey();
$curr_time = time();
$chr_filter = "/[!'()*]/";
$query = [];
$params['wts'] = $curr_time;
ksort($params);
foreach ($params as $key => $value) {
$value = preg_replace($chr_filter, '', $value);
$query[] = urlencode($key) . '=' . urlencode($value);
}
$query = implode('&', $query);
$wbi_sign = md5($query . $mixin_key);
return $query . '&w_rid=' . $wbi_sign;
}
private function getMixinKey(){
if(!empty($this->mixinKey)) return $this->mixinKey;
$url = 'https://api.bilibili.com/x/web-interface/nav';
$ret = $this->curl($url, null, $this->cookie);
$arr = json_decode($ret, true);
if(!$arr){
throw new Exception('请求失败');
}
if(!isset($arr['data']['wbi_img'])){
throw new Exception('获取WbiKeys失败');
}
$img_url = $arr['data']['wbi_img']['img_url'];
$sub_url = $arr['data']['wbi_img']['sub_url'];
$img_key = substr(basename($img_url), 0, strpos(basename($img_url), '.'));
$sub_key = substr(basename($sub_url), 0, strpos(basename($sub_url), '.'));
$key = $img_key . $sub_key;
$mixinKeyEncTab = [
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, 37, 48, 7, 16, 24, 55, 40,
61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
36, 20, 34, 44, 52
];
$t = '';
foreach ($mixinKeyEncTab as $n) $t .= $key[$n];
$this->mixinKey = substr($t, 0, 32);
return $this->mixinKey;
}
}