414 lines
12 KiB
Go
414 lines
12 KiB
Go
package xuexitong
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
"likedream-goframe/internal/application/xuexitong/entity"
|
||
errors2 "likedream-goframe/internal/common/errors"
|
||
"likedream-goframe/internal/framework/logger"
|
||
"strconv"
|
||
"time"
|
||
|
||
"github.com/avast/retry-go/v4"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
// attachmentDetectLive 从附件设置中检测并填充直播信息
|
||
func (c *XuexitongCourse) attachmentDetectLive(attachment *entity.AttachmentSetting, p *entity.ChapterPoint) {
|
||
for _, att := range attachment.Attachments {
|
||
jobid, _ := att["jobid"].(string)
|
||
if jobid != p.JobID {
|
||
continue
|
||
}
|
||
|
||
if authEnc, ok := att["authEnc"].(string); ok {
|
||
p.AuthEnc = authEnc
|
||
}
|
||
if liveDragEnc, ok := att["liveDragEnc"].(string); ok {
|
||
p.LiveDragEnc = liveDragEnc
|
||
}
|
||
if liveSetEnc, ok := att["liveSetEnc"].(string); ok {
|
||
p.LiveSetEnc = liveSetEnc
|
||
}
|
||
if otherInfo, ok := att["otherInfo"].(string); ok {
|
||
p.OtherInfo = otherInfo
|
||
}
|
||
if enc, ok := att["enc"].(string); ok {
|
||
p.Enc = enc
|
||
}
|
||
if liveSwDsEnc, ok := att["liveSwDsEnc"].(string); ok {
|
||
p.LiveSwDsEnc = liveSwDsEnc
|
||
}
|
||
if isJob, ok := att["job"].(bool); ok {
|
||
p.IsJob = isJob
|
||
}
|
||
if aid, ok := att["aid"].(float64); ok {
|
||
p.Aid = strconv.FormatInt(int64(aid), 10)
|
||
}
|
||
break
|
||
}
|
||
}
|
||
|
||
// AttachmentsDetectionLive 从附件设置中检测并填充直播信息
|
||
func (c *XuexitongCourse) AttachmentsDetectionLive(attachment *entity.AttachmentSetting, p *entity.PointLiveDto) error {
|
||
if attachment == nil {
|
||
return errors2.NewBusinessError(503, "附件设置为空")
|
||
}
|
||
|
||
for _, att := range attachment.Attachments {
|
||
jobid, _ := att["jobid"].(string)
|
||
if jobid != p.JobID {
|
||
continue
|
||
}
|
||
|
||
if authEnc, ok := att["authEnc"].(string); ok {
|
||
p.AuthEnc = authEnc
|
||
}
|
||
if liveDragEnc, ok := att["liveDragEnc"].(string); ok {
|
||
p.LiveDragEnc = liveDragEnc
|
||
}
|
||
if liveSetEnc, ok := att["liveSetEnc"].(string); ok {
|
||
p.LiveSetEnc = liveSetEnc
|
||
}
|
||
if otherInfo, ok := att["otherInfo"].(string); ok {
|
||
p.OtherInfo = otherInfo
|
||
}
|
||
if enc, ok := att["enc"].(string); ok {
|
||
p.Enc = enc
|
||
}
|
||
if liveSwDsEnc, ok := att["liveSwDsEnc"].(string); ok {
|
||
p.LiveSwDsEnc = liveSwDsEnc
|
||
}
|
||
if isJob, ok := att["job"].(bool); ok {
|
||
p.IsJob = isJob
|
||
}
|
||
if aid, ok := att["aid"].(float64); ok {
|
||
p.Aid = strconv.FormatInt(int64(aid), 10)
|
||
}
|
||
break
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// AutoWatchLive 自动观看直播任务点
|
||
func (c *XuexitongCourse) AutoWatchLive(ctx context.Context, course entity.XueXiTCourse, p *entity.PointLiveDto) error {
|
||
if !p.IsJob {
|
||
logger.Logger.Info("直播不是任务点,跳过", zap.String("jobID", p.JobID), zap.String("title", p.Title))
|
||
return nil
|
||
}
|
||
|
||
if err := c.PullLiveInfo(ctx, p); err != nil {
|
||
logger.Logger.Warn("获取直播信息失败", zap.Error(err))
|
||
}
|
||
|
||
if p.LiveStatusCode == 0 {
|
||
logger.Logger.Info("直播还未开始,跳过", zap.String("title", p.Title))
|
||
return nil
|
||
}
|
||
|
||
if p.VideoCompletePercent >= 90 {
|
||
logger.Logger.Info("直播已完成", zap.String("title", p.Title), zap.Float64("percent", p.VideoCompletePercent))
|
||
return nil
|
||
}
|
||
|
||
logger.Logger.Info("开始刷直播",
|
||
zap.String("jobID", p.JobID),
|
||
zap.String("title", p.Title),
|
||
zap.Float64("currentPercent", p.VideoCompletePercent))
|
||
|
||
if _, err := c.LiveRelationReport(ctx, p); err != nil {
|
||
logger.Logger.Warn("建立直播连接失败", zap.Error(err))
|
||
}
|
||
|
||
for {
|
||
report, err := c.LiveSaveTimeReport(ctx, p)
|
||
if err != nil {
|
||
logger.Logger.Error("提交直播观看进度失败", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
if err := c.PullLiveInfo(ctx, p); err != nil {
|
||
logger.Logger.Warn("更新直播信息失败", zap.Error(err))
|
||
}
|
||
|
||
logger.Logger.Debug("直播观看中",
|
||
zap.String("title", p.Title),
|
||
zap.Float64("percent", p.VideoCompletePercent),
|
||
zap.String("report", report))
|
||
|
||
if p.VideoCompletePercent >= 90 {
|
||
logger.Logger.Info("直播刷课完成", zap.String("title", p.Title))
|
||
c.onTaskComplete()
|
||
break
|
||
}
|
||
|
||
time.Sleep(30 * time.Second)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// AutoWatchLiveByPoint 自动观看直播任务点(使用统一的 ChapterPoint)
|
||
func (c *XuexitongCourse) AutoWatchLiveByPoint(ctx context.Context, course entity.XueXiTCourse, p *entity.ChapterPoint) error {
|
||
if !p.IsJob {
|
||
logger.Logger.Info("直播不是任务点,跳过", zap.String("jobID", p.JobID), zap.String("title", p.Title))
|
||
return nil
|
||
}
|
||
|
||
if err := c.PullLiveInfoByPoint(ctx, p); err != nil {
|
||
logger.Logger.Warn("获取直播信息失败", zap.Error(err))
|
||
}
|
||
|
||
if p.LiveStatusCode == 0 {
|
||
logger.Logger.Info("直播还未开始,跳过", zap.String("title", p.Title))
|
||
return nil
|
||
}
|
||
|
||
if p.VideoCompletePercent >= 90 {
|
||
logger.Logger.Info("直播已完成", zap.String("title", p.Title), zap.Float64("percent", p.VideoCompletePercent))
|
||
return nil
|
||
}
|
||
|
||
logger.Logger.Info("开始刷直播",
|
||
zap.String("jobID", p.JobID),
|
||
zap.String("title", p.Title),
|
||
zap.Float64("currentPercent", p.VideoCompletePercent))
|
||
|
||
if _, err := c.LiveRelationReportByPoint(ctx, p); err != nil {
|
||
logger.Logger.Warn("建立直播连接失败", zap.Error(err))
|
||
}
|
||
|
||
for {
|
||
report, err := c.LiveSaveTimeReportByPoint(ctx, p)
|
||
if err != nil {
|
||
logger.Logger.Error("提交直播观看进度失败", zap.Error(err))
|
||
return err
|
||
}
|
||
|
||
if err := c.PullLiveInfoByPoint(ctx, p); err != nil {
|
||
logger.Logger.Warn("更新直播信息失败", zap.Error(err))
|
||
}
|
||
|
||
logger.Logger.Debug("直播观看中",
|
||
zap.String("title", p.Title),
|
||
zap.Float64("percent", p.VideoCompletePercent),
|
||
zap.String("report", report))
|
||
|
||
if p.VideoCompletePercent >= 90 {
|
||
logger.Logger.Info("直播刷课完成", zap.String("title", p.Title))
|
||
c.onTaskComplete()
|
||
break
|
||
}
|
||
|
||
time.Sleep(30 * time.Second)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// PullLiveInfo 获取直播信息
|
||
func (c *XuexitongCourse) PullLiveInfo(ctx context.Context, p *entity.PointLiveDto) error {
|
||
urlStr := fmt.Sprintf("https://mooc1.chaoxing.com/ananas/live/liveinfo?liveid=%s&userid=%s&clazzid=%s&knowledgeid=%d&courseid=%s&jobid=%s&ut=s",
|
||
p.LiveId, p.UserId, p.ClassID, p.KnowledgeID, p.CourseID, p.JobID)
|
||
|
||
var body string
|
||
err := retry.Do(func() error {
|
||
resp, err := c.client.R().SetHeaders(map[string]string{
|
||
"User-Agent": GetUA("web"),
|
||
}).Get(urlStr)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if resp.StatusCode() != 200 {
|
||
return errors2.NewBusinessError(resp.StatusCode(), "获取直播信息失败")
|
||
}
|
||
body = resp.String()
|
||
return nil
|
||
}, retry.Attempts(3), retry.Delay(2*time.Second))
|
||
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
var result map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &result); err != nil {
|
||
return err
|
||
}
|
||
|
||
if status, ok := result["status"].(bool); ok && status {
|
||
if temp, ok := result["temp"].(map[string]interface{}); ok {
|
||
if data, ok := temp["data"].(map[string]interface{}); ok {
|
||
if percentValue, ok := data["percentValue"].(float64); ok {
|
||
p.VideoCompletePercent = percentValue
|
||
}
|
||
if duration, ok := data["duration"].(float64); ok {
|
||
p.VideoDuration = int(duration)
|
||
}
|
||
if liveStatus, ok := data["liveStatus"].(float64); ok {
|
||
p.LiveStatusCode = int(liveStatus)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// PullLiveInfoByPoint 获取直播信息(使用统一的 ChapterPoint)
|
||
func (c *XuexitongCourse) PullLiveInfoByPoint(ctx context.Context, p *entity.ChapterPoint) error {
|
||
urlStr := fmt.Sprintf("https://mooc1.chaoxing.com/ananas/live/liveinfo?liveid=%s&userid=%s&clazzid=%s&knowledgeid=%d&courseid=%s&jobid=%s&ut=s",
|
||
p.LiveId, p.UserId, p.ClassID, p.KnowledgeID, p.CourseID, p.JobID)
|
||
|
||
var body string
|
||
err := retry.Do(func() error {
|
||
resp, err := c.client.R().SetHeaders(map[string]string{
|
||
"User-Agent": GetUA("web"),
|
||
}).Get(urlStr)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if resp.StatusCode() != 200 {
|
||
return errors2.NewBusinessError(resp.StatusCode(), "获取直播信息失败")
|
||
}
|
||
body = resp.String()
|
||
return nil
|
||
}, retry.Attempts(3), retry.Delay(2*time.Second))
|
||
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
var result map[string]interface{}
|
||
if err := json.Unmarshal([]byte(body), &result); err != nil {
|
||
return err
|
||
}
|
||
|
||
if status, ok := result["status"].(bool); ok && status {
|
||
if temp, ok := result["temp"].(map[string]interface{}); ok {
|
||
if data, ok := temp["data"].(map[string]interface{}); ok {
|
||
if percentValue, ok := data["percentValue"].(float64); ok {
|
||
p.VideoCompletePercent = percentValue
|
||
}
|
||
if duration, ok := data["duration"].(float64); ok {
|
||
p.VideoDuration = int(duration)
|
||
}
|
||
if liveStatus, ok := data["liveStatus"].(float64); ok {
|
||
p.LiveStatusCode = int(liveStatus)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// LiveRelationReport 建立直播连接
|
||
func (c *XuexitongCourse) LiveRelationReport(ctx context.Context, p *entity.PointLiveDto) (string, error) {
|
||
urlStr := fmt.Sprintf("https://mooc1.chaoxing.com/mooc-ans/live/relation?courseid=%s&knowledgeid=%d&ut=s&jobid=%s&aid=%s",
|
||
p.CourseID, p.KnowledgeID, p.JobID, p.Aid)
|
||
|
||
var body string
|
||
err := retry.Do(func() error {
|
||
resp, err := c.client.R().SetHeaders(map[string]string{
|
||
"User-Agent": GetUA("web"),
|
||
}).Get(urlStr)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if resp.StatusCode() != 200 {
|
||
return errors2.NewBusinessError(resp.StatusCode(), "建立直播连接失败")
|
||
}
|
||
body = resp.String()
|
||
return nil
|
||
}, retry.Attempts(3), retry.Delay(2*time.Second))
|
||
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// LiveRelationReportByPoint 建立直播连接(使用统一的 ChapterPoint)
|
||
func (c *XuexitongCourse) LiveRelationReportByPoint(ctx context.Context, p *entity.ChapterPoint) (string, error) {
|
||
urlStr := fmt.Sprintf("https://mooc1.chaoxing.com/mooc-ans/live/relation?courseid=%s&knowledgeid=%d&ut=s&jobid=%s&aid=%s",
|
||
p.CourseID, p.KnowledgeID, p.JobID, p.Aid)
|
||
|
||
var body string
|
||
err := retry.Do(func() error {
|
||
resp, err := c.client.R().SetHeaders(map[string]string{
|
||
"User-Agent": GetUA("web"),
|
||
}).Get(urlStr)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if resp.StatusCode() != 200 {
|
||
return errors2.NewBusinessError(resp.StatusCode(), "建立直播连接失败")
|
||
}
|
||
body = resp.String()
|
||
return nil
|
||
}, retry.Attempts(3), retry.Delay(2*time.Second))
|
||
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// LiveSaveTimeReport 提交直播观看进度
|
||
func (c *XuexitongCourse) LiveSaveTimeReport(ctx context.Context, p *entity.PointLiveDto) (string, error) {
|
||
urlStr := fmt.Sprintf("https://zhibo.chaoxing.com/saveTimePc?streamName=%s&vdoid=%s&userId=%s&isStart=1&t=%d&courseId=%s",
|
||
p.StreamName, p.Vdoid, p.UserId, time.Now().UnixMilli(), p.CourseID)
|
||
|
||
var body string
|
||
err := retry.Do(func() error {
|
||
resp, err := c.client.R().SetHeaders(map[string]string{
|
||
"User-Agent": GetUA("web"),
|
||
}).Get(urlStr)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if resp.StatusCode() != 200 {
|
||
return errors2.NewBusinessError(resp.StatusCode(), "提交直播观看进度失败")
|
||
}
|
||
body = resp.String()
|
||
return nil
|
||
}, retry.Attempts(3), retry.Delay(2*time.Second))
|
||
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return body, nil
|
||
}
|
||
|
||
// LiveSaveTimeReportByPoint 提交直播观看进度(使用统一的 ChapterPoint)
|
||
func (c *XuexitongCourse) LiveSaveTimeReportByPoint(ctx context.Context, p *entity.ChapterPoint) (string, error) {
|
||
urlStr := fmt.Sprintf("https://zhibo.chaoxing.com/saveTimePc?streamName=%s&vdoid=%s&userId=%s&isStart=1&t=%d&courseId=%s",
|
||
p.StreamName, p.Vdoid, p.UserId, time.Now().UnixMilli(), p.CourseID)
|
||
|
||
var body string
|
||
err := retry.Do(func() error {
|
||
resp, err := c.client.R().SetHeaders(map[string]string{
|
||
"User-Agent": GetUA("web"),
|
||
}).Get(urlStr)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if resp.StatusCode() != 200 {
|
||
return errors2.NewBusinessError(resp.StatusCode(), "提交直播观看进度失败")
|
||
}
|
||
body = resp.String()
|
||
return nil
|
||
}, retry.Attempts(3), retry.Delay(2*time.Second))
|
||
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
|
||
return body, nil
|
||
}
|