444 lines
14 KiB
Java
444 lines
14 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 org.mosad.homeflix.application;
|
|
|
|
import java.io.File;
|
|
import java.io.FileReader;
|
|
import java.io.FileWriter;
|
|
import java.io.IOException;
|
|
import java.io.Writer;
|
|
import java.math.BigInteger;
|
|
import java.time.LocalDate;
|
|
import java.util.Locale;
|
|
import java.util.ResourceBundle;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
import org.kellerkinder.Alerts.JFX2BtnCancelAlert;
|
|
import org.kellerkinder.Alerts.JFXInfoAlert;
|
|
import org.mosad.homeflix.controller.DBController;
|
|
import org.mosad.homeflix.controller.OMDbAPIController;
|
|
import org.mosad.homeflix.controller.UpdateController;
|
|
import org.mosad.homeflix.controller.XMLController;
|
|
import org.mosad.homeflix.datatypes.FilmTabelDataType;
|
|
import org.mosad.homeflix.datatypes.PosterModeElement;
|
|
|
|
import com.eclipsesource.json.Json;
|
|
import com.eclipsesource.json.JsonArray;
|
|
import com.eclipsesource.json.JsonObject;
|
|
import com.jfoenix.controls.JFXButton;
|
|
import com.jfoenix.controls.JFXHamburger;
|
|
import com.jfoenix.transitions.hamburger.HamburgerBackArrowBasicTransition;
|
|
|
|
import javafx.animation.TranslateTransition;
|
|
import javafx.collections.FXCollections;
|
|
import javafx.collections.ObservableList;
|
|
import javafx.fxml.FXML;
|
|
import javafx.scene.control.ScrollPane;
|
|
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
|
|
import javafx.scene.effect.BoxBlur;
|
|
import javafx.scene.input.MouseEvent;
|
|
import javafx.scene.layout.AnchorPane;
|
|
import javafx.scene.layout.FlowPane;
|
|
import javafx.scene.layout.HBox;
|
|
import javafx.scene.layout.VBox;
|
|
import javafx.stage.DirectoryChooser;
|
|
import javafx.stage.FileChooser;
|
|
import javafx.stage.Stage;
|
|
import javafx.util.Duration;
|
|
|
|
public class MainWindowController {
|
|
|
|
// general
|
|
@FXML private AnchorPane mainAnchorPane;
|
|
|
|
@FXML private HBox topHBox;
|
|
@FXML private VBox sideMenuVBox;
|
|
|
|
@FXML private JFXHamburger menuHam;
|
|
|
|
@FXML private JFXButton aboutBtn;
|
|
@FXML private JFXButton settingsBtn;
|
|
|
|
// settings
|
|
@FXML private SettingsView settingsViewController;
|
|
|
|
// poster-mode
|
|
@FXML private ScrollPane posterModeScrollPane;
|
|
@FXML private FlowPane posterModeFlowPane;
|
|
|
|
@FXML private FilmDetailView filmDetailViewController;
|
|
@FXML private SeriesDetailView seriesDetailViewController;
|
|
|
|
private static MainWindowController instance = null;
|
|
private DBController dbController;
|
|
private XMLController xmlController;
|
|
private Stage primaryStage;
|
|
private static final Logger LOGGER = LogManager.getLogger(MainWindowController.class.getName());
|
|
|
|
private boolean menuTrue = false;
|
|
|
|
private String btnStyle;
|
|
|
|
private ObservableList<PosterModeElement> posterEmenents = FXCollections.observableArrayList();
|
|
private LocalDate lastValidCache = LocalDate.now().minusDays(30); // current date - 30 days is the last valid cache date
|
|
|
|
public MainWindowController() {
|
|
// the constructor
|
|
}
|
|
|
|
public static MainWindowController getInstance() {
|
|
if (instance == null) {
|
|
LOGGER.error("There was a fatal error: instance is null!");
|
|
instance = new MainWindowController();
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
|
|
public void initialize() {
|
|
instance = this;
|
|
xmlController = new XMLController();
|
|
dbController = DBController.getInstance();
|
|
|
|
if (!new File(XMLController.getDirHomeFlix() + "/sources.json").exists()) {
|
|
XMLController.getDirHomeFlix().mkdir();
|
|
LOGGER.warn("sources file not found");
|
|
|
|
addFirstSource();
|
|
xmlController.saveSettings();
|
|
}
|
|
}
|
|
|
|
public void init() {
|
|
LOGGER.info("Initializing Project-HomeFlix build " + Main.buildNumber);
|
|
|
|
// initialize the GUI and the DBController
|
|
primaryStage = (Stage) mainAnchorPane.getScene().getWindow(); // set primary stage for dialogs
|
|
posterModeScrollPane.setVbarPolicy(ScrollBarPolicy.ALWAYS);
|
|
setLocalUI();
|
|
applyColor(); // TODO only on first start
|
|
|
|
initActions();
|
|
dbController.init();
|
|
|
|
// load data list in gui
|
|
posterModeStartup();
|
|
|
|
checkAutoUpdate(); // TODO async
|
|
}
|
|
|
|
// Initializing the actions
|
|
private void initActions() {
|
|
|
|
// general actions
|
|
HamburgerBackArrowBasicTransition burgerTask = new HamburgerBackArrowBasicTransition(menuHam);
|
|
menuHam.addEventHandler(MouseEvent.MOUSE_PRESSED, (e) -> {
|
|
|
|
if (menuTrue) {
|
|
sideMenuSlideOut();
|
|
burgerTask.setRate(-1.0);
|
|
burgerTask.play();
|
|
menuTrue = false;
|
|
} else {
|
|
sideMenuSlideIn();
|
|
burgerTask.setRate(1.0);
|
|
burgerTask.play();
|
|
menuTrue = true;
|
|
}
|
|
|
|
if (settingsViewController.isVisible()) {
|
|
settingsViewController.setVisible(false);
|
|
}
|
|
});
|
|
}
|
|
|
|
// general fxml actions
|
|
@FXML
|
|
private void aboutBtnAction() {
|
|
String bodyText = "Project HomeFlix \nVersion: " + Main.version + " (Build: " + Main.buildNumber + ") \""
|
|
+ Main.versionName + "\" \n" + XMLController.getLocalBundle().getString("infoText");
|
|
JFXInfoAlert infoAlert = new JFXInfoAlert("Project HomeFlix", bodyText, btnStyle, primaryStage);
|
|
infoAlert.showAndWait();
|
|
}
|
|
|
|
@FXML
|
|
private void settingsBtnclicked() {
|
|
settingsViewController.setVisible(!settingsViewController.isVisible());
|
|
}
|
|
|
|
/**
|
|
* we need to get the path for the first source from the user and add it to
|
|
* sources.json, if the user ends the file-/directory-chooser the program will exit
|
|
*/
|
|
private void addFirstSource() {
|
|
JFX2BtnCancelAlert selectFirstSource = new JFX2BtnCancelAlert(
|
|
XMLController.getLocalBundle().getString("addSourceHeader"),
|
|
XMLController.getLocalBundle().getString("addSourceBody"),
|
|
"-fx-button-type: RAISED; -fx-background-color: #ee3523; -fx-text-fill: BLACK;",
|
|
XMLController.getLocalBundle().getString("addDirectory"),
|
|
XMLController.getLocalBundle().getString("addStreamSource"),
|
|
XMLController.getLocalBundle().getString("cancelBtnText"), primaryStage);
|
|
|
|
// directory action
|
|
selectFirstSource.setBtn1Action(e -> {
|
|
DirectoryChooser directoryChooser = new DirectoryChooser();
|
|
directoryChooser.setTitle(XMLController.getLocalBundle().getString("addDirectory"));
|
|
File selectedFolder = directoryChooser.showDialog(primaryStage);
|
|
if (selectedFolder != null && selectedFolder.exists()) {
|
|
selectFirstSource.getAlert().close();
|
|
writeSource(selectedFolder.getPath(), "local");
|
|
settingsViewController.loadInitSources();
|
|
} else {
|
|
LOGGER.error("The selected folder dosen't exist!");
|
|
System.exit(1);
|
|
}
|
|
});
|
|
|
|
// streaming action
|
|
selectFirstSource.setBtn2Action(e -> {
|
|
FileChooser fileChooser = new FileChooser();
|
|
fileChooser.setTitle(XMLController.getLocalBundle().getString("addStreamSource"));
|
|
File selectedFile = fileChooser.showOpenDialog(primaryStage);
|
|
if (selectedFile != null && selectedFile.exists()) {
|
|
selectFirstSource.getAlert().close();
|
|
writeSource(selectedFile.getPath(), "stream");
|
|
settingsViewController.loadInitSources();
|
|
} else {
|
|
LOGGER.error("The selected file dosen't exist!");
|
|
System.exit(1);
|
|
}
|
|
});
|
|
|
|
selectFirstSource.showAndWait();
|
|
}
|
|
|
|
/**
|
|
* add a source to the sources file
|
|
*
|
|
* @param path to the source
|
|
* @param mode of the source (local or streaming)
|
|
*/
|
|
void writeSource(String path, String mode) {
|
|
JsonArray newsources = null;
|
|
|
|
try {
|
|
// read old array
|
|
File oldSources = new File(XMLController.getDirHomeFlix() + "/sources.json");
|
|
newsources = oldSources.exists() ? Json.parse(new FileReader(XMLController.getDirHomeFlix() + "/sources.json")).asArray() : Json.array();
|
|
|
|
// add new source
|
|
JsonObject source = Json.object().add("path", path).add("mode", mode);
|
|
newsources.add(source);
|
|
Writer writer = new FileWriter(XMLController.getDirHomeFlix() + "/sources.json");
|
|
newsources.writeTo(writer);
|
|
writer.close();
|
|
} catch (IOException e) {
|
|
LOGGER.error("Error while writing sources file!", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* set the color of the GUI-Elements if usedColor is less than checkColor set
|
|
* text fill white, else black
|
|
*/
|
|
void applyColor() {
|
|
String menuBtnStyle;
|
|
BigInteger usedColor = new BigInteger(XMLController.getColor(), 16);
|
|
BigInteger checkColor = new BigInteger("78909cff", 16);
|
|
|
|
menuHam.getStyleClass().clear();
|
|
|
|
if (usedColor.compareTo(checkColor) == -1) {
|
|
btnStyle = "-fx-button-type: RAISED; -fx-background-color: #" + XMLController.getColor() + "; -fx-text-fill: WHITE;";
|
|
menuBtnStyle = "-fx-text-fill: WHITE;";
|
|
menuHam.getStyleClass().add("jfx-hamburgerW");
|
|
} else {
|
|
btnStyle = "-fx-button-type: RAISED; -fx-background-color: #" + XMLController.getColor() + "; -fx-text-fill: BLACK;";
|
|
menuBtnStyle = "-fx-text-fill: BLACK;";
|
|
menuHam.getStyleClass().add("jfx-hamburgerB");
|
|
}
|
|
|
|
// boxes and TextFields
|
|
sideMenuVBox.setStyle("-fx-background-color: #" + XMLController.getColor() + ";");
|
|
topHBox.setStyle("-fx-background-color: #" + XMLController.getColor() + ";");
|
|
|
|
// menu buttons
|
|
settingsBtn.setStyle(menuBtnStyle);
|
|
aboutBtn.setStyle(menuBtnStyle);
|
|
|
|
settingsViewController.updateColor(btnStyle);
|
|
}
|
|
|
|
// slide in in 400ms
|
|
private void sideMenuSlideIn() {
|
|
TranslateTransition translateTransition = new TranslateTransition(Duration.millis(400), sideMenuVBox);
|
|
translateTransition.setFromX(0);
|
|
translateTransition.setToX(150);
|
|
translateTransition.play();
|
|
}
|
|
|
|
// slide out in 400ms
|
|
private void sideMenuSlideOut() {
|
|
TranslateTransition translateTransition = new TranslateTransition(Duration.millis(400), sideMenuVBox);
|
|
translateTransition.setFromX(150);
|
|
translateTransition.setToX(0);
|
|
translateTransition.play();
|
|
}
|
|
|
|
/**
|
|
* set the local based on the languageChoisBox selection
|
|
*/
|
|
void setLocalUI() {
|
|
// TODO switch expressions
|
|
switch (XMLController.getUsrLocal()) {
|
|
case "en_US":
|
|
XMLController.setLocalBundle(ResourceBundle.getBundle("locals.HomeFlix-Local", Locale.US)); // us_English
|
|
break;
|
|
case "de_DE":
|
|
XMLController.setLocalBundle(ResourceBundle.getBundle("locals.HomeFlix-Local", Locale.GERMAN)); // German
|
|
break;
|
|
default:
|
|
XMLController.setLocalBundle(ResourceBundle.getBundle("locals.HomeFlix-Local", Locale.US)); // default local
|
|
break;
|
|
}
|
|
|
|
settingsViewController.updateGUILocal();
|
|
filmDetailViewController.updateGUILocal();
|
|
seriesDetailViewController.updateGUILocal();
|
|
|
|
aboutBtn.setText(XMLController.getLocalBundle().getString("info"));
|
|
settingsBtn.setText(XMLController.getLocalBundle().getString("settings"));
|
|
}
|
|
|
|
// if AutoUpdate, then check for updates
|
|
private void checkAutoUpdate() {
|
|
LOGGER.info("AutoUpdate: looking for updates on startup ...");
|
|
|
|
if (XMLController.isAutoUpdate() && UpdateController.isUpdateAvailable()) {
|
|
UpdateController.update();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Poser Mode
|
|
*/
|
|
|
|
private void posterModeStartup() {
|
|
checkAllPosters();
|
|
addAllPosters();
|
|
checkCache();
|
|
}
|
|
|
|
/**
|
|
* check if all posters are cached, if not cache the missing ones
|
|
*/
|
|
void checkAllPosters() {
|
|
ExecutorService executor = Executors.newFixedThreadPool(5);
|
|
|
|
dbController.refreshDataBase(); // refreshes the database after a source path was added
|
|
|
|
// get all not cached entries
|
|
for (FilmTabelDataType entry : dbController.getAllNotCachedEntries()) {
|
|
Runnable OMDbAPIWorker = new OMDbAPIController(entry);
|
|
executor.execute(OMDbAPIWorker);
|
|
}
|
|
executor.shutdown();
|
|
|
|
// TODO show loading screen
|
|
|
|
// wait for all OMDbAPI requests to finish
|
|
try {
|
|
executor.awaitTermination(1, TimeUnit.MINUTES);
|
|
} catch (InterruptedException e) {
|
|
LOGGER.error(e);
|
|
}
|
|
|
|
System.out.println("finished refresh");
|
|
}
|
|
|
|
/**
|
|
* add all cached films/series to the PosterMode GUI
|
|
*/
|
|
void addAllPosters() {
|
|
// refresh the posterModeElements list
|
|
posterEmenents.clear();
|
|
posterEmenents = dbController.getPosterElementsList(); // returns a list of all PosterElements stored in the database
|
|
|
|
// add button onAction
|
|
for (PosterModeElement element : posterEmenents) {
|
|
element.getButton().addEventHandler(MouseEvent.MOUSE_CLICKED, (event) -> {
|
|
enableBlur(); // blur the FlowPane
|
|
|
|
// if the selected element is a file it's a film, else a series
|
|
if (new File(element.getStreamURL()).isFile() || element.getStreamURL().contains("http")) {
|
|
filmDetailViewController.setFilm(element.getStreamURL());
|
|
filmDetailViewController.showPane();
|
|
} else {
|
|
seriesDetailViewController.setSeries(element.getStreamURL());
|
|
seriesDetailViewController.showPane();
|
|
}
|
|
});
|
|
}
|
|
|
|
posterModeFlowPane.getChildren().clear(); // remove all GUIElements from the posterModeFlowPane
|
|
posterModeFlowPane.getChildren().addAll(posterEmenents); // add all films/series as new GUIElements to the posterModeFlowPane
|
|
|
|
System.out.println("added gui elements");
|
|
}
|
|
|
|
// TODO can this be done in dbController? with dbController.refreshDataBase();
|
|
/**
|
|
* check if the cache is to old, if so update asynchron
|
|
*/
|
|
private void checkCache() {
|
|
ExecutorService executor = Executors.newFixedThreadPool(5);
|
|
|
|
for(FilmTabelDataType entry : dbController.getStreamsList()) {
|
|
if (dbController.getCacheDate(entry.getStreamUrl()).isBefore(lastValidCache)) {
|
|
// System.out.println(entry.getTitle() + " chached on: " + dbController.getCacheDate(entry.getStreamUrl()));
|
|
Runnable OMDbAPIWorker = new OMDbAPIController(entry);
|
|
executor.execute(OMDbAPIWorker);
|
|
}
|
|
}
|
|
|
|
executor.shutdown();
|
|
}
|
|
|
|
private void enableBlur() {
|
|
BoxBlur boxBlur = new BoxBlur();
|
|
boxBlur.setWidth(9);
|
|
boxBlur.setHeight(7);
|
|
boxBlur.setIterations(3);
|
|
posterModeFlowPane.setEffect(boxBlur);
|
|
}
|
|
|
|
public void disableBlur() {
|
|
posterModeFlowPane.setEffect(null);
|
|
}
|
|
}
|