using VideoDownloader.Core.Config; using VideoDownloader.Core.Dash; using VideoDownloader.Core.Hls; using VideoDownloader.Core.Models; // [임시] 실제 공개 DASH 매니페스트 파싱 검증: 트랙/세그먼트 구조 출력. if (args.Length > 0 && args[0] == "--dashparse") { var mpdUrl = args.Length > 1 ? args[1] : "https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd"; using var h = new HttpClient(); h.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0"); var xml = await h.GetStringAsync(mpdUrl); Console.WriteLine($"MPD {xml.Length} bytes"); var m = DashParser.Parse(xml, new Uri(mpdUrl)); if (m == null) { Console.WriteLine("PARSE NULL"); return 1; } if (m.Video != null) Console.WriteLine($"VIDEO: init={m.Video.InitUrl} segs={m.Video.Segments.Count} first={m.Video.Segments.FirstOrDefault()?.Url}"); if (m.Audio != null) Console.WriteLine($"AUDIO: init={m.Audio.InitUrl} segs={m.Audio.Segments.Count} first={m.Audio.Segments.FirstOrDefault()?.Url}"); else Console.WriteLine("AUDIO: (없음)"); return 0; } // [임시] 실제 공개 DASH 종단간 다운로드 검증. if (args.Length > 0 && (args[0] == "--dash" || args[0] == "--dashlow")) { var mpdUrl = args.Length > 1 ? args[1] : "https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd"; var outBase = args.Length > 2 ? args[2] : Path.Combine(Directory.GetCurrentDirectory(), "out", "dash"); Directory.CreateDirectory(Path.GetDirectoryName(outBase)!); var dashConfig = new AppConfig { Concurrency = 8 }; using var h = new HttpClient(); h.DefaultRequestHeaders.UserAgent.ParseAdd(dashConfig.UserAgent); h.Timeout = TimeSpan.FromMinutes(5); var dl = new DashDownloader(h, dashConfig); var prog = new Progress(p => { if (p.Stage == "downloading") Console.Write($"\rDASH: {p.Completed}/{p.Total} ({p.Percent,5:F1}%) "); else Console.WriteLine($"\n단계: {p.Stage}"); }); try { var final = await dl.DownloadAsync(new Uri(mpdUrl), outBase, prog); var fi = new FileInfo(final); Console.WriteLine($"\n완료: {final} ({fi.Length / 1048576.0:F2} MB)"); return 0; } catch (Exception e) { Console.WriteLine($"\n오류: {e.GetType().Name}: {e.Message}"); return 1; } } // 엔진 하네스: 공개 샘플 HLS 를 받아 파싱→병렬다운로드→병합→ffmpeg 리먹스까지 검증. // 사용: dotnet run --project VideoDownloader.Cli -- [출력경로(확장자빼고)] // 기본 URL = Mux 공개 테스트스트림(Tears of Steel 일부) if (args.Length > 0 && args[0] is "--help" or "-h") { Console.WriteLine("Usage: dotnet run --project VideoDownloader.Cli -- [outputBase]"); return 0; } var url = args.Length > 0 ? args[0] : "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"; var outputBase = args.Length > 1 ? args[1] : Path.Combine(Directory.GetCurrentDirectory(), "out", "sample"); Console.WriteLine($"대상: {url}"); Console.WriteLine($"출력: {outputBase}.mp4"); var config = new AppConfig { Concurrency = 8 }; using var http = new HttpClient(); http.DefaultRequestHeaders.UserAgent.ParseAdd(config.UserAgent); http.Timeout = TimeSpan.FromMinutes(3); var downloader = new HlsDownloader(http, config); var progress = new Progress(p => { if (p.Stage == "downloading") Console.Write($"\r다운로드: {p.Completed}/{p.Total} ({p.Percent,5:F1}%) "); else Console.WriteLine($"\n단계: {p.Stage} ..."); }); var sw = System.Diagnostics.Stopwatch.StartNew(); try { var final = await downloader.DownloadAsync(new Uri(url), outputBase, progress); sw.Stop(); var fi = new FileInfo(final); Console.WriteLine($"\n완료: {final}"); Console.WriteLine($"크기: {fi.Length / 1024.0 / 1024.0:F2} MB / 소요: {sw.Elapsed.TotalSeconds:F1}s"); return 0; } catch (Exception e) { Console.WriteLine($"\n오류: {e.GetType().Name}: {e.Message}"); return 1; }