962 lines
34 KiB
Java
962 lines
34 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.application;
|
|
|
|
import java.awt.Desktop;
|
|
import java.io.BufferedReader;
|
|
import java.io.File;
|
|
import java.io.FileReader;
|
|
import java.io.FileWriter;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.io.Writer;
|
|
import java.math.BigInteger;
|
|
import java.net.URLConnection;
|
|
import java.time.LocalDate;
|
|
import java.util.Locale;
|
|
import java.util.ResourceBundle;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
import org.kellerkinder.Alerts.JFXInfoAlert;
|
|
|
|
import com.eclipsesource.json.Json;
|
|
import com.eclipsesource.json.JsonArray;
|
|
import com.eclipsesource.json.JsonObject;
|
|
import com.jfoenix.controls.JFXButton;
|
|
import com.jfoenix.controls.JFXColorPicker;
|
|
import com.jfoenix.controls.JFXHamburger;
|
|
import com.jfoenix.controls.JFXSlider;
|
|
import com.jfoenix.controls.JFXTextField;
|
|
import com.jfoenix.controls.JFXToggleButton;
|
|
import com.jfoenix.transitions.hamburger.HamburgerBackArrowBasicTransition;
|
|
|
|
import javafx.animation.TranslateTransition;
|
|
import javafx.application.Platform;
|
|
import javafx.beans.value.ChangeListener;
|
|
import javafx.beans.value.ObservableValue;
|
|
import javafx.collections.FXCollections;
|
|
import javafx.collections.ObservableList;
|
|
import javafx.event.ActionEvent;
|
|
import javafx.event.EventHandler;
|
|
import javafx.fxml.FXML;
|
|
import javafx.scene.Node;
|
|
import javafx.scene.control.ChoiceBox;
|
|
import javafx.scene.control.ContextMenu;
|
|
import javafx.scene.control.Label;
|
|
import javafx.scene.control.MenuItem;
|
|
import javafx.scene.control.ScrollPane;
|
|
import javafx.scene.control.TableColumn;
|
|
import javafx.scene.control.TableView;
|
|
import javafx.scene.control.TreeItem;
|
|
import javafx.scene.control.TreeTableColumn;
|
|
import javafx.scene.control.TreeTableColumn.SortType;
|
|
import javafx.scene.control.TreeTableView;
|
|
import javafx.scene.image.Image;
|
|
import javafx.scene.image.ImageView;
|
|
import javafx.scene.input.MouseEvent;
|
|
import javafx.scene.layout.AnchorPane;
|
|
import javafx.scene.layout.HBox;
|
|
import javafx.scene.layout.VBox;
|
|
import javafx.scene.paint.Color;
|
|
import javafx.scene.text.Font;
|
|
import javafx.scene.text.FontWeight;
|
|
import javafx.scene.text.Text;
|
|
import javafx.scene.text.TextFlow;
|
|
import javafx.stage.DirectoryChooser;
|
|
import javafx.stage.FileChooser;
|
|
import javafx.stage.Stage;
|
|
import javafx.util.Duration;
|
|
import kellerkinder.HomeFlix.controller.DBController;
|
|
import kellerkinder.HomeFlix.controller.OMDbAPIController;
|
|
import kellerkinder.HomeFlix.controller.UpdateController;
|
|
import kellerkinder.HomeFlix.controller.XMLController;
|
|
import kellerkinder.HomeFlix.datatypes.FilmTabelDataType;
|
|
import kellerkinder.HomeFlix.datatypes.SourceDataType;
|
|
import kellerkinder.HomeFlix.player.Player;
|
|
|
|
public class MainWindowController {
|
|
|
|
// general/settings fxml elements
|
|
@FXML private AnchorPane mainAnchorPane;
|
|
@FXML private ScrollPane settingsScrollPane;
|
|
|
|
@FXML private JFXHamburger menuHam;
|
|
@FXML private HBox topHBox;
|
|
@FXML private VBox sideMenuVBox;
|
|
@FXML private TableView<SourceDataType> sourcesTable;
|
|
|
|
@FXML private JFXButton aboutBtn;
|
|
@FXML private JFXButton settingsBtn;
|
|
@FXML private JFXButton updateBtn;
|
|
@FXML private JFXButton addDirectoryBtn;
|
|
@FXML private JFXButton addStreamSourceBtn;
|
|
|
|
@FXML private JFXToggleButton autoUpdateToggleBtn;
|
|
@FXML private JFXToggleButton autoplayToggleBtn;
|
|
|
|
@FXML private JFXColorPicker colorPicker;
|
|
|
|
@FXML private ChoiceBox<String> languageChoisBox = new ChoiceBox<>();
|
|
@FXML private ChoiceBox<String> branchChoisBox = new ChoiceBox<>();
|
|
|
|
@FXML private JFXSlider fontsizeSlider;
|
|
|
|
@FXML private Label homeflixSettingsLbl;
|
|
@FXML private Label mainColorLbl;
|
|
@FXML private Label fontsizeLbl;
|
|
@FXML private Label languageLbl;
|
|
@FXML private Label updateLbl;
|
|
@FXML private Label branchLbl;
|
|
@FXML private Label sourcesLbl;
|
|
@FXML private Label versionLbl;
|
|
|
|
@FXML private TreeItem<SourceDataType> sourceRoot = new TreeItem<>(new SourceDataType("", ""));
|
|
@FXML private TableColumn<SourceDataType, String> sourceColumn;
|
|
@FXML private TableColumn<SourceDataType, String> modeColumn;
|
|
|
|
// table-mode
|
|
@FXML private AnchorPane tableModeAnchorPane;
|
|
@FXML private JFXTextField searchTextField;
|
|
|
|
@FXML private TreeTableView<FilmTabelDataType> filmsTreeTable;
|
|
@FXML private TreeTableColumn<FilmTabelDataType, String> columnStreamUrl;
|
|
@FXML private TreeTableColumn<FilmTabelDataType, String> columnTitle;
|
|
@FXML private TreeTableColumn<FilmTabelDataType, ImageView> columnFavorite;
|
|
@FXML private TreeTableColumn<FilmTabelDataType, String> columnSeason;
|
|
@FXML private TreeTableColumn<FilmTabelDataType, String> columnEpisode;
|
|
@FXML private TreeItem<FilmTabelDataType> filmRoot = new TreeItem<>(new FilmTabelDataType("", "", "", "", false, null, null));
|
|
|
|
|
|
@FXML private ScrollPane textScrollPane;
|
|
@FXML private TextFlow textFlow;
|
|
@FXML private ImageView posterImageView;
|
|
|
|
@FXML private JFXButton playbtn;
|
|
@FXML private JFXButton openfolderbtn;
|
|
@FXML private JFXButton returnBtn;
|
|
@FXML private JFXButton forwardBtn;
|
|
|
|
// poster-mode
|
|
@FXML private AnchorPane posterModeAnchorPane;
|
|
@FXML private ScrollPane posterModeScrollPane;
|
|
|
|
private DBController dbController;
|
|
private UpdateController updateController;
|
|
private XMLController xmlController;
|
|
private Stage primaryStage;
|
|
private static final Logger LOGGER = LogManager.getLogger(MainWindowController.class.getName());
|
|
|
|
private boolean menuTrue = false;
|
|
|
|
private final String version = "0.7.0";
|
|
private final String buildNumber = "169";
|
|
private final String versionName = "toothless dragon";
|
|
private String btnStyle;
|
|
|
|
private final int hashA = -647380320;
|
|
private int last;
|
|
private int indexTable;
|
|
private int indexList;
|
|
private int next;
|
|
private FilmTabelDataType currentTableFilm = new FilmTabelDataType("", "", "", "", false, null, null);
|
|
|
|
private ObservableList<String> languages = FXCollections.observableArrayList("English (en_US)", "Deutsch (de_DE)");
|
|
private ObservableList<String> branches = FXCollections.observableArrayList("stable", "beta");
|
|
private ObservableList<FilmTabelDataType> filterData = FXCollections.observableArrayList();
|
|
private ObservableList<FilmTabelDataType> filmsList = FXCollections.observableArrayList();
|
|
private static ObservableList<SourceDataType> sourcesList = FXCollections.observableArrayList();
|
|
private MenuItem like = new MenuItem("like");
|
|
private MenuItem dislike = new MenuItem("dislike"); // TODO one option (like or dislike)
|
|
private ContextMenu menu = new ContextMenu(like, dislike);
|
|
private LocalDate lastValidCache = LocalDate.now().minusDays(30); // current date - 30 days is the last valid cache date
|
|
|
|
public MainWindowController() {
|
|
// the constructor
|
|
}
|
|
|
|
@FXML
|
|
public void initialize() {
|
|
xmlController = new XMLController();
|
|
dbController = DBController.getInstance();
|
|
}
|
|
|
|
public void init() {
|
|
LOGGER.info("Initializing Project-HomeFlix build " + buildNumber);
|
|
|
|
xmlController.loadSettings(); // load settings
|
|
checkAutoUpdate();
|
|
|
|
// initialize the GUI and the DBController
|
|
primaryStage = (Stage) mainAnchorPane.getScene().getWindow(); // set primary stage for dialogs
|
|
initTabel();
|
|
initUI();
|
|
initActions();
|
|
dbController.init();
|
|
refreshAllFilms();
|
|
|
|
// load sources list in gui
|
|
addSourceToTable();
|
|
|
|
// posterModeStartup(); // TODO testing DO NOT USE THIS!!
|
|
}
|
|
|
|
// Initialize general UI elements
|
|
private void initUI() {
|
|
versionLbl.setText("Version: " + version + " (Build: " + buildNumber + ")");
|
|
fontsizeSlider.setValue(XMLController.getFontSize());
|
|
colorPicker.setValue(Color.valueOf(XMLController.getColor()));
|
|
|
|
updateBtn.setFont(Font.font("System", 12));
|
|
autoUpdateToggleBtn.setSelected(XMLController.isAutoUpdate());
|
|
autoplayToggleBtn.setSelected(XMLController.isAutoplay());
|
|
languageChoisBox.setItems(languages);
|
|
branchChoisBox.setItems(branches);
|
|
|
|
if (XMLController.isUseBeta()) {
|
|
branchChoisBox.getSelectionModel().select(1);
|
|
} else {
|
|
branchChoisBox.getSelectionModel().select(0);
|
|
}
|
|
|
|
setLocalUI();
|
|
applyColor();
|
|
}
|
|
|
|
/**
|
|
* Initialize the tables (treeTableViewfilm and sourcesTable)
|
|
* only needed for Tabel-Mode
|
|
*/
|
|
private void initTabel() {
|
|
|
|
// film Table
|
|
filmsTreeTable.setRoot(filmRoot);
|
|
filmsTreeTable.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY);
|
|
filmsTreeTable.setShowRoot(false);
|
|
|
|
// write content into cell
|
|
columnStreamUrl.setCellValueFactory(cellData -> cellData.getValue().getValue().streamUrlProperty());
|
|
columnTitle.setCellValueFactory(cellData -> cellData.getValue().getValue().titleProperty());
|
|
columnSeason.setCellValueFactory(cellData -> cellData.getValue().getValue().seasonProperty());
|
|
columnEpisode.setCellValueFactory(cellData -> cellData.getValue().getValue().episodeProperty());
|
|
columnFavorite.setCellValueFactory(cellData -> cellData.getValue().getValue().imageProperty());
|
|
|
|
// context menu for treeTableViewfilm
|
|
filmsTreeTable.setContextMenu(menu);
|
|
|
|
// sourcesTreeTable
|
|
sourceColumn.setCellValueFactory(cellData -> cellData.getValue().pathProperty());
|
|
modeColumn.setCellValueFactory(cellData -> cellData.getValue().modeProperty());
|
|
sourcesTable.setItems(sourcesList);
|
|
}
|
|
|
|
// 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 (settingsScrollPane.isVisible()) {
|
|
settingsScrollPane.setVisible(false);
|
|
}
|
|
});
|
|
|
|
languageChoisBox.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
|
|
@Override
|
|
public void changed(ObservableValue<? extends Number> ov, Number value, Number new_value) {
|
|
String local = languageChoisBox.getItems().get((int) new_value).toString();
|
|
local = local.substring(local.length() - 6, local.length() - 1); // reading only en_US from English (en_US)
|
|
XMLController.setUsrLocal(local);
|
|
setLocalUI();
|
|
xmlController.saveSettings();
|
|
}
|
|
});
|
|
|
|
branchChoisBox.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
|
|
@Override
|
|
public void changed(ObservableValue<? extends Number> ov, Number value, Number new_value) {
|
|
if (branchChoisBox.getItems().get((int) new_value).toString() == "beta") {
|
|
XMLController.setUseBeta(true);
|
|
} else {
|
|
XMLController.setUseBeta(false);
|
|
}
|
|
xmlController.saveSettings();
|
|
}
|
|
});
|
|
|
|
fontsizeSlider.valueProperty().addListener(new ChangeListener<Number>() {
|
|
@Override
|
|
public void changed(ObservableValue<? extends Number> ov, Number old_val, Number new_val) {
|
|
XMLController.setFontSize(fontsizeSlider.getValue());
|
|
if (!getCurrentTitle().isEmpty()) {
|
|
setSelectedFilmInfo(dbController.readCache(getCurrentStreamUrl()));
|
|
}
|
|
xmlController.saveSettings();
|
|
}
|
|
});
|
|
|
|
// Table-Mode actions
|
|
searchTextField.textProperty().addListener(new ChangeListener<String>() {
|
|
@Override
|
|
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
|
|
ObservableList<FilmTabelDataType> helpData;
|
|
filterData.clear();
|
|
filmRoot.getChildren().clear();
|
|
|
|
helpData = filmsList;
|
|
|
|
for (int i = 0; i < helpData.size(); i++) {
|
|
if (helpData.get(i).getTitle().toLowerCase().contains(searchTextField.getText().toLowerCase())) {
|
|
filterData.add(helpData.get(i)); // add data from newDaten to filteredData where title contains search input
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < filterData.size(); i++) {
|
|
filmRoot.getChildren().add(new TreeItem<FilmTabelDataType>(filterData.get(i))); // add filtered data to root node after search
|
|
}
|
|
if (searchTextField.getText().hashCode() == hashA) {
|
|
XMLController.setColor("000000");
|
|
colorPicker.setValue(new Color(0, 0, 0, 1));
|
|
applyColor();
|
|
}
|
|
}
|
|
});
|
|
|
|
like.setOnAction(new EventHandler<ActionEvent>() {
|
|
@Override
|
|
public void handle(ActionEvent event) {
|
|
dbController.like(getCurrentStreamUrl());
|
|
filmsList.set(indexList, dbController.getDatabaseFilm(getCurrentStreamUrl()));
|
|
refreshTableElement();
|
|
}
|
|
});
|
|
|
|
dislike.setOnAction(new EventHandler<ActionEvent>() {
|
|
@Override
|
|
public void handle(ActionEvent event) {
|
|
dbController.dislike(getCurrentStreamUrl());
|
|
filmsList.set(indexList, dbController.getDatabaseFilm(getCurrentStreamUrl()));
|
|
refreshTableElement();
|
|
}
|
|
});
|
|
|
|
/**
|
|
* FIXME fix bug when sort by ASCENDING, wrong order
|
|
*/
|
|
columnFavorite.sortTypeProperty().addListener(new ChangeListener<SortType>() {
|
|
@Override
|
|
public void changed(ObservableValue<? extends SortType> paramObservableValue, SortType paramT1, SortType paramT2) {
|
|
filmRoot.getChildren().clear();
|
|
filterData.clear();
|
|
|
|
if (paramT2.equals(SortType.DESCENDING)) {
|
|
// add favorites at the top
|
|
for (FilmTabelDataType film : filmsList) {
|
|
if (film.getFavorite()) {
|
|
filterData.add(0, film);
|
|
} else {
|
|
filterData.add(film);
|
|
}
|
|
}
|
|
} else {
|
|
// add favorites at the bottom
|
|
for (FilmTabelDataType film : filmsList) {
|
|
if (!film.getFavorite()) {
|
|
filterData.add(0, film);
|
|
} else {
|
|
filterData.add(film);
|
|
}
|
|
}
|
|
}
|
|
|
|
addFilmsToTable(filterData);
|
|
}
|
|
});
|
|
|
|
// Change-listener for treeTableViewfilm
|
|
filmsTreeTable.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Object>() {
|
|
@Override
|
|
public void changed(ObservableValue<?> observable, Object oldVal, Object newVal) {
|
|
if (filmsTreeTable.getSelectionModel().getSelectedItem() == null) {
|
|
return;
|
|
}
|
|
|
|
currentTableFilm = filmsTreeTable.getSelectionModel().getSelectedItem().getValue(); // set the current film object
|
|
indexTable = filmsTreeTable.getSelectionModel().getSelectedIndex(); // get selected items table index
|
|
for (FilmTabelDataType film : filmsList) {
|
|
if (film.equals(currentTableFilm)) {
|
|
indexList = filmsList.indexOf(film); // get selected items list index
|
|
}
|
|
}
|
|
|
|
last = indexTable - 1;
|
|
next = indexTable + 1;
|
|
|
|
|
|
if ((currentTableFilm.getCached().isAfter(lastValidCache) )|| dbController.searchCacheByURL(getCurrentStreamUrl())) {
|
|
LOGGER.info("loading from cache: " + getCurrentTitle());
|
|
setSelectedFilmInfo(dbController.readCache(getCurrentStreamUrl()));
|
|
// System.out.println("cache date is: " + currentTableFilm.getCached().toString());
|
|
} else {
|
|
// System.out.println("either not cached or to old!");
|
|
// System.out.println("cache date is: " + currentTableFilm.getCached().toString());
|
|
// this is not perfect!
|
|
new Thread(new Runnable() {
|
|
public void run() {
|
|
Thread omdbAPIThread = new Thread(new OMDbAPIController(dbController, currentTableFilm, XMLController.getOmdbAPIKey()));
|
|
omdbAPIThread.setName("OMDbAPI");
|
|
omdbAPIThread.start();
|
|
|
|
synchronized (omdbAPIThread) {
|
|
try {
|
|
omdbAPIThread.wait();
|
|
} catch (InterruptedException e) {
|
|
LOGGER.error(e);
|
|
}
|
|
// update the GUI for the selected film
|
|
Platform.runLater(() -> {
|
|
setSelectedFilmInfo(dbController.readCache(getCurrentStreamUrl()));
|
|
});
|
|
}
|
|
}
|
|
}).start();
|
|
}
|
|
}
|
|
});
|
|
|
|
// Poster-Mode actions
|
|
}
|
|
|
|
// Table-Mode fxml actions
|
|
@FXML
|
|
private void playbtnclicked() {
|
|
if (currentTableFilm.getStreamUrl().length() > 0) {
|
|
if (currentTableFilm.getStreamUrl().contains("_rootNode")) {
|
|
LOGGER.info("rootNode found, getting last watched episode");
|
|
currentTableFilm = dbController.getLastWatchedEpisode(currentTableFilm.getTitle());
|
|
}
|
|
|
|
if (isSupportedFormat(currentTableFilm)) {
|
|
new Player(getCurrentTableFilm());
|
|
} else {
|
|
LOGGER.error("using fallback player!");
|
|
if (System.getProperty("os.name").contains("Linux")) {
|
|
String line;
|
|
String output = "";
|
|
Process p;
|
|
try {
|
|
p = Runtime.getRuntime().exec("which vlc");
|
|
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
|
while ((line = input.readLine()) != null) {
|
|
output = line;
|
|
}
|
|
LOGGER.info("which vlc: " + output);
|
|
input.close();
|
|
} catch (IOException e1) {
|
|
e1.printStackTrace();
|
|
}
|
|
if (output.contains("which: no vlc") || output == "") {
|
|
JFXInfoAlert vlcInfoAlert = new JFXInfoAlert("Info",
|
|
XMLController.getLocalBundle().getString("vlcNotInstalled"),
|
|
btnStyle, primaryStage);
|
|
vlcInfoAlert.showAndWait();
|
|
} else {
|
|
try {
|
|
new ProcessBuilder("vlc", getCurrentStreamUrl()).start();
|
|
} catch (IOException e) {
|
|
LOGGER.warn("An error has occurred while opening the file!", e);
|
|
}
|
|
}
|
|
|
|
} else if (System.getProperty("os.name").contains("Windows") || System.getProperty("os.name").contains("Mac OS X")) {
|
|
try {
|
|
Desktop.getDesktop().open(new File(getCurrentStreamUrl()));
|
|
} catch (IOException e) {
|
|
LOGGER.warn("An error has occurred while opening the file!", e);
|
|
}
|
|
} else {
|
|
LOGGER.error(System.getProperty("os.name") + ", OS is not supported, please contact a developer! ");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
private void openfolderbtnclicked() {
|
|
File dest = new File(getCurrentStreamUrl()).getParentFile();
|
|
|
|
if (!System.getProperty("os.name").contains("Linux")) {
|
|
try {
|
|
Desktop.getDesktop().open(dest);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
private void returnBtnclicked(){
|
|
filmsTreeTable.getSelectionModel().select(last);
|
|
}
|
|
|
|
@FXML
|
|
private void forwardBtnclicked(){
|
|
filmsTreeTable.getSelectionModel().select(next);
|
|
}
|
|
|
|
// general fxml actions
|
|
@FXML
|
|
private void aboutBtnAction() {
|
|
String bodyText = "Project HomeFlix \nVersion: " + version + " (Build: " + buildNumber + ") \""
|
|
+ versionName + "\" \n" + XMLController.getLocalBundle().getString("infoText");
|
|
JFXInfoAlert infoAlert = new JFXInfoAlert("Project HomeFlix", bodyText, btnStyle, primaryStage);
|
|
infoAlert.showAndWait();
|
|
}
|
|
|
|
@FXML
|
|
private void settingsBtnclicked() {
|
|
if (settingsScrollPane.isVisible()) {
|
|
settingsScrollPane.setVisible(false);
|
|
} else {
|
|
settingsScrollPane.setVisible(true);
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
private void addDirectoryBtnAction(){
|
|
DirectoryChooser directoryChooser = new DirectoryChooser();
|
|
directoryChooser.setTitle(XMLController.getLocalBundle().getString("addDirectory"));
|
|
File selectedFolder = directoryChooser.showDialog(primaryStage);
|
|
if (selectedFolder != null && selectedFolder.exists()) {
|
|
addSource(selectedFolder.getPath(), "local");
|
|
} else {
|
|
LOGGER.error("The selected folder dosen't exist!");
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
private void addStreamSourceBtnAction(){
|
|
FileChooser fileChooser = new FileChooser();
|
|
fileChooser.setTitle(XMLController.getLocalBundle().getString("addStreamSource"));
|
|
File selectedFile = fileChooser.showOpenDialog(primaryStage);
|
|
if (selectedFile != null && selectedFile.exists()) {
|
|
addSource(selectedFile.getPath(), "stream");
|
|
} else {
|
|
LOGGER.error("The selected file dosen't exist!");
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
private void colorPickerAction() {
|
|
XMLController.setColor(colorPicker.getValue().toString().substring(2, 10));
|
|
xmlController.saveSettings();
|
|
applyColor();
|
|
}
|
|
|
|
@FXML
|
|
private void updateBtnAction() {
|
|
updateController = new UpdateController(this, buildNumber, XMLController.isUseBeta());
|
|
Thread updateThread = new Thread(updateController);
|
|
updateThread.setName("Updater");
|
|
updateThread.start();
|
|
}
|
|
|
|
@FXML
|
|
private void autoUpdateToggleBtnAction() {
|
|
XMLController.setAutoUpdate(!XMLController.isAutoUpdate());
|
|
xmlController.saveSettings();
|
|
}
|
|
|
|
@FXML
|
|
private void autoplayToggleBtnAction(){
|
|
XMLController.setAutoplay(!XMLController.isAutoplay());
|
|
xmlController.saveSettings();
|
|
}
|
|
|
|
// refresh the selected child of the root node
|
|
private void refreshTableElement() {
|
|
filmRoot.getChildren().get(indexTable).setValue(filmsList.get(indexList));
|
|
}
|
|
|
|
/**
|
|
* refresh all films in filmsList and in filmsTable
|
|
* clear the FilmsList and FilmRoot children, then update the database
|
|
*/
|
|
private void refreshAllFilms() {
|
|
filmsList.clear();
|
|
filmRoot.getChildren().clear();
|
|
dbController.refreshDataBase(); // refreshes the database after a source path was added
|
|
filmsList = dbController.getDatabaseFilmsList(); // returns a list of all films stored in the database
|
|
addFilmsToTable(filmsList);
|
|
}
|
|
|
|
/**
|
|
* add data from films-list to films-table
|
|
*/
|
|
public void addFilmsToTable(ObservableList<FilmTabelDataType> elementsList) {
|
|
|
|
for (FilmTabelDataType element : elementsList) {
|
|
|
|
// only if the entry contains a season and a episode it's a valid series
|
|
if (!element.getSeason().isEmpty() && !element.getEpisode().isEmpty()) {
|
|
|
|
// check if there is a series node to add the item
|
|
for (int i = 0; i < filmRoot.getChildren().size(); i++) {
|
|
if (filmRoot.getChildren().get(i).getValue().getTitle().equals(element.getTitle())) {
|
|
// if a root node exists, add element as child
|
|
TreeItem<FilmTabelDataType> episodeNode = new TreeItem<>(new FilmTabelDataType(
|
|
element.getStreamUrl(), element.getTitle(), element.getSeason(), element.getEpisode(),
|
|
element.getFavorite(), element.getCached(), element.getImage()));
|
|
filmRoot.getChildren().get(i).getChildren().add(episodeNode);
|
|
} else if (filmRoot.getChildren().get(i).nextSibling() == null) {
|
|
// if no root node exists, create one and add element as child
|
|
TreeItem<FilmTabelDataType> seriesRootNode = new TreeItem<>(new FilmTabelDataType(
|
|
element.getTitle() + "_rootNode", element.getTitle(), "", "", element.getFavorite(),
|
|
element.getCached() , element.getImage()));
|
|
filmRoot.getChildren().add(seriesRootNode);
|
|
}
|
|
}
|
|
} else {
|
|
// if season and episode are empty, we can assume the object is a film
|
|
filmRoot.getChildren().add(new TreeItem<FilmTabelDataType>(element));
|
|
}
|
|
}
|
|
}
|
|
|
|
// add a all elements of sourcesList to the sources table on the settings pane
|
|
public void addSourceToTable() {
|
|
for (SourceDataType source: sourcesList) {
|
|
sourceRoot.getChildren().add(new TreeItem<SourceDataType>(source)); // add data to root-node
|
|
}
|
|
}
|
|
|
|
/**
|
|
* add a source to the newsources list
|
|
* @param path to the source
|
|
* @param mode of the source (local or streaming)
|
|
*/
|
|
public void addSource(String path, String mode) {
|
|
JsonObject source = null;
|
|
JsonArray newsources = null;
|
|
|
|
try {
|
|
// read old array
|
|
File oldSources = new File(XMLController.getDirHomeFlix() + "/sources.json");
|
|
if (oldSources.exists()) {
|
|
newsources = Json.parse(new FileReader(XMLController.getDirHomeFlix() + "/sources.json")).asArray();
|
|
} else {
|
|
newsources = Json.array();
|
|
}
|
|
|
|
// add new source
|
|
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(e);
|
|
}
|
|
|
|
// clear old sources list/table
|
|
getSourcesList().clear();
|
|
sourceRoot.getChildren().clear();
|
|
|
|
// clear the FilmsList and FilmRoot children, then update the database
|
|
refreshAllFilms();
|
|
|
|
checkAllPosters(); // check if there is anything to cache
|
|
}
|
|
|
|
/**
|
|
* set the color of the GUI-Elements
|
|
* if usedColor is less than checkColor set text fill white, else black
|
|
*/
|
|
private void applyColor() {
|
|
String menuBtnStyle;
|
|
BigInteger usedColor = new BigInteger(XMLController.getColor(), 16);
|
|
BigInteger checkColor = new BigInteger("78909cff", 16);
|
|
|
|
if (usedColor.compareTo(checkColor) == -1) {
|
|
btnStyle = "-fx-button-type: RAISED; -fx-background-color: #" + XMLController.getColor() + "; -fx-text-fill: WHITE;";
|
|
menuBtnStyle = "-fx-text-fill: WHITE;";
|
|
|
|
playbtn.setGraphic(new ImageView(new Image("icons/ic_play_arrow_white_18dp_1x.png")));
|
|
returnBtn.setGraphic(new ImageView(new Image("icons/ic_skip_previous_white_18dp_1x.png")));
|
|
forwardBtn.setGraphic(new ImageView(new Image("icons/ic_skip_next_white_18dp_1x.png")));
|
|
|
|
menuHam.getStyleClass().clear();
|
|
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;";
|
|
|
|
playbtn.setGraphic(new ImageView(new Image("icons/ic_play_arrow_black_18dp_1x.png")));
|
|
returnBtn.setGraphic(new ImageView(new Image("icons/ic_skip_previous_black_18dp_1x.png")));
|
|
forwardBtn.setGraphic(new ImageView(new Image("icons/ic_skip_next_black_18dp_1x.png")));
|
|
|
|
menuHam.getStyleClass().clear();
|
|
menuHam.getStyleClass().add("jfx-hamburgerB");
|
|
}
|
|
|
|
// boxes and TextFields
|
|
sideMenuVBox.setStyle("-fx-background-color: #" + XMLController.getColor() + ";");
|
|
topHBox.setStyle("-fx-background-color: #" + XMLController.getColor() + ";");
|
|
searchTextField.setFocusColor(Color.valueOf(XMLController.getColor()));
|
|
|
|
// normal buttons
|
|
addDirectoryBtn.setStyle(btnStyle);
|
|
addStreamSourceBtn.setStyle(btnStyle);
|
|
updateBtn.setStyle(btnStyle);
|
|
playbtn.setStyle(btnStyle);
|
|
openfolderbtn.setStyle(btnStyle);
|
|
returnBtn.setStyle(btnStyle);
|
|
forwardBtn.setStyle(btnStyle);
|
|
|
|
// menu buttons
|
|
settingsBtn.setStyle(menuBtnStyle);
|
|
aboutBtn.setStyle(menuBtnStyle);
|
|
}
|
|
|
|
// slide in in 400ms
|
|
private void sideMenuSlideIn() {
|
|
sideMenuVBox.setVisible(true);
|
|
TranslateTransition translateTransition = new TranslateTransition(Duration.millis(400), sideMenuVBox);
|
|
translateTransition.setFromX(-150);
|
|
translateTransition.setToX(0);
|
|
translateTransition.play();
|
|
}
|
|
|
|
// slide out in 400ms
|
|
private void sideMenuSlideOut() {
|
|
TranslateTransition translateTransition = new TranslateTransition(Duration.millis(400), sideMenuVBox);
|
|
translateTransition.setFromX(0);
|
|
translateTransition.setToX(-150);
|
|
translateTransition.play();
|
|
}
|
|
|
|
/**
|
|
* set the local based on the languageChoisBox selection
|
|
*/
|
|
private void setLocalUI() {
|
|
switch (XMLController.getUsrLocal()) {
|
|
case "en_US":
|
|
XMLController.setLocalBundle(ResourceBundle.getBundle("locals.HomeFlix-Local", Locale.US)); // us_English
|
|
languageChoisBox.getSelectionModel().select(0);
|
|
break;
|
|
case "de_DE":
|
|
XMLController.setLocalBundle(ResourceBundle.getBundle("locals.HomeFlix-Local", Locale.GERMAN)); // German
|
|
languageChoisBox.getSelectionModel().select(1);
|
|
break;
|
|
default:
|
|
XMLController.setLocalBundle(ResourceBundle.getBundle("locals.HomeFlix-Local", Locale.US)); // default local
|
|
languageChoisBox.getSelectionModel().select(0);
|
|
break;
|
|
}
|
|
aboutBtn.setText(XMLController.getLocalBundle().getString("info"));
|
|
settingsBtn.setText(XMLController.getLocalBundle().getString("settings"));
|
|
searchTextField.setPromptText(XMLController.getLocalBundle().getString("tfSearch"));
|
|
openfolderbtn.setText(XMLController.getLocalBundle().getString("openFolder"));
|
|
updateBtn.setText(XMLController.getLocalBundle().getString("checkUpdates"));
|
|
addDirectoryBtn.setText(XMLController.getLocalBundle().getString("addDirectory"));
|
|
addStreamSourceBtn.setText(XMLController.getLocalBundle().getString("addStreamSource"));
|
|
homeflixSettingsLbl.setText(XMLController.getLocalBundle().getString("homeflixSettingsLbl"));
|
|
mainColorLbl.setText(XMLController.getLocalBundle().getString("mainColorLbl"));
|
|
fontsizeLbl.setText(XMLController.getLocalBundle().getString("fontsizeLbl"));
|
|
languageLbl.setText(XMLController.getLocalBundle().getString("languageLbl"));
|
|
autoUpdateToggleBtn.setText(XMLController.getLocalBundle().getString("autoUpdate"));
|
|
autoplayToggleBtn.setText(XMLController.getLocalBundle().getString("autoplay"));
|
|
branchLbl.setText(XMLController.getLocalBundle().getString("branchLbl"));
|
|
columnStreamUrl.setText(XMLController.getLocalBundle().getString("columnStreamUrl"));
|
|
columnTitle.setText(XMLController.getLocalBundle().getString("columnName"));
|
|
columnFavorite.setText(XMLController.getLocalBundle().getString("columnFavorite"));
|
|
columnSeason.setText(XMLController.getLocalBundle().getString("columnSeason"));
|
|
columnEpisode.setText(XMLController.getLocalBundle().getString("columnEpisode"));
|
|
}
|
|
|
|
private void setSelectedFilmInfo(String[] cacheData) {
|
|
Font font = Font.font("System", FontWeight.BOLD, (int) Math.round(XMLController.getFontSize()));
|
|
ObservableList<Node> textFlow = getTextFlow().getChildren();
|
|
|
|
// TODO this should move! ***
|
|
Text[] nameText = new Text[20];
|
|
nameText[0] = new Text(XMLController.getLocalBundle().getString("title") + ": ");
|
|
nameText[1] = new Text(XMLController.getLocalBundle().getString("year") + ": ");
|
|
nameText[2] = new Text(XMLController.getLocalBundle().getString("rated") + ": ");
|
|
nameText[3] = new Text(XMLController.getLocalBundle().getString("released") + ": ");
|
|
nameText[4] = new Text(XMLController.getLocalBundle().getString("season") + ": ");
|
|
nameText[5] = new Text(XMLController.getLocalBundle().getString("episode") + ": ");
|
|
nameText[6] = new Text(XMLController.getLocalBundle().getString("runtime") + ": ");
|
|
nameText[7] = new Text(XMLController.getLocalBundle().getString("genre") + ": ");
|
|
nameText[8] = new Text(XMLController.getLocalBundle().getString("director") + ": ");
|
|
nameText[9] = new Text(XMLController.getLocalBundle().getString("writer") + ": ");
|
|
nameText[10] = new Text(XMLController.getLocalBundle().getString("actors") + ": ");
|
|
nameText[11] = new Text(XMLController.getLocalBundle().getString("plot") + ": ");
|
|
nameText[12] = new Text(XMLController.getLocalBundle().getString("language") + ": ");
|
|
nameText[13] = new Text(XMLController.getLocalBundle().getString("country") + ": ");
|
|
nameText[14] = new Text(XMLController.getLocalBundle().getString("awards") + ": ");
|
|
nameText[15] = new Text(XMLController.getLocalBundle().getString("metascore") + ": ");
|
|
nameText[16] = new Text(XMLController.getLocalBundle().getString("imdbRating") + ": ");
|
|
nameText[17] = new Text(XMLController.getLocalBundle().getString("type") + ": ");
|
|
nameText[18] = new Text(XMLController.getLocalBundle().getString("boxOffice") + ": ");
|
|
nameText[19] = new Text(XMLController.getLocalBundle().getString("website") + ": ");
|
|
// ***
|
|
|
|
// set the correct font for the nameText
|
|
for (Text text : nameText) {
|
|
text.setFont(font);
|
|
}
|
|
|
|
// clear the textFlow and add the new text
|
|
textFlow.clear();
|
|
|
|
// TODO rework
|
|
for(int i = 0; i < 20; i++) {
|
|
if (cacheData[i] != null && (i == 5 || i == 6) && cacheData[5].length() == 0) {
|
|
// do nothing
|
|
} else if(cacheData[i] != null){
|
|
textFlow.addAll(nameText[i], new Text(cacheData[i] + "\n"));
|
|
}
|
|
}
|
|
|
|
getTextFlow().setStyle("-fx-font-size : " + ((int) Math.round(XMLController.getFontSize()) + 1) + "px;");
|
|
|
|
// add the image
|
|
if (new File(cacheData[20]).isFile()) {
|
|
try {
|
|
posterImageView.setImage(new Image(new File(cacheData[20]).toURI().toString()));
|
|
} catch (Exception e) {
|
|
posterImageView.setImage(new Image(cacheData[20]));
|
|
LOGGER.error("No Poster found, useing default.");
|
|
}
|
|
} else {
|
|
posterImageView.setImage(new Image(cacheData[20]));
|
|
}
|
|
}
|
|
|
|
// if AutoUpdate, then check for updates
|
|
private void checkAutoUpdate() {
|
|
|
|
if (XMLController.isAutoUpdate()) {
|
|
try {
|
|
LOGGER.info("AutoUpdate: looking for updates on startup ...");
|
|
updateController = new UpdateController(this, buildNumber, XMLController.isUseBeta());
|
|
Thread updateThread = new Thread(updateController);
|
|
updateThread.setName("Updater");
|
|
updateThread.start();
|
|
updateThread.join();
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* check if a film is supported by the HomeFlixPlayer or not
|
|
* this is the case if the mime type is mp4
|
|
* @param entry the film you want to check
|
|
* @return true if so, false if not
|
|
*/
|
|
private boolean isSupportedFormat(FilmTabelDataType film) {
|
|
String mimeType = URLConnection.guessContentTypeFromName(film.getStreamUrl());
|
|
return mimeType != null && (mimeType.contains("mp4") || mimeType.contains("vp6"));
|
|
}
|
|
|
|
/**
|
|
* Poser Mode WIP
|
|
*/
|
|
|
|
private void posterModeStartup() {
|
|
checkAllPosters();
|
|
}
|
|
|
|
/**
|
|
* check if all posters are cached, if not cache the missing ones
|
|
*/
|
|
private void checkAllPosters() {
|
|
// get all not cached entries, none of them should have a cached poster
|
|
ExecutorService executor = Executors.newFixedThreadPool(5);
|
|
|
|
for (FilmTabelDataType entry : dbController.getAllNotCachedEntries()) {
|
|
System.out.println(entry.getStreamUrl() + " is NOT cached!");
|
|
|
|
Runnable OMDbAPIWorker = new OMDbAPIController(dbController, entry, XMLController.getOmdbAPIKey());
|
|
executor.execute(OMDbAPIWorker);
|
|
}
|
|
executor.shutdown();
|
|
|
|
// TODO show loading screen
|
|
|
|
// update all elements from the database
|
|
refreshAllFilms();
|
|
}
|
|
|
|
// getter and setter
|
|
|
|
public FilmTabelDataType getCurrentTableFilm() {
|
|
return currentTableFilm;
|
|
}
|
|
|
|
public String getCurrentTitle() {
|
|
return currentTableFilm.getTitle();
|
|
}
|
|
|
|
public String getCurrentStreamUrl() {
|
|
return currentTableFilm.getStreamUrl();
|
|
}
|
|
|
|
public int getIndexTable() {
|
|
return indexTable;
|
|
}
|
|
|
|
public int getIndexList() {
|
|
return indexList;
|
|
}
|
|
|
|
public static ObservableList<SourceDataType> getSourcesList() {
|
|
return sourcesList;
|
|
}
|
|
|
|
public TextFlow getTextFlow() {
|
|
return textFlow;
|
|
}
|
|
|
|
public JFXButton getUpdateBtn() {
|
|
return updateBtn;
|
|
}
|
|
}
|