Пример #1
0
        /// <summary>
        /// API Handler when the Auto Segment button is clicked
        ///
        /// Replies with the text read (with orthography conversion applied) if eSpeak completed successfully, or "' if there was an error.
        /// </summary>
        public void ESpeakPreview(ApiRequest request)
        {
            Logger.WriteEvent("AudioSegmentationApi.ESpeakPreview(): ESpeakPreview started.");

            // Parse the JSON containing the text segmentation data.
            string json = request.RequiredPostJson();
            ESpeakPreviewRequest requestParameters = JsonConvert.DeserializeObject <ESpeakPreviewRequest>(json);

            string requestedLangCode = requestParameters.lang;
            string langCode          = GetBestSupportedLanguage(requestedLangCode);

            Logger.WriteEvent($"AudioSegmentationApi.ESpeakPreview(): langCode={langCode ?? "null"}");

            string text = requestParameters.text;

            text = SanitizeTextForESpeakPreview(text);

            string collectionPath = _bookSelection.CurrentSelection.CollectionSettings.FolderPath;
            string orthographyConversionMappingPath = Path.Combine(collectionPath, $"convert_{requestedLangCode}_to_{langCode}.txt");

            if (File.Exists(orthographyConversionMappingPath))
            {
                text = ApplyOrthographyConversion(text, orthographyConversionMappingPath);
            }

            // Even though you theoretically can pass the text through on the command line, it's better to write it to file.
            // The way the command line handles non-ASCII characters is not guaranteed to be the same as when reading from file.
            // It's more straightforward to just read/write it from file.
            // This causes the audio to match the audio that Aeneas will hear when it calls its eSpeak dependency.
            //   (Well, actually it was difficult to verify the exact audio that Aeneas hears, but for -v el "άλφα", verified reading from file caused audio duration to match, but passing on command line caused discrepancy in audio duration)
            string textToSpeakFullPath = Path.GetTempFileName();

            File.WriteAllText(textToSpeakFullPath, text, Encoding.UTF8);

            // No need to wait for espeak before responding.
            var response = new ESpeakPreviewResponse()
            {
                text     = text,
                lang     = langCode,
                filePath = File.Exists(orthographyConversionMappingPath) ? Path.GetFileName(orthographyConversionMappingPath) : ""
            };
            string responseJson = JsonConvert.SerializeObject(response);

            request.ReplyWithJson(responseJson);

            string stdout;
            string command = $"espeak -v {langCode} -f \"{textToSpeakFullPath}\"";
            bool   success = !DoesCommandCauseError(command, out stdout);

            RobustFile.Delete(textToSpeakFullPath);
            Logger.WriteEvent("AudioSegmentationApi.ESpeakPreview() Completed with success = " + success);
            if (!success)
            {
                var message = L10NSharp.LocalizationManager.GetString(
                    "EditTab.Toolbox.TalkingBookTool.ESpeakPreview.Error",
                    "eSpeak failed.",
                    "This text is shown if an error occurred while running eSpeak. eSpeak is a piece of software that this program uses to do text-to-speech (have the computer read text out loud).");
                NonFatalProblem.Report(ModalIf.None, PassiveIf.All, message, null, null, false);                        // toast without allowing error report.
            }
        }
Пример #2
0
        /// <summary>
        /// How this page looks has changed, so remove from our cache
        /// </summary>
        /// <param name="id"></param>
        public void PageChanged(string id)
        {
            Image image;

            if (_images.TryGetValue(id, out image))
            {
                _images.Remove(id);
                if (image.Tag != null)
                {
                    string thumbnailPath = image.Tag as string;
                    if (!string.IsNullOrEmpty(thumbnailPath))
                    {
                        if (RobustFile.Exists(thumbnailPath))
                        {
                            try
                            {
                                RobustFile.Delete(thumbnailPath);
                            }
                            catch (Exception)
                            {
                                Debug.Fail("Could not delete path (would not see this in release version)");
                                //oh well, couldn't delete it);
                                throw;
                            }
                        }
                    }
                }
                image.Dispose();
            }
        }
Пример #3
0
        private string GetPdfPath(string fname)
        {
            string path = null;

            // Sanitize fileName first
            string fileName = BookStorage.SanitizeNameForFileSystem(fname);

            for (int i = 0; i < 100; i++)
            {
                path = Path.Combine(Path.GetTempPath(), string.Format("{0}-{1}.pdf", fileName, i));
                if (!RobustFile.Exists(path))
                {
                    break;
                }

                try
                {
                    RobustFile.Delete(path);
                    break;
                }
                catch (Exception)
                {
                    //couldn't delete it? then increment the suffix and try again
                }
            }
            return(path);
        }
Пример #4
0
        public void DeletedBook_RaisesDeleteRepoBookFileEvent()
        {
            var bloomBookPath = Path.Combine(_repoFolder.FolderPath, "Books", "put book to delete.bloom");

            // Don't use PutBook here...changing the file immediately after putting it won't work,
            // because of the code that tries to prevent notifications of our own checkins.
            RobustFile.WriteAllText(bloomBookPath, @"This is original");             // no, not a zip at all

            var deletedBookName = "";

            _collection.StartMonitoring();
            ManualResetEvent bookDeletedRaised = new ManualResetEvent(false);
            EventHandler <DeleteRepoBookFileEventArgs> monitorFunction = (sender, args) =>
            {
                deletedBookName = args.BookFileName;
                bookDeletedRaised.Set();
            };

            _collection.DeleteRepoBookFile += monitorFunction;

            // sut (at least, triggers it and waits for it)
            RobustFile.Delete(bloomBookPath);

            var waitSucceeded = bookDeletedRaised.WaitOne(1000);

            // To avoid messing up other tests, clean up before asserting.
            _collection.DeleteRepoBookFile -= monitorFunction;
            _collection.StopMonitoring();

            Assert.That(waitSucceeded, "book deleted was not raised");
            Assert.That(deletedBookName, Is.EqualTo("put book to delete.bloom"));
        }
Пример #5
0
        public void CopyEmptyFile()
        {
            // Create an empty file.
            var emptyFile = Path.GetTempFileName();

            Assert.IsTrue(RobustFile.Exists(emptyFile));
            var info = new FileInfo(emptyFile);

            Assert.AreEqual(0, info.Length);

            var destName = emptyFile + "-xyzzy";

            // This was throwing a System.ArgumentOutOfRangeException exception before being fixed.
            RobustFile.Copy(emptyFile, destName, true);
            Assert.IsTrue(RobustFile.Exists(destName));
            var infoDest = new FileInfo(destName);

            Assert.AreEqual(0, infoDest.Length);

            // Clean up after ourselves.
            RobustFile.Delete(emptyFile);
            Assert.IsFalse(RobustFile.Exists(emptyFile));
            RobustFile.Delete(destName);
            Assert.IsFalse(RobustFile.Exists(destName));
        }
Пример #6
0
 public static void DeleteFolderThatMayBeInUse(string folder)
 {
     if (Directory.Exists(folder))
     {
         try
         {
             RobustIO.DeleteDirectory(folder, true);
         }
         catch (Exception e)
         {
             try
             {
                 Debug.WriteLine(e.Message);
                 //maybe we can at least clear it out a bit
                 string[] files = Directory.GetFiles(folder, "*.*", SearchOption.AllDirectories);
                 foreach (string s in files)
                 {
                     try
                     {
                         RobustFile.Delete(s);
                     }
                     catch (Exception)
                     {
                     }
                 }
                 //sleep and try again (in case some other thread will  let go of them)
                 Thread.Sleep(1000);
                 RobustIO.DeleteDirectory(folder, true);
             }
             catch (Exception)
             {
             }
         }
     }
 }
Пример #7
0
        /// <summary>
        /// Merge the specified input files into the specified output file. Returns null if successful,
        /// a string that may be useful in debugging if not.
        /// </summary>
        public static string MergeAudioFiles(IEnumerable <string> mergeFiles, string combinedAudioPath)
        {
            var ffmpeg = "/usr/bin/ffmpeg";             // standard Linux location

            if (SIL.PlatformUtilities.Platform.IsWindows)
            {
                ffmpeg = Path.Combine(BloomFileLocator.GetCodeBaseFolder(), "ffmpeg.exe");
            }
            if (RobustFile.Exists(ffmpeg))
            {
                var argsBuilder = new StringBuilder("-i \"concat:");
                foreach (var path in mergeFiles)
                {
                    argsBuilder.Append(path + "|");
                }
                argsBuilder.Length--;

                argsBuilder.Append($"\" -c copy \"{combinedAudioPath}\"");
                var result = CommandLineRunner.Run(ffmpeg, argsBuilder.ToString(), "", 60 * 10, new NullProgress());
                var output = result.ExitCode != 0 ? result.StandardError : null;
                if (output != null)
                {
                    RobustFile.Delete(combinedAudioPath);
                }

                return(output);
            }

            return("Could not find ffmpeg");
        }
Пример #8
0
        private void Recorder_Stopped(IAudioRecorder arg1, ErrorEventArgs arg2)
        {
            Recorder.Stopped -= Recorder_Stopped;
            Directory.CreateDirectory(System.IO.Path.GetDirectoryName(PathToCurrentAudioSegment));             // make sure audio directory exists
            int millisecondsToTrimFromEndForMouseClick = 100;

            try
            {
                var minimum = TimeSpan.FromMilliseconds(300);           // this is arbitrary
                AudioRecorder.TrimWavFile(PathToTemporaryWav, PathToCurrentAudioSegment, new TimeSpan(), TimeSpan.FromMilliseconds(millisecondsToTrimFromEndForMouseClick), minimum);
                RobustFile.Delete(PathToTemporaryWav);                  // Otherwise, these continue to clutter up the temp directory.
            }
            catch (Exception error)
            {
                Logger.WriteEvent(error.Message);
                RobustFile.Copy(PathToTemporaryWav, PathToCurrentAudioSegment, true);
            }

            //We don't actually need the mp3 now, so let people play with recording even without LAME (previously it could crash BL-3159).
            //We could put this off entirely until we make the ePUB.
            //I'm just gating this for now because maybe the thought was that it's better to do it a little at a time?
            //That's fine so long as it doesn't make the UI unresponsive on slow machines.
            if (LameEncoder.IsAvailable())
            {
                _mp3Encoder.Encode(PathToCurrentAudioSegment, PathToCurrentAudioSegment.Substring(0, PathToCurrentAudioSegment.Length - 4), new NullProgress());
                // Note: we need to keep the .wav file as well as the mp3 one. The mp3 format (or alternative mp4)
                // is required for ePUB. The wav file is a better permanent record of the recording; also,
                // it is used for playback.
            }
        }
Пример #9
0
        /// <summary>
        /// Encode the given file as an .mp3 file in the same directory.
        /// </summary>
        /// <returns>Path to the new file</returns>
        public string Encode(string sourcePath)
        {
            string destinationPath = Path.ChangeExtension(sourcePath, AudioRecording.kPublishableExtension);

            try
            {
                if (RobustFile.Exists(destinationPath))
                {
                    RobustFile.Delete(destinationPath);
                }
            }
            catch (Exception)
            {
                var shortMsg = LocalizationManager.GetString("LameEncoder.DeleteFailedShort", "Cannot replace mp3 file. Check antivirus");
                var longMsg  = LocalizationManager.GetString("LameEncoder.DeleteFailedLong", "Bloom could not replace an mp3 file. If this continues, check your antivirus.");
                NonFatalProblem.Report(ModalIf.None, PassiveIf.All, shortMsg, longMsg);
                return(null);
            }

            //-a downmix to mono
            string          arguments = $"-a \"{sourcePath}\" \"{destinationPath}\"";
            ExecutionResult result    = CommandLineRunner.Run(GetLamePath(), arguments, null, 60, new NullProgress());

            result.RaiseExceptionIfFailed("");
            return(destinationPath);
        }
Пример #10
0
        public void BulkPublishBloomPubSettings_GivenBulkPublishSettingsInXml_LoadsProperly()
        {
            var collectionName         = "loadBulkPublishSettingsTest";
            var collectionSettingsPath = Path.Combine(_folder.Path, $"{collectionName}.bloomCollection");

            if (RobustFile.Exists(collectionSettingsPath))
            {
                RobustFile.Delete(collectionSettingsPath);
            }

            string fileContents = @"<?xml version=""1.0"" encoding=""utf-8""?>
<Collection version=""0.2"">
  <BulkPublishBloomPubSettings>
    <MakeBookshelfFile>False</MakeBookshelfFile>
    <MakeBloomBundle>False</MakeBloomBundle>
    <BookshelfColor>#FF0000</BookshelfColor>
    <DistributionTag>distTag</DistributionTag>
    <BookshelfLabel>bookshelfLabel</BookshelfLabel>
  </BulkPublishBloomPubSettings>
</Collection>";

            RobustFile.WriteAllText(collectionSettingsPath, fileContents);

            // System under test
            var collectionSettings = new CollectionSettings(collectionSettingsPath);

            // Verification
            var bulkPublishSettings = collectionSettings.BulkPublishBloomPubSettings;

            Assert.That(bulkPublishSettings.makeBookshelfFile, Is.EqualTo(false), "makeBookshelfFile");
            Assert.That(bulkPublishSettings.makeBloomBundle, Is.EqualTo(false), "makeBloomBundle");
            Assert.That(bulkPublishSettings.bookshelfColor, Is.EqualTo("#FF0000"));
            Assert.That(bulkPublishSettings.distributionTag, Is.EqualTo("distTag"));
            Assert.That(bulkPublishSettings.bookshelfLabel, Is.EqualTo("bookshelfLabel"));
        }
Пример #11
0
        /// <summary>
        /// The thing here is that we need to guarantee unique names at the top level, so we wrap the books inside a folder
        /// with some unique name. As this involves copying the folder it is also a convenient place to omit any PDF files
        /// except the one we want.
        /// </summary>
        /// <param name="storageKeyOfBookFolder"></param>
        /// <param name="pathToBloomBookDirectory"></param>
        /// <param name="excludeAudio">If true, audio files are not uploaded.</param>
        public void UploadBook(string storageKeyOfBookFolder, string pathToBloomBookDirectory, IProgress progress, string pdfToInclude = null, bool excludeAudio = true)
        {
            BaseUrl = null;
            BookOrderUrlOfRecentUpload = null;
            DeleteBookData(_bucketName, storageKeyOfBookFolder);             // In case we're overwriting, get rid of any deleted files.

            //first, let's copy to temp so that we don't have to worry about changes to the original while we're uploading,
            //and at the same time introduce a wrapper with the last part of the unique key for this person+book
            string prefix         = "";                     // storageKey up to last slash (or empty)
            string tempFolderName = storageKeyOfBookFolder; // storage key after last slash (or all of it)

            // storageKeyOfBookFolder typically has a slash in it, email/id.
            // We only want the id as the temp folder name.
            // If there is anything before it, though, we want that as a prefix to make a parent 'folder' on parse.com.
            int index = storageKeyOfBookFolder.LastIndexOf('/');

            if (index >= 0)
            {
                prefix         = storageKeyOfBookFolder.Substring(0, index + 1);         // must include the slash
                tempFolderName = storageKeyOfBookFolder.Substring(index + 1);
            }

            var wrapperPath = Path.Combine(Path.GetTempPath(), tempFolderName);

            //If we previously uploaded the book, but then had a problem, this directory could still be on our harddrive. Clear it out.
            if (Directory.Exists(wrapperPath))
            {
                DeleteFileSystemInfo(new DirectoryInfo(wrapperPath));
            }

            Directory.CreateDirectory(wrapperPath);

            var destDirName = Path.Combine(wrapperPath, Path.GetFileName(pathToBloomBookDirectory));

            CopyDirectory(pathToBloomBookDirectory, destDirName);
            if (excludeAudio)
            {
                // Don't upload audio.
                string audioDir = Path.Combine(destDirName, "audio");
                if (Directory.Exists(audioDir))
                {
                    SIL.IO.RobustIO.DeleteDirectory(audioDir, true);
                }
            }
            var unwantedPdfs = Directory.EnumerateFiles(destDirName, "*.pdf").Where(x => Path.GetFileName(x) != pdfToInclude);

            foreach (var file in unwantedPdfs)
            {
                RobustFile.Delete(file);
            }
            // Don't upload corrupt htms that have been repaired
            foreach (var path in Directory.EnumerateFiles(destDirName, BookStorage.PrefixForCorruptHtmFiles + "*.htm"))
            {
                RobustFile.Delete(path);
            }
            UploadDirectory(prefix, wrapperPath, progress);

            DeleteFileSystemInfo(new DirectoryInfo(wrapperPath));
        }
Пример #12
0
        private static void SaveBadFile(string filepath)
        {
            var savedBadFile = filepath + "-BAD";

            if (RobustFile.Exists(savedBadFile))
            {
                RobustFile.Delete(savedBadFile);
            }
            RobustFile.Move(filepath, savedBadFile);
        }
Пример #13
0
        /// <summary>
        /// Download the files for this project from DBL into sourceDirectory and then
        /// copy them to the destination directory.
        /// Install should throw an Exception with an appropriate message if something goes wrong.
        /// </summary>
        /// <returns>
        ///   <c>true</c> if fonts were installed; otherwise <c>false</c>.
        /// </returns>
        /// <exception cref="ArgumentNullException">DBLEntryUid
        /// or
        /// DBLSourceUrl
        /// or
        /// Name</exception>
        public override bool Install()
        {
            // Easier to check parameters here than fill the temp directory with files
            // NOTE: This is not an exhaustive list of the required parameters!
            //       You will need to refer to InstallableResource.Install() for that.
            if (string.IsNullOrWhiteSpace(this.DBLEntryUid))
            {
                throw new ArgumentNullException(nameof(this.DBLEntryUid));
            }
            else if (string.IsNullOrWhiteSpace(this.DblSourceUrl))
            {
                throw new ArgumentNullException(nameof(this.DblSourceUrl));
            }
            else if (string.IsNullOrWhiteSpace(this.Name))
            {
                throw new ArgumentNullException(nameof(this.Name));
            }

            sourceDirectory = this.CreateTempSourceDirectory();
            string filePath = Path.Combine(sourceDirectory, this.Name + ProjectFileManager.resourceFileExtension);

            if (!this.GetFile(filePath))
            {
                if (RobustFile.Exists(filePath))
                {
                    // RobustFile only handles retries...
                    RobustFile.Delete(filePath);
                }

                return(false);
            }

            bool result;

            try
            {
                result = base.Install();
            }
            catch (UnauthorizedAccessException)
            {
                // Treat this like we couldn't get the resource from the DBL - ignore the error and continue.
                // This maybe caused by the file already being there and in use or the SF Project directory not existing
                result = false;
            }

            if (RobustFile.Exists(filePath))
            {
                RobustFile.Delete(filePath);
            }

            return(result);
        }
Пример #14
0
        public void OneTimeSetup()
        {
            _testFolder = new TemporaryFolder("SpreadsheetImporterWithBookTests");
            // We need 2 layers of temp folder because BringBookUpToDate will change the name of the book
            // folder to match an imported title.
            _bookFolder = new TemporaryFolder(_testFolder, "Book");
            var settings = new NewCollectionSettings();

            settings.Language1.Iso639Code = "en";
            settings.Language1.SetName("English", false);
            settings.SettingsFilePath = Path.Combine(_bookFolder.FolderPath, "dummy");

            var fileLocator = new BloomFileLocator(settings, new XMatterPackFinder(new string[] { }), ProjectContext.GetFactoryFileLocations(),
                                                   ProjectContext.GetFoundFileLocations(), ProjectContext.GetAfterXMatterFileLocations());
            var bookFilePath = Path.Combine(_bookFolder.FolderPath, "testBook.htm");

            if (File.Exists(bookFilePath))             // Shouldn't ever happen, but... just being careful.
            {
                RobustFile.Delete(bookFilePath);
            }
            _dom = SetupTestDom();
            // Write out our test book
            File.WriteAllText(bookFilePath, _dom.RawDom.OuterXml.ToString());
            var storage = new BookStorage(_bookFolder.FolderPath, fileLocator, new BookRenamedEvent(), settings);

            var book = new Bloom.Book.Book(new BookInfo(_bookFolder.FolderPath, true), storage, null,
                                           settings, new Bloom.Edit.PageSelection(),
                                           new PageListChangedEvent(), new BookRefreshEvent());

            // Create the regular production importer
            _importer = new SpreadsheetImporter(null, book, _bookFolder.FolderPath);

            // Set up the internal spreadsheet rows directly.
            var ss             = new InternalSpreadsheet();
            var columnForEn    = ss.AddColumnForLang("en", "English");
            var columnForImage = ss.GetColumnForTag(InternalSpreadsheet.ImageSourceColumnLabel);

            var newTitle = "My new book title";
            var titleRow = new ContentRow(ss);

            titleRow.AddCell(InternalSpreadsheet.BookTitleRowLabel);
            titleRow.SetCell(columnForEn, newTitle);

            var coverImageRow = new ContentRow(ss);

            coverImageRow.AddCell(InternalSpreadsheet.CoverImageRowLabel);
            coverImageRow.SetCell(columnForImage, Path.Combine("images", "Othello 199.jpg"));

            _importer.Import(ss);

            _resultElement = ReadResultingBookToXml(newTitle);
        }
Пример #15
0
        public static void GetUnusedFilenameTests(string basename, string expectedResult)
        {
            const string extension = ".txt";

            using (var folder = new TemporaryFolder("UnusedFilenameTest"))
            {
                var basePath = Path.Combine(folder.Path, basename + extension);
                RobustFile.Delete(basePath);                 // just in case
                RobustFile.WriteAllText(basePath, "test contents");
                var filename = ImageUtils.GetUnusedFilename(Path.GetDirectoryName(basePath), basename, extension);
                Assert.That(Path.GetFileNameWithoutExtension(filename), Is.EqualTo(expectedResult));
            }
        }
Пример #16
0
 internal static void RemoveSimulatedPageFile(string key)
 {
     if (key.StartsWith("file://"))
     {
         var uri = new Uri(key);
         RobustFile.Delete(uri.LocalPath);
         return;
     }
     lock (_urlToSimulatedPageContent)
     {
         _urlToSimulatedPageContent.Remove(key.FromLocalhost());
     }
 }
Пример #17
0
        private void MakeBloomPackInternal(string path, string dir, int dirNameOffset, string dirNamePrefix, bool forReaderTools)
        {
            try
            {
                if (RobustFile.Exists(path))
                {
                    // UI already got permission for this
                    RobustFile.Delete(path);
                }
                using (var pleaseWait = new SimpleMessageDialog("Creating BloomPack...", "Bloom"))
                {
                    try
                    {
                        pleaseWait.Show();
                        pleaseWait.BringToFront();
                        Application.DoEvents();                         // actually show it
                        Cursor.Current = Cursors.WaitCursor;

                        Logger.WriteEvent("BloomPack path will be " + path + ", made from " + dir + " with rootName " + Path.GetFileName(dir));
                        using (var fsOut = RobustFile.Create(path))
                        {
                            using (ZipOutputStream zipStream = new ZipOutputStream(fsOut))
                            {
                                zipStream.SetLevel(9);

                                CompressDirectory(dir, zipStream, dirNameOffset, dirNamePrefix, forReaderTools);

                                zipStream.IsStreamOwner = true;                                 // makes the Close() also close the underlying stream
                                zipStream.Close();
                            }
                        }

                        // show it
                        Logger.WriteEvent("Showing BloomPack on disk");
                        PathUtilities.SelectFileInExplorer(path);
                        Analytics.Track("Create BloomPack");
                    }
                    finally
                    {
                        Cursor.Current = Cursors.Default;
                        pleaseWait.Close();
                    }
                }
            }
            catch (Exception e)
            {
                ErrorReport.NotifyUserOfProblem(e, "Could not make the BloomPack at " + path);
            }
        }
Пример #18
0
        public void Dispose()
        {
            if (RobustFile.Exists(PdfFilePath))
            {
                try
                {
                    RobustFile.Delete(PdfFilePath);
                }
                catch (Exception)
                {
                }
            }

            GC.SuppressFinalize(this);
        }
Пример #19
0
 private static bool DeleteFileReportingAnyProblem(string path)
 {
     try
     {
         RobustFile.Delete(path);
         return(true);
     }
     catch (IOException e)
     {
         var msg =
             string.Format(
                 LocalizationManager.GetString("Errors.ProblemDeletingFile", "Bloom had a problem deleting this file: {0}"), path);
         ErrorReport.NotifyUserOfProblem(e, msg + Environment.NewLine + e.Message);
     }
     return(false);
 }
Пример #20
0
        /// <summary>
        /// Renames the {title}.htm HTM file to index.htm instead
        /// </summary>
        /// <param name="bookDirectory"></param>
        private static CreateArtifactsExitCode RenameBloomDigitalFiles(string bookDirectory)
        {
            string originalHtmFilePath = Bloom.Book.BookStorage.FindBookHtmlInFolder(bookDirectory);

            Debug.Assert(RobustFile.Exists(originalHtmFilePath), "Book HTM not found: " + originalHtmFilePath);
            if (!RobustFile.Exists(originalHtmFilePath))
            {
                return(CreateArtifactsExitCode.BookHtmlNotFound);
            }

            string newHtmFilePath = Path.Combine(bookDirectory, $"index.htm");

            RobustFile.Copy(originalHtmFilePath, newHtmFilePath);
            RobustFile.Delete(originalHtmFilePath);
            return(CreateArtifactsExitCode.Success);
        }
Пример #21
0
 /// <summary>
 /// If Bloom was launched by a fast splash screen program, signal it to close.
 /// </summary>
 private static void CloseFastSplashScreen()
 {
     if (SIL.PlatformUtilities.Platform.IsLinux)
     {
         RobustFile.Delete("/tmp/BloomLaunching.now");                 // (okay if file doesn't exist)
     }
     else if (SIL.PlatformUtilities.Platform.IsWindows)
     {
         // signal the native process (that launched us) to close the splash screen
         // (okay if there's nobody there to receive the signal)
         using (var closeSplashEvent = new EventWaitHandle(false,
                                                           EventResetMode.ManualReset, "CloseSquirrelSplashScreenEvent"))
         {
             closeSplashEvent.Set();
         }
     }
 }
Пример #22
0
        /// <summary>
        /// Merge the specified input files into the specified output file. Returns null if successful,
        /// a string that may be useful in debugging if not.
        /// </summary>
        public static string MergeAudioFiles(IEnumerable <string> mergeFiles, string combinedAudioPath)
        {
            var ffmpeg = "/usr/bin/ffmpeg";             // standard Linux location

            if (SIL.PlatformUtilities.Platform.IsWindows)
            {
                ffmpeg = Path.Combine(BloomFileLocator.GetCodeBaseFolder(), "ffmpeg.exe");
            }
            if (RobustFile.Exists(ffmpeg))
            {
                // A command something like
                //   ffmpeg -i "concat:stereo1.mp3|monaural2.mp3|stereo3.mp3" -c:a libmp3lame output.mp3
                // might work okay except that the output file characteristics appear to depend on the
                // first file in the list, and stereo recorded on only one side does come out only on
                // that one side if the file ends up being stereo, but not if the file ends up monaural.
                // This may or may not be preferable to flattening all of the files to monaural at the
                // standard recording rate before concatenating them.  The current approach at least
                // has deterministic output for all files.
                var monoFiles = TryEnsureConsistentInputFiles(ffmpeg, mergeFiles);
                try
                {
                    var argsBuilder = new StringBuilder("-i \"concat:");
                    argsBuilder.Append(string.Join("|", monoFiles));
                    argsBuilder.Append($"\" -c copy \"{combinedAudioPath}\"");
                    var result = CommandLineRunner.Run(ffmpeg, argsBuilder.ToString(), "", 60 * 10, new NullProgress());
                    var output = result.ExitCode != 0 ? result.StandardError : null;
                    if (output != null)
                    {
                        RobustFile.Delete(combinedAudioPath);
                    }
                    return(output);
                }
                finally
                {
                    foreach (var file in monoFiles)
                    {
                        if (!mergeFiles.Contains(file))
                        {
                            RobustFile.Delete(file);
                        }
                    }
                }
            }

            return("Could not find ffmpeg");
        }
Пример #23
0
        public void PutBook_DoesNotRaiseBookChangedEvent_OrDeletedEvent()
        {
            var folderPath = Path.Combine(_collectionFolder.FolderPath, "put existing book");
            var bookPath   = Path.Combine(folderPath, "put existing book.htm");

            Directory.CreateDirectory(folderPath);
            RobustFile.WriteAllText(bookPath, "<html><body>This is our newly put book</body></html>");
            _collection.PutBook(folderPath);             // create test situation without monitoring

            _collection.StartMonitoring();
            ManualResetEvent bookChangedRaised = new ManualResetEvent(false);

            _collection.OnChangedCalled = () => { bookChangedRaised.Set(); };
            bool bookChangedWasCalled = false;
            bool bookDeletedWasCalled = false;
            EventHandler <BookRepoChangeEventArgs> monitorFunction = (sender, args) =>
            {
                bookChangedWasCalled = true;
            };

            _collection.BookRepoChange += monitorFunction;
            EventHandler <DeleteRepoBookFileEventArgs> monitorFunction2 = (sender, args) =>
            {
                bookDeletedWasCalled = true;
            };

            _collection.DeleteRepoBookFile += monitorFunction2;

            //sut: put it again
            _collection.PutBook(folderPath);

            var waitSucceeded = bookChangedRaised.WaitOne(1000);

            // cleanup
            _collection.BookRepoChange     -= monitorFunction;
            _collection.DeleteRepoBookFile -= monitorFunction2;
            _collection.StopMonitoring();
            var bloomBookPath = Path.Combine(_repoFolder.FolderPath, "put existing book.bloom");

            RobustFile.Delete(bloomBookPath);

            Assert.That(waitSucceeded, "OnChanged was not called");
            Assert.That(bookChangedWasCalled, Is.False, "BookChanged wrongly called");
            Assert.That(bookDeletedWasCalled, Is.False, "BookDeleted wrongly called");
        }
Пример #24
0
 public void Copy()
 {
     using (var temp = new TempFile())
     {
         RobustFile.WriteAllText(temp.Path, "This is a test");
         using (var output = new TempFile())
         {
             RobustFile.Delete(output.Path);                     // apparently we can encounter a left-over temp file
             RobustFile.Copy(temp.Path, output.Path);
             Assert.That(File.ReadAllText(output.Path), Is.EqualTo("This is a test"));
             // output file now exists, w/o 3rd argument can't overwrite
             Assert.Throws <IOException>(() => RobustFile.Copy(temp.Path, output.Path));
             RobustFile.WriteAllText(temp.Path, "This is another test");
             RobustFile.Copy(temp.Path, output.Path, true);                     // overwrite, no exception
             Assert.That(File.ReadAllText(output.Path), Is.EqualTo("This is another test"));
         }
     }
 }
Пример #25
0
        public void Encode(string sourcePath, string destPathWithoutExtension, IProgress progress)
        {
            LocateAndRememberLAMEPath();

            if (RobustFile.Exists(destPathWithoutExtension + ".mp3"))
            {
                RobustFile.Delete(destPathWithoutExtension + ".mp3");
            }

            progress.WriteMessage(LocalizationManager.GetString("LameEncoder.Progress", " Converting to mp3", "Appears in progress indicator"));

            //-a downmix to mono
            string arguments = string.Format("-a \"{0}\" \"{1}.mp3\"", sourcePath, destPathWithoutExtension);
            //ClipRepository.RunCommandLine(progress, _pathToLAME, arguments);
            ExecutionResult result = CommandLineRunner.Run(_pathToLAME, arguments, null, 60, progress);

            result.RaiseExceptionIfFailed("");
        }
Пример #26
0
        private void CleanUpAfterPressTooShort()
        {
            // Seems sometimes on a very short click the recording actually got started while we were informing the user
            // that he didn't click long enough. Before we try to delete the file where the recording is taking place,
            // we have to stop it; otherwise, we will get an exception trying to delete it.
            while (Recording)
            {
                try
                {
                    Recorder.Stop();
                    Application.DoEvents();
                }
                catch (Exception)
                {
                }
            }
            // Don't kid the user we have a recording for this.
            // Also, the absence of the file is how the UI knows to switch back to the state where 'speak'
            // is the expected action.
            try
            {
                RobustFile.Delete(PathToCurrentAudioSegment);
            }
            catch (Exception error)
            {
                Logger.WriteError("Audio Recording trying to delete " + PathToCurrentAudioSegment, error);
                Debug.Fail("can't delete the recording even after we stopped:" + error.Message);
            }

            // If we had a prior recording, restore it...button press may have been a mistake.
            if (RobustFile.Exists(_backupPath))
            {
                try
                {
                    RobustFile.Copy(_backupPath, PathToCurrentAudioSegment, true);
                }
                catch (IOException e)
                {
                    Logger.WriteError("Audio Recording could not restore backup " + _backupPath, e);
                    // if we can't restore it we can't. Review: are there other exception types we should ignore? Should we bother the user?
                }
            }
        }
Пример #27
0
        public void MakePdf_BookNameIsChinese_OutputsPdf()
        {
            var maker = new PdfMaker();

            using (var input = TempFile.WithFilename("北京.html"))
                using (var output = TempFile.WithFilename("北京.pdf"))
                {
                    RobustFile.WriteAllText(input.Path, "<html><body>北京</body></html>");
                    RobustFile.Delete(output.Path);
                    RunMakePdf(maker, input.Path, output.Path, "A5", false, false, false,
                               PublishModel.BookletLayoutMethod.SideFold, PublishModel.BookletPortions.BookletPages);
                    //we don't actually have a way of knowing it did a booklet
                    Assert.IsTrue(File.Exists(output.Path), "Failed to convert trivial HTML file to PDF (Chinese filenames and content)");
                    var bytes = File.ReadAllBytes(output.Path);
                    Assert.Less(1000, bytes.Length, "Generated PDF file is way too small! (Chinese filenames and content)");
                    Assert.IsTrue(bytes [0] == (byte)'%' && bytes [1] == (byte)'P' && bytes [2] == (byte)'D' && bytes [3] == (byte)'F',
                                  "Generated PDF file started with the wrong 4-byte signature (Chinese filenames and content)");
                }
        }
Пример #28
0
        /// <summary>
        /// All we do at this point is make a file with a ".doc" extension and open it.
        /// </summary>
        /// <remarks>
        /// The .doc extension allows the operating system to recognize which program
        /// should open the file, and the program (whether Microsoft Word or LibreOffice
        /// or OpenOffice) seems to handle HTML content just fine.
        /// </remarks>
        public void ExportDocFormat(string path)
        {
            string sourcePath = _bookSelection.CurrentSelection.GetPathHtmlFile();

            if (RobustFile.Exists(path))
            {
                RobustFile.Delete(path);
            }
            // Linux (Trusty) LibreOffice requires slightly different metadata at the beginning
            // of the file in order to recognize it as HTML.  Otherwise it opens the file as raw
            // HTML (See https://silbloom.myjetbrains.com/youtrack/issue/BL-2276 if you don't
            // believe me.)  I don't know any perfect way to add this information to the file,
            // but a simple string replace should be safe.  This change works okay for both
            // Windows and Linux and for all three programs (Word, OpenOffice and Libre Office).
            string content      = RobustFile.ReadAllText(sourcePath);
            string fixedContent = content.Replace("<meta charset=\"UTF-8\">", "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">");

            RobustFile.WriteAllText(path, fixedContent);
        }
Пример #29
0
        private void HandleChooseWidget(ApiRequest request)
        {
            if (!View.Model.CanChangeImages())
            {
                // Enhance: more widget-specific message?
                MessageBox.Show(
                    LocalizationManager.GetString("EditTab.CantPasteImageLocked",
                                                  "Sorry, this book is locked down so that images cannot be changed."));
                request.ReplyWithText("");
                return;
            }

            using (var dlg = new DialogAdapters.OpenFileDialogAdapter
            {
                Multiselect = false,
                CheckFileExists = true,
                Filter = "Widget files|*.wdgt;*.html;*.htm"
            })
            {
                var result = dlg.ShowDialog();
                if (result != DialogResult.OK)
                {
                    request.ReplyWithText("");
                    return;
                }

                var fullWidgetPath = dlg.FileName;
                var ext            = Path.GetExtension(fullWidgetPath);
                if (ext.EndsWith("htm") || ext.EndsWith("html"))
                {
                    fullWidgetPath = EditingModel.CreateWidgetFromHtmlFolder(fullWidgetPath);
                }
                string activityName = View.Model.MakeWidget(fullWidgetPath);
                var    url          = UrlPathString.CreateFromUnencodedString(activityName);
                request.ReplyWithText(url.UrlEncodedForHttpPath);
                // clean up the temporary widget file we created.
                if (fullWidgetPath != dlg.FileName)
                {
                    RobustFile.Delete(fullWidgetPath);
                }
            }
        }
Пример #30
0
        /// <summary>
        /// Make a metadata, usually by just reading the meta.json file in the book folder.
        /// If some exception is thrown while trying to do that, or if it doesn't exist,
        /// Try reading a backup (and restore it if successful).
        /// If that also fails, return null.
        /// </summary>
        /// <param name="bookFolderPath"></param>
        /// <returns></returns>
        public static BookMetaData FromFolder(string bookFolderPath)
        {
            var          metaDataPath = MetaDataPath(bookFolderPath);
            BookMetaData result;

            if (TryReadMetaData(metaDataPath, out result))
            {
                return(result);
            }

            var backupPath = Path.ChangeExtension(metaDataPath, "bak");

            if (RobustFile.Exists(backupPath) && TryReadMetaData(backupPath, out result))
            {
                RobustFile.Delete(metaDataPath);                 // Don't think it's worth saving the corrupt one
                RobustFile.Move(backupPath, metaDataPath);
                return(result);
            }
            return(null);
        }