using System.Text; namespace VideoDownloader.Core.Util; public static class FileNameUtil { private static readonly char[] Invalid = Path.GetInvalidFileNameChars(); // 무의미한 제네릭 이름 (이름 추출 시 건너뜀) private static readonly HashSet GenericNames = new(StringComparer.OrdinalIgnoreCase) { "index", "playlist", "playlists", "master", "chunklist", "main", "play", "media", "video", "stream", "streaming", "vod", "output", "file", "hls", "dash", "manifest", "seg", "segment", "test", "default" }; /// 파일명으로 사용 불가능한 문자를 '_'로 치환하고 길이를 제한한다. public static string Sanitize(string? name, int maxLen = 120) { if (string.IsNullOrWhiteSpace(name)) return "video"; var extra = new[] { '"', '<', '>', '|', '*', '?' }; var sb = new StringBuilder(); foreach (var c in name.Trim()) { if (Array.IndexOf(Invalid, c) >= 0 || Array.IndexOf(extra, c) >= 0) sb.Append('_'); else sb.Append(c); } var result = sb.ToString().Trim().Trim('.'); if (string.IsNullOrWhiteSpace(result)) result = "video"; return result.Length > maxLen ? result[..maxLen].Trim() : result; } /// 미디어 URL 에서 '진짜 파일명'을 추출 (페이지 타이틀 사용 X). public static string DeriveName(Uri url) { var segs = url.Segments .Select(s => Uri.UnescapeDataString(s).Trim('/')) .Where(s => s.Length > 0) .ToArray(); for (int i = segs.Length - 1; i >= 0; i--) { var withoutExt = Path.GetFileNameWithoutExtension(segs[i]); if (string.IsNullOrWhiteSpace(withoutExt)) continue; if (GenericNames.Contains(withoutExt)) continue; // index.m3u8 등은 건너뜀 return Sanitize(withoutExt); } return Sanitize(url.Host); // fallback: 호스트명 } }