309 lines
9.1 KiB
Java
309 lines
9.1 KiB
Java
/**
|
|
* Project-HomeFlix
|
|
*
|
|
* Copyright 2016-2019 <@Seil0>
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
package kellerkinder.HomeFlix.player;
|
|
|
|
import java.io.File;
|
|
import java.util.Timer;
|
|
import java.util.TimerTask;
|
|
|
|
import com.jfoenix.controls.JFXButton;
|
|
import com.jfoenix.controls.JFXSlider;
|
|
|
|
import javafx.beans.binding.Bindings;
|
|
import javafx.beans.property.DoubleProperty;
|
|
import javafx.beans.value.ChangeListener;
|
|
import javafx.beans.value.ObservableValue;
|
|
import javafx.event.ActionEvent;
|
|
import javafx.event.EventHandler;
|
|
import javafx.fxml.FXML;
|
|
import javafx.scene.Cursor;
|
|
import javafx.scene.image.Image;
|
|
import javafx.scene.image.ImageView;
|
|
import javafx.scene.input.MouseEvent;
|
|
import javafx.scene.layout.HBox;
|
|
import javafx.scene.layout.VBox;
|
|
import javafx.scene.media.Media;
|
|
import javafx.scene.media.MediaPlayer;
|
|
import javafx.scene.media.MediaPlayer.Status;
|
|
import javafx.scene.media.MediaView;
|
|
import javafx.util.Duration;
|
|
import kellerkinder.HomeFlix.controller.DBController;
|
|
import kellerkinder.HomeFlix.controller.XMLController;
|
|
import kellerkinder.HomeFlix.datatypes.FilmTabelDataType;
|
|
|
|
public class PlayerController {
|
|
|
|
@FXML private MediaView mediaView;
|
|
|
|
@FXML private VBox bottomVBox;
|
|
|
|
@FXML private HBox controlsHBox;
|
|
|
|
@FXML private JFXSlider timeSlider;
|
|
|
|
@FXML private JFXButton stopBtn;
|
|
@FXML private JFXButton playBtn;
|
|
@FXML private JFXButton fullscreenBtn;
|
|
@FXML private JFXButton nextEpBtn;
|
|
|
|
private Player player;
|
|
private Media media;
|
|
private MediaPlayer mediaPlayer;
|
|
|
|
private FilmTabelDataType film;
|
|
private double currentTime = 0;
|
|
private double seekTime = 0;
|
|
private double startTime = 0;
|
|
private double duration = 0;
|
|
private int season = 0;
|
|
private int episode = 0;
|
|
private int countdown = 0;
|
|
private boolean mousePressed = false;
|
|
private boolean showControls = true;
|
|
private boolean autoplay;
|
|
|
|
private ImageView stop_black = new ImageView(new Image("icons/ic_stop_black_24dp_1x.png"));
|
|
private ImageView play_arrow_black = new ImageView(new Image("icons/ic_play_arrow_black_24dp_1x.png"));
|
|
private ImageView pause_black = new ImageView(new Image("icons/ic_pause_black_24dp_1x.png"));
|
|
private ImageView fullscreen_black = new ImageView(new Image("icons/ic_fullscreen_black_24dp_1x.png"));
|
|
private ImageView fullscreen_exit_black = new ImageView(new Image("icons/ic_fullscreen_exit_black_24dp_1x.png"));
|
|
|
|
/**
|
|
* create a new PlayerWindow object
|
|
* @param mainWCon the MainWindowController TODO do we need this?
|
|
* @param player the player object (needed for closing action)
|
|
* @param film the film object
|
|
*/
|
|
public PlayerController(Player player, String streamURL) {
|
|
this.player = player;
|
|
this.film = DBController.getInstance().getStream(streamURL);
|
|
}
|
|
|
|
/**
|
|
* initialize the PlayerWindow
|
|
*/
|
|
public void init() {
|
|
initActions();
|
|
|
|
if (film.getStreamUrl().startsWith("http")) {
|
|
media = new Media(film.getStreamUrl());
|
|
} else {
|
|
media = new Media(new File(film.getStreamUrl()).toURI().toString());
|
|
}
|
|
|
|
// create the MediaPlayer object
|
|
mediaPlayer = new MediaPlayer(media);
|
|
mediaView.setPreserveRatio(true);
|
|
mediaView.setMediaPlayer(mediaPlayer);
|
|
|
|
final DoubleProperty width = mediaView.fitWidthProperty();
|
|
final DoubleProperty height = mediaView.fitHeightProperty();
|
|
|
|
width.bind(Bindings.selectDouble(mediaView.sceneProperty(), "width"));
|
|
height.bind(Bindings.selectDouble(mediaView.sceneProperty(), "height"));
|
|
|
|
startTime = DBController.getInstance().getCurrentTime(film.getStreamUrl());
|
|
autoplay = XMLController.isAutoplay();
|
|
season = !film.getSeason().isEmpty() ? Integer.parseInt(film.getSeason()) : 0;
|
|
episode = !film.getEpisode().isEmpty() ? Integer.parseInt(film.getEpisode()) : 0;
|
|
|
|
initMediaPlayer();
|
|
|
|
// set the control elements to the correct value
|
|
stopBtn.setGraphic(stop_black);
|
|
playBtn.setGraphic(pause_black);
|
|
fullscreenBtn.setGraphic(fullscreen_exit_black);
|
|
timeSlider.setValue(0);
|
|
}
|
|
|
|
private void initMediaPlayer() {
|
|
// start the media if the player is ready
|
|
mediaPlayer.setOnReady(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
duration = media.getDuration().toMillis();
|
|
timeSlider.setMax((duration / 1000) / 60);
|
|
|
|
mediaPlayer.play();
|
|
mediaPlayer.seek(Duration.millis(startTime));
|
|
}
|
|
});
|
|
|
|
// every time the play time changes execute this
|
|
mediaPlayer.currentTimeProperty().addListener(new ChangeListener<Duration>() {
|
|
@Override
|
|
public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
|
|
currentTime = newValue.toMillis(); // set the current time
|
|
double timeToEnd = (duration - currentTime);
|
|
|
|
if (timeToEnd < 20000 && episode != 0 && autoplay) {
|
|
// show 20 seconds before the end a button (next episode in 10 seconds)
|
|
if (!nextEpBtn.isVisible())
|
|
nextEpBtn.setVisible(true);
|
|
|
|
if (countdown != (int) ((timeToEnd - 10000) / 1000)) {
|
|
countdown = (int) ((timeToEnd - 10000) / 1000);
|
|
nextEpBtn.setText("next episode in " + countdown + " seconds"); // TODO translate
|
|
bottomVBox.setVisible(true);
|
|
}
|
|
|
|
// if we are end time -10 seconds, do autoplay, if activated
|
|
if (timeToEnd < 10000) {
|
|
nextEpBtn.setVisible(false);
|
|
autoPlayNewFilm();
|
|
}
|
|
} else if (timeToEnd < 120) {
|
|
// if we are 120ms to the end stop the media
|
|
mediaPlayer.stop();
|
|
DBController.getInstance().setCurrentTime(film.getStreamUrl(), 0); // reset old video start time
|
|
playBtn.setGraphic(play_arrow_black);
|
|
} else {
|
|
if (nextEpBtn.isVisible())
|
|
nextEpBtn.setVisible(false);
|
|
}
|
|
|
|
if (!mousePressed) {
|
|
timeSlider.setValue((currentTime / 1000) / 60);
|
|
}
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
* initialize some PlayerWindow GUI-Elements actions
|
|
*/
|
|
private void initActions() {
|
|
|
|
player.getScene().addEventFilter(MouseEvent.MOUSE_MOVED, new EventHandler<MouseEvent>() {
|
|
// hide controls timer initialization
|
|
final Timer timer = new Timer();
|
|
TimerTask controlAnimationTask = null; // task to execute save operation
|
|
final long delayTime = 3000; // hide the controls after 2 seconds
|
|
|
|
@Override
|
|
public void handle(MouseEvent mouseEvent) {
|
|
|
|
// show controls
|
|
if (!showControls) {
|
|
player.getScene().setCursor(Cursor.DEFAULT);
|
|
bottomVBox.setVisible(true);
|
|
}
|
|
|
|
// hide controls
|
|
if (controlAnimationTask != null)
|
|
controlAnimationTask.cancel();
|
|
|
|
controlAnimationTask = new TimerTask() {
|
|
@Override
|
|
public void run() {
|
|
bottomVBox.setVisible(false);
|
|
player.getScene().setCursor(Cursor.NONE);
|
|
showControls = false;
|
|
}
|
|
};
|
|
timer.schedule(controlAnimationTask, delayTime);
|
|
}
|
|
});
|
|
|
|
// if the mouse on the timeSlider is released seek to the new position
|
|
timeSlider.setOnMouseReleased(new EventHandler<MouseEvent>() {
|
|
@Override
|
|
public void handle(MouseEvent event) {
|
|
mediaPlayer.seek(new Duration(seekTime));
|
|
mousePressed = false;
|
|
}
|
|
});
|
|
|
|
timeSlider.setOnMousePressed(new EventHandler<MouseEvent>() {
|
|
@Override
|
|
public void handle(MouseEvent event) {
|
|
mousePressed = true;
|
|
}
|
|
});
|
|
|
|
// get the new seek time
|
|
timeSlider.valueProperty().addListener(new ChangeListener<Number>() {
|
|
@Override
|
|
public void changed(ObservableValue<? extends Number> ov, Number old_val, Number new_val) {
|
|
seekTime = (double) new_val * 1000 * 60;
|
|
}
|
|
});
|
|
}
|
|
|
|
@FXML
|
|
void stopBtnAction(ActionEvent event) {
|
|
DBController.getInstance().setCurrentTime(film.getStreamUrl(), currentTime);
|
|
mediaPlayer.stop();
|
|
player.getStage().close();
|
|
}
|
|
|
|
@FXML
|
|
void fullscreenBtnAction(ActionEvent event) {
|
|
if (player.getStage().isFullScreen()) {
|
|
player.getStage().setFullScreen(false);
|
|
fullscreenBtn.setGraphic(fullscreen_black);
|
|
} else {
|
|
player.getStage().setFullScreen(true);
|
|
fullscreenBtn.setGraphic(fullscreen_exit_black);
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
void playBtnAction(ActionEvent event) {
|
|
if (mediaPlayer.getStatus().equals(Status.PLAYING)) {
|
|
mediaPlayer.pause();
|
|
playBtn.setGraphic(play_arrow_black);
|
|
} else {
|
|
mediaPlayer.play();
|
|
playBtn.setGraphic(pause_black);
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
void nextEpBtnAction(ActionEvent event) {
|
|
autoPlayNewFilm();
|
|
}
|
|
|
|
private void autoPlayNewFilm() {
|
|
autoplay = false;
|
|
DBController.getInstance().setCurrentTime(film.getStreamUrl(), 0); // reset old video start time
|
|
FilmTabelDataType nextFilm = DBController.getInstance().getNextEpisode(film.getTitle(), episode, season);
|
|
if (nextFilm != null) {
|
|
mediaPlayer.stop();
|
|
film = nextFilm;
|
|
init();
|
|
autoplay = true;
|
|
}
|
|
}
|
|
|
|
public MediaPlayer getMediaPlayer() {
|
|
return mediaPlayer;
|
|
}
|
|
|
|
public double getCurrentTime() {
|
|
return currentTime;
|
|
}
|
|
|
|
}
|