Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Intergrated with [Spam Filter](https://runelite.net/plugin-hub/show/spamfilter)
- Added `Use for dialogs` option to enable abbreviations for NPC dialogs
- `Shortened phrases` are now `Abbreviations`
- `Abbreviations` field is now `Custom abbreviations`
- Voice pack downloads now show the reason when they fail

## 1.2.6
- Updated Menu event handler to work with the latest update affecting submenus
Expand Down
39 changes: 36 additions & 3 deletions src/main/java/dev/phyce/naturalspeech/downloader/DownloadTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Objects;
import java.util.function.Supplier;
import javax.net.ssl.SSLException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
Expand All @@ -31,6 +37,8 @@ public class DownloadTask implements Supplier<File> {
private volatile float progress = 0;
@Getter
private volatile int error = 0;
@Getter
private volatile String errorMessage = null;

// TODO(Louis) Add a callback that updates the download progress
public DownloadTask(OkHttpClient httpClient, Path destination, HttpUrl url, boolean overwrite) {
Expand All @@ -48,7 +56,7 @@ public void download(ProgressListener progressListener) {
try (Response response = httpClient.newCall(req).execute()) {
if (!response.isSuccessful()) {
error = response.code();
throw new IOException("Failed to download file: " + response.message());
throw new IOException("Server returned HTTP " + response.code() + " " + response.message());
}

int length = (int) Objects.requireNonNull(response.body()).contentLength();
Expand Down Expand Up @@ -78,14 +86,39 @@ public void download(ProgressListener progressListener) {
downloading = false;
}
} catch (IOException e) {
log.error("Error downloading the file: {}", e.getMessage());
errorMessage = describe(e);
log.error("Error downloading {}: {}", url, errorMessage);
progress = 0; // Reset progress if download fails
error = 1;
if (error == 0) error = 1;
downloading = false;
}
}
}

private String describe(IOException e) {
if (e instanceof AccessDeniedException) {
return "Permission denied writing to " + destination;
}
if (e instanceof NoSuchFileException) {
return "Folder does not exist: " + destination.getParent();
}
if (e instanceof FileSystemException) {
FileSystemException fse = (FileSystemException) e;
String reason = fse.getReason() != null ? fse.getReason() : e.getClass().getSimpleName();
return "Cannot write to " + destination + " (" + reason + ")";
}
if (e instanceof UnknownHostException) {
return "Cannot resolve host: " + url.host() + " (check your internet connection)";
}
if (e instanceof ConnectException) {
return "Cannot connect to " + url.host() + ": " + e.getMessage();
}
if (e instanceof SSLException) {
return "Secure connection to " + url.host() + " failed: " + e.getMessage();
}
return e.getMessage() != null ? e.getMessage() : e.getClass().getSimpleName();
}

@Override
public synchronized File get() {
if (!finished && !downloading && error == 0) download(null);
Expand Down
28 changes: 19 additions & 9 deletions src/main/java/dev/phyce/naturalspeech/tts/ModelRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ public ModelLocal loadModelLocal(String modelName) throws IOException {
localVoiceValid = false;
// create the folder
if (!voiceFolder.toFile().mkdirs()) {
// if we fail to create the folder, just toss an error
throw new IOException("Failed to create voice folder.");
throw new IOException("Cannot create voice folder: " + voiceFolder
+ " (check folder permissions and that the path is writable)");
}
}

Expand All @@ -198,13 +198,23 @@ public ModelLocal loadModelLocal(String modelName) throws IOException {
voiceFolder.resolve(modelName + METADATA_EXTENSION));

// thread blocking download
File onnx = onnxTask.get();
File onnxMetadata = onnxMetadataTask.get();
File speakers = speakersTask.get();

if (!onnx.exists() || !onnxMetadata.exists() || !speakers.exists()) {
// if any of the files doesn't exist after validation, throw
throw new IOException("Voice files downloaded are missing.");
onnxTask.get();
onnxMetadataTask.get();
speakersTask.get();

DownloadTask[] tasks = {onnxTask, onnxMetadataTask, speakersTask};
StringBuilder failures = new StringBuilder();
for (DownloadTask task : tasks) {
if (task.getErrorMessage() != null || !task.getDestination().toFile().exists()) {
String reason = task.getErrorMessage() != null
? task.getErrorMessage()
: "file missing after download";
if (failures.length() > 0) failures.append("\n");
failures.append(task.getDestination().getFileName()).append(": ").append(reason);
}
}
if (failures.length() > 0) {
throw new IOException(failures.toString());
}

log.info("done... {}", modelName);
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/dev/phyce/naturalspeech/ui/panels/ModelListItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,18 @@ public void rebuild() {

modelRepository.loadModelLocal(modelUrl.getModelName());

} catch (IOException ignored) {
SwingUtilities.invokeLater(this::rebuild);
} catch (IOException e) {
log.error("Voice pack download failed: {}", modelUrl.getModelName(), e);
SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(this,
"<html><body style='width:300px'>"
+ "Could not download <b>" + modelUrl.getModelName() + "</b>:<br><br>"
+ e.getMessage().replace("\n", "<br>")
+ "</body></html>",
"Voice pack download failed",
JOptionPane.ERROR_MESSAGE);
rebuild();
});
}
});
});
Expand Down