diff --git a/pom.xml b/pom.xml index 9bc67e5..c9a948e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.alttprleague restreamtool - 3.1 + 3.3 diff --git a/src/main/java/com/alttprleague/LeagueRestreamTool.java b/src/main/java/com/alttprleague/LeagueRestreamTool.java index b5a66c4..adaf1a7 100644 --- a/src/main/java/com/alttprleague/LeagueRestreamTool.java +++ b/src/main/java/com/alttprleague/LeagueRestreamTool.java @@ -71,7 +71,7 @@ public class LeagueRestreamTool { public static void main(String[] args) throws IOException { var restreamTool = new LeagueRestreamTool(); - window = new JFrame("League Restream Tool V3.1"); + window = new JFrame("League Restream Tool V3.3"); URL iconURL = LeagueRestreamTool.class.getResource("/LeagueLogo.png"); window.setIconImage(ImageIO.read(iconURL)); window.setContentPane(restreamTool.pMain); @@ -248,6 +248,10 @@ private Process startLink(String streamFrom, int port, Console console, boolean } private void fetchChannel(String channelName) { + fetchChannel(channelName, true); + } + + private void fetchChannel(String channelName, boolean updateStreams) { try { var request = HttpRequest.newBuilder() .uri(URI.create("https://alttprleague.com/api/restream/?channel="+ channelName)) @@ -256,7 +260,7 @@ private void fetchChannel(String channelName) { httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()) .thenApply(HttpResponse::body) - .thenAccept(this::ProcessWebsiteResponse); + .thenAccept((r) -> ProcessWebsiteResponse(r, updateStreams)); } catch (Exception e) { consoleTopLeft.appendError("Error while fetching restream data from League website."); @@ -264,20 +268,34 @@ private void fetchChannel(String channelName) { } } - private void ProcessWebsiteResponse(String json) { - try { + private void ProcessWebsiteResponse(String json, boolean updateStreams) { var gson = new Gson(); - var channel = gson.fromJson(json, Channel.class); - if(!channel.error.isBlank()) { + channel = gson.fromJson(json, Channel.class); + if (!channel.error.isBlank()) { consoleTopLeft.appendError(channel.error); return; } - if(channel.episode == null) { - consoleTopLeft.appendError("No episode loaded on Restreamer Dashboard for "+channel.twitch_name+"."); + if (channel.episode == null) { + consoleTopLeft.appendError("No episode loaded on Restreamer Dashboard for " + channel.twitch_name + "."); return; } - this.channel = channel; + updateLayouts(); + if(updateStreams) + updateStreams(); + } + + private void updateStreams() { + startStreamLink(Screen.TOP_LEFT,channel.episode.players[0].streaming_from); + startStreamLink(Screen.TOP_RIGHT,channel.episode.players[1].streaming_from); + if(channel.episode.players.length == 4) { + startStreamLink(Screen.BOTTOM_LEFT,channel.episode.players[2].streaming_from); + startStreamLink(Screen.BOTTOM_RIGHT,channel.episode.players[3].streaming_from); + } + } + + private void updateLayouts() { + btnStreamKey.setEnabled(true); obsRelay.setStreamKey(channel.stream_key); @@ -305,7 +323,6 @@ private void ProcessWebsiteResponse(String json) { } // LEFT TEAM PLAYER 1 - startStreamLink(Screen.TOP_LEFT,channel.episode.players[0].streaming_from); preRaceTemplate.getElementById("p1_logo").attr("src",channel.episode.players[0].logo_url); //preRaceTemplate.getElementById("p1_sprite").attr("src",channel.episode.players[0].sprite_url); preRaceTemplate.getElementById("p1_name").text(channel.episode.players[0].name); @@ -316,7 +333,6 @@ private void ProcessWebsiteResponse(String json) { // LEFT TEAM PLAYER 2 if(channel.episode.players.length >= 3) { - startStreamLink(Screen.BOTTOM_LEFT,channel.episode.players[2].streaming_from); preRaceTemplate.getElementById("p3_logo").attr("src",channel.episode.players[2].logo_url); //preRaceTemplate.getElementById("p3_sprite").attr("src",channel.episode.players[2].sprite_url); preRaceTemplate.getElementById("p3_name").text(channel.episode.players[2].name); @@ -339,7 +355,6 @@ private void ProcessWebsiteResponse(String json) { // RIGHT TEAM PLAYER 1 - startStreamLink(Screen.TOP_RIGHT,channel.episode.players[1].streaming_from); preRaceTemplate.getElementById("p2_logo").attr("src",channel.episode.players[1].logo_url); //preRaceTemplate.getElementById("p2_sprite").attr("src",channel.episode.players[1].sprite_url); preRaceTemplate.getElementById("p2_name").text(channel.episode.players[1].name); @@ -349,7 +364,6 @@ private void ProcessWebsiteResponse(String json) { // RIGHT TEAM PLAYER 2 if(channel.episode.players.length >= 4) { - startStreamLink(Screen.BOTTOM_RIGHT,channel.episode.players[3].streaming_from); preRaceTemplate.getElementById("p4_logo").attr("src",channel.episode.players[3].logo_url); //preRaceTemplate.getElementById("p4_sprite").attr("src",channel.episode.players[3].sprite_url); preRaceTemplate.getElementById("p4_name").text(channel.episode.players[3].name); @@ -362,9 +376,12 @@ private void ProcessWebsiteResponse(String json) { preRaceTemplate.getElementById("stage").text(channel.episode.stage); preRaceTemplate.getElementById("mode").text(channel.episode.mode); preRaceTemplate.getElementById("open").text(channel.episode.season.open ? "Open":"Invitational"); + if(channel.episode.is_playoff && (channel.episode.background.toLowerCase().contains("power") || channel.episode.background.toLowerCase().contains("game4"))) { raceTemplate.getElementById("mode").removeClass("hidden"); raceTemplate.getElementById("mode").text(channel.episode.mode); + } else { + raceTemplate.getElementById("mode").remove(); } @@ -408,6 +425,9 @@ private void ProcessWebsiteResponse(String json) { cardContainer.appendChild(scheduleCard); for (Division division : channel.divisions) { + if(division.teams == null){ + division.teams = new Team[0]; + } ArrayList teams = new ArrayList<>(Arrays.asList(division.teams)); teams.sort(Comparator.comparingInt(team -> team.points)); Collections.reverse(teams); @@ -429,6 +449,7 @@ private void ProcessWebsiteResponse(String json) { cardContainer.appendChild(standingsCard); } + try { cardContainer.child(0).addClass("active"); FileWriter raceOut = new FileWriter(new File(baseDir,"RaceLayout.html")); @@ -453,6 +474,7 @@ private void ProcessWebsiteResponse(String json) { playlist.close(); + obsRelay.reloadLayout(); } catch (IOException e) { e.printStackTrace(); consoleTopLeft.appendError("Error Writing Files"); @@ -482,6 +504,7 @@ private void fetchRaceTimeData(String roomURL) { FileWriter timeOut = new FileWriter(new File(baseDir, "PreRaceLayout.html")); timeOut.write(doc.outerHtml()); timeOut.close(); + obsRelay.reloadLayout(); } catch (IOException e) { consoleTopLeft.appendError("Error updating layout with race start time."); } @@ -599,6 +622,10 @@ private void miOBSSettings(ActionEvent e) { panel.setVisible(true); } + private void ReloadStaff(ActionEvent e) { + fetchChannel(channel.twitch_name, false); + } + private enum Screen { TOP_LEFT,TOP_RIGHT,BOTTOM_LEFT,BOTTOM_RIGHT } @@ -632,6 +659,7 @@ private void initComponents() { obsStatus = new StatusLight(); btnOBSConnect = new JButton(); btnOBSSaveCrop = new JButton(); + btnReloadStaff = new JButton(); //======== pMain ======== { @@ -777,7 +805,8 @@ private void initComponents() { "insets 0,hidemode 3", // columns "[fill]" + - "[grow,fill]", + "[532,grow,fill]" + + "[right]", // rows "[]")); @@ -801,6 +830,11 @@ public void mouseClicked(MouseEvent e) { btnOBSSaveCrop.setEnabled(false); btnOBSSaveCrop.addActionListener(e -> btnOBSSaveCrop(e)); pUtil.add(btnOBSSaveCrop, "cell 0 0"); + + //---- btnReloadStaff ---- + btnReloadStaff.setText("Reload Staff"); + btnReloadStaff.addActionListener(e -> ReloadStaff(e)); + pUtil.add(btnReloadStaff, "cell 2 0"); } pBody.add(pUtil, "cell 0 2 6 1"); } @@ -837,5 +871,6 @@ public void mouseClicked(MouseEvent e) { private StatusLight obsStatus; private JButton btnOBSConnect; private JButton btnOBSSaveCrop; + private JButton btnReloadStaff; // JFormDesigner - End of variables declaration //GEN-END:variables @formatter:on } diff --git a/src/main/java/com/alttprleague/LeagueRestreamTool.jfd b/src/main/java/com/alttprleague/LeagueRestreamTool.jfd index 79d6a48..3918091 100644 --- a/src/main/java/com/alttprleague/LeagueRestreamTool.jfd +++ b/src/main/java/com/alttprleague/LeagueRestreamTool.jfd @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "8.0.0.0.194" Java: "17.0.4.1" encoding: "UTF-8" +JFDML JFormDesigner: "8.1.1.0.298" Java: "17.0.8" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -141,7 +141,7 @@ new FormModel { } ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "insets 0,hidemode 3" - "$columnConstraints": "[fill][grow,fill]" + "$columnConstraints": "[fill][532,grow,fill][right]" "$rowConstraints": "[]" } ) { name: "pUtil" @@ -167,6 +167,13 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 0 0" } ) + add( new FormComponent( "javax.swing.JButton" ) { + name: "btnReloadStaff" + "text": "Reload Staff" + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "ReloadStaff", true ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 0" + } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 0 2 6 1" } ) diff --git a/src/main/java/com/alttprleague/OBSClient.kt b/src/main/java/com/alttprleague/OBSClient.kt index 0a60676..2cdfd83 100644 --- a/src/main/java/com/alttprleague/OBSClient.kt +++ b/src/main/java/com/alttprleague/OBSClient.kt @@ -7,18 +7,21 @@ import io.obswebsocket.community.client.OBSRemoteController import io.obswebsocket.community.client.WebSocketCloseCode import io.obswebsocket.community.client.listener.lifecycle.ReasonThrowable import io.obswebsocket.community.client.message.event.sceneitems.SceneItemTransformChangedEvent -import io.obswebsocket.community.client.message.request.Request -import io.obswebsocket.community.client.message.request.Request.Data +import io.obswebsocket.community.client.message.request.inputs.PressInputPropertiesButtonRequest +import io.obswebsocket.community.client.message.request.ui.GetStudioModeEnabledRequest +import io.obswebsocket.community.client.message.response.RequestResponse import io.obswebsocket.community.client.message.response.config.SetStreamServiceSettingsResponse +import io.obswebsocket.community.client.message.response.inputs.PressInputPropertiesButtonResponse import io.obswebsocket.community.client.message.response.sceneitems.GetGroupSceneItemListResponse import io.obswebsocket.community.client.message.response.sceneitems.GetSceneItemListResponse import io.obswebsocket.community.client.message.response.sceneitems.GetSceneItemTransformResponse import io.obswebsocket.community.client.message.response.sceneitems.SetSceneItemTransformResponse import io.obswebsocket.community.client.message.response.scenes.GetGroupListResponse import io.obswebsocket.community.client.message.response.scenes.GetSceneListResponse +import io.obswebsocket.community.client.message.response.ui.GetStudioModeEnabledResponse import org.slf4j.LoggerFactory import java.util.* -import kotlin.collections.HashMap + class OBSClient( private val obsStatus: StatusLight, @@ -205,6 +208,7 @@ class OBSClient( } private fun pushCrop(containers: HashMap, top: Int, left: Int, right: Int, bottom: Int) { + if (!isConnected) return for (container in containers.keys) { val sourceID = containers[container]!! obsRemoteController.getSceneItemTransform(container, sourceID) { tResponse: GetSceneItemTransformResponse -> @@ -234,7 +238,7 @@ class OBSClient( fun setStreamKey(key: String?) { if (!isConnected) return - val settings = JsonObject() + val settings: JsonObject = JsonObject() settings.addProperty("bwtest", false) settings.addProperty("key", key) settings.addProperty("server", "auto") @@ -249,6 +253,13 @@ class OBSClient( } } + fun reloadLayout() { + if (!isConnected) return + obsRemoteController.sendRequest(PressInputPropertiesButtonRequest.builder().inputName("PreRaceLayout").propertyName("refreshnocache").build()) { _: PressInputPropertiesButtonResponse -> return@sendRequest } + obsRemoteController.sendRequest(PressInputPropertiesButtonRequest.builder().inputName("WebLayout").propertyName("refreshnocache").build()) { _: PressInputPropertiesButtonResponse -> return@sendRequest } + obsRemoteController.sendRequest(PressInputPropertiesButtonRequest.builder().inputName("PostRaceLayout").propertyName("refreshnocache").build()) { _: PressInputPropertiesButtonResponse -> return@sendRequest } + } + companion object { private val log = LoggerFactory.getLogger(OBSClient::class.java.name) } diff --git a/src/main/resources/html/FourPlayer.html b/src/main/resources/html/FourPlayer.html index 3400642..3605991 100644 --- a/src/main/resources/html/FourPlayer.html +++ b/src/main/resources/html/FourPlayer.html @@ -27,6 +27,7 @@ color: white; font-family: "Roboto", sans-serif; font-weight: 700; + font-size: 2.5rem; } iframe { @@ -87,7 +88,7 @@ } .hidden { - display: none; + visibility: hidden; } #layout { @@ -156,6 +157,8 @@ height: 49px; width: 477px; z-index: 1; + overflow: hidden; + text-align: center; } .player_name { @@ -163,6 +166,8 @@ height: 49px; width: 249px; z-index: 1; + overflow: hidden; + text-align: center; } #t1_name { @@ -283,7 +288,7 @@ src="https://images.alttprleague.com/static/league/img/player-default.png" alt="logo" /> -
Team 1
+
Team 1
Player 1
Player 3