/// <summary> /// [2020.7.7] For the purpose of resuming failed transcriptions, all the captions of the failed transcription would now be stored in the database. /// Before resuming, all the old captions would be queried out from the database and stored in a dictionary, which will be passed to the /// transcription function along with the resume time point. /// </summary> /// <param name="videoId"></param> /// <param name="taskParameters"></param> /// <returns></returns> protected async override Task OnConsume(string videoId, TaskParameters taskParameters, ClientActiveTasks cleanup) { registerTask(cleanup, videoId); // may throw AlreadyInProgress exception if (Globals.appSettings.MOCK_RECOGNITION == "MOCK") { buildMockCaptions(videoId); } using (var _context = CTDbContext.CreateDbContext()) { // TODO: taskParameters.Force should wipe all captions and reset the Transcription Status Video video = await _context.Videos.Include(v => v.Video1).Where(v => v.Id == videoId).FirstAsync(); // ! Note the 'Include' ; we don't build the whole tree of related Entities if (video.TranscriptionStatus == Video.TranscriptionStatusMessages.NOERROR) { GetLogger().LogInformation($"{videoId}:Skipping Transcribing of- already complete"); return; } // GetKey can throw if the video.Id is currently being transcribed // However registerTask should have already detected that Key key = TaskEngineGlobals.KeyProvider.GetKey(video.Id); video.TranscribingAttempts += 10; await _context.SaveChangesAsync(); try { // create Dictionary and pass it to the recognition function var captionsMap = new Dictionary <string, List <Caption> >(); // Set Source Language and Target (translation) Languages var sourceLanguage = String.IsNullOrWhiteSpace(Globals.appSettings.SPEECH_RECOGNITION_DIALECT) ? Languages.ENGLISH_AMERICAN : Globals.appSettings.SPEECH_RECOGNITION_DIALECT.Trim(); var translations = new List <string> { Languages.ENGLISH_AMERICAN, Languages.SIMPLIFIED_CHINESE, Languages.KOREAN, Languages.SPANISH, Languages.FRENCH }; if (!String.IsNullOrWhiteSpace(Globals.appSettings.LANGUAGE_TRANSLATIONS)) { translations = Globals.appSettings.LANGUAGE_TRANSLATIONS.Split(',').ToList(); } GetLogger().LogInformation($"{videoId}: ({sourceLanguage}). Translation(s) = ({String.Join(',', translations)})"); // Different languages may not be as complete // So find the minimum timespan of the max observed ending for each language TimeSpan shortestTime = TimeSpan.MaxValue; // Cant use TimeSpan.Zero to mean unset var startAfterMap = new Dictionary <string, TimeSpan>(); var allLanguages = new List <string>(translations); allLanguages.Add(sourceLanguage); foreach (string language in allLanguages) { var existing = await _captionQueries.GetCaptionsAsync(video.Id, language); captionsMap[language] = existing; startAfterMap[language] = TimeSpan.Zero; if (existing.Any()) { TimeSpan lastCaptionTime = existing.Select(c => c.End).Max(); startAfterMap[language] = lastCaptionTime; GetLogger().LogInformation($"{ videoId}:{language}. Last Caption at {lastCaptionTime}"); } } //var lastSuccessTime = shortestTime < TimeSpan.MaxValue ? shortestTime : TimeSpan.Zero; //if (video.JsonMetadata != null && video.JsonMetadata["LastSuccessfulTime"] != null) //{ // lastSuccessTime = TimeSpan.Parse(video.JsonMetadata["LastSuccessfulTime"].ToString()); //} var result = await _msTranscriptionService.RecognitionWithVideoStreamAsync(videoId, video.Video1, key, captionsMap, sourceLanguage, startAfterMap); if (video.JsonMetadata == null) { video.JsonMetadata = new JObject(); } TaskEngineGlobals.KeyProvider.ReleaseKey(key, video.Id); foreach (var captionsInLanguage in result.Captions) { var theLanguage = captionsInLanguage.Key; var theCaptions = captionsInLanguage.Value; if (theCaptions.Any()) { var t = _context.Transcriptions.SingleOrDefault(t => t.VideoId == video.Id && t.Language == theLanguage); GetLogger().LogInformation($"Find Existing Transcriptions null={t == null}"); // Did we get the default or an existing Transcription entity? if (t == null) { t = new Transcription() { Captions = theCaptions, Language = theLanguage, VideoId = video.Id }; _context.Add(t); } else { // Transcriptions already existed, we are just completing them, so add only the new ones // TODO/TOREVIEW: Does this filter actually help, if existing caption entries were edited by hand? var newCaptions = theCaptions.Where(c => c.Id == null); t.Captions.AddRange(newCaptions); } } } video.TranscriptionStatus = result.ErrorCode; video.JsonMetadata["LastSuccessfulTime"] = result.LastSuccessTime.ToString(); await _context.SaveChangesAsync(); _sceneDetectionTask.Publish(video.Id); video.Transcriptions.ForEach(t => _generateVTTFileTask.Publish(t.Id)); } catch (Exception ex) { GetLogger().LogError(ex, $"{videoId}: Transcription Exception:${ex.StackTrace}"); video.TranscribingAttempts += 1000; await _context.SaveChangesAsync(); throw; } } }
protected async override Task OnConsume(JObject jObject, TaskParameters taskParameters, ClientActiveTasks cleanup) { using (var _context = CTDbContext.CreateDbContext()) { var type = jObject["Type"].ToString(); if (type == TaskType.PeriodicCheck.ToString()) { await _slackLogger.PostMessageAsync("Periodic Check."); registerTask(cleanup, "PeriodicCheck"); _updateBoxTokenTask.Publish(""); _buildElasticIndexTask.Publish(""); _cleanUpElasticIndexTask.Publish(""); _exampleTask.Publish(""); await DownloadAllPlaylists(); await PendingJobs(); } else if (type == TaskType.DownloadAllPlaylists.ToString()) { await DownloadAllPlaylists(); } else if (type == TaskType.DownloadPlaylistInfo.ToString()) { var playlistId = jObject["PlaylistId"].ToString(); var playlist = await _context.Playlists.FindAsync(playlistId); _downloadPlaylistInfoTask.Publish(playlist.Id); } else if (type == TaskType.GenerateVTTFile.ToString()) { var transcriptionId = jObject["TranscriptionId"].ToString(); var transcription = await _context.Transcriptions.FindAsync(transcriptionId); _generateVTTFileTask.Publish(transcription.Id); } else if (type == TaskType.SceneDetection.ToString()) { var mediaId = jObject["mediaId"].ToString(); var media = _context.Medias.Find(mediaId); _scenedDetectionTask.Publish(media.Video.Id); } else if (type == TaskType.CreateBoxToken.ToString()) { var authCode = jObject["authCode"].ToString(); _createBoxTokenTask.Publish(authCode); } else if (type == TaskType.DownloadMedia.ToString()) { var mediaId = jObject["mediaId"].ToString(); var media = await _context.Medias.FindAsync(mediaId); _downloadMediaTask.Publish(media.Id); } //else if (type == TaskType.ConvertMedia.ToString()) //{ // var videoId = jObject["videoId"].ToString(); // var video = await _context.Videos.FindAsync(videoId); // _convertVideoToWavTask.Publish(video.Id); //} else if (type == TaskType.TranscribeVideo.ToString()) { var id = jObject["videoOrMediaId"].ToString(); GetLogger().LogInformation($"{type}:{id}"); var video = await _context.Videos.FindAsync(id); if (video == null) { var media = await _context.Medias.FindAsync(id); if (media != null) { GetLogger().LogInformation($"{id}: media Found. videoID=({media.VideoId})"); video = media.Video; } } if (video == null) { GetLogger().LogInformation($"No video found for video/mediaId ({id})"); return; } //TODO: These properties should not be literal strings bool deleteExisting = false; try { deleteExisting = jObject["DeleteExisting"].Value <bool>(); } catch (Exception) { } if (deleteExisting) { GetLogger().LogInformation($"{id}:Removing Transcriptions for video ({video.Id})"); var transcriptions = video.Transcriptions; _context.Transcriptions.RemoveRange(transcriptions); video.TranscriptionStatus = ""; // Could also remove LastSuccessTime and reset attempts await _context.SaveChangesAsync(); } _transcriptionTask.Publish(video.Id); } else if (type == TaskType.UpdateOffering.ToString()) { var offeringId = jObject["offeringId"].ToString(); (await _context.Playlists.Where(o => o.OfferingId == offeringId).ToListAsync()) .ForEach(p => _downloadPlaylistInfoTask.Publish(p.Id)); } else if (type == TaskType.ReTranscribePlaylist.ToString()) { var playlistId = jObject["PlaylistId"].ToString(); // Get all videos var videos = await _context.Playlists.Where(p => p.Id == playlistId) .SelectMany(p => p.Medias) .Where(e => e != null) .Select(m => m.Video) .ToListAsync(); // Delete all captions. This caused a null pointer exception because some elements were null // the above line and this line now have null filters var captions = videos.SelectMany(v => v.Transcriptions) .Where(e => e != null) .SelectMany(t => t.Captions).ToList(); _context.Captions.RemoveRange(captions); // TODO/TOREVIEW: No need to create in captions. Their IDs should be sufficient // Delete all Transcriptions var transcriptions = videos.SelectMany(v => v.Transcriptions).Where(e => e != null).ToList(); _context.Transcriptions.RemoveRange(transcriptions); videos.ForEach(v => { v.TranscribingAttempts = 0; v.TranscriptionStatus = null; }); await _context.SaveChangesAsync(); videos.ForEach(v => { _transcriptionTask.Publish(v.Id); }); } } }