68 lines
2.9 KiB
C#
68 lines
2.9 KiB
C#
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using VideoDownloader.Core.Config;
|
|
using VideoDownloader.Core.Models;
|
|
|
|
namespace VideoDownloader.Core.Dash;
|
|
|
|
/// <summary>
|
|
/// DASH(MPD) 다운로더. MPD 를 사전 파싱해 (a) DRM 여부 검증, (b) 진행률용 총 길이 산출한 뒤
|
|
/// ffmpeg 에게 네이티브 DASH 입력(-i mpd)으로 맡김 — 비디오/오디오 표현 선택·조합·fMP4 처리를
|
|
/// ffmpeg가 알아서 하므로 수동 세그먼트 병합보다 훨씬 견고. 클리어(비DRM) 전용.
|
|
/// </summary>
|
|
public class DashDownloader
|
|
{
|
|
private readonly HttpClient _http;
|
|
private readonly AppConfig _config;
|
|
|
|
private readonly Platform.IMediaMuxer _muxer;
|
|
|
|
public DashDownloader(HttpClient http, AppConfig config) : this(http, config, Platform.DesktopMediaMuxer.Default) { }
|
|
|
|
public DashDownloader(HttpClient http, AppConfig config, Platform.IMediaMuxer muxer) { _http = http; _config = config; _muxer = muxer; }
|
|
|
|
public async Task<string> DownloadAsync(Uri mpdUrl, string outputBase,
|
|
IProgress<DownloadProgress>? progress = null, CancellationToken ct = default)
|
|
{
|
|
string? dir = Path.GetDirectoryName(outputBase);
|
|
if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
|
|
|
|
// 사전 검증: 파싱(또는 DRM) + 총 길이.
|
|
double durationSec = 0;
|
|
try
|
|
{
|
|
var xml = await _http.GetStringAsync(mpdUrl, ct);
|
|
var manifest = DashParser.Parse(xml, mpdUrl);
|
|
if (manifest == null)
|
|
throw new InvalidOperationException("DASH 매니페스트 파싱 실패 (DRM 암호화이거나 미지원 형식)");
|
|
durationSec = manifest.DurationSec;
|
|
}
|
|
catch (OperationCanceledException) { throw; }
|
|
catch (InvalidOperationException) { throw; }
|
|
catch (Exception e)
|
|
{
|
|
throw new InvalidOperationException($"MPD 조회/파싱 실패: {e.Message}", e);
|
|
}
|
|
|
|
var ffmpeg = _muxer.ResolveFfmpeg(_config)
|
|
?? throw new InvalidOperationException("DASH 다운로드에는 ffmpeg가 필요합니다 (시스템 PATH 또는 설정의 ffmpeg 경로).");
|
|
|
|
var final = outputBase + ".mp4";
|
|
progress?.Report(new DownloadProgress(0, 0, "downloading", 0, "DASH 준비"));
|
|
bool success = false;
|
|
try
|
|
{
|
|
await _muxer.DownloadDashAsync(ffmpeg, mpdUrl, final, durationSec, _http, progress, ct);
|
|
if (!File.Exists(final) || new FileInfo(final).Length == 0)
|
|
throw new InvalidOperationException("ffmpeg 출력 파일이 비었거나 없습니다.");
|
|
success = true;
|
|
progress?.Report(new DownloadProgress(0, 0, "downloading", 100, null));
|
|
return final;
|
|
}
|
|
finally
|
|
{
|
|
if (!success) { try { if (File.Exists(final)) File.Delete(final); } catch { } }
|
|
}
|
|
}
|
|
|
|
}
|