1、去重
2、分享 3、视频播放 4、批量删除,重新下载 5、自定义标题(仅博主视频) 6、授权页面去掉博主添加,新增关注列表 7、图文视频合成优化 8、其他优化
This commit is contained in:
+248
-146
@@ -1,201 +1,303 @@
|
||||
using Quartz;
|
||||
using dy.net.dto;
|
||||
using dy.net.job;
|
||||
using Quartz;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace dy.net.service
|
||||
{
|
||||
/// <summary>
|
||||
/// 抖音相关定时任务服务
|
||||
/// </summary>
|
||||
public class DouyinQuartzJobService
|
||||
{
|
||||
private readonly ISchedulerFactory _schedulerFactory;
|
||||
private const string DefaultJobGroup = "group1";
|
||||
private const int DefaultIntervalMinutes = 30;
|
||||
private const int DefaultCronStartDelaySeconds = 30;
|
||||
private const int DefaultSimpleStartDelaySeconds = 3;
|
||||
|
||||
|
||||
|
||||
public DouyinQuartzJobService(ISchedulerFactory schedulerFactory)
|
||||
{
|
||||
_schedulerFactory = schedulerFactory;
|
||||
_schedulerFactory = schedulerFactory ?? throw new ArgumentNullException(nameof(schedulerFactory));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// 启动所有抖音相关定时任务
|
||||
/// </summary>
|
||||
/// <param name="expression"></param>
|
||||
/// <param name="delay"></param>
|
||||
/// <returns></returns>
|
||||
public async Task StartJob(string expression,int delay=5000)
|
||||
/// <param name="expression">Cron表达式或间隔分钟数</param>
|
||||
/// <param name="delayBetweenJobs">任务之间的启动延迟(毫秒)</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public async Task<bool> InitOrReStartAllJobs(string expression, int delayBetweenJobs = 5000)
|
||||
{
|
||||
await StartCollectJob(expression);
|
||||
|
||||
await Task.Delay(delay);
|
||||
//如果是数字则加1分钟,减少并发
|
||||
if (int.TryParse(expression, out int cron))
|
||||
if (string.IsNullOrWhiteSpace(expression))
|
||||
{
|
||||
cron++;
|
||||
expression = cron.ToString();
|
||||
Log.Warning("定时任务表达式为空,使用默认配置");
|
||||
expression = DefaultIntervalMinutes.ToString();
|
||||
}
|
||||
await StartFavoriteJob(expression);
|
||||
|
||||
await Task.Delay(delay);
|
||||
if (int.TryParse(expression, out int cron2))
|
||||
// 按顺序启动任务,避免并发
|
||||
var jobTasks = new List<Task<bool>>
|
||||
{
|
||||
cron2++;
|
||||
expression = cron2.ToString();
|
||||
}
|
||||
await StartUperPostJob(expression);
|
||||
//我收藏的作品
|
||||
StartJobAsync("collect", expression),
|
||||
//我喜欢的作品
|
||||
DelayAndStartJobAsync("favorite", expression, delayBetweenJobs),
|
||||
//关注的用户的作品
|
||||
DelayAndStartJobAsync("uper", expression, delayBetweenJobs * 2),
|
||||
//关注列表
|
||||
DelayAndStartJobAsync("follow_user", expression, delayBetweenJobs * 3)
|
||||
};
|
||||
|
||||
var results = await Task.WhenAll(jobTasks);
|
||||
return results.All(success => success);
|
||||
}
|
||||
|
||||
private async Task<bool> StartCollectJob(string expression)
|
||||
/// <summary>
|
||||
/// 启动关注同步任务(单次执行)
|
||||
/// </summary>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public async Task<bool> StartFollowJobOnceAsync()
|
||||
{
|
||||
return await StartOneTimeJobAsync("follow_user_once");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 延迟后启动任务
|
||||
/// </summary>
|
||||
private async Task<bool> DelayAndStartJobAsync(string jobKey, string expression, int delayMs)
|
||||
{
|
||||
if (delayMs > 0)
|
||||
{
|
||||
await Task.Delay(delayMs);
|
||||
}
|
||||
|
||||
// 如果是数字表达式,自动递增避免并发
|
||||
var adjustedExpression = AdjustExpressionForConcurrency(jobKey, expression);
|
||||
return await StartJobAsync(jobKey, adjustedExpression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调整任务表达式以避免并发
|
||||
/// </summary>
|
||||
private string AdjustExpressionForConcurrency(string jobKey, string expression)
|
||||
{
|
||||
if (!int.TryParse(expression, out int interval))
|
||||
return expression;
|
||||
|
||||
// 根据任务类型递增间隔,避免所有任务同时执行
|
||||
var jobIndex = _jobConfigs.Keys.ToList().IndexOf(jobKey);
|
||||
return (interval + jobIndex).ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动指定定时任务
|
||||
/// </summary>
|
||||
private async Task<bool> StartJobAsync(string configKey, string expression)
|
||||
{
|
||||
if (!_jobConfigs.TryGetValue(configKey, out var jobConfig))
|
||||
{
|
||||
Log.Error("找不到任务配置: {ConfigKey}", configKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var __scheduler1 = await _schedulerFactory.GetScheduler();
|
||||
var jobKey = new JobKey("dy.job.key.collect", "group1");
|
||||
var triggerKey = new TriggerKey("dy.trigger.key.collect", "group1");
|
||||
var scheduler = await _schedulerFactory.GetScheduler();
|
||||
var jobKey = new JobKey(jobConfig.JobKey, DefaultJobGroup);
|
||||
var triggerKey = new TriggerKey(jobConfig.TriggerKey, DefaultJobGroup);
|
||||
|
||||
var jobDetail = await __scheduler1.GetJobDetail(jobKey);
|
||||
if (jobDetail != null)
|
||||
// 删除已存在的任务
|
||||
await RemoveExistingJobAsync(scheduler, jobKey);
|
||||
|
||||
// 创建任务详情
|
||||
var jobDetail = JobBuilder.Create(jobConfig.JobType)
|
||||
.WithIdentity(jobKey)
|
||||
.WithDescription(jobConfig.Description)
|
||||
.Build();
|
||||
|
||||
// 创建触发器
|
||||
var trigger = CreateTrigger(triggerKey, expression, jobConfig.Description);
|
||||
if (trigger == null)
|
||||
{
|
||||
//await __scheduler1.Shutdown();
|
||||
await __scheduler1.DeleteJob(jobKey);//删掉原来的
|
||||
Log.Error("创建触发器失败: {JobDescription}", jobConfig.Description);
|
||||
return false;
|
||||
}
|
||||
|
||||
IJobDetail job = JobBuilder.Create<DouyinCollectSyncJob>()
|
||||
.WithIdentity(jobKey)
|
||||
.Build();
|
||||
ITrigger trigger;
|
||||
//var expression = Appsettings.Get("Interval");
|
||||
if (CronExpression.IsValidExpression(expression))
|
||||
{
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey)
|
||||
.WithCronSchedule(expression)
|
||||
.StartAt(DateTime.Now.AddSeconds(30))
|
||||
.StartNow()
|
||||
.Build();
|
||||
}
|
||||
else
|
||||
{
|
||||
int Interval = int.TryParse(expression, out int _Interval) ? _Interval : 30;
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey)
|
||||
//.StartNow()
|
||||
.StartAt(DateTime.Now.AddSeconds(3))
|
||||
.WithSimpleSchedule(x => x
|
||||
.WithIntervalInMinutes(Interval)
|
||||
.RepeatForever())
|
||||
.Build();
|
||||
}
|
||||
// Tell Quartz to schedule the job using our trigger
|
||||
await __scheduler1.ScheduleJob(job, trigger);
|
||||
// 调度任务
|
||||
await scheduler.ScheduleJob(jobDetail, trigger);
|
||||
Log.Information("启动定时任务成功 - {JobDescription}, 表达式: {Expression}",
|
||||
jobConfig.Description, expression);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Error("start dy.collect job error", ex);
|
||||
Log.Error(ex, "启动定时任务失败 - {JobDescription}", jobConfig.Description);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private async Task<bool> StartFavoriteJob(string expression)
|
||||
/// <summary>
|
||||
/// 启动单次执行任务
|
||||
/// </summary>
|
||||
private async Task<bool> StartOneTimeJobAsync(string configKey)
|
||||
{
|
||||
if (!_jobConfigs.TryGetValue(configKey, out var jobConfig))
|
||||
{
|
||||
Log.Error("找不到任务配置: {ConfigKey}", configKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var __scheduler1 = await _schedulerFactory.GetScheduler();
|
||||
var jobKey = new JobKey("dy.job.key.favorite", "group1");
|
||||
var triggerKey = new TriggerKey("dy.trigger.key.favorite", "group1");
|
||||
var scheduler = await _schedulerFactory.GetScheduler();
|
||||
var jobKey = new JobKey(jobConfig.JobKey, DefaultJobGroup);
|
||||
var triggerKey = new TriggerKey(jobConfig.TriggerKey, DefaultJobGroup);
|
||||
|
||||
var jobDetail = await __scheduler1.GetJobDetail(jobKey);
|
||||
if (jobDetail != null)
|
||||
{
|
||||
//await __scheduler1.Shutdown();
|
||||
await __scheduler1.DeleteJob(jobKey);//删掉原来的
|
||||
}
|
||||
// 删除已存在的任务
|
||||
await RemoveExistingJobAsync(scheduler, jobKey);
|
||||
|
||||
IJobDetail job = JobBuilder.Create<DouyinFavoritSyncJob>()
|
||||
.WithIdentity(jobKey)
|
||||
.Build();
|
||||
ITrigger trigger;
|
||||
//var expression = Appsettings.Get("Interval");
|
||||
if (CronExpression.IsValidExpression(expression))
|
||||
{
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey)
|
||||
.WithCronSchedule(expression)
|
||||
.StartAt(DateTime.Now.AddSeconds(30))
|
||||
.StartNow()
|
||||
.Build();
|
||||
}
|
||||
else
|
||||
{
|
||||
int Interval = int.TryParse(expression, out int _Interval) ? _Interval : 30;
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey)
|
||||
//.StartNow()
|
||||
.StartAt(DateTime.Now.AddSeconds(3))
|
||||
.WithSimpleSchedule(x => x
|
||||
.WithIntervalInMinutes(Interval)
|
||||
.RepeatForever())
|
||||
.Build();
|
||||
}
|
||||
// Tell Quartz to schedule the job using our trigger
|
||||
await __scheduler1.ScheduleJob(job, trigger);
|
||||
// 创建任务详情
|
||||
var jobDetail = JobBuilder.Create(jobConfig.JobType)
|
||||
.WithIdentity(jobKey)
|
||||
.WithDescription(jobConfig.Description)
|
||||
.Build();
|
||||
|
||||
// 创建立即执行的触发器(只执行一次)
|
||||
var trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey)
|
||||
.WithDescription($"{jobConfig.Description} - 单次执行")
|
||||
.StartNow()
|
||||
.Build();
|
||||
|
||||
// 调度任务
|
||||
await scheduler.ScheduleJob(jobDetail, trigger);
|
||||
Log.Information("启动单次任务成功 - {JobDescription}", jobConfig.Description);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Error("start dy.favorite job error", ex);
|
||||
Log.Error(ex, "启动单次任务失败 - {JobDescription}", jobConfig.Description);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> StartUperPostJob(string expression)
|
||||
/// <summary>
|
||||
/// 创建触发器(支持Cron表达式和简单间隔)
|
||||
/// </summary>
|
||||
private ITrigger? CreateTrigger(TriggerKey triggerKey, string expression, string jobDescription)
|
||||
{
|
||||
try
|
||||
// Cron表达式格式
|
||||
if (CronExpression.IsValidExpression(expression))
|
||||
{
|
||||
var __scheduler1 = await _schedulerFactory.GetScheduler();
|
||||
var jobKey = new JobKey("dy.job.key.uper", "group1");
|
||||
var triggerKey = new TriggerKey("dy.trigger.key.uper", "group1");
|
||||
return TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey)
|
||||
.WithDescription($"{jobDescription} - Cron调度")
|
||||
.WithCronSchedule(expression)
|
||||
.StartAt(DateTime.Now.AddSeconds(DefaultCronStartDelaySeconds))
|
||||
.Build();
|
||||
}
|
||||
|
||||
var jobDetail = await __scheduler1.GetJobDetail(jobKey);
|
||||
if (jobDetail != null)
|
||||
{
|
||||
//await __scheduler1.Shutdown();
|
||||
await __scheduler1.DeleteJob(jobKey);//删掉原来的
|
||||
}
|
||||
// 数字间隔格式(分钟)
|
||||
if (int.TryParse(expression, out int intervalMinutes))
|
||||
{
|
||||
intervalMinutes = Math.Max(1, intervalMinutes); // 最小间隔1分钟
|
||||
return TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey)
|
||||
.WithDescription($"{jobDescription} - 间隔{intervalMinutes}分钟")
|
||||
.StartAt(DateTime.Now.AddSeconds(DefaultSimpleStartDelaySeconds))
|
||||
.WithSimpleSchedule(x => x
|
||||
.WithIntervalInMinutes(intervalMinutes)
|
||||
.RepeatForever())
|
||||
.Build();
|
||||
}
|
||||
|
||||
IJobDetail job = JobBuilder.Create<DouyinUperPostSyncJob>()
|
||||
.WithIdentity(jobKey)
|
||||
.Build();
|
||||
ITrigger trigger;
|
||||
//var expression = Appsettings.Get("Interval");
|
||||
if (CronExpression.IsValidExpression(expression))
|
||||
{
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey)
|
||||
.WithCronSchedule(expression)
|
||||
.StartAt(DateTime.Now.AddSeconds(30))
|
||||
.StartNow()
|
||||
.Build();
|
||||
}
|
||||
else
|
||||
{
|
||||
int Interval = int.TryParse(expression, out int _Interval) ? _Interval : 30;
|
||||
trigger = TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey)
|
||||
//.StartNow()
|
||||
.StartAt(DateTime.Now.AddSeconds(3))
|
||||
.WithSimpleSchedule(x => x
|
||||
.WithIntervalInMinutes(Interval)
|
||||
// 无效表达式,使用默认配置
|
||||
Log.Warning("无效的任务表达式: {Expression},使用默认间隔{DefaultMinutes}分钟",
|
||||
expression, DefaultIntervalMinutes);
|
||||
|
||||
return TriggerBuilder.Create()
|
||||
.WithIdentity(triggerKey)
|
||||
.WithDescription($"{jobDescription} - 默认间隔调度")
|
||||
.StartAt(DateTime.Now.AddSeconds(DefaultSimpleStartDelaySeconds))
|
||||
.WithSimpleSchedule(x => x
|
||||
.WithIntervalInMinutes(DefaultIntervalMinutes)
|
||||
.RepeatForever())
|
||||
.Build();
|
||||
}
|
||||
// Tell Quartz to schedule the job using our trigger
|
||||
await __scheduler1.ScheduleJob(job, trigger);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Serilog.Log.Error("start dy.uper job error", ex);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
.Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除已存在的任务
|
||||
/// </summary>
|
||||
private async Task RemoveExistingJobAsync(IScheduler scheduler, JobKey jobKey)
|
||||
{
|
||||
if (await scheduler.CheckExists(jobKey))
|
||||
{
|
||||
Log.Information("移除已存在的任务: {JobKey}", jobKey);
|
||||
await scheduler.DeleteJob(jobKey);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 任务配置信息
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, JobConfig> _jobConfigs = new()
|
||||
{
|
||||
{
|
||||
"collect",
|
||||
new JobConfig(
|
||||
typeof(DouyinCollectSyncJob),
|
||||
"dy.job.key.collect",
|
||||
"dy.trigger.key.collect",
|
||||
"抖音收藏同步任务")
|
||||
},
|
||||
{
|
||||
"favorite",
|
||||
new JobConfig(
|
||||
typeof(DouyinFavoritSyncJob),
|
||||
"dy.job.key.favorite",
|
||||
"dy.trigger.key.favorite",
|
||||
"抖音点赞同步任务")
|
||||
},
|
||||
{
|
||||
"uper",
|
||||
new JobConfig(
|
||||
typeof(DouyinFollowedViedoSyncJob),
|
||||
"dy.job.key.uper",
|
||||
"dy.trigger.key.uper",
|
||||
"抖音UP主作品同步任务")
|
||||
},
|
||||
{
|
||||
"follow_user",
|
||||
new JobConfig(
|
||||
typeof(DouyinFollowedUsersSyncJob),
|
||||
"dy.job.key.follow_user",
|
||||
"dy.trigger.key.follow_user",
|
||||
"抖音关注同步任务")
|
||||
},
|
||||
{
|
||||
"follow_user_once",
|
||||
new JobConfig(
|
||||
typeof(DouyinFollowedUsersSyncJob),
|
||||
"dy.job.key.follow_user_once",
|
||||
"dy.trigger.key.follow_user_once",
|
||||
"抖音关注同步任务(单次执行)")
|
||||
}
|
||||
,
|
||||
//{
|
||||
// "redown_once",
|
||||
// new JobConfig(
|
||||
// typeof(DouyinReDownSyncJob),
|
||||
// "dy.job.key.redown_once",
|
||||
// "dy.trigger.key.redown_once",
|
||||
// "抖音重新下载任务(单次执行)")
|
||||
//}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user