Skip to content

03 Examples

Javid Sh edited this page Dec 1, 2021 · 5 revisions

Grabbing and Muxing Audio and Video Files

The following example is a console application that downloads separate audio/video files from YouTube and merges them into a single media file.

Packages: SharpGrabber, SharpGrabber.YouTube, SharpGrabber.Converter

public static class Program
{
	private static readonly HttpClient Client = new HttpClient();
	private static readonly HashSet<string> TempFiles = new HashSet<string>();

	public static async Task Main(string[] args)
	{
		var grabber = GrabberBuilder.New()
			.UseDefaultServices()
			.AddYouTube()
			.Build();
			
		Console.WriteLine("Enter FFMPEG path:");
		var ffmpegPath = Console.ReadLine().Trim();
		FFmpeg.AutoGen.ffmpeg.RootPath = ffmpegPath;

		Console.WriteLine("Enter a YouTube URL:");
		var url = Console.ReadLine();
		if (url == null)
			return;

		var result = await grabber.GrabAsync(new Uri(url, UriKind.Absolute));

		var audioStream = ChooseMonoMedia(result, MediaChannels.Audio);
		var videoStream = ChooseMonoMedia(result, MediaChannels.Video);
		if (audioStream == null)
			throw new InvalidOperationException("No audio stream detected.");
		if (videoStream == null)
			throw new InvalidOperationException("No video stream detected.");

		try
		{
			var audioPath = await DownloadMedia(audioStream, result);
			var videoPath = await DownloadMedia(videoStream, result);
			GenerateOutputFile(audioPath, videoPath, videoStream);
		}
		finally
		{
			foreach (var tempFile in TempFiles)
			{
				try
				{
					File.Delete(tempFile);
				}
				catch (Exception)
				{
					// ignored
				}
			}
		}
	}

	private static void GenerateOutputFile(string audioPath, string videoPath, GrabbedMedia videoStream)
	{
		Console.WriteLine("Output Path:");
		var outputPath = Console.ReadLine();
		if (string.IsNullOrWhiteSpace(outputPath))
			throw new Exception("No output path is specified.");
		var merger = new MediaMerger(outputPath);
		merger.AddStreamSource(audioPath, MediaStreamType.Audio);
		merger.AddStreamSource(videoPath, MediaStreamType.Video);
		merger.OutputMimeType = videoStream.Format.Mime;
		merger.OutputShortName = videoStream.Format.Extension;
		merger.Build();
		Console.WriteLine($"Output file successfully created.");
	}

	private static GrabbedMedia ChooseMonoMedia(GrabResult result, MediaChannels channel)
	{
		var resources = result.Resources<GrabbedMedia>()
			.Where(m => m.Channels == channel)
			.ToList();

		if (resources.Count == 0)
			return null;

		for (var i = 0; i < resources.Count; i++)
		{
			var resource = resources[i];
			Console.WriteLine($"{i}. {resource.Title ?? resource.FormatTitle ?? resource.Resolution}");
		}

		while (true)
		{
			Console.Write($"Choose the {channel} file: ");
			var choiceStr = Console.ReadLine();
			if (!int.TryParse(choiceStr, out var choice))
			{
				Console.WriteLine("Number expected.");
				continue;
			}

			if (choice < 0 || choice >= resources.Count)
			{
				Console.WriteLine("Invalid number.");
				continue;
			}

			return resources[choice];
		}
	}

	private static async Task<string> DownloadMedia(GrabbedMedia media, IGrabResult grabResult)
	{
		Console.WriteLine("Downloading {0}...", media.Title ?? media.FormatTitle ?? media.Resolution);
		using var response = await Client.GetAsync(media.ResourceUri);
		response.EnsureSuccessStatusCode();
		using var downloadStream = await response.Content.ReadAsStreamAsync();
		using var resourceStream = await grabResult.WrapStreamAsync(downloadStream);
		var path = Path.GetTempFileName();

		using var fileStream = new FileStream(path, FileMode.Create);
		TempFiles.Add(path);
		await resourceStream.CopyToAsync(fileStream);
		return path;
	}
}

Grabbing video from an M3U8 playlist

The following example is a console application that downloads all .ts segment files referenced in an M3U8 playlist file, and concatenates them into a single media file.

Packages: SharpGrabber, SharpGrabber.Hls, SharpGrabber.Converter

class Program
    {
        private static readonly IGrabber _grabber;
        private static readonly HttpClient _client = new();

        static Program()
        {
            _grabber = GrabberBuilder.New()
                .UseDefaultServices()
                .AddHls()
                .Build();
        }

        static async Task Main(string[] args)
        {
            Console.WriteLine("Enter FFMPEG path:");
            var ffmpegPath = Console.ReadLine().Trim();
            FFmpeg.AutoGen.ffmpeg.RootPath = ffmpegPath;

            Console.WriteLine("URL of the M3U8 file: ");
            var url = Console.ReadLine();

            await Grab(new Uri(url));
        }

        private static async Task Grab(Uri uri)
        {
            Console.WriteLine($"Grabbing from {uri}...");
            var grabResult = await _grabber.GrabAsync(uri).ConfigureAwait(false);

            var reference = grabResult.Resource<GrabbedHlsStreamReference>();
            if (reference != null)
            {
                // Redirect to an M3U8 playlist
                await Grab(reference.ResourceUri);
                return;
            }

            var metadataResources = grabResult.Resources<GrabbedHlsStreamMetadata>().ToArray();
            if (metadataResources.Length > 0)
            {
                // Description for one or more M3U8 playlists
                GrabbedHlsStreamMetadata selection;
                if (metadataResources.Length == 1)
                {
                    selection = metadataResources.Single();
                }
                else
                {
                    Console.WriteLine();
                    Console.WriteLine("=== Streams ===");
                    for (var i = 0; i < metadataResources.Length; i++)
                    {
                        var res = metadataResources[i];
                        Console.WriteLine("{0}. {1}", i + 1, $"{res.Name} {res.Resolution}");
                    }
                    Console.Write("Select a stream: ");
                    selection = metadataResources[int.Parse(Console.ReadLine()) - 1];
                }

                // Get information from the HLS stream
                var grabbedStream = await selection.Stream.Value;
                await Grab(grabbedStream, selection, grabResult);
                return;
            }

            throw new Exception("Could not grab the HLS stream.");
        }

        private static async Task Grab(GrabbedHlsStream stream, GrabbedHlsStreamMetadata metadata, GrabResult grabResult)
        {
            Console.WriteLine();
            Console.WriteLine("=== Downloading ===");
            Console.WriteLine("{0} segments", stream.Segments.Count);
            Console.WriteLine("Duration: {0}", stream.Length);

            var tempFiles = new List<string>();
            try
            {
                for (var i = 0; i < stream.Segments.Count; i++)
                {
                    var segment = stream.Segments[i];
                    Console.Write($"Downloading segment #{i + 1} {segment.Title}...");
                    var outputPath = Path.GetTempFileName();
                    tempFiles.Add(outputPath);
                    using var responseStream = await _client.GetStreamAsync(segment.Uri);
                    using var inputStream = await grabResult.WrapStreamAsync(responseStream);
                    using var outputStream = new FileStream(outputPath, FileMode.Create);
                    await inputStream.CopyToAsync(outputStream);
                    Console.WriteLine(" OK");
                }

                CreateOutputFile(tempFiles, metadata);
            }
            finally
            {
                foreach (var tempFile in tempFiles)
                {
                    if (File.Exists(tempFile))
                        File.Delete(tempFile);
                }
                Console.WriteLine("Cleaned up temp files.");
            }
        }

        private static void CreateOutputFile(List<string> tempFiles, GrabbedHlsStreamMetadata metadata)
        {
            Console.WriteLine("All segments were downloaded successfully.");
            Console.Write("Enter a path for the output file: ");
            var outputPath = Console.ReadLine();
            var concatenator = new MediaConcatenator(outputPath)
            {
                OutputMimeType = metadata.OutputFormat.Mime,
                OutputExtension = metadata.OutputFormat.Extension,
            };
            foreach (var tempFile in tempFiles)
                concatenator.AddSource(tempFile);
            concatenator.Build();
            Console.WriteLine("Output file created successfully!");
        }
    }
Clone this wiki locally