diff --git a/pom.xml b/pom.xml index b59426b..206b893 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ mplayer4anime mplayer4anime - 0.14.1-SNAPSHOT + 0.15-SNAPSHOT jar @@ -16,7 +16,7 @@ mplayer front end to play content pairs that are mostly used for anime (mka+mkv, mp4+ac3, mkv+srt) - 2018-2019 + 2018 Dmitry Isaenko https://developersu.blogspot.com/search/label/mplayer4anime diff --git a/src/main/java/mplayer4anime/AppPreferences.java b/src/main/java/mplayer4anime/AppPreferences.java index 3114110..47d54db 100644 --- a/src/main/java/mplayer4anime/AppPreferences.java +++ b/src/main/java/mplayer4anime/AppPreferences.java @@ -1,14 +1,34 @@ +/* + Copyright 2018-2021 Dmitry Isaenko + + This file is part of mplayer4anime. + + mplayer4anime 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. + + mplayer4anime 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 mplayer4anime. If not, see . + */ package mplayer4anime; import java.util.prefs.Preferences; // Rule application settings public class AppPreferences { + private static AppPreferences INSTANCE = new AppPreferences(); + private Preferences preferences = Preferences.userRoot().node("mplayer4anime"); - private Preferences preferences; + private AppPreferences(){} - public AppPreferences(){ - preferences = Preferences.userRoot().node("mplayer4anime"); + public static AppPreferences getINSTANCE() { + return INSTANCE; } public void setPath(String path){ @@ -96,5 +116,10 @@ public class AppPreferences { preferences.put("RECENT_PLS_" + i, ""); } } + // Window size + public double getSceneWidth(){ return preferences.getDouble("window_width", 1200.0); } + public void setSceneWidth(double value){ preferences.putDouble("window_width", value); } + public double getSceneHeight(){ return preferences.getDouble("window_height", 800.0); } + public void setSceneHeight(double value){ preferences.putDouble("window_height", value); } } diff --git a/src/main/java/mplayer4anime/Controller.java b/src/main/java/mplayer4anime/Controller.java index 49a81ef..eaec6ba 100644 --- a/src/main/java/mplayer4anime/Controller.java +++ b/src/main/java/mplayer4anime/Controller.java @@ -1,9 +1,26 @@ +/* + Copyright 2018-2021 Dmitry Isaenko + + This file is part of mplayer4anime. + + mplayer4anime 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. + + mplayer4anime 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 mplayer4anime. If not, see . + */ package mplayer4anime; import javafx.application.HostServices; import javafx.application.Platform; -import javafx.event.ActionEvent; -import javafx.event.EventHandler; +import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; @@ -18,38 +35,33 @@ import mplayer4anime.appPanes.ControllerSUB; import java.io.*; import java.net.URL; -import java.util.ListIterator; import java.util.ResourceBundle; public class Controller implements Initializable { @FXML - private ControllerPane mkvPaneController; - @FXML - private ControllerPane mkaPaneController; + private ControllerPane mkvPaneController, mkaPaneController; @FXML private ControllerSUB subPaneController; @FXML private Label statusLbl; @FXML private Menu recentlyOpenedMenu; - // Get preferences - private AppPreferences appPreferences = new AppPreferences(); - - private ResourceBundle resourceBundle; - @FXML private CheckMenuItem fullScreen; - - // Get host services for opening URLs etc. - private HostServices hostServices; - @FXML private TabPane tabPane; - @FXML private CheckMenuItem subsHide; - private String currentPlaylistLocation = null; //TODO: move to the constructor? + private final AppPreferences appPreferences = AppPreferences.getINSTANCE(); + + private ResourceBundle resourceBundle; + // Get host services for opening URLs etc. + private HostServices hostServices; + + private String currentPlaylistLocation; + + private MplayerSlave mplayer; // If application started with playlist passed as an argument, then we'll try to load it (if it's valid). public void setPlaylistAsArgument(String playlist) { @@ -79,150 +91,19 @@ public class Controller implements Initializable { subsHide.setSelected(appPreferences.getSubtitlesHideSelected()); String[] recentPlaylists = appPreferences.getRecentPlaylists(); - for (int i = recentPlaylists.length-1; i >= 0; i--) - if (!recentPlaylists[i].isEmpty()) - addRecentlyOpened(recentPlaylists[i]); + for (int i = recentPlaylists.length-1; i >= 0; i--) { + if (recentPlaylists[i].isEmpty()) + continue; + addRecentlyOpened(recentPlaylists[i]); + } + + mplayer = new MplayerSlave(resourceBundle); } void setHostServices(HostServices hostServices) { this.hostServices = hostServices; } - /* PLAYER COMMANDS */ - private boolean playerSingleCommand(String command){ - if (player != null && player.isAlive()) { - playerIn.print(command); - playerIn.print("\n"); - playerIn.flush(); - return true; - } else { return false; } - } - - @FXML - private void subsTriggerBtn(){ - if (playerSingleCommand("get_sub_visibility")) { - String returnedStr; - int returnedInt = 1; - try { - while ((returnedStr = playerOutErr.readLine()) != null) { - //System.out.println(returnedStr); - if (returnedStr.startsWith("ANS_SUB_VISIBILITY=")) { - returnedInt = Integer.parseInt(returnedStr.substring("ANS_SUB_VISIBILITY=".length())); - break; - } - } - } catch (IOException e) { - System.out.println("Can't determine whether subtitles enabled or disabled"); - } - - if (returnedInt == 1) - playerSingleCommand("sub_visibility 0"); - else - playerSingleCommand("sub_visibility 1"); - } - } - @FXML - private void fullscreenBtn(){ playerSingleCommand("vo_fullscreen"); } - @FXML - private void muteBtn(){ playerSingleCommand("mute"); } - @FXML - private void playPrevTrackBtn(){ - if (player != null && player.isAlive()) { - playerSingleCommand("quit"); - while (player.isAlive()); // TODO: remove crutch, implement bike - } - int index; - index = mkvPaneController.getElementSelectedIndex(); - if (index > 0) { - mkvPaneController.setElementSelectedByIndex(index-1); // .selectNext / .selectPrevious - playBtn(); - } - } - @FXML - private void playNextTrackBtn(){ - if (player != null && player.isAlive()) { - playerSingleCommand("quit"); - while (player.isAlive()); // TODO: remove crutch, implement bike - } - int index; - index = mkvPaneController.getElementSelectedIndex(); - // TODO: add 'link' button - if (index+1 < mkvPaneController.getElementsCount() ) { - mkvPaneController.setElementSelectedByIndex(index+1); - } - index = mkaPaneController.getElementSelectedIndex(); - if (index+1 < mkaPaneController.getElementsCount() ) { - mkaPaneController.setElementSelectedByIndex(index+1); - } - index = subPaneController.getElementSelectedIndex(); - if (index+1 < subPaneController.getElementsCount() ) { - subPaneController.setElementSelectedByIndex(index+1); - } - playBtn(); - } - - private Process player; - private PrintStream playerIn; - private BufferedReader playerOutErr; - - @FXML - private void playBtn(){ - if (mkvPaneController.getElementSelected() != null) { - boolean Audio = !mkaPaneController.isElementsListEmpty() && mkvPaneController.getElementSelectedIndex() < mkaPaneController.getElementsCount(); - boolean Subtitles = !subPaneController.isElementsListEmpty() && mkvPaneController.getElementSelectedIndex() < subPaneController.getElementsCount(); - boolean SubEncodingDefault = subPaneController.getSelectedEncoding().equals("default"); - - try { - if (player == null || !player.isAlive()) { - player = new ProcessBuilder( // FUCKING MAGIC! DON'T CHANGE SEQUENCE - appPreferences.getPath(), // It's a chance for Windows ;) - "-slave", - Audio?"-audiofile":"", - Audio? mkaPaneController.getElementSelected():"", - "-quiet", - fullScreen.isSelected() ? "-fs" : "", - mkvPaneController.getElementSelected(), - subsHide.isSelected()||Subtitles?"-nosub":"", // Turn off subtitles embedded into MKV file (and replace by localy-stored subs file if needed) - Subtitles?"-sub":"", - Subtitles? subPaneController.getElementSelected():"", - Subtitles?SubEncodingDefault?"":"-subcp":"", // Use subtitles -> YES -> Check if we need encoding - Subtitles?SubEncodingDefault?"": subPaneController.getSelectedEncoding():"" - ).start(); - - PipedInputStream readFrom = new PipedInputStream(256 * 1024); - PipedOutputStream writeTo = new PipedOutputStream(readFrom); - - playerOutErr = new BufferedReader(new InputStreamReader(readFrom)); - - new LineRedirecter(player.getInputStream(), writeTo).start(); - new LineRedirecter(player.getErrorStream(), writeTo).start(); - - playerIn = new PrintStream(player.getOutputStream()); - - /* If user desired to disable subtitles but populated the list in the SUB pane, then load them and disable visibility. - * It's should be done this way because if we won't pass them to mplayer during start them user won't be able to enable them later on. - * There is another bike could be implemented such as passing input file during load but it's far more dumb idea then current implementation. - */ - if (subsHide.isSelected()) - playerSingleCommand("sub_visibility 0"); - } else { - playerIn.print("pause"); - playerIn.print("\n"); - playerIn.flush(); - } - } catch (IOException e) { - ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorUnableToStartMplayer")); - } - } else { System.out.println("File not selected"); } - } - - @FXML - private void stopBtn(){playerSingleCommand("stop"); } - @FXML - private void volumeUpBtn(){ playerSingleCommand("volume +1 0"); } - @FXML - private void volumeDownBtn(){ playerSingleCommand("volume -1 0"); } - @FXML private void closeBtn() { Stage currentStage = (Stage) tabPane.getScene().getWindow(); @@ -312,21 +193,21 @@ public class Controller implements Initializable { } private void addRecentlyOpened(String playlistPath){ - ListIterator iteratorItem = recentlyOpenedMenu.getItems().listIterator(); - while (iteratorItem.hasNext()) { - MenuItem mi = iteratorItem.next(); - if (mi.getUserData() != null && mi.getUserData().equals(playlistPath)) { - recentlyOpenedMenu.getItems().remove(mi); - recentlyOpenedMenu.getItems().add(0, mi); + ObservableList items = recentlyOpenedMenu.getItems(); + for (MenuItem item : items) { + if (item.getUserData() != null && item.getUserData().equals(playlistPath)) { + items.remove(item); + items.add(0, item); return; } } MenuItem menuItem = new MenuItem(); - String fileNameOnly; + String playListName; - fileNameOnly = playlistPath.substring(playlistPath.lastIndexOf(File.separator) + 1); - menuItem.setText(fileNameOnly); + playListName = playlistPath.substring( + playlistPath.lastIndexOf(File.separator) + 1, playlistPath.lastIndexOf(".")); + menuItem.setText(playListName); menuItem.setUserData(playlistPath); menuItem.setOnAction(actionEvent -> { @@ -334,9 +215,90 @@ public class Controller implements Initializable { setAllLists(jsonStorage); }); // Limit list to 13 elements (2 in the end are separator and clear button) - if (recentlyOpenedMenu.getItems().size() >= 11) - recentlyOpenedMenu.getItems().remove(9, recentlyOpenedMenu.getItems().size() - 2); - recentlyOpenedMenu.getItems().add(0, menuItem); + if (items.size() >= 11) + items.remove(9, recentlyOpenedMenu.getItems().size() - 2); + items.add(0, menuItem); + } + + /* PLAYER */ + @FXML + private void subsTriggerBtn(){ + mplayer.subtitlesSwitch(); + } + @FXML + private void fullscreenBtn(){ + mplayer.fullscreenSwitch(); + } + @FXML + private void muteBtn(){ + mplayer.mute(); + } + @FXML + private void playPrevTrackBtn(){ + int index = mkvPaneController.getElementSelectedIndex(); + if (index <= 0) + return; + + mkvPaneController.setElementSelectedByIndex(index-1); + mplayer.forcePlay(appPreferences.getPath(), + mkvPaneController.getElementSelected(), + mkaPaneController.getElementSelected(), + subPaneController.getElementSelected(), + subPaneController.getSelectedEncoding(), + subsHide.isSelected(), + fullScreen.isSelected() + ); + } + @FXML + private void playNextTrackBtn(){ + int index = mkvPaneController.getElementSelectedIndex(); + + if (index + 1 < mkvPaneController.getElementsCount()) { + mkvPaneController.setElementSelectedByIndex(index + 1); + } + index = mkaPaneController.getElementSelectedIndex(); + if (index + 1 < mkaPaneController.getElementsCount()) { + mkaPaneController.setElementSelectedByIndex(index + 1); + } + index = subPaneController.getElementSelectedIndex(); + if (index + 1 < subPaneController.getElementsCount()) { + subPaneController.setElementSelectedByIndex(index + 1); + } + + mplayer.forcePlay(appPreferences.getPath(), + mkvPaneController.getElementSelected(), + mkaPaneController.getElementSelected(), + subPaneController.getElementSelected(), + subPaneController.getSelectedEncoding(), + subsHide.isSelected(), + fullScreen.isSelected() + ); + } + @FXML + private void playBtn(){ + if (mkvPaneController.getElementSelected() == null) + return; + + mplayer.playPause(appPreferences.getPath(), + mkvPaneController.getElementSelected(), + mkaPaneController.getElementSelected(), + subPaneController.getElementSelected(), + subPaneController.getSelectedEncoding(), + subsHide.isSelected(), + fullScreen.isSelected() + ); + } + @FXML + private void stopBtn(){ + mplayer.stop(); + } + @FXML + private void volumeUpBtn(){ + mplayer.volumeUp(); + } + @FXML + private void volumeDownBtn(){ + mplayer.volumeDown(); } } diff --git a/src/main/java/mplayer4anime/ISlaveModeAppOrchestration.java b/src/main/java/mplayer4anime/ISlaveModeAppOrchestration.java new file mode 100644 index 0000000..463e840 --- /dev/null +++ b/src/main/java/mplayer4anime/ISlaveModeAppOrchestration.java @@ -0,0 +1,23 @@ +/* + Copyright 2018-2021 Dmitry Isaenko + + This file is part of mplayer4anime. + + mplayer4anime 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. + + mplayer4anime 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 mplayer4anime. If not, see . + */ +package mplayer4anime; + +public interface ISlaveModeAppOrchestration { + // TODO: implement unified interface for mplayer and mpv +} diff --git a/src/main/java/mplayer4anime/LineRedirecter.java b/src/main/java/mplayer4anime/LineRedirecter.java index 30879db..4867d69 100644 --- a/src/main/java/mplayer4anime/LineRedirecter.java +++ b/src/main/java/mplayer4anime/LineRedirecter.java @@ -1,27 +1,44 @@ +/* + Copyright 2018-2021 Dmitry Isaenko + + This file is part of mplayer4anime. + + mplayer4anime 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. + + mplayer4anime 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 mplayer4anime. If not, see . + */ package mplayer4anime; import java.io.*; public class LineRedirecter extends Thread { - private InputStream inStr; - private OutputStream outStr; + private final InputStream inStream; + private final OutputStream outStream; - LineRedirecter(InputStream in, OutputStream out){ - this.inStr = in; - this.outStr = out; + LineRedirecter(InputStream inStream, OutputStream outStream){ + this.inStream = inStream; + this.outStream = outStream; } public void run(){ try { - BufferedReader bufferReader = new BufferedReader(new InputStreamReader(inStr)); - PrintStream printStream = new PrintStream(outStr); + BufferedReader bufferReader = new BufferedReader(new InputStreamReader(inStream)); + PrintStream printStream = new PrintStream(outStream); String playerOutput; - while ((playerOutput = bufferReader.readLine()) != null) { printStream.println(playerOutput); } - - }catch (IOException e){ + } + catch (IOException e){ e.printStackTrace(); } diff --git a/src/main/java/mplayer4anime/MainFX.java b/src/main/java/mplayer4anime/MainFX.java index 409722f..a386294 100644 --- a/src/main/java/mplayer4anime/MainFX.java +++ b/src/main/java/mplayer4anime/MainFX.java @@ -1,13 +1,22 @@ +/* + Copyright 2018-2021 Dmitry Isaenko + + This file is part of mplayer4anime. + + mplayer4anime 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. + + mplayer4anime 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 mplayer4anime. If not, see . + */ package mplayer4anime; -/** -Name: mplayer4anime -@author Dmitry Isaenko -License: GNU GPL v.3 -@version 0.12 -@see https://developersu.blogspot.com/search/label/mplayer4anime -@see https://github.com/developersu/mplayer4anime -2018-2019, Russia -*/ import javafx.application.Application; import javafx.fxml.FXMLLoader; @@ -55,17 +64,22 @@ public class MainFX extends Application { Runtime.getRuntime().addShutdownHook(new Thread(() -> tsih.interrupt())); primaryStage.getIcons().addAll( - new Image(MainFX.class.getResourceAsStream("/res/app_icon32x32.png")), - new Image(MainFX.class.getResourceAsStream("/res/app_icon48x48.png")), - new Image(MainFX.class.getResourceAsStream("/res/app_icon64x64.png")), - new Image(MainFX.class.getResourceAsStream("/res/app_icon128x128.png")) + new Image(getClass().getResourceAsStream("/res/app_icon32x32.png")), + new Image(getClass().getResourceAsStream("/res/app_icon48x48.png")), + new Image(getClass().getResourceAsStream("/res/app_icon64x64.png")), + new Image(getClass().getResourceAsStream("/res/app_icon128x128.png")) ); primaryStage.setTitle("mplayer4anime"); primaryStage.setMinWidth(500); primaryStage.setMinHeight(375); - primaryStage.setScene(new Scene(root, 1200, 800)); + Scene scene = new Scene(root, + AppPreferences.getINSTANCE().getSceneWidth(), + AppPreferences.getINSTANCE().getSceneHeight()); + primaryStage.setScene(scene); // Make linkage to controller method to handle exit() event in there. primaryStage.setOnHidden(e -> { + AppPreferences.getINSTANCE().setSceneHeight(scene.getHeight()); + AppPreferences.getINSTANCE().setSceneWidth(scene.getWidth()); tsih.interrupt(); controller.shutdown(); }); diff --git a/src/main/java/mplayer4anime/MplayerSlave.java b/src/main/java/mplayer4anime/MplayerSlave.java new file mode 100644 index 0000000..c552baa --- /dev/null +++ b/src/main/java/mplayer4anime/MplayerSlave.java @@ -0,0 +1,170 @@ +/* + Copyright 2018-2021 Dmitry Isaenko + + This file is part of mplayer4anime. + + mplayer4anime 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. + + mplayer4anime 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 mplayer4anime. If not, see . + */ +package mplayer4anime; + +import java.io.*; +import java.util.ResourceBundle; +import java.util.concurrent.TimeUnit; + +public class MplayerSlave implements ISlaveModeAppOrchestration { + private Process player; + private PrintStream playerIn; + private BufferedReader playerOutErr; + + private final ResourceBundle resourceBundle; + + public MplayerSlave(ResourceBundle resourceBundle){ + this.resourceBundle = resourceBundle; + } + + private boolean playerSingleCommand(String command){ + if (player != null && player.isAlive()) { + playerIn.print(command); + playerIn.print("\n"); + playerIn.flush(); + return true; + } + return false; + } + + public void subtitlesSwitch(){ + if (! playerSingleCommand("get_sub_visibility")) + return; + + try { + String returnedStr; + int returnedInt = 1; + while ((returnedStr = playerOutErr.readLine()) != null) { + if (returnedStr.startsWith("ANS_SUB_VISIBILITY=")) { + returnedInt = Integer.parseInt(returnedStr.substring("ANS_SUB_VISIBILITY=".length())); + break; + } + } + if (returnedInt == 1) + playerSingleCommand("sub_visibility 0"); + else + playerSingleCommand("sub_visibility 1"); + } + catch (IOException e) { + e.printStackTrace(); + System.out.println("Can't determine whether subtitles enabled or disabled"); + } + } + + public void fullscreenSwitch(){ + playerSingleCommand("vo_fullscreen"); + } + + public void mute(){ + playerSingleCommand("mute"); + } + + public void forcePlay(String mplayerPath, + String VideoFile, + String AudioFile, + String SubtitlesFile, + String subtitlesEncoding, + boolean subtitlesHidden, + boolean isFullscreen){ + try { + if (player != null && player.isAlive()){ + playerSingleCommand("quit"); + player.waitFor(500, TimeUnit.MILLISECONDS); + player.destroyForcibly(); + } + playPause(mplayerPath, VideoFile, AudioFile, SubtitlesFile, subtitlesEncoding, subtitlesHidden, isFullscreen); + } + catch (InterruptedException e){ + e.printStackTrace(); + } + } + + public boolean pause(){ + if (player == null || !player.isAlive()) + return false; + playerIn.print("pause"); + playerIn.print("\n"); + playerIn.flush(); + return true; + } + + public void playPause(String mplayerPath, + String VideoFile, + String AudioFile, + String SubtitlesFile, + String subtitlesEncoding, + boolean subtitlesHidden, + boolean isFullscreen){ + if (pause()) + return; + + boolean isAudio = AudioFile != null; + boolean isSubtitles = SubtitlesFile != null; + boolean SubEncodingDefault = subtitlesEncoding.equals("default"); + + try { + player = new ProcessBuilder( // FUCKING MAGIC! DON'T CHANGE SEQUENCE + mplayerPath, // It's a chance for Windows ;) + "-slave", + isAudio?"-audiofile":"", + isAudio?AudioFile:"", + "-quiet", + isFullscreen ? "-fs" : "", + VideoFile, + subtitlesHidden||isSubtitles?"-nosub":"", // Turn off subtitles embedded into MKV file (and replace by localy-stored subs file if needed) + isSubtitles?"-sub":"", + isSubtitles? SubtitlesFile:"", + isSubtitles?SubEncodingDefault?"":"-subcp":"", // Use subtitles -> YES -> Check if we need encoding + isSubtitles?SubEncodingDefault?"": subtitlesEncoding:"" + ).start(); + + PipedInputStream readFrom = new PipedInputStream(256 * 1024); + PipedOutputStream writeTo = new PipedOutputStream(readFrom); + + playerOutErr = new BufferedReader(new InputStreamReader(readFrom)); + + new LineRedirecter(player.getInputStream(), writeTo).start(); + new LineRedirecter(player.getErrorStream(), writeTo).start(); + + playerIn = new PrintStream(player.getOutputStream()); + + /* If user desired to disable subtitles but populated the list in the SUB pane, then load them and disable visibility. + * It should be done this way because if we didn't pass them to mplayer during start then user won't be able to enable them later on. + * There is another bike could be implemented such as passing input file during load but it's far more dumb idea than current implementation. + */ + if (subtitlesHidden) + playerSingleCommand("sub_visibility 0"); + } + catch (IOException e) { + ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorUnableToStartMplayer")); + } + } + + public void stop(){ + playerSingleCommand("stop"); + } + + public void volumeUp(){ + playerSingleCommand("volume +1 0"); + } + + public void volumeDown(){ + playerSingleCommand("volume -1 0"); + } +} diff --git a/src/main/java/mplayer4anime/ServiceWindow.java b/src/main/java/mplayer4anime/ServiceWindow.java index 91340cc..7e42bbe 100644 --- a/src/main/java/mplayer4anime/ServiceWindow.java +++ b/src/main/java/mplayer4anime/ServiceWindow.java @@ -1,12 +1,30 @@ +/* + Copyright 2018-2021 Dmitry Isaenko + + This file is part of mplayer4anime. + + mplayer4anime 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. + + mplayer4anime 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 mplayer4anime. If not, see . + */ package mplayer4anime; import javafx.scene.control.Alert; import javafx.scene.layout.Region; +/** + * Creates window with notification + * */ public class ServiceWindow { - /** - * Create window with notification - * */ public static void getErrorNotification(String title, String body){ Alert alertBox = new Alert(Alert.AlertType.ERROR); alertBox.setTitle(title); diff --git a/src/main/java/mplayer4anime/Settings/ControllerListsSelector.java b/src/main/java/mplayer4anime/Settings/ControllerListsSelector.java index 9c2683c..75175bd 100644 --- a/src/main/java/mplayer4anime/Settings/ControllerListsSelector.java +++ b/src/main/java/mplayer4anime/Settings/ControllerListsSelector.java @@ -1,32 +1,42 @@ +/* + Copyright 2018-2021 Dmitry Isaenko + + This file is part of mplayer4anime. + + mplayer4anime 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. + + mplayer4anime 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 mplayer4anime. If not, see . + */ package mplayer4anime.Settings; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; -import javafx.fxml.Initializable; import javafx.scene.control.ListView; import javafx.scene.control.TextField; import javafx.scene.control.TextFormatter; import javafx.scene.input.KeyEvent; -import java.net.URL; import java.util.Arrays; -import java.util.ResourceBundle; -public class ControllerListsSelector implements Initializable { +public class ControllerListsSelector{ @FXML private ListView listView; @FXML private TextField newRecordText; private ObservableList observableList; - private ResourceBundle resourceBundle; private boolean isListOfExtensions; - @Override - public void initialize(URL url, ResourceBundle rb) { - resourceBundle = rb; - } /** * Must be run on start * Set list content diff --git a/src/main/java/mplayer4anime/Settings/SettingsController.java b/src/main/java/mplayer4anime/Settings/SettingsController.java index 938723a..5600412 100644 --- a/src/main/java/mplayer4anime/Settings/SettingsController.java +++ b/src/main/java/mplayer4anime/Settings/SettingsController.java @@ -1,3 +1,21 @@ +/* + Copyright 2018-2021 Dmitry Isaenko + + This file is part of mplayer4anime. + + mplayer4anime 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. + + mplayer4anime 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 mplayer4anime. If not, see . + */ package mplayer4anime.Settings; import javafx.fxml.FXML; @@ -14,16 +32,12 @@ import java.util.ResourceBundle; import mplayer4anime.MediatorControl; public class SettingsController implements Initializable { - private AppPreferences appPreferences; @FXML - private ControllerListsSelector subExtensionListController; - @FXML - private ControllerListsSelector subEncodingListController; - @FXML - private ControllerListsSelector videoExtensionListController; - @FXML - private ControllerListsSelector audioExtensionListController; + private ControllerListsSelector subExtensionListController, + subEncodingListController, + videoExtensionListController, + audioExtensionListController; @FXML private Label pathToMplayerLbl; @FXML @@ -31,7 +45,7 @@ public class SettingsController implements Initializable { @Override public void initialize(URL url, ResourceBundle resBundle) { - appPreferences = new AppPreferences(); + appPreferences = AppPreferences.getINSTANCE(); pathToMplayerLbl.setText(appPreferences.getPath()); // Subtitles should be shown first? If TRUE, then set checkbox. @@ -72,16 +86,15 @@ public class SettingsController implements Initializable { @FXML private void Cancel(){ - Stage thisStage = (Stage) pathToMplayerLbl.getScene().getWindow(); // TODO: consider refactoring. Non-urgent. - thisStage.close(); + close(); } @FXML private void Ok(){ - this.Apply(); - Stage thisStage = (Stage) pathToMplayerLbl.getScene().getWindow(); // TODO: consider refactoring. Non-urgent. - thisStage.close(); + Apply(); + close(); } + @FXML private void Apply(){ appPreferences.setPath(pathToMplayerLbl.getText()); @@ -93,4 +106,9 @@ public class SettingsController implements Initializable { MediatorControl.getInstance().sentUpdates(); // TODO: implement list to track what should be updated } + + private void close(){ + Stage currentWindowStage = (Stage) pathToMplayerLbl.getScene().getWindow(); + currentWindowStage.close(); + } } diff --git a/src/main/java/mplayer4anime/Settings/SettingsWindow.java b/src/main/java/mplayer4anime/Settings/SettingsWindow.java index 2da0141..98c7744 100644 --- a/src/main/java/mplayer4anime/Settings/SettingsWindow.java +++ b/src/main/java/mplayer4anime/Settings/SettingsWindow.java @@ -1,3 +1,21 @@ +/* + Copyright 2018-2021 Dmitry Isaenko + + This file is part of mplayer4anime. + + mplayer4anime 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. + + mplayer4anime 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 mplayer4anime. If not, see . + */ package mplayer4anime.Settings; import javafx.fxml.FXMLLoader; @@ -5,7 +23,6 @@ import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; -import mplayer4anime.MainFX; import java.io.IOException; import java.util.Locale; @@ -29,15 +46,14 @@ public class SettingsWindow { try { Parent parentAbout = loaderSettings.load(); - //SettingsController settingsController = loaderSettings.getController(); stageAbout.setTitle(resourceBundle.getString("settings_SettingsName")); stageAbout.getIcons().addAll( - new Image(MainFX.class.getResourceAsStream("/res/settings_icon32x32.png")), - new Image(MainFX.class.getResourceAsStream("/res/settings_icon48x48.png")), - new Image(MainFX.class.getResourceAsStream("/res/settings_icon64x64.png")), - new Image(MainFX.class.getResourceAsStream("/res/settings_icon128x128.png")) - ); // TODO: change to something reliable + new Image("/res/settings_icon32x32.png"), + new Image("/res/settings_icon32x32.png"), + new Image("/res/settings_icon48x48.png"), + new Image("/res/settings_icon64x64.png"), + new Image("/res/settings_icon128x128.png")); stageAbout.setScene(new Scene(parentAbout, 570, 500)); stageAbout.show(); diff --git a/src/main/java/mplayer4anime/appPanes/ControllerPane.java b/src/main/java/mplayer4anime/appPanes/ControllerPane.java index 64a3a6d..0416fe3 100644 --- a/src/main/java/mplayer4anime/appPanes/ControllerPane.java +++ b/src/main/java/mplayer4anime/appPanes/ControllerPane.java @@ -1,3 +1,21 @@ +/* + Copyright 2018-2021 Dmitry Isaenko + + This file is part of mplayer4anime. + + mplayer4anime 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. + + mplayer4anime 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 mplayer4anime. If not, see . + */ package mplayer4anime.appPanes; import javafx.collections.FXCollections; @@ -10,28 +28,23 @@ import javafx.scene.control.ListView; import javafx.scene.input.KeyEvent; import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; -import javafx.util.Callback; import mplayer4anime.AppPreferences; import java.io.File; -import java.io.FilenameFilter; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.ResourceBundle; -public class ControllerPane implements Initializable { - +public class ControllerPane implements Initializable { private ResourceBundle resourceBundle; - // use folderToOpen same variable in all panes private static String folderToOpen; - private AppPreferences appPreferences; @FXML private ListView paneListView; - private ObservableList paneFileList = FXCollections.observableArrayList(); + private final ObservableList paneFileList = FXCollections.observableArrayList(); @FXML private Label paneLbl; @@ -42,8 +55,9 @@ public class ControllerPane implements Initializable { public void initialize(URL url, ResourceBundle resBundle) { SetCellFactory(paneListView); resourceBundle = resBundle; - appPreferences = new AppPreferences(); + appPreferences = AppPreferences.getINSTANCE(); } + public void setPaneType(String paneType){ this.paneType = paneType; @@ -69,12 +83,11 @@ public class ControllerPane implements Initializable { } /** Select element name (full path) using index recieved */ public String getElementSelected(){ - if (this.paneListView.getSelectionModel().getSelectedItem() != null) { - return this.paneFileList.get(this.getElementSelectedIndex()).toPath().toString(); - } - else { - return null; + File item = paneListView.getSelectionModel().getSelectedItem(); + if (item != null) { + return item.getAbsolutePath(); } + return null; } /** Select element in pane using index recieved */ public void setElementSelectedByIndex(int index){ @@ -98,33 +111,23 @@ public class ControllerPane implements Initializable { return elementsArray; } - private void SetCellFactory(ListView lv) { - lv.setCellFactory(new Callback, ListCell>() { - @Override - public ListCell call(ListView fileListView) { - return new ListCell(){ + private void SetCellFactory(ListView listView) { + listView.setCellFactory(cb -> new ListCell() { @Override - public void updateItem(File item, boolean empty){ + public void updateItem(File item, boolean empty) { // have to call super here super.updateItem(item, empty); - String trimmedName; - - if (item == null || empty){ + if (item == null || empty) { setText(null); + return; } - else { - trimmedName = item.getName(); - setText(trimmedName); - } + setText(item.getName()); } - }; - } - }); + }); } - /** - * Open file selector (Open folder button in UI). - * */ + + /** Open file selector (Open folder button in UI) */ @FXML void openDirChooser(){ String[] filesExtensionTmp; @@ -166,30 +169,25 @@ public class ControllerPane implements Initializable { if (directoryReceived != null) { File[] files; // Store files mkv/mka - files = directoryReceived.listFiles(new FilenameFilter() { - @Override - public boolean accept(File file, String Name) { - if (Name.lastIndexOf('.') > 0) { - int lastindex = Name.lastIndexOf('.'); - String ext = Name.substring(lastindex); - for (String key : filesExtension){ // TODO: add toLowerCase and validate whatever registry extension noted - if (ext.equals(key.substring(1))) - return true; - } - } else - return false; - return false; + files = directoryReceived.listFiles((file, Name) -> { + int lastIndexOfDot = Name.lastIndexOf('.'); + if (lastIndexOfDot > 0) { + String ext = Name.substring(lastIndexOfDot); + for (String key : filesExtension){ // TODO: add toLowerCase and validate whatever registry extension noted + if (ext.equals(key.substring(1))) + return true; + } } + return false; }); displayFiles(files); - } else { + } + else { System.out.println("No folder selected"); } } - /** - * Open file selector (Open files button in UI). - * */ + /** Open file selector (Open files button in UI) */ @FXML void openFilesChooser(){ String[] filesExtension; @@ -213,9 +211,8 @@ public class ControllerPane implements Initializable { lowerAndUpperExts.add(s); lowerAndUpperExts.add(s.toUpperCase()); } - filesExtension = lowerAndUpperExts.toArray(new String[lowerAndUpperExts.size()]); - List filesRecievedList; + List filesReceivedList; FileChooser fc = new FileChooser(); fc.setTitle(resourceBundle.getString("SelectFile")); @@ -223,16 +220,19 @@ public class ControllerPane implements Initializable { fc.setInitialDirectory(new File(System.getProperty("user.home"))); else fc.setInitialDirectory(new File(folderToOpen)); - fc.getExtensionFilters().addAll(new FileChooser.ExtensionFilter(paneType, filesExtension)); - filesRecievedList = fc.showOpenMultipleDialog(paneListView.getScene().getWindow()); - if (filesRecievedList != null){ // TODO: and !filesRecieved.isEmpty() - File[] filesRecieved = new File[filesRecievedList.size()]; - filesRecievedList.toArray(filesRecieved); - displayFiles(filesRecieved); - } else { + fc.getExtensionFilters().addAll(new FileChooser.ExtensionFilter(paneType, + lowerAndUpperExts.toArray(new String[0]))); + + filesReceivedList = fc.showOpenMultipleDialog(paneListView.getScene().getWindow()); + if (filesReceivedList == null) { System.out.println("No files selected"); + return; } + + File[] filesReceived = new File[filesReceivedList.size()]; + filesReceivedList.toArray(filesReceived); + displayFiles(filesReceived); } /** * Set files using lists. Used if playlist loaded @@ -250,7 +250,6 @@ public class ControllerPane implements Initializable { if (files != null && files.length > 0) { // spiced java magic Arrays.sort(files); - // Remember the folder used for MKV and reuse it when user opens MKA/subs folder (as new default path instead of user.home) folderToOpen = files[0].getParent(); //System.out.println(folderToOpen); diff --git a/src/main/resources/locale.properties b/src/main/resources/locale.properties index cfacac3..b245891 100644 --- a/src/main/resources/locale.properties +++ b/src/main/resources/locale.properties @@ -1,5 +1,5 @@ about_line1=mplayer4amine distributes under GNU GPLv3 license. -about_line2=Release: v0.14.1 +about_line2=Release: v0.15 about_line3=Development & maintenance by Dmitry Isaenko. about_AboutName=About main_tab_audio=Audio diff --git a/src/main/resources/locale_rus.properties b/src/main/resources/locale_rus.properties index 09eb876..aa4b42e 100644 --- a/src/main/resources/locale_rus.properties +++ b/src/main/resources/locale_rus.properties @@ -1,6 +1,6 @@ menu_File_Recent=\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0435 \u0444\u0430\u0439\u043B\u044B... about_line1=mplayer4amine \u0440\u0430\u0441\u043F\u0440\u043E\u0441\u0442\u0440\u0430\u043D\u044F\u0435\u0442\u0441\u044F \u043F\u043E \u043B\u0438\u0446\u0435\u043D\u0437\u0438\u0438 GNU GPLv3. -about_line2=\u0420\u0435\u043B\u0438\u0437: v0.14.1 +about_line2=\u0420\u0435\u043B\u0438\u0437: v0.15 about_line3=\u0420\u0430\u0437\u0440\u0430\u0431\u043E\u0442\u0430\u043D\u043E \u0438 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F \u0414\u043C\u0438\u0442\u0440\u0438\u0435\u043C \u0418\u0441\u0430\u0435\u043D\u043A\u043E. about_AboutName=\u041E \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438 main_tab_audio=\u0410\u0443\u0434\u0438\u043E diff --git a/src/main/resources/res/landing.css b/src/main/resources/res/landing.css index e0b9134..cd0f187 100644 --- a/src/main/resources/res/landing.css +++ b/src/main/resources/res/landing.css @@ -97,7 +97,7 @@ -fx-fill: #000000; } .tab-paneSettings .tab:selected SVGPath{ - -fx-fill: #f90000; + -fx-fill: #c30000; } .tab-paneSettings .tab{ -fx-background-color: #ffffff; diff --git a/src/main/resources/res/pocket.png b/src/main/resources/res/pocket.png index eea78aa..912a54f 100644 Binary files a/src/main/resources/res/pocket.png and b/src/main/resources/res/pocket.png differ