using System.Net; namespace dy.net.model.dto { public enum MediaDownloadFailureKind { None = 0, SourceUnavailable = 1, SourceForbidden = 2, SourceUnauthorized = 3, SourceNotFound = 4, SourceRateLimited = 5, SourceInvalidContent = 6, StorageUnavailable = 7, IntegrityCheckFailed = 8, Cancelled = 9 } public sealed class MediaDownloadResult { public bool Success { get; init; } public string ActualSavePath { get; init; } public MediaDownloadFailureKind FailureKind { get; init; } public int? HttpStatusCode { get; init; } public string SourceHost { get; init; } public int AttemptedUrlCount { get; init; } public DateTime? RetryAfter { get; init; } public string Message { get; init; } public void Deconstruct(out bool success, out string actualSavePath) { success = Success; actualSavePath = ActualSavePath; } public Exception ToException() => FailureKind switch { MediaDownloadFailureKind.StorageUnavailable => new MediaStorageException(Message ?? "媒体存储写入失败。"), MediaDownloadFailureKind.IntegrityCheckFailed => new MediaIntegrityException(Message ?? "媒体完整性校验失败。"), MediaDownloadFailureKind.Cancelled => new OperationCanceledException(Message ?? "媒体下载已取消。"), _ => new MediaSourceException(FailureKind, Message ?? "媒体来源不可用。", HttpStatusCode, SourceHost, RetryAfter) }; public static MediaDownloadResult Succeeded(string path, string host, int attempted) => new() { Success = true, ActualSavePath = path, SourceHost = host, AttemptedUrlCount = attempted }; } public sealed class MediaSourceException : Exception { public MediaSourceException( MediaDownloadFailureKind kind, string message, int? statusCode = null, string sourceHost = null, DateTime? retryAfter = null, Exception innerException = null) : base(message, innerException) { FailureKind = kind; StatusCode = statusCode; SourceHost = sourceHost; RetryAfter = retryAfter; } public MediaDownloadFailureKind FailureKind { get; } public int? StatusCode { get; } public string SourceHost { get; } public DateTime? RetryAfter { get; } } public sealed class MediaStorageException : IOException { public MediaStorageException(string message, Exception innerException = null) : base(message, innerException) { } } public sealed class MediaIntegrityException : IOException { public MediaIntegrityException(string message, Exception innerException = null) : base(message, innerException) { } } public enum SourceFailureDisposition { Continue = 0, WaitForSource = 1, RequiresAuthorization = 2 } public sealed class SourceAccessDecision { public bool Allowed { get; init; } public bool IsProbe { get; init; } public string Message { get; init; } public DateTime? RetryAt { get; init; } } }