feat: add fnOS packaging, storage workflows and release pipeline
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
using dy.net.model.dto;
|
||||
using dy.net.model.entity;
|
||||
using Newtonsoft.Json;
|
||||
using SqlSugar;
|
||||
using MediaStorageType = dy.net.model.dto.StorageType;
|
||||
|
||||
namespace dy.net.service
|
||||
{
|
||||
public class VideoExclusionService
|
||||
{
|
||||
private readonly ISqlSugarClient _db;
|
||||
private readonly VideoTaskService _tasks;
|
||||
|
||||
public VideoExclusionService(ISqlSugarClient db, VideoTaskService tasks)
|
||||
{
|
||||
_db = db;
|
||||
_tasks = tasks;
|
||||
}
|
||||
|
||||
public async Task<VideoExclusionPage> GetPageAsync(VideoExclusionPageRequest request)
|
||||
{
|
||||
request ??= new VideoExclusionPageRequest();
|
||||
var page = Math.Max(1, request.PageIndex);
|
||||
var size = Math.Clamp(request.PageSize, 1, 100);
|
||||
var query = _db.Queryable<DouyinVideoDelete>()
|
||||
.WhereIF(!string.IsNullOrWhiteSpace(request.Keyword), x => x.VideoTitle.Contains(request.Keyword)
|
||||
|| x.ViedoId.Contains(request.Keyword) || x.Author.Contains(request.Keyword));
|
||||
return new VideoExclusionPage
|
||||
{
|
||||
TotalCount = await query.CountAsync(),
|
||||
Items = await query.OrderByDescending(x => x.DeleteTime).Skip((page - 1) * size).Take(size).ToListAsync()
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<UnexcludeVideosResult> UnexcludeAsync(UnexcludeVideosRequest request)
|
||||
{
|
||||
if (request?.Ids == null || request.Ids.Count == 0) throw new InvalidOperationException("请选择要取消排除的视频。");
|
||||
var exclusions = await _db.Queryable<DouyinVideoDelete>().Where(x => request.Ids.Contains(x.Id)).ToListAsync();
|
||||
if (exclusions.Count == 0) throw new KeyNotFoundException("未找到永久排除记录。");
|
||||
var awemeIds = exclusions.Select(x => x.ViedoId).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();
|
||||
var allDuplicates = awemeIds.Count == 0
|
||||
? exclusions
|
||||
: await _db.Queryable<DouyinVideoDelete>().Where(x => awemeIds.Contains(x.ViedoId)).ToListAsync();
|
||||
var snapshots = request.CreateDownloadTask
|
||||
? allDuplicates.Select(TryGetSnapshot).Where(x => x != null).GroupBy(x => x.AwemeId).Select(x => x.First()).ToList()
|
||||
: new List<DouyinVideo>();
|
||||
VideoDownloadTask task = null;
|
||||
var result = new UnexcludeVideosResult();
|
||||
var transaction = await _db.Ado.UseTranAsync(async () =>
|
||||
{
|
||||
if (request.CreateDownloadTask && snapshots.Count > 0)
|
||||
{
|
||||
task = await _tasks.CreateTaskAsync(VideoTaskType.ExclusionRestore, VideoTaskTrigger.UserAction,
|
||||
$"取消排除恢复({snapshots.Count} 条)", storageType: snapshots[0].StorageType);
|
||||
foreach (var video in snapshots)
|
||||
{
|
||||
var exclusion = allDuplicates.First(x => x.ViedoId == video.AwemeId);
|
||||
await _tasks.AddItemAsync(task.Id, video.AwemeId, video.CookieId, null, video.ViedoType,
|
||||
video.VideoTitle, video.Author, video.VideoSavePath,
|
||||
new[] { video.VideoUrl }, video, video.FileSize, workerManaged: true);
|
||||
}
|
||||
}
|
||||
await _db.Deleteable<DouyinVideoDelete>()
|
||||
.Where(x => request.Ids.Contains(x.Id) || awemeIds.Contains(x.ViedoId)).ExecuteCommandAsync();
|
||||
var now = DateTime.Now;
|
||||
if (awemeIds.Count > 0)
|
||||
{
|
||||
var relatedTaskId = task?.Id;
|
||||
await _db.Updateable<VideoDownloadTaskItem>()
|
||||
.SetColumns(x => new VideoDownloadTaskItem
|
||||
{
|
||||
ExclusionReleasedAt = now,
|
||||
RelatedTaskId = relatedTaskId,
|
||||
UpdatedAt = now
|
||||
}).Where(x => awemeIds.Contains(x.AwemeId) && x.SkipReason == VideoTaskSkipReason.PermanentlyExcluded)
|
||||
.ExecuteCommandAsync();
|
||||
}
|
||||
});
|
||||
if (!transaction.IsSuccess) throw new InvalidOperationException("取消排除失败:" + transaction.ErrorMessage);
|
||||
|
||||
result.ReleasedCount = awemeIds.Count;
|
||||
result.QueuedCount = snapshots.Count;
|
||||
result.TaskId = task?.Id;
|
||||
result.CannotQueueIds = request.CreateDownloadTask
|
||||
? awemeIds.Where(id => snapshots.All(x => x.AwemeId != id)).ToList()
|
||||
: new List<string>();
|
||||
result.Message = result.CannotQueueIds.Count > 0
|
||||
? $"已取消排除 {result.ReleasedCount} 条,其中 {result.CannotQueueIds.Count} 条旧记录资料不足,将在下次正常同步时重新发现。"
|
||||
: task == null
|
||||
? $"已取消排除 {result.ReleasedCount} 条,将在下次正常同步时重新发现。"
|
||||
: $"已取消排除并创建 {result.QueuedCount} 条恢复下载任务。";
|
||||
return result;
|
||||
}
|
||||
|
||||
private static DouyinVideo TryGetSnapshot(DouyinVideoDelete exclusion)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(exclusion.RestoreSnapshotJson))
|
||||
{
|
||||
try
|
||||
{
|
||||
var snapshot = JsonConvert.DeserializeObject<DouyinVideo>(exclusion.RestoreSnapshotJson);
|
||||
if (snapshot != null && !string.IsNullOrWhiteSpace(snapshot.AwemeId) && !string.IsNullOrWhiteSpace(snapshot.CookieId)) return snapshot;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(exclusion.ViedoId) || string.IsNullOrWhiteSpace(exclusion.CookieId)
|
||||
|| !exclusion.VideoType.HasValue || string.IsNullOrWhiteSpace(exclusion.VideoUrl)) return null;
|
||||
return new DouyinVideo
|
||||
{
|
||||
Id = Guid.NewGuid().ToString("N"),
|
||||
AwemeId = exclusion.ViedoId,
|
||||
CookieId = exclusion.CookieId,
|
||||
ViedoType = exclusion.VideoType.Value,
|
||||
VideoTitle = exclusion.VideoTitle,
|
||||
VideoSavePath = exclusion.VideoSavePath,
|
||||
VideoUrl = exclusion.VideoUrl,
|
||||
AuthorId = exclusion.AuthorId,
|
||||
Author = exclusion.Author,
|
||||
StorageType = MediaStorageType.Local,
|
||||
SyncTime = DateTime.Now
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user