//Factory public static AccessKeys GetAccessKeys(string bucket) { var connectionsPath = FileLocationUtilities.GetFileDistributedWithApplication("connections.dll"); var lines = RobustFile.ReadAllLines(connectionsPath); switch (bucket) { case BloomS3Client.SandboxBucketName: // S3 'uploaderDev' user, who has permission to use the BloomLibraryBooks-Sandbox bucket. //parse.com silbloomlibrarysandbox return(new AccessKeys(lines[2], lines[3], lines[6], lines[7])); case BloomS3Client.UnitTestBucketName: return(new AccessKeys(lines[2], lines[3], lines[8], lines[9])); case BloomS3Client.ProductionBucketName: //S3 'uploader' user, who has permission to use the BloomLibraryBooks bucket //parse.com silbloomlibrary return(new AccessKeys(lines[0], lines[1], lines[4], lines[5])); case BloomS3Client.ProblemBookUploadsBucketName: return(new AccessKeys(lines[2], lines[3], null, null)); case BloomS3Client.BloomDesktopFiles: // For now, this is public read, and no one needs to write. return(new AccessKeys(null, null, null, null)); default: throw new ApplicationException("Bucket name not recognized: " + bucket); } }
private string[] GetUploadLogIfPresent(string folder) { var results = new string[0]; var fullFilepath = Path.Combine(folder, UploadLogFilename); if (RobustFile.Exists(fullFilepath)) // this is just looking in the same directory that we're uploading from { results = RobustFile.ReadAllLines(fullFilepath); } _uploadLogPath = fullFilepath; return(results); }
public List <Tuple <string, string> > GetSplitStartEndTimings(string inputAudioFilename, string inputTextFragmentsFilename, string outputTimingsFilename, string ttsEngineLang = "en") { // Just setting some default value here (Esperanto - which is more phonetic so we think it works well for a large variety), // but really rely-ing on the TTS override to specify the real lang, so this value doesn't really matter. string aeneasLang = "eo"; // Note: The version of FFMPEG in output/Debug or output/Release is probably not compatible with the version required by Aeneas. // Therefore change the working path to the book's audio folder. This has the benefit of allowing us to sidestep python's // inability to cope with non-ascii characters in file pathnames passed in on the command line by using bare filenames in the // command. See https://issues.bloomlibrary.org/youtrack/issue/BL-6927. string workingDirectory = Path.GetDirectoryName(inputAudioFilename); // I think this sets the boundary to the midpoint between the end of the previous sentence and the start of the next one. // This is good because by default, it would align it such that the subsequent audio started as close as possible to the beginning of it. Since there is a subtle pause when switching between two audio files, this left very little margin for error. string boundaryAdjustmentParams = "|task_adjust_boundary_algorithm=percent|task_adjust_boundary_percent_value=50"; // This identifies a "head" region of between 0 seconds or up to the max-specified duration (e.g. 5 seconds or 12 seconds) of silence/non-intelligible. // This would prevent it from being included in the first sentence's audio. (FYI, the hidden format will suppress it from the output timings file). // Specify 0 to turn this off. string audioHeadParams = $"|os_task_file_head_tail_format=hidden|is_audio_file_detect_head_min=0.00|is_audio_file_detect_head_max={maxAudioHeadDurationSec}"; var audioFile = Path.GetFileName(inputAudioFilename); var fragmentsFile = Path.GetFileName(inputTextFragmentsFilename); var outputFile = Path.GetFileName(outputTimingsFilename); string commandString = $"python -m aeneas.tools.execute_task \"{audioFile}\" \"{fragmentsFile}\" \"task_language={aeneasLang}|is_text_type=plain|os_task_file_format={kTimingsOutputFormat}{audioHeadParams}{boundaryAdjustmentParams}\" \"{outputFile}\" --runtime-configuration=\"tts_voice_code={ttsEngineLang}\""; string command; string arguments; if (Platform.IsLinux) { command = _pythonFound; arguments = commandString.Substring(7); } else { command = "CMD.EXE"; arguments = $"/C {commandString}"; } var processStartInfo = new ProcessStartInfo() { FileName = command, Arguments = arguments, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, WorkingDirectory = workingDirectory, CreateNoWindow = true }; SetPythonEncodingIfNeeded(processStartInfo); var process = Process.Start(processStartInfo); // TODO: Should I set a timeout? In general Aeneas is reasonably fast but it doesn't really seem like I could guarantee that it would return within a certain time.. // Block the current thread of execution until aeneas has completed, so that we can read the correct results from the output file. Logger.WriteMinorEvent("AudioSegmentationApi.GetSplitStartEndTimings(): Command started, preparing to wait..."); process.WaitForExit(); // Note: we could also request Aeneas write the standard output/error, or a log (or verbose log... or very verbose log) if desired if (process.ExitCode != 0) { var stdout = process.StandardOutput.ReadToEnd(); var stderr = process.StandardError.ReadToEnd(); var sb = new StringBuilder(); sb.AppendLine($"ERROR: python aeneas process to segment the audio file finished with exit code = {process.ExitCode}."); sb.AppendLine($"working directory = {workingDirectory}"); sb.AppendLine($"failed command = {commandString}"); sb.AppendLine($"process.stdout = {stdout.Trim()}"); sb.AppendLine($"process.stderr = {stderr.Trim()}"); // Add information found during check for dependencies: it might be the wrong python or ffmpeg... sb.AppendLine("--------"); sb.AppendLine($"python found = {_pythonFound}"); sb.AppendLine($"espeak found = {_espeakFound}"); sb.AppendLine($"ffmpeg found = {_ffmpegFound}"); sb.AppendLine($"aeneas information = {_aeneasInfo}"); sb.AppendLine("======== end of aeneas error report ========"); var msg = sb.ToString(); Console.Write(msg); Logger.WriteEvent(msg); } // This might throw exceptions, but IMO best to let the error handler pass it, and have the Javascript code be as robust as possible, instead of passing on error messages to user var segmentationResults = RobustFile.ReadAllLines(outputTimingsFilename); List <Tuple <string, string> > timingStartEndRangeList; if (kTimingsOutputFormat.Equals("srt", StringComparison.OrdinalIgnoreCase)) { timingStartEndRangeList = ParseTimingFileSRT(segmentationResults); } else { timingStartEndRangeList = ParseTimingFileTSV(segmentationResults); } Logger.WriteMinorEvent($"AudioSegmentationApi.GetSplitStartEndTimings(): Returning with count={timingStartEndRangeList.Count}."); return(timingStartEndRangeList); }
public string[] ReadAllLines(string path) => RobustFile.ReadAllLines(path);
public TeamCollectionManager(string localCollectionPath, BloomWebSocketServer webSocketServer, BookRenamedEvent bookRenamedEvent, BookStatusChangeEvent bookStatusChangeEvent, BookSelection bookSelection, CollectionClosing collectionClosingEvent, BookCollectionHolder bookCollectionHolder) { _webSocketServer = webSocketServer; _bookStatusChangeEvent = bookStatusChangeEvent; _localCollectionFolder = Path.GetDirectoryName(localCollectionPath); _bookCollectionHolder = bookCollectionHolder; BookSelection = bookSelection; collectionClosingEvent?.Subscribe((x) => { // When closing the collection...especially if we're restarting due to // changed settings!...we need to save any settings changes to the repo. // In such cases we can't safely wait for the change watcher to write things, // because (a) if we're shutting down for good, we just might not detect the // change before everything shuts down; and (b) if we're reopening the collection, // we might overwrite the change with current collection settings before we // save the new ones. if (CurrentCollection != null) { CurrentCollection.SyncLocalAndRepoCollectionFiles(false); } else if (Settings.HaveEnterpriseFeatures && CurrentCollectionEvenIfDisconnected != null && CurrentCollectionEvenIfDisconnected is DisconnectedTeamCollection disconnectedTC && disconnectedTC.DisconnectedBecauseNoEnterprise) { // We were disconnected because of Enterprise being off, but now the user has // turned Enterprise on again. We really need to save that, even though we usually don't // save settings changes when disconnected. Otherwise, restarting will restore the // no-enterprise state, and we will be stuck. // Note: We don't need to check for admin privileges here. If the user isn't an admin, // he could not have made any changes to settings, including turning on enterprise. var tempCollectionLinkPath = GetTcLinkPathFromLcPath(_localCollectionFolder); if (RobustFile.Exists(tempCollectionLinkPath)) { try { var repoFolderPath = RepoFolderPathFromLinkPath(tempCollectionLinkPath); var tempCollection = new FolderTeamCollection(this, _localCollectionFolder, repoFolderPath, bookCollectionHolder: _bookCollectionHolder); var problemWithConnection = tempCollection.CheckConnection(); if (problemWithConnection == null) { tempCollection.SyncLocalAndRepoCollectionFiles(false); } else { NonFatalProblem.Report(ModalIf.All, PassiveIf.All, "Bloom could not save your settings to the Team Collection: " + problemWithConnection.TextForDisplay, null, null, true); } } catch (Exception ex) { NonFatalProblem.Report(ModalIf.All, PassiveIf.All, "Bloom could not save your settings to the Team Collection", null, ex, true); } } // What if there's NOT a TC link file? Then it would be pathological to have a CurrentCollectionEvenIfDisconnected. // It's no longer a TC, so we don't need to save the settings to the TC. For now I'm just not going to do anything. } }); bookRenamedEvent.Subscribe(pair => { CurrentCollectionEvenIfDisconnected?.HandleBookRename(Path.GetFileName(pair.Key), Path.GetFileName(pair.Value)); }); var impersonatePath = Path.Combine(_localCollectionFolder, "impersonate.txt"); if (RobustFile.Exists(impersonatePath)) { var lines = RobustFile.ReadAllLines(impersonatePath); _overrideCurrentUser = lines.FirstOrDefault(); if (lines.Length > 1) { _overrideMachineName = lines[1]; } if (lines.Length > 2) { _overrideCurrentUserFirstName = lines[2]; } if (lines.Length > 3) { _overrideCurrentUserSurname = lines[3]; } } var localCollectionLinkPath = GetTcLinkPathFromLcPath(_localCollectionFolder); if (RobustFile.Exists(localCollectionLinkPath)) { try { var repoFolderPath = RepoFolderPathFromLinkPath(localCollectionLinkPath); CurrentCollection = new FolderTeamCollection(this, _localCollectionFolder, repoFolderPath, bookCollectionHolder: _bookCollectionHolder); // will be replaced if CheckConnection fails // BL-10704: We set this to the CurrentCollection BEFORE checking the connection, // so that there will be a valid MessageLog if we need it during CheckConnection(). // If CheckConnection() fails, it will reset this to a DisconnectedTeamCollection. CurrentCollectionEvenIfDisconnected = CurrentCollection; if (CheckConnection()) { CurrentCollection.SocketServer = SocketServer; CurrentCollection.TCManager = this; // Later, we will sync everything else, but we want the current collection settings before // we create the CollectionSettings object. if (ForceNextSyncToLocal) { ForceNextSyncToLocal = false; CurrentCollection.CopyRepoCollectionFilesToLocal(_localCollectionFolder); } else { CurrentCollection.SyncLocalAndRepoCollectionFiles(); } } // else CheckConnection has set up a DisconnectedRepo if that is relevant. } catch (Exception ex) { NonFatalProblem.Report(ModalIf.All, PassiveIf.All, "Bloom found Team Collection settings but could not process them", null, ex, true); CurrentCollection = null; CurrentCollectionEvenIfDisconnected = null; } } }