Drafting JNI for mpv API. Refactoring & code cleanup.

This commit is contained in:
Dmitry Isaenko 2021-09-12 16:32:55 +03:00
parent bd45f72b5a
commit e89cad31ef
23 changed files with 500 additions and 131 deletions

37
mpv_library/Makefile Normal file
View file

@ -0,0 +1,37 @@
# Compiler
CC=gcc
# Flags
CFLAGS=-O2
MKDIR_P = mkdir -p
APP_NAME = mpvjni
all: x86 amd64
x86:
$(MKDIR_P) ./x86
$(CC) ${CFLAGS} -m32 -c -fPIC -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" ${APP_NAME}.c -o ${APP_NAME}_x86.o
$(CC) ${CFLAGS} -m32 -shared -fPIC -o ./x86/${APP_NAME}.so ${APP_NAME}_x86.o -lc
amd64:
$(MKDIR_P) ./amd64
$(CC) ${CFLAGS} -m64 -c -fPIC -I"${JAVA_HOME}/include" -I"${JAVA_HOME}/include/linux" ${APP_NAME}.c -o ${APP_NAME}_amd64.o
$(CC) ${CFLAGS} -m64 -shared -fPIC -o ./amd64/${APP_NAME}.so ${APP_NAME}_amd64.o -lc
clean:
rm -rf \
${APP_NAME}_amd64.o \
${APP_NAME}_x86.o \
./x86 \
./amd64
headers:
cd /src/main/java
javac mplayer4anime/mpv/MpvJni.java -h ../../../mpv_library/
install: x86 amd64
install ./x86/${APP_NAME}.so ../src/main/resources/native/linux/x86/
install ./amd64/${APP_NAME}.so ../src/main/resources/native/linux/amd64/
uninstall:
rm ../src/main/resources/native/linux/x86/${APP_NAME}.so
rm ../src/main/resources/native/linux/amd64/${APP_NAME}.so

View file

@ -0,0 +1,21 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class mplayer4anime_mpv_MpvSlave */
#ifndef _Included_mplayer4anime_mpv_MpvSlave
#define _Included_mplayer4anime_mpv_MpvSlave
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: mplayer4anime_mpv_MpvSlave
* Method: play
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_mplayer4anime_mpv_MpvSlave_play
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

6
mpv_library/mpvjni.c Normal file
View file

@ -0,0 +1,6 @@
#include <mpv/client.h>
#include "mplayer4anime_mpv_MpvSlave.h"
JNIEXPORT void JNICALL Java_mplayer4anime_mpv_MpvSlave_play(JNIEnv * jnienv, jobject jobject){
};

View file

@ -122,4 +122,7 @@ public class AppPreferences {
public double getSceneHeight(){ return preferences.getDouble("window_height", 800.0); } public double getSceneHeight(){ return preferences.getDouble("window_height", 800.0); }
public void setSceneHeight(double value){ preferences.putDouble("window_height", value); } public void setSceneHeight(double value){ preferences.putDouble("window_height", value); }
public int getBackendEngineIndexId(){ return preferences.getInt("backend_player", 0); }
public void setBackendEngineIndexId(int value){ preferences.putInt("backend_player", value); }
} }

View file

@ -32,6 +32,8 @@ import mplayer4anime.Playlists.Playlists;
import mplayer4anime.Settings.SettingsWindow; import mplayer4anime.Settings.SettingsWindow;
import mplayer4anime.appPanes.ControllerPane; import mplayer4anime.appPanes.ControllerPane;
import mplayer4anime.appPanes.ControllerSUB; import mplayer4anime.appPanes.ControllerSUB;
import mplayer4anime.mplayer.MplayerSlave;
import mplayer4anime.mpv.MpvSlave;
import java.io.*; import java.io.*;
import java.net.URL; import java.net.URL;
@ -56,12 +58,10 @@ public class Controller implements Initializable {
private final AppPreferences appPreferences = AppPreferences.getINSTANCE(); private final AppPreferences appPreferences = AppPreferences.getINSTANCE();
private ResourceBundle resourceBundle; private ResourceBundle resourceBundle;
// Get host services for opening URLs etc.
private HostServices hostServices; private HostServices hostServices;
private String currentPlaylistLocation; private String currentPlaylistLocation;
private String backend;
private MplayerSlave mplayer; private ISlaveModeAppOrchestration player;
// 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) {
@ -96,8 +96,14 @@ public class Controller implements Initializable {
continue; continue;
addRecentlyOpened(recentPlaylists[i]); addRecentlyOpened(recentPlaylists[i]);
} }
if (appPreferences.getBackendEngineIndexId() == 0) {
mplayer = new MplayerSlave(resourceBundle); backend = "mplayer";
player = new MplayerSlave(resourceBundle);
}
else {
backend = "mpv";
player = new MpvSlave(resourceBundle);
}
} }
void setHostServices(HostServices hostServices) { void setHostServices(HostServices hostServices) {
@ -126,11 +132,15 @@ public class Controller implements Initializable {
} }
@FXML @FXML
private void infoBtn(){ new AboutWindow(this.hostServices); } // TODO: fix this shit with hostSerivces that doesn't work @ linux private void infoBtn(){
new AboutWindow(this.hostServices);
} // TODO: fix this shit with hostSerivces that doesn't work @ linux
/** SETTINGS HANDLE */ /** SETTINGS HANDLE */
@FXML @FXML
private void settingsBtn(){ new SettingsWindow(); } private void settingsBtn(){
new SettingsWindow();
}
// Get event that notify application in case some settings has been changed // Get event that notify application in case some settings has been changed
// This function called from MediatorControl after mediator receives request form SettingsController indicating that user updated some required fields. // This function called from MediatorControl after mediator receives request form SettingsController indicating that user updated some required fields.
void updateAfterSettingsChanged(){ void updateAfterSettingsChanged(){
@ -138,6 +148,18 @@ public class Controller implements Initializable {
subPaneController.setEncoding(appPreferences.getSubsEncodingList(), null); subPaneController.setEncoding(appPreferences.getSubsEncodingList(), null);
// In case of application failure should be better to save this immediately // In case of application failure should be better to save this immediately
appPreferences.setLastTimeUsedSubsEncoding(subPaneController.getSelectedEncoding()); appPreferences.setLastTimeUsedSubsEncoding(subPaneController.getSelectedEncoding());
switchBackend(appPreferences.getBackendEngineIndexId());
}
private void switchBackend(int newBackEndId){
if (newBackEndId == 0 && backend.equals("mpv")){
backend = "mplayer";
player = new MplayerSlave(resourceBundle);
return;
}
if (newBackEndId == 1 && backend.equals("mplayer")){
backend = "mpv";
player = new MpvSlave(resourceBundle);
}
} }
@FXML @FXML
@ -147,46 +169,53 @@ public class Controller implements Initializable {
} }
private void setAllLists(JsonStorage jsonStorage){ private void setAllLists(JsonStorage jsonStorage){
if (jsonStorage != null) { if (jsonStorage != null) {
mkvPaneController.cleanList();
mkaPaneController.cleanList();
subPaneController.cleanList();
mkvPaneController.setFilesFromList(jsonStorage.getVideo()); mkvPaneController.setFilesFromList(jsonStorage.getVideo());
mkaPaneController.setFilesFromList(jsonStorage.getAudio()); mkaPaneController.setFilesFromList(jsonStorage.getAudio());
subPaneController.setFilesFromList(jsonStorage.getSubs()); subPaneController.setFilesFromList(jsonStorage.getSubs());
subPaneController.selectEncodingValue(jsonStorage.getSubEncoding(), appPreferences); subPaneController.selectEncodingValue(jsonStorage.getSubEncoding(), appPreferences);
currentPlaylistLocation = Playlists.getPlaylistLocation(); // TODO: Implement listener? mmm... currentPlaylistLocation = Playlists.getPlaylistLocation(); // TODO: Implement listener? mmm...
//System.out.println(currentPlaylistLocation);
statusLbl.setText(currentPlaylistLocation); statusLbl.setText(currentPlaylistLocation);
addRecentlyOpened(currentPlaylistLocation); addRecentlyOpened(currentPlaylistLocation);
} }
} }
@FXML @FXML
private void saveBtn() { private void saveBtn() {
if (mkvPaneController.getElementsCount() == 0) if (mkvPaneController.getElementsCount() == 0) {
ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorUnableToSaveEmptyPlaylist")); ServiceWindow.getErrorNotification(resourceBundle.getString("Error"),
else { resourceBundle.getString("ErrorUnableToSaveEmptyPlaylist"));
JsonStorage jsonStorage = new JsonStorage(mkvPaneController.getElementsAll(), mkaPaneController.getElementsAll(), subPaneController.getElementsAll(), subPaneController.getSelectedEncoding()); return;
}
JsonStorage jsonStorage = new JsonStorage(
mkvPaneController.getElementsAll(),
mkaPaneController.getElementsAll(),
subPaneController.getElementsAll(),
subPaneController.getSelectedEncoding());
if (Playlists.SaveCurrent(resourceBundle, jsonStorage)) { if (Playlists.SaveCurrent(resourceBundle, jsonStorage)) {
this.currentPlaylistLocation = Playlists.getPlaylistLocation(); this.currentPlaylistLocation = Playlists.getPlaylistLocation();
this.statusLbl.setText(currentPlaylistLocation); //TODO: update header of the application to include this? this.statusLbl.setText(currentPlaylistLocation); //TODO: update header of the application to include this?
addRecentlyOpened(currentPlaylistLocation); addRecentlyOpened(currentPlaylistLocation);
} }
} }
}
@FXML @FXML
private void saveAsBtn() { private void saveAsBtn() {
if (mkvPaneController.getElementsCount() == 0) if (mkvPaneController.getElementsCount() == 0) {
ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorUnableToSaveEmptyPlaylist")); ServiceWindow.getErrorNotification(resourceBundle.getString("Error"),
else { resourceBundle.getString("ErrorUnableToSaveEmptyPlaylist"));
JsonStorage jsonStorage = new JsonStorage(mkvPaneController.getElementsAll(), mkaPaneController.getElementsAll(), subPaneController.getElementsAll(), subPaneController.getSelectedEncoding()); return;
}
JsonStorage jsonStorage = new JsonStorage(
mkvPaneController.getElementsAll(),
mkaPaneController.getElementsAll(),
subPaneController.getElementsAll(),
subPaneController.getSelectedEncoding());
if (Playlists.SaveAs(resourceBundle, jsonStorage)) { if (Playlists.SaveAs(resourceBundle, jsonStorage)) {
this.currentPlaylistLocation = Playlists.getPlaylistLocation(); this.currentPlaylistLocation = Playlists.getPlaylistLocation();
this.statusLbl.setText(currentPlaylistLocation); //TODO: update header of the application to include this? this.statusLbl.setText(currentPlaylistLocation); //TODO: update header of the application to include this?
addRecentlyOpened(currentPlaylistLocation); addRecentlyOpened(currentPlaylistLocation);
} }
} }
}
@FXML @FXML
private void cleanAllRecentlyOpened(){ private void cleanAllRecentlyOpened(){
recentlyOpenedMenu.getItems().remove(0,recentlyOpenedMenu.getItems().size() - 2); recentlyOpenedMenu.getItems().remove(0,recentlyOpenedMenu.getItems().size() - 2);
@ -223,15 +252,15 @@ public class Controller implements Initializable {
/* PLAYER */ /* PLAYER */
@FXML @FXML
private void subsTriggerBtn(){ private void subsTriggerBtn(){
mplayer.subtitlesSwitch(); player.subtitlesSwitch();
} }
@FXML @FXML
private void fullscreenBtn(){ private void fullscreenBtn(){
mplayer.fullscreenSwitch(); player.fullscreenSwitch();
} }
@FXML @FXML
private void muteBtn(){ private void muteBtn(){
mplayer.mute(); player.mute();
} }
@FXML @FXML
private void playPrevTrackBtn(){ private void playPrevTrackBtn(){
@ -240,7 +269,7 @@ public class Controller implements Initializable {
return; return;
mkvPaneController.setElementSelectedByIndex(index-1); mkvPaneController.setElementSelectedByIndex(index-1);
mplayer.forcePlay(appPreferences.getPath(), player.forcePlay(appPreferences.getPath(),
mkvPaneController.getElementSelected(), mkvPaneController.getElementSelected(),
mkaPaneController.getElementSelected(), mkaPaneController.getElementSelected(),
subPaneController.getElementSelected(), subPaneController.getElementSelected(),
@ -265,7 +294,7 @@ public class Controller implements Initializable {
subPaneController.setElementSelectedByIndex(index + 1); subPaneController.setElementSelectedByIndex(index + 1);
} }
mplayer.forcePlay(appPreferences.getPath(), player.forcePlay(appPreferences.getPath(),
mkvPaneController.getElementSelected(), mkvPaneController.getElementSelected(),
mkaPaneController.getElementSelected(), mkaPaneController.getElementSelected(),
subPaneController.getElementSelected(), subPaneController.getElementSelected(),
@ -279,7 +308,7 @@ public class Controller implements Initializable {
if (mkvPaneController.getElementSelected() == null) if (mkvPaneController.getElementSelected() == null)
return; return;
mplayer.playPause(appPreferences.getPath(), player.playPause(appPreferences.getPath(),
mkvPaneController.getElementSelected(), mkvPaneController.getElementSelected(),
mkaPaneController.getElementSelected(), mkaPaneController.getElementSelected(),
subPaneController.getElementSelected(), subPaneController.getElementSelected(),
@ -290,15 +319,15 @@ public class Controller implements Initializable {
} }
@FXML @FXML
private void stopBtn(){ private void stopBtn(){
mplayer.stop(); player.stop();
} }
@FXML @FXML
private void volumeUpBtn(){ private void volumeUpBtn(){
mplayer.volumeUp(); player.volumeUp();
} }
@FXML @FXML
private void volumeDownBtn(){ private void volumeDownBtn(){
mplayer.volumeDown(); player.volumeDown();
} }
} }

View file

@ -1,6 +0,0 @@
package mplayer4anime;
public interface IMediatorContol {
void registerMainController(mplayer4anime.Controller mc);
void sentUpdates();
}

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.IPC; package mplayer4anime.IPC;
import javafx.application.Platform; import javafx.application.Platform;
@ -11,8 +29,8 @@ import java.net.Socket;
class ServerSocketProvider implements Runnable{ class ServerSocketProvider implements Runnable{
private ServerSocket serverSocket; private final ServerSocket serverSocket;
private Controller controller; private final Controller controller;
ServerSocketProvider(Controller mainCntrl, ServerSocket srvSock){ ServerSocketProvider(Controller mainCntrl, ServerSocket srvSock){
this.serverSocket = srvSock; this.serverSocket = srvSock;
@ -42,6 +60,7 @@ class ServerSocketProvider implements Runnable{
} }
} }
catch (IOException ex){ catch (IOException ex){
ex.printStackTrace();
System.out.println("Socket has been closed."); System.out.println("Socket has been closed.");
} }
} }

View file

@ -7,6 +7,7 @@ import java.net.InetAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
// TODO: Rewrite and remove. Or just remove.
public class SingleInstanceHandler implements Runnable{ public class SingleInstanceHandler implements Runnable{
private ServerSocket servSock; private ServerSocket servSock;
@ -41,15 +42,9 @@ public class SingleInstanceHandler implements Runnable{
@Override @Override
public void run() { public void run() {
while (true) { while (! Thread.currentThread().isInterrupted());
if ( Thread.currentThread().isInterrupted() ){
try { try {
servSock.close(); servSock.close();
} catch (IOException e) { } catch (IOException ignore) {}
System.out.println("Internal issue: unable to create client socket.");
}
break;
}
}
} }
} }

View file

@ -19,5 +19,25 @@
package mplayer4anime; package mplayer4anime;
public interface ISlaveModeAppOrchestration { public interface ISlaveModeAppOrchestration {
// TODO: implement unified interface for mplayer and mpv void subtitlesSwitch();
void fullscreenSwitch();
void mute();
void forcePlay(String mplayerPath,
String VideoFile,
String AudioFile,
String SubtitlesFile,
String subtitlesEncoding,
boolean subtitlesHidden,
boolean isFullscreen);
boolean pause();
void playPause(String mplayerPath,
String VideoFile,
String AudioFile,
String SubtitlesFile,
String subtitlesEncoding,
boolean subtitlesHidden,
boolean isFullscreen);
void stop();
void volumeUp();
void volumeDown();
} }

View file

@ -1,14 +1,12 @@
package mplayer4anime; package mplayer4anime;
public class MediatorControl implements IMediatorContol{ public class MediatorControl{
private Controller mainController; private Controller mainController;
@Override
public void registerMainController(Controller mc) { public void registerMainController(Controller mc) {
mainController = mc; mainController = mc;
} }
@Override
public void sentUpdates() { public void sentUpdates() {
mainController.updateAfterSettingsChanged(); mainController.updateAfterSettingsChanged();
} }

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.Playlists; package mplayer4anime.Playlists;
import com.google.gson.*; import com.google.gson.*;
@ -9,12 +27,10 @@ import java.nio.charset.StandardCharsets;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class Playlists { public class Playlists {
private static String playlistLocation; private static String playlistLocation;
//TODO: Show popUp if unable to write! Or nothing to write! Or overwrite! //TODO: Show popUp if unable to write! Or nothing to write! Or overwrite!
/** //TODO: Disable 'Save' button if no files added
* Interface for Save As functionality
* */
public static boolean SaveAs(ResourceBundle resourceBundle, JsonStorage jStorage){ public static boolean SaveAs(ResourceBundle resourceBundle, JsonStorage jStorage){
File playlistFile; File playlistFile;
FileChooser fileChooser = new FileChooser(); FileChooser fileChooser = new FileChooser();
@ -27,49 +43,51 @@ public class Playlists {
return writeFile(resourceBundle, playlistFile, jStorage); return writeFile(resourceBundle, playlistFile, jStorage);
} }
/**
* Interface for Save functionality
* */
public static boolean SaveCurrent(ResourceBundle resourceBundle, JsonStorage jStorage) { public static boolean SaveCurrent(ResourceBundle resourceBundle, JsonStorage jStorage) {
if (playlistLocation == null || playlistLocation.equals("")){ if (playlistLocation == null || playlistLocation.equals("")){
return Playlists.SaveAs(resourceBundle, jStorage); return Playlists.SaveAs(resourceBundle, jStorage);
} }
else {
return writeFile(resourceBundle, new File(playlistLocation), jStorage); return writeFile(resourceBundle, new File(playlistLocation), jStorage);
} }
/**
* @return true for success, false for failure
* */
private static boolean writeFile(ResourceBundle resourceBundle, File playlistFile, JsonStorage jStorage){
if (playlistFile == null) {
ServiceWindow.getErrorNotification(resourceBundle.getString("Error"),
"Unable to save: File not selected");// TODO: translate
return false;
} }
// Working with file itself
private static boolean writeFile(ResourceBundle resourceBundle, File playlistFile, JsonStorage jStorage){
// TODO: Add 'Override pop-up notification!'
if (playlistFile != null) {
if (!playlistFile.getAbsolutePath().endsWith(".alpr")) { if (!playlistFile.getAbsolutePath().endsWith(".alpr")) {
playlistFile = new File(playlistFile.getAbsolutePath() + ".alpr"); playlistFile = new File(playlistFile.getAbsolutePath() + ".alpr");
} }
try (Writer writer = new OutputStreamWriter(new FileOutputStream(playlistFile.getAbsolutePath()), StandardCharsets.UTF_8))
try (Writer writer = new OutputStreamWriter(
new FileOutputStream(playlistFile.getAbsolutePath()), StandardCharsets.UTF_8))
{ {
Gson jsonObject = new GsonBuilder().setPrettyPrinting().create(); Gson jsonObject = new GsonBuilder().setPrettyPrinting().create();
jsonObject.toJson(jStorage, writer); jsonObject.toJson(jStorage, writer);
writer.close(); writer.close();
playlistLocation = playlistFile.getAbsolutePath(); playlistLocation = playlistFile.getAbsolutePath();
// Return success notification
return true; return true;
} catch (java.io.FileNotFoundException e){ } catch (java.io.FileNotFoundException e){
ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorFileNotFound")); ServiceWindow.getErrorNotification(resourceBundle.getString("Error"),
resourceBundle.getString("ErrorFileNotFound"));
} catch (java.io.UnsupportedEncodingException e){ } catch (java.io.UnsupportedEncodingException e){
ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorOnSaveIncorrectEncoding")); ServiceWindow.getErrorNotification(resourceBundle.getString("Error"),
resourceBundle.getString("ErrorOnSaveIncorrectEncoding"));
} catch (java.io.IOException e){ } catch (java.io.IOException e){
ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorOnSaveIOProblem")); ServiceWindow.getErrorNotification(resourceBundle.getString("Error"),
resourceBundle.getString("ErrorOnSaveIOProblem"));
} }
return false; return false;
} }
else {
System.out.println("Unable to save: File not selected");
return false;
}
}
/** /**
* Interface for Opening playlists via FileChooser * Interface for Opening playlists via FileChooser
* */ * */
@ -89,30 +107,30 @@ public class Playlists {
* Interface for Opening playlists using file itself * Interface for Opening playlists using file itself
* */ * */
public static JsonStorage ReadByPath(ResourceBundle resourceBundle, File playlistFile){ public static JsonStorage ReadByPath(ResourceBundle resourceBundle, File playlistFile){
if (playlistFile != null) { if (playlistFile == null) {
ServiceWindow.getErrorNotification(resourceBundle.getString("Error"),
"Playlist file not selected");// TODO: translate
return null;
}
try (Reader reader = new InputStreamReader(new FileInputStream(playlistFile))) { try (Reader reader = new InputStreamReader(new FileInputStream(playlistFile))) {
JsonStorage jStorage = new Gson().fromJson(reader, JsonStorage.class); JsonStorage jStorage = new Gson().fromJson(reader, JsonStorage.class);
if (jStorage != null){ if (jStorage != null){
playlistLocation = playlistFile.getAbsolutePath(); playlistLocation = playlistFile.getAbsolutePath();
//System.out.println("FILE:|"+playlistLocation+"|");
return jStorage; return jStorage;
} }
else
return null;
} catch (java.io.FileNotFoundException e){ } catch (java.io.FileNotFoundException e){
ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorFileNotFound")); ServiceWindow.getErrorNotification(resourceBundle.getString("Error"),
resourceBundle.getString("ErrorFileNotFound"));
} catch (com.google.gson.JsonSyntaxException e){ } catch (com.google.gson.JsonSyntaxException e){
ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorOnOpenIncorrectFormatOfFile")); ServiceWindow.getErrorNotification(resourceBundle.getString("Error"),
resourceBundle.getString("ErrorOnOpenIncorrectFormatOfFile"));
} catch (java.io.IOException e){ } catch (java.io.IOException e){
ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorOnOpenIOProblem")); ServiceWindow.getErrorNotification(resourceBundle.getString("Error"),
resourceBundle.getString("ErrorOnOpenIOProblem"));
} }
return null; return null;
} }
else {
System.out.println("Playlist file not selected");
return null;
}
}
/** Return path to file opened */ /** Return path to file opened */
public static String getPlaylistLocation(){ public static String getPlaylistLocation(){

View file

@ -20,6 +20,7 @@ package mplayer4anime;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.stage.Stage;
/** /**
* Creates window with notification * Creates window with notification
@ -30,7 +31,13 @@ public class ServiceWindow {
alertBox.setTitle(title); alertBox.setTitle(title);
alertBox.setHeaderText(null); alertBox.setHeaderText(null);
alertBox.setContentText(body); alertBox.setContentText(body);
alertBox.getDialogPane().setMinHeight(Region.USE_PREF_SIZE); // Java bug workaround for linux alertBox.getDialogPane().setMinWidth(Region.USE_PREF_SIZE);
alertBox.getDialogPane().setMinHeight(Region.USE_PREF_SIZE);
alertBox.setResizable(true);
alertBox.show(); alertBox.show();
Stage dialogStage = (Stage) alertBox.getDialogPane().getScene().getWindow();
dialogStage.setAlwaysOnTop(true);
dialogStage.toFront();
} }
} }

View file

@ -42,6 +42,8 @@ public class SettingsController implements Initializable {
private Label pathToMplayerLbl; private Label pathToMplayerLbl;
@FXML @FXML
private CheckBox subtitlesFirstCheckBox; private CheckBox subtitlesFirstCheckBox;
@FXML
private ChoiceBox<String> backEndEngineChoiceBox;
@Override @Override
public void initialize(URL url, ResourceBundle resBundle) { public void initialize(URL url, ResourceBundle resBundle) {
@ -55,6 +57,8 @@ public class SettingsController implements Initializable {
subEncodingListController.setList(appPreferences.getSubsEncodingList(), false); subEncodingListController.setList(appPreferences.getSubsEncodingList(), false);
videoExtensionListController.setList(appPreferences.getVideoExtensionsList(), true); videoExtensionListController.setList(appPreferences.getVideoExtensionsList(), true);
audioExtensionListController.setList(appPreferences.getAudioExtensionsList(), true); audioExtensionListController.setList(appPreferences.getAudioExtensionsList(), true);
backEndEngineChoiceBox.getItems().addAll("mplayer", "mpv");
backEndEngineChoiceBox.getSelectionModel().select(appPreferences.getBackendEngineIndexId());
} }
@FXML @FXML
@ -103,6 +107,7 @@ public class SettingsController implements Initializable {
appPreferences.setSubsEncodingList(subEncodingListController.getList()); appPreferences.setSubsEncodingList(subEncodingListController.getList());
appPreferences.setVideoExtensionsList(videoExtensionListController.getList()); appPreferences.setVideoExtensionsList(videoExtensionListController.getList());
appPreferences.setAudioExtensionsList(audioExtensionListController.getList()); appPreferences.setAudioExtensionsList(audioExtensionListController.getList());
appPreferences.setBackendEngineIndexId(backEndEngineChoiceBox.getSelectionModel().getSelectedIndex());
MediatorControl.getInstance().sentUpdates(); // TODO: implement list to track what should be updated MediatorControl.getInstance().sentUpdates(); // TODO: implement list to track what should be updated
} }

View file

@ -238,6 +238,7 @@ public class ControllerPane implements Initializable {
* Set files using lists. Used if playlist loaded * Set files using lists. Used if playlist loaded
* */ * */
public void setFilesFromList(String[] fileLocations){ public void setFilesFromList(String[] fileLocations){
cleanList();
if (fileLocations != null && fileLocations.length != 0) { if (fileLocations != null && fileLocations.length != 0) {
File[] files = new File[fileLocations.length]; File[] files = new File[fileLocations.length];
for (int i=0; i < fileLocations.length; i++) for (int i=0; i < fileLocations.length; i++)

View file

@ -16,7 +16,7 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with mplayer4anime. If not, see <https://www.gnu.org/licenses/>. along with mplayer4anime. If not, see <https://www.gnu.org/licenses/>.
*/ */
package mplayer4anime; package mplayer4anime.mplayer;
import java.io.*; import java.io.*;

View file

@ -16,7 +16,10 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with mplayer4anime. If not, see <https://www.gnu.org/licenses/>. along with mplayer4anime. If not, see <https://www.gnu.org/licenses/>.
*/ */
package mplayer4anime; package mplayer4anime.mplayer;
import mplayer4anime.ISlaveModeAppOrchestration;
import mplayer4anime.ServiceWindow;
import java.io.*; import java.io.*;
import java.util.ResourceBundle; import java.util.ResourceBundle;
@ -42,7 +45,7 @@ public class MplayerSlave implements ISlaveModeAppOrchestration {
} }
return false; return false;
} }
@Override
public void subtitlesSwitch(){ public void subtitlesSwitch(){
if (! playerSingleCommand("get_sub_visibility")) if (! playerSingleCommand("get_sub_visibility"))
return; return;
@ -66,15 +69,15 @@ public class MplayerSlave implements ISlaveModeAppOrchestration {
System.out.println("Can't determine whether subtitles enabled or disabled"); System.out.println("Can't determine whether subtitles enabled or disabled");
} }
} }
@Override
public void fullscreenSwitch(){ public void fullscreenSwitch(){
playerSingleCommand("vo_fullscreen"); playerSingleCommand("vo_fullscreen");
} }
@Override
public void mute(){ public void mute(){
playerSingleCommand("mute"); playerSingleCommand("mute");
} }
@Override
public void forcePlay(String mplayerPath, public void forcePlay(String mplayerPath,
String VideoFile, String VideoFile,
String AudioFile, String AudioFile,
@ -94,7 +97,7 @@ public class MplayerSlave implements ISlaveModeAppOrchestration {
e.printStackTrace(); e.printStackTrace();
} }
} }
@Override
public boolean pause(){ public boolean pause(){
if (player == null || !player.isAlive()) if (player == null || !player.isAlive())
return false; return false;
@ -103,7 +106,7 @@ public class MplayerSlave implements ISlaveModeAppOrchestration {
playerIn.flush(); playerIn.flush();
return true; return true;
} }
@Override
public void playPause(String mplayerPath, public void playPause(String mplayerPath,
String VideoFile, String VideoFile,
String AudioFile, String AudioFile,
@ -155,15 +158,15 @@ public class MplayerSlave implements ISlaveModeAppOrchestration {
ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorUnableToStartMplayer")); ServiceWindow.getErrorNotification(resourceBundle.getString("Error"), resourceBundle.getString("ErrorUnableToStartMplayer"));
} }
} }
@Override
public void stop(){ public void stop(){
playerSingleCommand("stop"); playerSingleCommand("stop");
} }
@Override
public void volumeUp(){ public void volumeUp(){
playerSingleCommand("volume +1 0"); playerSingleCommand("volume +1 0");
} }
@Override
public void volumeDown(){ public void volumeDown(){
playerSingleCommand("volume -1 0"); playerSingleCommand("volume -1 0");
} }

View file

@ -0,0 +1,108 @@
/*
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.mpv;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
public final class MpvJniLibraryLoader {
private MpvJniLibraryLoader(){}
public static boolean load(){
String osName = System.getProperty("os.name").toLowerCase().replace(" ", "");
String osArch = System.getProperty("os.arch").toLowerCase().replace(" ", "");
String libPostfix = "so";
if (osName.equals("linux")){
switch (osArch){
case "i386":
case "i586":
case "i686":
osArch = "x86";
break;
case "x86_64":
case "amd64":
osArch = "amd64";
break;
// case "arm":
// osArch = "arm";
// break;
default:
return false;
}
}
else
return false;
final URL url_ = MpvJniLibraryLoader.class.getResource("/native/"+osName+"/"+osArch+"/mpvjni."+libPostfix);
if (url_ == null)
return false;
String proto = url_.getProtocol();
File libraryFile;
if (proto.equals("file")){
// We can pick file from disk as is.
try {
libraryFile = new File(url_.toURI());
}
catch (URISyntaxException e){
e.printStackTrace();
return false;
}
}
else if (proto.equals("jar")){
// We have to export file to temp dir.
InputStream inStream = MpvJniLibraryLoader.class.getResourceAsStream("/native/"+osName+"/"+osArch+"/mpvjni."+libPostfix);
if (inStream == null)
return false;
// Create temp folder
try{
File tmpDirFile = File.createTempFile("jni", null);
if (! tmpDirFile.delete())
return false;
if (! tmpDirFile.mkdirs())
return false;
libraryFile = new File(tmpDirFile, "mpvjni."+libPostfix);
byte[] ioBuffer = new byte[8192];
FileOutputStream foStream = new FileOutputStream(libraryFile);
while (inStream.read(ioBuffer) != -1)
foStream.write(ioBuffer);
foStream.close();
inStream.close();
libraryFile.deleteOnExit();
tmpDirFile.deleteOnExit();
}
catch (IOException ioe){
ioe.printStackTrace();
return false;
}
}
else
return false;
//System.out.println("LIB LOCATION: "+libraryFile);
System.load(libraryFile.getAbsolutePath());
//System.out.println("LIB LOADED");
return true;
}
}

View file

@ -0,0 +1,96 @@
/*
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.mpv;
import mplayer4anime.ISlaveModeAppOrchestration;
import mplayer4anime.ServiceWindow;
import java.util.ResourceBundle;
public class MpvSlave implements ISlaveModeAppOrchestration {
static {
if (! MpvJniLibraryLoader.load()){
ServiceWindow.getErrorNotification("Error",
"Unable to load mpv back end library. Please use mplayer instead"); // TODO: use bundle & translate
}
}
native void play();
public MpvSlave(ResourceBundle resourceBundle){
}
@Override
public void subtitlesSwitch() {
}
@Override
public void fullscreenSwitch() {
}
@Override
public void mute() {
}
@Override
public void forcePlay(String mplayerPath,
String VideoFile,
String AudioFile,
String SubtitlesFile,
String subtitlesEncoding,
boolean subtitlesHidden,
boolean isFullscreen) {
}
@Override
public boolean pause() {
return false;
}
@Override
public void playPause(String mplayerPath,
String VideoFile,
String AudioFile,
String SubtitlesFile,
String subtitlesEncoding,
boolean subtitlesHidden,
boolean isFullscreen) {
}
@Override
public void stop() {
}
@Override
public void volumeUp() {
}
@Override
public void volumeDown() {
}
}

View file

@ -3,6 +3,7 @@
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?> <?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tab?> <?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?> <?import javafx.scene.control.TabPane?>
@ -42,6 +43,12 @@
<Font name="System Italic" size="12.0" /> <Font name="System Italic" size="12.0" />
</font> </font>
</Label> </Label>
<HBox alignment="CENTER_LEFT" spacing="5.0">
<children>
<Label text="%settings_backendSelect" />
<ChoiceBox fx:id="backEndEngineChoiceBox" prefWidth="150.0" />
</children>
</HBox>
<HBox alignment="CENTER_LEFT" VBox.vgrow="NEVER"> <HBox alignment="CENTER_LEFT" VBox.vgrow="NEVER">
<children> <children>
<CheckBox fx:id="subtitlesFirstCheckBox" mnemonicParsing="false" text="%settings_SubtitlesTabFirst" /> <CheckBox fx:id="subtitlesFirstCheckBox" mnemonicParsing="false" text="%settings_SubtitlesTabFirst" />

View file

@ -53,6 +53,7 @@ settings_Tab_Subtitles=Subtitles
settings_videoExtensionList=Avaliable video files extensions: settings_videoExtensionList=Avaliable video files extensions:
settings_audioExtensionList=Avaliable audio layer extensions: settings_audioExtensionList=Avaliable audio layer extensions:
ApplyBtn=Apply ApplyBtn=Apply
settings_backendSelect=Backend engine:

View file

@ -53,4 +53,5 @@ settings_Tab_Video=\u0412\u0438\u0434\u0435\u043E
settings_videoExtensionList=\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u0434\u043B\u044F \u0444\u0430\u0439\u043B\u043E\u0432 \u0432\u0438\u0434\u0435\u043E: settings_videoExtensionList=\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u0434\u043B\u044F \u0444\u0430\u0439\u043B\u043E\u0432 \u0432\u0438\u0434\u0435\u043E:
settings_audioExtensionList=\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u0434\u043B\u044F \u0430\u0443\u0434\u0438\u043E\u0434\u043E\u0440\u043E\u0436\u0435\u043A: settings_audioExtensionList=\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u044B\u0435 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F \u0434\u043B\u044F \u0430\u0443\u0434\u0438\u043E\u0434\u043E\u0440\u043E\u0436\u0435\u043A:
ApplyBtn=\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C ApplyBtn=\u041F\u0440\u0438\u043C\u0435\u043D\u0438\u0442\u044C
settings_backendSelect=\u0414\u0432\u0438\u0436\u043E\u043A \u0432\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0435\u0434\u0435\u043D\u0438\u044F:

Binary file not shown.

Binary file not shown.