update
This commit is contained in:
parent
d7cf86507a
commit
579fd2eaf6
@ -1,65 +1,177 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title>用户中心</title>
|
||||
<title>基础菜单 - Layui</title>
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="/layui/css/layui.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body class="layui-bg-gray">
|
||||
<div id="header"></div>
|
||||
<body>
|
||||
<div id="view"></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
<script id="demo" type="text/html">
|
||||
<h3>{{ d.title }}</h3>
|
||||
<ul>
|
||||
{{# layui.each(d.list, function(index, item){ }}
|
||||
<li>
|
||||
<span>{{ item.modname }}</span>
|
||||
<span>{{ item.alias }}:</span>
|
||||
<span>{{ item.site || '' }}</span>
|
||||
<div class="layui-panel" style="width: 260px; margin: 16px;">
|
||||
<ul class="layui-menu" id="demo-menu">
|
||||
<li lay-options="{id: 100}">
|
||||
<div class="layui-menu-body-title"><a href="javascript:;">menu item 1</a></div>
|
||||
</li>
|
||||
<li lay-options="{id: 101}">
|
||||
<div class="layui-menu-body-title">
|
||||
<a href="javascript:;">menu item 2 <span class="layui-badge-dot"></span></a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="layui-menu-item-divider"></li>
|
||||
<li class="layui-menu-item-group layui-menu-item-down" lay-options="{type: 'group'}">
|
||||
<div class="layui-menu-body-title">
|
||||
menu group <i class="layui-icon layui-icon-up"></i>
|
||||
</div>
|
||||
<ul>
|
||||
<li lay-options="{id: 103}">
|
||||
<div class="layui-menu-body-title">menu item 3-1</div>
|
||||
</li>
|
||||
<li class="layui-menu-item-group" lay-options="{type: 'group', isAllowSpread: false}">
|
||||
<div class="layui-menu-body-title">menu group 2</div>
|
||||
<ul>
|
||||
<li class="layui-menu-item-checked">
|
||||
<div class="layui-menu-body-title">menu item 3-2-1</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 3-2-2</div>
|
||||
</li>
|
||||
{{# }); }}
|
||||
{{# if(d.list.length === 0){ }}
|
||||
无数据
|
||||
{{# } }}
|
||||
</ul>
|
||||
</script>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 3-3</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="layui-menu-item-divider"></li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 4 <span class="layui-badge">1</span></div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 5</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 6</div>
|
||||
</li>
|
||||
<li class="layui-menu-item-parent" lay-options="{type: 'parent'}">
|
||||
<div class="layui-menu-body-title">
|
||||
menu item 7 Children
|
||||
<i class="layui-icon layui-icon-right"></i>
|
||||
</div>
|
||||
<div class="layui-panel layui-menu-body-panel">
|
||||
<ul>
|
||||
<li class="layui-menu-item-parent" lay-options="{type: 'parent'}">
|
||||
<div class="layui-menu-body-title">
|
||||
menu item 7-1
|
||||
<i class="layui-icon layui-icon-right"></i>
|
||||
</div>
|
||||
<div class="layui-panel layui-menu-body-panel">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 7-2-1</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 7-2-2</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 7-2-3</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 7-2-4</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 7-2</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 7-3</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>menu item 8</li>
|
||||
<li class="layui-menu-item-divider"></li>
|
||||
<li class="layui-menu-item-group" lay-options="{type: 'group', isAllowSpread: false}">
|
||||
<div class="layui-menu-body-title">menu group 9</div>
|
||||
<ul>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 9-1</div>
|
||||
</li>
|
||||
<li class="layui-menu-item-parent" lay-options="{type: 'parent'}">
|
||||
<div class="layui-menu-body-title">
|
||||
menu item 9-2
|
||||
<i class="layui-icon layui-icon-right"></i>
|
||||
</div>
|
||||
<div class="layui-panel layui-menu-body-panel">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 9-2-1</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 9-2-2</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 9-2-3</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 9-31</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="layui-menu-item-divider"></li>
|
||||
<li>
|
||||
<div class="layui-menu-body-title">menu item 10</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<script src="/layui/layui.js"></script>
|
||||
<script src="/js/jquery-3.2.1.js"></script>
|
||||
<script src="/js/CommonConfig.js"></script>
|
||||
<script>
|
||||
headerModel = 4;
|
||||
$('#header').load("/html/header.html");
|
||||
</script>
|
||||
<script src="/layui/layui.js"></script>
|
||||
<script>
|
||||
|
||||
layui.use(['form', 'laytpl'], function () {
|
||||
var form = layui.form;
|
||||
|
||||
// loadHeader($('#header').get(0))
|
||||
layui.use(function () {
|
||||
var dropdown = layui.dropdown;
|
||||
var layer = layui.layer;
|
||||
var laytpl = layui.laytpl
|
||||
var data = { //数据
|
||||
"title":"Layui常用模块"
|
||||
,"list":[{"modname":"弹层","alias":"layer","site":"layer.layui99.com"},{"modname":"表单","alias":"form"}]
|
||||
}
|
||||
var getTpl = $("#demo").get(0).innerHTML
|
||||
,view = document.getElementById('view');
|
||||
laytpl(getTpl).render(data, function(html){
|
||||
view.innerHTML = html;
|
||||
console.log(html)
|
||||
var util = layui.util;
|
||||
// 菜单点击事件
|
||||
dropdown.on('click(demo-menu)', function (options) {
|
||||
console.log(this, options);
|
||||
|
||||
// 显示 - 仅用于演示
|
||||
layer.msg(util.escape(JSON.stringify(options)));
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,5 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
@ -7,54 +8,84 @@
|
||||
<link rel="stylesheet" href="/layui/css/layui.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body class="layui-bg-gray">
|
||||
<div id="header"></div>
|
||||
<body>
|
||||
<div id="view"></div>
|
||||
|
||||
|
||||
<div class="layui-panel" style="width: 260px; margin: 16px;">
|
||||
<ul class="layui-menu" id="menusss">
|
||||
<!-- <div id="menuView"></div> -->
|
||||
<li class="layui-menu-item-group layui-menu-item-down" lay-options="{type: 'group'}">
|
||||
<div class="layui-menu-body-title">
|
||||
menu group <i class="layui-icon layui-icon-up"></i>
|
||||
</div>
|
||||
<ul>
|
||||
<li lay-options="{id: 103}">
|
||||
<div class="layui-menu-body-title">menu item 3-1</div>
|
||||
</li>
|
||||
<li lay-options="{id: 102}">
|
||||
<div class="layui-menu-body-title">menu item 3-2</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
<script id="demo" type="text/html">
|
||||
<h3>{{ d.title }}</h3>
|
||||
<ul>
|
||||
{{# layui.each(d.list, function(index, item){ }}
|
||||
<li>
|
||||
<span>{{ item.modname }}</span>
|
||||
<span>{{ item.alias }}:</span>
|
||||
<span>{{ item.site || '' }}</span>
|
||||
</li>
|
||||
<script id="menulist" type="text/html">
|
||||
<ul class="layui-tab-title">
|
||||
{{# layui.each(d.data, function(index, item){ }}
|
||||
<li lay-id="{{= item.live_room_id}}">{{= item.anchorName}}</li>
|
||||
{{# }); }}
|
||||
{{# if(d.list.length === 0){ }}
|
||||
{{# if(d.data.length === 0){ }}
|
||||
无数据
|
||||
{{# } }}
|
||||
</ul>
|
||||
</script>
|
||||
|
||||
<script src="/layui/layui.js"></script>
|
||||
<script src="/js/jquery-3.2.1.js"></script>
|
||||
<script src="/js/CommonConfig.js"></script>
|
||||
<script src="/js/httpUtils.js"></script>
|
||||
<script src="/layui/layui.js"></script>
|
||||
<script>
|
||||
headerModel = 2;
|
||||
$('#header').load("/html/header.html");
|
||||
</script>
|
||||
|
||||
layui.use(['form', 'laytpl'], function () {
|
||||
var form = layui.form;
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
var roomId = '17961';
|
||||
layui.use(['dropdown', 'laytpl'], function () {
|
||||
var dropdown = layui.dropdown;
|
||||
var layer = layui.layer;
|
||||
var laytpl = layui.laytpl
|
||||
var data = { //数据
|
||||
"title":"Layui常用模块"
|
||||
,"list":[{"modname":"弹层","alias":"layer","site":"layer.layui99.com"},{"modname":"表单","alias":"form"}]
|
||||
var laytpl = layui.laytpl;
|
||||
function initTabs() {
|
||||
getVideo(roomId, pageIndex, pageMax)
|
||||
.then(data => {
|
||||
initMenu(data)
|
||||
})
|
||||
|
||||
}
|
||||
var getTpl = $("#demo").get(0).innerHTML
|
||||
,view = document.getElementById('view');
|
||||
laytpl(getTpl).render(data, function(html){
|
||||
view.innerHTML = html;
|
||||
console.log(html)
|
||||
dropdown.on('click(menusss)', function (options) {
|
||||
console.log(this, options);
|
||||
|
||||
});
|
||||
function initMenu(data) {
|
||||
var view = $("#menuView").get(0);
|
||||
laytpl($('#menulist').get(0).innerHTML).render(data, function (html) {
|
||||
view.innerHTML = html;
|
||||
layer.close(loadIndex)
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
// initTabs()
|
||||
|
||||
}
|
||||
init()
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
@ -62,4 +93,5 @@
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
</html>
|
@ -7,7 +7,7 @@
|
||||
<li class="layui-nav-item user"><a href="/html/body/user.html">用户中心</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
<script src="/layui/layui.js"></script>
|
||||
<!-- <script src="/layui/layui.js"></script> -->
|
||||
|
||||
<script>
|
||||
$(".index").removeClass("layui-this")
|
||||
|
@ -47,7 +47,7 @@
|
||||
<input type="text" name="recordDanmuDate" id="recordDanmuDate" value="00:00:00 - 23:59:59"
|
||||
lay-verify="required" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-text-em" onclick="timeTips(this)"><i
|
||||
<div class="layui-form-mid layui-text-em" onclick="timeTips(false,this)"><i
|
||||
class="layui-icon layui-icon-help"></i> </div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
@ -56,7 +56,7 @@
|
||||
<input type="text" name="recordLiveDate" id="recordLiveDate" value="00:00:00 - 23:59:59"
|
||||
lay-verify="required" autocomplete="off" class="layui-input">
|
||||
</div>
|
||||
<div class="layui-form-mid layui-text-em" onclick="timeTips(this)"><i
|
||||
<div class="layui-form-mid layui-text-em" onclick="timeTips(true,this)"><i
|
||||
class="layui-icon layui-icon-help"></i> </div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
@ -89,8 +89,13 @@
|
||||
<script src="/js/httpUtils.js"></script>
|
||||
<script src="/js/CommonConfig.js"></script>
|
||||
<script>
|
||||
function timeTips(that) {
|
||||
function timeTips(isLive,that) {
|
||||
if(isLive){
|
||||
layer.tips('是从开始到结束时间范围内,主播开播会启动录制,超过结束范围不会中断正在录制的任务', that);
|
||||
}else{
|
||||
layer.tips('仅在当前时间范围内录制,如遇到正在直播,则延迟到下播时停止录制', that);
|
||||
}
|
||||
|
||||
}
|
||||
var roomId = getParam("roomId");
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
||||
{{# layui.each(d.data, function(index, item){ }}
|
||||
<div class="layui-col-xs3 layui-col-sm3 layui-col-md3">
|
||||
<div class="layui-bg-gray" style="padding: 16px;">
|
||||
<div class="layui-row layui-col-space15">
|
||||
<div class="layui-row layui-col-space16">
|
||||
<div class="layui-card">
|
||||
<div class="layui-card-header" ><img src="#" onerror="showImage('{{= item.anchorFace}}',this)" style="width: 40px; height: 40px;"/> {{= item.anchorName}}
|
||||
<span style="float: right;">直播状态:
|
||||
|
@ -121,15 +121,21 @@ function deleteRoomConfig(roomId){
|
||||
function getAllLive(page,limit){
|
||||
return get("/live/list?page="+page+"&limit="+limit)
|
||||
}
|
||||
function getAllConfig(page,limit){
|
||||
return get("/live/config/all?page="+page+"&limit="+limit)
|
||||
}
|
||||
//----------------首页相关接口end
|
||||
//----------------视频相关接口
|
||||
//----------------直播视频相关接口
|
||||
function startLiveVideo(roomId){
|
||||
return get("/live/video/start?roomId="+roomId)
|
||||
}
|
||||
function stopLiveVideo(roomId){
|
||||
return get("/live/video/stop?roomId="+roomId)
|
||||
}
|
||||
//----------------视频相关接口end
|
||||
function getVideo(roomId,page,limit){
|
||||
return get("/file/list?roomId="+roomId+"&page="+page+"&limit="+limit)
|
||||
}
|
||||
//----------------直播视频相关接口end
|
||||
//----------------弹幕相关接口
|
||||
function startLiveDanmu(roomId){
|
||||
return get("/live/danmu/start?roomId="+roomId)
|
||||
|
@ -81,7 +81,12 @@ public class LiveConfigController {
|
||||
@RequestMapping(value = "all", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public JSONObject getAllConfig(int page,int limit) {
|
||||
List<LiveConfigDatabaseBean> config = configService.getConfigs(page, limit);
|
||||
List<LiveConfigDatabaseBean> config ;
|
||||
if(page==-1||limit==-1) {
|
||||
config=configService.getAllConfig();
|
||||
}else{
|
||||
config=configService.getConfigs(page, limit);
|
||||
}
|
||||
if (config != null) {
|
||||
return ResultData.success(config,configService.getConfigCount());
|
||||
}
|
||||
|
@ -2,22 +2,18 @@ package com.yutou.bilibili.Controllers;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.yutou.bilibili.datas.ResultData;
|
||||
import com.yutou.bilibili.datas.VideoFilePath;
|
||||
import com.yutou.bilibili.services.LiveDanmuService;
|
||||
import com.yutou.bilibili.services.LiveService;
|
||||
import com.yutou.bilibili.services.LiveVideoService;
|
||||
import com.yutou.bilibili.services.LiveVideoDownloadService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.val;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
public class LiveController {
|
||||
@Resource
|
||||
LiveVideoService videoService;
|
||||
LiveVideoDownloadService videoService;
|
||||
@Resource
|
||||
LiveDanmuService danmuService;
|
||||
@Resource
|
||||
|
@ -1,13 +1,11 @@
|
||||
package com.yutou.bilibili.Controllers;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
|
||||
import com.yutou.biliapi.net.WebSocketManager;
|
||||
import com.yutou.bilibili.datas.ResultData;
|
||||
import com.yutou.bilibili.datas.ReturnCode;
|
||||
import com.yutou.bilibili.services.LiveVideoService;
|
||||
import com.yutou.bilibili.services.LiveVideoDownloadService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -18,7 +16,7 @@ import java.util.List;
|
||||
@Controller
|
||||
public class LiveVideoController {
|
||||
@Resource
|
||||
LiveVideoService videoService;
|
||||
LiveVideoDownloadService videoService;
|
||||
|
||||
@RequestMapping("/live/video/list")
|
||||
@ResponseBody
|
||||
|
@ -5,17 +5,14 @@ import com.yutou.bilibili.Tools.FileServerUtils;
|
||||
import com.yutou.bilibili.Tools.Tools;
|
||||
import com.yutou.bilibili.datas.ResultData;
|
||||
import com.yutou.bilibili.datas.VideoFilePath;
|
||||
import com.yutou.bilibili.services.LiveVideoService;
|
||||
import com.yutou.bilibili.services.LiveVideoDownloadService;
|
||||
import com.yutou.common.okhttp.HttpDownloadUtils;
|
||||
import com.yutou.common.utils.AppTools;
|
||||
import com.yutou.common.utils.Base64Tools;
|
||||
import com.yutou.common.utils.Log;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.val;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -34,16 +31,16 @@ import java.util.List;
|
||||
@Controller
|
||||
public class VideoFileController {
|
||||
@Resource
|
||||
LiveVideoService videoService;
|
||||
LiveVideoDownloadService videoService;
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping("/file/list")
|
||||
public JSONObject getFileList(String roomId) {
|
||||
public JSONObject getFileList(int page,int limit,String roomId) {
|
||||
List<VideoFilePath> list;
|
||||
if (StringUtils.hasText(roomId)) {
|
||||
list = videoService.getVideoPath(roomId);
|
||||
} else {
|
||||
list = videoService.getAllVideoPath();
|
||||
list = videoService.getAllVideoPath(page,limit);
|
||||
}
|
||||
return ResultData.success(list);
|
||||
}
|
||||
|
@ -1,21 +1,19 @@
|
||||
package com.yutou.bilibili.Tools;
|
||||
|
||||
import com.yutou.bilibili.services.LiveVideoService;
|
||||
import com.yutou.bilibili.services.LiveVideoDownloadService;
|
||||
import com.yutou.bilibili.services.SystemService;
|
||||
import com.yutou.common.utils.FFmpegUtils;
|
||||
import com.yutou.common.utils.Log;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Component
|
||||
public class ApplicationClose implements ApplicationListener<ContextClosedEvent> {
|
||||
@Resource
|
||||
SystemService systemConfigService;
|
||||
@Resource
|
||||
LiveVideoService videoService;
|
||||
LiveVideoDownloadService videoService;
|
||||
@Override
|
||||
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
|
||||
Log.i("服务结束");
|
||||
|
@ -8,6 +8,7 @@ import java.util.List;
|
||||
public class VideoFilePath {
|
||||
private String name;
|
||||
private String roomId;
|
||||
private String uid;
|
||||
private String path;
|
||||
private String cover;
|
||||
private boolean isParent;
|
||||
|
@ -27,7 +27,7 @@ public class LiveConfigService {
|
||||
if (!StringUtils.hasText(roomId)) {
|
||||
return null;
|
||||
}
|
||||
bean.setRoomId(new String(roomId));
|
||||
bean.setRoomId(roomId);
|
||||
try {
|
||||
LiveRoomInfo body = BiliLiveNetApiManager.getInstance().getApi(null).getRoomInfo(String.valueOf(bean.getRoomId())).execute().body().getData();
|
||||
MasterInfoBean infoBean = BiliLiveNetApiManager.getInstance().getApi(null).getMasterInfo(String.valueOf(body.getUid())).execute().body().getData();
|
||||
|
@ -6,11 +6,8 @@ import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
|
||||
import com.yutou.biliapi.net.BiliLiveNetApiManager;
|
||||
import com.yutou.bilibili.datas.web.LiveData;
|
||||
import com.yutou.common.okhttp.HttpBody;
|
||||
import com.yutou.common.utils.AppTools;
|
||||
import com.yutou.common.utils.Log;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.val;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -22,7 +19,7 @@ import java.util.List;
|
||||
public class LiveService {
|
||||
BiliLiveConfigDatabase liveConfigDatabase;
|
||||
@Resource
|
||||
LiveVideoService videoService;
|
||||
LiveVideoDownloadService videoDownloadService;
|
||||
@Resource
|
||||
LiveDanmuService danmuService;
|
||||
LiveApi api;
|
||||
@ -43,7 +40,7 @@ public class LiveService {
|
||||
liveData.setAnchorUid(config.getAnchorUid());
|
||||
liveData.setAnchorName(config.getAnchorName());
|
||||
liveData.setAnchorFace(config.getAnchorFace());
|
||||
liveData.setDownloadVideo(videoService.checkDownload(config.getRoomId()));
|
||||
liveData.setDownloadVideo(videoDownloadService.checkDownload(config.getRoomId()));
|
||||
liveData.setDanmu(danmuService.check(config.getRoomId()));
|
||||
try {
|
||||
LiveRoomInfo body = api.getRoomInfo(config.getRoomId()).execute().body().getData();
|
||||
|
@ -0,0 +1,369 @@
|
||||
package com.yutou.bilibili.services;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.util.DateUtils;
|
||||
import com.yutou.biliapi.api.LiveApi;
|
||||
import com.yutou.biliapi.bean.live.LiveRoomConfig;
|
||||
import com.yutou.biliapi.bean.live.LiveRoomInfo;
|
||||
import com.yutou.biliapi.bean.live.LiveRoomPlayInfo;
|
||||
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||
import com.yutou.biliapi.bean.live.database.LiveVideoDatabaseBean;
|
||||
import com.yutou.biliapi.bean.login.LoginCookieDatabaseBean;
|
||||
import com.yutou.biliapi.databases.BiliBiliLoginDatabase;
|
||||
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
|
||||
import com.yutou.biliapi.databases.BiliLiveDatabase;
|
||||
import com.yutou.biliapi.enums.LiveProtocol;
|
||||
import com.yutou.biliapi.enums.LiveVideoCodec;
|
||||
import com.yutou.biliapi.enums.LiveVideoDefinition;
|
||||
import com.yutou.biliapi.enums.LiveVideoFormat;
|
||||
import com.yutou.biliapi.net.BiliLiveNetApiManager;
|
||||
import com.yutou.bilibili.Tools.DateFormatUtils;
|
||||
import com.yutou.bilibili.Tools.FileServerUtils;
|
||||
import com.yutou.bilibili.Tools.LiveInfoNfoTools;
|
||||
import com.yutou.bilibili.datas.VideoFilePath;
|
||||
import com.yutou.bilibili.interfaces.DownloadInterface;
|
||||
import com.yutou.common.okhttp.HttpCallback;
|
||||
import com.yutou.common.okhttp.HttpDownloadUtils;
|
||||
import com.yutou.common.record.AbsVideoRecord;
|
||||
import com.yutou.common.utils.ConfigTools;
|
||||
import com.yutou.common.utils.FFmpegUtils;
|
||||
import com.yutou.common.utils.Log;
|
||||
import okhttp3.Headers;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.alibaba.fastjson2.util.DateUtils.DateTimeFormatPattern.DATE_FORMAT_10_DASH;
|
||||
|
||||
@Service
|
||||
public class LiveVideoDownloadService {
|
||||
private final ThreadPoolExecutor executor;
|
||||
private final List<String> userStopList = new ArrayList<>();//手动停止列表
|
||||
private final AbsVideoRecord videoRecord;
|
||||
|
||||
public LiveVideoDownloadService() {
|
||||
Log.i("初始化下载服务");
|
||||
videoRecord = new FFmpegUtils();
|
||||
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
|
||||
}
|
||||
|
||||
public boolean checkDownload(String roomId) {
|
||||
return videoRecord.check(roomId);
|
||||
}
|
||||
|
||||
public void clearUserStopList() {
|
||||
userStopList.clear();
|
||||
}
|
||||
|
||||
public void start(LiveConfigDatabaseBean bean, boolean isUser) {
|
||||
if (!isUser && userStopList.contains(bean.getRoomId().toString())) {
|
||||
return;
|
||||
}
|
||||
if (isUser) {
|
||||
userStopList.remove(bean.getRoomId().toString());
|
||||
}
|
||||
if (videoRecord.check(bean.getRoomId().toString())) {
|
||||
return;
|
||||
}
|
||||
BiliLiveNetApiManager.getInstance().getApi(bean.getRecordUid()).getRoomInfo(bean.getRoomId().toString()).enqueue(new HttpCallback<LiveRoomInfo>() {
|
||||
@Override
|
||||
public void onResponse(Headers headers, int code, String status, LiveRoomInfo response, String rawResponse) {
|
||||
if (response.getLiveStatus() == 1) {
|
||||
VideoTask task = new VideoTask(bean, response);
|
||||
executor.execute(task);
|
||||
} else {
|
||||
Log.i("移除下载");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable throwable) {
|
||||
Log.i("移除下载");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void stop(String roomId, boolean isUser) {
|
||||
if (isUser) {
|
||||
userStopList.add(roomId);
|
||||
}
|
||||
videoRecord.kill(roomId);
|
||||
}
|
||||
|
||||
|
||||
public JSONArray getDownloadTasks() {
|
||||
JSONArray array = new JSONArray();
|
||||
array.addAll(videoRecord.getRoomIds());
|
||||
return array;
|
||||
}
|
||||
|
||||
public void stopAll() {
|
||||
videoRecord.killAll();
|
||||
}
|
||||
|
||||
private class VideoTask implements Runnable {
|
||||
LiveConfigDatabaseBean bean;
|
||||
boolean isDownload = true;
|
||||
LiveApi api;
|
||||
String savePath;
|
||||
File rootPath;
|
||||
LiveConfigDatabaseBean config;
|
||||
BiliLiveDatabase database;
|
||||
LiveVideoDatabaseBean videoDatabaseBean;
|
||||
LiveRoomInfo roomInfo;
|
||||
|
||||
public VideoTask(LiveConfigDatabaseBean bean, LiveRoomInfo roomInfo) {
|
||||
this.bean = bean;
|
||||
this.roomInfo = roomInfo;
|
||||
api = BiliLiveNetApiManager.getInstance().getApi(bean.getRecordUid());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (roomInfo.getLiveStatus() == 1) {
|
||||
String time = DateUtils.format(new Date().getTime(), DATE_FORMAT_10_DASH);
|
||||
rootPath = new File(bean.getRecordPath() + File.separator + bean.getAnchorName() + File.separator + time + File.separator + roomInfo.getTitle());
|
||||
savePath = rootPath.getAbsolutePath() + File.separator + "[" +
|
||||
DateUtils.format(new Date(),
|
||||
"yyyy-MM-dd HH-mm-ss") + "]" + roomInfo.getTitle() + ".flv";
|
||||
if (!rootPath.exists()) {
|
||||
rootPath.mkdirs();
|
||||
}
|
||||
record(bean, roomInfo);
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
videoRecord.kill(bean.getRoomId().toString());
|
||||
api.getRoomInfo(config.getRoomId().toString()).enqueue(new HttpCallback<LiveRoomInfo>() {
|
||||
@Override
|
||||
public void onResponse(Headers headers, int code, String status, LiveRoomInfo response, String rawResponse) {
|
||||
if (response.getLiveStatus() == 1) {
|
||||
LiveVideoDownloadService.this.start(bean, false);
|
||||
} else {
|
||||
LiveVideoDownloadService.this.stop(bean.getRoomId().toString(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable throwable) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void record(LiveConfigDatabaseBean bean, LiveRoomInfo roomInfo) {
|
||||
this.config = bean;
|
||||
isDownload = true;
|
||||
LiveRoomConfig config = new LiveRoomConfig();
|
||||
config.setLoginUid(bean.getRecordUid());
|
||||
config.setRoomId(bean.getRoomId());
|
||||
config.setAnchorName(bean.getAnchorName());
|
||||
config.setLogin(StringUtils.hasText(bean.getRecordUid()));
|
||||
config.setRoomInfo(roomInfo);
|
||||
config.setRootPath(bean.getRecordPath());
|
||||
database = new BiliLiveDatabase(config);
|
||||
HttpDownloadUtils.download(new HttpDownloadUtils.Builder().setUrl(roomInfo.getKeyframe())
|
||||
.setPath(rootPath.getAbsolutePath())
|
||||
.setFileName("poster.jpg"));
|
||||
LiveInfoNfoTools.saveLiveInfoNfo(roomInfo, rootPath.getAbsolutePath(), new File(savePath).getName().replace(".flv", ".nfo"));
|
||||
saveLiveInfo(roomInfo);
|
||||
api.getLiveRoomPlayInfo(
|
||||
bean.getRoomId().toString(),
|
||||
LiveProtocol.getAll(),
|
||||
LiveVideoFormat.getAll(),
|
||||
LiveVideoCodec.getAll(),
|
||||
LiveVideoDefinition.ORIGINAL.getValue()).enqueue(new HttpCallback<LiveRoomPlayInfo>() {
|
||||
@Override
|
||||
public void onResponse(Headers headers, int code, String status, LiveRoomPlayInfo response, String rawResponse) {
|
||||
LiveRoomPlayInfo.Codec codec = response.getPlayurlInfo().getPlayurl().getStream().get(0).getFormat().get(0).getCodec().get(0);
|
||||
String url = codec.getUrlInfo().get(0).getHost() + codec.getBaseUrl() + codec.getUrlInfo().get(0).getExtra();
|
||||
|
||||
if (bean.getRecordLiveModel() == 1) {
|
||||
javaRecord(url, response);
|
||||
} else {
|
||||
ffmpeg(url, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable throwable) {
|
||||
Log.e(throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void javaRecord(String url, LiveRoomPlayInfo playInfo) {
|
||||
|
||||
HttpDownloadUtils.download(new HttpDownloadUtils.Builder()
|
||||
.setUrl(url)
|
||||
.setPath(savePath)
|
||||
.setDownloadInterface(new DownloadInterface() {
|
||||
@Override
|
||||
public void onDownloadStart() {
|
||||
super.onDownloadStart();
|
||||
VideoTask.this.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDownloading(double soFarBytes, double totalBytes) {
|
||||
return isDownload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownload(File file) {
|
||||
super.onDownload(file);
|
||||
stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
super.onError(e);
|
||||
stop();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void ffmpeg(String url, LiveRoomPlayInfo playInfo) {
|
||||
String ffmpegPath = ConfigTools.load(ConfigTools.CONFIG, "ffmpeg", String.class);
|
||||
String cookie = "";
|
||||
LoginCookieDatabaseBean ck = BiliBiliLoginDatabase.getInstance().getCookie(config.getRecordUid());
|
||||
if (ck != null) {
|
||||
cookie = ck.toCookieString();
|
||||
}
|
||||
|
||||
FFmpegUtils.Builder builder = new FFmpegUtils.Builder()
|
||||
.withParam("-user_agent", ConfigTools.getUserAgent())
|
||||
.withParam("-headers", "Referer: https://live.bilibili.com")
|
||||
// .withNotSymbolParam("-progress", "-")
|
||||
.withNotSymbolParam("-threads", "8")
|
||||
.withNotSymbolParam("-c:v", "copy")
|
||||
.withNotSymbolParam("-y", "");
|
||||
if (ck != null) {
|
||||
builder = builder.withParam("-cookies", cookie);
|
||||
}
|
||||
FFmpegUtils command = builder.build(config.getRoomId(), ffmpegPath, url, savePath);
|
||||
Log.i(command.getCommand());
|
||||
try {
|
||||
command.start(new DownloadInterface() {
|
||||
@Override
|
||||
public void onDownloadStart() {
|
||||
super.onDownloadStart();
|
||||
VideoTask.this.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDownloading(double soFarBytes, double totalBytes) {
|
||||
if (!isDownload) {
|
||||
command.stop();
|
||||
}
|
||||
return super.onDownloading(soFarBytes, totalBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownload(File file) {
|
||||
super.onDownload(file);
|
||||
Log.e("下载完成 ");
|
||||
stop();
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void onStart() {
|
||||
videoDatabaseBean = new LiveVideoDatabaseBean();
|
||||
videoDatabaseBean.setPath(savePath);
|
||||
videoDatabaseBean.setRoomInfoJson(JSONObject.toJSONString(roomInfo));
|
||||
videoDatabaseBean.setStartTime(new Date());
|
||||
database.addLiveInfo(videoDatabaseBean);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveLiveInfo(LiveRoomInfo roomInfo) {
|
||||
|
||||
}
|
||||
|
||||
public List<VideoFilePath> getAllVideoPath(int page, int limit) {
|
||||
BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase();
|
||||
List<LiveConfigDatabaseBean> list = configDatabase.getConfigs(page, limit);
|
||||
configDatabase.close();
|
||||
List<VideoFilePath> filePathList = new ArrayList<>();
|
||||
for (LiveConfigDatabaseBean bean : list) {
|
||||
filePathList.addAll(getVideoFilePath(bean));
|
||||
}
|
||||
return filePathList;
|
||||
}
|
||||
|
||||
public List<VideoFilePath> getVideoPath(String roomId) {
|
||||
BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase();
|
||||
LiveConfigDatabaseBean bean = configDatabase.getConfig(roomId);
|
||||
configDatabase.close();
|
||||
return new ArrayList<>(getVideoFilePath(bean));
|
||||
}
|
||||
|
||||
private List<VideoFilePath> getVideoFilePath(LiveConfigDatabaseBean configBean) {
|
||||
List<VideoFilePath> filePathList = new ArrayList<>();
|
||||
String recordPath = configBean.getRecordPath() + File.separator + configBean.getAnchorName();
|
||||
File recordDir = new File(recordPath);
|
||||
BiliLiveDatabase database = new BiliLiveDatabase(LiveRoomConfig.buildConfig(configBean.getRoomId()), recordDir.getAbsolutePath());
|
||||
VideoFilePath path = createVideoRootFilePath(configBean, recordDir);
|
||||
if (recordDir.exists()) {
|
||||
List<LiveVideoDatabaseBean> infos = database.getLiveInfos();
|
||||
database.close();
|
||||
path.setChildren(getVideoInfo(infos));
|
||||
}
|
||||
filePathList.add(path);
|
||||
return filePathList;
|
||||
}
|
||||
|
||||
private VideoFilePath createVideoRootFilePath(LiveConfigDatabaseBean config, File db) {
|
||||
VideoFilePath path = new VideoFilePath();
|
||||
path.setRoomId(config.getRoomId());
|
||||
path.setCover(config.getAnchorFace());
|
||||
path.setName(config.getAnchorName());
|
||||
path.setUid(config.getAnchorUid());
|
||||
path.setParent(true);
|
||||
path.setPath(FileServerUtils.toUrl(db.getParent()));
|
||||
return path;
|
||||
}
|
||||
|
||||
private List<VideoFilePath> getVideoInfo(List<LiveVideoDatabaseBean> videoList) {
|
||||
List<VideoFilePath> filePathList = new ArrayList<>();
|
||||
for (LiveVideoDatabaseBean bean : videoList) {
|
||||
VideoFilePath path = new VideoFilePath();
|
||||
LiveRoomInfo roomInfo = JSONObject.parseObject(bean.getRoomInfoJson(), LiveRoomInfo.class);
|
||||
path.setRoomId(roomInfo.getRoomId());
|
||||
path.setName(roomInfo.getTitle());
|
||||
path.setUid(roomInfo.getUid());
|
||||
path.setPath(DateFormatUtils.format(bean.getSql_time()));
|
||||
path.setCover(roomInfo.getKeyframe());
|
||||
path.setParent(false);
|
||||
path.setChildren(null);
|
||||
filePathList.add(path);
|
||||
}
|
||||
return filePathList;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
LiveVideoDownloadService service = new LiveVideoDownloadService();
|
||||
List<VideoFilePath> path = service.getAllVideoPath(1, 8);
|
||||
Log.i("path.size() = " + path.size());
|
||||
Log.i(JSONArray.toJSONString(path));
|
||||
}
|
||||
}
|
@ -1,371 +1,8 @@
|
||||
package com.yutou.bilibili.services;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.util.DateUtils;
|
||||
import com.yutou.biliapi.api.LiveApi;
|
||||
import com.yutou.biliapi.bean.live.LiveRoomConfig;
|
||||
import com.yutou.biliapi.bean.live.LiveRoomInfo;
|
||||
import com.yutou.biliapi.bean.live.LiveRoomPlayInfo;
|
||||
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||
import com.yutou.biliapi.bean.live.database.LiveVideoDatabaseBean;
|
||||
import com.yutou.biliapi.bean.login.LoginCookieDatabaseBean;
|
||||
import com.yutou.biliapi.databases.BiliBiliLoginDatabase;
|
||||
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
|
||||
import com.yutou.biliapi.databases.BiliLiveDatabase;
|
||||
import com.yutou.biliapi.enums.LiveProtocol;
|
||||
import com.yutou.biliapi.enums.LiveVideoCodec;
|
||||
import com.yutou.biliapi.enums.LiveVideoDefinition;
|
||||
import com.yutou.biliapi.enums.LiveVideoFormat;
|
||||
import com.yutou.biliapi.net.BiliLiveNetApiManager;
|
||||
import com.yutou.biliapi.net.WebSocketManager;
|
||||
import com.yutou.bilibili.Tools.DateFormatUtils;
|
||||
import com.yutou.bilibili.Tools.FileServerUtils;
|
||||
import com.yutou.bilibili.Tools.LiveInfoNfoTools;
|
||||
import com.yutou.bilibili.Tools.Tools;
|
||||
import com.yutou.bilibili.datas.VideoFilePath;
|
||||
import com.yutou.bilibili.interfaces.DownloadInterface;
|
||||
import com.yutou.common.okhttp.HttpCallback;
|
||||
import com.yutou.common.okhttp.HttpDownloadUtils;
|
||||
import com.yutou.common.record.AbsVideoRecord;
|
||||
import com.yutou.common.utils.ConfigTools;
|
||||
import com.yutou.common.utils.FFmpegUtils;
|
||||
import com.yutou.common.utils.Log;
|
||||
import okhttp3.Headers;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.alibaba.fastjson2.util.DateUtils.DateTimeFormatPattern.DATE_FORMAT_10_DASH;
|
||||
|
||||
@Service
|
||||
public class LiveVideoService {
|
||||
private final ThreadPoolExecutor executor;
|
||||
private final List<String> userStopList = new ArrayList<>();//手动停止列表
|
||||
private final AbsVideoRecord videoRecord;
|
||||
|
||||
public LiveVideoService() {
|
||||
Log.i("初始化下载服务");
|
||||
videoRecord = new FFmpegUtils();
|
||||
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
|
||||
}
|
||||
|
||||
public boolean checkDownload(String roomId) {
|
||||
return videoRecord.check(roomId);
|
||||
}
|
||||
public void clearUserStopList() {
|
||||
userStopList.clear();
|
||||
}
|
||||
|
||||
public void start(LiveConfigDatabaseBean bean, boolean isUser) {
|
||||
if (!isUser && userStopList.contains(bean.getRoomId().toString())) {
|
||||
return;
|
||||
}
|
||||
if (isUser) {
|
||||
userStopList.remove(bean.getRoomId().toString());
|
||||
}
|
||||
if (videoRecord.check(bean.getRoomId().toString())) {
|
||||
return;
|
||||
}
|
||||
BiliLiveNetApiManager.getInstance().getApi(bean.getRecordUid()).getRoomInfo(bean.getRoomId().toString()).enqueue(new HttpCallback<LiveRoomInfo>() {
|
||||
@Override
|
||||
public void onResponse(Headers headers, int code, String status, LiveRoomInfo response, String rawResponse) {
|
||||
if (response.getLiveStatus() == 1) {
|
||||
VideoTask task = new VideoTask(bean, response);
|
||||
executor.execute(task);
|
||||
} else {
|
||||
Log.i("移除下载");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable throwable) {
|
||||
Log.i("移除下载");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void stop(String roomId, boolean isUser) {
|
||||
if (isUser) {
|
||||
userStopList.add(roomId);
|
||||
}
|
||||
videoRecord.kill(roomId);
|
||||
}
|
||||
|
||||
|
||||
public JSONArray getDownloadTasks() {
|
||||
JSONArray array = new JSONArray();
|
||||
array.addAll(videoRecord.getRoomIds());
|
||||
return array;
|
||||
}
|
||||
|
||||
public void stopAll() {
|
||||
videoRecord.killAll();
|
||||
}
|
||||
|
||||
private class VideoTask implements Runnable {
|
||||
LiveConfigDatabaseBean bean;
|
||||
boolean isDownload = true;
|
||||
LiveApi api;
|
||||
String savePath;
|
||||
File rootPath;
|
||||
LiveConfigDatabaseBean config;
|
||||
BiliLiveDatabase database;
|
||||
LiveVideoDatabaseBean videoDatabaseBean;
|
||||
LiveRoomInfo roomInfo;
|
||||
|
||||
public VideoTask(LiveConfigDatabaseBean bean, LiveRoomInfo roomInfo) {
|
||||
this.bean = bean;
|
||||
this.roomInfo = roomInfo;
|
||||
api = BiliLiveNetApiManager.getInstance().getApi(bean.getRecordUid());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (roomInfo.getLiveStatus() == 1) {
|
||||
String time = DateUtils.format(new Date().getTime(), DATE_FORMAT_10_DASH);
|
||||
rootPath = new File(bean.getRecordPath() + File.separator + bean.getAnchorName() + File.separator + time + File.separator + roomInfo.getTitle());
|
||||
savePath = rootPath.getAbsolutePath() + File.separator + "[" +
|
||||
DateUtils.format(new Date(),
|
||||
"yyyy-MM-dd HH-mm-ss") + "]" + roomInfo.getTitle() + ".flv";
|
||||
if (!rootPath.exists()) {
|
||||
rootPath.mkdirs();
|
||||
}
|
||||
record(bean, roomInfo);
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
videoRecord.kill(bean.getRoomId().toString());
|
||||
api.getRoomInfo(config.getRoomId().toString()).enqueue(new HttpCallback<LiveRoomInfo>() {
|
||||
@Override
|
||||
public void onResponse(Headers headers, int code, String status, LiveRoomInfo response, String rawResponse) {
|
||||
if (response.getLiveStatus() == 1) {
|
||||
LiveVideoService.this.start(bean, false);
|
||||
} else {
|
||||
LiveVideoService.this.stop(bean.getRoomId().toString(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable throwable) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void record(LiveConfigDatabaseBean bean, LiveRoomInfo roomInfo) {
|
||||
this.config = bean;
|
||||
isDownload = true;
|
||||
LiveRoomConfig config = new LiveRoomConfig();
|
||||
config.setLoginUid(bean.getRecordUid());
|
||||
config.setRoomId(bean.getRoomId());
|
||||
config.setAnchorName(bean.getAnchorName());
|
||||
config.setLogin(StringUtils.hasText(bean.getRecordUid()));
|
||||
config.setRoomInfo(roomInfo);
|
||||
config.setRootPath(bean.getRecordPath());
|
||||
database = new BiliLiveDatabase(config);
|
||||
HttpDownloadUtils.download(new HttpDownloadUtils.Builder().setUrl(roomInfo.getKeyframe())
|
||||
.setPath(rootPath.getAbsolutePath())
|
||||
.setFileName("poster.jpg"));
|
||||
LiveInfoNfoTools.saveLiveInfoNfo(roomInfo, rootPath.getAbsolutePath(), new File(savePath).getName().replace(".flv", ".nfo"));
|
||||
saveLiveInfo(roomInfo);
|
||||
api.getLiveRoomPlayInfo(
|
||||
bean.getRoomId().toString(),
|
||||
LiveProtocol.getAll(),
|
||||
LiveVideoFormat.getAll(),
|
||||
LiveVideoCodec.getAll(),
|
||||
LiveVideoDefinition.ORIGINAL.getValue()).enqueue(new HttpCallback<LiveRoomPlayInfo>() {
|
||||
@Override
|
||||
public void onResponse(Headers headers, int code, String status, LiveRoomPlayInfo response, String rawResponse) {
|
||||
LiveRoomPlayInfo.Codec codec = response.getPlayurlInfo().getPlayurl().getStream().get(0).getFormat().get(0).getCodec().get(0);
|
||||
String url = codec.getUrlInfo().get(0).getHost() + codec.getBaseUrl() + codec.getUrlInfo().get(0).getExtra();
|
||||
|
||||
if (bean.getRecordLiveModel() == 1) {
|
||||
javaRecord(url, response);
|
||||
} else {
|
||||
ffmpeg(url, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable throwable) {
|
||||
Log.e(throwable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void javaRecord(String url, LiveRoomPlayInfo playInfo) {
|
||||
|
||||
HttpDownloadUtils.download(new HttpDownloadUtils.Builder()
|
||||
.setUrl(url)
|
||||
.setPath(savePath)
|
||||
.setDownloadInterface(new DownloadInterface() {
|
||||
@Override
|
||||
public void onDownloadStart() {
|
||||
super.onDownloadStart();
|
||||
VideoTask.this.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDownloading(double soFarBytes, double totalBytes) {
|
||||
return isDownload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownload(File file) {
|
||||
super.onDownload(file);
|
||||
stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
super.onError(e);
|
||||
stop();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void ffmpeg(String url, LiveRoomPlayInfo playInfo) {
|
||||
String ffmpegPath = ConfigTools.load(ConfigTools.CONFIG, "ffmpeg", String.class);
|
||||
String cookie = "";
|
||||
LoginCookieDatabaseBean ck = BiliBiliLoginDatabase.getInstance().getCookie(config.getRecordUid());
|
||||
if (ck != null) {
|
||||
cookie = ck.toCookieString();
|
||||
}
|
||||
|
||||
FFmpegUtils.Builder builder = new FFmpegUtils.Builder()
|
||||
.withParam("-user_agent", ConfigTools.getUserAgent())
|
||||
.withParam("-headers", "Referer: https://live.bilibili.com")
|
||||
// .withNotSymbolParam("-progress", "-")
|
||||
.withNotSymbolParam("-threads", "8")
|
||||
.withNotSymbolParam("-c:v", "copy")
|
||||
.withNotSymbolParam("-y", "");
|
||||
if (ck != null) {
|
||||
builder = builder.withParam("-cookies", cookie);
|
||||
}
|
||||
FFmpegUtils command = builder.build(config.getRoomId(), ffmpegPath, url, savePath);
|
||||
Log.i(command.getCommand());
|
||||
try {
|
||||
command.start(new DownloadInterface() {
|
||||
@Override
|
||||
public void onDownloadStart() {
|
||||
super.onDownloadStart();
|
||||
VideoTask.this.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDownloading(double soFarBytes, double totalBytes) {
|
||||
if (!isDownload) {
|
||||
command.stop();
|
||||
}
|
||||
return super.onDownloading(soFarBytes, totalBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownload(File file) {
|
||||
super.onDownload(file);
|
||||
Log.e("下载完成 ");
|
||||
stop();
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void onStart() {
|
||||
videoDatabaseBean = new LiveVideoDatabaseBean();
|
||||
videoDatabaseBean.setPath(savePath);
|
||||
videoDatabaseBean.setRoomInfoJson(JSONObject.toJSONString(roomInfo));
|
||||
videoDatabaseBean.setStartTime(new Date());
|
||||
database.addLiveInfo(videoDatabaseBean);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveLiveInfo(LiveRoomInfo roomInfo) {
|
||||
|
||||
}
|
||||
|
||||
public List<VideoFilePath> getAllVideoPath() {
|
||||
BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase();
|
||||
List<LiveConfigDatabaseBean> list = configDatabase.getAllConfig();
|
||||
configDatabase.close();
|
||||
List<VideoFilePath> filePathList = new ArrayList<>();
|
||||
for (LiveConfigDatabaseBean bean : list) {
|
||||
filePathList.addAll(getVideoFilePath(bean));
|
||||
}
|
||||
return filePathList;
|
||||
}
|
||||
|
||||
public List<VideoFilePath> getVideoPath(String roomId) {
|
||||
BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase();
|
||||
LiveConfigDatabaseBean bean = configDatabase.getConfig(new String(roomId));
|
||||
configDatabase.close();
|
||||
return new ArrayList<>(getVideoFilePath(bean));
|
||||
}
|
||||
|
||||
private List<VideoFilePath> getVideoFilePath(LiveConfigDatabaseBean configBean) {
|
||||
List<VideoFilePath> filePathList = new ArrayList<>();
|
||||
String recordPath = configBean.getRecordPath() + File.separator + configBean.getAnchorName();
|
||||
File recordDir = new File(recordPath);
|
||||
if (recordDir.exists()) {
|
||||
|
||||
BiliLiveDatabase database = new BiliLiveDatabase(LiveRoomConfig.buildConfig(configBean.getRoomId().toString()), recordDir.getAbsolutePath());
|
||||
VideoFilePath path = createVideoRootFilePath(configBean, recordDir);
|
||||
List<LiveVideoDatabaseBean> infos = database.getLiveInfos();
|
||||
database.close();
|
||||
path.setChildren(getVideoInfo(infos));
|
||||
filePathList.add(path);
|
||||
}
|
||||
return filePathList;
|
||||
}
|
||||
|
||||
private VideoFilePath createVideoRootFilePath(LiveConfigDatabaseBean config, File db) {
|
||||
VideoFilePath path = new VideoFilePath();
|
||||
path.setRoomId(config.getRoomId().toString());
|
||||
path.setCover(config.getAnchorFace());
|
||||
path.setName(config.getAnchorName());
|
||||
path.setParent(true);
|
||||
path.setPath(FileServerUtils.toUrl(db.getParent()));
|
||||
return path;
|
||||
}
|
||||
|
||||
private List<VideoFilePath> getVideoInfo(List<LiveVideoDatabaseBean> videoList) {
|
||||
List<VideoFilePath> filePathList = new ArrayList<>();
|
||||
for (LiveVideoDatabaseBean bean : videoList) {
|
||||
VideoFilePath path = new VideoFilePath();
|
||||
LiveRoomInfo roomInfo = JSONObject.parseObject(bean.getRoomInfoJson(), LiveRoomInfo.class);
|
||||
path.setRoomId(roomInfo.getRoomId().toString());
|
||||
path.setName(roomInfo.getTitle());
|
||||
path.setPath(DateFormatUtils.format(bean.getSql_time()));
|
||||
path.setCover(roomInfo.getKeyframe());
|
||||
path.setParent(false);
|
||||
path.setChildren(null);
|
||||
filePathList.add(path);
|
||||
}
|
||||
return filePathList;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
LiveVideoService service = new LiveVideoService();
|
||||
List<VideoFilePath> path = service.getAllVideoPath();
|
||||
Log.i("path.size() = " + path.size());
|
||||
Log.i(JSONArray.toJSONString(path));
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,16 @@
|
||||
package com.yutou.bilibili.services;
|
||||
|
||||
import com.yutou.biliapi.api.LiveApi;
|
||||
import com.yutou.biliapi.bean.live.LiveRoomConfig;
|
||||
import com.yutou.biliapi.bean.live.LiveRoomInfo;
|
||||
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
|
||||
import com.yutou.biliapi.net.BiliLiveNetApiManager;
|
||||
import com.yutou.biliapi.net.WebSocketManager;
|
||||
import com.yutou.bilibili.Tools.DateFormatUtils;
|
||||
import com.yutou.bilibili.databases.SystemConfigDatabases;
|
||||
import com.yutou.bilibili.datas.SystemConfigDatabaseBean;
|
||||
import com.yutou.common.okhttp.HttpBody;
|
||||
import com.yutou.common.utils.Log;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
import retrofit2.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
@ -30,7 +19,7 @@ import java.util.concurrent.TimeUnit;
|
||||
@Service
|
||||
public class SystemService {
|
||||
@Resource
|
||||
LiveVideoService videoService;
|
||||
LiveVideoDownloadService videoService;
|
||||
@Resource
|
||||
LiveDanmuService danmuService;
|
||||
SystemConfigDatabases databases = new SystemConfigDatabases();
|
||||
@ -67,6 +56,8 @@ public class SystemService {
|
||||
try {
|
||||
if (bean.isRecordDanmu() && bean.checkRecordDanmuTime()) {
|
||||
recordDanmu(bean);
|
||||
} else if (!bean.checkRecordDanmuTime()) {
|
||||
stopRecordDanmu(bean);
|
||||
}
|
||||
if (bean.isRecordLive() && bean.checkRecordLiveTime()) {
|
||||
recordVideo(bean);
|
||||
@ -79,6 +70,12 @@ public class SystemService {
|
||||
}, 0, getLoopTimer(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void stopRecordDanmu(LiveConfigDatabaseBean bean) {
|
||||
if (!videoService.checkDownload(bean.getRoomId())) {
|
||||
danmuService.stop(bean.getRoomId(), false);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
scheduled.cancel(true);
|
||||
videoService.stopAll();
|
||||
|
Loading…
Reference in New Issue
Block a user