Compare commits
31 Commits
d9919f4d26
...
dev_新版
| Author | SHA1 | Date | |
|---|---|---|---|
| c635d73911 | |||
| 516e030266 | |||
| d2bcd5c64a | |||
| 6ee610f4cc | |||
| 1803ef845c | |||
| 0497e9d0e2 | |||
| e089c57c01 | |||
| b5c85409cf | |||
| 23861a897f | |||
| 313f8ac914 | |||
| 3ac3c8e5e5 | |||
| a12b1b5bd8 | |||
| dad4bcff15 | |||
| 1bfca68514 | |||
| 6f57c60a75 | |||
| fa8523da97 | |||
| e450964cd8 | |||
| e4e5696b70 | |||
| 31dd9cf1a1 | |||
| 59a6641d50 | |||
| 5ae1b474f9 | |||
| 8afb8f8f0b | |||
| 3f10e19e50 | |||
| 52e4312a32 | |||
| 4474da490f | |||
| 903ba44bbb | |||
| 38854993d0 | |||
| ad84c71a30 | |||
| e8cceef419 | |||
| 48a15c8769 | |||
| b5c2153ad0 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -35,4 +35,5 @@ build/
|
||||
### dir ###
|
||||
/live/
|
||||
/databases/
|
||||
/cache/
|
||||
/cache/
|
||||
/logs/
|
||||
87
Web/css/inputTag.css
Normal file
87
Web/css/inputTag.css
Normal file
@@ -0,0 +1,87 @@
|
||||
@keyframes fariy-fadein {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.fairy-tag-container {
|
||||
width: auto;
|
||||
min-height: 100px;
|
||||
padding: 5px;
|
||||
border: 1px solid #e6e6e6;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.fairy-tag-container:hover {
|
||||
border-color: #d2d2d2;
|
||||
}
|
||||
.fairy-tag-container span.fairy-tag {
|
||||
float: left;
|
||||
font-size: 13px;
|
||||
padding: 5px 8px;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 5px;
|
||||
border-radius: 2px;
|
||||
line-height: 16px;
|
||||
}
|
||||
.fairy-tag-container span.fairy-tag a {
|
||||
font-size: 11px;
|
||||
font-weight: bolder;
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.fairy-tag-container span.fairy-tag a:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.fairy-tag-container span.fairy-bg-red {
|
||||
background-color: #FF5722;
|
||||
}
|
||||
.fairy-tag-container span.fairy-bg-orange {
|
||||
background-color: #FFB800;
|
||||
}
|
||||
.fairy-tag-container span.fairy-bg-green {
|
||||
background-color: #009688;
|
||||
}
|
||||
.fairy-tag-container span.fairy-bg-cyan {
|
||||
background-color: #2F4056;
|
||||
}
|
||||
.fairy-tag-container span.fairy-bg-blue {
|
||||
background-color: #1E9FFF;
|
||||
}
|
||||
.fairy-tag-container span.fairy-bg-black {
|
||||
background-color: #393D49;
|
||||
}
|
||||
.fairy-tag-container span.fairy-bg-red,
|
||||
.fairy-tag-container span.fairy-bg-orange,
|
||||
.fairy-tag-container span.fairy-bg-green,
|
||||
.fairy-tag-container span.fairy-bg-cyan,
|
||||
.fairy-tag-container span.fairy-bg-blue,
|
||||
.fairy-tag-container span.fairy-bg-black {
|
||||
color: #ffffff;
|
||||
}
|
||||
.fairy-tag-container .fairy-anim-fadein {
|
||||
animation: fariy-fadein 0.3s both;
|
||||
}
|
||||
.fairy-tag-container .fairy-tag-input[type='text'] {
|
||||
width: 80px;
|
||||
font-size: 13px;
|
||||
padding: 6px;
|
||||
background: transparent;
|
||||
border: 0 none;
|
||||
outline: 0;
|
||||
}
|
||||
.fairy-tag-container .fairy-tag-input[type='text']:focus::-webkit-input-placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
.fairy-tag-container .fairy-tag-input[type='text']:focus:-moz-placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
.fairy-tag-container .fairy-tag-input[type='text']:focus:-moz-placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
.fairy-tag-container .fairy-tag-input[type='text']:focus:-ms-input-placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
/*# sourceMappingURL=inputTag.css.map */
|
||||
31
Web/html/body/config.html
Normal file
31
Web/html/body/config.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!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>
|
||||
<link rel="stylesheet" href="/layui/css/layui.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="header"></div>
|
||||
不想做了,鸽
|
||||
</body>
|
||||
<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 = 5;
|
||||
$('#header').load("/html/header.html");
|
||||
</script>
|
||||
<script>
|
||||
|
||||
</script>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
</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">
|
||||
@@ -11,15 +12,56 @@
|
||||
<div id="header"></div>
|
||||
不想做了,鸽
|
||||
</body>
|
||||
<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 src="/js/lodash.min.js"></script>
|
||||
<script src="/js/Chart.min.js"></script>
|
||||
|
||||
<script>
|
||||
headerModel=3;
|
||||
headerModel = 3;
|
||||
var roomId = getParam('roomId');
|
||||
$('#header').load("/html/header.html");
|
||||
</script>
|
||||
|
||||
<script>
|
||||
function init() {
|
||||
if (!hasString(getParam('roomId'))) {
|
||||
showAnchor();
|
||||
return
|
||||
}
|
||||
initTabs()
|
||||
initBarrageRenderer();
|
||||
}
|
||||
function showAnchor() {
|
||||
|
||||
layer.open({
|
||||
type: 2, // page 层类型
|
||||
area: ['320px', '400px'],
|
||||
title: '选择主播',
|
||||
shade: 0.6, // 遮罩透明度
|
||||
shadeClose: false, // 点击遮罩区域,关闭弹层
|
||||
maxmin: true, // 允许全屏最小化
|
||||
anim: 1, // 0-6 的动画形式,-1 不开启
|
||||
btn: ['确定', '取消'],
|
||||
content: '/html/ui/selectAnchor.html',
|
||||
yes: function (index, layero) {
|
||||
var iframeWin = window[layero.find('iframe')[0]['name']];
|
||||
var anchorRoomId = iframeWin.$('#anchorRoomId')[0].value;
|
||||
if (!hasString(anchorRoomId)) {
|
||||
layer.msg("请选择")
|
||||
return
|
||||
}
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.set('roomId', anchorRoomId);
|
||||
window.location.href = url.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
init();
|
||||
</script>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
</html>
|
||||
@@ -15,7 +15,9 @@
|
||||
<script type="text/html" id="toolbarDemo">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" onclick="createRoom()">添加新房间</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-event="getCheckData">获取选中行数据</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-event="editArray">批量编辑</button>
|
||||
<button class="layui-btn layui-btn-sm" lay-event="deleteArray">批量删除</button>
|
||||
<button class="layui-btn layui-btn-sm layui-bg-red" lay-event="deleteAll">全部删除</button>
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/html" id="toolDemo">
|
||||
@@ -41,11 +43,22 @@
|
||||
layer.open({
|
||||
type: 2,
|
||||
title: "添加新房间",
|
||||
maxmin: true,
|
||||
area: ['600px', '500px'],
|
||||
content: '/html/ui/createConfig.html?roomId=' + roomId
|
||||
|
||||
});
|
||||
}
|
||||
function editArrayRoom(array) {
|
||||
layer.open({
|
||||
type: 2,
|
||||
title: "批量编辑",
|
||||
maxmin: true,
|
||||
area: ['600px', '500px'],
|
||||
content: '/html/ui/createConfig.html?array=' + array
|
||||
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
layui.use(['table', 'dropdown'], function () {
|
||||
@@ -77,13 +90,13 @@
|
||||
{ field: 'anchorName', title: '用户名', width: 100, fixed: 'left' },
|
||||
{ field: 'anchorFace', title: '头像', width: 80, templet: '<div><image src="" onerror="showImage(\'{{= d.anchorFace }}\',this);" style="width: 30px;height: 30px;"></div>' },
|
||||
{ field: 'live_room_id', title: '房间号', width: 80, templet: '<div><a href="https://live.bilibili.com/{{= d.live_room_id}}" target="_blank">{{= d.live_room_id}}</a>' },
|
||||
{ field: 'recordPath', title: '保存路径', width: 120 },
|
||||
{ field: 'keyword', title: '监听关键词', width: 120 },
|
||||
{ field: 'recordDanmu', title: '录制弹幕', width: 120, sort: true },
|
||||
{ field: 'recordLive', title: '录制视频', width: 120, sort: true },
|
||||
{ field: 'syncDanmuForLive', title: '同步录制', width: 120, sort: true },
|
||||
{ field: 'recordDanmuDate', title: '弹幕录制预定时间', width: 100, sort: true },
|
||||
{ field: 'recordLiveDate', title: '视频录制预定时间', width: 100, sort: true },
|
||||
{ field: 'recordUid', title: '录制账号', width: 120, sort: true, templet: '<div><a href="https://space.bilibili.com/{{= d.recordUid}}" target="_blank">{{= d.recordUid}}</a>' },
|
||||
{ field: 'recordDanmu', title: '录制弹幕', width: 120, sort: true },
|
||||
{ field: 'sql_time', title: '添加时间', width: 100 },
|
||||
{ fixed: "right", title: "操作", width: 190, align: "center", toolbar: "#toolDemo" }
|
||||
]],
|
||||
@@ -105,10 +118,56 @@
|
||||
var id = obj.config.id;
|
||||
var checkStatus = table.checkStatus(id);
|
||||
var othis = lay(this);
|
||||
var data = checkStatus.data;
|
||||
var array = data.map(function (item) {
|
||||
return item.live_room_id
|
||||
})
|
||||
var arrayName = data.map(function (item) {
|
||||
return item.anchorName
|
||||
})
|
||||
switch (obj.event) {
|
||||
case 'getCheckData':
|
||||
var data = checkStatus.data;
|
||||
layer.alert(layui.util.escape(JSON.stringify(data)));
|
||||
case 'deleteArray':
|
||||
if (array.length === 0) {
|
||||
layer.msg('没有数据', { icon: 2 })
|
||||
return
|
||||
}
|
||||
layer.confirm('确定删除 ' + arrayName + "?", { icon: 3 }, function () {
|
||||
var loadIndex = showLoadingDialog();
|
||||
deleteArrayRoomConfig(array)
|
||||
.then(json => {
|
||||
layer.close(loadIndex)
|
||||
layer.msg(json.data, { icon: 1 });
|
||||
table.reloadData('roomList', {}, false)
|
||||
})
|
||||
|
||||
}, function () {
|
||||
|
||||
});
|
||||
break;
|
||||
case 'editArray':
|
||||
if (array.length === 0) {
|
||||
layer.msg('没有数据', { icon: 2 })
|
||||
return
|
||||
}
|
||||
editArrayRoom(array)
|
||||
break;
|
||||
case 'deleteAll':
|
||||
layer.confirm('确定删除所有配置?', { icon: 3 }, function () {
|
||||
var loadIndex = showLoadingDialog();
|
||||
deleteAllRoomConfig()
|
||||
.then(json => {
|
||||
layer.close(loadIndex)
|
||||
if (json.status === -1) {
|
||||
layer.msg(json.message, { icon: 2 });
|
||||
} else {
|
||||
layer.msg(json.data, { icon: 1 });
|
||||
}
|
||||
table.reloadData('roomList', {}, false)
|
||||
})
|
||||
|
||||
}, function () {
|
||||
|
||||
});
|
||||
break;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -24,7 +24,11 @@
|
||||
<script type="text/html" id="toolDemo">
|
||||
<div class="layui-clear-space">
|
||||
<a class="layui-btn layui-btn-xs" lay-event="del">删除</a>
|
||||
<a class="layui-btn layui-btn-xs" lay-event="input">导入关注</a>
|
||||
<!-- <a class="layui-btn layui-btn-xs" lay-event="input">导入关注</a> -->
|
||||
<a class="layui-btn layui-btn-xs" lay-event="more">
|
||||
导入
|
||||
<i class="layui-icon layui-icon-down"></i>
|
||||
</a>
|
||||
</div>
|
||||
</script>
|
||||
<script>
|
||||
@@ -143,8 +147,28 @@
|
||||
var checkStatus = table.checkStatus(id);
|
||||
var othis = lay(this);
|
||||
switch (obj.event) {
|
||||
case 'input':
|
||||
onClickInput(data.uid)
|
||||
case 'more':
|
||||
dropdown.render({
|
||||
elem: this, // 触发事件的 DOM 对象
|
||||
show: true, // 外部事件触发即显示
|
||||
data: [{
|
||||
title: '关注列表',
|
||||
id: 'follow'
|
||||
}, {
|
||||
title: '正在开播',
|
||||
id: 'live'
|
||||
}],
|
||||
click: function (menudata) {
|
||||
if (menudata.id === 'follow') {
|
||||
onClickInput(data.uid)
|
||||
} else if (menudata.id === 'live') {
|
||||
onClickFollow(data.uid)
|
||||
}
|
||||
},
|
||||
align: 'right', // 右对齐弹出
|
||||
style: 'box-shadow: 1px 1px 10px rgb(0 0 0 / 12%);' // 设置额外样式
|
||||
})
|
||||
|
||||
break;
|
||||
};
|
||||
});
|
||||
@@ -172,11 +196,34 @@
|
||||
window.location.href = url.toString();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function onClickFollow(uid) {
|
||||
layer.open({
|
||||
type: 2, // page 层类型
|
||||
area: ['650px', '400px'],
|
||||
title: '选择主播',
|
||||
shade: 0.6, // 遮罩透明度
|
||||
shadeClose: false, // 点击遮罩区域,关闭弹层
|
||||
maxmin: true, // 允许全屏最小化
|
||||
anim: 1, // 0-6 的动画形式,-1 不开启
|
||||
// btn: ['确定', '取消'],
|
||||
content: '/html/ui/userFollowLive.html?uid=' + uid,
|
||||
yes: function (index, layero) {
|
||||
var iframeWin = window[layero.find('iframe')[0]['name']];
|
||||
var anchorRoomId = iframeWin.$('#anchorRoomId')[0].value;
|
||||
if (!hasString(anchorRoomId)) {
|
||||
layer.msg("请选择")
|
||||
return
|
||||
}
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.set('roomId', anchorRoomId);
|
||||
window.location.href = url.toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -6,11 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<title>视频中心</title>
|
||||
<link rel="stylesheet" href="/layui/css/layui.css">
|
||||
<style>
|
||||
canvas {
|
||||
width: 600px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="layui-bg-gray">
|
||||
@@ -27,33 +23,44 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs7">
|
||||
<div class="grid-demo" id="container" style="margin-top: 16px; width: 100%;">
|
||||
<video id="videoElement" controls style="width: 100%;"></video>
|
||||
<div class="layui-row">
|
||||
<div class="grid-demo" id="container" style="margin-top: 16px; width: 100%;">
|
||||
<video id="videoElement" controls style="width: 100%;"></video>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-row">
|
||||
<div class="layui-form layui-col-xs1">
|
||||
<input type="checkbox" id="danmuCheckBox" title="弹幕" lay-skin="tag" lay-filter="danmuCheckBox" on
|
||||
checked>
|
||||
</div>
|
||||
<div class="layui-col-xs9" style="text-align: center;">
|
||||
<div id="slider" lay-options="{value: 100,input:true}"></div>
|
||||
<span>弹幕透明度</span>
|
||||
</div>
|
||||
<div class="layui-col-xs2" style="text-align: right; align-self: center;">
|
||||
弹幕装载数:<span id="danmuSize">你猜</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-col-xs2" style="margin-left: 16px; width: 22%; margin-top: 16px;">
|
||||
<p style="font-size: 4;font-weight: bold; text-align: center;">Super Chat 列表</p>
|
||||
<div class="flow-demo" id="flow" style="height: 60vh; overflow: auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs2"> </div>
|
||||
<div class="layui-form layui-col-xs1">
|
||||
<input type="checkbox" id="danmuCheckBox" title="弹幕" lay-skin="tag" lay-filter="danmuCheckBox" on checked>
|
||||
</div>
|
||||
|
||||
<div class="layui-col-xs5" style="text-align: center;">
|
||||
<div id="slider" lay-options="{value: 100,input:true}"></div>
|
||||
<span>弹幕透明度</span>
|
||||
<div class="layui-col-xs2" style="margin-left: 16px; width: 22%; margin-top: 16px;">
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md12">
|
||||
<canvas id="giftChart" style="width: 100%; max-height: 100%;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-md12">
|
||||
<p style="font-size: 4;font-weight: bold; text-align: center;">Super Chat 列表</p>
|
||||
<div class="flow-demo" id="flow" style="max-height: 60vh; overflow: auto;"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-row">
|
||||
<div class="layui-col-xs2"> </div>
|
||||
<div class="layui-col-xs7">
|
||||
<canvas id="giftChart" height="200"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 10vh;"></div>
|
||||
|
||||
|
||||
<!-- <div style="margin-bottom: 10vh;"></div> -->
|
||||
</body>
|
||||
<script id="menulist" type="text/html">
|
||||
<ul class="layui-tab-title">
|
||||
@@ -232,45 +239,71 @@
|
||||
initChart(roomId, options.id)
|
||||
|
||||
});
|
||||
var chartView = null;
|
||||
function initChart(roomId, videoId) {
|
||||
getVideoGiftInfo(roomId, videoId)
|
||||
.then(data => {
|
||||
if (data.status != 100) {
|
||||
.then(json => {
|
||||
if (json.status != 100) {
|
||||
return
|
||||
}
|
||||
var lables = [];
|
||||
var values = [];
|
||||
data.data.guardInfo.forEach(item => {
|
||||
lables.push(item.gift_name+"\n"+item.total_price/100+"¥")
|
||||
json.data.guardInfo.forEach(item => {
|
||||
lables.push(item.gift_name + "\n" + item.total_price + "¥")
|
||||
values.push(item.total_num)
|
||||
});
|
||||
data.data.giftInfo.forEach(item => {
|
||||
lables.push(item.gift_name+"\n"+item.total_price/100+"¥")
|
||||
json.data.giftInfo.forEach(item => {
|
||||
lables.push(item.gift_name + "\n" + item.total_price + "¥")
|
||||
values.push(item.total_gift_num)
|
||||
});
|
||||
|
||||
new Chart($('#giftChart').get(0), {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: lables,
|
||||
datasets: [{
|
||||
label: '礼物(总额:'+data.data.price/100+"¥)",
|
||||
data: values,
|
||||
borderWidth: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
setChart(lables, values, json)
|
||||
initSC(json.data.superChat)
|
||||
})
|
||||
|
||||
}
|
||||
function setChart(lables, values, data) {
|
||||
if (chartView !== null) {
|
||||
chartView.destroy()
|
||||
}
|
||||
chartView = new Chart($('#giftChart').get(0), {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: lables,
|
||||
datasets: [{
|
||||
label: '礼物(总额:' + data.data.price + "¥)",
|
||||
data: values,
|
||||
borderWidth: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true
|
||||
}
|
||||
},
|
||||
onClick: (e) => {
|
||||
var giftName = e.chart.tooltip.title[0]
|
||||
console.log(e.chart.tooltip.title)
|
||||
|
||||
layer.open({
|
||||
type: 2, // page 层类型
|
||||
area: ['650px', '400px'],
|
||||
title: '礼物详情',
|
||||
shade: 0.6, // 遮罩透明度
|
||||
shadeClose: false, // 点击遮罩区域,关闭弹层
|
||||
maxmin: true, // 允许全屏最小化
|
||||
anim: 1, // 0-6 的动画形式,-1 不开启
|
||||
// btn: ['确定', '取消'],
|
||||
content: '/html/ui/videoGiftItem.html?roomId=' + roomId + "&videoId=" + videoId + "&giftName=" + giftName,
|
||||
yes: function (index, layero) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function initMenu(data) {
|
||||
var view = $("#menuView").get(0);
|
||||
laytpl($('#menulist').get(0).innerHTML).render(data, function (html) {
|
||||
@@ -313,7 +346,8 @@
|
||||
}
|
||||
function playVideo(url) {
|
||||
var video = document.getElementById('videoElement');
|
||||
var videoSrc = url;
|
||||
var videoSrc = window.location.origin + url;
|
||||
console.log('url = ' + videoSrc)
|
||||
var hls = new Hls();
|
||||
hls.loadSource(videoSrc);
|
||||
hls.attachMedia(video);
|
||||
@@ -325,12 +359,22 @@
|
||||
}
|
||||
function initDanmu() {
|
||||
barrageRenderer.setBarrages([])
|
||||
getDanmu(getParam('roomId'), videoId)
|
||||
loadDanmu(0)
|
||||
}
|
||||
function loadDanmu(page) {
|
||||
getDanmu(getParam('roomId'), videoId, page)
|
||||
.then(data => {
|
||||
barrageRenderer.setBarrages(data.data.danmu);
|
||||
initSC(data.data.superChat)
|
||||
if (page === 0) {
|
||||
$('#danmuSize').get(0).innerHTML = data.data.danmuCount;
|
||||
barrageRenderer.setBarrages(data.data.danmu)
|
||||
} else {
|
||||
barrageRenderer.barrageLayoutCalculate.allBarrageInstances.push(...data.data.danmu);
|
||||
}
|
||||
if (data.data.danmu.length === 3000) {
|
||||
loadDanmu(page + 1)
|
||||
}
|
||||
// barrageRenderer.setBarrages(data.data.danmu);
|
||||
})
|
||||
|
||||
}
|
||||
function initSC(scList) {
|
||||
flow.load({
|
||||
@@ -339,10 +383,7 @@
|
||||
isAuto: true,
|
||||
isLazyimg: false,
|
||||
done: function (page, next) { // 加载下一页
|
||||
// 模拟插入
|
||||
console.log(scList)
|
||||
laytpl($('#superChatCard').get(0).innerHTML).render(scList, function (html) {
|
||||
console.log(html)
|
||||
next(html, false)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<li class="layui-nav-item video"><a href="/html/body/video.html">视频管理</a></li>
|
||||
<li class="layui-nav-item data" ><a href="/html/body/data.html">数据中心</a></li>
|
||||
<li class="layui-nav-item user"><a href="/html/body/user.html">用户中心</a></li>
|
||||
<li class="layui-nav-item config layui-layout-right"> <a href="/html/body/config.html"><i class="layui-icon layui-icon-set-fill" style="font-size: 20px; margin-right: 10px;"></i>系统设置</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
<!-- <script src="/layui/layui.js"></script> -->
|
||||
@@ -15,11 +16,13 @@
|
||||
$(".video").removeClass("layui-this")
|
||||
$(".data").removeClass("layui-this")
|
||||
$(".user").removeClass("layui-this")
|
||||
$(".config").removeClass("layui-this")
|
||||
switch(headerModel){
|
||||
case 0:$(".index").addClass('layui-this');break;
|
||||
case 1:$(".live").addClass('layui-this');break;
|
||||
case 2:$(".video").addClass('layui-this');break;
|
||||
case 3:$(".data").addClass('layui-this');break;
|
||||
case 4:$(".user").addClass('layui-this');break;
|
||||
case 5:$(".config").addClass('layui-this');break;
|
||||
}
|
||||
</script>
|
||||
@@ -1,5 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="/layui/css/layui.css">
|
||||
<link rel="stylesheet" href="/css/inputTag.css">
|
||||
<style>
|
||||
.layui-form-label {
|
||||
width: 120px !important;
|
||||
@@ -29,25 +34,43 @@
|
||||
<input type="checkbox" name="recordLive" lay-skin="switch" lay-filter="switchTest" title="启用|禁用">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item" pane>
|
||||
<label class="layui-form-label">录制视频时<br>同步录制弹幕</label>
|
||||
<div class="layui-input-block"><br>
|
||||
<input type="checkbox" name="syncDanmuForLive" lay-skin="switch" lay-filter="switchSync" title="启用|禁用">
|
||||
<i class="layui-icon layui-icon-help layui-text-em " onclick="timeTips(3,this)"></i>
|
||||
<br>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="layui-form-item" pane>
|
||||
<label class="layui-form-label">录制日期</label>
|
||||
<div class="layui-input-block">
|
||||
<input type="checkbox" checked name="week_1" title="周一" lay-skin="tag">
|
||||
<input type="checkbox" checked name="week_2" title="周二" lay-skin="tag">
|
||||
<input type="checkbox" checked name="week_3" title="周三" lay-skin="tag">
|
||||
<input type="checkbox" checked name="week_4" title="周四" lay-skin="tag">
|
||||
<input type="checkbox" checked name="week_5" title="周五" lay-skin="tag">
|
||||
<input type="checkbox" checked name="week_6" title="周六" lay-skin="tag">
|
||||
<input type="checkbox" checked name="week_7" title="周日" lay-skin="tag">
|
||||
<input type="checkbox" name="week_1" title="周一" lay-skin="tag">
|
||||
<input type="checkbox" name="week_2" title="周二" lay-skin="tag">
|
||||
<input type="checkbox" name="week_3" title="周三" lay-skin="tag">
|
||||
<input type="checkbox" name="week_4" title="周四" lay-skin="tag">
|
||||
<input type="checkbox" name="week_5" title="周五" lay-skin="tag">
|
||||
<input type="checkbox" name="week_6" title="周六" lay-skin="tag">
|
||||
<input type="checkbox" name="week_7" title="周日" lay-skin="tag">
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">关键词检测</label>
|
||||
<div class="layui-input-inline fairy-tag-container" style="width: 60%;">
|
||||
<input type="text" id="keywordList" name="keywordList" autocomplete="off"
|
||||
class="layui-input fairy-tag-input ">
|
||||
</div>
|
||||
<i class="layui-icon layui-icon-help" onclick="timeTips(4,this)"></i>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
<label class="layui-form-label">弹幕录制时间</label>
|
||||
<div class="layui-input-inline">
|
||||
<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(false,this)"><i
|
||||
<div class="layui-form-mid layui-text-em" onclick="timeTips(1,this)"><i
|
||||
class="layui-icon layui-icon-help"></i> </div>
|
||||
</div>
|
||||
<div class="layui-form-item">
|
||||
@@ -56,7 +79,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(true,this)"><i
|
||||
<div class="layui-form-mid layui-text-em" onclick="timeTips(2,this)"><i
|
||||
class="layui-icon layui-icon-help"></i> </div>
|
||||
</div>
|
||||
<div class="layui-inline">
|
||||
@@ -88,23 +111,38 @@
|
||||
<script src="/js/jquery-3.2.1.js"></script>
|
||||
<script src="/js/httpUtils.js"></script>
|
||||
<script src="/js/CommonConfig.js"></script>
|
||||
<script src="/js/inputTag.js"></script>
|
||||
<script>
|
||||
function timeTips(isLive,that) {
|
||||
if(isLive){
|
||||
layer.tips('是从开始到结束时间范围内,主播开播会启动录制,超过结束范围不会中断正在录制的任务', that);
|
||||
}else{
|
||||
layer.tips('仅在当前时间范围内录制,如遇到正在直播,则延迟到下播时停止录制', that);
|
||||
function timeTips(type, that) {
|
||||
var tips = ''
|
||||
switch (type) {
|
||||
case 1: tips = '仅在时间范围内录制,如遇到正在直播,则延迟到下播时停止录制<p style="color:red;">注:如果启用同步录制功能,那本设置将无效</p>'; break;
|
||||
case 2: tips = '仅在时间范围内录制,主播开播会启动录制,正在直播时超过结束范围则延迟到下播时停止录制'; break;
|
||||
case 3: tips = '启用后,录制视频时会同步录制弹幕,下播后会同步停止录制.同时上面录制弹幕按钮将失效<p style="color:red;">启用该选项必须启用录制视频功能才可有效</p>'; break;
|
||||
case 4: tips = '在监测时间范围内(周X、时段),开播标题包含关键词才会开始录制<p style="color:red;">仅针对录制视频功能,录制弹幕不受关键词影响</p>'; break;
|
||||
}
|
||||
|
||||
layer.alert(tips)
|
||||
|
||||
}
|
||||
|
||||
var roomId = getParam("roomId");
|
||||
var editArray = getParam("array")
|
||||
|
||||
layui.use(['form', 'laytpl', 'laydate'], function () {
|
||||
var form = layui.form;
|
||||
var layer = layui.layer;
|
||||
var laytpl = layui.laytpl;
|
||||
var laydate = layui.laydate;
|
||||
var inputTag = layui.inputTag;
|
||||
var windowsIndex;
|
||||
var keywordList=[];
|
||||
inputTag = inputTag.render({
|
||||
elem: '#keywordList',
|
||||
data: [],
|
||||
onChange: function (data, value, type) {
|
||||
keywordList = data;
|
||||
}
|
||||
});
|
||||
form.render();
|
||||
// 提交事件
|
||||
form.on('submit(submit-form)', function (data) {
|
||||
@@ -121,17 +159,38 @@
|
||||
weeks.push(i.toString());
|
||||
}
|
||||
}
|
||||
field.keywordList = keywordList
|
||||
field.weeks = weeks;
|
||||
console.log(field)
|
||||
addRoomConfig(field)
|
||||
.then(json => {
|
||||
layer.msg(json.message, function () {
|
||||
if (json.status == 100) {
|
||||
close()
|
||||
}
|
||||
})
|
||||
if (editArray === null) {
|
||||
var loadIndex = showLoadingDialog();
|
||||
addRoomConfig(field)
|
||||
.then(json => {
|
||||
layer.close(loadIndex)
|
||||
layer.msg(json.message, function () {
|
||||
if (json.status == 100) {
|
||||
close()
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
} else {
|
||||
var array = []
|
||||
editArray.split(',').forEach(element => {
|
||||
array.push(element)
|
||||
});
|
||||
var loadIndex = showLoadingDialog();
|
||||
setArrayRoomConfig(array, field)
|
||||
.then(json => {
|
||||
layer.close(loadIndex)
|
||||
layer.msg(json.message, function () {
|
||||
if (json.status == 100) {
|
||||
close()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
return false; // 阻止默认 form 跳转
|
||||
});
|
||||
@@ -149,12 +208,23 @@
|
||||
type: 'time',
|
||||
range: true
|
||||
});
|
||||
form.on('switch(switchSync)', function (data) {
|
||||
if (form.val('form-filter').syncDanmuForLive === 'on') {
|
||||
$("[name='recordDanmu']").prop("disabled", true);
|
||||
} else {
|
||||
$("[name='recordDanmu']").prop("disabled", false);
|
||||
}
|
||||
form.render();
|
||||
});
|
||||
function init() {
|
||||
getUserInfo();
|
||||
if (roomId !== null) {
|
||||
$('#btn_reset').get(0).disabled = true;
|
||||
editRoom()
|
||||
}
|
||||
if (editArray !== null) {
|
||||
editArrayRoom()
|
||||
}
|
||||
}
|
||||
function getUserInfo() {
|
||||
getBiliAllUser()
|
||||
@@ -178,17 +248,42 @@
|
||||
'recordDanmu': json.recordDanmu,
|
||||
'recordDanmuDate': json.recordDanmuDate,
|
||||
'recordLiveDate': json.recordLiveDate,
|
||||
'syncDanmuForLive': json.syncDanmuForLive,
|
||||
'week_1':false,
|
||||
'week_2':false,
|
||||
'week_3':false,
|
||||
'week_4':false,
|
||||
'week_5':false,
|
||||
'week_6':false,
|
||||
'week_7':false,
|
||||
};
|
||||
|
||||
for (let i = 0; i < json.weeks.length; i++) {
|
||||
result[`week_${json.weeks[i]}`] = true;
|
||||
}
|
||||
form.val('form-filter',result);
|
||||
|
||||
if(json.keyword===undefined||json.keyword===null){
|
||||
json.keyword=[]
|
||||
}
|
||||
keywordList = json.keyword;
|
||||
inputTag.setData(json.keyword)
|
||||
form.val('form-filter', result);
|
||||
if (json.syncDanmuForLive) {
|
||||
$("[name='recordDanmu']").prop("disabled", true);
|
||||
} else {
|
||||
$("[name='recordDanmu']").prop("disabled", false);
|
||||
}
|
||||
$('#url').get(0).disabled = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
function editArrayRoom() {
|
||||
$('#btn_reset').get(0).disabled = true;
|
||||
$('#url').get(0).disabled = true;
|
||||
var result = {
|
||||
'url': editArray
|
||||
};
|
||||
form.val('form-filter', result);
|
||||
}
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
@@ -46,7 +46,7 @@
|
||||
elem: '#followList',
|
||||
url: '/live/config/follow?userId=' + userId,
|
||||
toolbar: '#toolbarDemo',
|
||||
height: '350px',
|
||||
height: 'full',
|
||||
totalRow: true, // 开启合计行
|
||||
page: true,
|
||||
response: {
|
||||
|
||||
132
Web/html/ui/userFollowLive.html
Normal file
132
Web/html/ui/userFollowLive.html
Normal file
@@ -0,0 +1,132 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="/layui/css/layui.css">
|
||||
</head>
|
||||
<table class="layui-hide" id="followList" lay-filter="followTable"></table>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
<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 type="text/html" id="toolbarDemo">
|
||||
<div class="layui-btn-container">
|
||||
<button class="layui-btn layui-btn-sm" lay-event="select">配置选择项</button>
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/html" id="toolDemo">
|
||||
<div class="layui-clear-space">
|
||||
<a class="layui-btn layui-btn-xs" lay-event="add">添加</a>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script>
|
||||
var userId = getParam("uid");
|
||||
|
||||
layui.use(function () {
|
||||
var laytpl = layui.laytpl;
|
||||
var table = layui.table;
|
||||
|
||||
|
||||
table.render({
|
||||
elem: '#followList',
|
||||
url: '/live/config/followLive?userId=' + userId,
|
||||
toolbar: '#toolbarDemo',
|
||||
height: 'full',
|
||||
totalRow: false, // 开启合计行
|
||||
page: false,
|
||||
response: {
|
||||
statusCode: 100 // 重新规定成功的状态码为 200,table 组件默认为 0
|
||||
},
|
||||
parseData: function (res) {
|
||||
return {
|
||||
"code": res.status, //解析接口状态
|
||||
"msg": res.message, //解析提示文本
|
||||
"data": res.data, //解析数据列表
|
||||
"count": res.count
|
||||
};
|
||||
},
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: 'uname', title: '用户名', width: 150, fixed: 'left' },
|
||||
{ field: 'uid', title: 'UID', width: 150, sort: true, templet: '<div><a href="https://space.bilibili.com/{{= d.uid}}" target="_blank">{{= d.uid}}</a>' },
|
||||
{ field: 'room_id', title: '房间号', width: 150, sort: true, templet: '<div><a href="https://live.bilibili.com/{{= d.room_id}}" target="_blank">{{= d.room_id}}</a>' },
|
||||
{ field: 'face', title: '头像', width: 80, templet: '<div><image src="" onerror="showImage(\'{{= d.face }}\',this);" style="width: 30px;height: 30px;"></div>' },
|
||||
{ field: 'title', title: '标题', width: 150, },
|
||||
{ fixed: "right", title: "操作", width: 50, align: "center", toolbar: "#toolDemo" }
|
||||
]],
|
||||
done: function () {
|
||||
onDone()
|
||||
},
|
||||
error: function (res, msg) {
|
||||
console.log(res, msg)
|
||||
}
|
||||
});
|
||||
function onDone() {
|
||||
table.on('toolbar(followTable)', function (obj) {
|
||||
if (obj.event === 'select') {
|
||||
var data = table.checkStatus(obj.config.id).data;
|
||||
console.log(data)
|
||||
var array = []
|
||||
data.forEach(item => {
|
||||
array.push({
|
||||
"roomId": item.room_id,
|
||||
"uname": item.uname
|
||||
})
|
||||
});
|
||||
var loadIndex = layer.msg('配置中', {
|
||||
icon: 16,
|
||||
shade: 0.6
|
||||
});
|
||||
addFollowRoomList(JSON.stringify(array))
|
||||
.then(json => {
|
||||
layer.close(loadIndex)
|
||||
layer.msg("已成功配置" + json.count + "个房间")
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
table.on('tool(followTable)', function (obj) {
|
||||
if (obj.event === 'add') {
|
||||
var loadIndex = layer.msg('配置中', {
|
||||
icon: 16,
|
||||
shade: 0.6
|
||||
});
|
||||
var array = []
|
||||
array.push({
|
||||
"roomId": obj.data.room_id,
|
||||
"uname": obj.data.uname
|
||||
})
|
||||
addFollowRoomList(JSON.stringify(array))
|
||||
.then(data => {
|
||||
layer.close(loadIndex)
|
||||
layer.msg("已成功配置" + data.count + "个房间")
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
function openTips(message, mid) {
|
||||
layer.alert(message, {
|
||||
title: "提示",
|
||||
btn: ['打开配置页', '前往UP主页', '取消'],
|
||||
btnAlign: 'c', // 按钮居中显示
|
||||
btn1: function () {
|
||||
window.open("/html/body/live.html?type=createRoom", '_blank')
|
||||
},
|
||||
btn2: function () {
|
||||
window.open("https://space.bilibili.com/" + mid, '_blank')
|
||||
},
|
||||
btn3: function () {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
</script>
|
||||
68
Web/html/ui/videoGiftItem.html
Normal file
68
Web/html/ui/videoGiftItem.html
Normal file
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="/layui/css/layui.css">
|
||||
</head>
|
||||
<table class="layui-hide" id="giftList" lay-filter="followTable"></table>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
<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>
|
||||
var roomId = getParam("roomId");
|
||||
var videoId = getParam("videoId");
|
||||
var giftName = getParam("giftName");
|
||||
|
||||
layui.use(function () {
|
||||
var laytpl = layui.laytpl;
|
||||
var table = layui.table;
|
||||
|
||||
|
||||
table.render({
|
||||
elem: '#giftList',
|
||||
url: '/live/gift/info/item?roomId=' + roomId + "&videoId=" + videoId + "&giftName=" + giftName,
|
||||
height: 'full',
|
||||
totalRow: false, // 开启合计行
|
||||
page: false,
|
||||
response: {
|
||||
statusCode: 100 // 重新规定成功的状态码为 200,table 组件默认为 0
|
||||
},
|
||||
parseData: function (res) {
|
||||
return {
|
||||
"code": res.status, //解析接口状态
|
||||
"msg": res.message, //解析提示文本
|
||||
"data": res.data, //解析数据列表
|
||||
"count": res.count
|
||||
};
|
||||
},
|
||||
cols: [[
|
||||
{ type: 'checkbox', fixed: 'left' },
|
||||
{ field: 'icon', title: '礼物', width: 80, templet: '<div><image src="" onerror="showImage(\'{{= d.icon }}\',this);" style="width: 30px;height: 30px;"></div>' },
|
||||
{ field: 'gift_id', title: '礼物ID', width: 80, sort: true },
|
||||
{ field: 'sender_name', title: '用户名', width: 150, sort: true, templet: '<div><a href="https://space.bilibili.com/{{= d.sender_uid}}" target="_blank">{{= d.sender_name}}</a>' },
|
||||
{ field: 'sender_face', title: '用户头像', width: 80, templet: '<div><image src="" onerror="showImage(\'{{= d.sender_face }}\',this);" style="width: 30px;height: 30px;"></div>' },
|
||||
{ field: 'gift_num', title: '数量', width: 80, sort: true },
|
||||
{ field: 'price', title: '价值(电池)', width: 80, sort: true },
|
||||
{ field: 'sql_time', title: '时间', width: 180 }
|
||||
]],
|
||||
done: function () {
|
||||
onDone()
|
||||
},
|
||||
error: function (res, msg) {
|
||||
console.log(res, msg)
|
||||
}
|
||||
});
|
||||
function onDone() {
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
</script>
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<body>
|
||||
<div id="header"></div>
|
||||
<div class="layui-row layui-col-space15">
|
||||
<div class="layui-row">
|
||||
<div id="follow">
|
||||
</div>
|
||||
<div id="card"></div>
|
||||
@@ -59,21 +59,24 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="layui-card-body">
|
||||
<img src="#" onerror="showTmpImage('{{= item.cover}}',this)" onclick="toLive('{{= item.roomId}}')" style="width: 100%; height: 100%; object-fit: cover;"/><br>
|
||||
<div style="overflow: hidden;position: relative; width: 100%; height: 100%; padding-bottom: 52%;">
|
||||
<img src="#" onerror="showTmpImage('{{= item.cover}}',this)" onclick="toLive('{{= item.roomId}}')" style="max-width: 100%; max-height: 100%; object-fit: cover; display: block; margin: auto; position: absolute; "/><br>
|
||||
</div>
|
||||
|
||||
<!-- <img src="#" onerror="showImage('{{= item.cover}}',this)" onclick="toLive('{{= item.roomId}}')" style="width: 100%; height: 100%;"/><br> -->
|
||||
{{= item.title}}<br>
|
||||
<span style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; ">{{= item.title}}</span> <br>
|
||||
<p>开播时长:{{= item.liveTime}}</p>
|
||||
直播录制状态:
|
||||
{{# if(item.downloadVideo){ }}
|
||||
<span style="color: #16b777" onclick="clickVideo('{{= item.roomId}}',true)">录制中</span>
|
||||
<span style="color: #16b777" onclick="clickVideo('{{= item.roomId}}',true,this)">{{= item.videoListen}}</span>
|
||||
{{# } else{ }}
|
||||
<span style="color: #FD482C" onclick="clickVideo('{{= item.roomId}}',false)">待机中</span>
|
||||
<span style="color: #FD482C" onclick="clickVideo('{{= item.roomId}}',false,this)">{{= item.videoListen}}</span>
|
||||
{{# }; }}<br>
|
||||
弹幕录制状态:
|
||||
{{# if(item.danmu){ }}
|
||||
<span style="color: #16b777" onclick="clickDanmu('{{= item.roomId}}',true)">录制中</span>
|
||||
<span style="color: #16b777" onclick="clickDanmu('{{= item.roomId}}',true,this)">{{= item.danmuListen}}</span>
|
||||
{{# } else{ }}
|
||||
<span style="color: #FD482C" onclick="clickDanmu('{{= item.roomId}}',false)">待机中</span>
|
||||
<span style="color: #FD482C" onclick="clickDanmu('{{= item.roomId}}',false,this)">{{= item.danmuListen}}</span>
|
||||
{{# }; }}<br>
|
||||
</div>
|
||||
</div>
|
||||
@@ -95,21 +98,21 @@
|
||||
console.log(roomId)
|
||||
window.open("https://live.bilibili.com/" + roomId, '_blank')
|
||||
}
|
||||
function clickVideo(roomId, status) {
|
||||
const title = "是否" + (status ? "停止" : "启动") + "录制视频?"
|
||||
function clickVideo(roomId, status, that) {
|
||||
const title = "是否" + (status ? "停止" : "启动") + "录制视频?<p style='color:red;'>手动干预后不再自动监听,第二天或重新配置可清除该状态</p>"
|
||||
layer.confirm(title, { icon: 3 }, function () {
|
||||
if (status) {
|
||||
stopLiveVideo(roomId)
|
||||
.then(data => {
|
||||
layer.msg(data.message, { icon: (data.status == 100 ? 1 : 0) }, function () {
|
||||
location.reload();
|
||||
//location.reload();
|
||||
});
|
||||
})
|
||||
} else {
|
||||
startLiveVideo(roomId)
|
||||
.then(data => {
|
||||
layer.msg(data.message, { icon: (data.status == 100 ? 1 : 0) }, function () {
|
||||
location.reload();
|
||||
//location.reload();
|
||||
});
|
||||
})
|
||||
}
|
||||
@@ -119,13 +122,13 @@
|
||||
});
|
||||
}
|
||||
function clickDanmu(roomId, status) {
|
||||
const title = "是否" + (status ? "停止" : "启动") + "录制弹幕?"
|
||||
const title = "是否" + (status ? "停止" : "启动") + "录制弹幕?<p style='color:red;'>手动干预后不再自动监听,第二天或重新配置可清除该状态</p>"
|
||||
layer.confirm(title, { icon: 3 }, function () {
|
||||
if (status) {
|
||||
stopLiveDanmu(roomId)
|
||||
.then(data => {
|
||||
layer.msg(data.message, { icon: (data.status == 100 ? 1 : 0) }, function () {
|
||||
location.reload();
|
||||
// location.reload();
|
||||
});
|
||||
|
||||
})
|
||||
@@ -133,12 +136,12 @@
|
||||
startLiveDanmu(roomId)
|
||||
.then(data => {
|
||||
layer.msg(data.message, { icon: (data.status == 100 ? 1 : 0) }, function () {
|
||||
location.reload();
|
||||
// location.reload();
|
||||
});
|
||||
})
|
||||
}
|
||||
}, function () {
|
||||
layer.msg('点击取消的回调');
|
||||
|
||||
});
|
||||
}
|
||||
function confirmFollow(userId) {
|
||||
@@ -195,7 +198,7 @@
|
||||
// if(document.getElementById(item.data)!==null){
|
||||
// document.getElementById(item.data).innerHTML=item.message;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// })
|
||||
initFollowStatus(true)
|
||||
|
||||
@@ -1,71 +1,86 @@
|
||||
function get(url){
|
||||
return fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
return data;
|
||||
})
|
||||
.catch(error => {
|
||||
return error;
|
||||
});
|
||||
}
|
||||
function post(url,formData,isJSON) {
|
||||
return fetch(url, {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
if(isJSON){
|
||||
function get(url) {
|
||||
return fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
return data;
|
||||
})
|
||||
.catch(error => {
|
||||
return error;
|
||||
});
|
||||
}
|
||||
function post(url, formData, isJSON) {
|
||||
return sendPost(url, true, formData, isJSON)
|
||||
}
|
||||
function sendPost(url, isFormData, formData, isJSON) {
|
||||
var obj = {};
|
||||
if (isFormData) {
|
||||
obj = {
|
||||
method: "POST",
|
||||
body: formData
|
||||
}
|
||||
return response.blob();
|
||||
})
|
||||
.then(blob => {
|
||||
if(isJSON){
|
||||
return blob;
|
||||
} else {
|
||||
obj = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(formData)
|
||||
}
|
||||
const imageUrl = URL.createObjectURL(blob);
|
||||
return imageUrl;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
throw error; // 重新抛出错误以便外部捕获
|
||||
});
|
||||
}
|
||||
return fetch(url, obj)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
if (isJSON) {
|
||||
return response.json();
|
||||
}
|
||||
return response.blob();
|
||||
})
|
||||
.then(blob => {
|
||||
if (isJSON) {
|
||||
return blob;
|
||||
}
|
||||
const imageUrl = URL.createObjectURL(blob);
|
||||
return imageUrl;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
throw error; // 重新抛出错误以便外部捕获
|
||||
});
|
||||
}
|
||||
|
||||
function post2blob(url,formData) {
|
||||
function post2blob(url, formData) {
|
||||
return fetch(url, {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then(blob => {
|
||||
return blob;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
throw error; // 重新抛出错误以便外部捕获
|
||||
});
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then(blob => {
|
||||
return blob;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
throw error; // 重新抛出错误以便外部捕获
|
||||
});
|
||||
}
|
||||
|
||||
function buildFormData(json){
|
||||
function buildFormData(json) {
|
||||
const formData = new FormData();
|
||||
|
||||
Object.keys(json).forEach(key => {
|
||||
const value = json[key];
|
||||
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((item, index) => {
|
||||
formData.append(`${key}[${index}]`, item);
|
||||
@@ -81,101 +96,119 @@ function buildFormData(json){
|
||||
|
||||
//----------------直播配置相关接口
|
||||
function getLiveVideoList() {
|
||||
return get("/live/video/list");
|
||||
return get("/live/video/list");
|
||||
}
|
||||
function getHttpImage(url) {
|
||||
const encode = encodeURI(url);
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("url", encode);
|
||||
return post("/file/img",formData,false)
|
||||
.then(blob=>{
|
||||
return blob;
|
||||
})
|
||||
return post("/file/img", formData, false)
|
||||
.then(blob => {
|
||||
return blob;
|
||||
})
|
||||
}
|
||||
function getHttpTmpImage(url) {
|
||||
const encode = encodeURI(url);
|
||||
const formData = new FormData();
|
||||
formData.append("url", encode);
|
||||
return post2blob("/file/imgTmp",formData)
|
||||
.then(blob=>{
|
||||
return blob;
|
||||
})
|
||||
return post2blob("/file/imgTmp", formData)
|
||||
.then(blob => {
|
||||
return blob;
|
||||
})
|
||||
}
|
||||
function addRoomConfig(json){
|
||||
const formData=buildFormData(json)
|
||||
return post("/live/config/set",formData,true)
|
||||
function addRoomConfig(config) {
|
||||
const formData = buildFormData(config)
|
||||
return post("/live/config/set", formData, true)
|
||||
}
|
||||
function getRoomConfig(roomId){
|
||||
return get("/live/config/get?roomId="+roomId)
|
||||
function setArrayRoomConfig(rooms, config) {
|
||||
var json = {
|
||||
"config": config,
|
||||
"array": rooms
|
||||
}
|
||||
return sendPost("/live/config/set/array", false, json, true)
|
||||
}
|
||||
function deleteRoomConfig(roomId){
|
||||
return get("/live/config/delete?roomId="+roomId)
|
||||
function deleteArrayRoomConfig(rooms) {
|
||||
return sendPost("/live/config/delete/array", false, rooms, true)
|
||||
}
|
||||
function checkFollowStatus(userId){
|
||||
return get("/live/config/follow/check?userId="+userId)
|
||||
function deleteAllRoomConfig() {
|
||||
return get("/live/config/delete/all")
|
||||
}
|
||||
function confirmFollowStatus(userId){
|
||||
return get("/live/config/follow/confirm?userId="+userId)
|
||||
function getRoomConfig(roomId) {
|
||||
return get("/live/config/get?roomId=" + roomId)
|
||||
}
|
||||
function addAllFollow(userId){
|
||||
return get("/live/config/follow/all?userId="+userId)
|
||||
function deleteRoomConfig(roomId) {
|
||||
return get("/live/config/delete?roomId=" + roomId)
|
||||
}
|
||||
function addFollow(uid,anchorId){
|
||||
return get("/live/config/follow/add?anchorId="+anchorId+"&uid="+uid)
|
||||
function checkFollowStatus(userId) {
|
||||
return get("/live/config/follow/check?userId=" + userId)
|
||||
}
|
||||
function addFollowList(uid,array){
|
||||
function confirmFollowStatus(userId) {
|
||||
return get("/live/config/follow/confirm?userId=" + userId)
|
||||
}
|
||||
function addAllFollow(userId) {
|
||||
return get("/live/config/follow/all?userId=" + userId)
|
||||
}
|
||||
function addFollow(uid, anchorId) {
|
||||
return get("/live/config/follow/add?anchorId=" + anchorId + "&uid=" + uid)
|
||||
}
|
||||
function addFollowList(uid, array) {
|
||||
const formData = new FormData();
|
||||
formData.append("array", array);
|
||||
return post("/live/config/follow/addList?uid="+uid,formData,true)
|
||||
return post("/live/config/follow/addList?uid=" + uid, formData, true)
|
||||
}
|
||||
function addFollowRoomList(array) {
|
||||
const formData = new FormData();
|
||||
formData.append("array", array);
|
||||
return post("/live/config/follow/roomId/addList", formData, true)
|
||||
}
|
||||
//----------------直播配置相关接口end
|
||||
//----------------首页相关接口
|
||||
function getAllLive(page,limit){
|
||||
return get("/live/list?page="+page+"&limit="+limit)
|
||||
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)
|
||||
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 startLiveVideo(roomId) {
|
||||
return get("/live/video/start?roomId=" + roomId)
|
||||
}
|
||||
function stopLiveVideo(roomId){
|
||||
return get("/live/video/stop?roomId="+roomId)
|
||||
function stopLiveVideo(roomId) {
|
||||
return get("/live/video/stop?roomId=" + roomId)
|
||||
}
|
||||
function getVideo(roomId,page,limit){
|
||||
return get("/file/list?roomId="+roomId+"&page="+page+"&limit="+limit)
|
||||
function getVideo(roomId, page, limit) {
|
||||
return get("/file/list?roomId=" + roomId + "&page=" + page + "&limit=" + limit)
|
||||
}
|
||||
function getPlayerVideo(roomId,videoId){
|
||||
return get('/video/play?roomId='+roomId+"&videoId="+videoId)
|
||||
function getPlayerVideo(roomId, videoId) {
|
||||
return get('/video/play?roomId=' + roomId + "&videoId=" + videoId)
|
||||
}
|
||||
//----------------直播视频相关接口end
|
||||
//----------------弹幕相关接口
|
||||
function startLiveDanmu(roomId){
|
||||
return get("/live/danmu/start?roomId="+roomId)
|
||||
function startLiveDanmu(roomId) {
|
||||
return get("/live/danmu/start?roomId=" + roomId)
|
||||
}
|
||||
function stopLiveDanmu(roomId){
|
||||
return get("/live/danmu/stop?roomId="+roomId)
|
||||
function stopLiveDanmu(roomId) {
|
||||
return get("/live/danmu/stop?roomId=" + roomId)
|
||||
}
|
||||
function getDanmu(roomId,videoId){
|
||||
return get("/live/danmu/get?roomId="+roomId+"&videoId="+videoId)
|
||||
function getDanmu(roomId, videoId, page) {
|
||||
return get("/live/danmu/get?roomId=" + roomId + "&videoId=" + videoId + "&page=" + page)
|
||||
}
|
||||
//----------------弹幕相关接口end
|
||||
//----------------用户相关接口
|
||||
function getBiliAllUser(){
|
||||
function getBiliAllUser() {
|
||||
return get("/user/list")
|
||||
}
|
||||
function login(){
|
||||
function login() {
|
||||
return get("/user/login")
|
||||
}
|
||||
function refreshCookie(userId){
|
||||
return get("/user/refreshCookie?uid="+userId)
|
||||
function refreshCookie(userId) {
|
||||
return get("/user/refreshCookie?uid=" + userId)
|
||||
}
|
||||
//----------------用户相关接口end
|
||||
//----------------礼物相关接口
|
||||
function getVideoGiftInfo(roomId,videoId){
|
||||
return get("/live/gift/info?roomId="+roomId+"&videoId="+videoId)
|
||||
function getVideoGiftInfo(roomId, videoId) {
|
||||
return get("/live/gift/info?roomId=" + roomId + "&videoId=" + videoId)
|
||||
}
|
||||
//----------------礼物相关接口end
|
||||
170
Web/js/inputTag.js
Normal file
170
Web/js/inputTag.js
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Name: inputTag
|
||||
* Author: cshaptx4869
|
||||
* Project: https://github.com/cshaptx4869/inputTag
|
||||
*/
|
||||
(function (define) {
|
||||
define(['jquery'], function ($) {
|
||||
"use strict";
|
||||
|
||||
class InputTag {
|
||||
|
||||
options = {
|
||||
elem: '.fairy-tag-input',
|
||||
theme: ['fairy-bg-red', 'fairy-bg-orange', 'fairy-bg-green', 'fairy-bg-cyan', 'fairy-bg-blue', 'fairy-bg-black'],
|
||||
data: [],
|
||||
removeKeyNum: 8,
|
||||
createKeyNum: 13,
|
||||
permanentData: [],
|
||||
sortable: false
|
||||
};
|
||||
get elem() {
|
||||
return $(this.options.elem);
|
||||
}
|
||||
|
||||
get copyData() {
|
||||
return [...this.options.data];
|
||||
}
|
||||
|
||||
constructor(options) {
|
||||
this.render(options);
|
||||
}
|
||||
|
||||
render(options) {
|
||||
this.init(options);
|
||||
this.listen();
|
||||
}
|
||||
setData(data){
|
||||
this.options.data=data;
|
||||
this.init(this.options)
|
||||
}
|
||||
|
||||
init(options) {
|
||||
var spans = '', that = this;
|
||||
this.options = $.extend(this.options, options);
|
||||
!this.elem.attr('placeholder') && this.elem.attr('placeholder', '添加标签');
|
||||
$.each(this.options.data, function (index, item) {
|
||||
spans += that.spanHtml(item);
|
||||
});
|
||||
this.elem.before(spans);
|
||||
}
|
||||
|
||||
listen() {
|
||||
var that = this;
|
||||
|
||||
this.elem.parent().on('click', 'a', function () {
|
||||
that.removeItem($(this).parent('span'));
|
||||
});
|
||||
|
||||
this.elem.parent().on('click', function () {
|
||||
that.elem.focus();
|
||||
});
|
||||
|
||||
this.elem.keydown(function (event) {
|
||||
var keyNum = (event.keyCode ? event.keyCode : event.which);
|
||||
if (keyNum === that.options.removeKeyNum) {
|
||||
if (!that.elem.val().trim()) {
|
||||
var closeItems = that.elem.parent().find('a');
|
||||
if (closeItems.length) {
|
||||
that.removeItem($(closeItems[closeItems.length - 1]).parent('span'));
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
} else if (keyNum === that.options.createKeyNum) {
|
||||
that.createItem();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
this.elem.blur(function () {
|
||||
that.createItem();
|
||||
});
|
||||
if (this.options.sortable) {
|
||||
Sortable.create(this.elem.parent()[0], {
|
||||
handle: 'span',
|
||||
onSort: function (event) {
|
||||
that.options.data = that.elem.parent()
|
||||
.find('span.fairy-tag>span')
|
||||
.map(function (index, item) {
|
||||
return $(item).text();
|
||||
}).toArray();
|
||||
|
||||
that.onChange($(event.item).children('span').text(), 'sort');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
createItem() {
|
||||
var value = this.elem.val().trim();
|
||||
|
||||
if (this.options.beforeCreate && typeof this.options.beforeCreate === 'function') {
|
||||
var modifiedValue = this.options.beforeCreate(this.copyData, value);
|
||||
if (typeof modifiedValue == 'string' && modifiedValue) {
|
||||
value = modifiedValue;
|
||||
} else {
|
||||
value = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (!this.options.data.includes(value)) {
|
||||
this.options.data.push(value);
|
||||
this.elem.before(this.spanHtml(value));
|
||||
this.onChange(value, 'create');
|
||||
}
|
||||
}
|
||||
|
||||
this.elem.val('');
|
||||
}
|
||||
|
||||
removeItem(target) {
|
||||
var that = this;
|
||||
var closeSpan = target.remove(),
|
||||
closeSpanText = $(closeSpan).children('span').text();
|
||||
var value = that.options.data.splice($.inArray(closeSpanText, that.options.data), 1);
|
||||
value.length === 1 && that.onChange(value[0], 'remove');
|
||||
}
|
||||
|
||||
randomColor() {
|
||||
return this.options.theme[Math.floor(Math.random() * this.options.theme.length)];
|
||||
}
|
||||
|
||||
spanHtml(value) {
|
||||
return '<span class="fairy-tag fairy-anim-fadein ' + this.randomColor() + '">' +
|
||||
'<span>' + value + '</span>' +
|
||||
(this.options.permanentData.includes(value) ? '' : '<a href="#" title="删除标签">×</a>') +
|
||||
'</span>';
|
||||
}
|
||||
|
||||
onChange(value, type) {
|
||||
this.options.onChange && typeof this.options.onChange === 'function' && this.options.onChange(this.copyData, value, type);
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.copyData;
|
||||
}
|
||||
|
||||
clearData() {
|
||||
this.options.data = [];
|
||||
this.elem.prevAll('span.fairy-tag').remove();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
render(options) {
|
||||
return new InputTag(options);
|
||||
}
|
||||
}
|
||||
});
|
||||
}(typeof define === 'function' && define.amd ? define : function (deps, factory) {
|
||||
var MOD_NAME = 'inputTag';
|
||||
if (typeof module !== 'undefined' && module.exports) { //Node
|
||||
module.exports = factory(require('jquery'));
|
||||
} else if (window.layui && layui.define) {
|
||||
layui.define('jquery', function (exports) { //layui加载
|
||||
exports(MOD_NAME, factory(layui.jquery));
|
||||
});
|
||||
} else {
|
||||
window[MOD_NAME] = factory(window.jQuery);
|
||||
}
|
||||
}));
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
15
pom.xml
15
pom.xml
@@ -144,6 +144,21 @@
|
||||
<artifactId>selenium-java</artifactId>
|
||||
<version>4.26.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
package com.yutou;
|
||||
|
||||
import com.yutou.common.okhttp.HttpLoggingInterceptor;
|
||||
import com.yutou.common.utils.ConfigTools;
|
||||
import com.yutou.common.utils.Log;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication()
|
||||
public class BilibiliApplication {
|
||||
|
||||
public static String version = "0.8.4";
|
||||
public static String version = "0.10";
|
||||
|
||||
public static void main(String[] args) {
|
||||
HttpLoggingInterceptor.setLog(false);
|
||||
Log.i("启动版本", version);
|
||||
HttpLoggingInterceptor.setLog(ConfigTools.load(ConfigTools.CONFIG, "HttpLog", Boolean.class, false));
|
||||
SpringApplication.run(BilibiliApplication.class, args);
|
||||
}
|
||||
//TODO 优化 1 手动中断录制后,没有时间戳问题需要解决,看是提示转码还是有什么其他解决方案 | 改成了通过q来退出
|
||||
//TODO 优化 2 创建nfo文件看看要不要改成录制后生成,或者触发打包指令再生成
|
||||
//TODO 优化 3 录制完成前应该也允许查看礼物信息和SC以及弹幕,未停止录制改成从开始时间到当前时间的弹幕和礼物信息,已停止录制则询问是否转码
|
||||
//TODO 优化 4 视频页面礼物的图标,有数据计算错误以及单项猛增的话导致其他项目无法查看的问题(可参考泛式死亡笔记录播) | 换成饼状图
|
||||
//TODO 修复 1 开播时有概率连续触发创建nfo和记录视频到数据宽度问题.
|
||||
//TODO 测试 1 需要测试网络中断下,弹幕重连机制
|
||||
//TODO 测试 2 在导出jar包后再测试完整的录制功能
|
||||
//TODO 测试 3 需要确认项目的内存占用情况
|
||||
//TODO 开发 1 完成用户列表的所有功能,包括移出用户和筛选ck和登陆扫码二维码的优化
|
||||
//TODO 优化 2 创建nfo文件看看要不要改成录制后生成,或者触发打包指令再生成|改了
|
||||
//TODO 优化 3 录制完成前应该也允许查看礼物信息和SC以及弹幕,未停止录制改成从开始时间到当前时间的弹幕和礼物信息,已停止录制则询问是否转码|改了
|
||||
//TODO 优化 4 视频页面礼物的图标,有数据计算错误以及单项猛增的话导致其他项目无法查看的问题(可参考泛式死亡笔记录播) | 换成饼状图 | 改了
|
||||
//TODO 修复 1 开播时有概率连续触发创建nfo和记录视频到数据宽度问题. | 改了
|
||||
//TODO 测试 1 需要测试网络中断下,弹幕重连机制|好像没问题
|
||||
//TODO 测试 2 在导出jar包后再测试完整的录制功能|没问题
|
||||
//TODO 测试 3 需要确认项目的内存占用情况|好像还行
|
||||
//TODO 开发 1 完成用户列表的所有功能,包括移出用户和筛选ck和登陆扫码二维码的优化|改了
|
||||
//TODO 开发 2 数据中心
|
||||
//TODO 开发 3 视频页面打包导出视频和弹幕
|
||||
//TODO 开发 4 弹幕随开播时间录制
|
||||
//TODO 开发 5 追加关键词检测
|
||||
//TODO 开发 4 弹幕随开播时间录制|有了
|
||||
//TODO 开发 5 追加关键词检测|加了
|
||||
}
|
||||
|
||||
@@ -66,5 +66,9 @@ public interface LiveApi {
|
||||
@POST("/room/v1/Room/get_status_info_by_uids")
|
||||
Call<HttpBody<Map<String,LiveAnchorInfo>>> getLiveRoomStatus(@Body
|
||||
JSONObject uids);
|
||||
|
||||
@GET("/xlive/web-ucenter/v1/xfetter/GetWebList")
|
||||
Call<HttpBody<FollowLive>> getUserFollowLive(
|
||||
@Query("hit_ab")boolean hit_ab,
|
||||
@Query("_")long time
|
||||
);
|
||||
}
|
||||
|
||||
107
src/main/java/com/yutou/biliapi/bean/live/FollowLive.java
Normal file
107
src/main/java/com/yutou/biliapi/bean/live/FollowLive.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package com.yutou.biliapi.bean.live;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.yutou.common.okhttp.BaseBean;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class FollowLive extends BaseBean {
|
||||
|
||||
@JSONField(name = "rooms")
|
||||
List<Room> rooms;
|
||||
@JSONField(name = "list")
|
||||
List<Room> list;
|
||||
|
||||
@lombok.Data
|
||||
public static class Room {
|
||||
@JSONField(name = "title")
|
||||
private String title;
|
||||
|
||||
@JSONField(name = "room_id")
|
||||
private String roomId;
|
||||
|
||||
@JSONField(name = "uid")
|
||||
private String uid;
|
||||
|
||||
@JSONField(name = "online")
|
||||
private int online;
|
||||
|
||||
@JSONField(name = "live_time")
|
||||
private String liveTime;
|
||||
|
||||
@JSONField(name = "live_status")
|
||||
private int liveStatus;
|
||||
|
||||
@JSONField(name = "short_id")
|
||||
private int shortId;
|
||||
|
||||
@JSONField(name = "area")
|
||||
private int area;
|
||||
|
||||
@JSONField(name = "area_name")
|
||||
private String areaName;
|
||||
|
||||
@JSONField(name = "area_v2_id")
|
||||
private int areaV2Id;
|
||||
|
||||
@JSONField(name = "area_v2_name")
|
||||
private String areaV2Name;
|
||||
|
||||
@JSONField(name = "area_v2_parent_name")
|
||||
private String areaV2ParentName;
|
||||
|
||||
@JSONField(name = "area_v2_parent_id")
|
||||
private int areaV2ParentId;
|
||||
|
||||
@JSONField(name = "uname")
|
||||
private String uname;
|
||||
|
||||
@JSONField(name = "face")
|
||||
private String face;
|
||||
|
||||
@JSONField(name = "tag_name")
|
||||
private String tagName;
|
||||
|
||||
@JSONField(name = "tags")
|
||||
private String tags;
|
||||
|
||||
@JSONField(name = "cover_from_user")
|
||||
private String coverFromUser;
|
||||
|
||||
@JSONField(name = "keyframe")
|
||||
private String keyframe;
|
||||
|
||||
@JSONField(name = "lock_till")
|
||||
private String lockTill;
|
||||
|
||||
@JSONField(name = "hidden_till")
|
||||
private String hiddenTill;
|
||||
|
||||
@JSONField(name = "broadcast_type")
|
||||
private int broadcastType;
|
||||
|
||||
@JSONField(name = "is_encrypt")
|
||||
private boolean isEncrypt;
|
||||
|
||||
@JSONField(name = "link")
|
||||
private String link;
|
||||
|
||||
@JSONField(name = "nickname")
|
||||
private String nickname;
|
||||
|
||||
@JSONField(name = "roomname")
|
||||
private String roomName;
|
||||
|
||||
@JSONField(name = "roomid")
|
||||
private String roomId2;
|
||||
|
||||
@JSONField(name = "liveTime")
|
||||
private long liveTimeLong;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -14,13 +14,13 @@ public class LiveRoomConfig {
|
||||
String roomId;
|
||||
String anchorName;
|
||||
boolean isLogin;
|
||||
String rootPath="live";
|
||||
String rootPath = "live";
|
||||
LiveDanmuInfo liveInfo;
|
||||
LiveRoomInfo roomInfo;
|
||||
|
||||
public String getLoginUid() {
|
||||
if("null".equals(loginUid)){
|
||||
loginUid=null;
|
||||
if ("null".equals(loginUid)) {
|
||||
loginUid = null;
|
||||
}
|
||||
return loginUid;
|
||||
}
|
||||
@@ -42,9 +42,11 @@ public class LiveRoomConfig {
|
||||
return Objects.hashCode(roomId);
|
||||
}
|
||||
|
||||
public static LiveRoomConfig buildConfig(String roomId){
|
||||
|
||||
@Deprecated
|
||||
public static LiveRoomConfig buildConfigTmp(String roomId) {
|
||||
BiliLiveConfigDatabase database = new BiliLiveConfigDatabase();
|
||||
LiveConfigDatabaseBean bean = database.getConfig(new String(roomId));
|
||||
LiveConfigDatabaseBean bean = database.getConfig(roomId);
|
||||
database.close();
|
||||
LiveRoomConfig config = new LiveRoomConfig();
|
||||
config.setLoginUid(bean.getRecordUid());
|
||||
|
||||
@@ -34,10 +34,12 @@ public class LiveConfigDatabaseBean extends AbsDatabasesBean {
|
||||
private boolean isRecordLive;
|
||||
@JSONField(name = "recordDanmu")
|
||||
private boolean isRecordDanmu;
|
||||
@JSONField(name = "syncDanmuForLive")
|
||||
private boolean isSyncDanmuForLive;
|
||||
@JSONField(name = "keyword")
|
||||
private List<String> keywordList;
|
||||
private List<String> keywordList=new ArrayList<>();
|
||||
@JSONField(name = "weeks")
|
||||
private List<String> weeks=Arrays.asList("1","2","3","4","5","6","7");
|
||||
private List<String> weeks=new ArrayList<>();
|
||||
@JSONField(name = "recordPath")
|
||||
private String recordPath = "live";
|
||||
@JSONField(name = "recordUid")
|
||||
|
||||
@@ -8,6 +8,7 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@@ -23,7 +24,7 @@ public class LiveDanmuDatabaseBean extends AbsDatabasesBean {
|
||||
@JSONField(name = "color")
|
||||
private String fontColor;
|
||||
@JSONField(name = "time")
|
||||
private long time;
|
||||
private Date time;
|
||||
@JSONField(name = "uid")
|
||||
private String uid;
|
||||
@JSONField(name = "uname")
|
||||
@@ -40,7 +41,7 @@ public class LiveDanmuDatabaseBean extends AbsDatabasesBean {
|
||||
model = danmu.getModel();
|
||||
fontSize = danmu.getFontSize();
|
||||
fontColor = danmu.getFontColor();
|
||||
time = danmu.getTime();
|
||||
time = new Date(danmu.getTime());
|
||||
uid = danmu.getUid();
|
||||
uname = danmu.getUname();
|
||||
}
|
||||
@@ -52,7 +53,7 @@ public class LiveDanmuDatabaseBean extends AbsDatabasesBean {
|
||||
data.setModel(model);
|
||||
data.setFontSize(fontSize);
|
||||
data.setFontColor(fontColor);
|
||||
data.setTime(time);
|
||||
data.setTime(time.getTime());
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public class LiveGiftDatabaseBean extends AbsDatabasesBean {
|
||||
@JSONField(name = "id")
|
||||
int id;
|
||||
@JSONField(name = "gift_id")
|
||||
private int giftId;
|
||||
private long giftId;
|
||||
@JSONField(name = "gift_name")
|
||||
private String giftName;
|
||||
@JSONField(name = "price")
|
||||
@@ -22,9 +22,9 @@ public class LiveGiftDatabaseBean extends AbsDatabasesBean {
|
||||
@JSONField(name = "icon")
|
||||
private String icon;
|
||||
@JSONField(name = "gift_num")
|
||||
private int giftNum;
|
||||
private long giftNum;
|
||||
@JSONField(name = "sender_uid")
|
||||
private long senderUid;
|
||||
private String senderUid;
|
||||
@JSONField(name = "sender_name")
|
||||
private String senderName;
|
||||
@JSONField(name = "sender_face")
|
||||
|
||||
@@ -14,6 +14,8 @@ public class LiveVideoDatabaseBean extends AbsDatabasesBean {
|
||||
String roomInfoJson;
|
||||
@JSONField(name = "start_time")
|
||||
Date startTime;
|
||||
@JSONField(name = "stop_time")
|
||||
Date stopTime;
|
||||
@JSONField(name = "path")
|
||||
String path;
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ public class WSSendGift extends WSData {
|
||||
@JSONField(name = "base")
|
||||
private Base base;
|
||||
@JSONField(name = "uid")
|
||||
private long uid;
|
||||
private String uid;
|
||||
}
|
||||
|
||||
@lombok.Data
|
||||
|
||||
@@ -2,27 +2,26 @@ package com.yutou.biliapi.databases;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.util.DateUtils;
|
||||
import com.yutou.biliapi.bean.live.*;
|
||||
import com.yutou.biliapi.bean.live.database.*;
|
||||
import com.yutou.biliapi.bean.websocket.live.*;
|
||||
import com.yutou.bilibili.Tools.DateFormatUtils;
|
||||
import com.yutou.bilibili.datas.web.LiveWebDanmuBean;
|
||||
import com.yutou.common.databases.AbsDatabasesBean;
|
||||
import com.yutou.common.databases.SQLiteManager;
|
||||
import com.yutou.common.okhttp.HttpDownloadUtils;
|
||||
import com.yutou.common.utils.Log;
|
||||
import lombok.Getter;
|
||||
import org.apache.poi.ss.usermodel.DataFormat;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static com.alibaba.fastjson2.util.DateUtils.DateTimeFormatPattern.DATE_FORMAT_10_DASH;
|
||||
|
||||
public class BiliLiveDatabase extends SQLiteManager {
|
||||
@Getter
|
||||
LiveRoomConfig config;
|
||||
String fileName;
|
||||
File rootPath;
|
||||
@@ -111,15 +110,13 @@ public class BiliLiveDatabase extends SQLiteManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void addSource(WSData bean) {
|
||||
public synchronized void addSource(WSData bean) {
|
||||
// Log.i("BiliLiveDatabase.addSource", config.getRoomId());
|
||||
add(new LiveSourceDatabaseBean(bean));
|
||||
addData(bean);
|
||||
}
|
||||
|
||||
public JSONObject getGiftInfo(long startTimeLong, long endTimeLong) {
|
||||
String startTime = DateFormatUtils.getInstance().format(startTimeLong);
|
||||
String endTime = DateFormatUtils.getInstance().format(endTimeLong);
|
||||
String giftSql = "WITH filtered_gifts AS (" +
|
||||
"SELECT " +
|
||||
"`gift_name`," +
|
||||
@@ -127,13 +124,13 @@ public class BiliLiveDatabase extends SQLiteManager {
|
||||
"SUM(`gift_num`) AS `total_gift_num`," +
|
||||
"CASE " +
|
||||
"WHEN `coin_type` = 'silver' THEN 0 " +
|
||||
"ELSE SUM(`price` * `gift_num`) " +
|
||||
"ELSE SUM(`price` / 1000 * `gift_num`) " +
|
||||
"END AS `total_price`" +
|
||||
"FROM " +
|
||||
"`gift` " +
|
||||
"WHERE " +
|
||||
"`sql_time` >= '" + startTime + "' " +
|
||||
"AND `sql_time` <= '" + endTime + "' " +
|
||||
"`sql_time` >= '" + startTimeLong + "' " +
|
||||
"AND `sql_time` <= '" + endTimeLong + "' " +
|
||||
"GROUP BY " +
|
||||
"`gift_name`, `coin_type`" +
|
||||
")" +
|
||||
@@ -146,11 +143,11 @@ public class BiliLiveDatabase extends SQLiteManager {
|
||||
"filtered_gifts " +
|
||||
"GROUP BY " +
|
||||
"`gift_name`, `icon`;";
|
||||
String guardSql = "SELECT `gift_name`, SUM(`num`) AS `total_num`,SUM(`price`*`num`) as `total_price`" +
|
||||
"FROM `guardBuy` where `sql_time` >= '" + startTime + "' and `sql_time` <= '" + endTime + "'" +
|
||||
String guardSql = "SELECT `gift_name`, SUM(`num`) AS `total_num`,SUM(`price` / 1000 *`num`) as `total_price`" +
|
||||
"FROM `guardBuy` where `sql_time` >= '" + startTimeLong + "' and `sql_time` <= '" + endTimeLong + "'" +
|
||||
"GROUP BY `gift_name`;";
|
||||
String superChatSql = "SELECT SUM(`price`*100) as `total_price`, count(`price`) as `total_count`" +
|
||||
"FROM `superChat` where `sql_time` >= '" + startTime + "' and `sql_time` <= '" + endTime + "';";
|
||||
String superChatSql = "SELECT SUM(`price`/ 1000) as `total_price`, count(`price`) as `total_count`" +
|
||||
"FROM `superChat` where `sql_time` >= '" + startTimeLong + "' and `sql_time` <= '" + endTimeLong + "';";
|
||||
JSONObject json = new JSONObject();
|
||||
JSONArray giftInfo = get(giftSql);
|
||||
JSONArray guardInfo = get(guardSql);
|
||||
@@ -177,15 +174,14 @@ public class BiliLiveDatabase extends SQLiteManager {
|
||||
}
|
||||
|
||||
private void createInfo(LiveVideoDatabaseBean bean) {
|
||||
String format = DateFormatUtils.getInstance().format(bean.getSql_time());
|
||||
if (get(bean.getTableName(), " `sql_time` = '" + format + "'", LiveVideoDatabaseBean.class).isEmpty()) {
|
||||
if (get(bean.getTableName(), " `sql_time` = '" + bean.getSql_time().getTime() + "'", LiveVideoDatabaseBean.class).isEmpty()) {
|
||||
add(bean);
|
||||
} else {
|
||||
update(bean);
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends AbsDatabasesBean> List<T> getOfTime(String startTime, String endTime, Class<T> clazz) {
|
||||
public <T extends AbsDatabasesBean> List<T> getOfTime(Long startTime, Long endTime, Class<T> clazz) {
|
||||
String tableName = null;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String where = null;
|
||||
@@ -210,9 +206,64 @@ public class BiliLiveDatabase extends SQLiteManager {
|
||||
return super.get(tableName, where, clazz);
|
||||
}
|
||||
|
||||
|
||||
public List<LiveWebDanmuBean.Danmu> getDanmu(Long startTime, Long endTime, int page, int pageSize) {
|
||||
List<LiveWebDanmuBean.Danmu> list = new ArrayList<>();
|
||||
|
||||
String query = "SELECT danmu AS text, fontSize, color, (sql_time - ?) AS `time`, "
|
||||
+ "CASE WHEN model < 4 THEN 'scroll' "
|
||||
+ " WHEN model = 4 THEN 'bottom' "
|
||||
+ " WHEN model = 5 THEN 'top' "
|
||||
+ "END AS model "
|
||||
+ "FROM danmu "
|
||||
+ "WHERE sql_time BETWEEN ? AND ? "
|
||||
+ "ORDER BY sql_time "
|
||||
+ "LIMIT ?, ?";
|
||||
try (PreparedStatement pstmt = conn.prepareStatement(query)) {
|
||||
|
||||
// 设置参数
|
||||
pstmt.setLong(1, startTime);
|
||||
pstmt.setLong(2, startTime);
|
||||
pstmt.setLong(3, endTime);
|
||||
pstmt.setInt(4, (page - 1) * pageSize); // 计算偏移量
|
||||
pstmt.setInt(5, pageSize);
|
||||
|
||||
pstmt.closeOnCompletion();
|
||||
ResultSet rs = pstmt.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
String text = rs.getString("text");
|
||||
int fontSize = rs.getInt("fontSize");
|
||||
String color = rs.getString("color");
|
||||
long time = rs.getLong("time");
|
||||
String model = rs.getString("model");
|
||||
LiveWebDanmuBean.Danmu danmu = new LiveWebDanmuBean.Danmu();
|
||||
danmu.setText(text);
|
||||
danmu.setFontSize(fontSize);
|
||||
danmu.setColor("#" + color);
|
||||
danmu.setTime(time);
|
||||
danmu.setBarrageType(model);
|
||||
list.add(danmu);
|
||||
}
|
||||
rs.close();
|
||||
} catch (SQLException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends AbsDatabasesBean> List<T> get(String table, String where, Class<T> tClass) {
|
||||
return super.get(table, where, tClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount(String table) {
|
||||
return super.getCount(table);
|
||||
}
|
||||
|
||||
public LiveVideoDatabaseBean getVideo(String videoId) {
|
||||
for (LiveVideoDatabaseBean info : getLiveInfos()) {
|
||||
System.out.println(videoId + " " + info.getSql_time().getTime());
|
||||
if (videoId.trim().equals(String.valueOf(info.getSql_time().getTime()))) {
|
||||
return info;
|
||||
}
|
||||
@@ -244,8 +295,8 @@ public class BiliLiveDatabase extends SQLiteManager {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
BiliLiveDatabase biliLiveDatabase = new BiliLiveDatabase(LiveRoomConfig.buildConfig("7688602"));
|
||||
biliLiveDatabase.resetSQL();
|
||||
BiliLiveDatabase biliLiveDatabase = new BiliLiveDatabase(LiveRoomConfig.buildConfigTmp("33989"));
|
||||
// biliLiveDatabase.resetSQL();
|
||||
biliLiveDatabase.resetData();
|
||||
|
||||
}
|
||||
|
||||
@@ -15,16 +15,19 @@ import com.yutou.biliapi.bean.websocket.WebSocketBody;
|
||||
import com.yutou.biliapi.bean.websocket.WebSocketHeader;
|
||||
import com.yutou.biliapi.bean.websocket.live.WSData;
|
||||
import com.yutou.biliapi.databases.BiliBiliLoginDatabase;
|
||||
import com.yutou.biliapi.databases.BiliLiveDatabase;
|
||||
import com.yutou.biliapi.utils.BiliUserUtils;
|
||||
import com.yutou.biliapi.utils.BytesUtils;
|
||||
import com.yutou.bilibili.services.LiveDatabasesService;
|
||||
import com.yutou.bilibili.services.LiveService;
|
||||
import com.yutou.common.okhttp.HttpBody;
|
||||
import com.yutou.common.okhttp.HttpCallback;
|
||||
import com.yutou.common.utils.ConfigTools;
|
||||
import com.yutou.common.utils.Log;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.Setter;
|
||||
import okhttp3.Headers;
|
||||
import org.java_websocket.client.WebSocketClient;
|
||||
import org.java_websocket.handshake.ServerHandshake;
|
||||
import org.springframework.stereotype.Service;
|
||||
import retrofit2.Response;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -39,24 +42,19 @@ import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class WebSocketManager {
|
||||
@Service
|
||||
public class WebSocketServer {
|
||||
ThreadPoolExecutor executor;
|
||||
private static WebSocketManager instance;
|
||||
Map<LiveRoomConfig, DanmuTask> roomMap;
|
||||
private final List<String> userStopList = new ArrayList<>();//手动停止列表
|
||||
@Resource
|
||||
LiveDatabasesService liveDatabasesService;
|
||||
|
||||
private WebSocketManager() {
|
||||
private WebSocketServer() {
|
||||
roomMap = new HashMap<>();
|
||||
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
|
||||
}
|
||||
|
||||
public static WebSocketManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new WebSocketManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public boolean checkRoom(LiveRoomConfig roomConfig) {
|
||||
return roomMap.containsKey(roomConfig);
|
||||
}
|
||||
@@ -72,16 +70,16 @@ public class WebSocketManager {
|
||||
}
|
||||
|
||||
public void addRoom(LiveRoomConfig roomConfig, boolean isUser) {
|
||||
if (!isUser && userStopList.contains(roomConfig.getRoomId().toString())) {
|
||||
if (!isUser && userStopList.contains(roomConfig.getRoomId())) {
|
||||
return;
|
||||
}
|
||||
if (checkRoom(roomConfig)) {
|
||||
return;
|
||||
}
|
||||
if (isUser) {
|
||||
userStopList.remove(roomConfig.getRoomId().toString());
|
||||
userStopList.remove(roomConfig.getRoomId());
|
||||
}
|
||||
DanmuTask task = new DanmuTask(roomConfig);
|
||||
DanmuTask task = new DanmuTask(roomConfig, isUser);
|
||||
roomMap.put(roomConfig, task);
|
||||
Log.i("添加websocket任务");
|
||||
executor.execute(task);
|
||||
@@ -89,23 +87,33 @@ public class WebSocketManager {
|
||||
|
||||
public void stopRoom(String roomId, boolean isUser) {
|
||||
LiveRoomConfig roomConfig = new LiveRoomConfig();
|
||||
roomConfig.setRoomId(new String(roomId));
|
||||
roomConfig.setRoomId(roomId);
|
||||
if (checkRoom(roomConfig)) {
|
||||
roomMap.get(roomConfig).close();
|
||||
roomMap.remove(roomConfig);
|
||||
}
|
||||
if (isUser) {
|
||||
userStopList.add(roomConfig.getRoomId().toString());
|
||||
userStopList.add(roomConfig.getRoomId());
|
||||
}
|
||||
}
|
||||
|
||||
private static class DanmuTask implements Runnable {
|
||||
public void removeUserStopList(String roomId) {
|
||||
userStopList.remove(roomId);
|
||||
}
|
||||
|
||||
public boolean isUserStopList(String roomId) {
|
||||
return userStopList.contains(roomId);
|
||||
}
|
||||
|
||||
private class DanmuTask implements Runnable {
|
||||
LiveRoomConfig roomConfig;
|
||||
WebSocketClientTh client;
|
||||
boolean isUser;
|
||||
|
||||
public DanmuTask(LiveRoomConfig config) {
|
||||
public DanmuTask(LiveRoomConfig config, boolean isUser) {
|
||||
this.roomConfig = config;
|
||||
WebSocketManager.getInstance().roomMap.put(roomConfig, this);
|
||||
this.isUser = isUser;
|
||||
roomMap.put(roomConfig, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -113,12 +121,12 @@ public class WebSocketManager {
|
||||
LiveApi api = BiliLiveNetApiManager.getInstance().getApi(roomConfig.getLoginUid());
|
||||
Response<HttpBody<LiveRoomInfo>> execute = null;
|
||||
try {
|
||||
execute = api.getRoomInfo(roomConfig.getRoomId().toString()).execute();
|
||||
execute = api.getRoomInfo(roomConfig.getRoomId()).execute();
|
||||
if (execute.isSuccessful()) {
|
||||
roomConfig.setRoomInfo(execute.body() != null ? execute.body().getData() : null);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
WebSocketManager.getInstance().roomMap.remove(roomConfig);
|
||||
roomMap.remove(roomConfig);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
api.getLiveRoomDanmuInfo(String.valueOf(roomConfig.getRoomId())).enqueue(new HttpCallback<LiveDanmuInfo>() {
|
||||
@@ -130,9 +138,9 @@ public class WebSocketManager {
|
||||
// url="ws://127.0.0.1:8765";
|
||||
try {
|
||||
roomConfig.setLiveInfo(response);
|
||||
client = new WebSocketClientTh(new URI(url), roomConfig);
|
||||
client = new WebSocketClientTh(new URI(url), roomConfig, isUser);
|
||||
} catch (URISyntaxException e) {
|
||||
WebSocketManager.getInstance().roomMap.remove(roomConfig);
|
||||
roomMap.remove(roomConfig);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
@@ -140,7 +148,7 @@ public class WebSocketManager {
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable throwable) {
|
||||
WebSocketManager.getInstance().roomMap.remove(roomConfig);
|
||||
roomMap.remove(roomConfig);
|
||||
Log.e(throwable);
|
||||
}
|
||||
});
|
||||
@@ -151,20 +159,21 @@ public class WebSocketManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static class WebSocketClientTh extends WebSocketClient {
|
||||
private LiveRoomConfig roomConfig;
|
||||
private HeartbeatTask heartbeatTask;
|
||||
BiliLiveDatabase liveDatabase;
|
||||
private boolean itTmp = true;
|
||||
private class WebSocketClientTh extends WebSocketClient {
|
||||
private final LiveRoomConfig roomConfig;
|
||||
private final HeartbeatTask heartbeatTask;
|
||||
private final boolean isUser;
|
||||
private final String logTag;
|
||||
|
||||
public WebSocketClientTh(URI serverUri, LiveRoomConfig roomId) {
|
||||
public WebSocketClientTh(URI serverUri, LiveRoomConfig roomId, boolean isUser) {
|
||||
super(serverUri);
|
||||
Log.i("WebSocketClientTh.WebSocketClientTh : " + serverUri);
|
||||
logTag = "WebSocket-" + roomId.getRoomId();
|
||||
Log.getDynamicLogger(logTag).info("WebSocketClientTh.WebSocketClientTh = {}", serverUri);
|
||||
this.isUser = isUser;
|
||||
this.roomConfig = roomId;
|
||||
liveDatabase = new BiliLiveDatabase(roomConfig);
|
||||
Brotli4jLoader.ensureAvailability();
|
||||
heartbeatTask = new HeartbeatTask();
|
||||
addHeader("User-Agent", ConfigTools.getUserAgent());
|
||||
heartbeatTask = new HeartbeatTask();
|
||||
connect();
|
||||
}
|
||||
|
||||
@@ -174,7 +183,7 @@ public class WebSocketManager {
|
||||
heartbeatTask.setSocket(this);
|
||||
heartbeatTask.sendInitAuthData();
|
||||
new Timer().schedule(heartbeatTask, 1000, 30000);
|
||||
Log.i("WebSocketClientTh.onOpen", roomConfig.getRoomId());
|
||||
Log.getDynamicLogger(logTag).info("WebSocketClientTh.onOpen,{}", roomConfig.getRoomId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -191,18 +200,21 @@ public class WebSocketManager {
|
||||
|
||||
@Override
|
||||
public void onClose(int i, String s, boolean b) {
|
||||
Log.e("WebSocketClientTh.onClose", "i = " + i + ", s = " + s + ", b = " + b, roomConfig.getRoomId(), heartbeatTask.socket.isOpen());
|
||||
WebSocketManager.getInstance().roomMap.remove(roomConfig);
|
||||
liveDatabase.close();
|
||||
Log.getDynamicLogger(logTag).error("WebSocketClientTh.onClose. {},{},{}", "i = " + i + ", s = " + s + ", b = " + b, roomConfig.getRoomId(), heartbeatTask.socket.isOpen());
|
||||
roomMap.remove(roomConfig);
|
||||
heartbeatTask.cancel();
|
||||
if (i == 1006) {
|
||||
if (LiveService.checkLive(roomConfig.getRoomId())) {
|
||||
addRoom(roomConfig, isUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Exception e) {
|
||||
Log.i("WebSocketClientTh.onError", roomConfig.getRoomId());
|
||||
Log.getDynamicLogger(logTag).error("WebSocketClientTh.onError,{}", roomConfig.getRoomId());
|
||||
Log.e(e);
|
||||
WebSocketManager.getInstance().roomMap.remove(roomConfig);
|
||||
liveDatabase.close();
|
||||
roomMap.remove(roomConfig);
|
||||
heartbeatTask.cancel();
|
||||
}
|
||||
|
||||
@@ -212,23 +224,28 @@ public class WebSocketManager {
|
||||
* @param data 待压缩的数据
|
||||
*/
|
||||
public void decompress(byte[] data) {
|
||||
byte[] bytes = new byte[data.length - 16];
|
||||
WebSocketHeader header = new WebSocketHeader(data);
|
||||
System.arraycopy(data, header.getHeaderSize(), bytes, 0, data.length - header.getHeaderSize());
|
||||
// Log.i("数据大小:" + header.getDataSize() + " 协议:" + header.getAgree() + " 头部大小:" + header.getHeaderSize() + " 命令:" + header.getCmdData());
|
||||
switch (header.getAgree()) {
|
||||
case 0:
|
||||
case 1:
|
||||
danmu(bytes);
|
||||
break;
|
||||
default:
|
||||
unzipDanmu(bytes, header.getAgree() == 3);
|
||||
}
|
||||
Thread.ofVirtual()
|
||||
.name("TaskUnDanmu-" + Thread.currentThread().getName())
|
||||
.start(() -> {
|
||||
byte[] bytes = new byte[data.length - 16];
|
||||
WebSocketHeader header = new WebSocketHeader(data);
|
||||
System.arraycopy(data, header.getHeaderSize(), bytes, 0, data.length - header.getHeaderSize());
|
||||
switch (header.getAgree()) {
|
||||
case 0:
|
||||
case 1:
|
||||
danmu(bytes);
|
||||
break;
|
||||
default:
|
||||
unzipDanmu(bytes, header.getAgree() == 3);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void danmu(byte[] bytes) {
|
||||
//Log.i("未压缩:" + new String(bytes));
|
||||
Log.getDynamicLogger(logTag).info("未压缩:{}", new String(bytes));
|
||||
}
|
||||
|
||||
private void unzipDanmu(byte[] bytes, boolean useHeader) {
|
||||
@@ -237,29 +254,24 @@ public class WebSocketManager {
|
||||
DirectDecompress directDecompress = Decoder.decompress(bytes);
|
||||
if (directDecompress.getResultStatus() == DecoderJNI.Status.DONE) {
|
||||
WebSocketBody body = new WebSocketBody(directDecompress.getDecompressedData());
|
||||
// Log.i("协议:" + useHeader + " 命令数:" + body.getBodyList().size());
|
||||
Log.getDynamicLogger(logTag).info("协议:{},命令数:{}", useHeader, body.getBodyList().size());
|
||||
for (JSONObject json : body.getBodyList()) {
|
||||
WSData parse = WSData.parse(json);
|
||||
liveDatabase.addSource(parse);
|
||||
// Log.i("解压:" + parse);
|
||||
liveDatabasesService.getLiveDatabase(roomConfig.getRoomId()).addSource(parse);
|
||||
Log.getDynamicLogger(logTag).info("解压:{}", parse);
|
||||
}
|
||||
// Log.i();
|
||||
// Log.i();
|
||||
} else {
|
||||
Log.e(new RuntimeException("解压失败"));
|
||||
Log.getDynamicLogger(logTag).error(new RuntimeException("解压失败"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(e);
|
||||
Log.getDynamicLogger(logTag).error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Setter
|
||||
private class HeartbeatTask extends TimerTask {
|
||||
WebSocketClientTh socket;
|
||||
|
||||
public void setSocket(WebSocketClientTh socket) {
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
@@ -284,31 +296,26 @@ public class WebSocketManager {
|
||||
json.put("uid", 0);
|
||||
}
|
||||
LoginCookieDatabaseBean cookie = BiliBiliLoginDatabase.getInstance().getCookie(roomConfig.getLoginUid());
|
||||
Log.d("cookie:", cookie, "RoomId:" + roomConfig);
|
||||
String buvid = BiliUserUtils.getBuvid(cookie);
|
||||
if (buvid != null) {
|
||||
try {
|
||||
json.put("roomid", new BigInteger(roomConfig.getRoomId()));
|
||||
json.put("protover", 3);
|
||||
json.put("buvid", buvid);
|
||||
json.put("platform", "web");
|
||||
json.put("type", 2);
|
||||
json.put("key", roomConfig.getLiveInfo().getToken());
|
||||
byte[] bytes = {0, 16, 0, 1, 0, 0, 0, 7, 0, 0, 0, 1};
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
try {
|
||||
json.put("roomid", new BigInteger(roomConfig.getRoomId()));
|
||||
json.put("protover", 3);
|
||||
// json.put("buvid3", BiliUserUtils.getBuvid(roomConfig.isLogin() ? BiliBiliLoginDatabase.getInstance().getCookie(roomConfig.getRoomId()) : null));
|
||||
json.put("platform", "web");
|
||||
json.put("type", 2);
|
||||
json.put("key", roomConfig.getLiveInfo().getToken());
|
||||
byte[] bytes = {0, 16, 0, 1, 0, 0, 0, 7, 0, 0, 0, 1};
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
// Log.i("bytes.length = " + bytes.length);
|
||||
Log.i(json);
|
||||
outputStream.write(BytesUtils.toLH(json.toString().length() + 16));
|
||||
outputStream.write(bytes);
|
||||
outputStream.write(json.toJSONString().getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.flush();
|
||||
Log.i(json);
|
||||
outputStream.write(BytesUtils.toLH(json.toString().length() + 16));
|
||||
outputStream.write(bytes);
|
||||
outputStream.write(json.toJSONString().getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.flush();
|
||||
// BytesUtils.printHex(outputStream.toByteArray());
|
||||
Log.i(socket.isOpen(), json.toString());
|
||||
socket.send(outputStream.toByteArray());
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(e);
|
||||
}
|
||||
Log.i(socket.isOpen(), json.toString());
|
||||
socket.send(outputStream.toByteArray());
|
||||
} catch (Exception e) {
|
||||
Log.e(e);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@ package com.yutou.bilibili.Controllers;
|
||||
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.yutou.biliapi.bean.live.FollowLive;
|
||||
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||
import com.yutou.biliapi.bean.login.LoginUserDatabaseBean;
|
||||
import com.yutou.biliapi.bean.user.UserFollowingsBean;
|
||||
import com.yutou.bilibili.Tools.DateFormatUtils;
|
||||
import com.yutou.bilibili.Tools.Tools;
|
||||
import com.yutou.bilibili.datas.ResultData;
|
||||
import com.yutou.bilibili.datas.ReturnCode;
|
||||
@@ -12,18 +14,17 @@ import com.yutou.bilibili.services.LiveConfigService;
|
||||
import com.yutou.bilibili.services.LiveLoginService;
|
||||
import com.yutou.bilibili.services.LiveUserService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/live/config/")
|
||||
@@ -46,6 +47,17 @@ public class LiveConfigController {
|
||||
return ResultData.success(bean, bean.getTotal());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping("followLive")
|
||||
public JSONObject getFollowLive(String userId) {
|
||||
List<FollowLive.Room> followLive = userService.getUserFollowLive(userId);
|
||||
if (followLive == null) {
|
||||
return ResultData.fail(ReturnCode.RC999);
|
||||
}
|
||||
followLive.forEach(item -> item.setLiveTime(DateFormatUtils.getInstance().convertSeconds(Long.parseLong(item.getLiveTime()))));
|
||||
return ResultData.success(followLive, followLive.size());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping("follow/add")
|
||||
public JSONObject addFollow(String uid, String anchorId) {
|
||||
@@ -79,7 +91,21 @@ public class LiveConfigController {
|
||||
follow.setUname(jsonObject.getString("uname"));
|
||||
return follow;
|
||||
}).toList();
|
||||
return ResultData.success(userService.followAll(uid,list));
|
||||
return ResultData.success(userService.followAll(uid, list));
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping("follow/roomId/addList")
|
||||
public JSONObject addFollowListToRoomId(String array) {
|
||||
if (!StringUtils.hasText(array)) {
|
||||
return ResultData.fail(ReturnCode.RC500);
|
||||
}
|
||||
JSONArray jsonArray = JSONArray.parseArray(array);
|
||||
List<LiveConfigDatabaseBean> list = jsonArray.stream().map(o -> {
|
||||
JSONObject jsonObject = (JSONObject) o;
|
||||
return configService.addConfig(jsonObject.getString("roomId"), new LiveConfigDatabaseBean());
|
||||
}).toList();
|
||||
return ResultData.success(list, list.size());
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@@ -110,6 +136,55 @@ public class LiveConfigController {
|
||||
return ResultData.success(ReturnCode.RC100);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "set/array", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public JSONObject addArrayConfig(@RequestBody JSONObject jsonObject) {
|
||||
JSONObject config = jsonObject.getJSONObject("config");
|
||||
LiveConfigDatabaseBean bean = config.to(LiveConfigDatabaseBean.class);
|
||||
if (!bean.verifyLiveTimer()) {
|
||||
return ResultData.fail(ReturnCode.RC999.getCode(), "视频录制时间格式错误");
|
||||
}
|
||||
if (!bean.verifyDanmuTimer()) {
|
||||
return ResultData.fail(ReturnCode.RC999.getCode(), "弹幕录制时间格式错误");
|
||||
}
|
||||
if ("on".equals(config.getString("recordDanmu"))) {
|
||||
bean.setRecordDanmu(true);
|
||||
}
|
||||
if ("on".equals(config.getString("recordLive"))) {
|
||||
bean.setRecordLive(true);
|
||||
}
|
||||
if ("on".equals(config.getString("syncDanmuForLive"))) {
|
||||
bean.setSyncDanmuForLive(true);
|
||||
}
|
||||
if (config.containsKey("keywordList")) {
|
||||
bean.setKeywordList(config.getList("keywordList", String.class));
|
||||
}
|
||||
JSONArray jsonArray = jsonObject.getJSONArray("array");
|
||||
List<LiveConfigDatabaseBean> list = jsonArray.stream().map(roomId -> configService.addConfig(roomId.toString(), bean)).toList();
|
||||
int countNull = list.stream().filter(Objects::isNull).toList().size();
|
||||
return ResultData.success("成功配置" + (list.size() - countNull) + "个直播间");
|
||||
}
|
||||
|
||||
@RequestMapping(value = "delete/array", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public JSONObject deleteArrayConfig(@RequestBody JSONArray jsonArray) {
|
||||
List<Boolean> list = jsonArray.stream().map(roomId -> configService.deleteConfig(roomId.toString())).toList();
|
||||
int countNull = list.stream().filter(it -> !it).toList().size();
|
||||
return ResultData.success("成功删除" + (list.size() - countNull) + "个直播间");
|
||||
}
|
||||
|
||||
@RequestMapping(value = "delete/all", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public JSONObject deleteAllConfig() {
|
||||
for (LiveConfigDatabaseBean bean : configService.getAllConfig()) {
|
||||
configService.deleteConfig(bean.getRoomId());
|
||||
}
|
||||
if (configService.getAllConfig().isEmpty()) {
|
||||
return ResultData.success("成功删除");
|
||||
} else {
|
||||
return ResultData.fail(-1, "删除失败,剩余:" + configService.getAllConfig().size() + "个直播间");
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "set", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.yutou.bilibili.Controllers;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.yutou.bilibili.datas.ResultData;
|
||||
import com.yutou.bilibili.services.LiveDanmuService;
|
||||
@@ -10,6 +11,8 @@ 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
|
||||
@@ -22,13 +25,36 @@ public class LiveController {
|
||||
|
||||
@RequestMapping("/live/list")
|
||||
@ResponseBody
|
||||
public JSONObject getLiveList(int page,int limit) {
|
||||
return ResultData.success(liveService.getLiveList(page,limit), liveService.getConfigCount());
|
||||
public JSONObject getLiveList(int page, int limit) {
|
||||
List<JSONObject> list = liveService.getLiveList(page, limit).stream().map(it -> {
|
||||
JSONObject json = JSONObject.parseObject(JSON.toJSONString(it));
|
||||
if (videoService.isUserStopList(it.getRoomId())) {
|
||||
json.put("videoListen", "已暂停");
|
||||
} else if (it.isDownloadVideo()) {
|
||||
json.put("videoListen", "录制中");
|
||||
} else {
|
||||
json.put("videoListen", "待机中");
|
||||
}
|
||||
if (danmuService.isUserStopList(it.getRoomId())) {
|
||||
json.put("danmuListen", "已暂停");
|
||||
} else if (it.isDanmu()) {
|
||||
json.put("danmuListen", "录制中");
|
||||
} else {
|
||||
json.put("danmuListen", "待机中");
|
||||
}
|
||||
return json;
|
||||
}).toList();
|
||||
return ResultData.success(list, liveService.getConfigCount());
|
||||
}
|
||||
|
||||
@RequestMapping("/live/gift/info")
|
||||
@ResponseBody
|
||||
public JSONObject download(String roomId, String videoId) {
|
||||
return ResultData.success(liveService.getGiftInfo(roomId,videoId));
|
||||
public JSONObject info(String roomId, String videoId) {
|
||||
return ResultData.success(liveService.getGiftInfo(roomId, videoId));
|
||||
}
|
||||
@RequestMapping("/live/gift/info/item")
|
||||
@ResponseBody
|
||||
public JSONObject infoItem(String roomId,String videoId,String giftName) {
|
||||
return ResultData.success(liveService.getGiftItemInfo(roomId, videoId, giftName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package com.yutou.bilibili.Controllers;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.yutou.biliapi.bean.live.LiveRoomConfig;
|
||||
import com.yutou.biliapi.net.WebSocketManager;
|
||||
import com.yutou.bilibili.datas.ResultData;
|
||||
import com.yutou.bilibili.datas.ReturnCode;
|
||||
import com.yutou.bilibili.services.LiveDanmuService;
|
||||
@@ -44,7 +42,7 @@ public class LiveDanmuController {
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping("/live/danmu/get")
|
||||
public JSONObject getDanmu(String roomId, String videoId) {
|
||||
return ResultData.success(service.getDanmu(roomId, videoId));
|
||||
public JSONObject getDanmu(String roomId, String videoId, int page) {
|
||||
return ResultData.success(service.getDanmu(roomId, videoId, page));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
|
||||
import com.yutou.bilibili.datas.ResultData;
|
||||
import com.yutou.bilibili.datas.ReturnCode;
|
||||
import com.yutou.bilibili.services.LiveDatabasesService;
|
||||
import com.yutou.bilibili.services.LiveVideoDownloadService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Controller;
|
||||
@@ -17,6 +18,8 @@ import java.util.List;
|
||||
public class LiveVideoController {
|
||||
@Resource
|
||||
LiveVideoDownloadService videoService;
|
||||
@Resource
|
||||
LiveDatabasesService databasesService;
|
||||
|
||||
@RequestMapping("/live/video/list")
|
||||
@ResponseBody
|
||||
@@ -34,9 +37,7 @@ public class LiveVideoController {
|
||||
@RequestMapping("/live/video/start")
|
||||
@ResponseBody
|
||||
public JSONObject startDownload(String roomId) {
|
||||
BiliLiveConfigDatabase liveConfigDatabase = new BiliLiveConfigDatabase();
|
||||
List<LiveConfigDatabaseBean> list = liveConfigDatabase.getAllConfig();
|
||||
liveConfigDatabase.close();
|
||||
List<LiveConfigDatabaseBean> list = databasesService.getConfigDatabase().getAllConfig();
|
||||
for (LiveConfigDatabaseBean bean : list) {
|
||||
if (bean.getRoomId().toString().equals(roomId)) {
|
||||
videoService.start(bean, true);
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
package com.yutou.bilibili.Controllers;
|
||||
|
||||
import com.yutou.bilibili.services.LiveDanmuService;
|
||||
import com.yutou.bilibili.services.LiveDatabasesService;
|
||||
import com.yutou.bilibili.services.LiveVideoDownloadService;
|
||||
import com.yutou.common.okhttp.HttpLoggingInterceptor;
|
||||
import com.yutou.common.utils.FFmpegUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
public class TestControllers {
|
||||
@RequestMapping("/root/all")
|
||||
@Resource
|
||||
LiveDatabasesService databasesService;
|
||||
@Resource
|
||||
LiveDanmuService danmuService;
|
||||
@Resource
|
||||
LiveVideoDownloadService videoDownloadService;
|
||||
@RequestMapping("/test/database")
|
||||
@ResponseBody
|
||||
public String test(){
|
||||
return "hello world";
|
||||
return "缓存:"+databasesService.getCacheInfo()+", 弹幕数:"+danmuService.getLiveRoomList().size()+", 视频数:"+videoDownloadService.getDownloadTasks().size();
|
||||
}
|
||||
@ResponseBody
|
||||
@RequestMapping("/root/log")
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
@@ -57,7 +58,7 @@ public class VideoFileController {
|
||||
try {
|
||||
length = new URL(url).openConnection().getContentLength();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
return Tools.getFile(new File("Web"+ File.separator+"assets"+File.separator+"def.png"));
|
||||
}
|
||||
if (img.exists()&&length==img.length()) {
|
||||
return Tools.getFile(img);
|
||||
@@ -109,6 +110,13 @@ public class VideoFileController {
|
||||
@RequestMapping("/video/play")
|
||||
@ResponseBody
|
||||
public JSONObject getVideoUrl(String roomId, String videoId) {
|
||||
return ResultData.success("/play/"+videoService.getVideoPlay(roomId, videoId));
|
||||
String url = videoService.getVideoPlay(roomId, videoId);
|
||||
/* String[] split = url.split("/");
|
||||
StringBuilder sb=new StringBuilder();
|
||||
for (String s : split) {
|
||||
sb.append(URLEncoder.encode(s,StandardCharsets.UTF_8)).append("/");
|
||||
}
|
||||
sb.setLength(sb.length()-1);*/
|
||||
return ResultData.success("/live"+url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.yutou.bilibili.Tools;
|
||||
|
||||
import com.yutou.bilibili.services.LiveDatabasesService;
|
||||
import com.yutou.bilibili.services.LiveVideoDownloadService;
|
||||
import com.yutou.bilibili.services.SystemService;
|
||||
import com.yutou.common.utils.Log;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -14,10 +16,14 @@ public class ApplicationClose implements ApplicationListener<ContextClosedEvent>
|
||||
SystemService systemConfigService;
|
||||
@Resource
|
||||
LiveVideoDownloadService videoService;
|
||||
@Resource
|
||||
LiveDatabasesService databasesService;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
|
||||
public void onApplicationEvent(@NotNull ContextClosedEvent contextClosedEvent) {
|
||||
Log.i("服务结束");
|
||||
systemConfigService.stop();
|
||||
videoService.stopAll();
|
||||
databasesService.closeAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package com.yutou.bilibili.Tools;
|
||||
|
||||
import com.yutou.biliapi.bean.live.LiveRoomInfo;
|
||||
import com.yutou.bilibili.services.SystemService;
|
||||
import com.yutou.common.utils.DynamicLogFile;
|
||||
import com.yutou.common.utils.Log;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* 服务启动后执行
|
||||
@@ -16,11 +20,11 @@ import java.util.logging.Logger;
|
||||
public class ApplicationInit implements ApplicationRunner {
|
||||
@Resource
|
||||
SystemService systemConfigService;
|
||||
Logger logger = LogManager.getLogger(ApplicationInit.class);
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
Logger logger = Logger.getLogger("ApplicationInit");
|
||||
logger.log(Level.INFO, "服务启动后执行");
|
||||
logger.info("服务启动后执行");
|
||||
systemConfigService.start();
|
||||
}
|
||||
|
||||
|
||||
22
src/main/java/com/yutou/bilibili/Tools/ApplicationTime.java
Normal file
22
src/main/java/com/yutou/bilibili/Tools/ApplicationTime.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package com.yutou.bilibili.Tools;
|
||||
|
||||
import com.yutou.bilibili.services.LiveDanmuService;
|
||||
import com.yutou.bilibili.services.LiveVideoDownloadService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@EnableScheduling
|
||||
public class ApplicationTime {
|
||||
@Resource
|
||||
LiveVideoDownloadService videoService;
|
||||
@Resource
|
||||
LiveDanmuService danmuService;
|
||||
@Scheduled(cron = "1 0 0 * * *")
|
||||
public void reset(){
|
||||
videoService.clearUserStopList();
|
||||
danmuService.clearUserList();
|
||||
}
|
||||
}
|
||||
@@ -76,6 +76,17 @@ public class DateFormatUtils {
|
||||
Date time = parse(date, format);
|
||||
return format(time, format);
|
||||
}
|
||||
public String convertSeconds(long totalSeconds) {
|
||||
// 计算总小时数
|
||||
long hours = totalSeconds / 3600;
|
||||
// 剩余的秒数
|
||||
long remainingSecondsAfterHours = totalSeconds % 3600;
|
||||
// 计算分钟数
|
||||
long minutes = remainingSecondsAfterHours / 60;
|
||||
// 最后剩余的秒数
|
||||
long seconds = remainingSecondsAfterHours % 60;
|
||||
return String.format("%d小时%d分%d秒", hours, minutes, seconds);
|
||||
}
|
||||
public String formatMillis(long millis) {
|
||||
Duration duration = Duration.ofMillis(millis);
|
||||
int seconds = (int) (duration.getSeconds() % 60);
|
||||
|
||||
@@ -22,7 +22,7 @@ import java.io.File;
|
||||
public class LiveInfoNfoTools {
|
||||
public static void saveLiveInfoNfo(LiveRoomInfo info, String path,String xmlName) {
|
||||
try {
|
||||
LiveRoomConfig config = LiveRoomConfig.buildConfig(info.getRoomId().toString());
|
||||
LiveRoomConfig config = LiveRoomConfig.buildConfigTmp(info.getRoomId());
|
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder dBuilder = null;
|
||||
dBuilder = dbFactory.newDocumentBuilder();
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package com.yutou.bilibili.datas.web;
|
||||
|
||||
import com.yutou.biliapi.bean.live.database.LiveSuperChatDatabaseBean;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class LiveVideoDanmu {
|
||||
|
||||
List<Danmu> danmu = new ArrayList<>();
|
||||
List<SuperChat> superChat = new ArrayList<>();
|
||||
|
||||
|
||||
@Data
|
||||
public static class Addition {
|
||||
private int grade = 2;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Danmu {
|
||||
public static final String DANMU_TYPE_SCROLL = "scroll";
|
||||
public static final String DANMU_TYPE_TOP = "top";
|
||||
public static final String DANMU_TYPE_BOTTOM = "bottom";
|
||||
private String id;
|
||||
private String barrageType = DANMU_TYPE_SCROLL;
|
||||
private long time;
|
||||
private String text;
|
||||
private int fontSize = 25;
|
||||
private int lineHeight = 1;
|
||||
private String color = "#ffffff";
|
||||
private Addition addition = new Addition();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class SuperChat {
|
||||
private int id;
|
||||
private long price;
|
||||
private String uid;
|
||||
private long start_time;
|
||||
private long duration;
|
||||
private String message;
|
||||
private String message_trans;
|
||||
private String message_font_color;
|
||||
private String backgroundBottomColor;
|
||||
private String userName;
|
||||
private String userNameColor;
|
||||
private String userAvatar;
|
||||
|
||||
public SuperChat(long videoTime, LiveSuperChatDatabaseBean bean) {
|
||||
this.id = bean.getId();
|
||||
this.price = bean.getPrice();
|
||||
this.uid = bean.getUid();
|
||||
this.start_time = (bean.getSql_time().getTime() - videoTime) / 1000;
|
||||
this.duration = bean.getEnd_time() - bean.getStart_time();
|
||||
this.message = bean.getMessage();
|
||||
this.message_trans = bean.getMessage_trans();
|
||||
this.message_font_color = bean.getMessage_font_color();
|
||||
this.backgroundBottomColor = bean.getBackgroundBottomColor();
|
||||
this.userName = bean.getUserName();
|
||||
this.userAvatar = bean.getUserAvatar();
|
||||
this.userNameColor=bean.getUserNameColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.yutou.bilibili.datas.web;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class LiveWebDanmuBean {
|
||||
|
||||
List<Danmu> danmu = new ArrayList<>();
|
||||
boolean isNextDanmu = false;
|
||||
long danmuCount = 0;
|
||||
|
||||
@Data
|
||||
public static class Addition {
|
||||
private int grade = 2;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Danmu {
|
||||
public static final String DANMU_TYPE_SCROLL = "scroll";
|
||||
public static final String DANMU_TYPE_TOP = "top";
|
||||
public static final String DANMU_TYPE_BOTTOM = "bottom";
|
||||
private String id;
|
||||
private String barrageType = DANMU_TYPE_SCROLL;
|
||||
private long time;
|
||||
private String text;
|
||||
private int fontSize = 25;
|
||||
private int lineHeight = 1;
|
||||
private String color = "#ffffff";
|
||||
private Addition addition = new Addition();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.yutou.bilibili.datas.web;
|
||||
|
||||
import com.yutou.biliapi.bean.live.database.LiveSuperChatDatabaseBean;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SuperChatGiftBean {
|
||||
private int id;
|
||||
private long price;
|
||||
private String uid;
|
||||
private long start_time;
|
||||
private long duration;
|
||||
private String message;
|
||||
private String message_trans;
|
||||
private String message_font_color;
|
||||
private String backgroundBottomColor;
|
||||
private String userName;
|
||||
private String userNameColor;
|
||||
private String userAvatar;
|
||||
|
||||
public SuperChatGiftBean(long videoTime, LiveSuperChatDatabaseBean bean) {
|
||||
this.id = bean.getId();
|
||||
this.price = bean.getPrice();
|
||||
this.uid = bean.getUid();
|
||||
this.start_time = (bean.getSql_time().getTime() - videoTime) / 1000;
|
||||
this.duration = bean.getEnd_time() - bean.getStart_time();
|
||||
this.message = bean.getMessage();
|
||||
this.message_trans = bean.getMessage_trans();
|
||||
this.message_font_color = bean.getMessage_font_color();
|
||||
this.backgroundBottomColor = bean.getBackgroundBottomColor();
|
||||
this.userName = bean.getUserName();
|
||||
this.userAvatar = bean.getUserAvatar();
|
||||
this.userNameColor = bean.getUserNameColor();
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,12 @@ import java.util.List;
|
||||
|
||||
@Service
|
||||
public class LiveConfigService {
|
||||
BiliLiveConfigDatabase database = new BiliLiveConfigDatabase();
|
||||
@Resource
|
||||
LiveDatabasesService databasesService;
|
||||
@Resource
|
||||
LiveDanmuService danmuService;
|
||||
@Resource
|
||||
LiveVideoDownloadService videoDownloadService;
|
||||
|
||||
public LiveConfigDatabaseBean addConfig(String url, LiveConfigDatabaseBean bean) {
|
||||
if (!StringUtils.hasText(url)) {
|
||||
@@ -35,7 +40,9 @@ public class LiveConfigService {
|
||||
bean.setRoomId(body.getRoomId());
|
||||
bean.setAnchorFace(infoBean.getInfo().getFace());
|
||||
bean.setAnchorName(infoBean.getInfo().getUname());
|
||||
database.setConfig(bean);
|
||||
databasesService.getConfigDatabase().setConfig(bean);
|
||||
danmuService.removeUserStopList(roomId);
|
||||
videoDownloadService.removeUserStopList(roomId);
|
||||
return bean;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -43,38 +50,39 @@ public class LiveConfigService {
|
||||
}
|
||||
|
||||
public LiveConfigDatabaseBean updateConfig(String roomId, LiveConfigDatabaseBean bean) {
|
||||
LiveConfigDatabaseBean config = database.getConfig(roomId);
|
||||
LiveConfigDatabaseBean config = databasesService.getConfigDatabase().getConfig(roomId);
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
bean.setRoomId(roomId);
|
||||
bean.setSql_time(config.getSql_time());
|
||||
database.setConfig(bean);
|
||||
databasesService.getConfigDatabase().setConfig(bean);
|
||||
return bean;
|
||||
}
|
||||
|
||||
public boolean deleteConfig(String roomId) {
|
||||
LiveConfigDatabaseBean config = database.getConfig(roomId);
|
||||
LiveConfigDatabaseBean config = databasesService.getConfigDatabase().getConfig(roomId);
|
||||
if (config == null) {
|
||||
return false;
|
||||
|
||||
}
|
||||
return database.deleteConfig(roomId);
|
||||
return databasesService.getConfigDatabase().deleteConfig(roomId);
|
||||
}
|
||||
|
||||
public List<LiveConfigDatabaseBean> getAllConfig() {
|
||||
return database.getAllConfig();
|
||||
return databasesService.getConfigDatabase().getAllConfig();
|
||||
}
|
||||
public List<LiveConfigDatabaseBean> getConfigs(int page,int limit) {
|
||||
return database.getConfigs(page, limit);
|
||||
|
||||
public List<LiveConfigDatabaseBean> getConfigs(int page, int limit) {
|
||||
return databasesService.getConfigDatabase().getConfigs(page, limit);
|
||||
}
|
||||
|
||||
public LiveConfigDatabaseBean getConfig(String roomId) {
|
||||
return database.getConfig(roomId);
|
||||
return databasesService.getConfigDatabase().getConfig(roomId);
|
||||
}
|
||||
|
||||
public File getFace(String roomId) {
|
||||
LiveConfigDatabaseBean config = database.getConfig(new String(roomId));
|
||||
LiveConfigDatabaseBean config = databasesService.getConfigDatabase().getConfig(new String(roomId));
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -102,6 +110,6 @@ public class LiveConfigService {
|
||||
}
|
||||
|
||||
public int getConfigCount() {
|
||||
return database.getConfigCount();
|
||||
return databasesService.getConfigDatabase().getConfigCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,72 +5,82 @@ import com.alibaba.fastjson2.JSONObject;
|
||||
import com.yutou.biliapi.bean.live.LiveRoomConfig;
|
||||
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||
import com.yutou.biliapi.bean.live.database.LiveDanmuDatabaseBean;
|
||||
import com.yutou.biliapi.bean.live.database.LiveSuperChatDatabaseBean;
|
||||
import com.yutou.biliapi.bean.live.database.LiveVideoDatabaseBean;
|
||||
import com.yutou.biliapi.bean.websocket.live.WSData;
|
||||
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
|
||||
import com.yutou.biliapi.databases.BiliLiveDatabase;
|
||||
import com.yutou.biliapi.net.WebSocketManager;
|
||||
import com.yutou.biliapi.net.WebSocketServer;
|
||||
import com.yutou.bilibili.Tools.AssTools;
|
||||
import com.yutou.bilibili.Tools.DateFormatUtils;
|
||||
import com.yutou.bilibili.Tools.Tools;
|
||||
import com.yutou.bilibili.datas.web.LiveVideoDanmu;
|
||||
import com.yutou.common.utils.FFmpegUtils;
|
||||
import com.yutou.bilibili.datas.web.LiveWebDanmuBean;
|
||||
import com.yutou.common.utils.Log;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class LiveDanmuService {
|
||||
@Resource
|
||||
LiveDatabasesService liveDatabasesService;
|
||||
@Resource
|
||||
WebSocketServer webSocketServer;
|
||||
|
||||
public void start(String roomId, boolean isUser) {
|
||||
WebSocketManager.getInstance().addRoom(LiveRoomConfig.buildConfig(roomId), isUser);
|
||||
webSocketServer.addRoom(liveDatabasesService.buildConfig(roomId), isUser);
|
||||
}
|
||||
|
||||
public void start(LiveConfigDatabaseBean roomId, boolean isUser) {
|
||||
webSocketServer.addRoom(liveDatabasesService.buildConfig(roomId.getRoomId()), isUser);
|
||||
}
|
||||
|
||||
public boolean check(String roomId) {
|
||||
LiveRoomConfig roomConfig = new LiveRoomConfig();
|
||||
roomConfig.setRoomId(roomId);
|
||||
return WebSocketManager.getInstance().checkRoom(roomConfig);
|
||||
return webSocketServer.checkRoom(roomConfig);
|
||||
}
|
||||
|
||||
public void stop(String roomId, boolean isUser) {
|
||||
WebSocketManager.getInstance().stopRoom(roomId, isUser);
|
||||
webSocketServer.stopRoom(roomId, isUser);
|
||||
}
|
||||
|
||||
public JSONArray getLiveRoomList() {
|
||||
return WebSocketManager.getInstance().getLiveRoomList();
|
||||
return webSocketServer.getLiveRoomList();
|
||||
}
|
||||
|
||||
public void clearUserList() {
|
||||
WebSocketManager.getInstance().clearUserStopList();
|
||||
webSocketServer.clearUserStopList();
|
||||
}
|
||||
public void removeUserStopList(String roomId) {
|
||||
webSocketServer.removeUserStopList(roomId);
|
||||
}
|
||||
public boolean isUserStopList(String roomId) {
|
||||
return webSocketServer.isUserStopList(roomId);
|
||||
}
|
||||
|
||||
public List<File> getDanmuFileList(String roomId) {
|
||||
BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase();
|
||||
LiveConfigDatabaseBean bean = configDatabase.getConfig(roomId);
|
||||
configDatabase.close();
|
||||
LiveConfigDatabaseBean bean = liveDatabasesService.getConfigDatabase().getConfig(roomId);
|
||||
return Tools.scanFile(new File(bean.getRecordPath() + File.separator + bean.getAnchorName()));
|
||||
}
|
||||
|
||||
public void saveDanmuXML(LiveVideoDatabaseBean videoDatabaseBean, BiliLiveDatabase database) {
|
||||
File videoFile = new File(videoDatabaseBean.getPath());
|
||||
long videoTime = FFmpegUtils.getVideoTime(videoFile) + videoDatabaseBean.getStartTime().getTime();
|
||||
long videoTime;
|
||||
if (videoDatabaseBean.getStopTime() == null) {
|
||||
videoTime = System.currentTimeMillis();
|
||||
} else {
|
||||
videoTime = videoDatabaseBean.getStopTime().getTime();
|
||||
}
|
||||
Log.i("开始时间:" + videoDatabaseBean.getStartTime().getTime());
|
||||
Log.i("结束时间:" + videoTime);
|
||||
String startTime = DateFormatUtils.getInstance().format(videoDatabaseBean.getStartTime());
|
||||
String endTime = DateFormatUtils.getInstance().format(videoTime);
|
||||
List<LiveDanmuDatabaseBean> danmus = database.getOfTime(startTime, endTime, LiveDanmuDatabaseBean.class);
|
||||
List<LiveDanmuDatabaseBean> danmus = database.getOfTime(videoDatabaseBean.getStartTime().getTime(), videoTime, LiveDanmuDatabaseBean.class);
|
||||
Log.i("弹幕数量:" + danmus.size());
|
||||
AssTools assTools = new AssTools(videoFile.getName().replace(".flv", ""), videoDatabaseBean.getStartTime());
|
||||
AssTools assTools = new AssTools(videoFile.getName().replace(".m3u8", ""), videoDatabaseBean.getStartTime());
|
||||
for (LiveDanmuDatabaseBean dm : danmus) {
|
||||
assTools.addDanmu(dm.createDanmuData());
|
||||
}
|
||||
assTools.saveDanmu(videoFile.getAbsolutePath().replace(".flv", ".ass"));
|
||||
assTools.saveDanmu(videoFile.getAbsolutePath().replace(".m3u8", ".ass"));
|
||||
}
|
||||
|
||||
public String toTimeString(long videoTime) {
|
||||
@@ -83,59 +93,55 @@ public class LiveDanmuService {
|
||||
return String.format("%d小时%d分钟%d秒", hours, minutes, finalRemainingSeconds);
|
||||
}
|
||||
|
||||
public LiveVideoDanmu getDanmu(String roomId, String videoId) {
|
||||
LiveVideoDanmu danmus = new LiveVideoDanmu();
|
||||
BiliLiveDatabase liveDatabase = new BiliLiveDatabase(LiveRoomConfig.buildConfig(roomId));
|
||||
LiveVideoDatabaseBean videoBean = liveDatabase.getVideo(videoId);
|
||||
if (videoBean == null) {
|
||||
return new LiveVideoDanmu();
|
||||
}
|
||||
File videoFile = new File(videoBean.getPath().replace(".flv", ".mp4"));
|
||||
if (!videoFile.exists()) {
|
||||
videoFile = new File(videoBean.getPath());
|
||||
}
|
||||
long videoTime = FFmpegUtils.getVideoTime(videoFile);
|
||||
long startTime = Long.parseLong(videoId);
|
||||
long endTime = Long.parseLong(videoId) + videoTime;
|
||||
// videoTime = 0;
|
||||
if (videoTime == 0) {
|
||||
endTime = System.currentTimeMillis();
|
||||
}
|
||||
List<LiveDanmuDatabaseBean> danmuList = liveDatabase.getOfTime(DateFormatUtils.getInstance().format(startTime), DateFormatUtils.getInstance().format(endTime), LiveDanmuDatabaseBean.class);
|
||||
List<LiveSuperChatDatabaseBean> superChatList = liveDatabase.getOfTime(DateFormatUtils.getInstance().format(startTime), DateFormatUtils.getInstance().format(endTime), LiveSuperChatDatabaseBean.class);
|
||||
for (LiveDanmuDatabaseBean bean : danmuList) {
|
||||
LiveVideoDanmu.Danmu danmu = createDanmu(bean, startTime);
|
||||
danmus.getDanmu().add(danmu);
|
||||
}
|
||||
for (LiveSuperChatDatabaseBean bean : superChatList) {
|
||||
LiveVideoDanmu.SuperChat superChat = new LiveVideoDanmu.SuperChat(startTime, bean);
|
||||
danmus.getSuperChat().add(superChat);
|
||||
public LiveWebDanmuBean getDanmu(String roomId, String videoId, int page) {
|
||||
LiveWebDanmuBean danmus = new LiveWebDanmuBean();
|
||||
BiliLiveDatabase liveDatabase = liveDatabasesService.getLiveDatabase(roomId);
|
||||
try {
|
||||
LiveVideoDatabaseBean videoBean = liveDatabase.getVideo(videoId);
|
||||
if (videoBean == null) {
|
||||
return new LiveWebDanmuBean();
|
||||
}
|
||||
long startTime = videoBean.getStartTime().getTime();
|
||||
long endTime = videoBean.getStopTime() == null ? System.currentTimeMillis() : videoBean.getStopTime().getTime();
|
||||
|
||||
long count = liveDatabase.getCount(new LiveDanmuDatabaseBean().getTableName());
|
||||
int pageSize = 3000;
|
||||
int pageCount = (int) Math.ceil((double) count / pageSize);
|
||||
|
||||
List<LiveWebDanmuBean.Danmu> danmuList = liveDatabase.getDanmu(startTime, endTime, page, pageSize);
|
||||
danmus.getDanmu().addAll(danmuList);
|
||||
danmus.setDanmuCount(count);
|
||||
if (page < pageCount) {
|
||||
danmus.setNextDanmu(true);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(e);
|
||||
}
|
||||
|
||||
liveDatabase.close();
|
||||
return danmus;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static LiveVideoDanmu.Danmu createDanmu(LiveDanmuDatabaseBean bean, long startTime) {
|
||||
LiveVideoDanmu.Danmu danmu = new LiveVideoDanmu.Danmu();
|
||||
private static LiveWebDanmuBean.Danmu createDanmu(LiveDanmuDatabaseBean bean, long startTime) {
|
||||
LiveWebDanmuBean.Danmu danmu = new LiveWebDanmuBean.Danmu();
|
||||
danmu.setId(bean.getId() + "");
|
||||
danmu.setText(bean.getDanmu());
|
||||
danmu.setColor("#" + bean.getFontColor());
|
||||
danmu.setFontSize(bean.getFontSize());
|
||||
danmu.setTime(bean.getTime() - startTime);
|
||||
danmu.setTime(bean.getTime().getTime() - startTime);
|
||||
if (bean.getModel() < 4) {
|
||||
danmu.setBarrageType(LiveVideoDanmu.Danmu.DANMU_TYPE_SCROLL);
|
||||
danmu.setBarrageType(LiveWebDanmuBean.Danmu.DANMU_TYPE_SCROLL);
|
||||
} else if (bean.getModel() == 4) {
|
||||
danmu.setBarrageType(LiveVideoDanmu.Danmu.DANMU_TYPE_BOTTOM);
|
||||
danmu.setBarrageType(LiveWebDanmuBean.Danmu.DANMU_TYPE_BOTTOM);
|
||||
} else if (bean.getModel() == 5) {
|
||||
danmu.setBarrageType(LiveVideoDanmu.Danmu.DANMU_TYPE_TOP);
|
||||
danmu.setBarrageType(LiveWebDanmuBean.Danmu.DANMU_TYPE_TOP);
|
||||
}
|
||||
return danmu;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
BiliLiveDatabase database = new BiliLiveDatabase(LiveRoomConfig.buildConfig("17961"));
|
||||
BiliLiveDatabase database = new BiliLiveDatabase(LiveRoomConfig.buildConfigTmp("17961"));
|
||||
for (LiveVideoDatabaseBean info : database.getLiveInfos()) {
|
||||
System.out.println(info);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.yutou.bilibili.services;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.yutou.biliapi.bean.live.LiveRoomConfig;
|
||||
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||
import com.yutou.biliapi.databases.BiliLiveConfigDatabase;
|
||||
import com.yutou.biliapi.databases.BiliLiveDatabase;
|
||||
import com.yutou.common.utils.Log;
|
||||
import lombok.Getter;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Getter
|
||||
@Service
|
||||
public class LiveDatabasesService {
|
||||
private static final Cache<String, BiliLiveDatabase> liveDatabases = CacheBuilder.newBuilder()
|
||||
.maximumSize(1000)
|
||||
.expireAfterAccess(2, TimeUnit.MINUTES)
|
||||
.removalListener(it -> {
|
||||
if (it.wasEvicted()) {
|
||||
if (it.getValue() != null) {
|
||||
((BiliLiveDatabase) it.getValue()).close();
|
||||
Log.i("缓存到期移除", ((BiliLiveDatabase) it.getValue()).getConfig().getRoomId());
|
||||
}
|
||||
}
|
||||
})
|
||||
.build();
|
||||
private final BiliLiveConfigDatabase configDatabase;
|
||||
|
||||
private LiveDatabasesService() {
|
||||
configDatabase = new BiliLiveConfigDatabase();
|
||||
}
|
||||
public String getCacheInfo() {
|
||||
return "总数:" + liveDatabases.size() + "," + Arrays.toString(liveDatabases.asMap().keySet().toArray()) + "," + Arrays.toString(liveDatabases.asMap().values().toArray());
|
||||
}
|
||||
|
||||
public BiliLiveDatabase getLiveDatabase(String roomId) {
|
||||
try {
|
||||
return liveDatabases.get(roomId, () -> new BiliLiveDatabase(buildConfig(roomId)));
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void closeAll() {
|
||||
try {
|
||||
for (BiliLiveDatabase db : liveDatabases.asMap().values()) {
|
||||
db.close();
|
||||
}
|
||||
} finally {
|
||||
liveDatabases.invalidateAll();
|
||||
configDatabase.close();
|
||||
}
|
||||
}
|
||||
|
||||
public LiveRoomConfig buildConfig(String roomId) {
|
||||
LiveConfigDatabaseBean bean = configDatabase.getConfig(roomId);
|
||||
LiveRoomConfig config = new LiveRoomConfig();
|
||||
config.setLoginUid(bean.getRecordUid());
|
||||
config.setRoomId(bean.getRoomId());
|
||||
config.setAnchorName(bean.getAnchorName());
|
||||
config.setLogin(StringUtils.hasText(bean.getRecordUid()));
|
||||
config.setRootPath(bean.getRecordPath());
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -4,48 +4,44 @@ import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.yutou.biliapi.api.LiveApi;
|
||||
import com.yutou.biliapi.bean.live.LiveAnchorInfo;
|
||||
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.databases.BiliLiveConfigDatabase;
|
||||
import com.yutou.biliapi.bean.live.database.*;
|
||||
import com.yutou.biliapi.databases.BiliLiveDatabase;
|
||||
import com.yutou.biliapi.net.BiliLiveNetApiManager;
|
||||
import com.yutou.bilibili.Tools.DateFormatUtils;
|
||||
import com.yutou.bilibili.datas.web.LiveData;
|
||||
import com.yutou.common.okhttp.BaseBean;
|
||||
import com.yutou.bilibili.datas.web.LiveWebDanmuBean;
|
||||
import com.yutou.bilibili.datas.web.SuperChatGiftBean;
|
||||
import com.yutou.common.okhttp.HttpLoggingInterceptor;
|
||||
import com.yutou.common.utils.FFmpegUtils;
|
||||
import com.yutou.common.utils.Log;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class LiveService {
|
||||
BiliLiveConfigDatabase liveConfigDatabase;
|
||||
@Resource
|
||||
LiveVideoDownloadService videoDownloadService;
|
||||
@Resource
|
||||
LiveDanmuService danmuService;
|
||||
@Resource
|
||||
LiveDatabasesService databasesService;
|
||||
LiveApi api;
|
||||
|
||||
|
||||
public LiveService() {
|
||||
liveConfigDatabase = new BiliLiveConfigDatabase();
|
||||
api = BiliLiveNetApiManager.getInstance().getApi(null);
|
||||
}
|
||||
|
||||
public int getConfigCount() {
|
||||
return liveConfigDatabase.getAllConfig().size();
|
||||
return databasesService.getConfigDatabase().getAllConfig().size();
|
||||
}
|
||||
|
||||
public List<LiveData> getLiveList(int page, int limit) {
|
||||
List<LiveConfigDatabaseBean> allConfig = liveConfigDatabase.getAllConfig();
|
||||
List<LiveConfigDatabaseBean> allConfig = databasesService.getConfigDatabase().getAllConfig();
|
||||
List<LiveData> liveDataList = new ArrayList<>();
|
||||
if (allConfig.isEmpty()) {
|
||||
return liveDataList;
|
||||
@@ -87,7 +83,7 @@ public class LiveService {
|
||||
if (info.getLiveTime() == 0) {
|
||||
liveData.setLiveTime("未开播");
|
||||
} else {
|
||||
liveData.setLiveTime(DateFormatUtils.getInstance().formatMillis(System.currentTimeMillis() - info.getLiveTime()*1000));
|
||||
liveData.setLiveTime(DateFormatUtils.getInstance().formatMillis(System.currentTimeMillis() - info.getLiveTime() * 1000));
|
||||
}
|
||||
liveData.setDownloadVideo(videoDownloadService.checkDownload(info.getRoomId()));
|
||||
liveData.setDanmu(danmuService.check(info.getRoomId()));
|
||||
@@ -108,22 +104,63 @@ public class LiveService {
|
||||
}
|
||||
|
||||
public JSONObject getGiftInfo(String roomId, String videoId) {
|
||||
BiliLiveDatabase database = new BiliLiveDatabase(LiveRoomConfig.buildConfig(roomId));
|
||||
BiliLiveDatabase database = databasesService.getLiveDatabase(roomId);
|
||||
LiveVideoDatabaseBean videoBean = database.getVideo(videoId);
|
||||
if (videoBean == null) {
|
||||
return null;
|
||||
}
|
||||
File videoFile = new File(videoBean.getPath().replace(".flv", ".mp4"));
|
||||
if (!videoFile.exists()) {
|
||||
videoFile = new File(videoBean.getPath());
|
||||
long startTime = videoBean.getStartTime().getTime();
|
||||
long endTime = videoBean.getStopTime() == null ? System.currentTimeMillis() : videoBean.getStopTime().getTime();
|
||||
List<SuperChatGiftBean> superChatList = database.getOfTime(startTime, endTime, LiveSuperChatDatabaseBean.class)
|
||||
.stream().map(it -> new SuperChatGiftBean(startTime, it)).toList();
|
||||
JSONObject giftInfo = database.getGiftInfo(startTime, endTime);
|
||||
giftInfo.put("superChat", superChatList);
|
||||
return giftInfo;
|
||||
}
|
||||
|
||||
public JSONArray getGiftItemInfo(String roomId, String videoId, String giftName) {
|
||||
BiliLiveDatabase database = databasesService.getLiveDatabase(roomId);
|
||||
LiveVideoDatabaseBean videoBean = database.getVideo(videoId);
|
||||
if (videoBean == null) {
|
||||
return new JSONArray();
|
||||
}
|
||||
long videoTime = FFmpegUtils.getVideoTime(videoFile);
|
||||
long startTime = Long.parseLong(videoId);
|
||||
long endTime = Long.parseLong(videoId) + videoTime;
|
||||
if(videoTime==0){
|
||||
endTime=System.currentTimeMillis();
|
||||
long startTime = videoBean.getStartTime().getTime();
|
||||
long endTime = videoBean.getStopTime() == null ? System.currentTimeMillis() : videoBean.getStopTime().getTime();
|
||||
String where = " `sql_time` >= " + "\"" + startTime + "\"" +
|
||||
" and " + " `sql_time` <= " + "\"" + endTime + "\"" +
|
||||
" and " + " `gift_name` = " + "\"" + giftName + "\"";
|
||||
List<LiveGiftDatabaseBean> list = database.get(new LiveGiftDatabaseBean().getTableName(), where, LiveGiftDatabaseBean.class);
|
||||
List<LiveGuardBuyBean> list2 = database.get(new LiveGuardBuyBean().getTableName(), where, LiveGuardBuyBean.class);
|
||||
JSONArray array = new JSONArray();
|
||||
array.addAll(list);
|
||||
array.addAll(
|
||||
list2.stream().map(it -> {
|
||||
LiveGiftDatabaseBean tmp = new LiveGiftDatabaseBean();
|
||||
tmp.setGiftId(it.getGiftID());
|
||||
tmp.setGiftName(it.getGiftName());
|
||||
tmp.setGiftNum(it.getNum());
|
||||
tmp.setPrice(it.getPrice());
|
||||
tmp.setIcon("/assets/def.png");
|
||||
tmp.setSenderUid(it.getUid());
|
||||
tmp.setSenderName(it.getUsername());
|
||||
tmp.setSenderFace("/assets/def.png");
|
||||
tmp.setSql_time(it.getSql_time());
|
||||
return tmp;
|
||||
}).toList()
|
||||
);
|
||||
|
||||
return array;
|
||||
|
||||
}
|
||||
|
||||
public static boolean checkLive(String roomId) {
|
||||
try {
|
||||
LiveRoomInfo data = BiliLiveNetApiManager.getInstance().getApi(null).getRoomInfo(roomId).execute().body().getData();
|
||||
return data.getLiveStatus() == 1;
|
||||
} catch (IOException|NullPointerException e) {
|
||||
Log.e(e);
|
||||
return false;
|
||||
}
|
||||
return database.getGiftInfo(startTime, endTime);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -132,4 +169,6 @@ public class LiveService {
|
||||
List<LiveData> data = service.getLiveList(1, 16);
|
||||
System.out.println(data.size());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.yutou.bilibili.services;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.yutou.biliapi.api.UserApi;
|
||||
import com.yutou.biliapi.bean.live.FollowLive;
|
||||
import com.yutou.biliapi.bean.live.SpiBean;
|
||||
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||
import com.yutou.biliapi.bean.login.LoginCookieDatabaseBean;
|
||||
@@ -9,7 +9,7 @@ import com.yutou.biliapi.bean.user.UserFollowingsBean;
|
||||
import com.yutou.biliapi.bean.user.UserHomeInfoBean;
|
||||
import com.yutou.biliapi.bean.user.UserInfoBean;
|
||||
import com.yutou.biliapi.databases.BiliBiliLoginDatabase;
|
||||
import com.yutou.biliapi.net.BiliCookieManager;
|
||||
import com.yutou.biliapi.net.BiliLiveNetApiManager;
|
||||
import com.yutou.biliapi.net.BiliUserNetApiManager;
|
||||
import com.yutou.biliapi.net.WebSignManager;
|
||||
import com.yutou.bilibili.datas.ResultData;
|
||||
@@ -34,6 +34,14 @@ public class LiveUserService {
|
||||
@Resource
|
||||
LiveConfigService configService;
|
||||
|
||||
public List<FollowLive.Room> getUserFollowLive(String userId) {
|
||||
try {
|
||||
FollowLive followLive = BiliLiveNetApiManager.getInstance().getApi(userId).getUserFollowLive(true, System.currentTimeMillis()).execute().body().getData();
|
||||
return followLive.getRooms();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
public UserFollowingsBean getUserFollowings(String userId, int page, int num) {
|
||||
LoginCookieDatabaseBean cookie = userLoginService.getCookie(userId);
|
||||
UserApi api = BiliUserNetApiManager.getInstance().getUserApi(cookie);
|
||||
|
||||
@@ -18,11 +18,9 @@ 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.WebSignManager;
|
||||
import com.yutou.biliapi.net.WebSocketServer;
|
||||
import com.yutou.bilibili.Tools.DateFormatUtils;
|
||||
import com.yutou.bilibili.Tools.FileServerUtils;
|
||||
import com.yutou.bilibili.Tools.LiveInfoNfoTools;
|
||||
import com.yutou.bilibili.Tools.ProcessUtils;
|
||||
import com.yutou.bilibili.datas.VideoFilePath;
|
||||
import com.yutou.bilibili.interfaces.DownloadInterface;
|
||||
import com.yutou.common.okhttp.HttpCallback;
|
||||
@@ -31,14 +29,16 @@ 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 jakarta.annotation.Resource;
|
||||
import okhttp3.Headers;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -49,6 +49,10 @@ public class LiveVideoDownloadService {
|
||||
private final ThreadPoolExecutor executor;
|
||||
private final List<String> userStopList = new ArrayList<>();//手动停止列表
|
||||
private final AbsVideoRecord videoRecord;
|
||||
@Resource
|
||||
LiveDatabasesService liveDatabasesService;
|
||||
@Resource
|
||||
WebSocketServer webSocketServer;
|
||||
|
||||
public LiveVideoDownloadService() {
|
||||
Log.i("初始化下载服务");
|
||||
@@ -63,21 +67,40 @@ public class LiveVideoDownloadService {
|
||||
public void clearUserStopList() {
|
||||
userStopList.clear();
|
||||
}
|
||||
public void removeUserStopList(String roomId) {
|
||||
userStopList.remove(roomId);
|
||||
}
|
||||
public boolean isUserStopList(String roomId) {
|
||||
return userStopList.contains(roomId);
|
||||
}
|
||||
|
||||
public void start(LiveConfigDatabaseBean bean, boolean isUser) {
|
||||
if (!isUser && userStopList.contains(bean.getRoomId().toString())) {
|
||||
if (!isUser && userStopList.contains(bean.getRoomId())) {
|
||||
return;
|
||||
}
|
||||
if (isUser) {
|
||||
userStopList.remove(bean.getRoomId().toString());
|
||||
userStopList.remove(bean.getRoomId());
|
||||
}
|
||||
if (videoRecord.check(bean.getRoomId().toString())) {
|
||||
if (videoRecord.check(bean.getRoomId())) {
|
||||
return;
|
||||
}
|
||||
BiliLiveNetApiManager.getInstance().getApi(bean.getRecordUid()).getRoomInfo(bean.getRoomId().toString()).enqueue(new HttpCallback<LiveRoomInfo>() {
|
||||
BiliLiveNetApiManager.getInstance().getApi(bean.getRecordUid()).getRoomInfo(bean.getRoomId()).enqueue(new HttpCallback<LiveRoomInfo>() {
|
||||
@Override
|
||||
public void onResponse(Headers headers, int code, String status, LiveRoomInfo response, String rawResponse) {
|
||||
if (response.getLiveStatus() == 1) {
|
||||
if (bean.getKeywordList()!=null&&!bean.getKeywordList().isEmpty() && !isUser) {
|
||||
String foundKey = bean.getKeywordList().stream()
|
||||
.filter(key -> response.getTitle().contains(key))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
if (foundKey != null) {
|
||||
Log.i(response.getRoomId(), "检测到开播关键词", foundKey, response.getTitle());
|
||||
} else {
|
||||
Log.i(response.getRoomId(), "未检测到关键词", response.getTitle(), Arrays.toString(bean.getKeywordList().toArray()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
VideoTask task = new VideoTask(bean, response);
|
||||
executor.execute(task);
|
||||
} else {
|
||||
@@ -119,8 +142,7 @@ public class LiveVideoDownloadService {
|
||||
String savePath;
|
||||
File rootPath;
|
||||
LiveConfigDatabaseBean config;
|
||||
BiliLiveDatabase database;
|
||||
LiveVideoDatabaseBean videoDatabaseBean;
|
||||
LiveVideoDatabaseBean videoDatabaseBean = null;
|
||||
LiveRoomInfo roomInfo;
|
||||
|
||||
public VideoTask(LiveConfigDatabaseBean bean, LiveRoomInfo roomInfo) {
|
||||
@@ -134,10 +156,10 @@ public class LiveVideoDownloadService {
|
||||
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 + "[" +
|
||||
rootPath = new File(bean.getRecordPath() + File.separator + bean.getAnchorName() + File.separator + time + File.separator + "[" +
|
||||
DateUtils.format(new Date(),
|
||||
"yyyy-MM-dd HH-mm-ss") + "]" + roomInfo.getTitle() + ".flv";
|
||||
"HH-mm-ss") + "]" + roomInfo.getTitle());
|
||||
savePath = rootPath.getAbsolutePath() + File.separator + roomInfo.getTitle() + ".m3u8";
|
||||
if (!rootPath.exists()) {
|
||||
rootPath.mkdirs();
|
||||
}
|
||||
@@ -148,14 +170,14 @@ public class LiveVideoDownloadService {
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
videoRecord.kill(bean.getRoomId().toString());
|
||||
api.getRoomInfo(config.getRoomId().toString()).enqueue(new HttpCallback<LiveRoomInfo>() {
|
||||
videoRecord.kill(bean.getRoomId());
|
||||
api.getRoomInfo(config.getRoomId()).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);
|
||||
LiveVideoDownloadService.this.stop(bean.getRoomId(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,21 +199,9 @@ public class LiveVideoDownloadService {
|
||||
config.setLogin(StringUtils.hasText(bean.getRecordUid()));
|
||||
config.setRoomInfo(roomInfo);
|
||||
config.setRootPath(bean.getRecordPath());
|
||||
database = new BiliLiveDatabase(config);
|
||||
try {
|
||||
var keyframe = roomInfo.getKeyframe();
|
||||
if (!StringUtils.hasText(keyframe)) {
|
||||
keyframe = roomInfo.getUserCover();
|
||||
}
|
||||
HttpDownloadUtils.download(new HttpDownloadUtils.Builder().setUrl(keyframe)
|
||||
.setPath(rootPath.getAbsolutePath())
|
||||
.setFileName("poster.jpg"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
saveLiveInfo(roomInfo);
|
||||
api.getLiveRoomPlayInfo(
|
||||
bean.getRoomId().toString(),
|
||||
bean.getRoomId(),
|
||||
LiveProtocol.getAll(),
|
||||
LiveVideoFormat.getAll(),
|
||||
LiveVideoCodec.getAll(),
|
||||
@@ -220,7 +230,7 @@ public class LiveVideoDownloadService {
|
||||
|
||||
|
||||
String url = urlInfo.getHost() + codec.getBaseUrl() + urlInfo.getExtra();
|
||||
Log.i("下载直播",rawResponse,codec.toString(),urlInfo.toString(),"URL:"+url);
|
||||
Log.i("下载直播", rawResponse, codec.toString(), urlInfo.toString(), "URL:" + url);
|
||||
|
||||
if (bean.getRecordLiveModel() == 1) {
|
||||
javaRecord(url, response);
|
||||
@@ -285,20 +295,24 @@ public class LiveVideoDownloadService {
|
||||
// .withNotSymbolParam("-loglevel", "error")
|
||||
// .withNotSymbolParam("-progress", "-")
|
||||
// .withNotSymbolParam("-fflags", "+genpts")
|
||||
.withNotSymbolParam("-threads", "8")
|
||||
.withNotSymbolParam("-bufsize", "10M")
|
||||
// .withNotSymbolParam("-threads", "8")//看bili-go也没有加这个,改成设置好了
|
||||
// .withNotSymbolParam("-bufsize", "10M")
|
||||
.withNotSymbolParam("-f", "segment")
|
||||
.withNotSymbolParam("-rw_timeout", "60000000")
|
||||
.withNotSymbolParam("-segment_time", "60")
|
||||
.withNotSymbolParam("-segment_format", "mpegts")
|
||||
.withNotSymbolParam("-map", "0")
|
||||
.withParam("-segment_list", savePath)
|
||||
.withNotSymbolParam("-c", "copy")
|
||||
.withNotSymbolParam("-bsf:a", "aac_adtstoasc")
|
||||
// .withNotSymbolParam("-loglevel", "debug")
|
||||
.withNotSymbolParam("-y", "")
|
||||
//-reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 2
|
||||
|
||||
// .withNotSymbolParam("-progress",new File("cache",config.getRoomId()+".txt").getAbsolutePath()); //输出进度日志,暂时没啥用
|
||||
;
|
||||
if (ck != null) {
|
||||
// builder = builder.withParam("-cookies", cookie);
|
||||
}
|
||||
FFmpegUtils command = builder.build(config.getRoomId(), ffmpegPath, url, savePath);
|
||||
FFmpegUtils command = builder.build(config.getRoomId(), ffmpegPath, url, savePath.replace(".m3u8", "-%04d.ts"));
|
||||
Log.i(command.getCommandDecode());
|
||||
try {
|
||||
command.start(new DownloadInterface() {
|
||||
@@ -313,6 +327,7 @@ public class LiveVideoDownloadService {
|
||||
VideoTask.this.onStart();
|
||||
Log.i("启动录制:" + playInfo.getRoomId());
|
||||
task = null;
|
||||
|
||||
cancel();
|
||||
}
|
||||
};
|
||||
@@ -330,7 +345,18 @@ public class LiveVideoDownloadService {
|
||||
if (task != null) {
|
||||
task.cancel();
|
||||
task = null;
|
||||
try {
|
||||
FileUtils.deleteDirectory(rootPath);
|
||||
} catch (IOException e) {
|
||||
Log.i(rootPath.getAbsolutePath());
|
||||
Log.e(e);
|
||||
}
|
||||
}
|
||||
if (videoDatabaseBean != null) {
|
||||
videoDatabaseBean.setStopTime(new Date());
|
||||
liveDatabasesService.getLiveDatabase(bean.getRoomId()).addLiveInfo(videoDatabaseBean);
|
||||
}
|
||||
stopRecordDanmu();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -345,9 +371,22 @@ public class LiveVideoDownloadService {
|
||||
videoDatabaseBean.setPath(savePath);
|
||||
videoDatabaseBean.setRoomInfoJson(JSONObject.toJSONString(roomInfo));
|
||||
videoDatabaseBean.setStartTime(new Date());
|
||||
database.addLiveInfo(videoDatabaseBean);
|
||||
liveDatabasesService.getLiveDatabase(bean.getRoomId()).addLiveInfo(videoDatabaseBean);
|
||||
recordDanmu();
|
||||
// LiveInfoNfoTools.saveLiveInfoNfo(roomInfo, rootPath.getAbsolutePath(), new File(savePath).getName().replace(".flv", ".nfo"));
|
||||
}
|
||||
|
||||
//录制弹幕
|
||||
private void recordDanmu() {
|
||||
if (bean.isSyncDanmuForLive() && !webSocketServer.checkRoom(liveDatabasesService.buildConfig(bean.getRoomId()))) {
|
||||
webSocketServer.addRoom(liveDatabasesService.buildConfig(bean.getRoomId()), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void stopRecordDanmu() {
|
||||
if (bean.isSyncDanmuForLive() && webSocketServer.checkRoom(liveDatabasesService.buildConfig(bean.getRoomId()))) {
|
||||
webSocketServer.stopRoom(bean.getRoomId(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,22 +395,18 @@ public class LiveVideoDownloadService {
|
||||
}
|
||||
|
||||
public VideoFilePath getVideoPath(String roomId) {
|
||||
BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase();
|
||||
LiveConfigDatabaseBean bean = configDatabase.getConfig(roomId);
|
||||
configDatabase.close();
|
||||
LiveConfigDatabaseBean bean = liveDatabasesService.getConfigDatabase().getConfig(roomId);
|
||||
return getVideoFilePath(bean);
|
||||
}
|
||||
|
||||
private VideoFilePath getVideoFilePath(LiveConfigDatabaseBean configBean) {
|
||||
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();
|
||||
List<LiveVideoDatabaseBean> infos = liveDatabasesService.getLiveDatabase(configBean.getRoomId()).getLiveInfos();
|
||||
path.setChildren(getVideoInfo(infos));
|
||||
}
|
||||
database.close();
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -409,23 +444,21 @@ public class LiveVideoDownloadService {
|
||||
|
||||
public String getVideoPlay(String roomId, String videoId) {
|
||||
String ffmpegPath = ConfigTools.load(ConfigTools.CONFIG, "ffmpeg", String.class);
|
||||
BiliLiveConfigDatabase configDatabase = new BiliLiveConfigDatabase();
|
||||
LiveConfigDatabaseBean config = configDatabase.getConfig(roomId);
|
||||
LiveConfigDatabaseBean config = liveDatabasesService.getConfigDatabase().getConfig(roomId);
|
||||
String recordPath = config.getRecordPath() + File.separator + config.getAnchorName();
|
||||
configDatabase.close();
|
||||
LiveVideoDatabaseBean videoInfo = null;
|
||||
BiliLiveDatabase liveDatabase = new BiliLiveDatabase(LiveRoomConfig.buildConfig(roomId), new File(recordPath).getAbsolutePath());
|
||||
for (LiveVideoDatabaseBean info : liveDatabase.getLiveInfos()) {
|
||||
for (LiveVideoDatabaseBean info : liveDatabasesService.getLiveDatabase(roomId).getLiveInfos()) {
|
||||
if (videoId.trim().equals(String.valueOf(info.getSql_time().getTime()))) {
|
||||
videoInfo = info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
liveDatabase.close();
|
||||
if (videoInfo != null) {
|
||||
File videoFile = new File(videoInfo.getPath().replace(".flv", ".mp4"));
|
||||
File videoFile = new File(videoInfo.getPath().replace("-%04d.ts", ".m3u8"));
|
||||
if (!videoFile.exists()) {
|
||||
videoFile = new File(videoInfo.getPath());
|
||||
} else {
|
||||
return videoInfo.getPath().replace(new File("live").getAbsolutePath(), "").replace(File.separator, "/").replace("-%04d.ts", ".m3u8");
|
||||
}
|
||||
FFmpegUtils ffmpeg = FFmpegUtils.segment(videoId, ffmpegPath, videoFile, ConfigTools.load(ConfigTools.CONFIG, "outVideoPath", String.class));
|
||||
System.out.println(ffmpeg.getCommandDecode());
|
||||
|
||||
@@ -22,10 +22,12 @@ public class SystemService {
|
||||
LiveVideoDownloadService videoService;
|
||||
@Resource
|
||||
LiveDanmuService danmuService;
|
||||
@Resource
|
||||
LiveDatabasesService databasesService;
|
||||
|
||||
SystemConfigDatabases databases = new SystemConfigDatabases();
|
||||
private ScheduledExecutorService timer;
|
||||
private ScheduledFuture<?> scheduled;
|
||||
BiliLiveConfigDatabase liveConfigDatabase = new BiliLiveConfigDatabase();
|
||||
|
||||
public long getLoopTimer() {
|
||||
SystemConfigDatabaseBean config = databases.getConfig();
|
||||
@@ -35,7 +37,6 @@ public class SystemService {
|
||||
return config.getTimerLoop();
|
||||
}
|
||||
|
||||
private final String resetTimer = "00:00:00 - 00:01:00";
|
||||
|
||||
public void start() {
|
||||
if (timer == null) {
|
||||
@@ -46,20 +47,23 @@ public class SystemService {
|
||||
scheduled.cancel(true);
|
||||
}
|
||||
scheduled = timer.scheduleAtFixedRate(() -> {
|
||||
List<LiveConfigDatabaseBean> list = liveConfigDatabase.getAllConfig();
|
||||
List<LiveConfigDatabaseBean> list = databasesService.getConfigDatabase().getAllConfig();
|
||||
Log.i("循环任务:" + list.size());
|
||||
if (DateFormatUtils.getInstance().checkTime(null, resetTimer)) {
|
||||
videoService.clearUserStopList();
|
||||
danmuService.clearUserList();
|
||||
}
|
||||
for (LiveConfigDatabaseBean bean : list) {
|
||||
try {
|
||||
if (bean.isRecordDanmu() && bean.checkRecordDanmuTime()) {
|
||||
// 如果bean需要录制弹幕,并且检查录制弹幕时间,并且不需要同步直播弹幕,则录制弹幕
|
||||
if (bean.isRecordDanmu() && bean.checkRecordDanmuTime() && !bean.isSyncDanmuForLive()) {
|
||||
recordDanmu(bean);
|
||||
} else if (!bean.checkRecordDanmuTime()) {
|
||||
} else if (!bean.checkRecordDanmuTime() && !bean.isSyncDanmuForLive()) {
|
||||
// 如果不在录制弹幕时间,并且不需要同步直播弹幕,则停止录制弹幕
|
||||
stopRecordDanmu(bean);
|
||||
}
|
||||
// 如果bean需要录制直播,并且检查录制直播时间
|
||||
if (bean.isRecordLive() && bean.checkRecordLiveTime()) {
|
||||
if (bean.isSyncDanmuForLive() && !danmuService.check(bean.getRoomId()) && videoService.checkDownload(bean.getRoomId())) {
|
||||
recordDanmu(bean);
|
||||
}
|
||||
// 录制视频
|
||||
recordVideo(bean);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -77,13 +81,15 @@ public class SystemService {
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
scheduled.cancel(true);
|
||||
if(scheduled!=null) {
|
||||
scheduled.cancel(true);
|
||||
}
|
||||
videoService.stopAll();
|
||||
}
|
||||
|
||||
// 录制弹幕
|
||||
private void recordDanmu(LiveConfigDatabaseBean bean) {
|
||||
danmuService.start(bean.getRoomId().toString(), false);
|
||||
danmuService.start(bean, false);
|
||||
}
|
||||
|
||||
// 录制视频
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.alibaba.fastjson2.JSONObject;
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import com.alibaba.fastjson2.util.DateUtils;
|
||||
import com.yutou.biliapi.bean.live.database.LiveConfigDatabaseBean;
|
||||
import com.yutou.bilibili.Tools.DateFormatUtils;
|
||||
import com.yutou.common.inter.ISqlDatabaseBean;
|
||||
import com.yutou.common.utils.Log;
|
||||
import lombok.Data;
|
||||
@@ -87,7 +88,7 @@ public abstract class SQLiteManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
public synchronized void close() {
|
||||
try {
|
||||
conn.close();
|
||||
} catch (SQLException throwables) {
|
||||
@@ -103,134 +104,123 @@ public abstract class SQLiteManager {
|
||||
}
|
||||
}
|
||||
|
||||
protected <T extends AbsDatabasesBean> void add(T t) {
|
||||
protected synchronized <T extends AbsDatabasesBean> void add(T t) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
PreparedStatement statement = null;
|
||||
try {
|
||||
|
||||
StringBuilder value = new StringBuilder();
|
||||
sb.append("INSERT INTO `").append(t.getTableName()).append("` ");
|
||||
sb.append("(");
|
||||
value.append("(");
|
||||
try (PreparedStatement statement = getConnection().prepareStatement(buildInsertSql(t))) {
|
||||
JSONObject json = t.toJson();
|
||||
Set<String> keySet = json.keySet();
|
||||
for (String key : keySet) {
|
||||
if ("id".equals(key)) {
|
||||
continue;
|
||||
}
|
||||
sb.append("`").append(key).append("`,");
|
||||
//value.append("'").append(json.get(key)).append("',");
|
||||
value.append("?").append(",");
|
||||
}
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
value.deleteCharAt(value.length() - 1);
|
||||
value.append(")");
|
||||
sb.append(") VALUES ");
|
||||
sb.append(value);
|
||||
statement = getConnection().prepareStatement(sb.toString());
|
||||
int i = 1;
|
||||
for (String key : keySet) {
|
||||
if ("id".equals(key)) {
|
||||
continue;
|
||||
}
|
||||
Object value = json.get(key);
|
||||
if ("sql_time".equals(key)) {
|
||||
statement.setLong(i++, json.getDate(key).getTime());
|
||||
statement.setLong(i++, DateFormatUtils.getInstance().parse(value.toString(),DateFormatUtils.DEFAULT_PATTERN).getTime());
|
||||
continue;
|
||||
}
|
||||
if (json.get(key) instanceof String) {
|
||||
statement.setString(i++, json.get(key).toString());
|
||||
} else if (json.get(key) instanceof Integer) {
|
||||
statement.setInt(i++, json.getInteger(key));
|
||||
} else if (json.get(key) instanceof Long) {
|
||||
statement.setLong(i++, json.getLong(key));
|
||||
} else if (json.get(key) instanceof Boolean) {
|
||||
statement.setBoolean(i++, json.getBoolean(key));
|
||||
} else if (json.get(key) instanceof Date) {
|
||||
statement.setLong(i++, json.getDate(key).getTime());
|
||||
if (value instanceof String) {
|
||||
statement.setString(i++, (String) value);
|
||||
} else if (value instanceof Integer) {
|
||||
statement.setInt(i++, (Integer) value);
|
||||
} else if (value instanceof Long) {
|
||||
statement.setLong(i++, (Long) value);
|
||||
} else if (value instanceof Boolean) {
|
||||
statement.setBoolean(i++, (Boolean) value);
|
||||
} else if (value instanceof Date) {
|
||||
statement.setLong(i++, (Long) value);
|
||||
} else {
|
||||
statement.setObject(i++, json.get(key));
|
||||
statement.setObject(i++, value);
|
||||
}
|
||||
}
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
Log.e(e, sb);
|
||||
Log.e(e, sb.toString());
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
try {
|
||||
if (statement != null) {
|
||||
statement.closeOnCompletion();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected <T extends AbsDatabasesBean> void update(T t) {
|
||||
PreparedStatement statement = null;
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
StringBuilder setPart = new StringBuilder();
|
||||
private String buildInsertSql(AbsDatabasesBean bean) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
StringBuilder value = new StringBuilder();
|
||||
sb.append("INSERT INTO `").append(bean.getTableName()).append("` ");
|
||||
sb.append("(");
|
||||
value.append("(");
|
||||
JSONObject json = bean.toJson();
|
||||
Set<String> keySet = json.keySet();
|
||||
for (String key : keySet) {
|
||||
if ("id".equals(key)) {
|
||||
continue;
|
||||
}
|
||||
sb.append("`").append(key).append("`,");
|
||||
value.append("?").append(",");
|
||||
}
|
||||
sb.deleteCharAt(sb.length() - 1); // 删除最后一个逗号
|
||||
value.deleteCharAt(value.length() - 1); // 删除最后一个逗号
|
||||
value.append(")");
|
||||
sb.append(") VALUES ").append(value);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected synchronized <T extends AbsDatabasesBean> void update(T t) {
|
||||
try (PreparedStatement statement = getConnection().prepareStatement(buildUpdateSql(t))) {
|
||||
JSONObject json = t.toJson();
|
||||
Set<String> keySet = json.keySet();
|
||||
|
||||
for (String key : keySet) {
|
||||
if ("id".equals(key) || "sql_time".equals(key)) {
|
||||
continue;
|
||||
}
|
||||
setPart.append("`").append(key).append("` = ?, ");
|
||||
}
|
||||
|
||||
setPart.deleteCharAt(setPart.length() - 2);
|
||||
|
||||
sb.append("UPDATE `").append(t.getTableName()).append("` ");
|
||||
sb.append("SET ").append(setPart);
|
||||
sb.append(" WHERE `sql_time` = ?");
|
||||
|
||||
statement = getConnection().prepareStatement(sb.toString());
|
||||
|
||||
int i = 1;
|
||||
for (String key : keySet) {
|
||||
if ("id".equals(key) || "sql_time".equals(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (json.get(key) instanceof String) {
|
||||
statement.setString(i++, json.getString(key));
|
||||
} else if (json.get(key) instanceof Integer) {
|
||||
statement.setInt(i++, json.getInteger(key));
|
||||
} else if (json.get(key) instanceof Long) {
|
||||
statement.setLong(i++, json.getLong(key));
|
||||
} else if (json.get(key) instanceof Boolean) {
|
||||
statement.setBoolean(i++, json.getBoolean(key));
|
||||
} else if (json.get(key) instanceof Date) {
|
||||
statement.setLong(i++, json.getDate(key).getTime());
|
||||
Object value = json.get(key);
|
||||
if (value instanceof String) {
|
||||
statement.setString(i++, (String) value);
|
||||
} else if (value instanceof Integer) {
|
||||
statement.setInt(i++, (Integer) value);
|
||||
} else if (value instanceof Long) {
|
||||
statement.setLong(i++, (Long) value);
|
||||
} else if (value instanceof Boolean) {
|
||||
statement.setBoolean(i++, (Boolean) value);
|
||||
} else if (value instanceof Date) {
|
||||
statement.setTimestamp(i++, new Timestamp(((Date) value).getTime()));
|
||||
} else {
|
||||
statement.setObject(i++, json.get(key));
|
||||
statement.setObject(i++, value);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置 sql_time 的值
|
||||
long id = t.getSql_time().getTime();
|
||||
statement.setLong(i, id);
|
||||
long sqlTime = t.getSql_time().getTime();
|
||||
statement.setLong(i, sqlTime);
|
||||
|
||||
statement.executeUpdate();
|
||||
|
||||
} catch (SQLException e) {
|
||||
Log.e(e);
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
try {
|
||||
if (statement != null) {
|
||||
statement.closeOnCompletion();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
Log.e(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String buildUpdateSql(AbsDatabasesBean bean) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
StringBuilder setPart = new StringBuilder();
|
||||
|
||||
JSONObject json = bean.toJson();
|
||||
Set<String> keySet = json.keySet();
|
||||
|
||||
for (String key : keySet) {
|
||||
if ("id".equals(key) || "sql_time".equals(key)) {
|
||||
continue;
|
||||
}
|
||||
setPart.append("`").append(key).append("` = ?, ");
|
||||
}
|
||||
|
||||
setPart.deleteCharAt(setPart.length() - 2); // 删除最后一个逗号和空格
|
||||
|
||||
sb.append("UPDATE `").append(bean.getTableName()).append("` ");
|
||||
sb.append("SET ").append(setPart);
|
||||
sb.append(" WHERE `sql_time` = ?");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
protected JSONArray getJSONArray(String table) {
|
||||
return getJSONArray(table, null);
|
||||
}
|
||||
@@ -377,14 +367,13 @@ public abstract class SQLiteManager {
|
||||
return json;
|
||||
}
|
||||
|
||||
protected <T extends AbsDatabasesBean> boolean delete(T t) {
|
||||
protected synchronized <T extends AbsDatabasesBean> boolean delete(T t) {
|
||||
Statement statement = null;
|
||||
try {
|
||||
String id = DateUtils.format(t.getSql_time(), "yyyy-MM-dd HH:mm:ss.SSS");
|
||||
statement = getConnection().createStatement();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("DELETE FROM `").append(t.getTableName()).append("` ");
|
||||
sb.append(" WHERE `sql_time` = ").append("'").append(id).append("'");
|
||||
sb.append(" WHERE `sql_time` = ").append("'").append(t.getSql_time().getTime()).append("'");
|
||||
int ret = statement.executeUpdate(sb.toString());
|
||||
return ret != 0;
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -8,13 +8,14 @@ import okio.Buffer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class HttpLoggingInterceptor implements Interceptor {
|
||||
private static final String TAG = "HttpLogging";
|
||||
|
||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
private static final Charset UTF8 = StandardCharsets.UTF_8;
|
||||
|
||||
private volatile Level printLevel = Level.BODY;
|
||||
private java.util.logging.Level colorLevel;
|
||||
@@ -51,7 +52,7 @@ public class HttpLoggingInterceptor implements Interceptor {
|
||||
private void log(String message) {
|
||||
//logger.log(colorLevel, message);
|
||||
if (prLog) {
|
||||
Log.i(TAG, message);
|
||||
Log.getDynamicLogger(TAG).info(message);
|
||||
}
|
||||
//Log.e(TAG,message);
|
||||
}
|
||||
|
||||
123
src/main/java/com/yutou/common/utils/DynamicLogFile.java
Normal file
123
src/main/java/com/yutou/common/utils/DynamicLogFile.java
Normal file
@@ -0,0 +1,123 @@
|
||||
package com.yutou.common.utils;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.yutou.bilibili.Tools.DateFormatUtils;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
import org.apache.logging.log4j.core.Layout;
|
||||
import org.apache.logging.log4j.core.LoggerContext;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.core.appender.rolling.*;
|
||||
import org.apache.logging.log4j.core.config.Configuration;
|
||||
import org.apache.logging.log4j.core.appender.RollingFileAppender;
|
||||
import org.apache.logging.log4j.core.layout.PatternLayout;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class DynamicLogFile {
|
||||
// 创建一个缓存,用于存储Logger对象,最大容量为1000,过期时间为10分钟
|
||||
static Cache<String, Logger> cache = CacheBuilder.newBuilder()
|
||||
.maximumSize(1000)
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.removalListener(it -> {
|
||||
if (it.wasEvicted()) {
|
||||
if (it.getKey() != null) {
|
||||
String loggerName = (String) it.getKey();
|
||||
remove(loggerName, true);
|
||||
}
|
||||
}
|
||||
})
|
||||
.build();
|
||||
|
||||
// 根据loggerName获取Logger对象,如果缓存中不存在,则创建一个新的Logger对象并放入缓存
|
||||
public static Logger getLogger(String loggerName) {
|
||||
try {
|
||||
return cache.get(loggerName, () -> {
|
||||
configureLogger(loggerName);
|
||||
return LogManager.getLogger(loggerName);
|
||||
});
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 配置Logger对象
|
||||
private static void configureLogger(String loggerName) {
|
||||
|
||||
LoggerContext context = (LoggerContext) LogManager.getContext(false);
|
||||
Configuration config = context.getConfiguration();
|
||||
|
||||
// 创建日志格式
|
||||
Layout<String> layout = PatternLayout.newBuilder()
|
||||
.withPattern("%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%thread] (%F:%L) : %m%n")
|
||||
.build();
|
||||
|
||||
// 创建时间触发策略
|
||||
TimeBasedTriggeringPolicy timePolicy = TimeBasedTriggeringPolicy.newBuilder()
|
||||
.build();
|
||||
|
||||
// 创建文件大小触发策略
|
||||
SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy("100 MB");
|
||||
|
||||
// 创建组合触发策略
|
||||
CompositeTriggeringPolicy triggeringPolicy = CompositeTriggeringPolicy.createPolicy(timePolicy, sizePolicy);
|
||||
|
||||
|
||||
// 创建滚动文件Appender
|
||||
RollingFileAppender appender = RollingFileAppender.newBuilder()
|
||||
.setName(loggerName)
|
||||
.withFileName("logs" + "/" + DateFormatUtils.getInstance().format(new Date(), "yyyy-MM-dd") + "/" + loggerName + ".log")
|
||||
.withFilePattern("logs" + "/" + "%d{yyyy-MM-dd}" + "/" + loggerName + "-%i.log.gz")
|
||||
.setLayout(layout)
|
||||
.setImmediateFlush(true)
|
||||
.withAppend(true)
|
||||
.setIgnoreExceptions(false)
|
||||
.withPolicy(triggeringPolicy)
|
||||
.build();
|
||||
|
||||
appender.start();
|
||||
config.addAppender(appender);
|
||||
|
||||
// 获取Logger对象
|
||||
org.apache.logging.log4j.core.Logger coreLogger = context.getLogger(loggerName);
|
||||
if (coreLogger == null) {
|
||||
throw new IllegalStateException("Logger with name " + loggerName + " does not exist.");
|
||||
}
|
||||
|
||||
// 将Appender添加到Logger对象中
|
||||
coreLogger.addAppender(appender);
|
||||
coreLogger.setLevel(Level.ALL);
|
||||
coreLogger.setAdditive(false);
|
||||
|
||||
// 更新Logger对象
|
||||
context.updateLoggers();
|
||||
|
||||
}
|
||||
|
||||
// 移除Logger对象
|
||||
public static void remove(String loggerName) {
|
||||
remove(loggerName, false);
|
||||
}
|
||||
|
||||
// 私有方法,移除Logger对象,isAuto参数用于判断是否是自动移除
|
||||
private static void remove(String loggerName, boolean isAuto) {
|
||||
if (!isAuto) {
|
||||
cache.invalidate(loggerName);
|
||||
}
|
||||
LoggerContext context = (LoggerContext) LogManager.getContext(false);
|
||||
Configuration config = context.getConfiguration();
|
||||
org.apache.logging.log4j.core.Logger coreLogger = context.getLogger(loggerName);
|
||||
Appender appender = config.getAppender(loggerName);
|
||||
if (appender != null) {
|
||||
appender.stop();
|
||||
coreLogger.removeAppender(appender);
|
||||
}
|
||||
config.getAppenders().remove(loggerName);
|
||||
context.updateLoggers();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -182,7 +182,6 @@ public class FFmpegUtils extends AbsVideoRecord {
|
||||
process.exitValue();
|
||||
return (long) (Double.parseDouble(data) * 1000);
|
||||
} catch (Exception e) {
|
||||
Log.e(e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.yutou.common.utils;
|
||||
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Marker;
|
||||
import org.apache.logging.log4j.MarkerManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.util.StackLocatorUtil;
|
||||
|
||||
|
||||
@@ -14,6 +12,14 @@ public class Log {
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
public static Logger getDynamicLogger(String loggerName) {
|
||||
return DynamicLogFile.getLogger(loggerName);
|
||||
}
|
||||
|
||||
public static void removeDynamicLogger(String loggerName) {
|
||||
DynamicLogFile.remove(loggerName);
|
||||
}
|
||||
|
||||
public static void i(Object... log) {
|
||||
if (!((boolean) ConfigTools.load(ConfigTools.CONFIG, "logcat"))) {
|
||||
return;
|
||||
|
||||
@@ -27,7 +27,7 @@ public class RedisTools {
|
||||
//Properties properties = PropertyUtil.loadProperties("jedis.properties");
|
||||
//host = properties.getProperty("redis.host");
|
||||
//port = Integer.valueOf(properties.getProperty("redis.port"));
|
||||
host = "172.21.35.118";
|
||||
host = "192.168.31.148";
|
||||
port = 6379;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ public class RedisTools {
|
||||
String ret = jedis.set(key, value);
|
||||
jedis.close();
|
||||
} catch (Exception e) {
|
||||
// TODO: handle exception
|
||||
Log.e(e);
|
||||
return false;
|
||||
}
|
||||
@@ -71,7 +70,6 @@ public class RedisTools {
|
||||
}
|
||||
jedis.close();
|
||||
} catch (Exception e) {
|
||||
// TODO: handle exception
|
||||
Log.e(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
server.port=8080
|
||||
server.port=8880
|
||||
logging.config=classpath:log4j2.xml
|
||||
logging.file.path=./logs
|
||||
logging.level.com.log.controller = trace
|
||||
# 启用Hibernate SQL日志
|
||||
spring.jpa.show-sql=true
|
||||
spring.jpa.properties.hibernate.format_sql=true
|
||||
logging.level.org.hibernate.SQL=DEBUG
|
||||
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
|
||||
37
src/main/resources/log4j2.xml
Normal file
37
src/main/resources/log4j2.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<!-- 控制台输出 -->
|
||||
<Console name="ConsoleAppender" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p [%thread] (%F:%L) : %m%n%throwable"/>
|
||||
</Console>
|
||||
|
||||
<!-- 动态路由日志文件 -->
|
||||
<Routing name="RoutingAppender">
|
||||
<Routes pattern="$${date:yyyy-MM-dd}">
|
||||
<Route>
|
||||
<RollingFile name="Rolling-${date:yyyy-MM-dd}" fileName="logs/${date:yyyy-MM-dd}/system.log"
|
||||
filePattern="logs/%d{yyyy-MM-dd}/system-%i.log.gz">
|
||||
<PatternLayout>
|
||||
<pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%thread] (%F:%L) : %m%n%throwable</pattern>
|
||||
</PatternLayout>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy />
|
||||
<SizeBasedTriggeringPolicy size="1024 MB"/>
|
||||
</Policies>
|
||||
</RollingFile>
|
||||
</Route>
|
||||
</Routes>
|
||||
</Routing>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<!-- 根日志记录器 -->
|
||||
<Root level="info">
|
||||
<AppenderRef ref="ConsoleAppender"/>
|
||||
<AppenderRef ref="RoutingAppender"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user