<commit_before>﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using Palaso.Extensions;
using Palaso.IO;


namespace Palaso.Media
{
	/// <summary>
	/// This class uses ffmpeg to gather information about media streams
	/// </summary>
	public class MediaInfo
	{
		/// <summary>
		/// The actual ffmpeg output
		/// </summary>
		public string RawData;

		private  MediaInfo(string ffmpegOutput)
		{
			RawData = ffmpegOutput;
			Audio = new AudioInfo(ffmpegOutput);
			if (ffmpegOutput.Contains("Video:"))
			{
				Video = new VideoInfo(ffmpegOutput);
			}

		}



		///<summary>
		/// Returns false if it can't find ffmpeg
		///</summary>
		static public bool HaveNecessaryComponents
		{
			get
			{
				return FFmpegRunner.HaveNecessaryComponents;
			}
		}

		static public MediaInfo GetInfo(string path)
		{
			if(!HaveNecessaryComponents)
			{
				return new MediaInfo("Could not locate FFMpeg");
			}
			var p = new Process();
			p.StartInfo.FileName = FFmpegRunner.LocateAndRememberFFmpeg();
			p.StartInfo.Arguments = ( "-i " + "\""+path+"\"");
			p.StartInfo.RedirectStandardError = true;
			p.StartInfo.UseShellExecute = false;
			p.StartInfo.CreateNoWindow = true;
			p.Start();

			string ffmpegOutput = p.StandardError.ReadToEnd();
			if(!p.WaitForExit(3000))
			{
				try
				{
					p.Kill();
				}
				catch(Exception)
				{ //couldn't stop it, oh well.
				}
				throw new ApplicationException("FFMpeg seems to have hung");
			}
			return new MediaInfo(ffmpegOutput);
		}

		public AudioInfo Audio { get; private set;}
		public VideoInfo Video { get; private set;}
		//future public ImageInfo Image { get; private set;}

		/// <summary>
		/// used by both audio and video, so it lives up here
		/// </summary>
		internal static TimeSpan GetDuration(string ffmpegOutput)
		{
				var match = Regex.Match(ffmpegOutput, "Duration: ([^,]*),");
				if (match.Groups.Count == 2)
				{
					var durationString = match.Groups[1].Value;

					TimeSpan durationTime;
					if (TimeSpan.TryParse(durationString, out durationTime))
					{
						return durationTime;
					}
				}
				return default(TimeSpan);
		}


		public class AudioInfo
		{

			internal AudioInfo(string ffmpegOutput)
			{
				ExtractDuration(ffmpegOutput);
				ExtractBitDepth(ffmpegOutput);
				ExtractSamplesPerSecond(ffmpegOutput);
				ExtractChannels(ffmpegOutput);
				ExtractEncoding(ffmpegOutput);
			}

			private void ExtractEncoding(string ffmpegOutput)
			{

				var match = Regex.Match(ffmpegOutput, "Audio: ([^,]*)");
				if (match.Groups.Count == 2)
				{
					Encoding = match.Groups[1].Value;
				}
			}

			private void ExtractChannels(string ffmpegOutput)
			{
				var match = Regex.Match(ffmpegOutput, ", ([^,]*) channels");
				if (match.Groups.Count == 2)
				{
					var value = match.Groups[1].Value;

					int channelCount;

					if (int.TryParse(value, out channelCount))
					{
						this.ChannelCount = channelCount;
					}
				}
			}

			private void ExtractSamplesPerSecond(string ffmpegOutput)
			{
				var match = Regex.Match(ffmpegOutput, ", ([^,]*) Hz");
				if (match.Groups.Count == 2)
				{
					var value = match.Groups[1].Value;

					int frequency;

					if (int.TryParse(value, out frequency))
					{
						this.SamplesPerSecond = frequency;
					}
				}
			}

			private void ExtractBitDepth(string ffmpegOutput)
			{

				var match = Regex.Match(ffmpegOutput, @"pcm_s(\d+)");
				if (match.Groups.Count == 2)
				{
					var value = match.Groups[1].Value;
<commit_msg>Fix MediaInfo channel info on Trusty<commit_after>﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using Palaso.Extensions;
using Palaso.IO;


namespace Palaso.Media
{
	/// <summary>
	/// This class uses ffmpeg to gather information about media streams
	/// </summary>
	public class MediaInfo
	{
		/// <summary>
		/// The actual ffmpeg output
		/// </summary>
		public string RawData;

		private  MediaInfo(string ffmpegOutput)
		{
			RawData = ffmpegOutput;
			Audio = new AudioInfo(ffmpegOutput);
			if (ffmpegOutput.Contains("Video:"))
			{
				Video = new VideoInfo(ffmpegOutput);
			}
		}

		///<summary>
		/// Returns false if it can't find ffmpeg
		///</summary>
		static public bool HaveNecessaryComponents
		{
			get
			{
				return FFmpegRunner.HaveNecessaryComponents;
			}
		}

		static public MediaInfo GetInfo(string path)
		{
			if(!HaveNecessaryComponents)
			{
				return new MediaInfo("Could not locate FFMpeg");
			}
			var p = new Process();
			p.StartInfo.FileName = FFmpegRunner.LocateAndRememberFFmpeg();
			p.StartInfo.Arguments = ( "-i " + "\""+path+"\"");
			p.StartInfo.RedirectStandardError = true;
			p.StartInfo.UseShellExecute = false;
			p.StartInfo.CreateNoWindow = true;
			p.Start();

			string ffmpegOutput = p.StandardError.ReadToEnd();
			if(!p.WaitForExit(3000))
			{
				try
				{
					p.Kill();
				}
				catch(Exception)
				{ //couldn't stop it, oh well.
				}
				throw new ApplicationException("FFMpeg seems to have hung");
			}
			return new MediaInfo(ffmpegOutput);
		}

		public AudioInfo Audio { get; private set;}
		public VideoInfo Video { get; private set;}
		//future public ImageInfo Image { get; private set;}

		/// <summary>
		/// used by both audio and video, so it lives up here
		/// </summary>
		internal static TimeSpan GetDuration(string ffmpegOutput)
		{
				var match = Regex.Match(ffmpegOutput, "Duration: ([^,]*),");
				if (match.Groups.Count == 2)
				{
					var durationString = match.Groups[1].Value;

					TimeSpan durationTime;
					if (TimeSpan.TryParse(durationString, out durationTime))
					{
						return durationTime;
					}
				}
				return default(TimeSpan);
		}

		public class AudioInfo
		{

			internal AudioInfo(string ffmpegOutput)
			{
				ExtractDuration(ffmpegOutput);
				ExtractBitDepth(ffmpegOutput);
				ExtractSamplesPerSecond(ffmpegOutput);
				ExtractChannels(ffmpegOutput);
				ExtractEncoding(ffmpegOutput);
			}

			private void ExtractEncoding(string ffmpegOutput)
			{

				var match = Regex.Match(ffmpegOutput, "Audio: ([^,]*)");
				if (match.Groups.Count == 2)
				{
					Encoding = match.Groups[1].Value;
				}
			}

			private void ExtractChannels(string ffmpegOutput)
			{
				var regex = new Regex(", ([^,]*) channels");
				if (regex.IsMatch(ffmpegOutput))
				{
					var match = regex.Match(ffmpegOutput);
					var value = match.Groups[1].Value;

					int channelCount;

					if (int.TryParse(value, out channelCount))
					{
						this.ChannelCount = channelCount;
					}
					return;
				}
				// older versions (i.e. 9.18 which comes with trusty) have a slightly different
				// output format
				regex = new Regex(", (mono|stereo|quad|hexagonal|octagonal|downmix|[2-7].[0-1](\\(.\\))?),");
				if (regex.IsMatch(ffmpegOutput))
				{
					var match = regex.Match(ffmpegOutput);
					var value = match.Groups[1].Value;
					switch (value)
					{
						case "mono":
							this.ChannelCount = 1;
							break;
						case "stereo":
						case "downmix":
							this.ChannelCount = 2;
							break;
						case "2.1":
						case "3.0":
						case "3.0(back)":
							this.ChannelCount = 3;
							break;
						case "3.1":
						case "4.0":
						case "quad":
						case "quad(side)":
							this.ChannelCount = 4;
							break;
						case "4.1":
						case "5.0":
							this.ChannelCount = 5;
							break;
						case "5.1":
						case "6.0":
						case "6.0(front)":
						case "hexagonal":
							this.ChannelCount = 6;
							break;
						case "6.1":
						case "6.1(front)":
						case "7.0":
						case "7.0(front)":
							this.ChannelCount = 7;
							break;
						case "7.1":
						case "7.1(wide)":
						case "octagonal":
							this.ChannelCount = 8;
							break;
					}
				}
			}

			private void ExtractSamplesPerSecond(string ffmpegOutput)
			{
				var match = Regex.Match(ffmpegOutput, ", ([^,]*) Hz");
				if (match.Groups.Count == 2)
				{
					var value = match.Groups[1].Value;

					int frequency;

					if (int.TryParse(value, out frequency))
					{
						this.SamplesPerSecond = frequency;
					}
				}
			}

			private void ExtractBitDepth(string ffmpegOutput)
			{

				var match = Regex.Match(ffmpegOutput, @"pcm_s(\d+)");
				if (match.Groups.Count == 2)
				{
					var value = match.Groups[1].Value;

{'fraction_non_alphanumeric': 0.1295527346067149, 'fraction_numerical': 0.00889785265156009, 'mean_word_length': 2.6892778993435447, 'pattern_counts': {'":': 23, '<': 19, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7215755', 'n_tokens_mistral': 3710, 'n_tokens_neox': 2753, 'n_words': 627}