using Microsoft.AspNetCore.Mvc; namespace RadicadeServer.CDN { [Route("cdn/video/")] public class VideosCDN : Controller { private static readonly StringComparison PathComparison = OperatingSystem.IsWindows() ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; [HttpGet("{*datablob}")] public IActionResult GetDataBlob(string datablob) { if (string.IsNullOrEmpty(datablob)) return NotFound(); if (datablob.Contains("..", StringComparison.Ordinal) || datablob.Contains('\\') || datablob.Contains(':') || datablob.StartsWith('/')) return NotFound(); var fullPath = Path.GetFullPath(Path.Join(DBConfig.CDNVideos, datablob)); var root = DBConfig.CDNVideos.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar; if (!fullPath.StartsWith(root, PathComparison)) return NotFound(); var candidates = System.IO.File.Exists(fullPath) ? new[] { fullPath } : new[] { fullPath + ".mp4", fullPath + ".m4v", fullPath + ".webm", fullPath + ".mov", fullPath + ".ogv" }; var existing = candidates.FirstOrDefault(p => System.IO.File.Exists(p)); if (existing == null) return NotFound(); var ext = Path.GetExtension(existing).ToLowerInvariant(); var contentType = ext switch { ".mp4" or ".m4v" => "video/mp4", ".webm" => "video/webm", ".mov" => "video/quicktime", ".ogv" => "video/ogg", _ => "application/octet-stream" }; Response.Headers["Accept-Ranges"] = "bytes"; return PhysicalFile(existing, contentType, enableRangeProcessing: true); } } }