Skip to content

Commit

Permalink
Version 5.0.0
Browse files Browse the repository at this point in the history
- 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
DontEatOreo committed Mar 14, 2023
1 parent e037bab commit a848db8
Show file tree
Hide file tree
Showing 6 changed files with 353 additions and 331 deletions.
148 changes: 148 additions & 0 deletions Converter.cs
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);
}
}
87 changes: 87 additions & 0 deletions Downloader.cs
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);
}
}
76 changes: 76 additions & 0 deletions Globals.cs
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
}
Loading

0 comments on commit a848db8

Please sign in to comment.