-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Refactored code into separate .cs files, removed progress bar and updated dependencies - Splitting the logic in Program.cs into separate .cs files for improved maintainability and modularity. - Replaced the progress bar with a percentage indicator for simplicity and better user experience. - Updated the yt-dlp library nuget package to the latest version for bug fixes and performance improvements. - Added handling for Reddit video IDs, allowing for seamless playback of Reddit-hosted videos. Signed-off-by: DontEatOreo <57304299+DontEatOreo@users.noreply.github.com>
- Loading branch information
1 parent
e037bab
commit a848db8
Showing
6 changed files
with
353 additions
and
331 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
using Xabe.FFmpeg; | ||
using static dis.Globals; | ||
|
||
namespace dis; | ||
|
||
public class Converter | ||
{ | ||
public static async Task ConvertVideo(string videoFilePath, | ||
string? resolution, | ||
bool generateRandomFileName, | ||
string outputDirectory, | ||
int crf, | ||
int audioBitRate, | ||
string? videoCodec) | ||
{ | ||
if (!ResolutionList.Contains(resolution)) | ||
resolution = null; | ||
|
||
var videoCodecEnum = videoCodec is null | ||
? VideoCodec.libx264 | ||
: ValidVideoCodesMap[videoCodec]; | ||
|
||
var compressedVideoPath = ReplaceVideoExtension(videoFilePath, videoCodecEnum); | ||
|
||
var uuid = Guid.NewGuid().ToString()[..4]; | ||
var outputFileName = Path.GetFileName(compressedVideoPath); | ||
if (generateRandomFileName) | ||
outputFileName = $"{uuid}{Path.GetExtension(compressedVideoPath)}"; | ||
|
||
if (File.Exists(Path.Combine(outputDirectory, outputFileName))) | ||
outputFileName = $"{Path.GetFileNameWithoutExtension(outputFileName)}-{uuid}{Path.GetExtension(compressedVideoPath)}"; | ||
|
||
var outputFilePath = Path.Combine(outputDirectory, outputFileName); | ||
|
||
var mediaInfo = await FFmpeg.GetMediaInfo(videoFilePath); | ||
var videoStream = mediaInfo.VideoStreams.FirstOrDefault(); | ||
var audioStream = mediaInfo.AudioStreams.FirstOrDefault(); | ||
|
||
if (videoStream is null && audioStream is null) | ||
{ | ||
Console.Error.WriteLine("There is no video or audio stream in the file"); | ||
Environment.Exit(1); | ||
} | ||
|
||
if (videoStream != null && resolution != null) | ||
SetResolution(videoStream, resolution); | ||
|
||
var conversion = FFmpeg.Conversions.New() | ||
.SetPreset(ConversionPreset.VerySlow) | ||
.SetPixelFormat(PixelFormat.yuv420p10le) | ||
.AddParameter($"-crf {crf}"); | ||
|
||
if (videoStream != null) | ||
{ | ||
AddOptimizedFilter(conversion, videoStream, videoCodecEnum); | ||
conversion.AddStream(videoStream); | ||
} | ||
|
||
if (audioStream != null) | ||
{ | ||
audioStream.SetBitrate(audioBitRate); | ||
audioStream.SetCodec(videoCodecEnum is VideoCodec.vp8 or VideoCodec.vp9 or VideoCodec.av1 | ||
? AudioCodec.libopus | ||
: AudioCodec.aac); | ||
conversion.AddStream(audioStream); | ||
} | ||
|
||
Progress.FFmpegProgressBar(conversion); | ||
conversion.SetOutput(outputFilePath); | ||
await conversion.Start(); | ||
Console.CancelKeyPress += (_, args) => | ||
{ | ||
if (args.SpecialKey is not ConsoleSpecialKey.ControlC) | ||
return; | ||
File.Delete(outputFilePath); | ||
File.Delete(videoFilePath); | ||
Console.WriteLine("Canceled"); | ||
}; | ||
Console.WriteLine($"Converted video saved at: {outputFilePath}"); | ||
} | ||
|
||
private static string ReplaceVideoExtension(string videoPath, VideoCodec videoCodec) | ||
{ | ||
var extension = string.Empty; | ||
foreach (var item in ValidVideoExtensionsMap | ||
.Where(item => item.Item2 == videoCodec)) | ||
{ | ||
extension = item.Item1; | ||
break; | ||
} | ||
|
||
return Path.ChangeExtension(videoPath, extension); | ||
} | ||
|
||
private static void SetResolution(IVideoStream videoStream, string resolution) | ||
{ | ||
double originalWidth = videoStream.Width; | ||
double originalHeight = videoStream.Height; | ||
|
||
// Parse the resolution input string (remove the "p" suffix) | ||
var resolutionInt = int.Parse(resolution[..^1]); | ||
|
||
// Calculate the aspect ratio of the input video | ||
var aspectRatio = originalWidth / originalHeight; | ||
|
||
// Calculate the output width and height based on the desired resolution and aspect ratio | ||
var outputWidth = (int)Math.Round(resolutionInt * aspectRatio); | ||
var outputHeight = resolutionInt; | ||
|
||
// Round the output width and height to even numbers | ||
outputWidth -= outputWidth % 2; | ||
outputHeight -= outputHeight % 2; | ||
|
||
// Set the output size | ||
videoStream.SetSize(outputWidth, outputHeight); | ||
} | ||
|
||
|
||
private static void AddOptimizedFilter(IConversion conversion, IVideoStream videoStream, VideoCodec videoCodec) | ||
{ | ||
switch (videoCodec) | ||
{ | ||
case VideoCodec.av1: | ||
{ | ||
conversion.AddParameter(string.Join(" ", Av1Args)); | ||
{ | ||
switch (videoStream.Framerate) | ||
{ | ||
case < 24: | ||
conversion.AddParameter("-cpu-used 2"); | ||
break; | ||
case > 60: | ||
conversion.AddParameter("-cpu-used 6"); | ||
break; | ||
case > 30 and < 60: | ||
conversion.AddParameter("-cpu-used 4"); | ||
break; | ||
} | ||
break; | ||
} | ||
} | ||
case VideoCodec.vp9: | ||
conversion.AddParameter(string.Join(" ", Vp9Args)); | ||
break; | ||
} | ||
videoStream.SetCodec(videoCodec); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
using Pastel; | ||
using YoutubeDLSharp; | ||
using YoutubeDLSharp.Metadata; | ||
using static dis.Globals; | ||
|
||
namespace dis; | ||
|
||
public class Downloader | ||
{ | ||
public static async Task<(bool, string? videoId)> DownloadTask(string url, | ||
bool keepWaterMarkValue, | ||
bool sponsorBlockValue) | ||
{ | ||
YoutubeDl.OutputFolder = TempDir; // Set the output folder to the temp directory | ||
|
||
RunResult<VideoData> videoInfo = await YoutubeDl.RunVideoDataFetch(url); | ||
if (!videoInfo.Success) | ||
{ | ||
Console.Error.WriteLine("Failed to fetch video data".Pastel(ConsoleColor.Red)); | ||
return (false, null); | ||
} | ||
|
||
var videoId = videoInfo.Data.ID; | ||
|
||
var isLive = videoInfo.Data.IsLive ?? false; | ||
if (isLive) | ||
{ | ||
Console.Error.WriteLine("Live streams are not supported".Pastel(ConsoleColor.Red)); | ||
return (false, null); | ||
} | ||
|
||
/* | ||
* Previously you could've download TikTok videos without water mark just by using the "download_addr-2". | ||
* But now TikTok has changed the format id to "h264_540p_randomNumber-0" so we need to get the random number | ||
*/ | ||
|
||
bool videoDownload; | ||
if (url.Contains("tiktok") && keepWaterMarkValue) | ||
{ | ||
var tikTokValue = videoInfo.Data.Formats | ||
.Where(format => !string.IsNullOrEmpty(format.FormatId) && format.FormatId.Contains("h264_540p_")) | ||
.Select(format => format.FormatId.Split('_').Last().Split('-').First()) | ||
.FirstOrDefault(); | ||
|
||
await YoutubeDl.RunVideoDownload(url, | ||
progress: Progress.YtDlProgress, | ||
overrideOptions: new YoutubeDLSharp.Options.OptionSet | ||
{ | ||
Format = $"h264_540p_{tikTokValue}-0" | ||
}); | ||
videoDownload = true; | ||
} | ||
else if (url.Contains("youtu") && sponsorBlockValue) | ||
{ | ||
await YoutubeDl.RunVideoDownload(url, | ||
progress: Progress.YtDlProgress, | ||
overrideOptions: new YoutubeDLSharp.Options.OptionSet | ||
{ | ||
SponsorblockRemove = "all" | ||
}); | ||
videoDownload = true; | ||
} | ||
else | ||
{ | ||
var runDownload = await YoutubeDl.RunVideoDownload(url, progress: Progress.YtDlProgress); | ||
videoDownload = runDownload.Success; | ||
} | ||
|
||
// New line after the progress bar | ||
Console.WriteLine(); | ||
|
||
// Use reddit post id instead of video id that way you can know from which post the video was downloaded | ||
if (url.Contains("reddit")) | ||
{ | ||
var slashIndex = url.IndexOf("/comments/", StringComparison.Ordinal); // Get the index of the first slash | ||
var endIndex = | ||
url.IndexOf("/", slashIndex + 10, StringComparison.Ordinal); // Get the index of the second slash | ||
videoId = url[(slashIndex + 10)..endIndex]; // Get the video id | ||
} | ||
|
||
if (videoDownload) | ||
return (true, videoId); | ||
|
||
await Console.Error.WriteLineAsync($"{"There was an error downloading the video".Pastel(ConsoleColor.Red)}"); | ||
return (false, null); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
using Xabe.FFmpeg; | ||
using YoutubeDLSharp; | ||
|
||
namespace dis; | ||
|
||
public class Globals | ||
{ | ||
public static readonly YoutubeDL YoutubeDl = new() | ||
{ | ||
FFmpegPath = "ffmpeg", | ||
YoutubeDLPath = "yt-dlp", | ||
OutputFileTemplate = "%(id)s.%(ext)s", | ||
OverwriteFiles = false | ||
}; | ||
|
||
#region Strings | ||
|
||
public static readonly string TempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()[..4]); | ||
|
||
public static readonly string[] ResolutionList = | ||
{ | ||
"144p", | ||
"240p", | ||
"360p", | ||
"480p", | ||
"720p", | ||
"1080p", | ||
"1440p", | ||
"2160p" | ||
}; | ||
|
||
public static readonly Dictionary<string, VideoCodec> ValidVideoCodesMap = new() | ||
{ | ||
{ "h264", VideoCodec.libx264}, | ||
{ "libx264", VideoCodec.libx264}, | ||
{ "h265", VideoCodec.hevc}, | ||
{ "libx265", VideoCodec.hevc}, | ||
{ "hevc", VideoCodec.hevc}, | ||
{ "vp8", VideoCodec.vp8}, | ||
{ "libvpx", VideoCodec.vp8}, | ||
{ "vp9", VideoCodec.vp9}, | ||
{ "libvpx-vp9", VideoCodec.vp9}, | ||
{ "av1", VideoCodec.av1}, | ||
{ "libaom-av1", VideoCodec.av1} | ||
}; | ||
|
||
public static readonly List<(string, VideoCodec)> ValidVideoExtensionsMap = new() | ||
{ | ||
("mp4", VideoCodec.libx264), | ||
("mp4", VideoCodec.hevc), | ||
("webm", VideoCodec.vp8), | ||
("webm", VideoCodec.vp9), | ||
("webm", VideoCodec.av1) | ||
}; | ||
|
||
public static readonly string[] Av1Args = { | ||
"-lag-in-frames 48", | ||
"-row-mt 1", | ||
"-tile-rows 0", | ||
"-tile-columns 1" | ||
}; | ||
|
||
public static readonly string[] Vp9Args = { | ||
"-row-mt 1", | ||
"-lag-in-frames 25", | ||
"-cpu-used 4", | ||
"-auto-alt-ref 1", | ||
"-arnr-maxframes 7", | ||
"-arnr-strength 4", | ||
"-aq-mode 0", | ||
"-enable-tpl 1", | ||
"-row-mt 1", | ||
}; | ||
|
||
#endregion | ||
} |
Oops, something went wrong.