예제 #1
0
 public void RegisterWithApiHandler(BloomApiHandler apiHandler)
 {
     // Must not require sync, because it launches a Bloom dialog, which will make other api requests
     // that will be blocked if this is locked.
     apiHandler.RegisterEndpointHandler("bookCommand/exportToSpreadsheet",
                                        (request) =>
     {
         _spreadsheetApi.ShowExportToSpreadsheetUI(GetBookObjectFromPost(request));
         request.PostSucceeded();
     }, true, false);
     apiHandler.RegisterEndpointHandler("bookCommand/enhanceLabel", (request) =>
     {
         // We want this to be fast...many things are competing for api handling threads while
         // Bloom is starting up, including many buttons sending this request...
         // so we'll postpone until idle even searching for the right BookInfo.
         var collection = request.RequiredParam("collection-id").Trim();
         var id         = request.RequiredParam("id");
         RequestButtonLabelUpdate(collection, id);
         request.PostSucceeded();
     }, false, false);
     apiHandler.RegisterBooleanEndpointHandler("bookCommand/decodable",
                                               request => { return(_collectionModel.IsBookDecodable); },
                                               (request, b) => { _collectionModel.SetIsBookDecodable(b); },
                                               true);
     apiHandler.RegisterBooleanEndpointHandler("bookCommand/leveled",
                                               request => { return(_collectionModel.IsBookLeveled); },
                                               (request, b) => { _collectionModel.SetIsBookLeveled(b); },
                                               true);
     apiHandler.RegisterEndpointLegacy("bookCommand/", HandleBookCommand, true);
 }
예제 #2
0
 public void RegisterWithApiHandler(BloomApiHandler apiHandler)
 {
     apiHandler.RegisterEndpointHandler("teamCollection/repoFolderPath", HandleRepoFolderPath, false);
     apiHandler.RegisterEndpointHandler("teamCollection/isTeamCollectionEnabled", HandleIsTeamCollectionEnabled, false);
     apiHandler.RegisterEndpointHandler("teamCollection/bookStatus", HandleBookStatus, false, false);
     apiHandler.RegisterEndpointHandler("teamCollection/selectedBookStatus", HandleSelectedBookStatus, false);
     apiHandler.RegisterEndpointHandler("teamCollection/attemptLockOfCurrentBook", HandleAttemptLockOfCurrentBook, true);
     apiHandler.RegisterEndpointHandler("teamCollection/checkInCurrentBook", HandleCheckInCurrentBook, true);
     apiHandler.RegisterEndpointHandler("teamCollection/forgetChangesInSelectedBook", HandleForgetChangesInSelectedBook, true);
     apiHandler.RegisterEndpointHandler("teamCollection/chooseFolderLocation", HandleChooseFolderLocation, true);
     apiHandler.RegisterEndpointHandler("teamCollection/createTeamCollection", HandleCreateTeamCollection, true);
     apiHandler.RegisterEndpointHandler("teamCollection/joinTeamCollection", HandleJoinTeamCollection, true);
     apiHandler.RegisterEndpointHandler("teamCollection/getLog", HandleGetLog, false);
     apiHandler.RegisterEndpointHandler("teamCollection/getCollectionName", HandleGetCollectionName, false);
     apiHandler.RegisterEndpointHandler("teamCollection/showCreateTeamCollectionDialog", HandleShowCreateTeamCollectionDialog, true);
     apiHandler.RegisterEndpointHandler("teamCollection/reportBadZip", HandleReportBadZip, true);
     apiHandler.RegisterEndpointHandler("teamCollection/showRegistrationDialog", HandleShowRegistrationDialog, true, false);
     apiHandler.RegisterEndpointHandler("teamCollection/getHistory", HandleGetHistory, true);
     apiHandler.RegisterEndpointHandler("teamCollection/checkinMessage", HandleCheckinMessage, false);
     apiHandler.RegisterEndpointHandler("teamCollection/forceUnlock", HandleForceUnlock, false);
     apiHandler.RegisterBooleanEndpointHandler("teamCollection/logImportant", HandleLogImportant, null, false);
 }
예제 #3
0
        public void RegisterWithApiHandler(BloomApiHandler apiHandler)
        {
            // This is just for storing the user preference of method
            // If we had a couple of these, we could just have a generic preferences api
            // that browser-side code could use.
            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "method", request =>
            {
                if (request.HttpMethod == HttpMethods.Get)
                {
                    var method = Settings.Default.PublishAndroidMethod;
                    if (!new string[] { "wifi", "usb", "file" }.Contains(method))
                    {
                        method = "wifi";
                    }
                    request.ReplyWithText(method);
                }
                else                 // post
                {
                    Settings.Default.PublishAndroidMethod = request.RequiredPostString();
#if __MonoCS__
                    if (Settings.Default.PublishAndroidMethod == "usb")
                    {
                        _progress.MessageWithoutLocalizing("Sorry, this method is not available on Linux yet.");
                    }
#endif
                    request.PostSucceeded();
                }
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "backColor", request =>
            {
                if (request.HttpMethod == HttpMethods.Get)
                {
                    if (request.CurrentBook != _coverColorSourceBook)
                    {
                        _coverColorSourceBook = request.CurrentBook;
                        ImageUtils.TryCssColorFromString(request.CurrentBook?.GetCoverColor() ?? "", out _thumbnailBackgroundColor);
                    }
                    request.ReplyWithText(ToCssColorString(_thumbnailBackgroundColor));
                }
                else                 // post
                {
                    // ignore invalid colors (very common while user is editing hex)
                    Color newColor;
                    var newColorAsString = request.RequiredPostString();
                    if (ImageUtils.TryCssColorFromString(newColorAsString, out newColor))
                    {
                        _thumbnailBackgroundColor = newColor;
                        request.CurrentBook.SetCoverColor(newColorAsString);
                    }
                    request.PostSucceeded();
                }
            }, true);

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "motionBookMode",
                                                      readRequest =>
            {
                // If the user has taken off all possible motion, force not having motion in the
                // Bloom Reader book.  See https://issues.bloomlibrary.org/youtrack/issue/BL-7680.
                if (!readRequest.CurrentBook.HasMotionPages)
                {
                    readRequest.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion = false;
                }
                return(readRequest.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion);
            },
                                                      (writeRequest, value) =>
            {
                writeRequest.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion = value;
                writeRequest.CurrentBook.BookInfo.SavePublishSettings();
                _webSocketServer.SendEvent("publish", "motionChanged");
            }
                                                      , true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "updatePreview", request =>
            {
                MakeBloompubPreview(request, false);
            }, false);


            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "thumbnail", request =>
            {
                var coverImage = request.CurrentBook.GetCoverImagePath();
                if (coverImage == null)
                {
                    request.Failed("no cover image");
                }
                else
                {
                    // We don't care as much about making it resized as making its background transparent.
                    using (var thumbnail = TempFile.CreateAndGetPathButDontMakeTheFile())
                    {
                        if (_thumbnailBackgroundColor == Color.Transparent)
                        {
                            ImageUtils.TryCssColorFromString(request.CurrentBook?.GetCoverColor(), out _thumbnailBackgroundColor);
                        }
                        RuntimeImageProcessor.GenerateEBookThumbnail(coverImage, thumbnail.Path, 256, 256, _thumbnailBackgroundColor);
                        request.ReplyWithImage(thumbnail.Path);
                    }
                }
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "usb/start", request =>
            {
#if !__MonoCS__
                SetState("UsbStarted");
                UpdatePreviewIfNeeded(request);
                _usbPublisher.Connect(request.CurrentBook, _thumbnailBackgroundColor, GetSettings());
#endif
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "usb/stop", request =>
            {
#if !__MonoCS__
                _usbPublisher.Stop(disposing: false);
                SetState("stopped");
#endif
                request.PostSucceeded();
            }, true);
            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "wifi/start", request =>
            {
                SetState("ServingOnWifi");
                UpdatePreviewIfNeeded(request);
                _wifiPublisher.Start(request.CurrentBook, request.CurrentCollectionSettings, _thumbnailBackgroundColor, GetSettings());

                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "wifi/stop", request =>
            {
                _wifiPublisher.Stop();
                SetState("stopped");
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "file/save", request =>
            {
                UpdatePreviewIfNeeded(request);
                FilePublisher.Save(request.CurrentBook, _bookServer, _thumbnailBackgroundColor, _progress, GetSettings());
                SetState("stopped");
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "file/bulkSaveBloomPubsParams", request =>
            {
                request.ReplyWithJson(JsonConvert.SerializeObject(_collectionSettings.BulkPublishBloomPubSettings));
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "file/bulkSaveBloomPubs", request =>
            {
                // update what's in the collection so that we remember for next time
                _collectionSettings.BulkPublishBloomPubSettings = request.RequiredPostObject <BulkBloomPubPublishSettings>();
                _collectionSettings.Save();

                _bulkBloomPubCreator.PublishAllBooks(_collectionSettings.BulkPublishBloomPubSettings);
                SetState("stopped");
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "textToClipboard", request =>
            {
                PortableClipboard.SetText(request.RequiredPostString());
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "canHaveMotionMode",
                                                      request =>
            {
                return(request.CurrentBook.HasMotionPages);
            },
                                                      null,  // no write action
                                                      false,
                                                      true); // we don't really know, just safe default

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "canRotate",
                                                      request =>
            {
                return(request.CurrentBook.BookInfo.PublishSettings.BloomPub.Motion && request.CurrentBook.HasMotionPages);
            },
                                                      null,  // no write action
                                                      false,
                                                      true); // we don't really know, just safe default

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "defaultLandscape",
                                                      request =>
            {
                return(request.CurrentBook.GetLayout().SizeAndOrientation.IsLandScape);
            },
                                                      null,  // no write action
                                                      false,
                                                      true); // we don't really know, just safe default
            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "languagesInBook", request =>
            {
                try
                {
                    InitializeLanguagesInBook(request);

                    Dictionary <string, InclusionSetting> textLangsToPublish  = request.CurrentBook.BookInfo.PublishSettings.BloomPub.TextLangs;
                    Dictionary <string, InclusionSetting> audioLangsToPublish = request.CurrentBook.BookInfo.PublishSettings.BloomPub.AudioLangs;

                    var result = "[" + string.Join(",", _allLanguages.Select(kvp =>
                    {
                        string langCode = kvp.Key;

                        bool includeText = false;
                        if (textLangsToPublish != null && textLangsToPublish.TryGetValue(langCode, out InclusionSetting includeTextSetting))
                        {
                            includeText = includeTextSetting.IsIncluded();
                        }

                        bool includeAudio = false;
                        if (audioLangsToPublish != null && audioLangsToPublish.TryGetValue(langCode, out InclusionSetting includeAudioSetting))
                        {
                            includeAudio = includeAudioSetting.IsIncluded();
                        }

                        var value = new LanguagePublishInfo()
                        {
                            code             = kvp.Key,
                            name             = request.CurrentBook.PrettyPrintLanguage(langCode),
                            complete         = kvp.Value,
                            includeText      = includeText,
                            containsAnyAudio = _languagesWithAudio.Contains(langCode),
                            includeAudio     = includeAudio
                        };
                        var json = JsonConvert.SerializeObject(value);
                        return(json);
                    })) + "]";

                    request.ReplyWithText(result);
                }
예제 #4
0
        public void RegisterWithApiHandler(BloomApiHandler apiHandler)
        {
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "save", request =>
            {
                {
                    string suggestedName = string.Format("{0}-{1}.epub", Path.GetFileName(_bookSelection.CurrentSelection.FolderPath),
                                                         _bookSelection.CurrentSelection.BookData.Language1.GetNameInLanguage("en"));
                    using (var dlg = new DialogAdapters.SaveFileDialogAdapter())
                    {
                        if (!string.IsNullOrEmpty(_lastDirectory) && Directory.Exists(_lastDirectory))
                        {
                            dlg.InitialDirectory = _lastDirectory;
                        }
                        dlg.FileName        = suggestedName;
                        dlg.Filter          = "EPUB|*.epub";
                        dlg.OverwritePrompt = true;
                        if (DialogResult.OK == dlg.ShowDialog())
                        {
                            _lastDirectory = Path.GetDirectoryName(dlg.FileName);
                            lock (_epubMakerLock)
                            {
                                _pendingSaveAsPath = dlg.FileName;
                                if (!_stagingEpub)
                                {
                                    // we can do it right now. No need to check version etc., because anything
                                    // that will change the epub we want to save will immediately trigger a new
                                    // preview, and we will be staging it until we have it.
                                    SaveAsEpub();
                                }
                                // If we ARE in the middle of staging the epub...quite possible since this
                                // handler is registered with permission to execute in parallel with other
                                // API handlers, the user just has to click Save before the preview is finished...
                                // then we need not do any more here. A call to SaveAsEpub at the end of the
                                // preview generation process will pick up the pending request in _pendingSaveAsPath
                                // and complete the Save.
                            }

                            ReportProgress(LocalizationManager.GetString("PublishTab.Epub.Done", "Done"));
                            ReportAnalytics("Save ePUB");
                        }
                    }

                    request.PostSucceeded();
                }
            }, true, false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "epubSettings", request =>
            {
                if (request.HttpMethod == HttpMethods.Get)
                {
                    request.ReplyWithJson(GetEpubSettings());
                }
                else
                {
                    // post is deprecated.
                    throw new ApplicationException("epubSettings POST is deprecated");
                }
            }, false);

            // The backend here was written with an enum that had two choices for how to publish descriptions, but we only ever
            // have used one of them so far in the UI. So this is a boolean api that converts to an enum underlying value.
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "imageDescriptionSetting",
                                                      request => request.CurrentBook.BookInfo.MetaData.Epub_HowToPublishImageDescriptions == BookInfo.HowToPublishImageDescriptions.OnPage,
                                                      (request, onPage) =>
            {
                request.CurrentBook.BookInfo.MetaData.Epub_HowToPublishImageDescriptions = onPage
                                                ? BookInfo.HowToPublishImageDescriptions.OnPage
                                                : BookInfo.HowToPublishImageDescriptions.None;
                request.CurrentBook.BookInfo.Save();
                var newSettings = _desiredEpubSettings.Clone();
                newSettings.howToPublishImageDescriptions = request.CurrentBook.BookInfo.MetaData.Epub_HowToPublishImageDescriptions;
                RefreshPreview(newSettings);
            },
                                                      false);

            // Saving a checkbox setting that the user ticks to say "Use my E-reader's font sizes"
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "removeFontSizesSetting",
                                                      request => request.CurrentBook.BookInfo.MetaData.Epub_RemoveFontSizes,
                                                      (request, booleanSetting) => {
                request.CurrentBook.BookInfo.MetaData.Epub_RemoveFontSizes = booleanSetting;
                request.CurrentBook.BookInfo.Save();
                var newSettings             = _desiredEpubSettings.Clone();
                newSettings.removeFontSizes = booleanSetting;
                RefreshPreview(newSettings);
            },
                                                      false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "updatePreview", request =>
            {
                RefreshPreview(_desiredEpubSettings);
                request.PostSucceeded();
            }, false);             // in fact, must NOT be on UI thread

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "abortPreview", request =>
            {
                AbortMakingEpub();

                request.PostSucceeded();
            }, false, false);
        }
        public void RegisterWithApiHandler(BloomApiHandler apiHandler)
        {
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "bookName", request =>
            {
                request.ReplyWithText(request.CurrentBook.TitleBestForUserDisplay);
            }, false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "showAccessibilityChecker", request =>
            {
                AccessibilityCheckWindow.StaticShow(() => _webSocketServer.SendEvent(kWebSocketContext, kWindowActivated));
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "descriptionsForAllImages", request =>
            {
                var problems    = AccessibilityCheckers.CheckDescriptionsForAllImages(request.CurrentBook);
                var resultClass = problems.Any() ? "failed" : "passed";
                request.ReplyWithJson(new { resultClass = resultClass, problems = problems });
            }, false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "audioForAllImageDescriptions", request =>
            {
                var problems    = AccessibilityCheckers.CheckAudioForAllImageDescriptions(request.CurrentBook);
                var resultClass = problems.Any() ? "failed" : "passed";
                request.ReplyWithJson(new { resultClass = resultClass, problems = problems });
            }, false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "audioForAllText", request =>
            {
                var problems    = AccessibilityCheckers.CheckAudioForAllText(request.CurrentBook);
                var resultClass = problems.Any() ? "failed" : "passed";
                request.ReplyWithJson(new { resultClass = resultClass, problems = problems });
            }, false);

            // Just a checkbox that the user ticks to say "yes, I checked this"
            // At this point, we don't have a way to clear that when the book changes.
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "noEssentialInfoByColor",
                                                      request => request.CurrentBook.BookInfo.MetaData.A11y_NoEssentialInfoByColor,
                                                      (request, b) => {
                request.CurrentBook.BookInfo.MetaData.A11y_NoEssentialInfoByColor = b;
                request.CurrentBook.Save();
            },
                                                      false);

            // Just a checkbox that the user ticks to say "yes, I checked this"
            // At this point, we don't have a way to clear that when the book changes.
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "noTextIncludedInAnyImages",
                                                      request => request.CurrentBook.BookInfo.MetaData.A11y_NoTextIncludedInAnyImages,
                                                      (request, b) => {
                request.CurrentBook.BookInfo.MetaData.A11y_NoTextIncludedInAnyImages = b;
                request.CurrentBook.Save();
            },
                                                      false);

            //enhance: this might have to become async to work on large books on slow computers
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "aceByDaisyReportUrl", request => { MakeAceByDaisyReport(request); },
                                               false, false
                                               );

            // A checkbox that the user ticks in the Accessible Image tool to request a preview
            // of how things might look with cataracts.
            // For now this doesn't seem worth persisting, except for the session so it sticks from page to page.
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "cataracts",
                                                      request => _simulateCataracts,
                                                      (request, b) => { _simulateCataracts = b; },
                                                      false);
            // A checkbox that the user ticks in the Accessible Image tool to request a preview
            // of how things might look with color-blindness, and a set of radio buttons
            // for choosing different kinds of color-blindness.
            // For now these doesn't seem worth persisting, except for the session so it sticks from page to page.
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "colorBlindness",
                                                      request => _simulateColorBlindness,
                                                      (request, b) => { _simulateColorBlindness = b; },
                                                      false);
            apiHandler.RegisterEnumEndpointHandler(kApiUrlPart + "kindOfColorBlindness",
                                                   request => _kindOfColorBlindness,
                                                   (request, kind) => _kindOfColorBlindness = kind, false);
        }
예제 #6
0
        public void RegisterWithApiHandler(BloomApiHandler apiHandler)
        {
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "recordVideo", request =>
            {
                RecordVideo(request);
                request.PostSucceeded();
            }, true, false);

            // This is sent directly from BloomPlayer when it gets to the end of making the recording.
            // The player gives Bloom a list of all the sounds it played and their timings so we can
            // merge them into the captured video.
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "soundLog", request =>
            {
                var soundLog = request.RequiredPostJson();
                _recordVideoWindow.StopRecording(soundLog);
                request.PostSucceeded();
            }, true, false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "playVideo", request =>
            {
                _recordVideoWindow.PlayVideo();
                request.PostSucceeded();
            }, true, false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "settings", request =>
            {
                if (request.HttpMethod == HttpMethods.Get)
                {
                    var settings      = request.CurrentBook.BookInfo.PublishSettings.AudioVideo;
                    var lastPageIndex = request.CurrentBook.GetPages().Count() - 1;
                    // this default might be too high but I don't think it will ever be too low.
                    // The HTML side will handle it being too high.
                    var pageRange = new[] { 0, lastPageIndex };
                    if (settings.PageRange != null && settings.PageRange.Length == 2)
                    {
                        // I wanted to do something like this as validation, in case the book changed
                        // since we saved. But we don't have an accurate page count of the modified version
                        // of the book that the range applies to yet. (e.g., switch to device xmatter, strip
                        // activities,...
                        //pageRange[1] = Math.Min(settings.PageRange[1], lastPageIndex); // not more than the pages we have
                        //pageRange[0] = Math.Min(settings.PageRange[0], pageRange[1] - 1); // at least one less than lim
                        //if (pageRange[0] < 0)
                        //	pageRange[0] = 0;
                        //if (pageRange[1] < pageRange[0] + 1)
                        //	pageRange[1] = Math.Min(pageRange[0] + 1, lastPageIndex);
                        pageRange = settings.PageRange;
                    }
                    request.ReplyWithJson(new {
                        format        = settings.Format,
                        pageTurnDelay = settings.PageTurnDelayDouble,
                        motion        = settings.Motion,
                        pageRange
                    });
                }
                else
                {
                    var data        = DynamicJson.Parse(request.RequiredPostJson());
                    var settings    = request.CurrentBook.BookInfo.PublishSettings.AudioVideo;
                    var oldMotion   = settings.Motion;
                    settings.Format = data.format;
                    settings.PageTurnDelayDouble = data.pageTurnDelay;
                    settings.Motion = data.motion;
                    // pageRange also comes in as doubles. "as double[]" does not work, so have to do them individually.
                    settings.PageRange = new int[0];
                    // Typescript passes an empty array if all pages are selected.
                    // This allows us naturally to keep everything selected until the user explicitly changes something.
                    if (data.pageRange.IsArray && data.pageRange.Count == 2)
                    {
                        var start          = (int)data.pageRange[0];
                        var end            = (int)data.pageRange[1];
                        settings.PageRange = new[] { start, end };
                    }

                    request.CurrentBook.BookInfo.SavePublishSettings();

                    _recordVideoWindow?.SetPageReadTime(settings.PageTurnDelayDouble.ToString());
                    _recordVideoWindow?.SetFormat(request.CurrentBook.BookInfo.PublishSettings.AudioVideo.Format,
                                                  ShouldRecordAsLandscape(request.CurrentBook), request.CurrentBook.GetLayout());
                    if (settings.Motion != oldMotion)
                    {
                        UpdatePreview(request);                         // does its own success/fail
                    }
                    else
                    {
                        request.PostSucceeded();
                    }
                }
            }, true, false);
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "videoSettings", request =>
            {
                if (request.HttpMethod == HttpMethods.Get)
                {
                    request.ReplyWithText(request.CurrentBook.BookInfo.PublishSettings.AudioVideo.PlayerSettings ?? "");
                }
                else
                {
                    request.CurrentBook.BookInfo.PublishSettings.AudioVideo.PlayerSettings =
                        request.RequiredPostString();
                    request.CurrentBook.BookInfo.SavePublishSettings();
                    request.PostSucceeded();
                }
            }, true, false);


            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "hasActivities",
                                                      request =>
            {
                return(request.CurrentBook.HasActivities);
            },
                                                      null,  // no write action
                                                      false,
                                                      true); // we don't really know, just safe default

            // Returns true if publish to MP3 is supported for this book, false otherwise.
            // To be eligible to publish to MP3, the book must contain narration audio.
            // (There's not any point to an MP3 if the book has absolutely no audio...
            // Even a book with background music but no narration, there's not too much point to an MP3 either)
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "isMP3FormatSupported", request =>
            {
                // ENHANCE: If desired, you can make it only consider languages in the book that are currently relevant,
                // instead of any language in the book.
                var narrationAudioLangs = request.CurrentBook.GetLanguagesWithAudio();
                return(narrationAudioLangs.Any());
            }, null, false, false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "startRecording", request =>
            {
                var videoList        = request.GetPostStringOrNull();
                var anyVideoHasAudio = _recordVideoWindow?.AnyVideoHasAudio(videoList, request.CurrentBook.FolderPath) ?? false;
                if (anyVideoHasAudio)
                {
                    var messageBoxButtons = new[]
                    {
                        new MessageBoxButton()
                        {
                            Text = LocalizationManager.GetString("Common.Continue", "Continue"), Id = "continue"
                        },
                        new MessageBoxButton()
                        {
                            Text = LocalizationManager.GetString("Common.Cancel", "Cancel"), Id = "cancel", Default = true
                        }
                    };
                    if (BloomMessageBox.Show(null,
                                             LocalizationManager.GetString("PublishTab.RecordVideo.NoAudioInVideo",
                                                                           "Currently, Bloom does not support including audio from Sign Language videos in video output."),
                                             messageBoxButtons) == "cancel")
                    {
                        request.Failed("canceled");
                        _recordVideoWindow.Close();
                        return;
                    }
                }
                _recordVideoWindow?.StartFfmpegForVideoCapture();
                request.PostSucceeded();
            }, true, false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "shouldUseOriginalPageSize",
                                               request =>
            {
                RecordVideoWindow.GetDataForFormat(request.CurrentBook.BookInfo.PublishSettings.AudioVideo.Format,
                                                   ShouldRecordAsLandscape(request.CurrentBook), request.CurrentBook.GetLayout(),
                                                   out _, out _, out _, out bool useOriginalPageSize);
                request.ReplyWithBoolean(useOriginalPageSize);
            },
                                               true, // has to be on UI thread because it uses Bloom's main window to find the right screen
                                               false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "tooBigForScreenMsg",
                                               request =>
            {
                request.ReplyWithText(RecordVideoWindow.GetDataForFormat(request.CurrentBook.BookInfo.PublishSettings.AudioVideo.Format,
                                                                         ShouldRecordAsLandscape(request.CurrentBook), request.CurrentBook.GetLayout(),
                                                                         out _, out _, out _, out _));
            },
                                               true, // has to be on UI thread because it uses Bloom's main window to find the right screen
                                               false);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "saveVideo", request =>
            {
                if (_recordVideoWindow == null)
                {
                    // This shouldn't be possible, but just in case, we'll kick off the recording now.
                    RecordVideo(request);
                }

                _recordVideoWindow?.SetBook(request.CurrentBook);

                // If we bring up the dialog inside this API call, it may time out.
                Application.Idle += SaveVideoOnIdle;
                request.PostSucceeded();
            }, true, false);


            apiHandler.RegisterEndpointHandler(kApiUrlPart + "updatePreview", request => { UpdatePreview(request); }, false);
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "displaySettings", request =>
            {
                Process.Start("desk.cpl");
                request.PostSucceeded();
            }, false);
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "isScalingActive",
                                                      request => IsScalingActive(),
                                                      null, true);
        }
예제 #7
0
        public void RegisterWithApiHandler(BloomApiHandler apiHandler)
        {
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "save", HandleEpubSave, true, false);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "epubSettings", request =>
            {
                if (request.HttpMethod == HttpMethods.Get)
                {
                    request.ReplyWithJson(GetEpubSettings());
                }
                else
                {
                    // post is deprecated.
                    throw new ApplicationException("epubSettings POST is deprecated");
                }
            }, false);

            // The backend here was written with an enum that had two choices for how to publish descriptions, but we only ever
            // have used one of them so far in the UI. So this is a boolean api that converts to an enum underlying value.
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "imageDescriptionSetting",
                                                      request => request.CurrentBook.BookInfo.PublishSettings.Epub.HowToPublishImageDescriptions == BookInfo.HowToPublishImageDescriptions.OnPage,
                                                      (request, onPage) =>
            {
                request.CurrentBook.BookInfo.PublishSettings.Epub.HowToPublishImageDescriptions = onPage
                                                ? BookInfo.HowToPublishImageDescriptions.OnPage
                                                : BookInfo.HowToPublishImageDescriptions.None;
                request.CurrentBook.BookInfo.Save();
                var newSettings = _desiredEpubSettings.Clone();
                newSettings.howToPublishImageDescriptions = request.CurrentBook.BookInfo.PublishSettings.Epub.HowToPublishImageDescriptions;
                RefreshPreview(newSettings);
            },
                                                      false);

            // Saving a checkbox setting that the user ticks to say "Use my E-reader's font sizes"
            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "removeFontSizesSetting",
                                                      request => request.CurrentBook.BookInfo.PublishSettings.Epub.RemoveFontSizes,
                                                      (request, booleanSetting) => {
                request.CurrentBook.BookInfo.PublishSettings.Epub.RemoveFontSizes = booleanSetting;
                request.CurrentBook.BookInfo.Save();
                var newSettings             = _desiredEpubSettings.Clone();
                newSettings.removeFontSizes = booleanSetting;
                RefreshPreview(newSettings);
            },
                                                      false);

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "updatePreview", request =>
            {
                RefreshPreview(_desiredEpubSettings);
                request.PostSucceeded();
                if (request.CurrentBook?.ActiveLanguages != null)
                {
                    var message = new LicenseChecker().CheckBook(request.CurrentBook, request.CurrentBook.ActiveLanguages.ToArray());
                    _webSocketServer.SendString(kWebsocketContext, kWebsocketState_LicenseOK, (message == null) ? "true" : "false");
                }
            }, false);             // in fact, must NOT be on UI thread

            apiHandler.RegisterEndpointLegacy(kApiUrlPart + "abortPreview", request =>
            {
                AbortMakingEpub();

                request.PostSucceeded();
            }, false, false);
        }
        public void RegisterWithApiHandler(BloomApiHandler apiHandler)
        {
            // This is just for storing the user preference of method
            // If we had a couple of these, we could just have a generic preferences api
            // that browser-side code could use.
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "method", request =>
            {
                if (request.HttpMethod == HttpMethods.Get)
                {
                    var method = Settings.Default.PublishAndroidMethod;
                    if (!new string[] { "wifi", "usb", "file" }.Contains(method))
                    {
                        method = "wifi";
                    }
                    request.ReplyWithText(method);
                }
                else                 // post
                {
                    Settings.Default.PublishAndroidMethod = request.RequiredPostString();
#if __MonoCS__
                    if (Settings.Default.PublishAndroidMethod == "usb")
                    {
                        _progress.MessageWithoutLocalizing("Sorry, this method is not available on Linux yet.");
                    }
#endif
                    request.PostSucceeded();
                }
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "backColor", request =>
            {
                if (request.HttpMethod == HttpMethods.Get)
                {
                    if (request.CurrentBook != _coverColorSourceBook)
                    {
                        _coverColorSourceBook = request.CurrentBook;
                        ImageUtils.TryCssColorFromString(request.CurrentBook?.GetCoverColor() ?? "", out _thumbnailBackgroundColor);
                    }
                    request.ReplyWithText(ToCssColorString(_thumbnailBackgroundColor));
                }
                else                 // post
                {
                    // ignore invalid colors (very common while user is editing hex)
                    Color newColor;
                    var newColorAsString = request.RequiredPostString();
                    if (ImageUtils.TryCssColorFromString(newColorAsString, out newColor))
                    {
                        _thumbnailBackgroundColor = newColor;
                        request.CurrentBook.SetCoverColor(newColorAsString);
                    }
                    request.PostSucceeded();
                }
            }, true);

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "motionBookMode",
                                                      readRequest =>
            {
                // If the user has taken off all possible motion, force not having motion in the
                // Bloom Reader book.  See https://issues.bloomlibrary.org/youtrack/issue/BL-7680.
                if (!readRequest.CurrentBook.HasMotionPages)
                {
                    readRequest.CurrentBook.MotionMode = false;
                }
                return(readRequest.CurrentBook.MotionMode);
            },
                                                      (writeRequest, value) =>
            {
                writeRequest.CurrentBook.MotionMode = value;
            }
                                                      , true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "updatePreview", request =>
            {
                if (request.HttpMethod == HttpMethods.Post)
                {
                    // This is already running on a server thread, so there doesn't seem to be any need to kick off
                    // another background one and return before the preview is ready. But in case something in C#
                    // might one day kick of a new preview, or we find we do need a background thread,
                    // I've made it a websocket broadcast when it is ready.
                    // If we've already left the publish tab...we can get a few of these requests queued up when
                    // a tester rapidly toggles between views...abandon the attempt
                    if (!PublishHelper.InPublishTab)
                    {
                        request.Failed("aborted, no longer in publish tab");
                        return;
                    }
                    try
                    {
                        UpdatePreview(request);
                        request.PostSucceeded();
                    }
                    catch (Exception e)
                    {
                        request.Failed("Error while updating preview. Message: " + e.Message);
                        NonFatalProblem.Report(ModalIf.Alpha, PassiveIf.All, "Error while updating preview.", null, e, true);
                    }
                }
            }, false);


            apiHandler.RegisterEndpointHandler(kApiUrlPart + "thumbnail", request =>
            {
                var coverImage = request.CurrentBook.GetCoverImagePath();
                if (coverImage == null)
                {
                    request.Failed("no cover image");
                }
                else
                {
                    // We don't care as much about making it resized as making its background transparent.
                    using (var thumbnail = TempFile.CreateAndGetPathButDontMakeTheFile())
                    {
                        if (_thumbnailBackgroundColor == Color.Transparent)
                        {
                            ImageUtils.TryCssColorFromString(request.CurrentBook?.GetCoverColor(), out _thumbnailBackgroundColor);
                        }
                        RuntimeImageProcessor.GenerateEBookThumbnail(coverImage, thumbnail.Path, 256, 256, _thumbnailBackgroundColor);
                        request.ReplyWithImage(thumbnail.Path);
                    }
                }
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "usb/start", request =>
            {
#if !__MonoCS__
                SetState("UsbStarted");
                UpdatePreviewIfNeeded(request);
                _usbPublisher.Connect(request.CurrentBook, _thumbnailBackgroundColor, GetSettings());
#endif
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "usb/stop", request =>
            {
#if !__MonoCS__
                _usbPublisher.Stop();
                SetState("stopped");
#endif
                request.PostSucceeded();
            }, true);
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "wifi/start", request =>
            {
                SetState("ServingOnWifi");
                UpdatePreviewIfNeeded(request);
                _wifiPublisher.Start(request.CurrentBook, request.CurrentCollectionSettings, _thumbnailBackgroundColor, GetSettings());

                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "wifi/stop", request =>
            {
                _wifiPublisher.Stop();
                SetState("stopped");
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "file/save", request =>
            {
                UpdatePreviewIfNeeded(request);
                FilePublisher.Save(request.CurrentBook, _bookServer, _thumbnailBackgroundColor, _progress, GetSettings());
                SetState("stopped");
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "cleanup", request =>
            {
                Stop();
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterEndpointHandler(kApiUrlPart + "textToClipboard", request =>
            {
                PortableClipboard.SetText(request.RequiredPostString());
                request.PostSucceeded();
            }, true);

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "canHaveMotionMode",
                                                      request =>
            {
                return(request.CurrentBook.HasMotionPages);
            },
                                                      null,  // no write action
                                                      false,
                                                      true); // we don't really know, just safe default

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "canRotate",
                                                      request =>
            {
                return(request.CurrentBook.MotionMode && request.CurrentBook.HasMotionPages);
            },
                                                      null,  // no write action
                                                      false,
                                                      true); // we don't really know, just safe default

            apiHandler.RegisterBooleanEndpointHandler(kApiUrlPart + "defaultLandscape",
                                                      request =>
            {
                return(request.CurrentBook.GetLayout().SizeAndOrientation.IsLandScape);
            },
                                                      null,  // no write action
                                                      false,
                                                      true); // we don't really know, just safe default
            apiHandler.RegisterEndpointHandler(kApiUrlPart + "languagesInBook", request =>
            {
                try
                {
                    InitializeLanguagesInBook(request);

                    Dictionary <string, InclusionSetting> textLangsToPublish  = request.CurrentBook.BookInfo.MetaData.TextLangsToPublish.ForBloomPUB;
                    Dictionary <string, InclusionSetting> audioLangsToPublish = request.CurrentBook.BookInfo.MetaData.AudioLangsToPublish.ForBloomPUB;

                    var result = "[" + string.Join(",", _allLanguages.Select(kvp =>
                    {
                        string langCode = kvp.Key;

                        bool includeText = false;
                        if (textLangsToPublish != null && textLangsToPublish.TryGetValue(langCode, out InclusionSetting includeTextSetting))
                        {
                            includeText = includeTextSetting.IsIncluded();
                        }

                        bool includeAudio = false;
                        if (audioLangsToPublish != null && audioLangsToPublish.TryGetValue(langCode, out InclusionSetting includeAudioSetting))
                        {
                            includeAudio = includeAudioSetting.IsIncluded();
                        }

                        var value = new LanguagePublishInfo()
                        {
                            code             = kvp.Key,
                            name             = request.CurrentBook.PrettyPrintLanguage(langCode),
                            complete         = kvp.Value,
                            includeText      = includeText,
                            containsAnyAudio = _languagesWithAudio.Contains(langCode),
                            includeAudio     = includeAudio
                        };
                        var json = JsonConvert.SerializeObject(value);
                        return(json);
                    })) + "]";

                    request.ReplyWithText(result);
                }