feat: add fnOS packaging, storage workflows and release pipeline
This commit is contained in:
@@ -14,6 +14,8 @@ namespace dy.net.service
|
||||
{
|
||||
private readonly ISchedulerFactory _schedulerFactory;
|
||||
private readonly DouyinCookieService douyinCookieService;
|
||||
private readonly DouyinCommonService _commonService;
|
||||
private readonly VideoTaskService _videoTasks;
|
||||
private const string DefaultJobGroup = "dysync.net";
|
||||
private const int DefaultIntervalMinutes = 30;
|
||||
private const int DefaultCronStartDelaySeconds = 30;
|
||||
@@ -85,15 +87,91 @@ namespace dy.net.service
|
||||
"dy.job.key.sync_follow_user_once",
|
||||
"dy.trigger.key.sync_follow_user_once",
|
||||
"抖音关注同步任务(单次执行)")
|
||||
},
|
||||
{
|
||||
VideoTypeEnum.dy_live_monitor,
|
||||
new JobConfig(
|
||||
typeof(DouyinLiveStatusJob),
|
||||
"dy.job.key.live_monitor",
|
||||
"dy.trigger.key.live_monitor",
|
||||
"抖音博主直播状态监测任务")
|
||||
}
|
||||
};
|
||||
|
||||
public DouyinQuartzJobService(ISchedulerFactory schedulerFactory,DouyinCookieService douyinCookieService)
|
||||
public DouyinQuartzJobService(
|
||||
ISchedulerFactory schedulerFactory,
|
||||
DouyinCookieService douyinCookieService,
|
||||
DouyinCommonService commonService,
|
||||
VideoTaskService videoTasks)
|
||||
{
|
||||
_schedulerFactory = schedulerFactory ?? throw new ArgumentNullException(nameof(schedulerFactory));
|
||||
this.douyinCookieService = douyinCookieService;
|
||||
_commonService = commonService;
|
||||
_videoTasks = videoTasks;
|
||||
}
|
||||
|
||||
public async Task<List<VideoDownloadTask>> TriggerVideoJobsNowAsync(VideoTypeEnum? requestedType = null)
|
||||
{
|
||||
var cookies = await douyinCookieService.GetOpendCookiesAsync();
|
||||
if (cookies == null || cookies.Count == 0) throw new InvalidOperationException("没有已启用的抖音授权账号。");
|
||||
var storageType = _commonService.GetConfig()?.StorageType ?? StorageType.Local;
|
||||
var enabled = GetTaskEnableConditions(cookies, storageType);
|
||||
var types = new List<VideoTypeEnum>();
|
||||
if (enabled.IsFavoriteEnabled) types.Add(VideoTypeEnum.dy_favorite);
|
||||
if (enabled.IsCollectEnabled) types.Add(VideoTypeEnum.dy_collects);
|
||||
if (enabled.IsFollowedEnabled) types.Add(VideoTypeEnum.dy_follows);
|
||||
if (enabled.IsCustomCollectEnabled) types.Add(VideoTypeEnum.dy_custom_collect);
|
||||
if (enabled.IsMixEnabled) types.Add(VideoTypeEnum.dy_mix);
|
||||
if (enabled.IsSeriesEnabled) types.Add(VideoTypeEnum.dy_series);
|
||||
if (types.Count == 0) throw new InvalidOperationException("没有已启用且路径完整的视频同步类型。");
|
||||
if (requestedType.HasValue)
|
||||
{
|
||||
if (!IsVideoSyncType(requestedType.Value))
|
||||
throw new InvalidOperationException("指定的任务类型不是可手动执行的视频同步类型。");
|
||||
if (!types.Contains(requestedType.Value))
|
||||
{
|
||||
var message = requestedType.Value == VideoTypeEnum.dy_follows
|
||||
? "关注视频同步未启用:请检查账号的关注下载开关、当前存储的关注路径,以及是否有博主开启同步。"
|
||||
: $"{requestedType.Value.GetDesc()}同步未启用或当前存储路径未配置。";
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
types = new List<VideoTypeEnum> { requestedType.Value };
|
||||
}
|
||||
|
||||
var scheduler = await _schedulerFactory.GetScheduler();
|
||||
var created = new List<VideoDownloadTask>();
|
||||
foreach (var type in types)
|
||||
{
|
||||
var config = JobConfigs[type];
|
||||
var jobKey = new JobKey(config.JobKey, DefaultJobGroup);
|
||||
if (!await scheduler.CheckExists(jobKey))
|
||||
await StartJobAsync(type, (_commonService.GetConfig()?.Cron ?? DefaultIntervalMinutes).ToString());
|
||||
var task = await _videoTasks.CreateTaskAsync(VideoTaskType.Sync, VideoTaskTrigger.Manual,
|
||||
$"{type.GetDesc()}手动同步", type, storageType);
|
||||
try
|
||||
{
|
||||
await scheduler.TriggerJob(jobKey, new JobDataMap
|
||||
{
|
||||
["video-task-id"] = task.Id,
|
||||
["video-task-trigger"] = "manual"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var message = $"{type.GetDesc()}手动同步未能进入调度,请稍后重试。";
|
||||
await _videoTasks.CompleteTaskAsync(task.Id, message);
|
||||
Log.Error(ex, "手动同步任务调度失败:{VideoType}, TaskId={TaskId}", type, task.Id);
|
||||
throw new InvalidOperationException(message, ex);
|
||||
}
|
||||
created.Add(task);
|
||||
}
|
||||
return created;
|
||||
}
|
||||
|
||||
internal static bool IsVideoSyncType(VideoTypeEnum type) => type is
|
||||
VideoTypeEnum.dy_favorite or VideoTypeEnum.dy_collects or VideoTypeEnum.dy_follows or
|
||||
VideoTypeEnum.dy_custom_collect or VideoTypeEnum.dy_mix or VideoTypeEnum.dy_series;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化或重启所有抖音定时任务
|
||||
@@ -126,7 +204,8 @@ namespace dy.net.service
|
||||
await RemoveAllExistingJobs(scheduler);
|
||||
|
||||
// 4. 检查各类型任务的启用条件
|
||||
var taskEnableConditions = GetTaskEnableConditions(validCookies);
|
||||
var storageType = _commonService.GetConfig()?.StorageType ?? StorageType.Local;
|
||||
var taskEnableConditions = GetTaskEnableConditions(validCookies, storageType);
|
||||
|
||||
// 5. 启动符合条件的定时任务
|
||||
int successfullyStartedJobs = 0;
|
||||
@@ -144,6 +223,14 @@ namespace dy.net.service
|
||||
continue;
|
||||
}
|
||||
|
||||
// 直播监测完全独立于视频同步,固定每5分钟检查已单独开启的博主。
|
||||
if (jobKey == VideoTypeEnum.dy_live_monitor)
|
||||
{
|
||||
bool startSuccess = await StartSingleJobAsync(jobKey, "5");
|
||||
if (startSuccess) successfullyStartedJobs++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据不同任务类型和启用条件启动任务
|
||||
bool isTaskEnabled = jobKey switch
|
||||
{
|
||||
@@ -196,16 +283,40 @@ namespace dy.net.service
|
||||
/// </summary>
|
||||
/// <param name="cookies">有效的抖音Cookie列表</param>
|
||||
/// <returns>任务启用条件集合</returns>
|
||||
private static TaskEnableConditions GetTaskEnableConditions(IEnumerable<DouyinCookie> cookies)
|
||||
private static TaskEnableConditions GetTaskEnableConditions(IEnumerable<DouyinCookie> cookies, StorageType storageType)
|
||||
{
|
||||
bool HasPath(DouyinCookie cookie, VideoTypeEnum type)
|
||||
{
|
||||
if (storageType.IsRemote())
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
VideoTypeEnum.dy_favorite => !string.IsNullOrWhiteSpace(cookie.WebDavFavoritePath),
|
||||
VideoTypeEnum.dy_follows => !string.IsNullOrWhiteSpace(cookie.WebDavFollowPath),
|
||||
VideoTypeEnum.dy_mix => !string.IsNullOrWhiteSpace(cookie.WebDavMixPath),
|
||||
VideoTypeEnum.dy_series => !string.IsNullOrWhiteSpace(cookie.WebDavSeriesPath),
|
||||
_ => !string.IsNullOrWhiteSpace(cookie.WebDavCollectPath)
|
||||
};
|
||||
}
|
||||
|
||||
return type switch
|
||||
{
|
||||
VideoTypeEnum.dy_favorite => !string.IsNullOrWhiteSpace(cookie.FavSavePath),
|
||||
VideoTypeEnum.dy_follows => !string.IsNullOrWhiteSpace(cookie.UpSavePath),
|
||||
VideoTypeEnum.dy_mix => !string.IsNullOrWhiteSpace(cookie.MixPath) || !string.IsNullOrWhiteSpace(cookie.SavePath),
|
||||
VideoTypeEnum.dy_series => !string.IsNullOrWhiteSpace(cookie.SeriesPath) || !string.IsNullOrWhiteSpace(cookie.SavePath),
|
||||
_ => !string.IsNullOrWhiteSpace(cookie.SavePath)
|
||||
};
|
||||
}
|
||||
|
||||
return new TaskEnableConditions
|
||||
{
|
||||
IsCollectEnabled = cookies.Any(x => x.DownCollect && !x.UseCollectFolder && !string.IsNullOrWhiteSpace(x.SavePath)),
|
||||
IsFavoriteEnabled = cookies.Any(x => x.DownFavorite && !string.IsNullOrWhiteSpace(x.FavSavePath)),
|
||||
IsFollowedEnabled = cookies.Any(x => x.DownFollowd && !string.IsNullOrWhiteSpace(x.UpSavePath)),
|
||||
IsMixEnabled = cookies.Any(x => x.DownMix && !string.IsNullOrWhiteSpace(x.MixPath)),
|
||||
IsSeriesEnabled = cookies.Any(x => x.DownSeries && !string.IsNullOrWhiteSpace(x.SeriesPath)),
|
||||
IsCustomCollectEnabled = cookies.Any(x => x.UseCollectFolder && !string.IsNullOrWhiteSpace(x.SavePath))
|
||||
IsCollectEnabled = cookies.Any(x => x.DownCollect && !x.UseCollectFolder && HasPath(x, VideoTypeEnum.dy_collects)),
|
||||
IsFavoriteEnabled = cookies.Any(x => x.DownFavorite && HasPath(x, VideoTypeEnum.dy_favorite)),
|
||||
IsFollowedEnabled = cookies.Any(x => x.DownFollowd && HasPath(x, VideoTypeEnum.dy_follows)),
|
||||
IsMixEnabled = cookies.Any(x => x.DownMix && HasPath(x, VideoTypeEnum.dy_mix)),
|
||||
IsSeriesEnabled = cookies.Any(x => x.DownSeries && HasPath(x, VideoTypeEnum.dy_series)),
|
||||
IsCustomCollectEnabled = cookies.Any(x => x.UseCollectFolder && HasPath(x, VideoTypeEnum.dy_custom_collect))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -423,4 +534,4 @@ namespace dy.net.service
|
||||
public bool IsCustomCollectEnabled { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user