diff --git a/Grayjay.ClientServer/Extensions.cs b/Grayjay.ClientServer/Extensions.cs index 15c12376..ffc9e44b 100644 --- a/Grayjay.ClientServer/Extensions.cs +++ b/Grayjay.ClientServer/Extensions.cs @@ -196,6 +196,24 @@ public static string SanitizeFileName(this string fileName) fileName = fileName.Replace(invalidChar, '_'); return fileName; } + + // Windows MAX_PATH is 260; reserve some room for the directory prefix. + private const int MaxFileNameLength = 200; + /// + /// Truncates a file name (after sanitization) so that the base name fits within + /// characters, preserving the extension. + /// + public static string TruncateFileName(this string fileName) + { + if (fileName.Length <= MaxFileNameLength) + return fileName; + + string ext = Path.GetExtension(fileName); // e.g. ".mp4" + string baseName = Path.GetFileNameWithoutExtension(fileName); + int allowedBase = MaxFileNameLength - ext.Length; + if (allowedBase < 1) allowedBase = 1; + return baseName.Substring(0, Math.Min(baseName.Length, allowedBase)) + ext; + } public static string SanitizeFileNameWithPath(this string path) { string dirName = Path.GetDirectoryName(path); diff --git a/Grayjay.ClientServer/Models/Downloads/VideoDownload.cs b/Grayjay.ClientServer/Models/Downloads/VideoDownload.cs index 80461395..c9c80da0 100644 --- a/Grayjay.ClientServer/Models/Downloads/VideoDownload.cs +++ b/Grayjay.ClientServer/Models/Downloads/VideoDownload.cs @@ -321,7 +321,7 @@ public async Task Download(ManagedHttpClient client, Action onProgress, var representation = videoRepresentations?.FirstOrDefault(); var mimeType = representation?.MimeType ?? VideoSource.Container; - VideoFileName = $"{VideoDetails.ID.Value} [{VideoSource.Width}x{VideoSource.Height}].{VideoHelper.VideoContainerToExtension(mimeType)}".SanitizeFileName(); + VideoFileName = $"{VideoDetails.ID.Value} [{VideoSource.Width}x{VideoSource.Height}].{VideoHelper.VideoContainerToExtension(mimeType)}".SanitizeFileName().TruncateFileName(); VideoFilePath = Path.Combine(downloadDir, VideoFileName); } string audioDash = (AudioSource is DashManifestRawAudioSource dAudioSource) ? dAudioSource.Generate() : null; @@ -331,12 +331,12 @@ public async Task Download(ManagedHttpClient client, Action onProgress, var representation = audioRepresentations?.FirstOrDefault(); var mimeType = representation?.MimeType ?? AudioSource.Container; - AudioFileName = $"{VideoDetails.ID.Value} [{AudioSource.Language}-{AudioSource.Bitrate}].{VideoHelper.AudioContainerToExtension(mimeType)}".SanitizeFileName(); + AudioFileName = $"{VideoDetails.ID.Value} [{AudioSource.Language}-{AudioSource.Bitrate}].{VideoHelper.AudioContainerToExtension(mimeType)}".SanitizeFileName().TruncateFileName(); AudioFilePath = Path.Combine(downloadDir , AudioFileName); } if(SubtitleSourcetoUse != null) { - SubtitleFileName = $"{VideoDetails.ID.Value} [{SubtitleSourcetoUse.Name}].{VideoHelper.SubtitleContainerToExtension(SubtitleSourcetoUse.Format)}".SanitizeFileName(); + SubtitleFileName = $"{VideoDetails.ID.Value} [{SubtitleSourcetoUse.Name}].{VideoHelper.SubtitleContainerToExtension(SubtitleSourcetoUse.Format)}".SanitizeFileName().TruncateFileName(); SubtitleFilePath = Path.Combine(downloadDir, SubtitleFileName); } var progressLock = new Object();