namespace VideoBrowser.Core
{
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using VideoBrowser.Common;
using VideoBrowser.Extensions;
using VideoBrowser.Helpers;
///
/// Defines the .
///
public static class YoutubeDl
{
#region Constants
private const string ErrorSignIn = "YouTube said: Please sign in to view this video.";
#endregion Constants
#region Fields
public static string YouTubeDlPath = Path.Combine(AppEnvironment.AppBinaryDirectory, "youtube-dl.exe");
#endregion Fields
#region Methods
///
/// Gets current youtube-dl version.
///
/// The .
public static string GetVersion()
{
string version = string.Empty;
ProcessHelper.StartProcess(YouTubeDlPath, Commands.Version,
delegate (Process process, string line)
{
// Only one line gets printed, so assume any non-empty line is the version
if (!string.IsNullOrEmpty(line))
version = line.Trim();
},
null, null).WaitForExit();
return version;
}
///
/// Returns a of the given video.
///
/// The url to the video.
/// The authentication.
/// The .
public static VideoInfo GetVideoInfo(string url,
YoutubeAuthentication authentication = null)
{
string json_dir = AppEnvironment.GetJsonDirectory();
string json_file = string.Empty;
string arguments = string.Format(Commands.GetJsonInfo,
json_dir,
url,
authentication == null ? string.Empty : authentication.ToCmdArgument());
VideoInfo video = new VideoInfo();
LogHeader(arguments);
ProcessHelper.StartProcess(YouTubeDlPath, arguments,
delegate (Process process, string line)
{
line = line.Trim();
if (line.StartsWith("[info] Writing video description metadata as JSON to:"))
{
// Store file path
json_file = line.Substring(line.IndexOf(":") + 1).Trim();
}
else if (line.Contains("Refetching age-gated info webpage"))
{
video.RequiresAuthentication = true;
}
},
delegate (Process process, string error)
{
error = error.Trim();
if (error.Contains(ErrorSignIn))
{
video.RequiresAuthentication = true;
}
else if (error.StartsWith("ERROR:"))
{
video.Failure = true;
video.FailureReason = error.Substring("ERROR: ".Length);
}
}, null)
.WaitForExit();
if (!video.Failure && !video.RequiresAuthentication)
{
video.DeserializeJson(json_file);
}
return video;
}
///
/// The GetVideoInfoBatchAsync.
///
/// The urls.
/// The videoReady.
/// The authentication.
/// The .
public static async Task GetVideoInfoBatchAsync(ICollection urls,
Action videoReady,
YoutubeAuthentication authentication = null)
{
string json_dir = AppEnvironment.GetJsonDirectory();
string arguments = string.Format(Commands.GetJsonInfoBatch, json_dir, string.Join(" ", urls));
var videos = new OrderedDictionary();
var jsonFiles = new Dictionary();
var findVideoID = new Regex(@"(?:\]|ERROR:)\s(.{11}):", RegexOptions.Compiled);
var findVideoIDJson = new Regex(@":\s.*\\(.{11})_", RegexOptions.Compiled);
LogHeader(arguments);
await Task.Run(() =>
{
ProcessHelper.StartProcess(YouTubeDlPath, arguments,
(Process process, string line) =>
{
line = line.Trim();
Match m;
string id;
VideoInfo video = null;
if ((m = findVideoID.Match(line)).Success)
{
id = findVideoID.Match(line).Groups[1].Value;
video = videos.Get(id, new VideoInfo() { Id = id });
}
if (line.StartsWith("[info] Writing video description metadata as JSON to:"))
{
id = findVideoIDJson.Match(line).Groups[1].Value;
var jsonFile = line.Substring(line.IndexOf(":") + 1).Trim();
jsonFiles.Put(id, jsonFile);
video = videos[id] as VideoInfo;
video.DeserializeJson(jsonFile);
videoReady(video);
}
else if (line.Contains("Refetching age-gated info webpage"))
{
video.RequiresAuthentication = true;
}
},
(Process process, string error) =>
{
error = error.Trim();
var id = findVideoID.Match(error).Groups[1].Value;
var video = videos.Get(id, new VideoInfo() { Id = id });
if (error.Contains(ErrorSignIn))
{
video.RequiresAuthentication = true;
}
else if (error.StartsWith("ERROR:"))
{
video.Failure = true;
video.FailureReason = error.Substring("ERROR: ".Length);
}
}, null)
.WaitForExit();
});
}
///
/// Writes log footer to log.
///
public static void LogFooter()
{
// Write log footer to stream.
// Possibly write elapsed time and/or error in future.
Logger.Info("-" + Environment.NewLine);
}
///
/// Writes log header to log.
///
/// The arguments to log in header.
/// The caller.
public static void LogHeader(string arguments, [CallerMemberName]string caller = "")
{
Logger.Info("[" + DateTime.Now + "]");
Logger.Info("version: " + GetVersion());
Logger.Info("caller: " + caller);
Logger.Info("cmd: " + arguments.Trim());
Logger.Info(string.Empty);
Logger.Info("OUTPUT");
}
///
/// The Update.
///
/// The .
public static async Task Update()
{
var returnMsg = string.Empty;
var versionRegex = new Regex(@"(\d{4}\.\d{2}\.\d{2})");
await Task.Run(delegate
{
ProcessHelper.StartProcess(YouTubeDlPath, Commands.Update,
delegate (Process process, string line)
{
Match m;
if ((m = versionRegex.Match(line)).Success)
returnMsg = m.Groups[1].Value;
},
delegate (Process process, string line)
{
returnMsg = "Failed";
}, null)
.WaitForExit();
});
return returnMsg;
}
#endregion Methods
}
}