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(); }
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."); }
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); }