优化视频数据库数据保存逻辑,每次任务执行保存视频数量超过设置的每次查询数量时 跳出当前循环
This commit is contained in:
+23
-32
@@ -72,7 +72,7 @@ namespace dy.net.job
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否下载图片并合成视频
|
/// 是否下载图片并合成视频
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool _downImageVideo;
|
//private bool _downImageVideo;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -138,13 +138,10 @@ namespace dy.net.job
|
|||||||
if (config.BatchCount > 0)
|
if (config.BatchCount > 0)
|
||||||
count = config.BatchCount.ToString();
|
count = config.BatchCount.ToString();
|
||||||
|
|
||||||
// 3. 初始化是否下载图片视频的设置
|
// 3. 在处理Cookie之前执行的预处理操作
|
||||||
InitializeDownImageVideoSetting(config);
|
|
||||||
|
|
||||||
// 4. 在处理Cookie之前执行的预处理操作(子类可重写)
|
|
||||||
await BeforeProcessCookies();
|
await BeforeProcessCookies();
|
||||||
|
|
||||||
// 5. 获取所有有效的Cookie
|
// 4. 获取所有有效的Cookie
|
||||||
var cookies = await GetValidCookies();
|
var cookies = await GetValidCookies();
|
||||||
if (cookies == null || !cookies.Any())
|
if (cookies == null || !cookies.Any())
|
||||||
{
|
{
|
||||||
@@ -314,10 +311,10 @@ namespace dy.net.job
|
|||||||
int syncCount = 0; // 本次同步成功的视频数量
|
int syncCount = 0; // 本次同步成功的视频数量
|
||||||
string cursor = "0"; // 初始游标
|
string cursor = "0"; // 初始游标
|
||||||
bool hasMore = true; // 是否还有更多数据
|
bool hasMore = true; // 是否还有更多数据
|
||||||
|
|
||||||
|
//查询关注列表开启了同步的关注
|
||||||
var follows = await douyinFollowService.GetSyncFollows(cookie.MyUserId);
|
var follows = await douyinFollowService.GetSyncFollows(cookie.MyUserId);
|
||||||
|
|
||||||
|
|
||||||
//var ups = JsonConvert.DeserializeObject<List<DouyinUpSecUserIdDto>>(cookie.UpSecUserIds);
|
|
||||||
var firstUp = follows?.Where(x => !string.IsNullOrWhiteSpace(x.SecUid)).FirstOrDefault();
|
var firstUp = follows?.Where(x => !string.IsNullOrWhiteSpace(x.SecUid)).FirstOrDefault();
|
||||||
if (firstUp == null)
|
if (firstUp == null)
|
||||||
{
|
{
|
||||||
@@ -385,6 +382,13 @@ namespace dy.net.job
|
|||||||
// 保存视频信息到数据库
|
// 保存视频信息到数据库
|
||||||
syncCount += await SaveVideos(videos);
|
syncCount += await SaveVideos(videos);
|
||||||
|
|
||||||
|
//当syncCount达到上限时,跳出循环
|
||||||
|
if (config.BatchCount > 0 && syncCount >= config.BatchCount)
|
||||||
|
{
|
||||||
|
Log.Debug($"{JobType}-Cookie[{cookie.UserName}]本次同步达到上限{config.BatchCount},停止同步!!!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// 随机延迟,模拟人类操作,避免请求过快
|
// 随机延迟,模拟人类操作,避免请求过快
|
||||||
await Task.Delay(_random.Next(5, 10) * 1000);
|
await Task.Delay(_random.Next(5, 10) * 1000);
|
||||||
}
|
}
|
||||||
@@ -414,7 +418,7 @@ namespace dy.net.job
|
|||||||
{
|
{
|
||||||
if (File.Exists(exitVideo.VideoSavePath))
|
if (File.Exists(exitVideo.VideoSavePath))
|
||||||
{
|
{
|
||||||
Serilog.Log.Debug($"视频-{exitVideo.AwemeId}-[{exitVideo.VideoTitle}]已存在,跳过");
|
Log.Debug($"视频-{exitVideo.AwemeId}-[{exitVideo.VideoTitle}]已存在,跳过");
|
||||||
continue;// 已存在则跳过
|
continue;// 已存在则跳过
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -434,14 +438,11 @@ namespace dy.net.job
|
|||||||
videos.Add(video);
|
videos.Add(video);
|
||||||
|
|
||||||
// 如果配置了下载图片视频,则处理图片集并合成视频
|
// 如果配置了下载图片视频,则处理图片集并合成视频
|
||||||
if (_downImageVideo)
|
if (config.DownImageVideo || config.DownMp3 || config.DownImage)
|
||||||
{
|
{
|
||||||
if (config.DownImageVideo || config.DownMp3 || config.DownImage)
|
var mergevideo = await ProcessImageSetAndMergeToVideo(cookie, item, data, config, followed);
|
||||||
{
|
if (mergevideo != null)
|
||||||
var mergevideo = await ProcessImageSetAndMergeToVideo(cookie, item, data, config, followed);
|
videos.Add(mergevideo);
|
||||||
if (mergevideo != null)
|
|
||||||
videos.Add(mergevideo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return videos;
|
return videos;
|
||||||
@@ -480,7 +481,11 @@ namespace dy.net.job
|
|||||||
var savePath = Path.Combine(saveFolder, fileName);
|
var savePath = Path.Combine(saveFolder, fileName);
|
||||||
|
|
||||||
// 如果文件已存在,跳过
|
// 如果文件已存在,跳过
|
||||||
if (File.Exists(savePath)) return null;
|
if (File.Exists(savePath))
|
||||||
|
{
|
||||||
|
Log.Debug($"{JobType}-视频[{DouyinFileNameHelper.SanitizePath(item.Desc)}]已存在,跳过下载.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Log.Debug($"{JobType}-视频[{DouyinFileNameHelper.SanitizePath(item.Desc)}]开始下载...");
|
Log.Debug($"{JobType}-视频[{DouyinFileNameHelper.SanitizePath(item.Desc)}]开始下载...");
|
||||||
// 随机延迟,模拟人类操作
|
// 随机延迟,模拟人类操作
|
||||||
@@ -656,7 +661,7 @@ namespace dy.net.job
|
|||||||
if (!videos.Any()) return 0;
|
if (!videos.Any()) return 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await douyinVideoService.batchInsert(videos);
|
await douyinVideoService.BatchInsertOrUpdate(videos);
|
||||||
|
|
||||||
var redowns = await douyinCommonService.GetAllRedown();
|
var redowns = await douyinCommonService.GetAllRedown();
|
||||||
if (redowns != null && redowns.Any())
|
if (redowns != null && redowns.Any())
|
||||||
@@ -798,20 +803,6 @@ namespace dy.net.job
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化是否下载图片视频的设置
|
|
||||||
/// 从环境变量和配置文件中读取设置,环境变量优先级更高
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="config">应用配置</param>
|
|
||||||
private void InitializeDownImageVideoSetting(AppConfig config)
|
|
||||||
{
|
|
||||||
var downImageVideoConfig = Appsettings.Get("DOWN_IMGVIDEO");
|
|
||||||
if (!string.IsNullOrWhiteSpace(downImageVideoConfig))
|
|
||||||
{
|
|
||||||
downImageVideoConfig = downImageVideoConfig.ToLower();
|
|
||||||
_downImageVideo = config.DownImageVideo && downImageVideoConfig == "1";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 清理保存失败的视频文件
|
/// 清理保存失败的视频文件
|
||||||
|
|||||||
@@ -73,6 +73,5 @@ namespace dy.net.model
|
|||||||
/// 自动去重-逻辑是遇到相同ID的视频直接跳过
|
/// 自动去重-逻辑是遇到相同ID的视频直接跳过
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AutoDistinct { get; set; }
|
public bool AutoDistinct { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace dy.net.repository
|
|||||||
var where = this.Db.Queryable<DouyinVideo>()
|
var where = this.Db.Queryable<DouyinVideo>()
|
||||||
//.WhereIF(!string.IsNullOrWhiteSpace(title), x => x.VideoTitle.Contains(title))
|
//.WhereIF(!string.IsNullOrWhiteSpace(title), x => x.VideoTitle.Contains(title))
|
||||||
.WhereIF(!string.IsNullOrWhiteSpace(dto.Title), x => x.VideoTitle.Contains(dto.Title))
|
.WhereIF(!string.IsNullOrWhiteSpace(dto.Title), x => x.VideoTitle.Contains(dto.Title))
|
||||||
.WhereIF(!string.IsNullOrWhiteSpace(dto.Author), x => x.Author == dto.Author)
|
.WhereIF(!string.IsNullOrWhiteSpace(dto.Author), x => x.Author.Contains(dto.Author))
|
||||||
.WhereIF(start.HasValue, x => x.SyncTime >= start.Value)
|
.WhereIF(start.HasValue, x => x.SyncTime >= start.Value)
|
||||||
.WhereIF(end.HasValue, x => x.SyncTime <= end.Value)
|
.WhereIF(end.HasValue, x => x.SyncTime <= end.Value)
|
||||||
.WhereIF(start2.HasValue, x => x.CreateTime >= start2.Value)
|
.WhereIF(start2.HasValue, x => x.CreateTime >= start2.Value)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using dy.net.dto;
|
|||||||
using dy.net.model;
|
using dy.net.model;
|
||||||
using dy.net.repository;
|
using dy.net.repository;
|
||||||
using dy.net.utils;
|
using dy.net.utils;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -21,38 +22,66 @@ namespace dy.net.service
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<bool> batchInsert(List<DouyinVideo> videos)
|
public async Task<bool> BatchInsertOrUpdate(List<DouyinVideo> videos)
|
||||||
{
|
{
|
||||||
|
// 边界处理:传入列表为空直接返回成功
|
||||||
// 边界处理:如果传入的列表为空,直接返回成功(或根据业务返回false)
|
|
||||||
if (videos == null || !videos.Any())
|
if (videos == null || !videos.Any())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// 1. 提取待插入的所有AwemeId(去重,减少数据库查询压力)
|
// 1. 提取所有AwemeId(无需去重,用户保证无重复)
|
||||||
var newAwemeIds = videos.Select(x => x.AwemeId)
|
var allAwemeIds = videos.Select(v => v.AwemeId).ToList();
|
||||||
.Distinct()
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// 2. 查询数据库中已存在的AwemeId(只查需要的字段,提高效率)
|
// 2. 查询数据库中已存在的视频记录(用于后续更新)
|
||||||
var existingAwemeIds = await _dyCollectVideoRepository
|
var existingVideos = await _dyCollectVideoRepository
|
||||||
.Query(x => newAwemeIds.Contains(x.AwemeId)) // 使用Query方法构建查询
|
.Query(x => allAwemeIds.Contains(x.AwemeId))
|
||||||
.Select(x => x.AwemeId) // 只获取AwemeId,减少数据传输
|
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
// 3. 过滤出数据库中不存在的视频(只保留新记录)
|
// 3. 分拆数据集:不存在的(插入)、已存在的(更新)
|
||||||
|
var existingAwemeIdSet = existingVideos.Select(v => v.AwemeId).ToHashSet();
|
||||||
var videosToInsert = videos
|
var videosToInsert = videos
|
||||||
.Where(video => !existingAwemeIds.Contains(video.AwemeId))
|
.Where(v => !existingAwemeIdSet.Contains(v.AwemeId))
|
||||||
|
.ToList();
|
||||||
|
var videosToUpdate = videos
|
||||||
|
.Where(v => existingAwemeIdSet.Contains(v.AwemeId))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// 4. 如果没有需要插入的新记录,直接返回成功
|
// 4. 事务包裹:确保插入/更新原子性
|
||||||
if (!videosToInsert.Any())
|
var transaction = await _dyCollectVideoRepository.UseTranAsync(async () =>
|
||||||
return true;
|
{
|
||||||
|
int insertedCount = 0;
|
||||||
|
int updatedCount = 0;
|
||||||
|
|
||||||
// 5. 批量插入过滤后的新记录
|
// 5. 批量插入新记录
|
||||||
var insertedCount = await _dyCollectVideoRepository.InsertRangeAsync(videosToInsert);
|
if (videosToInsert.Any())
|
||||||
|
{
|
||||||
|
insertedCount = await _dyCollectVideoRepository.InsertRangeAsync(videosToInsert);
|
||||||
|
}
|
||||||
|
|
||||||
// 返回是否插入成功(至少插入一条)
|
// 6. 批量更新已存在记录(核心逻辑)
|
||||||
return insertedCount > 0;
|
if (videosToUpdate.Any())
|
||||||
|
{
|
||||||
|
// 建立AwemeId与待更新数据的映射(O(1)匹配效率)
|
||||||
|
var updateMap = videosToUpdate.ToDictionary(v => v.AwemeId);
|
||||||
|
|
||||||
|
// 遍历已存在实体,赋值需要更新的字段
|
||||||
|
List<DouyinVideo> updates= new List<DouyinVideo>();
|
||||||
|
foreach (var existingVideo in existingVideos)
|
||||||
|
{
|
||||||
|
if (updateMap.TryGetValue(existingVideo.AwemeId, out var updateData))
|
||||||
|
{
|
||||||
|
existingVideo.VideoSavePath = updateData.VideoSavePath;
|
||||||
|
existingVideo.VideoCoverSavePath = updateData.VideoCoverSavePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 批量更新数据库
|
||||||
|
updatedCount = await _dyCollectVideoRepository.UpdateRangeAsync(existingVideos);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, ex =>
|
||||||
|
{
|
||||||
|
Serilog.Log.Error(ex, "批量插入/更新抖音视频失败,AwemeIds:{AwemeIds}", string.Join(",", allAwemeIds));
|
||||||
|
});
|
||||||
|
return transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user