From c7911a1eef7cf761f3592412cfd9169180ba62a2 Mon Sep 17 00:00:00 2001 From: Jeddunk Date: Fri, 15 Nov 2019 04:16:00 +0100 Subject: [PATCH] "Get list of DLC" function now implemented List of Steam games are now pulled from another API (IStoreService) Create steamapps.json if missing (needed on first run) Improved Search function Code Improvements --- .gitignore | 1 + auto-cream-api.iml | 41 ++----- pom.xml | 46 ++++++-- src/main/java/Controller.java | 160 +++++++++++++++----------- src/main/java/SteamAppsListCache.java | 122 ++++++++++---------- src/main/java/pojo/App.java | 110 ++++++++++++++++++ src/main/java/pojo/Download.java | 58 ++++++++++ src/main/java/pojo/Rezponze.java | 60 ++++++++++ src/main/java/pojo/SteamAppsList.java | 25 ++++ src/main/resources/mainWindow.fxml | 2 +- 10 files changed, 449 insertions(+), 176 deletions(-) create mode 100644 src/main/java/pojo/App.java create mode 100644 src/main/java/pojo/Download.java create mode 100644 src/main/java/pojo/Rezponze.java create mode 100644 src/main/java/pojo/SteamAppsList.java diff --git a/.gitignore b/.gitignore index 0dc5435..a463f20 100644 --- a/.gitignore +++ b/.gitignore @@ -157,3 +157,4 @@ $RECYCLE.BIN/ cream_api.ini steamapps.json +/test.json diff --git a/auto-cream-api.iml b/auto-cream-api.iml index 81ae37b..4599ba8 100644 --- a/auto-cream-api.iml +++ b/auto-cream-api.iml @@ -18,39 +18,16 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index f5203f2..dbc27e5 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,27 @@ + + + + false + + bintray-jerady-maven + bintray + https://dl.bintray.com/jerady/maven + + + + + + false + + bintray-jerady-maven + bintray-plugins + https://dl.bintray.com/jerady/maven + + + com.jfoenix @@ -58,11 +79,6 @@ 8.0.8 compile - org.apache.commons commons-configuration2 @@ -73,11 +89,6 @@ commons-beanutils 1.9.4 - - com.ibasco.agql - agql-steam-webapi - 0.1.7 - de.jensd fontawesomefx-commons @@ -90,5 +101,20 @@ 4.7.0-5 compile + + com.google.code.gson + gson + 2.8.6 + + + org.jsoup + jsoup + 1.12.1 + + + com.konghq + unirest-java + 3.1.02 + \ No newline at end of file diff --git a/src/main/java/Controller.java b/src/main/java/Controller.java index d29c1bc..c8145f4 100644 --- a/src/main/java/Controller.java +++ b/src/main/java/Controller.java @@ -1,39 +1,60 @@ -import com.ibasco.agql.protocols.valve.steam.webapi.pojos.SteamApp; import com.jfoenix.controls.*; import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; -import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.Tooltip; import javafx.stage.FileChooser; import org.apache.commons.configuration2.ex.ConfigurationException; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import pojo.App; import java.io.File; import java.io.IOException; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; public class Controller { private CreamApiConfig config = CreamApiConfig.getInstance(); private SteamAppsListCache cache = new SteamAppsListCache(); - @FXML public JFXTextField path_textfield; - @FXML public JFXTextField appId_textfield; - @FXML public JFXTextField game_name_textfield; - @FXML public JFXComboBox language_combobox; - @FXML public JFXTextArea dlc_textarea; - @FXML public JFXCheckBox extra_protection_checkbox; - @FXML public JFXCheckBox offline_checkbox; - @FXML public JFXCheckBox unlock_all_checkbox; - @FXML public JFXButton reset_button; - @FXML public JFXButton save_button; - @FXML public JFXButton getAppId_button; - @FXML public JFXButton path_button; - @FXML public JFXButton retrieveDlcList_button; + @FXML + public JFXTextField path_textfield; + @FXML + public JFXTextField appId_textfield; + @FXML + public JFXTextField game_name_textfield; + @FXML + public JFXComboBox language_combobox; + @FXML + public JFXTextArea dlc_textarea; + @FXML + public JFXCheckBox extra_protection_checkbox; + @FXML + public JFXCheckBox offline_checkbox; + @FXML + public JFXCheckBox unlock_all_checkbox; + @FXML + public JFXButton reset_button; + @FXML + public JFXButton save_button; + @FXML + public JFXButton getAppId_button; + @FXML + public JFXButton path_button; + @FXML + public JFXButton retrieveDlcList_button; - public Controller() {} + public Controller() { + } @FXML public void initialize() { appId_textfield.textProperty().addListener(appIdChangesGameName()); - retrieveDlcList_button.setDisable(true); // WIP + //retrieveDlcList_button.setDisable(true); // WIP generate_tooltips(); fix_dlc_textarea_prompt_text(); read(); @@ -66,7 +87,7 @@ public class Controller { int appId; try { appId = Integer.parseInt(newValue); - final SteamApp game = cache.getGame(appId); + final App game = cache.getGame(appId); if (game != null) { game_name_textfield.setText(game.getName()); } else { @@ -98,72 +119,73 @@ public class Controller { } public void getAppId() { - final SteamApp game = cache.findGame(game_name_textfield.getText()); + final App game = cache.findGame(game_name_textfield.getText()); if (game == null) { appId_textfield.setText("-1"); } else { - appId_textfield.setText(String.valueOf(game.getAppid())); + appId_textfield.setText(String.valueOf(game.getAppId())); } } public void unlockAll_disableDlcTextArea() { dlc_textarea.setDisable(unlock_all_checkbox.isSelected()); + retrieveDlcList_button.setDisable(unlock_all_checkbox.isSelected()); } + /** + * Gets DLC from both the Steam Store and SteamDB, since the latter has a (weird) limit of 64(?) DLCs. SteamDB + * also lists DLC not available for purchase. + */ public void getDlcList() { - // https://github.com/Sak32009/GetDLCInfoFromSteamDB/blob/master/sak32009-get-dlc-info-from-steamdb.user.js - /*async getData() { - // CHECK IF THE APPID HAS DLCs - if (!$("#dlc").length) { - return false; + Map steamStoreDLCs = new HashMap<>(); + Map steamDbDLCs = new HashMap<>(); + //StringBuilder sb = new StringBuilder(); + try { + // Steam Store + Document steamDoc = Jsoup + .connect("https://store.steampowered.com/app/" + appId_textfield.getText() + "/") + .get(); + Elements steamDLCs = steamDoc.getElementsByClass("game_area_dlc_row"); + for (Element dlc : steamDLCs) { + String dlc_id = dlc.attr("data-ds-appid"); + String dlc_name = dlc + .getElementsByClass("game_area_dlc_name") + .text().replace("\n", "").trim(); + steamStoreDLCs.put(Integer.parseInt(dlc_id), dlc_name); } - // SELF - const self = this; - // SET APPID - this.steamDB.appID = $(".scope-app[data-appid]").data("appid"); - // SET APPID NAME - this.steamDB.appIDName = $("td[itemprop='name']").text(); - // GET APPID DLCS FROM TAB - $("tr.app[data-appid]").each((_index, _values) => { - const $this = $(_values); - const appID = $this.data("appid"); - const appIDName = $this.find(`td:nth-of-type(2)`).text().trim(); - // ADD DATA - self.steamDB.appIDDLCs[appID] = { - name: appIDName - }; - // +1 - self.steamDB.appIDDLCsCount += 1; - }); - // GET APPID DLCS FROM REQUEST - await this.getHttpRequest(`${self.info.steamDBLinked + this.steamDB.appID}`, ({ - responseText - }) => { - // APPS - const $apps = $($.parseHTML(responseText)).find("tr.app[data-appid]"); - // FETCH APPS - $apps.each((_index, _values) => { - const $this = $(_values); - const appID = $this.attr("data-appid"); - const appIDType = $this.find("td:nth-of-type(2)").text().trim(); - const appIDName = $this.find("td:nth-of-type(3)").text().trim(); - // CHECK IF EXISTS - if (!(appID in self.steamDB.appIDDLCs) && appIDType === "DLC") { - // ADD DATA - self.steamDB.appIDDLCs[appID] = { - name: appIDName - }; - // +1 - self.steamDB.appIDDLCsCount += 1; + // SteamDB + Document steamDbDoc = Jsoup + .connect("https://steamdb.info/app/" + appId_textfield.getText() + "/dlc/") + .get(); + Element steamDbDlcSection = steamDbDoc.getElementById("dlc"); + Elements steamDbDLCElements = steamDbDlcSection.getElementsByClass("app"); + for (Element dlc : steamDbDLCElements) { + String dlc_id = dlc.attr("data-appid"); + String dlc_name = "Unknown DLC " + dlc_id; + Elements td = dlc.getElementsByTag("td"); + if (!td.isEmpty()) { + dlc_name = td.get(1).text().replace("\n", "").trim(); } - }); - // RUN - self.start(); - }); - }*/ + steamDbDLCs.put(Integer.parseInt(dlc_id), dlc_name); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (NullPointerException e) { + // ignore + } + + Map allDLCs = new HashMap<>(steamStoreDLCs); + steamDbDLCs.forEach(allDLCs::putIfAbsent); + StringBuilder sb = new StringBuilder(); + LinkedHashMap collect = allDLCs.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, + (oldValue, newValue) -> oldValue, LinkedHashMap::new)); + collect.forEach((k, v) -> sb.append(k).append("=").append(v).append("\n")); + dlc_textarea.setText(sb.toString()); } - public void openFileChooser(ActionEvent event) { + public void openFileChooser() { FileChooser chooser = new FileChooser(); chooser.setTitle("Choose steam_api(64).dll..."); FileChooser.ExtensionFilter filter = diff --git a/src/main/java/SteamAppsListCache.java b/src/main/java/SteamAppsListCache.java index 81cbe48..62b702d 100644 --- a/src/main/java/SteamAppsListCache.java +++ b/src/main/java/SteamAppsListCache.java @@ -1,58 +1,66 @@ import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; -import com.ibasco.agql.protocols.valve.steam.webapi.SteamWebApiClient; -import com.ibasco.agql.protocols.valve.steam.webapi.interfaces.SteamApps; -import com.ibasco.agql.protocols.valve.steam.webapi.pojos.SteamApp; +import kong.unirest.HttpResponse; +import kong.unirest.Unirest; +import pojo.App; +import pojo.Download; +import pojo.SteamAppsList; import java.io.*; import java.lang.reflect.Type; -import java.text.MessageFormat; import java.time.Duration; import java.time.Instant; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @SuppressWarnings("WeakerAccess") public class SteamAppsListCache { - static class SteamAppsList { - Instant timestamp; - List steamAppsList; - } - private SteamAppsList list = new SteamAppsList(); private File cacheFile = new File("steamapps.json"); public SteamAppsListCache() { - getListFromFile(); - if (Instant.now().isAfter(list.timestamp.plus(Duration.ofDays(3)))) { - getListFromApi(); + boolean fileFound = true; + try { + getListFromFile(); + } catch (FileNotFoundException e) { + System.err.println("File does not exist, trying to create steamapps.json for the first time..."); + fileFound = false; + } + if (!fileFound || Instant.now().isAfter(list.getTimestamp().plus(Duration.ofDays(3)))) { + sync(); + } + } + + private void sync() { + getListFromApi(); + try { saveListToFile(); + } catch (IOException ex) { + ex.printStackTrace(); } } - public SteamApp getGame (int appId) { - for (SteamApp app : list.steamAppsList) { - if (app.getAppid() == appId) { + public App getGame(int appId) { + return list.getSteamAppsList().stream().filter(app -> app.getAppId() == appId).findFirst().orElse(null); + } + + @SuppressWarnings("unused") + public App getGame(String name) { + return list.getSteamAppsList().stream() + .filter(app -> app.getName().equalsIgnoreCase(name)).findFirst().orElse(null); + } + + public App findGame(String name) { + for (App app : list.getSteamAppsList()) { + if (app.getName().toLowerCase().replaceAll("[^a-zA-Z0-9\\s+]", "") + .startsWith(name.toLowerCase().replaceAll("[^a-zA-Z0-9\\s+]", ""))) { return app; } } - return null; - } - - public SteamApp getGame (String name) { - for (SteamApp app : list.steamAppsList) { - if (app.getName().equalsIgnoreCase(name)) { - return app; - } - } - return null; - } - - public SteamApp findGame(String name) { - for (SteamApp app : list.steamAppsList) { - if (app.getName().toLowerCase().contains(name.toLowerCase())) { + for (App app : list.getSteamAppsList()) { + if (app.getName().toLowerCase().replaceAll("[^a-zA-Z0-9\\s+]", "") + .contains(name.toLowerCase().replaceAll("[^a-zA-Z0-9\\s+]", ""))) { return app; } } @@ -60,44 +68,30 @@ public class SteamAppsListCache { } private void getListFromApi() { - SteamWebApiClient client = new SteamWebApiClient(); - SteamApps steamApps = new SteamApps(client); - /*SteamStorefront storefront = new SteamStorefront(client); - StoreAppDetails appDetails = storefront.getAppDetails(440).exceptionally(throwable -> { - System.err.println(MessageFormat.format("Error Occurred: {}", throwable)); - return new StoreAppDetails(); - }).join(); - System.out.println(appDetails); - Gson gson = new Gson(); - System.out.println(gson.toJson(appDetails));*/ - list.timestamp = Instant.now(); - list.steamAppsList = steamApps.getAppList().exceptionally(throwable -> { - System.err.println(MessageFormat.format("Error Occurred: {}", throwable)); - return new ArrayList<>(); - }).join(); + HttpResponse httpResponse = + Unirest.get("https://api.steampowered.com/IStoreService/GetAppList/v1/" + + "?key=E427256C579D3CDF1D504810E8F5B948&include_games=1&max_results=50000").asString(); + List apps = new Gson() + .fromJson(httpResponse.getBody(), Download.class) + .getResponse().getApps(); + list.setTimestamp(Instant.now()); + list.setSteamAppsList(apps); } - private void saveListToFile() { + private void saveListToFile() throws IOException { Gson gson = new Gson(); String jsonString = gson.toJson(list); - try { - BufferedWriter fOut = new BufferedWriter(new FileWriter(cacheFile)); - fOut.write(jsonString); - fOut.close(); - } catch (IOException e) { - e.printStackTrace(); - } + BufferedWriter fOut = new BufferedWriter(new FileWriter(cacheFile)); + fOut.write(jsonString); + fOut.close(); } - private void getListFromFile() { - try { - BufferedReader fIn = new BufferedReader(new FileReader(cacheFile)); - String json = fIn.lines().collect(Collectors.joining()); - final Type type = new TypeToken() {}.getType(); - Gson gson = new Gson(); - list = gson.fromJson(json, type); - } catch (IOException e) { - e.printStackTrace(); - } + private void getListFromFile() throws FileNotFoundException { + BufferedReader fIn = new BufferedReader(new FileReader(cacheFile)); + String json = fIn.lines().collect(Collectors.joining()); + final Type type = new TypeToken() { + }.getType(); + Gson gson = new Gson(); + list = gson.fromJson(json, type); } } diff --git a/src/main/java/pojo/App.java b/src/main/java/pojo/App.java new file mode 100644 index 0000000..3130f12 --- /dev/null +++ b/src/main/java/pojo/App.java @@ -0,0 +1,110 @@ +package pojo; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +import java.util.Objects; + +@SuppressWarnings("unused") +public class App { + + @SerializedName("appid") + @Expose + private Integer appId; + @SerializedName("name") + @Expose + private String name; + @SerializedName("last_modified") + @Expose + private Integer lastModified; + @SerializedName("price_change_number") + @Expose + private Integer priceChangeNumber; + + public Integer getAppId() { + return appId; + } + + public void setAppId(Integer appId) { + this.appId = appId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getLastModified() { + return lastModified; + } + + public void setLastModified(Integer lastModified) { + this.lastModified = lastModified; + } + + public Integer getPriceChangeNumber() { + return priceChangeNumber; + } + + public void setPriceChangeNumber(Integer priceChangeNumber) { + this.priceChangeNumber = priceChangeNumber; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(App.class.getName()).append('@').append(Integer.toHexString(System.identityHashCode(this))).append('['); + sb.append("appid"); + sb.append('='); + sb.append(((this.appId == null)?"":this.appId)); + sb.append(','); + sb.append("name"); + sb.append('='); + sb.append(((this.name == null)?"":this.name)); + sb.append(','); + sb.append("lastModified"); + sb.append('='); + sb.append(((this.lastModified == null)?"":this.lastModified)); + sb.append(','); + sb.append("priceChangeNumber"); + sb.append('='); + sb.append(((this.priceChangeNumber == null)?"":this.priceChangeNumber)); + sb.append(','); + if (sb.charAt((sb.length()- 1)) == ',') { + sb.setCharAt((sb.length()- 1), ']'); + } else { + sb.append(']'); + } + return sb.toString(); + } + + @Override + public int hashCode() { + int result = 1; + result = ((result* 31)+((this.name == null)? 0 :this.name.hashCode())); + result = ((result* 31)+((this.lastModified == null)? 0 :this.lastModified.hashCode())); + result = ((result* 31)+((this.priceChangeNumber == null)? 0 :this.priceChangeNumber.hashCode())); + result = ((result* 31)+((this.appId == null)? 0 :this.appId.hashCode())); + return result; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof App)) { + return false; + } + App rhs = ((App) other); + boolean b1 = Objects.equals(this.name, rhs.name); + boolean b2 = Objects.equals(this.lastModified, rhs.lastModified); + boolean b3 = Objects.equals(this.priceChangeNumber, rhs.priceChangeNumber); + boolean b4 = Objects.equals(this.appId, rhs.appId); + return b1 && b2 && b3 && b4; + } + +} diff --git a/src/main/java/pojo/Download.java b/src/main/java/pojo/Download.java new file mode 100644 index 0000000..384ef0d --- /dev/null +++ b/src/main/java/pojo/Download.java @@ -0,0 +1,58 @@ +package pojo; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +import java.util.Objects; + +public class Download { + + @SerializedName("response") + @Expose + private Rezponze response; + + public Rezponze getResponse() { + return response; + } + + public void setResponse(Rezponze response) { + this.response = response; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(Download.class.getName()).append('@').append(Integer.toHexString(System.identityHashCode(this))).append('['); + sb.append("response"); + sb.append('='); + sb.append(((this.response == null)?"":this.response)); + sb.append(','); + if (sb.charAt((sb.length()- 1)) == ',') { + sb.setCharAt((sb.length()- 1), ']'); + } else { + sb.append(']'); + } + return sb.toString(); + } + + @Override + public int hashCode() { + int result = 1; + result = ((result* 31)+((this.response == null)? 0 :this.response.hashCode())); + return result; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof Download)) { + return false; + } + Download rhs = ((Download) other); + return Objects.equals(this.response, rhs.response); + //return ((this.response == rhs.response)||((this.response!= null)&&this.response.equals(rhs.response))); + } + +} diff --git a/src/main/java/pojo/Rezponze.java b/src/main/java/pojo/Rezponze.java new file mode 100644 index 0000000..d2263dd --- /dev/null +++ b/src/main/java/pojo/Rezponze.java @@ -0,0 +1,60 @@ +package pojo; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +public class Rezponze { + + @SerializedName("apps") + @Expose + private List apps = new ArrayList<>(); + + public List getApps() { + return apps; + } + + public void setApps(List apps) { + this.apps = apps; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(Rezponze.class.getName()).append('@').append(Integer.toHexString(System.identityHashCode(this))).append('['); + sb.append("apps"); + sb.append('='); + sb.append(((this.apps == null)?"":this.apps)); + sb.append(','); + if (sb.charAt((sb.length()- 1)) == ',') { + sb.setCharAt((sb.length()- 1), ']'); + } else { + sb.append(']'); + } + return sb.toString(); + } + + @Override + public int hashCode() { + int result = 1; + result = ((result* 31)+((this.apps == null)? 0 :this.apps.hashCode())); + return result; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof Rezponze)) { + return false; + } + Rezponze rhs = ((Rezponze) other); + return Objects.equals(this.apps, rhs.apps); + //return ((this.apps == rhs.apps)||((this.apps!= null)&&this.apps.equals(rhs.apps))); + } + +} diff --git a/src/main/java/pojo/SteamAppsList.java b/src/main/java/pojo/SteamAppsList.java new file mode 100644 index 0000000..24cca4a --- /dev/null +++ b/src/main/java/pojo/SteamAppsList.java @@ -0,0 +1,25 @@ +package pojo; + +import java.time.Instant; +import java.util.List; + +public class SteamAppsList { + private Instant timestamp; + private List steamAppsList; + + public Instant getTimestamp() { + return timestamp; + } + + public void setTimestamp(Instant timestamp) { + this.timestamp = timestamp; + } + + public List getSteamAppsList() { + return steamAppsList; + } + + public void setSteamAppsList(List steamAppsList) { + this.steamAppsList = steamAppsList; + } +} diff --git a/src/main/resources/mainWindow.fxml b/src/main/resources/mainWindow.fxml index fbb9b04..92cc63f 100644 --- a/src/main/resources/mainWindow.fxml +++ b/src/main/resources/mainWindow.fxml @@ -48,7 +48,7 @@ - +