diff --git a/src/main/java/com/mykola2312/mptv/Main.java b/src/main/java/com/mykola2312/mptv/Main.java index e3b51e9..3ae5892 100644 --- a/src/main/java/com/mykola2312/mptv/Main.java +++ b/src/main/java/com/mykola2312/mptv/Main.java @@ -84,7 +84,6 @@ public class Main { TaskDispatcher dispatcher = new TaskDispatcher(); dispatcher.updateTaskConfig(config.tasks); dispatcher.registerTask(crawler); - dispatcher.registerTask(processService); new Thread(dispatcher).start(); @@ -95,12 +94,10 @@ public class Main { // start PiIR PiIR piir = new PiIR(config.piir); - try { - piir.spawn(); - + if (piir.spawn()) { processService.registerProcess(piir); - } catch (IOException e) { - logger.error("failed to spawn piir. fatal. exiting", e); + } else { + logger.error("failed to spawn piir. exiting"); System.exit(1); } diff --git a/src/main/java/com/mykola2312/mptv/mpv/MPV.java b/src/main/java/com/mykola2312/mptv/mpv/MPV.java index ee5172e..2ab1399 100644 --- a/src/main/java/com/mykola2312/mptv/mpv/MPV.java +++ b/src/main/java/com/mykola2312/mptv/mpv/MPV.java @@ -83,11 +83,13 @@ public class MPV implements TaskProcess { private MPVReader reader; private Thread readerThread; + private Float lastPlaybackTime = null; + private static final Path MPV_SOCKET_PATH = Path.of("/tmp/mptv-mpv.sock"); private static final long WAIT_MILLIS = 250; private static final int WAIT_ATTEMPTS = 5; - private void waitForConnection(Path socketPath) throws IOException { + private void waitForConnection(Path socketPath) throws MPVSocketFailure { for (int i = 0; i < WAIT_ATTEMPTS; i++) { try { Thread.sleep(WAIT_MILLIS); @@ -101,10 +103,12 @@ public class MPV implements TaskProcess { logger.info(String.format("connected to socket %s", socket.toString())); } catch (SocketException e) { - logger.error("SocketException", e); + logger.info("SocketException.. trying to connect"); closeConnection(); + } catch (IOException e) { + throw new MPVSocketFailure(e); } catch (InterruptedException e) { - break; + throw new MPVSocketFailure(e); } } } @@ -118,19 +122,62 @@ public class MPV implements TaskProcess { } @Override - public boolean spawn() throws IOException { - process = Runtime.getRuntime().exec(new String[] { - "mpv", "--vo=gpu", "--ao=pulse", "--fullscreen", "--input-ipc-server=" + MPV_SOCKET_PATH, url - }); + public boolean spawn() { + try { + process = Runtime.getRuntime().exec(new String[] { + "mpv", "--vo=gpu", "--ao=pulse", "--fullscreen", "--input-ipc-server=" + MPV_SOCKET_PATH, url + }); - waitForConnection(MPV_SOCKET_PATH); + waitForConnection(MPV_SOCKET_PATH); + } catch (IOException e) { + return false; + } catch (MPVSocketFailure e) { + return false; + } - return isAlive(); + return process.isAlive(); + } + + private boolean checkPlayback() { + try { + // get playback + MPVCommandResult result = executeCommand( + new MPVGetProperty(MPVProperty.PLAYBACK_TIME)); + + Float playbackTime; + try { + playbackTime = Float.parseFloat(result.data); + } catch (NumberFormatException e) { + logger.error("FAILED TO PARSE PLAYBACK DATA: " + result.data); + return false; + } + logger.info("playbackTime " + playbackTime); + + // if we have previous playback - compare them, + // if not changed, then player stuck + if (lastPlaybackTime != null) { + boolean playbackChanged = (playbackTime - lastPlaybackTime) > 0.1; + + lastPlaybackTime = playbackTime; + return playbackChanged; + } else { // just set first playback + lastPlaybackTime = playbackTime; + return true; + } + } catch (MPVCommandTimeout e) { + logger.warn("mpv ipc timeout bruh"); + return false; + } } @Override public boolean isAlive() { - return process != null ? process.isAlive() : false; + // if we have process, check if playback still going on + if (process != null) { + return checkPlayback(); + } else { + return false; + } } @Override @@ -153,7 +200,7 @@ public class MPV implements TaskProcess { private static final long COMMAND_TIMEOUT = 2000L; - public MPVCommandResult writeCommand(MPVCommand command) { + public MPVCommandResult executeCommand(MPVCommand command) { try { commandRequestId = command.setRequestId(requestIdCounter++); diff --git a/src/main/java/com/mykola2312/mptv/mpv/MPVCommandResult.java b/src/main/java/com/mykola2312/mptv/mpv/MPVCommandResult.java index 0335dbc..0971fab 100644 --- a/src/main/java/com/mykola2312/mptv/mpv/MPVCommandResult.java +++ b/src/main/java/com/mykola2312/mptv/mpv/MPVCommandResult.java @@ -1,11 +1,15 @@ package com.mykola2312.mptv.mpv; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; public class MPVCommandResult { public int request_id; public String error; + + // parse always as string to avoid headache with different types + @JsonProperty("data") public String data; public static MPVCommandResult deserialize(String data) throws JsonProcessingException { diff --git a/src/main/java/com/mykola2312/mptv/mpv/MPVGetProperty.java b/src/main/java/com/mykola2312/mptv/mpv/MPVGetProperty.java new file mode 100644 index 0000000..5401965 --- /dev/null +++ b/src/main/java/com/mykola2312/mptv/mpv/MPVGetProperty.java @@ -0,0 +1,17 @@ +package com.mykola2312.mptv.mpv; + +import java.util.Arrays; +import java.util.List; + +public class MPVGetProperty extends MPVCommand { + private final MPVProperty property; + + public MPVGetProperty(MPVProperty property) { + this.property = property; + } + + @Override + protected List serializeCommand() { + return Arrays.asList("get_property", property.toString()); + } +} diff --git a/src/main/java/com/mykola2312/mptv/mpv/MPVProperty.java b/src/main/java/com/mykola2312/mptv/mpv/MPVProperty.java index 0184bbc..773d8ee 100644 --- a/src/main/java/com/mykola2312/mptv/mpv/MPVProperty.java +++ b/src/main/java/com/mykola2312/mptv/mpv/MPVProperty.java @@ -1,7 +1,8 @@ package com.mykola2312.mptv.mpv; public enum MPVProperty { - VOLUME ("volume"); + VOLUME ("volume"), + PLAYBACK_TIME ("playback-time"); private final String name; diff --git a/src/main/java/com/mykola2312/mptv/mpv/MPVSocketFailure.java b/src/main/java/com/mykola2312/mptv/mpv/MPVSocketFailure.java new file mode 100644 index 0000000..a047d6e --- /dev/null +++ b/src/main/java/com/mykola2312/mptv/mpv/MPVSocketFailure.java @@ -0,0 +1,7 @@ +package com.mykola2312.mptv.mpv; + +public class MPVSocketFailure extends RuntimeException { + public MPVSocketFailure(Throwable cause) { + super("fatal IPC failure. cannot continue"); + } +} diff --git a/src/main/java/com/mykola2312/mptv/piir/PiIR.java b/src/main/java/com/mykola2312/mptv/piir/PiIR.java index b968843..2345acd 100644 --- a/src/main/java/com/mykola2312/mptv/piir/PiIR.java +++ b/src/main/java/com/mykola2312/mptv/piir/PiIR.java @@ -117,10 +117,14 @@ public class PiIR implements TaskProcess { } @Override - public boolean spawn() throws IOException { - process = Runtime.getRuntime().exec(new String[] { - "unbuffer", exec, "dump", "--gpio", String.valueOf(gpio) - }); + public boolean spawn() { + try { + process = Runtime.getRuntime().exec(new String[] { + "unbuffer", exec, "dump", "--gpio", String.valueOf(gpio) + }); + } catch (IOException e) { + return false; + } input = process.getInputStream(); reader = new PiIRReader(this, input); diff --git a/src/main/java/com/mykola2312/mptv/task/ProcessService.java b/src/main/java/com/mykola2312/mptv/task/ProcessService.java index d55f729..f3e7e81 100644 --- a/src/main/java/com/mykola2312/mptv/task/ProcessService.java +++ b/src/main/java/com/mykola2312/mptv/task/ProcessService.java @@ -29,6 +29,7 @@ public class ProcessService implements Task { public void dispatch() { for (TaskProcess process : processes) { if (!process.isAlive()) { + logger.info("restarting process " + process.toString()); try { process.stop(); process.spawn(); diff --git a/src/main/java/com/mykola2312/mptv/task/TaskProcess.java b/src/main/java/com/mykola2312/mptv/task/TaskProcess.java index b492c36..2cfb074 100644 --- a/src/main/java/com/mykola2312/mptv/task/TaskProcess.java +++ b/src/main/java/com/mykola2312/mptv/task/TaskProcess.java @@ -1,9 +1,7 @@ package com.mykola2312.mptv.task; -import java.io.IOException; - public interface TaskProcess { - public boolean spawn() throws IOException; + public boolean spawn(); public boolean isAlive(); public void stop(); } diff --git a/src/main/java/com/mykola2312/mptv/ui/MenuPanel.java b/src/main/java/com/mykola2312/mptv/ui/MenuPanel.java index dd13517..45635ba 100644 --- a/src/main/java/com/mykola2312/mptv/ui/MenuPanel.java +++ b/src/main/java/com/mykola2312/mptv/ui/MenuPanel.java @@ -21,7 +21,6 @@ import static com.mykola2312.mptv.tables.Channel.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; -import java.io.IOException; public class MenuPanel extends JPanel { private static final Logger logger = LoggerFactory.getLogger(MenuPanel.class); @@ -81,14 +80,12 @@ public class MenuPanel extends JPanel { } MPV newPlayer = new MPV(url); - try { - if (newPlayer.spawn()) { - player = newPlayer; + if (newPlayer.spawn()) { + player = newPlayer; - Main.processService.registerProcess(player); - } - } catch (IOException e) { - logger.error("failed to spawn player", e); + Main.processService.registerProcess(player); + } else { + logger.error("failed to spawn mpv"); } }