static void Main(string[] args) { // Here we setup the environment. // We step back from the project location until we reach the project's base folder and establish folder locations from there. // Getting the root path of the project. string rootPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); while (rootPath.Contains("TwitchBackend")) { rootPath = Directory.GetParent(rootPath).FullName; } string testPath = rootPath + "\\TestData\\"; rootPath += "\\"; ConfigurationManager.AppSettings["RootPath"] = rootPath; // Configuring Path locations. string scriptsPath = rootPath + "Scripts\\"; string broadcastsPath = rootPath + "Broadcasts\\"; string analyzedMatchesPath = rootPath + "AnalyzedBroadcasts\\"; string tensorflowDataPath = rootPath + "TensorflowData\\"; string highlightVideosPath = rootPath + "HighlightVideos\\"; string twitchVodsPath = rootPath + "TwitchVods\\"; ConfigurationManager.AppSettings["ScriptsPath"] = scriptsPath; ConfigurationManager.AppSettings["BroadcastsPath"] = broadcastsPath; ConfigurationManager.AppSettings["TwitchVodsPath"] = twitchVodsPath; ConfigurationManager.AppSettings["AnalyzedMatchesPath"] = analyzedMatchesPath; ConfigurationManager.AppSettings["TensorflowDataPath"] = tensorflowDataPath; ConfigurationManager.AppSettings["HighlightVideosPath"] = highlightVideosPath; ConfigurationManager.AppSettings["FilterTemplatePath"] = scriptsPath + "broadcastFilterTemplate.png"; ConfigurationManager.AppSettings["TestPath"] = testPath; // Initialise existing broadcast data from previous sessions. FilteredMatchesManager.LoadFromJson(); // Check for new videos to process. var files = Directory.GetFiles(twitchVodsPath); List <string> videosToProcess = new List <string>(); // Filter out non video files. foreach (var file in files) { if (file.EndsWith(".mp4")) { videosToProcess.Add(file); } } // Process videos, stripping out non-gameplay elements. var filteredMatches = new BroadcastFilter().FilterBroadcasts(videosToProcess); Console.WriteLine(""); Console.WriteLine("Populating chat-logs for filtered matches"); Console.WriteLine(""); // Load in filtered chat-log info into each filtered match. foreach (var filteredMatch in filteredMatches) { if (!filteredMatch.IsPopulated) { filteredMatch.PopulateMatchChatLogs(); } } // Save the newly discovered filtered match info to our manager. FilteredMatchesManager.AddFilteredMatches(filteredMatches); var matchAnalyzer = new MatchAnalyzer(); var matchCollection = new List <List <MatchMetricGroup> >(); Console.WriteLine(""); Console.WriteLine("Analyzing matches."); Console.WriteLine(""); // Go through each filtered match and analyze it for selected gameplay metrics (kills, ultimate usage, ect.). foreach (var filteredMatch in FilteredMatchesManager.FilteredMatches) { matchCollection.Add(matchAnalyzer.AnalyzeMatches(filteredMatch)); } // Save the newly discovered analysis to our manager. foreach (var collection in matchCollection) { AnalyzedMatchesManager.AddAnalyzedMatches(collection); } Console.WriteLine(""); Console.WriteLine("Match analysis complete."); Console.WriteLine(""); // Ensuring a Json file is generated and in sync with the local files generated via analysis. AnalyzedMatchesManager.LoadFromFiles(); AnalyzedMatchesManager.SaveToJson(); Console.WriteLine(""); Console.WriteLine("Loading analyzed match info into Deep Learning Predictor."); Console.WriteLine(""); var deepLearner = new NeuralNetController(); var highlightGenerator = new HighlightMaker(); // Finally we generate a highlight for each discovered match we find. foreach (var match in AnalyzedMatchesManager.AnalyzedMatches) { if (!match.Match.IsInstantReplay) { var highlightInfo = deepLearner.GetHighlightPeriod(match); var highlightVideoPath = highlightGenerator.CreateHighlight(highlightInfo, match.Match); Console.WriteLine($"Highlight video created at: {highlightVideoPath} for match: {match.Match.GetFileName(false)}"); } } // Here all UnFiltered Broadcasts are Filtered, then all Analyzed Matches are Analyzed, then all untrained instant-replays are trained and all unpredicted matches are predicted. // All Analyzed matches then have their corresponding highlight video rendered, available to the user to view. Console.WriteLine($"Processing complete: Go to {highlightVideosPath} to see highlight videos."); Console.WriteLine("Press any key to exit."); Console.ReadKey(); }
/// <summary> /// Loads in the filteredMatch list file. If a local analysis file exists it will use this first to avoid unnecessary processing. /// </summary> public static void LoadFromFiles() { var filteredMatches = FilteredMatchesManager.FilteredMatches; AnalyzedMatches = new List <MatchMetricGroup>(); var folders = Directory.GetDirectories(AnalyzedMatchesPath); // Go over each filtered match and look up their corresponding analysis files. foreach (var filteredMatch in filteredMatches) { foreach (var folder in folders) { if (folder.Contains(filteredMatch.Broadcast.Id.ToString())) { // Analysis files found. var files = Directory.GetFiles(folder); // Go over each match and create a corresponding analysis object using the local analysis files. // If no file found, perform analysis and create an object from scratch (and a corresponding analysis file group.). foreach (var match in filteredMatch.Matches) { // Check everything requires exists. MatchMetricGroup metrics; bool killsFound = false, turretsFound = false, ultimatesFound = false, baronFound = false, dragonFound = false, inhibitorFound = false; string killPath = null, ultimatePath = null, turretsPath = null, baronPath = null, dragonPath = null, inhibitorPath = null; foreach (var file in files) { if (file.Contains(match.GetFileName(false))) { if (file.Contains("kills")) { killPath = file; killsFound = true; } if (file.Contains("ultimates")) { ultimatePath = file; ultimatesFound = true; } if (file.Contains("turrets")) { turretsPath = file; turretsFound = true; } if (file.Contains("baron")) { baronPath = file; baronFound = true; } if (file.Contains("dragon")) { dragonPath = file; dragonFound = true; } if (file.Contains("inhibitor")) { inhibitorPath = file; inhibitorFound = true; } } } // If one or more metrics missing, (re)generate analysis info. if (!(ultimatesFound && killsFound && turretsFound && baronFound && dragonFound && inhibitorFound)) { metrics = new MatchAnalyzer().AnalyzeMatch(match, filteredMatch); } // Load analysis info from file. else { metrics = new MatchAnalyzer().ParseMatch(killPath, ultimatePath, turretsPath, baronPath, dragonPath, inhibitorPath, match); } AnalyzedMatches.Add(metrics); } } } } }
/// <summary> /// Uses the Tensorflow model to predict the most exciting moment. /// Returns metadata about the highlight. /// </summary> /// <param name="matchMetricGroup"></param> /// <returns></returns> public HighlightInfo GetHighlightPeriod(MatchMetricGroup matchMetricGroup, bool testConfiguration = false) { if (testConfiguration) { _deepLearnerScriptPath = ConfigurationManager.AppSettings["AltScriptsPath"] + "DeepLearningModel.py"; } // Packages matchMetricGroup info into a chunked up form that Tensorflow can understand and creates a csv file for the Tensorflow python script to reference. var(matchPath, predictedDataPath) = PrepareMatchForTensorFlow(matchMetricGroup, false); // Runs the python script that outputs a csv file for the predictions the Tensorflow model made about the excitement level at a particular time in the match. GetHighlightInfo(matchPath, predictedDataPath); // Load in the predictions made by the model. List <string> predictedDataRaw; try { predictedDataRaw = File.ReadAllLines(_tensorflowPath + "Predictions\\" + predictedDataPath).ToList(); } catch (Exception) { // Very rare edge case when multiple parallel uses of the model can cause a failure to predict. GetHighlightInfo(matchPath, predictedDataPath); predictedDataRaw = File.ReadAllLines(_tensorflowPath + "Predictions\\" + predictedDataPath).ToList(); } Console.WriteLine(predictedDataPath + " Complete."); List <double> predictedData = new List <double>(); List <double> matchOffset = new List <double>(); var matchAnalyzer = new MatchAnalyzer(); // Parse predicted data into relevant objects. reference is via an offset from the original raw Broadcast video start time. var counter = 0.0; foreach (var line in predictedDataRaw) { predictedData.Add(double.Parse(line)); matchOffset.Add(matchAnalyzer.ConvertVideoTimeToMatchOffset(counter * 15, matchMetricGroup.Match)); counter += 1; } // Find the most exciting period in the match and its score. var highestScore = 0.0; var index = 0; var highestScoreIndex = 0; foreach (var score in predictedData) { if (score > highestScore) { highestScore = score; highestScoreIndex = index; } index += 1; } // We offset the start by an additional 90 seconds to account for time slippage. return(new HighlightInfo(matchOffset[highestScoreIndex] + 90, 40, highestScore)); }