1
0
Fork 0

IT WORRRKSSSSS.

master
Ambrose Chua 2015-10-15 21:41:28 +08:00
parent 0cf2a744fa
commit e07f095a82
20 changed files with 1006 additions and 661 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

BIN
src/icons/warning.32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

View File

@ -1,7 +1,10 @@
package io.makerforce.undefined;
import io.makerforce.undefined.util.LibraryManager;
import io.makerforce.undefined.view.InterfaceController;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
@ -10,12 +13,10 @@ import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Main extends Application {
ChangeListener<LibraryManager.LibraryManagerState> ch = null; // Because it doesn't work in there
private Stage mainStage;
public static void main(String[] args) {
@ -30,27 +31,60 @@ public class Main extends Application {
initStage.initStyle(StageStyle.UNDECORATED);
initStage.setScene(splashScene);
initStage.toFront();
initStage.setAlwaysOnTop(true);
initStage.show();
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.schedule(() -> Platform.runLater(() -> {
initStage.close();
ch = (state, oldValue, newValue) -> {
InterfaceController.getLibraryManager().stateProperty().removeListener(ch);
//ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
//executor.schedule(() -> Platform.runLater(() -> {
Platform.runLater(() -> {
Parent root = null;
try {
root = FXMLLoader.load(Main.this.getClass().getResource("view/interface.fxml"));
} catch (IOException e) {
e.printStackTrace();
System.exit(284);
}
mainStage = new Stage(StageStyle.DECORATED);
mainStage.setTitle("Undefined");
mainStage.setScene(new Scene(root, 640, 480));
mainStage.setMinHeight(((BorderPane) root).getMinHeight());
mainStage.setMinWidth(((BorderPane) root).getMinWidth());
mainStage.setOnCloseRequest((event) -> {
LibraryManager.unScheduleAll();
});
mainStage.show();
//executor.shutdown();
InterfaceController.getLibraryManager().stateProperty().set(LibraryManager.UPDATING);
InterfaceController.getLibraryManager().stateProperty().set(LibraryManager.READY);
Parent root = null;
Thread initStageClose = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Platform.runLater(() -> {
initStage.close();
});
});
initStageClose.start();
});
//}), 1500, TimeUnit.MILLISECONDS);
};
InterfaceController.getLibraryManager().stateProperty().addListener(ch);
Thread init = new Thread(() -> {
try {
root = FXMLLoader.load(getClass().getResource("view/interface.fxml"));
} catch (IOException e) {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(284);
}
mainStage = new Stage(StageStyle.DECORATED);
mainStage.setTitle("Undefined");
mainStage.setScene(new Scene(root, 640, 480));
mainStage.setMinHeight(((BorderPane) root).getMinHeight());
mainStage.setMinWidth(((BorderPane) root).getMinWidth());
mainStage.show();
executor.shutdown();
}), 1500, TimeUnit.MILLISECONDS);
InterfaceController.getLibraryManager().update(); // IT DOES REAL WORK MIND YOU. SO COOL. SADLY, SCHEDULING UPDATES DONT WORK YET
});
init.start();
}
}

View File

@ -1,7 +1,6 @@
package io.makerforce.undefined.model;
import io.makerforce.undefined.util.Util;
import javafx.collections.ObservableList;
import org.json.JSONArray;
import org.json.JSONObject;
@ -26,8 +25,4 @@ public class Album extends ItemList<Track> {
}
}
public ObservableList<Track> getTracks() {
return this.getItems();
}
}

View File

@ -1,13 +1,14 @@
package io.makerforce.undefined.model;
import io.makerforce.undefined.util.Util;
import javafx.collections.ObservableList;
import org.json.JSONObject;
import java.net.URL;
public class Artist extends ItemList<Album> {
public ItemList<Track> tracks = new ItemList<>();
public Artist() {
super();
}
@ -21,11 +22,12 @@ public class Artist extends ItemList<Album> {
o.getJSONObject("albums").keySet().forEach((key) -> {
Album a = new Album(o.getJSONObject("albums").getJSONObject(key), key, artistName, endPoint);
super.getItems().add(a);
tracks.getItems().addAll(a.getItems());
});
}
public ObservableList<Album> getAlbums() {
return this.getItems();
public ItemList<Track> getTracks() {
return tracks;
}
}

View File

@ -1,13 +1,16 @@
package io.makerforce.undefined.model;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.StringProperty;
import java.net.URL;
public interface Item {
public URL getPicture();
ObjectProperty<URL> pictureProperty();
public String getTitle();
StringProperty titleProperty();
public String getSubtitle();
StringProperty subtitleProperty();
}

View File

@ -1,5 +1,9 @@
package io.makerforce.undefined.model;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@ -7,16 +11,16 @@ import java.net.URL;
public class ItemList<T> implements Item {
private URL picture;
private String title;
private String subtitle;
private ObjectProperty<URL> picture = new SimpleObjectProperty<>();
private StringProperty title = new SimpleStringProperty();
private StringProperty subtitle = new SimpleStringProperty();
private ObservableList<T> items = FXCollections.observableArrayList();
public ItemList(URL picture, String title, String subtitle) {
this.picture = picture;
this.title = title;
this.subtitle = subtitle;
this.picture.set(picture);
this.title.set(title);
this.subtitle.set(subtitle);
}
public ItemList(URL picture, String title) {
@ -27,15 +31,15 @@ public class ItemList<T> implements Item {
this(null, "", "");
}
public URL getPicture() {
public ObjectProperty<URL> pictureProperty() {
return picture;
}
public String getTitle() {
public StringProperty titleProperty() {
return title;
}
public String getSubtitle() {
public StringProperty subtitleProperty() {
return subtitle;
}

View File

@ -1,12 +1,14 @@
package io.makerforce.undefined.model;
import javafx.collections.ObservableList;
import org.json.JSONObject;
import java.net.URL;
public class Library extends ItemList<Artist> {
public ItemList<Album> albums = new ItemList<>();
public ItemList<Track> tracks = new ItemList<>();
public Library() {
super();
}
@ -16,11 +18,17 @@ public class Library extends ItemList<Artist> {
o.getJSONObject("artists").keySet().forEach((key) -> {
Artist a = new Artist(o.getJSONObject("artists").getJSONObject(key), key, endPoint);
super.getItems().add(a);
albums.getItems().addAll(a.getItems());
tracks.getItems().addAll(a.getTracks().getItems());
});
}
public ObservableList<Artist> getArtists() {
return this.getItems();
public ItemList<Album> getAlbums() {
return albums;
}
public ItemList<Track> getTracks() {
return tracks;
}
}

View File

@ -1,6 +1,9 @@
package io.makerforce.undefined.model;
import io.makerforce.undefined.util.Util;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.json.JSONArray;
import org.json.JSONObject;
@ -8,15 +11,15 @@ import java.net.URL;
public class Track implements Item {
private URL file;
private URL picture;
private String title;
private String artist;
private String album;
private String year;
private int trackNumber; // track.no OR number
private int totalTracks;
private String[] genre;
private ObjectProperty<URL> file = new SimpleObjectProperty<>();
private ObjectProperty<URL> picture = new SimpleObjectProperty<>();
private StringProperty title = new SimpleStringProperty();
private StringProperty artist = new SimpleStringProperty();
private StringProperty album = new SimpleStringProperty();
private StringProperty year = new SimpleStringProperty();
private IntegerProperty trackNumber = new SimpleIntegerProperty(); // track.no OR number
private IntegerProperty totalTracks = new SimpleIntegerProperty();
private ObservableList<String> genre = FXCollections.observableArrayList();
public Track() {
@ -29,72 +32,71 @@ public class Track implements Item {
public Track(JSONObject track, String albumName, String artistName, URL endPoint) {
this();
title = Util.getNotEmpty(
title.set(Util.getNotEmpty(
track.getString("title"),
(track.has("filetitle") ? track.getString("filetitle") : "")
);
file = Util.toURLOrNull(endPoint + track.getString("file"));
picture = Util.toURLOrNull(endPoint + track.getString("picture"));
artist = Util.getNotEmpty(
));
file.set(Util.toURLOrNull(endPoint + track.getString("file")));
picture.set(Util.toURLOrNull(endPoint + track.getString("picture")));
artist.set(Util.getNotEmpty(
(track.getJSONArray("artist").length() > 0 ? track.getJSONArray("artist").getString(0) : ""),
artistName
);
album = Util.getNotEmpty(
));
album.set(Util.getNotEmpty(
track.getString("album"),
albumName
);
year = track.getString("year");
trackNumber = track.getInt("number"); // track.getJSONObject("track").getInt("no");
totalTracks = track.getJSONObject("track").getInt("of");
));
year.set(track.getString("year"));
trackNumber.set(track.getInt("number")); // track.getJSONObject("track").getInt("no");
totalTracks.set(track.getJSONObject("track").getInt("of"));
JSONArray genres = track.getJSONArray("genre");
genre = new String[genres.length()];
for (int i = 0; i < genres.length(); i++) {
genre[i] = genres.getString(i);
genre.add(genres.getString(i));
}
}
public URL getPicture() {
public ObjectProperty<URL> pictureProperty() {
return picture;
}
public String getTitle() {
public StringProperty titleProperty() {
return title;
}
public String getSubtitle() {
public StringProperty subtitleProperty() {
return artist;
}
public URL getFile() {
public ObjectProperty<URL> fileProperty() {
return file;
}
public String getArtist() {
public StringProperty artistProperty() {
return artist;
}
public String getAlbum() {
public StringProperty albumProperty() {
return album;
}
public String getYear() {
public StringProperty yearProperty() {
return year;
}
public int getTrackNumber() {
public IntegerProperty trackNumberProperty() {
return trackNumber;
}
public int getTotalTracks() {
public IntegerProperty totalTracksProperty() {
return totalTracks;
}
public String[] getGenre() {
public ObservableList<String> getGenre() {
return genre;
}
public String toString() {
return title + " (" + artist + ", " + album + ")";
return title.get() + " (" + artist.get() + ", " + album.get() + ")";
}
}

View File

@ -1,6 +1,7 @@
package io.makerforce.undefined.util;
import io.makerforce.undefined.model.Library;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import org.json.JSONObject;
@ -11,6 +12,7 @@ import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@ -21,16 +23,13 @@ public class LibraryManager {
public static final LibraryManagerState ERROR = new LibraryManagerState("error");
public static final LibraryManagerState READY = new LibraryManagerState("ready");
public static final String DEFAULT_ENDPOINT = "http://ambrose.makerforce.io:8080/";
public static final String DEFAULT_ENDPOINT = "http://ambrose.makerforce.io:8080";
private static ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);
private SimpleObjectProperty<LibraryManagerState> state = new SimpleObjectProperty<>();
private URL endPoint;
private Library l;
private ScheduledExecutorService scheduledExecutorService = null;
private boolean hasScheduled = false;
private ScheduledFuture<?> future;
//private static ArrayList<ScheduledFuture> futures = new ArrayList<>();
public LibraryManager() {
state.set(EMPTY);
@ -53,6 +52,13 @@ public class LibraryManager {
}
}
public static void unScheduleAll() {
//futures.forEach((future) -> {
// future.cancel(true);
//});
scheduledExecutorService.shutdownNow();
}
public Library getLibrary() {
return l;
}
@ -62,53 +68,67 @@ public class LibraryManager {
}
public void update() {
new Thread(() -> {
try {
state.set(UPDATING);
try {
state.set(UPDATING);
HttpURLConnection con = (HttpURLConnection) endPoint.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", "Mozilla/5.0");
con.setRequestProperty("X-Undefined-Client", "JavaFX/0.0.2");
HttpURLConnection con = (HttpURLConnection) endPoint.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", "Mozilla/5.0");
con.setRequestProperty("X-Undefined-Client", "JavaFX/0.0.2");
int responseCode = con.getResponseCode();
System.out.println("Response Code: " + responseCode);
int responseCode = con.getResponseCode();
System.out.println("Response Code: " + responseCode);
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
JSONObject obj = new JSONObject(response.toString());
l = new Library(obj, endPoint);
state.set(READY);
} catch (IOException e) {
e.printStackTrace();
state.set(ERROR);
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
}).run();
in.close();
JSONObject obj = new JSONObject(response.toString());
l = new Library(obj, endPoint);
state.set(READY);
} catch (IOException e) {
e.printStackTrace();
state.set(ERROR);
}
}
public void schedule() {
if (scheduledExecutorService == null) {
scheduledExecutorService = new ScheduledThreadPoolExecutor(1);
scheduledExecutorService.scheduleAtFixedRate(() -> {
this.update();
}, 0, 60, TimeUnit.SECONDS);
//this.update();
if (future == null) {
future = scheduledExecutorService.scheduleWithFixedDelay(() -> {
//this.update(); // DISABLED BECAUSE: Will stop playback if it's playing due to bad implementation. And anyway the server-side stuff won't change.
state.set(UPDATING);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
state.set(READY);
}, 0, 60, TimeUnit.SECONDS); // Poke every minute (although nothing will change on the remote server)
//futures.add(future);
} else {
throw new Error("Updates are already scheduled");
}
}
public void scheduleRate() {
future.getDelay(TimeUnit.MILLISECONDS);
}
public void unSchedule() {
scheduledExecutorService.shutdownNow();
scheduledExecutorService = null;
future.cancel(true);
//futures.remove(future);
}
public ObjectProperty<LibraryManagerState> stateProperty() {
return state;
}
public static final class LibraryManagerState {

View File

@ -4,10 +4,12 @@ import io.makerforce.undefined.model.Track;
import javafx.beans.property.*;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.util.Duration;
import java.net.URL;
import java.util.ArrayList;
public class PlayManager {
@ -21,7 +23,7 @@ public class PlayManager {
private ArrayList<Track> queue = new ArrayList<>();
private IntegerProperty currentTrack = new SimpleIntegerProperty(0);
private IntegerProperty currentTrack = new SimpleIntegerProperty(-1);
private MediaPlayer player;
@ -30,7 +32,12 @@ public class PlayManager {
private DoubleProperty currentPercent = new SimpleDoubleProperty();
private ObjectProperty<Duration> timeLeft = new SimpleObjectProperty<>();
private ObjectProperty<Duration> currentTime = new SimpleObjectProperty<>();
private ObjectProperty<Duration> totalTime = new SimpleObjectProperty<>();
private ObjectProperty<Duration> totalTime = new SimpleObjectProperty<>(new Duration(0));
private ObjectProperty<URL> currentPicture = new SimpleObjectProperty<>();
private StringProperty currentTitle = new SimpleStringProperty();
private StringProperty currentArtist = new SimpleStringProperty();
private ChangeListener<Duration> currentTimeChange = new ChangeListener<Duration>() {
@Override
public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration c) {
@ -45,53 +52,116 @@ public class PlayManager {
}
private void load(Track track) {
player = new MediaPlayer(new Media(track.getFile().toString()));
player = new MediaPlayer(new Media(track.fileProperty().get().toString()));
// To player
player.muteProperty().bind(muted);
player.volumeProperty().bind(volume);
// From player
player.currentTimeProperty().addListener(currentTimeChange);
totalTime.set(player.totalDurationProperty().get());
totalTime.bind(player.totalDurationProperty());
currentPicture.bind(track.pictureProperty());
currentTitle.bind(track.titleProperty());
currentArtist.bind(track.artistProperty());
}
public void play() {
if (state.equals(PAUSED) || state.equals(STOPPED)) {
player.play();
state.set(PLAYING);
} else if (state.equals(ENDED)) {
if (currentTrack.get() == -1) {
next();
} else if (player == null) {
} else {
if (state.get() == PAUSED || state.get() == STOPPED) {
player.play();
state.set(PLAYING);
state.get();
} else if (state.get() == ENDED) {
next();
}
}
}
public void pause() {
if (state.equals(PLAYING)) {
player.pause();
if (player == null) {
} else {
if (state.get() == PLAYING) {
player.pause();
state.set(PAUSED);
}
}
}
public void stop() {
if (player == null) {
} else {
player.stop();
state.set(PAUSED);
}
}
public void previous() {
if (currentTime.get().toSeconds() < 1) {
player.stop();
if (currentTrack.get() - 1 > 0) {
currentTrack.subtract(1);
load(queue.get(currentTrack.get()));
}
if (player == null) {
} else {
player.seek(new Duration(0));
if (currentTime.get().toSeconds() < 2) {
stop();
if (currentTrack.get() - 1 > 0) {
currentTrack.set(currentTrack.get() - 1);
load(queue.get(currentTrack.get()));
play();
}
} else {
player.seek(new Duration(0));
}
}
}
public void next() {
player.stop();
if (currentTrack.get() + 1 < queue.size()) {
currentTrack.add(1);
load(queue.get(currentTrack.get()));
if (player != null) {
stop();
}
if (currentTrack.get() + 1 < queue.size()) {
currentTrack.set(currentTrack.get() + 1);
load(queue.get(currentTrack.get()));
play();
}
}
public void seek(Duration t) {
player.seek(t);
}
public void setIndex(int selectedIndex) {
currentTrack.set(selectedIndex - 1);
next();
}
public void addToQueue(Track track) {
queue.add(track);
}
public void addAllToQueue(ObservableList<Track> tracks) {
queue.addAll(tracks);
}
public void clearQueue() {
if (player != null) {
stop();
}
queue.clear();
currentTrack.set(-1);
}
// Was for creating a new PlayManager, but decided to just reset the queue
/*
public void destroy() {
queue.clear();
player.stop();
player.dispose();
}
*/
public BooleanProperty muteProperty() {
return muted;
}
@ -120,6 +190,18 @@ public class PlayManager {
return totalTime;
}
public ObjectProperty<URL> currentPictureProperty() {
return currentPicture;
}
public StringProperty currentTitleProperty() {
return currentTitle;
}
public StringProperty currentArtistProperty() {
return currentArtist;
}
public static final class PlayManagerState {
private final String text;

View File

@ -1,5 +1,8 @@
package io.makerforce.undefined.util;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.image.Image;
import javafx.util.Duration;
import org.json.JSONArray;
import org.json.JSONException;
@ -47,11 +50,20 @@ public class Util {
public static String getNotEmpty(String... strings) {
for (String s : strings) {
if (!s.isEmpty()) {
if (s != null && !s.isEmpty()) {
return s;
}
}
return "";
}
public static ObjectProperty<Image> getImageFromURLProperty(ObjectProperty<URL> urlObjectProperty) {
ObjectProperty<Image> i = new SimpleObjectProperty<>();
urlObjectProperty.addListener(u -> {
if (u != null) {
i.set(new Image(urlObjectProperty.get().toString()));
}
});
return i;
}
}

View File

@ -1,5 +1,6 @@
package io.makerforce.undefined.view;
import io.makerforce.undefined.model.Item;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
@ -51,4 +52,8 @@ public class CoverItemController extends AnchorPane {
this(null, "", "", "");
}
public CoverItemController(Item i) {
this(new Image(String.valueOf(i.pictureProperty().get())), i.titleProperty().get(), i.subtitleProperty().get());
}
}

View File

@ -1,5 +1,6 @@
package io.makerforce.undefined.view;
import io.makerforce.undefined.model.Item;
import io.makerforce.undefined.model.ItemList;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.FlowPane;
@ -21,4 +22,16 @@ public class CoverListController extends FlowPane {
}
}
public void setItemList(ItemList list) {
itemList = list;
list.getItems().forEach(i -> {
this.getChildren().add(new CoverItemController((Item) i));
});/*
this.getChildren().addAll(
(Collection<CoverItemController>)
list.getItems().stream().map(i -> new CoverItemController((Item) i))
.collect(Collectors.toList()));
*/
}
}

View File

@ -1,6 +1,5 @@
package io.makerforce.undefined.view;
import io.makerforce.undefined.model.Track;
import io.makerforce.undefined.util.LibraryManager;
import io.makerforce.undefined.util.PlayManager;
import io.makerforce.undefined.util.Util;
@ -11,22 +10,22 @@ import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.VBox;
import javafx.util.Duration;
import org.json.JSONObject;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class InterfaceController {
//private MediaPlayer player;
private static PlayManager player = new PlayManager();
private static LibraryManager libraryManager = new LibraryManager();
@FXML
private VBox leftPane;
@FXML
private VBox currentDetails;
@FXML
@ -35,23 +34,19 @@ public class InterfaceController {
private Label currentTitle;
@FXML
private Label currentArtist;
@FXML
private ListView<String> showList;
private ObservableList<String> showListItems = FXCollections.observableArrayList("Albums", "Artists", "Songs");
private ObservableList<String> showListItems = FXCollections.observableArrayList("Artists", "Albums", "Songs");
@FXML
private ScrollPane scrollPane;
@FXML
private FlowPane flowPane;
//@FXML
private TrackListController trackList;
//@FXML
private CoverListController coverList;
private CoverListController albumList;
//@FXML
private CoverListController artistList;
private Image playIcon = new Image("/icons/play3.48.png");
private Image pauseIcon = new Image("/icons/pause2.48.png");
@FXML
private Button pausePlayButton;
@FXML
@ -66,48 +61,44 @@ public class InterfaceController {
private Label playbackTimeLabel;
@FXML
private Label playbackLeftLabel;
@FXML
private ToggleButton muteToggle;
@FXML
private ImageView muteToggleIcon;
@FXML
private Slider volumeSlider;
private Image cloudUpdatingIcon = new Image("/icons/cloud-download.32.png");
private Image cloudReadyIcon = new Image("/icons/cloud-check.32.png");
private Image cloudErrorIcon = new Image("/icons/warning.32.png");
@FXML
private ImageView statusIcon;
//private MediaPlayer player;
private PlayManager player = new PlayManager();
public InterfaceController() {
}
public static PlayManager getPlayManager() {
return player;
}
public static LibraryManager getLibraryManager() {
return libraryManager;
}
public void initialize() {
trackList = new TrackListController();
coverList = new CoverListController();
albumList = new CoverListController();
artistList = new CoverListController();
// Temporary stuff
/*
currentImage.setImage(new Image("http://ambrose.makerforce.io:8080/art/Alan%20Walker/Fade/1"));
currentArtist.setText("Alan Walker");
currentTitle.setText("Fade");
trackList = new TrackListController(new Image("http://ambrose.makerforce.io:8080/art/Alan%20Walker/Spectre/1"), "Spectre", "Alan Walker");
coverList = new CoverListController();
flowPane.getChildren().add(new CoverItemController(new Image("http://ambrose.makerforce.io:8080/art/Alan%20Walker/Spectre/1"), "Spectre", "Alan Walker"));
flowPane.getChildren().add(new CoverItemController(new Image("http://ambrose.makerforce.io:8080/art/Alan%20Walker/Fade/1"), "Fade", "Alan Walker"));
flowPane.getChildren().add(new CoverItemController(new Image("http://ambrose.makerforce.io:8080/art/Alan%20Walker/Fade/1"), "Alan Walker"));
flowPane.getChildren().add(new CoverItemController(new Image("http://ambrose.makerforce.io:8080/art/Alan%20Walker/Fade/1"), "Alan Walker"));
*/
/*
try {
player.addToQueue(new Track(new JSONObject(""), "", "", new URL(LibraryManager.DEFAULT_ENDPOINT)));
player.addToQueue(new Track(new JSONObject("{\"title\":\"Fade\",\"artist\":[\"Alan Walker\"],\"albumartist\":[\"Various Artists\"],\"album\":\"Fade\",\"year\":\"2014\",\"track\":{\"no\":7,\"of\":0},\"genre\":[\"Dance & House\"],\"disk\":{\"no\":1,\"of\":0},\"picture\":\"/art/Alan%20Walker/Fade/1\",\"duration\":0,\"number\":1,\"file\":\"/tracks/Alan%20Walker/Fade/Fade.mp3\"}"), "", "", new URL(LibraryManager.DEFAULT_ENDPOINT)));
} catch (MalformedURLException e) {
e.printStackTrace();
}
*/
// UI Bindings
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
@ -126,22 +117,56 @@ public class InterfaceController {
showList.selectionModelProperty().get().selectedIndexProperty().addListener(observable2 -> {
int sel = showList.selectionModelProperty().get().getSelectedIndex();
if (sel == 0) {
scrollPane.setContent(flowPane);
scrollPane.setContent(artistList);
// artists
} else if (sel == 1) {
scrollPane.setContent(flowPane);
scrollPane.setContent(albumList);
// albums
} else if (sel == 2) {
scrollPane.setContent(trackList);
}
});
// Library bindings
libraryManager.stateProperty().addListener(observable -> {
if (libraryManager.stateProperty().get() == LibraryManager.UPDATING) {
statusIcon.setImage(cloudUpdatingIcon);
} else if (libraryManager.stateProperty().get() == LibraryManager.READY) { // HMM tells me that not on FX thread...
Platform.runLater(() -> {
trackList.setItemList(libraryManager.getLibrary().getTracks());
albumList.setItemList(libraryManager.getLibrary().getAlbums());
artistList.setItemList(libraryManager.getLibrary());
statusIcon.setImage(cloudReadyIcon);
});
clearStatusIcon();
} else if (libraryManager.stateProperty().get() == LibraryManager.ERROR) {
statusIcon.setImage(cloudErrorIcon);
//clearStatusIcon();
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
libraryManager.update();
t.cancel(); // Here's the solution to the problem of unable to shut down.
}
}, 1000); // Quick hack to trigger more frequent updates.
}
});
//libraryManager.schedule();
// Player bindings
// To player
player.muteProperty().bind(muteToggle.selectedProperty());
player.volumeProperty().bind(volumeSlider.valueProperty());
// Player events
// From player
currentImage.imageProperty().bind(Util.getImageFromURLProperty(player.currentPictureProperty()));
currentTitle.textProperty().bind(player.currentTitleProperty());
currentArtist.textProperty().bind(player.currentArtistProperty());
// Playback status
// Playback status (From player)
player.stateProperty().addListener((observable, old, state) -> {
if (state == PlayManager.PLAYING) {
pausePlayButtonIcon.setImage(pauseIcon);
@ -150,23 +175,23 @@ public class InterfaceController {
}
});
// Update slider position when music plays.
// Update slider position when music plays. (From player)
playbackSlider.valueProperty().bind(player.currentPercent());
// Update labels.
playbackSlider.valueProperty().addListener((observable1, oldValue1, v) -> {
// Update labels. (From slider)
playbackSlider.valueProperty().addListener((observable, old, v) -> {
Duration d = new Duration(v.doubleValue() * player.totalTimeProperty().get().toMillis());
playbackTimeLabel.setText(Util.durationToString(d));
playbackLeftLabel.setText(Util.durationToString(d.subtract(player.totalTimeProperty().get())));
});
// Seek when slider is released.
playbackSlider.valueChangingProperty().addListener((observable, oldValue, c) -> {
// Seek when slider is released. (To player)
playbackSlider.valueChangingProperty().addListener((observable, old, c) -> {
if (c) {
playbackSlider.valueProperty().unbind();
} else {
playbackSlider.valueProperty().bind(player.currentPercent());
//player.seek(new Duration(playbackSlider.getValue() * player.totalTimeProperty().get().toMillis()));
player.seek(new Duration(playbackSlider.getValue() * player.totalTimeProperty().get().toMillis()));
}
});
@ -186,13 +211,30 @@ public class InterfaceController {
@FXML
private void nextTrack() {
player.next();
}
@FXML
private void previousTrack() {
player.previous();
}
private void clearStatusIcon(int milliseconds) {
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
statusIcon.setImage(null);
t.cancel(); // Here's the solution to the problem of unable to shut down.
}
}, milliseconds);
}
private void clearStatusIcon() {
clearStatusIcon(4000);
}
}

View File

@ -1,9 +1,13 @@
package io.makerforce.undefined.view;
import io.makerforce.undefined.model.ItemList;
import io.makerforce.undefined.model.Track;
import javafx.beans.value.ObservableIntegerValue;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
@ -24,6 +28,19 @@ public class TrackListController extends VBox {
@FXML
private ImageView imageView;
@FXML
private TableColumn<Track, Integer> trackNumberTableColumn;
@FXML
private TableColumn<Track, String> trackTitleTableColumn;
@FXML
private TableColumn<Track, String> trackArtistTableColumn;
@FXML
private TableColumn<Track, String> trackAlbumTableColumn;
@FXML
private TableColumn<Track, String> trackLengthTableColumn;
private ItemList itemList;
public TrackListController(Image image, String title, String subtitle) {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("tracklist.fxml"));
fxmlLoader.setRoot(this);
@ -37,6 +54,17 @@ public class TrackListController extends VBox {
titleLabel.setText(title);
subtitleLabel.setText(subtitle);
coverContainer.setManaged(!(subtitle.isEmpty() && title.isEmpty()));
trackNumberTableColumn.setCellValueFactory(track -> track.getValue().trackNumberProperty().asObject());
trackTitleTableColumn.setCellValueFactory(track -> track.getValue().titleProperty());
trackArtistTableColumn.setCellValueFactory(track -> track.getValue().artistProperty());
trackAlbumTableColumn.setCellValueFactory(track -> track.getValue().albumProperty());
tableView.getSelectionModel().selectedIndexProperty().addListener((selectedIndex) -> {
InterfaceController.getPlayManager().clearQueue();
InterfaceController.getPlayManager().addAllToQueue(itemList.getItems());
InterfaceController.getPlayManager().setIndex(((ObservableIntegerValue) selectedIndex).get());
//InterfaceController.getPlayManager().play();
System.out.println(((ObservableIntegerValue) selectedIndex).get());
});
}
public TrackListController(Image image, String title) {
@ -47,4 +75,9 @@ public class TrackListController extends VBox {
this(null, "", "");
}
public void setItemList(ItemList list) {
itemList = list;
tableView.setItems(list.getItems());
}
}

View File

@ -6,7 +6,7 @@
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400.0"
minWidth="600.0" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40"
minWidth="600.0" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/null"
fx:controller="io.makerforce.undefined.view.InterfaceController">
<bottom>
<HBox alignment="CENTER_LEFT" spacing="7.0" BorderPane.alignment="CENTER_LEFT">
@ -66,17 +66,21 @@
</Button>
</children>
<HBox.margin>
<Insets left="7.0" right="7.0"/>
<Insets/>
</HBox.margin>
<padding>
<Insets left="7.0" right="7.0"/>
</padding>
</HBox>
<Separator orientation="VERTICAL"/>
<Separator minWidth="-Infinity" orientation="VERTICAL" prefWidth="2.0"/>
<HBox alignment="CENTER" spacing="7.0" HBox.hgrow="ALWAYS">
<children>
<Label fx:id="playbackTimeLabel" minWidth="42.0" prefHeight="24.0" text="--:--"/>
<StackPane HBox.hgrow="ALWAYS">
<children>
<ProgressBar fx:id="bufferProgress" minHeight="-Infinity" prefHeight="8.0"
prefWidth="1000.0" progress="0.0" StackPane.alignment="BOTTOM_CENTER">
prefWidth="1000.0" progress="0.0" visible="false"
StackPane.alignment="BOTTOM_CENTER">
<StackPane.margin>
<Insets bottom="-5.0" left="4.0" right="6.0"/>
</StackPane.margin>
@ -91,8 +95,11 @@
<HBox.margin>
<Insets/>
</HBox.margin>
<padding>
<Insets left="7.0" right="7.0"/>
</padding>
</HBox>
<Separator orientation="VERTICAL"/>
<Separator minWidth="-Infinity" orientation="VERTICAL" prefWidth="2.0"/>
<HBox alignment="CENTER" spacing="7.0">
<children>
<ToggleButton fx:id="muteToggle" mnemonicParsing="false">
@ -111,16 +118,26 @@
<Font size="1.0"/>
</font>
</ToggleButton>
<Slider fx:id="volumeSlider" max="1.0" min="0.0" prefHeight="16.0" prefWidth="64.0"
<Slider fx:id="volumeSlider" max="1.0" min="0.0" minWidth="-Infinity" prefWidth="64.0"
value="1.0"/>
</children>
<padding>
<Insets left="7.0" right="7.0"/>
</padding>
</HBox>
<Separator orientation="VERTICAL"/>
<Separator minWidth="-Infinity" orientation="VERTICAL" prefWidth="2.0"/>
<HBox alignment="CENTER" prefWidth="16.0">
<children>
<ImageView fx:id="statusIcon" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true"
preserveRatio="true"/>
<ImageView fx:id="statusIcon" fitHeight="16.0" fitWidth="16.0" opacity="0.75"
pickOnBounds="true" preserveRatio="true">
<HBox.margin>
<Insets/>
</HBox.margin>
</ImageView>
</children>
<padding>
<Insets bottom="3.5" left="3.5" right="3.5" top="3.5"/>
</padding>
</HBox>
</children>
<padding>

View File

@ -6,7 +6,7 @@
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<fx:root xmlns:fx="http://javafx.com/fxml/1" type="VBox" xmlns="http://javafx.com/javafx/8.0.40">
<fx:root xmlns:fx="http://javafx.com/fxml/1" type="VBox" xmlns="http://javafx.com/javafx/null">
<children>
<HBox fx:id="coverContainer" alignment="CENTER">
<children>
@ -29,11 +29,16 @@
</HBox>
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
<columns>
<TableColumn editable="false" prefWidth="28.0" resizable="false" sortable="false" text="#"/>
<TableColumn editable="false" prefWidth="200.0" sortable="false" text="Title"/>
<TableColumn editable="false" prefWidth="90.0" sortable="false" text="Artist"/>
<TableColumn editable="false" prefWidth="90.0" sortable="false" text="Album"/>
<TableColumn editable="false" prefWidth="70.0" sortable="false" text="Length"/>
<TableColumn fx:id="trackNumberTableColumn" editable="false" prefWidth="28.0" resizable="false"
sortable="false" text="#"/>
<TableColumn fx:id="trackTitleTableColumn" editable="false" prefWidth="200.0" sortable="false"
text="Title"/>
<TableColumn fx:id="trackArtistTableColumn" editable="false" prefWidth="90.0" sortable="false"
text="Artist"/>
<TableColumn fx:id="trackAlbumTableColumn" editable="false" prefWidth="90.0" sortable="false"
text="Album"/>
<TableColumn fx:id="trackLengthTableColumn" editable="false" prefWidth="70.0" sortable="false"
text="Length"/>
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>