Ejemplo n.º 1
0
        private void UsbFailConnect(Exception e)
        {
            Stop();
            _progress.Message(idSuffix: "UnableToConnect",
                              message: "Unable to connect to any Android device.");

            _progress.ErrorWithoutLocalizing("\tTechnical details to share with the development team: " + e);
            Logger.WriteError(e);
            Stopped();
        }
Ejemplo n.º 2
0
        public void Start(Book.Book book, CollectionSettings collectionSettings, Color backColor)
        {
            if (_wifiAdvertiser != null)
            {
                Stop();
            }

            // This listens for a BloomReader to request a book.
            // It requires a firewall hole allowing Bloom to receive messages on _portToListen.
            // We initialize it before starting the Advertiser to avoid any chance of a race condition
            // where a BloomReader manages to request an advertised book before we start the listener.
            _wifiListener = new BloomReaderUDPListener();
            _wifiListener.NewMessageReceived += (sender, args) =>
            {
                var json = Encoding.UTF8.GetString(args.Data);
                try
                {
                    dynamic settings = JsonConvert.DeserializeObject(json);
                    // The property names used here must match the ones in BloomReader, doInBackground method of SendMessage,
                    // a private class of NewBookListenerService.
                    var androidIpAddress = (string)settings.deviceAddress;

                    var androidName = (string)settings.deviceName;
                    // This prevents the device (or other devices) from queuing up requests while we're busy with this one.
                    // In effect, the Android is only allowed to request a retry after we've given up this try at sending.
                    // Of course, there are async effects from network latency. But if we do get another request while
                    // handling this one, we will ignore it, since StartSendBook checks for a transfer in progress.
                    _wifiAdvertiser.Paused = true;
                    StartSendBookOverWiFi(book, androidIpAddress, androidName, backColor);
                    // Returns immediately. But we don't resume advertisements until the async send completes.
                }
                // If there's something wrong with the JSON (maybe an obsolete or newer version of reader?)
                // just ignore the request.
                catch (Exception ex) when(ex is JsonReaderException || ex is JsonSerializationException)
                {
                    _progress.Error(id: "BadBookRequest",
                                    message: "Got a book request we could not process. Possibly the device is running an incompatible version of BloomReader?");

                    //this is too technical/hard to translate
                    _progress.ErrorWithoutLocalizing($" Request contains {json}; trying to interpret as JSON we got {ex.Message}");
                }
            };

            var pathHtmlFile = book.GetPathHtmlFile();

            _wifiAdvertiser = new WiFiAdvertiser(_progress)
            {
                BookTitle     = BookStorage.SanitizeNameForFileSystem(book.Title),             // must be the exact same name as the file we will send if requested
                TitleLanguage = collectionSettings.Language1Iso639Code,
                BookVersion   = Book.Book.MakeVersionCode(File.ReadAllText(pathHtmlFile), pathHtmlFile)
            };

            AndroidView.CheckBookLayout(book, _progress);
            _wifiAdvertiser.Start();

            _progress.Message(id: "WifiInstructions1",
                              message: "On the Android, run Bloom Reader, open the menu and choose 'Receive Books from computer'.");
            _progress.Message(id: "WifiInstructions2",
                              message: "You can do this on as many devices as you like. Make sure each device is connected to the same network as this computer.");
        }
Ejemplo n.º 3
0
 private void Work()
 {
     _progress.Message(id: "beginAdvertising", message: "Advertising book to Bloom Readers on local network...");
     try
     {
         while (true)
         {
             if (!Paused)
             {
                 UpdateAdvertisementBasedOnCurrentIpAddress();
                 _client.BeginSend(_sendBytes, _sendBytes.Length, _endPoint, SendCallback, _client);
             }
             Thread.Sleep(1000);
         }
     }
     catch (ThreadAbortException)
     {
         _progress.Message(id: "Stopped", message: "Stopped Advertising.");
         _client.Close();
     }
     catch (Exception error)
     {
         // not worth localizing
         _progress.ErrorWithoutLocalizing($"Error in Advertiser: {error.Message}");
     }
 }
        private void MakeAceByDaisyReport(ApiRequest request)
        {
            // First check whether ace has been installed.
            var daisyDirectory = FindAceByDaisyOrTellUser(request);             // this method does the request.fail() if needed

            if (string.IsNullOrEmpty(daisyDirectory))
            {
                return;
            }
            // As of version 1.0.2, the ace report has stylesheets on the internet.  See https://issues.bloomlibrary.org/youtrack/issue/BL-6118.
            // To be specific, https://cdn.datatables.net/1.10.15/css/dataTables.bootstrap4.min.css and
            // https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css.
            if (!UrlLookup.CheckGeneralInternetAvailability(true))
            {
                _webSocketProgress.ErrorWithoutLocalizing(
                    "Sorry, you must have an internet connection in order to view the Ace by DAISY report.");
                request.Failed();
                return;
            }

            var reportRootDirectory = Path.Combine(System.IO.Path.GetTempPath(), "daisy-ace-reports");

            // Do our best at clearing out previous runs.
            // This call is ok if the directory does not exist at all.
            SIL.IO.RobustIO.DeleteDirectoryAndContents(reportRootDirectory);
            // This call is ok if the above failed and it still exists
            Directory.CreateDirectory(reportRootDirectory);

            // was having a problem with some files from previous reports getting locked.
            // so give new folder names if needed
            var haveReportedError = false;
            var errorMessage      = "Unknown Error";

            var version = GetAceByDaisyVersion(daisyDirectory);

            if (version.old)
            {
                _webSocketProgress.MessageWithoutLocalizing(
                    $"You appear to have an older version ({version.version}) of ACE by Daisy. This may cause problems.",
                    ProgressKind.Warning);
                _webSocketProgress.MessageWithoutLocalizing(
                    "We recommend you run \"npm install @daisy/ace -g\" from a command line to get the latest.",
                    ProgressKind.Warning);
            }

            var started = DateTime.Now;

            var epubPath = MakeEpub(reportRootDirectory, _webSocketProgress);

            // Try 3 times. It could be that this is no longer needed, but working on a developer
            // machine isn't proof.
            for (var i = 0; i < 3; i++)
            {
                var randomName      = Guid.NewGuid().ToString();
                var reportDirectory = Path.Combine(reportRootDirectory, randomName);

                var       arguments             = $"ace.js --verbose -o \"{reportDirectory}\" \"{epubPath}\"";
                const int kSecondsBeforeTimeout = 60;
                var       progress = new NullProgress();
                _webSocketProgress.MessageWithoutLocalizing("Running Ace by DAISY");

                ExecutionResult res    = null;
                string          ldpath = null;
                try
                {
                    // Without this variable switching on Linux, the chrome inside ace finds the
                    // wrong version of a library as part of our mozilla code.
                    ldpath = Environment.GetEnvironmentVariable("LD_LIBRARY_PATH");
                    Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", null);
                    res = CommandLineRunner.Run("node", arguments, Encoding.UTF8, daisyDirectory, kSecondsBeforeTimeout,
                                                progress,
                                                (dummy) => { });
                }
                finally
                {
                    // Restore the variable for our next geckofx browser to find.
                    if (!String.IsNullOrEmpty(ldpath))
                    {
                        Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", ldpath);
                    }
                }

                if (res.DidTimeOut)
                {
                    errorMessage = $"Daisy Ace timed out after {kSecondsBeforeTimeout} seconds.";
                    _webSocketProgress.ErrorWithoutLocalizing(errorMessage);
                    continue;
                }

                var answerPath = Path.Combine(reportDirectory, "report.html");
                if (!File.Exists(answerPath))
                {
                    // This hasn't been effectively reproduced, but there was a case where this would fail at least
                    // half the time on a book, reproducable. That book had 2 pages pointing at placeholder.png,
                    // and we were getting an error related to it being locked. So we deduce that ace was trying
                    // to copy the file twice, at the same time (normal nodejs code is highly async).
                    // Now the problem is not reproducable, but I'm leaving in this code that tried to deal with it.
                    errorMessage = $"Exit code{res.ExitCode}{Environment.NewLine}" +
                                   $"Standard Error{Environment.NewLine}{res.StandardError}{Environment.NewLine}" +
                                   $"Standard Out{res.StandardOutput}";

                    _webSocketProgress.ErrorWithoutLocalizing(errorMessage);

                    continue;                     // something went wrong, try again
                }

                // The html client is set to treat a text reply as a url of the report. Make sure it's valid for being a URL.
                // See https://silbloom.myjetbrains.com/youtrack/issue/BL-6197.
                request.ReplyWithText("/bloom/" + answerPath.EscapeFileNameForHttp());
                if (version.old)
                {
                    // If we displayed an important message about updating ACE, make sure the user
                    // has SOME time to see it.
                    // Review: is this long enough? Should we look for some other way to show the
                    // problem? At present the problems caused by an old version (1.1 is the oldest
                    // contemporary with Bloom) is fairly minor.
                    while (DateTime.Now - started < new TimeSpan(0, 0, 0, 5))
                    {
                        Thread.Sleep(100);
                    }
                }
                return;
            }

            // If we get this far, we give up.
            ReportErrorAndFailTheRequest(request, errorMessage);
        }