Update image, add save-window-size-on-close feature, start separating mplayer controls out of the in-app controller-class that should make application architecture more solid in future and give a chance to add other back ends support. Start code refactoring, adding copyleft banners.

This commit is contained in:
Dmitry Isaenko 2021-09-12 05:39:33 +03:00
parent 2fbdbc805f
commit 08aef521fe
16 changed files with 563 additions and 291 deletions

View file

@ -8,7 +8,7 @@
<name>mplayer4anime</name> <name>mplayer4anime</name>
<artifactId>mplayer4anime</artifactId> <artifactId>mplayer4anime</artifactId>
<version>0.14.1-SNAPSHOT</version> <version>0.15-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
@ -16,7 +16,7 @@
<description> <description>
mplayer front end to play content pairs that are mostly used for anime (mka+mkv, mp4+ac3, mkv+srt) mplayer front end to play content pairs that are mostly used for anime (mka+mkv, mp4+ac3, mkv+srt)
</description> </description>
<inceptionYear>2018-2019</inceptionYear> <inceptionYear>2018</inceptionYear>
<organization> <organization>
<name>Dmitry Isaenko</name> <name>Dmitry Isaenko</name>
<url>https://developersu.blogspot.com/search/label/mplayer4anime</url> <url>https://developersu.blogspot.com/search/label/mplayer4anime</url>

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
package mplayer4anime; package mplayer4anime;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
// Rule application settings // Rule application settings
public class AppPreferences { public class AppPreferences {
private static AppPreferences INSTANCE = new AppPreferences();
private Preferences preferences = Preferences.userRoot().node("mplayer4anime");
private Preferences preferences; private AppPreferences(){}
public AppPreferences(){ public static AppPreferences getINSTANCE() {
preferences = Preferences.userRoot().node("mplayer4anime"); return INSTANCE;
} }
public void setPath(String path){ public void setPath(String path){
@ -96,5 +116,10 @@ public class AppPreferences {
preferences.put("RECENT_PLS_" + i, ""); 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); }
} }

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
package mplayer4anime; package mplayer4anime;
import javafx.application.HostServices; import javafx.application.HostServices;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.event.ActionEvent; import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.*; import javafx.scene.control.*;
@ -18,38 +35,33 @@ import mplayer4anime.appPanes.ControllerSUB;
import java.io.*; import java.io.*;
import java.net.URL; import java.net.URL;
import java.util.ListIterator;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class Controller implements Initializable { public class Controller implements Initializable {
@FXML @FXML
private ControllerPane mkvPaneController; private ControllerPane mkvPaneController, mkaPaneController;
@FXML
private ControllerPane mkaPaneController;
@FXML @FXML
private ControllerSUB subPaneController; private ControllerSUB subPaneController;
@FXML @FXML
private Label statusLbl; private Label statusLbl;
@FXML @FXML
private Menu recentlyOpenedMenu; private Menu recentlyOpenedMenu;
// Get preferences
private AppPreferences appPreferences = new AppPreferences();
private ResourceBundle resourceBundle;
@FXML @FXML
private CheckMenuItem fullScreen; private CheckMenuItem fullScreen;
// Get host services for opening URLs etc.
private HostServices hostServices;
@FXML @FXML
private TabPane tabPane; private TabPane tabPane;
@FXML @FXML
private CheckMenuItem subsHide; 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). // 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) { public void setPlaylistAsArgument(String playlist) {
@ -79,150 +91,19 @@ public class Controller implements Initializable {
subsHide.setSelected(appPreferences.getSubtitlesHideSelected()); subsHide.setSelected(appPreferences.getSubtitlesHideSelected());
String[] recentPlaylists = appPreferences.getRecentPlaylists(); String[] recentPlaylists = appPreferences.getRecentPlaylists();
for (int i = recentPlaylists.length-1; i >= 0; i--) for (int i = recentPlaylists.length-1; i >= 0; i--) {
if (!recentPlaylists[i].isEmpty()) if (recentPlaylists[i].isEmpty())
continue;
addRecentlyOpened(recentPlaylists[i]); addRecentlyOpened(recentPlaylists[i]);
} }
mplayer = new MplayerSlave(resourceBundle);
}
void setHostServices(HostServices hostServices) { void setHostServices(HostServices hostServices) {
this.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 @FXML
private void closeBtn() { private void closeBtn() {
Stage currentStage = (Stage) tabPane.getScene().getWindow(); Stage currentStage = (Stage) tabPane.getScene().getWindow();
@ -312,21 +193,21 @@ public class Controller implements Initializable {
} }
private void addRecentlyOpened(String playlistPath){ private void addRecentlyOpened(String playlistPath){
ListIterator<MenuItem> iteratorItem = recentlyOpenedMenu.getItems().listIterator(); ObservableList<MenuItem> items = recentlyOpenedMenu.getItems();
while (iteratorItem.hasNext()) { for (MenuItem item : items) {
MenuItem mi = iteratorItem.next(); if (item.getUserData() != null && item.getUserData().equals(playlistPath)) {
if (mi.getUserData() != null && mi.getUserData().equals(playlistPath)) { items.remove(item);
recentlyOpenedMenu.getItems().remove(mi); items.add(0, item);
recentlyOpenedMenu.getItems().add(0, mi);
return; return;
} }
} }
MenuItem menuItem = new MenuItem(); MenuItem menuItem = new MenuItem();
String fileNameOnly; String playListName;
fileNameOnly = playlistPath.substring(playlistPath.lastIndexOf(File.separator) + 1); playListName = playlistPath.substring(
menuItem.setText(fileNameOnly); playlistPath.lastIndexOf(File.separator) + 1, playlistPath.lastIndexOf("."));
menuItem.setText(playListName);
menuItem.setUserData(playlistPath); menuItem.setUserData(playlistPath);
menuItem.setOnAction(actionEvent -> { menuItem.setOnAction(actionEvent -> {
@ -334,9 +215,90 @@ public class Controller implements Initializable {
setAllLists(jsonStorage); setAllLists(jsonStorage);
}); });
// Limit list to 13 elements (2 in the end are separator and clear button) // Limit list to 13 elements (2 in the end are separator and clear button)
if (recentlyOpenedMenu.getItems().size() >= 11) if (items.size() >= 11)
recentlyOpenedMenu.getItems().remove(9, recentlyOpenedMenu.getItems().size() - 2); items.remove(9, recentlyOpenedMenu.getItems().size() - 2);
recentlyOpenedMenu.getItems().add(0, menuItem); 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();
} }
} }

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
package mplayer4anime;
public interface ISlaveModeAppOrchestration {
// TODO: implement unified interface for mplayer and mpv
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
package mplayer4anime; package mplayer4anime;
import java.io.*; import java.io.*;
public class LineRedirecter extends Thread { public class LineRedirecter extends Thread {
private InputStream inStr; private final InputStream inStream;
private OutputStream outStr; private final OutputStream outStream;
LineRedirecter(InputStream in, OutputStream out){ LineRedirecter(InputStream inStream, OutputStream outStream){
this.inStr = in; this.inStream = inStream;
this.outStr = out; this.outStream = outStream;
} }
public void run(){ public void run(){
try { try {
BufferedReader bufferReader = new BufferedReader(new InputStreamReader(inStr)); BufferedReader bufferReader = new BufferedReader(new InputStreamReader(inStream));
PrintStream printStream = new PrintStream(outStr); PrintStream printStream = new PrintStream(outStream);
String playerOutput; String playerOutput;
while ((playerOutput = bufferReader.readLine()) != null) { while ((playerOutput = bufferReader.readLine()) != null) {
printStream.println(playerOutput); printStream.println(playerOutput);
} }
}
}catch (IOException e){ catch (IOException e){
e.printStackTrace(); e.printStackTrace();
} }

View file

@ -1,13 +1,22 @@
package mplayer4anime; /*
/** Copyright 2018-2021 Dmitry Isaenko
Name: mplayer4anime
@author Dmitry Isaenko This file is part of mplayer4anime.
License: GNU GPL v.3
@version 0.12 mplayer4anime is free software: you can redistribute it and/or modify
@see https://developersu.blogspot.com/search/label/mplayer4anime it under the terms of the GNU General Public License as published by
@see https://github.com/developersu/mplayer4anime the Free Software Foundation, either version 3 of the License, or
2018-2019, Russia (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 <https://www.gnu.org/licenses/>.
*/ */
package mplayer4anime;
import javafx.application.Application; import javafx.application.Application;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
@ -55,17 +64,22 @@ public class MainFX extends Application {
Runtime.getRuntime().addShutdownHook(new Thread(() -> tsih.interrupt())); Runtime.getRuntime().addShutdownHook(new Thread(() -> tsih.interrupt()));
primaryStage.getIcons().addAll( primaryStage.getIcons().addAll(
new Image(MainFX.class.getResourceAsStream("/res/app_icon32x32.png")), new Image(getClass().getResourceAsStream("/res/app_icon32x32.png")),
new Image(MainFX.class.getResourceAsStream("/res/app_icon48x48.png")), new Image(getClass().getResourceAsStream("/res/app_icon48x48.png")),
new Image(MainFX.class.getResourceAsStream("/res/app_icon64x64.png")), new Image(getClass().getResourceAsStream("/res/app_icon64x64.png")),
new Image(MainFX.class.getResourceAsStream("/res/app_icon128x128.png")) new Image(getClass().getResourceAsStream("/res/app_icon128x128.png"))
); );
primaryStage.setTitle("mplayer4anime"); primaryStage.setTitle("mplayer4anime");
primaryStage.setMinWidth(500); primaryStage.setMinWidth(500);
primaryStage.setMinHeight(375); 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. // Make linkage to controller method to handle exit() event in there.
primaryStage.setOnHidden(e -> { primaryStage.setOnHidden(e -> {
AppPreferences.getINSTANCE().setSceneHeight(scene.getHeight());
AppPreferences.getINSTANCE().setSceneWidth(scene.getWidth());
tsih.interrupt(); tsih.interrupt();
controller.shutdown(); controller.shutdown();
}); });

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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");
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
package mplayer4anime; package mplayer4anime;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
public class ServiceWindow {
/** /**
* Create window with notification * Creates window with notification
* */ * */
public class ServiceWindow {
public static void getErrorNotification(String title, String body){ public static void getErrorNotification(String title, String body){
Alert alertBox = new Alert(Alert.AlertType.ERROR); Alert alertBox = new Alert(Alert.AlertType.ERROR);
alertBox.setTitle(title); alertBox.setTitle(title);

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
package mplayer4anime.Settings; package mplayer4anime.Settings;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter; import javafx.scene.control.TextFormatter;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import java.net.URL;
import java.util.Arrays; import java.util.Arrays;
import java.util.ResourceBundle;
public class ControllerListsSelector implements Initializable { public class ControllerListsSelector{
@FXML @FXML
private ListView<String> listView; private ListView<String> listView;
@FXML @FXML
private TextField newRecordText; private TextField newRecordText;
private ObservableList<String> observableList; private ObservableList<String> observableList;
private ResourceBundle resourceBundle;
private boolean isListOfExtensions; private boolean isListOfExtensions;
@Override
public void initialize(URL url, ResourceBundle rb) {
resourceBundle = rb;
}
/** /**
* Must be run on start * Must be run on start
* Set list content * Set list content

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
package mplayer4anime.Settings; package mplayer4anime.Settings;
import javafx.fxml.FXML; import javafx.fxml.FXML;
@ -14,16 +32,12 @@ import java.util.ResourceBundle;
import mplayer4anime.MediatorControl; import mplayer4anime.MediatorControl;
public class SettingsController implements Initializable { public class SettingsController implements Initializable {
private AppPreferences appPreferences; private AppPreferences appPreferences;
@FXML @FXML
private ControllerListsSelector subExtensionListController; private ControllerListsSelector subExtensionListController,
@FXML subEncodingListController,
private ControllerListsSelector subEncodingListController; videoExtensionListController,
@FXML audioExtensionListController;
private ControllerListsSelector videoExtensionListController;
@FXML
private ControllerListsSelector audioExtensionListController;
@FXML @FXML
private Label pathToMplayerLbl; private Label pathToMplayerLbl;
@FXML @FXML
@ -31,7 +45,7 @@ public class SettingsController implements Initializable {
@Override @Override
public void initialize(URL url, ResourceBundle resBundle) { public void initialize(URL url, ResourceBundle resBundle) {
appPreferences = new AppPreferences(); appPreferences = AppPreferences.getINSTANCE();
pathToMplayerLbl.setText(appPreferences.getPath()); pathToMplayerLbl.setText(appPreferences.getPath());
// Subtitles should be shown first? If TRUE, then set checkbox. // Subtitles should be shown first? If TRUE, then set checkbox.
@ -72,16 +86,15 @@ public class SettingsController implements Initializable {
@FXML @FXML
private void Cancel(){ private void Cancel(){
Stage thisStage = (Stage) pathToMplayerLbl.getScene().getWindow(); // TODO: consider refactoring. Non-urgent. close();
thisStage.close();
} }
@FXML @FXML
private void Ok(){ private void Ok(){
this.Apply(); Apply();
Stage thisStage = (Stage) pathToMplayerLbl.getScene().getWindow(); // TODO: consider refactoring. Non-urgent. close();
thisStage.close();
} }
@FXML @FXML
private void Apply(){ private void Apply(){
appPreferences.setPath(pathToMplayerLbl.getText()); 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 MediatorControl.getInstance().sentUpdates(); // TODO: implement list to track what should be updated
} }
private void close(){
Stage currentWindowStage = (Stage) pathToMplayerLbl.getScene().getWindow();
currentWindowStage.close();
}
} }

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
package mplayer4anime.Settings; package mplayer4anime.Settings;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
@ -5,7 +23,6 @@ import javafx.scene.Parent;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.stage.Stage; import javafx.stage.Stage;
import mplayer4anime.MainFX;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
@ -29,15 +46,14 @@ public class SettingsWindow {
try { try {
Parent parentAbout = loaderSettings.load(); Parent parentAbout = loaderSettings.load();
//SettingsController settingsController = loaderSettings.getController();
stageAbout.setTitle(resourceBundle.getString("settings_SettingsName")); stageAbout.setTitle(resourceBundle.getString("settings_SettingsName"));
stageAbout.getIcons().addAll( stageAbout.getIcons().addAll(
new Image(MainFX.class.getResourceAsStream("/res/settings_icon32x32.png")), new Image("/res/settings_icon32x32.png"),
new Image(MainFX.class.getResourceAsStream("/res/settings_icon48x48.png")), new Image("/res/settings_icon32x32.png"),
new Image(MainFX.class.getResourceAsStream("/res/settings_icon64x64.png")), new Image("/res/settings_icon48x48.png"),
new Image(MainFX.class.getResourceAsStream("/res/settings_icon128x128.png")) new Image("/res/settings_icon64x64.png"),
); // TODO: change to something reliable new Image("/res/settings_icon128x128.png"));
stageAbout.setScene(new Scene(parentAbout, 570, 500)); stageAbout.setScene(new Scene(parentAbout, 570, 500));
stageAbout.show(); stageAbout.show();

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
package mplayer4anime.appPanes; package mplayer4anime.appPanes;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@ -10,11 +28,9 @@ import javafx.scene.control.ListView;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.stage.DirectoryChooser; import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import javafx.util.Callback;
import mplayer4anime.AppPreferences; import mplayer4anime.AppPreferences;
import java.io.File; import java.io.File;
import java.io.FilenameFilter;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -22,16 +38,13 @@ import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class ControllerPane implements Initializable { public class ControllerPane implements Initializable {
private ResourceBundle resourceBundle; private ResourceBundle resourceBundle;
// use folderToOpen same variable in all panes
private static String folderToOpen; private static String folderToOpen;
private AppPreferences appPreferences; private AppPreferences appPreferences;
@FXML @FXML
private ListView<File> paneListView; private ListView<File> paneListView;
private ObservableList<File> paneFileList = FXCollections.observableArrayList(); private final ObservableList<File> paneFileList = FXCollections.observableArrayList();
@FXML @FXML
private Label paneLbl; private Label paneLbl;
@ -42,8 +55,9 @@ public class ControllerPane implements Initializable {
public void initialize(URL url, ResourceBundle resBundle) { public void initialize(URL url, ResourceBundle resBundle) {
SetCellFactory(paneListView); SetCellFactory(paneListView);
resourceBundle = resBundle; resourceBundle = resBundle;
appPreferences = new AppPreferences(); appPreferences = AppPreferences.getINSTANCE();
} }
public void setPaneType(String paneType){ public void setPaneType(String paneType){
this.paneType = paneType; this.paneType = paneType;
@ -69,13 +83,12 @@ public class ControllerPane implements Initializable {
} }
/** Select element name (full path) using index recieved */ /** Select element name (full path) using index recieved */
public String getElementSelected(){ public String getElementSelected(){
if (this.paneListView.getSelectionModel().getSelectedItem() != null) { File item = paneListView.getSelectionModel().getSelectedItem();
return this.paneFileList.get(this.getElementSelectedIndex()).toPath().toString(); if (item != null) {
return item.getAbsolutePath();
} }
else {
return null; return null;
} }
}
/** Select element in pane using index recieved */ /** Select element in pane using index recieved */
public void setElementSelectedByIndex(int index){ public void setElementSelectedByIndex(int index){
this.paneListView.getSelectionModel().select(index); this.paneListView.getSelectionModel().select(index);
@ -98,33 +111,23 @@ public class ControllerPane implements Initializable {
return elementsArray; return elementsArray;
} }
private void SetCellFactory(ListView<File> lv) { private void SetCellFactory(ListView<File> listView) {
lv.setCellFactory(new Callback<ListView<File>, ListCell<File>>() { listView.setCellFactory(cb -> new ListCell<File>() {
@Override
public ListCell<File> call(ListView<File> fileListView) {
return new ListCell<File>(){
@Override @Override
public void updateItem(File item, boolean empty) { public void updateItem(File item, boolean empty) {
// have to call super here // have to call super here
super.updateItem(item, empty); super.updateItem(item, empty);
String trimmedName;
if (item == null || empty) { if (item == null || empty) {
setText(null); setText(null);
return;
} }
else { setText(item.getName());
trimmedName = item.getName();
setText(trimmedName);
}
}
};
} }
}); });
} }
/**
* Open file selector (Open folder button in UI). /** Open file selector (Open folder button in UI) */
* */
@FXML @FXML
void openDirChooser(){ void openDirChooser(){
String[] filesExtensionTmp; String[] filesExtensionTmp;
@ -166,30 +169,25 @@ public class ControllerPane implements Initializable {
if (directoryReceived != null) { if (directoryReceived != null) {
File[] files; // Store files mkv/mka File[] files; // Store files mkv/mka
files = directoryReceived.listFiles(new FilenameFilter() { files = directoryReceived.listFiles((file, Name) -> {
@Override int lastIndexOfDot = Name.lastIndexOf('.');
public boolean accept(File file, String Name) { if (lastIndexOfDot > 0) {
if (Name.lastIndexOf('.') > 0) { String ext = Name.substring(lastIndexOfDot);
int lastindex = Name.lastIndexOf('.');
String ext = Name.substring(lastindex);
for (String key : filesExtension){ // TODO: add toLowerCase and validate whatever registry extension noted for (String key : filesExtension){ // TODO: add toLowerCase and validate whatever registry extension noted
if (ext.equals(key.substring(1))) if (ext.equals(key.substring(1)))
return true; return true;
} }
} else
return false;
return false;
} }
return false;
}); });
displayFiles(files); displayFiles(files);
} else { }
else {
System.out.println("No folder selected"); System.out.println("No folder selected");
} }
} }
/** /** Open file selector (Open files button in UI) */
* Open file selector (Open files button in UI).
* */
@FXML @FXML
void openFilesChooser(){ void openFilesChooser(){
String[] filesExtension; String[] filesExtension;
@ -213,9 +211,8 @@ public class ControllerPane implements Initializable {
lowerAndUpperExts.add(s); lowerAndUpperExts.add(s);
lowerAndUpperExts.add(s.toUpperCase()); lowerAndUpperExts.add(s.toUpperCase());
} }
filesExtension = lowerAndUpperExts.toArray(new String[lowerAndUpperExts.size()]);
List<File> filesRecievedList; List<File> filesReceivedList;
FileChooser fc = new FileChooser(); FileChooser fc = new FileChooser();
fc.setTitle(resourceBundle.getString("SelectFile")); fc.setTitle(resourceBundle.getString("SelectFile"));
@ -223,16 +220,19 @@ public class ControllerPane implements Initializable {
fc.setInitialDirectory(new File(System.getProperty("user.home"))); fc.setInitialDirectory(new File(System.getProperty("user.home")));
else else
fc.setInitialDirectory(new File(folderToOpen)); fc.setInitialDirectory(new File(folderToOpen));
fc.getExtensionFilters().addAll(new FileChooser.ExtensionFilter(paneType, filesExtension));
filesRecievedList = fc.showOpenMultipleDialog(paneListView.getScene().getWindow()); fc.getExtensionFilters().addAll(new FileChooser.ExtensionFilter(paneType,
if (filesRecievedList != null){ // TODO: and !filesRecieved.isEmpty() lowerAndUpperExts.toArray(new String[0])));
File[] filesRecieved = new File[filesRecievedList.size()];
filesRecievedList.toArray(filesRecieved); filesReceivedList = fc.showOpenMultipleDialog(paneListView.getScene().getWindow());
displayFiles(filesRecieved); if (filesReceivedList == null) {
} else {
System.out.println("No files selected"); 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 * Set files using lists. Used if playlist loaded
@ -250,7 +250,6 @@ public class ControllerPane implements Initializable {
if (files != null && files.length > 0) { if (files != null && files.length > 0) {
// spiced java magic // spiced java magic
Arrays.sort(files); 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) // 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(); folderToOpen = files[0].getParent();
//System.out.println(folderToOpen); //System.out.println(folderToOpen);

View file

@ -1,5 +1,5 @@
about_line1=mplayer4amine distributes under GNU GPLv3 license. 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_line3=Development & maintenance by Dmitry Isaenko.
about_AboutName=About about_AboutName=About
main_tab_audio=Audio main_tab_audio=Audio

View file

@ -1,6 +1,6 @@
menu_File_Recent=\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0438\u0435 \u0444\u0430\u0439\u043B\u044B... 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_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_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 about_AboutName=\u041E \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438
main_tab_audio=\u0410\u0443\u0434\u0438\u043E main_tab_audio=\u0410\u0443\u0434\u0438\u043E

View file

@ -97,7 +97,7 @@
-fx-fill: #000000; -fx-fill: #000000;
} }
.tab-paneSettings .tab:selected SVGPath{ .tab-paneSettings .tab:selected SVGPath{
-fx-fill: #f90000; -fx-fill: #c30000;
} }
.tab-paneSettings .tab{ .tab-paneSettings .tab{
-fx-background-color: #ffffff; -fx-background-color: #ffffff;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 34 KiB