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 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() .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 UnexcludeAsync(UnexcludeVideosRequest request) { if (request?.Ids == null || request.Ids.Count == 0) throw new InvalidOperationException("请选择要取消排除的视频。"); var exclusions = await _db.Queryable().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().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(); 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() .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() .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(); 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(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 }; } } }