完善 任务预览功能
This commit is contained in:
parent
59c8171ce8
commit
eb45f0fd41
|
|
@ -18,7 +18,7 @@
|
||||||
<Routes @rendermode="InteractiveServer" />
|
<Routes @rendermode="InteractiveServer" />
|
||||||
<script type="text/javascript" src="@("https://unpkg.com/@antv/g2plot@2.4.17/dist/g2plot.min.js")"></script>
|
<script type="text/javascript" src="@("https://unpkg.com/@antv/g2plot@2.4.17/dist/g2plot.min.js")"></script>
|
||||||
<script src="_content/AntDesign/js/ant-design-blazor.js"></script>
|
<script src="_content/AntDesign/js/ant-design-blazor.js"></script>
|
||||||
<script src="_content/AntDesign.Charts/ant-design-charts-blazor.js"></script>
|
@* <script src="_content/AntDesign.Charts/ant-design-charts-blazor.js"></script> *@
|
||||||
<script src="_framework/blazor.web.js"></script>
|
<script src="_framework/blazor.web.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
using System.Security.Policy;
|
||||||
|
|
||||||
|
namespace Learn.VideoAnalysis.Components.Pages.Dto
|
||||||
|
{
|
||||||
|
public class TaskShowRoute : PageModel
|
||||||
|
{
|
||||||
|
|
||||||
|
public int ID;
|
||||||
|
public void OnGet(int id)
|
||||||
|
{
|
||||||
|
ID = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,80 +1,72 @@
|
||||||
@page "/VideoTaskShow/{tid}"
|
@page "/VideoTaskShow/{taskId:long}"
|
||||||
<div>
|
|
||||||
<input type="file" id="videoFile" accept="video/*">
|
|
||||||
<input type="file" id="subtitleFile" accept=".json">
|
|
||||||
<input type="file" id="segmentsFile" accept=".json">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="video-container">
|
<div id="video-container">
|
||||||
<video id="videoPlayer" controls></video>
|
@if (videoKnows != null)
|
||||||
|
{
|
||||||
|
<div id="segmentsContainer">
|
||||||
|
@for (int i = 0; i < videoKnows.Length; i++)
|
||||||
|
{
|
||||||
|
var item = videoKnows[i];
|
||||||
|
<button class="kBtn" onclick="spClick(@i,this)">@getF(item) @item.Theme </button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<video id="videoPlayer" controls autoplay>
|
||||||
|
<source type="video/mp4" />
|
||||||
|
</video>
|
||||||
<div id="subtitleArea" class="subtitles"></div>
|
<div id="subtitleArea" class="subtitles"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="segmentsContainer"></div>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const
|
window.b = []
|
||||||
|
window.subtitles = []
|
||||||
|
var displayButton = []
|
||||||
|
var lastSegments = null;
|
||||||
|
function init() {
|
||||||
const videoPlayer = document.getElementById('videoPlayer');
|
const videoPlayer = document.getElementById('videoPlayer');
|
||||||
const subtitleArea = document.getElementById('subtitleArea');
|
const subtitleArea = document.getElementById('subtitleArea');
|
||||||
|
//视频时间变化时
|
||||||
|
|
||||||
let subtitles = @captionsArr
|
|
||||||
let segments = @videoKnows
|
|
||||||
let displayButton = []
|
|
||||||
let lastSegments = null;
|
|
||||||
window.onload(() => {
|
|
||||||
videoPlayer.src = @videoPath
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
videoPlayer.addEventListener('timeupdate', function () {
|
videoPlayer.addEventListener('timeupdate', function () {
|
||||||
|
if (displayButton.length == 0) initKD()
|
||||||
const currentTime = videoPlayer.currentTime;
|
const currentTime = videoPlayer.currentTime;
|
||||||
subtitleArea.textContent = '';
|
subtitleArea.textContent = '';
|
||||||
|
//字幕
|
||||||
subtitles.forEach(subtitle => {
|
window.subtitles.forEach(subtitle => {
|
||||||
|
let textContent = subtitle.text
|
||||||
let textContent = subtitle.Text || subtitle.Content;
|
if (currentTime >= subtitle.start
|
||||||
if (currentTime >= subtitle.Start
|
&& currentTime <= subtitle.end
|
||||||
&& currentTime <= subtitle.End
|
|
||||||
&& subtitleArea.textContent != textContent) {
|
&& subtitleArea.textContent != textContent) {
|
||||||
|
|
||||||
subtitleArea.textContent = textContent;
|
subtitleArea.textContent = textContent;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let segment = displayButton.find(s => currentTime >= s.开始秒
|
//时间片
|
||||||
&& currentTime <= s.结束秒)
|
let segment = displayButton.find(s => currentTime >= s.startTime
|
||||||
|
&& currentTime <= s.endTime)
|
||||||
if (segment) {
|
if (segment) {
|
||||||
segment.button.style.backgroundColor = "rgb(238, 200, 118)";
|
segment.button.style.backgroundColor = "rgb(238, 200, 118)";
|
||||||
if (lastSegments && lastSegments != segment) lastSegments.button.style.backgroundColor = "rgb(240, 249, 235)";
|
if (lastSegments && lastSegments != segment) lastSegments.button.style.backgroundColor = "rgb(240, 249, 235)";
|
||||||
lastSegments = segment
|
lastSegments = segment
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
function getF(segment) {
|
|
||||||
let sf = parseInt(segment.开始秒 / 60)
|
|
||||||
let sm = parseInt(segment.开始秒 % 60)
|
|
||||||
let ef = parseInt(segment.结束秒 / 60)
|
|
||||||
let em = parseInt(segment.结束秒 % 60)
|
|
||||||
return `${sf}:${sm} - ${ef}:${em}`
|
|
||||||
}
|
}
|
||||||
function displaySegments() {
|
function initKD() {
|
||||||
const segmentsContainer = document.getElementById('segmentsContainer');
|
let btns = document.getElementsByClassName("kBtn")
|
||||||
segmentsContainer.innerHTML = '';
|
if(btns.length == 0) return
|
||||||
segments.forEach(segment => {
|
displayButton = window.b.map((s, i) => { return { ...s, button: btns[i] } })
|
||||||
const button = document.createElement('button');
|
}
|
||||||
displayButton.push(segment);
|
//后端传递初始化数据
|
||||||
segment.button = button;
|
function setDB(a, b,c) {
|
||||||
button.textContent =
|
console.log("setDB", a, b,c)
|
||||||
`${getF(segment)} ${segment.主题}`;
|
window.subtitles = a
|
||||||
button.addEventListener('click', () => {
|
window.b = b
|
||||||
videoPlayer.currentTime = segment.开始秒;
|
const videoPlayer = document.getElementById('videoPlayer');
|
||||||
videoPlayer.play();
|
videoPlayer.src=c
|
||||||
});
|
init()
|
||||||
segmentsContainer.appendChild(button);
|
}
|
||||||
});
|
//点击时间片时
|
||||||
|
function spClick(i, button) {
|
||||||
|
videoPlayer.currentTime = displayButton[i].startTime;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@code {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using Learn.VideoAnalysis.Controllers.Dto;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||||
|
using Microsoft.JSInterop;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -14,15 +15,21 @@ using VideoAnalysisCore.Common;
|
||||||
using VideoAnalysisCore.Enum;
|
using VideoAnalysisCore.Enum;
|
||||||
using VideoAnalysisCore.Model;
|
using VideoAnalysisCore.Model;
|
||||||
using VideoAnalysisCore.Model.Dto;
|
using VideoAnalysisCore.Model.Dto;
|
||||||
|
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||||
|
|
||||||
namespace Learn.VideoAnalysis.Components.Pages
|
namespace Learn.VideoAnalysis.Components.Pages
|
||||||
{
|
{
|
||||||
public partial class VideoTaskShow : ComponentBase
|
public partial class VideoTaskShow : ComponentBase
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 任务id
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public long? taskId { get; set; }
|
||||||
[Inject] private ConfirmService ComfirmService { get; set; } = default!;
|
[Inject] private ConfirmService ComfirmService { get; set; } = default!;
|
||||||
[Inject] private IHttpContextAccessor HttpContext { get; set; } = default!;
|
[Inject] private IHttpContextAccessor HttpContext { get; set; } = default!;
|
||||||
[Inject] private Repository<VideoTask> taskDB { get; set; } = default!;
|
[Inject] private Repository<VideoTask> taskDB { get; set; } = default!;
|
||||||
|
[Inject] private IJSRuntime JSRuntime { get; set; } = default!;
|
||||||
|
|
||||||
private VideoTask nowTask { get; set; } = default!;
|
private VideoTask nowTask { get; set; } = default!;
|
||||||
private string videoPath { get; set; } = default!;
|
private string videoPath { get; set; } = default!;
|
||||||
|
|
@ -42,24 +49,37 @@ namespace Learn.VideoAnalysis.Components.Pages
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public string getF(VideoKnowRes segment)
|
||||||
|
{
|
||||||
|
var sf = (int)((segment.StartTime ?? 0) / 60);
|
||||||
|
var sm = (int)((segment.StartTime ?? 0) % 60);
|
||||||
|
var ef = (int)((segment.EndTime ?? 0) / 60);
|
||||||
|
var em = (int)((segment.EndTime ?? 0) % 60);
|
||||||
|
return $"{sf}:{sm} - {ef}: {em}";
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化
|
/// 初始化
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override async void OnInitialized()
|
protected override async void OnInitialized()
|
||||||
{
|
{
|
||||||
var routeData = HttpContext.HttpContext.GetRouteData();
|
if (this.taskId is null)
|
||||||
if (routeData is null)
|
|
||||||
return;
|
return;
|
||||||
long taskId = (long)routeData.Values["id"];
|
long taskId = this.taskId.Value;
|
||||||
nowTask = await taskDB.GetFirstAsync(s => s.Id == taskId);
|
nowTask = await taskDB.GetFirstAsync(s => s.Id == taskId);
|
||||||
if(nowTask is null)
|
if(nowTask is null)
|
||||||
return;
|
return;
|
||||||
captionsArr = RedisExpand.Redis.HMGet<SenseVoiceRes[]>(RedisExpandKey.Task(taskId), "Captions").FirstOrDefault();
|
captionsArr = RedisExpand.Redis.HMGet<SenseVoiceRes[]>(RedisExpandKey.Task(taskId), "Captions").FirstOrDefault();
|
||||||
videoKnows = RedisExpand.Redis.HMGet<VideoKnowRes[]>(RedisExpandKey.Task(taskId), "VideoKnows").FirstOrDefault();
|
videoKnows = RedisExpand.Redis.HMGet<VideoKnowRes[]>(RedisExpandKey.Task(taskId), "VideoKnows").FirstOrDefault();
|
||||||
videoPath = AppCommon.GetVideoPath(nowTask.Id.ToString());
|
videoPath = AppCommon.GetVideoPath(nowTask.Id.ToString());
|
||||||
|
|
||||||
|
await JSRuntime.InvokeVoidAsync("setDB", captionsArr,videoKnows, videoPath);
|
||||||
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> Comfirm(string message)
|
private async Task<bool> Comfirm(string message)
|
||||||
|
|
|
||||||
|
|
@ -6,27 +6,37 @@
|
||||||
|
|
||||||
#video-container {
|
#video-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 1600px;
|
width: 1660px;
|
||||||
height: 850px;
|
height: 850px;
|
||||||
float: left;
|
float: left;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
video {
|
video {
|
||||||
width: 100%;
|
width: 94%;
|
||||||
height: 100%;
|
height: 85%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtitles {
|
.subtitles {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -40px;
|
bottom: 200px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: white;
|
color: white;
|
||||||
background-color: rgba(0, 0, 0, 0.7);
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
#segmentsContainer:is(:hover) {
|
||||||
|
width: 400px;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
#segmentsContainer {
|
#segmentsContainer {
|
||||||
|
transition:right 0.7s;
|
||||||
|
z-index:999;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background-color: #e3e3e3c2;
|
||||||
|
position: absolute;
|
||||||
|
right: -180px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 265px;
|
width: 265px;
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ namespace Learn.VideoAnalysis
|
||||||
// app.UseExceptionHandler("/Login");
|
// app.UseExceptionHandler("/Login");
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.UseStaticFiles(new StaticFileOptions
|
app.UseStaticFiles(new StaticFileOptions
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,10 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
|
||||||
foreach (var next in timeBases)
|
foreach (var next in timeBases)
|
||||||
{
|
{
|
||||||
// 如果类型相同,则扩展时间段
|
// 如果类型相同,则扩展时间段
|
||||||
if (current.主题 == next.主题)
|
if (current.Theme == next.Theme)
|
||||||
{
|
{
|
||||||
current.结束秒 = Math.Max(current.结束秒.Value, next.结束秒.Value);
|
current.EndTime = Math.Max(current.EndTime.Value, next.EndTime.Value);
|
||||||
current.内容总结 += next.内容总结;
|
current.Content += next.Content;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,24 @@ namespace VideoAnalysisCore.AICore.GPT.Dto
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 问题解释
|
/// 问题解释
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float? 开始秒 { get; set; }
|
public float? StartTime { get; set; }
|
||||||
public float? 结束秒 { get; set; }
|
public float? EndTime { get; set; }
|
||||||
public float? 持续时间 => (结束秒 ?? 0) - 开始秒??0;
|
/// <summary>
|
||||||
public string? 主题 { get; set; }
|
/// 持续时间
|
||||||
public string? 章节 { get; set; }
|
/// </summary>
|
||||||
public string? 内容总结 { get; set; }
|
public float? KeepTime => (EndTime ?? 0) - StartTime ?? 0;
|
||||||
|
/// <summary>
|
||||||
|
/// 主题
|
||||||
|
/// </summary>
|
||||||
|
public string? Theme { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 章节
|
||||||
|
/// </summary>
|
||||||
|
public string? Section { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 内容总结
|
||||||
|
/// </summary>
|
||||||
|
public string? Content { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
public class FileNameInfo
|
public class FileNameInfo
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,7 @@ namespace VideoAnalysisCore.Common
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tid"></param>
|
/// <param name="tid"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string GetVideoPath(string tid) =>
|
public static string GetVideoPath(string tid) => $"./video/{tid}/{tid}.mp4";
|
||||||
Path.Combine(TaskCachedFile, tid, tid + ".mp4");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ namespace VideoAnalysisCore.Common
|
||||||
var taskData = await DbScoped.SugarScope.Queryable<VideoTask>()
|
var taskData = await DbScoped.SugarScope.Queryable<VideoTask>()
|
||||||
.FirstAsync(s => s.Id == tId);
|
.FirstAsync(s => s.Id == tId);
|
||||||
taskData.ChatAnalysis = gptRes;
|
taskData.ChatAnalysis = gptRes;
|
||||||
taskData.ChatAnalysisScore = gptRes.Assessment.Merit?.Sum(s => s.Score) ?? 0;
|
taskData.ChatAnalysisScore = gptRes?.Assessment?.Merit?.Sum(s => s.Score) ?? 0;
|
||||||
taskData.ErrorMessage = string.Empty;
|
taskData.ErrorMessage = string.Empty;
|
||||||
taskData.LastEnum = RedisChannelEnum.EndTask;
|
taskData.LastEnum = RedisChannelEnum.EndTask;
|
||||||
await DbScoped.SugarScope.Updateable(taskData)
|
await DbScoped.SugarScope.Updateable(taskData)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue