fix: upload skin
This commit is contained in:
@@ -33,6 +33,8 @@ import org.jackhuang.hmcl.auth.offline.OfflineAccount;
|
|||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||||
import org.jackhuang.hmcl.game.TexturesLoader;
|
import org.jackhuang.hmcl.game.TexturesLoader;
|
||||||
import org.jackhuang.hmcl.setting.Accounts;
|
import org.jackhuang.hmcl.setting.Accounts;
|
||||||
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.DialogController;
|
import org.jackhuang.hmcl.ui.DialogController;
|
||||||
import org.jackhuang.hmcl.ui.construct.PromptDialogPane;
|
import org.jackhuang.hmcl.ui.construct.PromptDialogPane;
|
||||||
@@ -82,9 +84,9 @@ public class AccountListItem extends RadioButton {
|
|||||||
return new AccountListItemSkin(this);
|
return new AccountListItemSkin(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refresh() {
|
private Task<?> refreshAsync() {
|
||||||
account.clearCache();
|
return Task.runAsync(() -> {
|
||||||
thread(() -> {
|
account.clearCache();
|
||||||
try {
|
try {
|
||||||
account.logIn();
|
account.logIn();
|
||||||
} catch (CredentialExpiredException e) {
|
} catch (CredentialExpiredException e) {
|
||||||
@@ -94,13 +96,19 @@ public class AccountListItem extends RadioButton {
|
|||||||
// ignore cancellation
|
// ignore cancellation
|
||||||
} catch (Exception e1) {
|
} catch (Exception e1) {
|
||||||
LOG.log(Level.WARNING, "Failed to refresh " + account + " with password", e1);
|
LOG.log(Level.WARNING, "Failed to refresh " + account + " with password", e1);
|
||||||
|
throw e1;
|
||||||
}
|
}
|
||||||
} catch (AuthenticationException e) {
|
} catch (AuthenticationException e) {
|
||||||
LOG.log(Level.WARNING, "Failed to refresh " + account + " with token", e);
|
LOG.log(Level.WARNING, "Failed to refresh " + account + " with token", e);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void refresh() {
|
||||||
|
refreshAsync().whenComplete(e -> {}).start();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean canUploadSkin() {
|
public boolean canUploadSkin() {
|
||||||
return account instanceof YggdrasilAccount && !(account instanceof AuthlibInjectorAccount);
|
return account instanceof YggdrasilAccount && !(account instanceof AuthlibInjectorAccount);
|
||||||
}
|
}
|
||||||
@@ -121,12 +129,15 @@ public class AccountListItem extends RadioButton {
|
|||||||
Controllers.prompt(new PromptDialogPane.Builder(i18n("account.skin.upload"), (questions, resolve, reject) -> {
|
Controllers.prompt(new PromptDialogPane.Builder(i18n("account.skin.upload"), (questions, resolve, reject) -> {
|
||||||
PromptDialogPane.Builder.CandidatesQuestion q = (PromptDialogPane.Builder.CandidatesQuestion) questions.get(0);
|
PromptDialogPane.Builder.CandidatesQuestion q = (PromptDialogPane.Builder.CandidatesQuestion) questions.get(0);
|
||||||
String model = q.getValue() == 0 ? "" : "slim";
|
String model = q.getValue() == 0 ? "" : "slim";
|
||||||
try {
|
refreshAsync()
|
||||||
((YggdrasilAccount) account).uploadSkin(model, selectedFile.toPath());
|
.thenRunAsync(() -> ((YggdrasilAccount) account).uploadSkin(model, selectedFile.toPath()))
|
||||||
resolve.run();
|
.thenComposeAsync(this::refreshAsync)
|
||||||
} catch (AuthenticationException e) {
|
.thenRunAsync(Schedulers.javafx(), resolve::run)
|
||||||
reject.accept(AddAccountPane.accountException(e));
|
.whenComplete(Schedulers.javafx(), e -> {
|
||||||
}
|
if (e != null) {
|
||||||
|
reject.accept(AddAccountPane.accountException(e));
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
}).addQuestion(new PromptDialogPane.Builder.CandidatesQuestion(i18n("account.skin.model"),
|
}).addQuestion(new PromptDialogPane.Builder.CandidatesQuestion(i18n("account.skin.model"),
|
||||||
i18n("account.skin.model.default"), i18n("account.skin.model.slim"))));
|
i18n("account.skin.model.default"), i18n("account.skin.model.slim"))));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ public class YggdrasilAccount extends Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void uploadSkin(String model, Path file) throws AuthenticationException, UnsupportedOperationException {
|
public void uploadSkin(String model, Path file) throws AuthenticationException, UnsupportedOperationException {
|
||||||
service.uploadSkin(characterUUID, model, file);
|
service.uploadSkin(characterUUID, session.getAccessToken(), model, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String randomClientToken() {
|
private static String randomClientToken() {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
|||||||
import org.jackhuang.hmcl.util.gson.ValidationTypeAdapterFactory;
|
import org.jackhuang.hmcl.util.gson.ValidationTypeAdapterFactory;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
import org.jackhuang.hmcl.util.io.HttpMultipartRequest;
|
import org.jackhuang.hmcl.util.io.HttpMultipartRequest;
|
||||||
|
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
import org.jackhuang.hmcl.util.javafx.ObservableOptionalCache;
|
import org.jackhuang.hmcl.util.javafx.ObservableOptionalCache;
|
||||||
|
|
||||||
@@ -151,16 +152,21 @@ public class YggdrasilService {
|
|||||||
requireEmpty(request(provider.getInvalidationURL(), createRequestWithCredentials(accessToken, clientToken)));
|
requireEmpty(request(provider.getInvalidationURL(), createRequestWithCredentials(accessToken, clientToken)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void uploadSkin(UUID uuid, String model, Path file) throws AuthenticationException, UnsupportedOperationException {
|
public void uploadSkin(UUID uuid, String accessToken, String model, Path file) throws AuthenticationException, UnsupportedOperationException {
|
||||||
try {
|
try {
|
||||||
HttpURLConnection con = NetworkUtils.createHttpConnection(provider.getSkinUploadURL(uuid));
|
HttpURLConnection con = NetworkUtils.createHttpConnection(provider.getSkinUploadURL(uuid));
|
||||||
con.setRequestMethod("PUT");
|
con.setRequestMethod("PUT");
|
||||||
|
con.setRequestProperty("Authorization", "Bearer " + accessToken);
|
||||||
con.setDoOutput(true);
|
con.setDoOutput(true);
|
||||||
try (HttpMultipartRequest request = new HttpMultipartRequest(con)) {
|
try (HttpMultipartRequest request = new HttpMultipartRequest(con)) {
|
||||||
|
request.param("model", model);
|
||||||
try (InputStream fis = Files.newInputStream(file)) {
|
try (InputStream fis = Files.newInputStream(file)) {
|
||||||
request.file("file", FileUtils.getName(file), "image/" + FileUtils.getExtension(file), fis);
|
request.file("file", FileUtils.getName(file), "image/" + FileUtils.getExtension(file), fis);
|
||||||
}
|
}
|
||||||
request.param("model", model);
|
}
|
||||||
|
String response = IOUtils.readFullyAsString(con.getInputStream());
|
||||||
|
if (response.startsWith("{")) {
|
||||||
|
handleErrorMessage(fromJson(response, ErrorResponse.class));
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AuthenticationException(e);
|
throw new AuthenticationException(e);
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2020 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
package org.jackhuang.hmcl.util.io;
|
package org.jackhuang.hmcl.util.io;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -17,6 +34,8 @@ public class HttpMultipartRequest implements Closeable {
|
|||||||
|
|
||||||
public HttpMultipartRequest(HttpURLConnection urlConnection) throws IOException {
|
public HttpMultipartRequest(HttpURLConnection urlConnection) throws IOException {
|
||||||
this.urlConnection = urlConnection;
|
this.urlConnection = urlConnection;
|
||||||
|
urlConnection.setDoOutput(true);
|
||||||
|
urlConnection.setUseCaches(false);
|
||||||
urlConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
|
urlConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
|
||||||
|
|
||||||
stream = new ByteArrayOutputStream();
|
stream = new ByteArrayOutputStream();
|
||||||
@@ -31,9 +50,9 @@ public class HttpMultipartRequest implements Closeable {
|
|||||||
addLine("--" + boundary);
|
addLine("--" + boundary);
|
||||||
addLine(String.format("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"", name, filename));
|
addLine(String.format("Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"", name, filename));
|
||||||
addLine("Content-Type: " + contentType);
|
addLine("Content-Type: " + contentType);
|
||||||
addLine("Content-Transfer-Encoding: binary");
|
|
||||||
addLine("");
|
addLine("");
|
||||||
IOUtils.copyTo(inputStream, stream);
|
IOUtils.copyTo(inputStream, stream);
|
||||||
|
addLine("");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user