// Request from sign language tool, issued when a complete recording has been captured.
        // It is passed as a binary blob that is the actual content that needs to be made into
        // an mp4 file. (At this point we don't try to handle recordings too big for this approach.)
        // We make a file (with an arbitrary guid name) and attempt to make it the recording for the
        // first page element with class bloom-videoContainer.
        private void HandleRecordedVideoRequest(ApiRequest request)
        {
            lock (request)
            {
                var videoFolder = BookStorage.GetVideoDirectoryAndEnsureExistence(CurrentBook.FolderPath);
                var fileName    = GetNewVideoFileName();
                var path        = Path.Combine(videoFolder, fileName);
                using (var rawVideo = TempFile.CreateAndGetPathButDontMakeTheFile())
                {
                    using (var rawVideoOutput = new FileStream(rawVideo.Path, FileMode.Create))
                    {
                        // Do NOT just get RawPostData and try to write it to a file; this
                        // typically runs out of memory for anything more than about 2min of video.
                        // (It write to a MemoryStream, and this seems to manage memory very badly.
                        // 66MB should not be a huge problem, but somehow it is.)
                        using (var rawVideoInput = request.RawPostStream)
                        {
                            rawVideoInput.CopyTo(rawVideoOutput);
                        }
                    }
                    // The output stream should be closed before trying to access the newly written file.
                    SaveVideoFile(path, rawVideo.Path);
                }

                var relativePath = BookStorage.GetVideoFolderName + Path.GetFileName(path);
                request.ReplyWithText(UrlPathString.CreateFromUnencodedString(relativePath).UrlEncodedForHttpPath);
            }
        }
Beispiel #2
0
        // Request from sign language tool to import a video.
        private void HandleImportVideoRequest(ApiRequest request)
        {
            string path = null;

            View.Invoke((Action)(() => {
                var videoFiles = LocalizationManager.GetString("EditTab.Toolbox.SignLanguage.FileDialogVideoFiles", "Video files");
                var dlg = new DialogAdapters.OpenFileDialogAdapter
                {
                    Multiselect = false,
                    CheckFileExists = true,
                    Filter = $"{videoFiles} (*.mp4)|*.mp4"
                };
                var result = dlg.ShowDialog();
                if (result == DialogResult.OK)
                {
                    path = dlg.FileName;
                }
            }));
            if (!string.IsNullOrEmpty(path))
            {
                _importedVideoIntoBloom = true;
                var newVideoPath = Path.Combine(BookStorage.GetVideoDirectoryAndEnsureExistence(CurrentBook.FolderPath), GetNewVideoFileName());                 // Use a new name to defeat caching.
                RobustFile.Copy(path, newVideoPath);
                var relativePath = BookStorage.GetVideoFolderName + Path.GetFileName(newVideoPath);
                request.ReplyWithText(UrlPathString.CreateFromUnencodedString(relativePath).UrlEncodedForHttpPath);
            }
            else
            {
                // If the user canceled, we didn't exactly succeed, but having the user cancel is such a normal
                // event that posting a failure, which is a nuisance to ignore, is not warranted.
                request.ReplyWithText("");
            }
        }
        /// <summary>
        /// In case the user has been editing video files outside of Bloom, for example,
        /// after using the Show In Folder command in the sign language toolbox,
        /// we want to update the pages that show those videos to use the new versions.
        /// Unfortunately something, probably the browser itself, seems to be very
        /// persistent about caching videos, even though our server tells it not to cache
        /// things in the book folder (and debug builds don't ever tell it to cache anything).
        /// We prevent cache hits on modified files by adding a fake param to the URI.
        /// (Also we will remove any trimming...see comment below.)
        /// This function is called whenever Bloom is activated in Edit mode.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="eventArgs"></param>
        public void CheckForChangedVideoOnActivate(object sender, EventArgs eventArgs)
        {
            // We're only going to check for video changes on the current book, if any.
            // It's not inconceivable that whatever caches videos will keep a cached one
            // for another book, but I think trying to modify books we don't even have open
            // is to dangerous, as well as quite difficult.
            if (CurrentBook == null)
            {
                return;
            }

            // On Linux, this method interferes with successfully referencing a newly imported video file.
            // See https://issues.bloomlibrary.org/youtrack/issue/BL-6723.  Ignoring just the one call to
            // this method suffices for things  to work.  (On Windows, the sequence of events differs, but
            // this change is safe.)
            if (_importedVideoIntoBloom)
            {
                _importedVideoIntoBloom = false;
                return;
            }
            var videoFolderPath = BookStorage.GetVideoDirectoryAndEnsureExistence(CurrentBook.FolderPath);
            var filesModifiedSinceDeactivate = new DirectoryInfo(videoFolderPath)
                                               .GetFiles("*.mp4")
                                               .Where(f => GetRealLastModifiedTime(f) > DeactivateTime)
                                               .Select(f => f.FullName)
                                               .ToList();

            if (!filesModifiedSinceDeactivate.Any())
            {
                return;
            }

            // We might modify the current page, but the user may also have modified it
            // without doing anything to cause a Save before the deactivate. So save their
            // changes before we go to work on it.
            Model.SaveNow();

            foreach (var videoPath in filesModifiedSinceDeactivate)
            {
                var expectedSrcAttr = UrlPathString.CreateFromUnencodedString(BookStorage.GetVideoFolderName + Path.GetFileName(videoPath));
                var videoElts       = CurrentBook.RawDom.SafeSelectNodes($"//video/source[contains(@src,'{expectedSrcAttr.UrlEncodedForHttpPath}')]");
                if (videoElts.Count == 0)
                {
                    continue;                     // not used in book, ignore
                }
                // OK, the user has modified the file outside of Bloom. Something is determined to cache video.
                // Defeat it by setting a fake param.
                // Note that doing this will discard any fragment in the existing URL, typically trimming.
                // I think this is good...if the user has edited the video, we should start over assuming he
                // wants all of it.

                var newSrcAttr = UrlPathString.CreateFromUnencodedString(BookStorage.GetVideoFolderName + Path.GetFileName(videoPath));
                HtmlDom.SetSrcOfVideoElement(newSrcAttr, new ElementProxy((XmlElement)videoElts[0]), true, "?now=" + DateTime.Now.Ticks);
            }

            // We could try to figure out whether one of the modified videos is on the current page.
            // But that's the most likely video to be modified, and it doesn't take long to reload,
            // and this only happens in the very special case that the user has modified a video outside
            // of Bloom.
            View.UpdateSingleDisplayedPage(_pageSelection.CurrentSelection);

            // Likewise, this is probably overkill, but it's a probably-rare case.
            View.UpdateAllThumbnails();
        }