diff --git a/README.md b/README.md index 28c280c..dcc865a 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,13 @@ The effect has two different ways to set the colors, **album art** and **color p To add a color to the palette, use the color selection dropdown, and then press the _Add Color_ button to add it to the list. To remove a color from the palette, select the color in the list, then press the remove button. Finally, press the save button to apply your changes and update the colors. To go back to album art mode, open the settings drop down and enable it again. When you exit the program, both your color palette and color made will be saved for next time. +##### Switching the Effect +

+ +

+ +To change the effect, open the setting drop down, and move your mouse over the 'Choose Effect' option and another menu, with the [available effects](#available-effects) should appear. Your current effect will appear above the 'Now Playing' label in the playback view. + ## Troubleshooting @@ -87,22 +94,14 @@ To add a color to the palette, use the color selection dropdown, and then press * It can take a moment for the program to notice the song has changed, but once you see it change on the program, it should be reflected on the device. * If it still doesn't respond, in the settings menu there is a _reload effect_ button. Press that to restart the effect. If it doesn't work, I recommend restarting the program. - +## Available Effects: +- Pulse Beat: The classic and original effect. Creates a ripple that pulses out from a random panel on every beat. +- Fireworks: Lights up a random group of panels on every beat, like distant fireworks. +- Vibe: More minimal and bright, on every beat the color of a random panel gets brighter, and every bar the color changes. ## Future Goals -For future releases, here is my list of some of my goals ordered by priority: - -#### v1.0: -- [X] Refactoring and Improving Performance -- [X] Redo of the Color Palette UI -- [X] UI Overhaul - -#### v1.5: -- [ ] Adding Java Documentation to Source Code -- [ ] Adding a Firework Effect -- [ ] Adding Player Controls to UI - +All of my future goals for releases are in the project section of this repository, and you can keep track of the progress of them. If you have any suggestions for features, you can open an issue and I'll look into it. ## Credits diff --git a/assets/step4.png b/assets/step4.png new file mode 100644 index 0000000..35b32d8 Binary files /dev/null and b/assets/step4.png differ diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/DataManager.java b/src/main/java/dev/jaxcksn/nanoleafMusic/DataManager.java index 8207c4d..db377ec 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/DataManager.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/DataManager.java @@ -5,6 +5,7 @@ package dev.jaxcksn.nanoleafMusic; +import ch.qos.logback.classic.Logger; import com.github.kevinsawicki.http.HttpRequest; import dev.jaxcksn.nanoleafMusic.effects.EffectType; import dev.jaxcksn.nanoleafMusic.utility.DataManagerException; @@ -12,6 +13,7 @@ import dev.jaxcksn.nanoleafMusic.utility.dMEC; import io.github.rowak.nanoleafapi.Aurora; import io.github.rowak.nanoleafapi.StatusCodeException; +import org.slf4j.LoggerFactory; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; @@ -25,9 +27,11 @@ public class DataManager { private static final Preferences preferences = Preferences.userNodeForPackage(Main.class); public boolean hasSaved; + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.DataManager"); public DataManager() { - String testForSaved = preferences.get("savedDevice",null); + String testForSaved = preferences.get("savedDevice", null); hasSaved = testForSaved != null && !testForSaved.isEmpty(); } @@ -39,6 +43,7 @@ public void saveDevice(Aurora device) { ";" + device.getAccessToken(); preferences.put("savedDevice", str); + logger.info("Saved {} to preferences", device.getName()); try { preferences.flush(); } catch (BackingStoreException e) { @@ -49,6 +54,7 @@ public void saveDevice(Aurora device) { public Aurora loadDevice() { String saved = preferences.get("savedDevice",null); if (saved == null || saved.isEmpty() || !hasSaved) { + logger.error("Could not load from preferences, key is null or empty."); throw new DataManagerException("Could not load from preferences, key is null or empty.", dMEC.NDS); } else { try { @@ -57,12 +63,15 @@ public Aurora loadDevice() { int port = Integer.parseInt(deviceData[1]); String accessToken = deviceData[2]; try { + logger.info("Loading device at {} from preferences", hostName); return new Aurora(hostName,port,"v1",accessToken); } catch (StatusCodeException | HttpRequest.HttpRequestException e) { + logger.error("Error creating device object from saved data.", e); throw new DataManagerException("Error creating device object from saved data.",dMEC.ISD); } } catch (Exception e) { + logger.error("Could not process saved device data, string may be malformed.", e); throw new DataManagerException("Could not process saved device data, string may be malformed.", dMEC.MDS); } } @@ -70,17 +79,12 @@ public Aurora loadDevice() { public void removeDevice() { preferences.remove("savedDevice"); + logger.info("Removed saved device from preferences"); hasSaved = false; } public static Settings loadSettings() { - boolean albumColors = preferences.getBoolean("useAlbumColors",true); - int albumPaletteLength = preferences.getInt("numberOfAlbumColors",6); - if(albumPaletteLength > 12) { - albumPaletteLength = 12; - } else if (albumPaletteLength < 3) { - albumPaletteLength = 3; - } + boolean albumColors = preferences.getBoolean("useAlbumColors", true); String colorPalette = preferences.get("colorPalette", "#FF0000,#00FF00,#0000FF"); if (colorPalette.length() > 95) { colorPalette = colorPalette.substring(0, 95); @@ -89,27 +93,33 @@ public static Settings loadSettings() { } String effectString = preferences.get("selectedEffect", "PULSEBEAT"); EffectType activeEffectType = EffectType.valueOf(effectString); - return new Settings(albumColors, albumPaletteLength, colorPalette, activeEffectType); + logger.info("Loaded settings from preferences"); + return new Settings(albumColors, colorPalette, activeEffectType); } public static void updateSettings(Settings settings) { preferences.putBoolean("useAlbumColors", settings.albumColors); preferences.put("colorPalette", settings.colorPalette); + preferences.put("savedEffect", settings.activeEffectType.toString()); + logger.info("Updated settings in preferences"); } public static void changeAlbumMode(boolean b) { preferences.putBoolean("useAlbumColors", b); + logger.info("Changed album mode to {} in preferences", b); } public static void changeEffectType(EffectType effectType) { preferences.put("selectedEffect", effectType.toString()); + logger.info("Changed saved effect type to {} in preferences", effectType); } public static void clearSavedData() { try { preferences.clear(); + logger.info("Cleared all data from preferences"); } catch (BackingStoreException e) { - e.printStackTrace(); + Main.showException(e); } } diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/EffectManager.java b/src/main/java/dev/jaxcksn/nanoleafMusic/EffectManager.java index fb6ee29..c0194ee 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/EffectManager.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/EffectManager.java @@ -5,6 +5,7 @@ package dev.jaxcksn.nanoleafMusic; +import ch.qos.logback.classic.Logger; import com.wrapper.spotify.SpotifyApi; import com.wrapper.spotify.exceptions.SpotifyWebApiException; import com.wrapper.spotify.model_objects.credentials.AuthorizationCodeCredentials; @@ -18,10 +19,7 @@ import com.wrapper.spotify.requests.data.tracks.GetAudioAnalysisForTrackRequest; import de.androidpit.colorthief.ColorThief; import dev.jaxcksn.nanoleafMusic.controllers.PlaybackView; -import dev.jaxcksn.nanoleafMusic.effects.EffectType; -import dev.jaxcksn.nanoleafMusic.effects.FireworkEffect; -import dev.jaxcksn.nanoleafMusic.effects.MusicEffect; -import dev.jaxcksn.nanoleafMusic.effects.PulseBeatEffect; +import dev.jaxcksn.nanoleafMusic.effects.*; import dev.jaxcksn.nanoleafMusic.utility.PaletteColor; import dev.jaxcksn.nanoleafMusic.utility.Settings; import dev.jaxcksn.nanoleafMusic.utility.SpecificAudioAnalysis; @@ -34,6 +32,7 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; import org.apache.hc.core5.http.ParseException; +import org.slf4j.LoggerFactory; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; @@ -41,7 +40,6 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.net.URL; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -64,13 +62,14 @@ public class EffectManager { public Settings settings; public Color[] palette = new Color[]{Color.RED, Color.BLUE, Color.GREEN}; private ScheduledExecutorService sES; + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.EffectManager"); public EffectManager(SpotifyApi spotifyApi, int expiresIn, Aurora device, PlaybackView viewController) { this.spotifyApi = spotifyApi; this.expiresIn = expiresIn; this.device = device; this.viewController = viewController; - settings = DataManager.loadSettings(); switch (settings.activeEffectType) { case FIREWORKS: @@ -79,14 +78,16 @@ public EffectManager(SpotifyApi spotifyApi, int expiresIn, Aurora device, Playba case PULSEBEAT: this.activeEffect = new PulseBeatEffect(palette, device); break; + case VIBE: + this.activeEffect = new VibeEffect(palette, device); + break; } - System.out.println("\u001b[92;1m✔\u001b[0m Effect Manager Loaded"); + logger.info("Effect manager object initialized, starting refresh timer"); startRefreshTimer(); } public void switchEffect(EffectType effectType) { - System.out.println("\u001b[96;1mℹ\u001b[0m Changing Effect"); settings.activeEffectType = effectType; Color[] currentPalette = activeEffect.getPalette(); switch (effectType) { @@ -96,16 +97,20 @@ public void switchEffect(EffectType effectType) { case PULSEBEAT: this.activeEffect = new PulseBeatEffect(currentPalette, device); break; + case VIBE: + this.activeEffect = new VibeEffect(currentPalette, device); + break; } } public void reloadEffect() { - System.out.println("\n" + "\u001b[96;1mℹ\u001b[0m Attempting to Restart Effect"); + logger.info("Reloading Effect..."); + logger.warn("Attempting to shut down the Scheduled Executor Service. If this fails, the program can't recover."); sES.shutdownNow(); try { - if (!sES.awaitTermination(30, TimeUnit.SECONDS)) { - System.err.println("Pool did not terminate"); + if (!sES.awaitTermination(60, TimeUnit.SECONDS)) { + throw new Exception("Scheduled Executor Service failed to shutdown"); } else { isPlaying = false; isRunning = false; @@ -115,10 +120,11 @@ public void reloadEffect() { activeEffect.setPalette(palette); } this.startEffect(); - System.out.println("\u001b[92;1m✔\u001b[0m Finished Restarting Effect\n"); + logger.info("Finished reloading the effect"); } - } catch (InterruptedException e) { - e.printStackTrace(); + } catch (Exception e) { + Main.showException(e); + System.exit(1); } @@ -135,7 +141,7 @@ private void startRefreshTimer() { spotifyApi.setRefreshToken(authorizationCodeCredentials.getRefreshToken()); expiresIn = authorizationCodeCredentials.getExpiresIn(); } catch (ParseException | IOException e) { - e.printStackTrace(); + Main.showException(e); } catch (SpotifyWebApiException spotifyWebApiException) { showSWAE(spotifyWebApiException); } @@ -151,15 +157,15 @@ public void startEffect() { if (!settings.albumColors) { palette = PaletteColor.toEffectColorArray(settings.colorPalette); } - - sES = Executors.newScheduledThreadPool(4 * Runtime.getRuntime().availableProcessors()); - System.out.println("\u001b[96;1mℹ\u001b[0m Using " + 4 * Runtime.getRuntime().availableProcessors() + " threads."); + int availableProcessors = Runtime.getRuntime().availableProcessors(); + sES = Executors.newScheduledThreadPool(4 * availableProcessors, new Main.NamedThreadFactory("effect")); + logger.info("There are {} cores available, using a thread pool of {} threads", availableProcessors, 4 * availableProcessors); Runnable effectPulseTask = () -> { if (isPlaying) { try { pulseTask(); } catch (StatusCodeException | IOException e) { - e.printStackTrace(); + Main.showException(e); } } }; @@ -168,7 +174,7 @@ public void startEffect() { try { spotifyTask(); } catch (ParseException | IOException | InterruptedException e) { - e.printStackTrace(); + Main.showException(e); } catch (SpotifyWebApiException spotifyWebApiException) { showSWAE(spotifyWebApiException); } @@ -176,18 +182,18 @@ public void startEffect() { //Prevents UI From Freezing Up when Nothing is Playing Thread initThread = new Thread(this::initEffect); + initThread.setName("effect-init"); initThread.start(); - System.out.println("\u001b[92;1m✔\u001b[0m Starting Initialization"); try { initThread.join(); } catch (InterruptedException e) { - e.printStackTrace(); + Main.showException(e); } sES.scheduleAtFixedRate(effectPulseTask, 0, 100, TimeUnit.MILLISECONDS); - System.out.println("\u001b[92;1m✔\u001b[0m Pulse Timers Started"); + logger.info("Effect timer was scheduled"); sES.scheduleAtFixedRate(spotifyUpdateTask, 0, 2000, TimeUnit.MILLISECONDS); - System.out.println("\u001b[92;1m✔\u001b[0m Spotify Update Timers Started"); + logger.info("Spotify Poll timer was scheduled"); displayTrackInformation(true, false); } } @@ -196,7 +202,7 @@ private void initEffect() { try { CurrentlyPlaying currentlyPlaying = getCurrentlyPlaying(); if (currentlyPlaying == null) { - System.out.println("\u001b[96;1mℹ\u001b[0m Current Playback returned null, starting wait loop."); + logger.warn("Nothing is playing on Spotify, polling again in 5 seconds."); } while (currentlyPlaying == null && !isPlaying) { TimeUnit.SECONDS.sleep(5); @@ -207,9 +213,9 @@ private void initEffect() { currentTrackAnalysis = getTrackAnalysis(currentTrack.getId()); progress = currentlyPlaying.getProgress_ms(); isPlaying = true; - System.out.println("\u001b[92;1m✔\u001b[0m Finished initialization"); + logger.info("Finished effect initialization"); } catch (ParseException | IOException | InterruptedException e) { - e.printStackTrace(); + Main.showException(e); } catch (SpotifyWebApiException spotifyWebApiException) { showSWAE(spotifyWebApiException); } @@ -261,49 +267,34 @@ private void pulseTask() throws StatusCodeException, IOException { private void spotifyTask() throws ParseException, SpotifyWebApiException, IOException, InterruptedException { CurrentlyPlaying currentPlayback = getCurrentlyPlaying(); if (currentPlayback == null) { + if (isPlaying) { + logger.warn("Nothing is playing on Spotify. "); + } isPlaying = false; - CountDownLatch playLatch = new CountDownLatch(1); - displayTrackInformation(false, false); - new Thread(() -> { - try { - CurrentlyPlaying current = getCurrentlyPlaying(); - while (current == null) { - TimeUnit.SECONDS.sleep(4); - current = getCurrentlyPlaying(); - } - playLatch.countDown(); - } catch (ParseException | IOException | InterruptedException e) { - e.printStackTrace(); - } catch (SpotifyWebApiException spotifyWebApiException) { - showSWAE(spotifyWebApiException); - } - }).start(); - playLatch.await(); - currentPlayback = getCurrentlyPlaying(); - } + viewController.setPlayback(false); + } else { + Track newTrack = ((Track) currentPlayback.getItem()); + if (!currentTrack.getId().equals(newTrack.getId())) { + currentTrack = newTrack; + currentTrackAnalysis = getTrackAnalysis(newTrack.getId()); + progress = currentPlayback.getProgress_ms(); + displayTrackInformation(true, false); + } - Track newTrack = ((Track) currentPlayback.getItem()); - if(!currentTrack.getId().equals(newTrack.getId())) { - currentTrack = newTrack; - currentTrackAnalysis = getTrackAnalysis(newTrack.getId()); - progress = currentPlayback.getProgress_ms(); - displayTrackInformation(true, false); - } + float progressDifference = Math.abs(currentPlayback.getProgress_ms() - progress); + if (currentPlayback.getIs_playing() && !isPlaying) { + isPlaying = true; - float progressDifference = Math.abs(currentPlayback.getProgress_ms() - progress); - if(currentPlayback.getIs_playing() && !isPlaying) { - isPlaying = true; - progress = currentPlayback.getProgress_ms() + 500; - displayTrackInformation(true, false); - } else if(!currentPlayback.getIs_playing() && isPlaying) { - isPlaying = false; - progress = currentPlayback.getProgress_ms(); - displayTrackInformation(true, true); - } else if (currentPlayback.getIs_playing() && progressDifference >= 10) { - progress = currentPlayback.getProgress_ms(); + progress = currentPlayback.getProgress_ms() + 500; + displayTrackInformation(true, false); + } else if (!currentPlayback.getIs_playing() && isPlaying) { + isPlaying = false; + progress = currentPlayback.getProgress_ms(); + displayTrackInformation(true, true); + } else if (currentPlayback.getIs_playing() && progressDifference >= 10) { + progress = currentPlayback.getProgress_ms(); + } } - - } public void displayTrackInformation(boolean updateArt, boolean isPaused) { @@ -335,7 +326,7 @@ public void displayTrackInformation(boolean updateArt, boolean isPaused) { int[][] colorArray = ColorThief.getPalette(image, 6); activeEffect.setPalette(colorArray); } catch (IOException e) { - e.printStackTrace(); + Main.showException(e); } viewController.setPlayback(currentTrack.getName(), songArtists, artworkURL); }).start(); diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/Main.java b/src/main/java/dev/jaxcksn/nanoleafMusic/Main.java index 5566950..09dc039 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/Main.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/Main.java @@ -6,35 +6,52 @@ package dev.jaxcksn.nanoleafMusic; import ca.weblite.objc.Proxy; -import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import dev.jaxcksn.nanoleafMusic.utility.NSProcessInfoUtils; import javafx.application.Application; +import javafx.application.Platform; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; +import javafx.scene.control.Alert; +import javafx.scene.control.Label; +import javafx.scene.control.TextArea; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; import javafx.scene.text.Font; import javafx.stage.Stage; import org.slf4j.LoggerFactory; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.Locale; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; public class Main extends Application { private static Proxy appNapPrevented; + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.Main"); public static void main(String[] args) { - Logger root = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - root.setLevel(Level.INFO); + logger.info("Application Started"); if (isMac()) { + logger.info("macOS detected, attempting to prevent App Nap"); appNapPrevented = NSProcessInfoUtils.beginActivityWithOptions("Needs to be alive to constantly update effect."); } - System.out.println("\u001b[92;1m✔\u001b[0m Starting Application"); + + Thread.UncaughtExceptionHandler handler = ((th, ex) -> { + showException(new Exception(ex)); + }); + Thread.setDefaultUncaughtExceptionHandler(handler); + launch(args); } @Override public void start(Stage stage) throws Exception { + Thread.currentThread().setName("app"); Parent root = FXMLLoader.load(getClass().getResource("/connectToDevice.fxml")); Scene scene = new Scene(root, 400, 300); @@ -44,10 +61,28 @@ public void start(Stage stage) throws Exception { Font.loadFont(getClass().getResourceAsStream("/fonts/OpenSans-ExtraBold.ttf"), 12); Font.loadFont(getClass().getResourceAsStream("/fonts/HankRnd-Black.ttf"), 12); root.getStylesheets().add("/gui.css"); + logger.info("JavaFX Assets Loaded"); stage.setTitle("nanoleafMusic"); stage.setResizable(false); stage.setScene(scene); + logger.info("Setting JavaFX scene to 'ConnectToDevice' view"); stage.show(); + + } + + public static class NamedThreadFactory implements ThreadFactory { + String name; + AtomicInteger threadNo = new AtomicInteger(0); + + public NamedThreadFactory(String name) { + this.name = name; + } + + @Override + public Thread newThread(Runnable r) { + String threadName = name + "-" + threadNo.incrementAndGet(); + return new Thread(r, threadName); + } } @Override @@ -62,4 +97,42 @@ public static boolean isMac() { String OS = System.getProperty("os.name", "unknown").toLowerCase(Locale.ROOT); return OS.contains("mac"); } + + public static void showException(Exception e) { + logger.error("An Exception was Thrown", e); + Platform.runLater(() -> { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("Exception"); + alert.setHeaderText("Program Exception Thrown"); + alert.setContentText("An exception or error was thrown while running the program. The program might not run after this happens."); + + // Create expandable Exception. + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + String exceptionText = sw.toString(); + + Label label = new Label("The exception stacktrace was:"); + + TextArea textArea = new TextArea(exceptionText); + textArea.setEditable(false); + textArea.setWrapText(true); + + textArea.setMaxWidth(Double.MAX_VALUE); + textArea.setMaxHeight(Double.MAX_VALUE); + GridPane.setVgrow(textArea, Priority.ALWAYS); + GridPane.setHgrow(textArea, Priority.ALWAYS); + + GridPane expContent = new GridPane(); + expContent.setMaxWidth(Double.MAX_VALUE); + expContent.add(label, 0, 0); + expContent.add(textArea, 0, 1); + +// Set expandable Exception into the dialog pane. + alert.getDialogPane().setExpandableContent(expContent); + alert.getDialogPane().getStylesheets().add("/gui.css"); + alert.showAndWait(); + }); + } } + diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/SpotifyManager.java b/src/main/java/dev/jaxcksn/nanoleafMusic/SpotifyManager.java index 4e29ecf..d531170 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/SpotifyManager.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/SpotifyManager.java @@ -5,6 +5,7 @@ package dev.jaxcksn.nanoleafMusic; +import ch.qos.logback.classic.Logger; import com.wrapper.spotify.SpotifyApi; import com.wrapper.spotify.SpotifyHttpManager; import com.wrapper.spotify.exceptions.SpotifyWebApiException; @@ -19,6 +20,7 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; import org.apache.hc.core5.http.ParseException; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.PrintWriter; @@ -36,7 +38,8 @@ public class SpotifyManager { .setRedirectUri(REDIRECT_URI) .build(); public CallbackServer cbServer; - + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.SpotifyManager"); public URI connectURI; public int expiresIn; @@ -45,7 +48,7 @@ public SpotifyManager() { try { pkceVerification = PKCE.generateCodeVerifier(); } catch (UnsupportedEncodingException e) { - e.printStackTrace(); + Main.showException(e); } AuthorizationCodeUriRequest authorizationCodeUriRequest = null; @@ -54,12 +57,12 @@ public SpotifyManager() { .scope("user-read-currently-playing, user-read-playback-state") .build(); } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); + Main.showException(e); } assert authorizationCodeUriRequest != null; connectURI = authorizationCodeUriRequest.execute(); cbServer = new CallbackServer(); - System.out.println("\u001b[92;1m✔\u001b[0m Started Callback Server"); + logger.info("Callback server started and listening at localhost:8001/connect"); } public void getCredentials(String accessCode) { @@ -71,7 +74,7 @@ public void getCredentials(String accessCode) { spotifyApi.setRefreshToken(authorizationCodeCredentials.getRefreshToken()); expiresIn = authorizationCodeCredentials.getExpiresIn(); } catch (ParseException | IOException e) { - e.printStackTrace(); + Main.showException(e); } catch (SpotifyWebApiException spotifyWebApiException) { Alert alert = new Alert(Alert.AlertType.ERROR); alert.setTitle("Exception"); diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/ConnectToDevice.java b/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/ConnectToDevice.java index 98b89af..d8d3634 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/ConnectToDevice.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/ConnectToDevice.java @@ -5,6 +5,7 @@ package dev.jaxcksn.nanoleafMusic.controllers; +import ch.qos.logback.classic.Logger; import dev.jaxcksn.nanoleafMusic.DataManager; import dev.jaxcksn.nanoleafMusic.Main; import dev.jaxcksn.nanoleafMusic.utility.DataManagerException; @@ -29,6 +30,7 @@ import net.straylightlabs.hola.sd.Instance; import net.straylightlabs.hola.sd.Query; import net.straylightlabs.hola.sd.Service; +import org.slf4j.LoggerFactory; import java.awt.*; import java.io.IOException; @@ -51,10 +53,12 @@ public class ConnectToDevice { private ObservableList deviceList; private List auroraList; private DataManager dataManager; + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.ConnectToDevice"); public void initialize() { dataManager = new DataManager(); - if(!dataManager.hasSaved) { + if (!dataManager.hasSaved) { reconnectBtn.setDisable(true); } @@ -64,6 +68,7 @@ public void initialize() { findDevices(); nanoleafList.setItems(deviceList); }); + refreshThread.setName("initial-mDNS"); refreshThread.start(); } catch (BufferUnderflowException ignored) { @@ -99,8 +104,8 @@ private List findNanoleaf() throws IOException { System.out.println("IPV6Exception"); } })); - } catch (IOException e) { - e.printStackTrace(); + } catch (Exception e) { + Main.showException(e); } return auroras; @@ -146,12 +151,13 @@ private void findDevices() { List auroras = null; try { auroras = findNanoleaf(); + logger.info("Found {} devices from mDNS query", auroras.size()); auroraList = auroras; for (AuroraMetadata aurora : auroras) { deviceList.add(aurora.getDeviceName()); } } catch (IOException e) { - e.printStackTrace(); + Main.showException(e); } setLoading(false); } @@ -167,7 +173,7 @@ public void reconnectToDevice(ActionEvent actionEvent) { DialogPane dialogPane = alert.getDialogPane(); dialogPane.getStylesheets().add("/gui.css"); alert.showAndWait(); - System.out.println("\u001b[92;1m✔\u001b[0m Connected to Nanoleaf"); + logger.info("Successfully connected to {}", savedDevice.getName()); transitionToSpotify(savedDevice); } catch (DataManagerException e) { Alert alert = new Alert(Alert.AlertType.ERROR); @@ -236,23 +242,27 @@ public void refreshList(ActionEvent actionEvent) { deviceList = FXCollections.observableArrayList(); try { Thread refreshThread = new Thread(() -> { + findDevices(); nanoleafList.setItems(deviceList); }); + refreshThread.setName("refresh-mDNS"); refreshThread.start(); } catch (BufferUnderflowException e) { - System.out.println("\u001b[91;1mX\u001b[0m No Devices Found."); + Main.showException(e); } } private void getAccessToken(AuroraMetadata metadata) { try { + logger.info("Asking {} for an access token", metadata.getDeviceName()); String accessToken = Setup.createAccessToken(metadata.getHostName(),metadata.getPort(),"v1"); Aurora connectedDevice = new Aurora(metadata.getHostName(),metadata.getPort(),"v1",accessToken); + logger.info("Successfully connected to {}", metadata.getDeviceName()); Alert alert = new Alert(Alert.AlertType.CONFIRMATION); alert.setHeaderText("Save device for quick reconnect?"); - alert.setContentText("You can save the device and access token to quickly reconnect last time. Saving this device will overwrite any previous saved devices."); + alert.setContentText("You can opt to save this device and access token to quickly reconnect next time. Saving this device will overwrite any previous saved devices."); DialogPane dialogPane = alert.getDialogPane(); dialogPane.setMinHeight(Region.USE_PREF_SIZE); dialogPane.getStylesheets().add("/gui.css"); @@ -260,7 +270,6 @@ private void getAccessToken(AuroraMetadata metadata) { if (result.isPresent() && result.get() == ButtonType.OK) { dataManager.saveDevice(connectedDevice); } - System.out.println("\u001b[92;1m✔\u001b[0m Connected to Nanoleaf"); transitionToSpotify(connectedDevice); } catch (StatusCodeException.ForbiddenException e) { @@ -273,7 +282,7 @@ private void getAccessToken(AuroraMetadata metadata) { alert.showAndWait(); setLoading(false); } catch (StatusCodeException e) { - e.printStackTrace(); + Main.showException(e); } } @@ -286,10 +295,10 @@ private void transitionToSpotify(Aurora device) { Stage stage = (Stage) nanoleafList.getScene().getWindow(); Scene scene = new Scene(root, 400, 300); scene.getStylesheets().add("/gui.css"); + logger.info("Setting JavaFX scene to 'ConnectToSpotify' view"); stage.setScene(scene); - } catch (IOException e) { - e.printStackTrace(); + Main.showException(e); } } diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/ConnectToSpotify.java b/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/ConnectToSpotify.java index 80b94d5..6733ba0 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/ConnectToSpotify.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/ConnectToSpotify.java @@ -5,6 +5,7 @@ package dev.jaxcksn.nanoleafMusic.controllers; +import ch.qos.logback.classic.Logger; import dev.jaxcksn.nanoleafMusic.Main; import dev.jaxcksn.nanoleafMusic.SpotifyManager; import io.github.rowak.nanoleafapi.Aurora; @@ -18,6 +19,7 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.Region; import javafx.stage.Stage; +import org.slf4j.LoggerFactory; import java.awt.*; import java.io.IOException; @@ -25,7 +27,8 @@ public class ConnectToSpotify { private Aurora device; private SpotifyManager spotifyManager; - + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.ConnectToSpotify"); @FXML private BorderPane borderPane; @FXML @@ -44,8 +47,10 @@ public void startConnection(javafx.event.ActionEvent actionEvent) { if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { try { Desktop.getDesktop().browse(spotifyManager.connectURI); + logger.info("Opened authentication URL in browser"); String accessCode = spotifyManager.cbServer.getAuthCode(); spotifyManager.getCredentials(accessCode); + logger.info("Successfully created Spotify API credentials from accessCode"); Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setHeaderText("Spotify Connected"); alert.setContentText("Spotify was successfully connected."); @@ -56,7 +61,7 @@ public void startConnection(javafx.event.ActionEvent actionEvent) { spotifyManager.cbServer.destroy(); transitionToPlayer(); } catch (IOException e) { - e.printStackTrace(); + Main.showException(e); } } } @@ -75,11 +80,11 @@ private void transitionToPlayer() { Stage stage = (Stage) borderPane.getScene().getWindow(); Scene scene = new Scene(root, 400, 300); scene.getStylesheets().add("/gui.css"); + logger.info("Setting JavaFX scene to 'PlaybackView' view"); stage.setScene(scene); - } catch (IOException e) { - e.printStackTrace(); + Main.showException(e); } } diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/PlaybackView.java b/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/PlaybackView.java index 946e494..a360fd0 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/PlaybackView.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/controllers/PlaybackView.java @@ -5,6 +5,7 @@ package dev.jaxcksn.nanoleafMusic.controllers; +import ch.qos.logback.classic.Logger; import com.wrapper.spotify.SpotifyApi; import com.wrapper.spotify.model_objects.specification.ArtistSimplified; import dev.jaxcksn.nanoleafMusic.DataManager; @@ -28,6 +29,7 @@ import javafx.scene.shape.Rectangle; import javafx.scene.text.Text; import javafx.stage.Stage; +import org.slf4j.LoggerFactory; import java.io.IOException; @@ -47,16 +49,21 @@ public class PlaybackView { public RadioMenuItem FireworksToggle; public RadioMenuItem PulseBeatToggle; public Label EffectLabel; + public RadioMenuItem VibeToggle; private EffectManager effectManager; - + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.PlaybackView"); private Scene palettePickerScene; public void initData(SpotifyApi spotifyApi, int expiresIn, Aurora device) { + logger.info("Initializing the playback view"); PulseBeatToggle.setUserData(EffectType.PULSEBEAT); FireworksToggle.setUserData(EffectType.FIREWORKS); + VibeToggle.setUserData(EffectType.VIBE); effectManager = new EffectManager(spotifyApi, expiresIn, device, this); Settings loadedSettings = effectManager.settings; + logger.info("Album mode set to {}", loadedSettings.albumColors); if (loadedSettings.albumColors) { colorPaletteSelector.setDisable(true); albumColorsCheckbox.setSelected(true); @@ -70,13 +77,21 @@ public void initData(SpotifyApi spotifyApi, int expiresIn, Aurora device) { case PULSEBEAT: PulseBeatToggle.setSelected(true); FireworksToggle.setSelected(false); + VibeToggle.setSelected(false); EffectLabel.setText("PULSEBEAT"); break; case FIREWORKS: PulseBeatToggle.setSelected(false); FireworksToggle.setSelected(true); + VibeToggle.setSelected(false); EffectLabel.setText("FIREWORKS"); break; + case VIBE: + PulseBeatToggle.setSelected(false); + FireworksToggle.setSelected(false); + VibeToggle.setSelected(true); + EffectLabel.setText("VIBE"); + break; } effectRadio.selectedToggleProperty().addListener((ObservableValue ov, @@ -89,12 +104,20 @@ public void initData(SpotifyApi spotifyApi, int expiresIn, Aurora device) { albumColorsCheckbox.selectedProperty().addListener((ObservableValue ov, Boolean old_val, Boolean new_val) -> { colorPaletteSelector.setDisable(new_val); effectManager.settings.albumColors = new_val; - new Thread(() -> DataManager.changeAlbumMode(new_val)).start(); + if (new_val) { + effectManager.displayTrackInformation(true, false); + } + + Thread setAlbumColorData = new Thread(() -> DataManager.changeAlbumMode(new_val)); + setAlbumColorData.setName("data"); + setAlbumColorData.start(); }); - new Thread(() -> { + Thread startEffectThread = new Thread(() -> { effectManager.startEffect(); - }).start(); + }); + startEffectThread.setName("effect-start"); + startEffectThread.start(); FXMLLoader palettePickerLoader = new FXMLLoader(Main.class.getResource("/palettePicker.fxml")); try { @@ -104,8 +127,9 @@ public void initData(SpotifyApi spotifyApi, int expiresIn, Aurora device) { palettePicker.updatePalette(); palettePickerScene = new Scene(palettePickerRoot, 400, 300); palettePickerScene.getStylesheets().add("/gui.css"); + logger.info("Palette picker is ready"); } catch (IOException e) { - e.printStackTrace(); + Main.showException(e); } } @@ -146,7 +170,9 @@ public void showColorView(ActionEvent event) { } public void reloadEffectManager(ActionEvent event) { + setLoading(true); effectManager.reloadEffect(); + setLoading(false); } private void setLoading(boolean status) { @@ -164,10 +190,11 @@ public void changeEffect(EffectType effectType) { setLoading(false); }).start(); - new Thread(() -> { + Thread changeEffectThread = new Thread(() -> { DataManager.changeEffectType(effectType); - }).start(); - + }); + changeEffectThread.setName("data"); + changeEffectThread.start(); EffectLabel.setText(effectType.toString()); } diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/effects/EffectType.java b/src/main/java/dev/jaxcksn/nanoleafMusic/effects/EffectType.java index cbd151d..a7946a0 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/effects/EffectType.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/effects/EffectType.java @@ -6,5 +6,5 @@ package dev.jaxcksn.nanoleafMusic.effects; public enum EffectType { - PULSEBEAT, FIREWORKS + PULSEBEAT, FIREWORKS, VIBE } diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/effects/FireworkEffect.java b/src/main/java/dev/jaxcksn/nanoleafMusic/effects/FireworkEffect.java index 392e1d5..cba9010 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/effects/FireworkEffect.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/effects/FireworkEffect.java @@ -5,9 +5,12 @@ package dev.jaxcksn.nanoleafMusic.effects; +import ch.qos.logback.classic.Logger; +import dev.jaxcksn.nanoleafMusic.Main; import dev.jaxcksn.nanoleafMusic.utility.SpecificAudioAnalysis; import io.github.rowak.nanoleafapi.*; import io.github.rowak.nanoleafapi.effectbuilder.CustomEffectBuilder; +import org.slf4j.LoggerFactory; import java.util.Random; @@ -20,6 +23,8 @@ public class FireworkEffect implements MusicEffect { private int paletteIndex = 0; public final EffectType effectType = EffectType.FIREWORKS; public boolean songChanged = false; + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.MusicEffect"); public FireworkEffect(Color[] palette, Aurora device) { this.palette = palette; @@ -27,10 +32,10 @@ public FireworkEffect(Color[] palette, Aurora device) { try { this.panels = device.panelLayout().getPanels(); } catch (StatusCodeException e) { - e.printStackTrace(); + Main.showException(e); } this.random = new Random(); - + logger.info("Fireworks effect was loaded"); } @Override @@ -60,7 +65,6 @@ private int[] adjustLuma(int[] color, double brightness) { public void setPalette(int[][] colors) { if (!albumMode) { - System.out.println("\u001b[96;1mℹ\u001b[0m Changed to Album Mode"); albumMode = true; } Color[] newPalette = new Color[colors.length]; @@ -84,22 +88,6 @@ public void setPalette(int[][] colors) { } palette = newPalette; - - /* Disabling this feature until I can prefect it. - int betterColor = 0; - int[] color0 = {newPalette[0].getRed(), newPalette[0].getGreen(), newPalette[0].getBlue()}; - int[] color1 = {newPalette[1].getRed(), newPalette[1].getGreen(), newPalette[1].getBlue()}; - int[] color2 = {newPalette[2].getRed(), newPalette[2].getGreen(), newPalette[2].getBlue()}; - double color1Colorfulness = calculateColorful(color1); - if (color1Colorfulness >= calculateColorful(color0)) { - if (calculateColorful(color2) >= color1Colorfulness) { - betterColor = 2; - } else { - betterColor = 1; - } - } - accentColor = new PaletteColor(javafx.scene.paint.Color.rgb(newPalette[betterColor].getRed(), newPalette[betterColor].getGreen(), newPalette[betterColor].getBlue())); - */ } public EffectType getEffectType() { @@ -109,7 +97,6 @@ public EffectType getEffectType() { //If the palette is manually set public void setPalette(Color[] colors) { if (albumMode) { - System.out.println("\u001b[96;1mℹ\u001b[0m Changed to Palette Mode"); albumMode = false; } palette = colors; @@ -123,7 +110,7 @@ public void run(SpecificAudioAnalysis analysis) throws StatusCodeException { int originPanelIndex = random.nextInt(panels.length); int panelID = panels[originPanelIndex].getId(); Panel[] neighbors = panels[originPanelIndex].getNeighbors(panels); - int fireworkLength = random.nextInt(neighbors.length); + int fireworkLength = random.nextInt(neighbors.length + 1); CustomEffectBuilder ceb = new CustomEffectBuilder(device); Frame toColor = new Frame(colorRGB[0], colorRGB[1], colorRGB[2], 0, 1); Frame toBlack = new Frame(0, 0, 0, 0, 5); @@ -143,7 +130,9 @@ public void run(SpecificAudioAnalysis analysis) throws StatusCodeException { try { device.effects().displayEffect(ceb.build("", false)); } catch (StatusCodeException e) { - e.printStackTrace(); + logger.warn("Unrecoverable exception was thrown. Shutting down program."); + Main.showException(e); + System.exit(1); } }).start(); diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/effects/PulseBeatEffect.java b/src/main/java/dev/jaxcksn/nanoleafMusic/effects/PulseBeatEffect.java index 237d9a8..1d93a31 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/effects/PulseBeatEffect.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/effects/PulseBeatEffect.java @@ -5,10 +5,13 @@ package dev.jaxcksn.nanoleafMusic.effects; +import ch.qos.logback.classic.Logger; import com.wrapper.spotify.model_objects.miscellaneous.AudioAnalysisSegment; +import dev.jaxcksn.nanoleafMusic.Main; import dev.jaxcksn.nanoleafMusic.utility.SpecificAudioAnalysis; import io.github.rowak.nanoleafapi.*; import io.github.rowak.nanoleafapi.effectbuilder.CustomEffectBuilder; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; @@ -46,6 +49,8 @@ public static float loudnessToPercent(float loudness, float max) { private int paletteIndex = 0; public boolean albumMode = false; public final EffectType effectType = EffectType.PULSEBEAT; + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.MusicEffect"); public PulseBeatEffect(Color[] palette, Aurora aurora) { this.palette = palette; @@ -53,10 +58,10 @@ public PulseBeatEffect(Color[] palette, Aurora aurora) { try { this.panels = aurora.panelLayout().getPanels(); } catch (StatusCodeException e) { - e.printStackTrace(); + Main.showException(e); } random = new Random(); - System.out.println("\u001b[92;1m✔\u001b[0m Pulse Beat Loaded"); + logger.info("Pulsebeat Effect was loaded"); } @Override @@ -94,7 +99,6 @@ private int[] adjustLuma(int[] color, double brightness) { public void setPalette(int[][] colors) { if (!albumMode) { - System.out.println("\u001b[96;1mℹ\u001b[0m Changed to Album Mode"); albumMode = true; } Color[] newPalette = new Color[colors.length]; @@ -123,7 +127,6 @@ public void setPalette(int[][] colors) { //If the palette is manually set public void setPalette(Color[] colors) { if (albumMode) { - System.out.println("\u001b[96;1mℹ\u001b[0m Changed to Palette Mode"); albumMode = false; } palette = colors; @@ -153,7 +156,9 @@ public void run(SpecificAudioAnalysis analysis) throws StatusCodeException { try { aurora.effects().displayEffect(ceb.build("", false)); } catch (StatusCodeException sce) { - sce.printStackTrace(); + logger.warn("Unrecoverable exception was thrown. Shutting down program."); + Main.showException(sce); + System.exit(1); } }).start(); setNextPaletteColor(); diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/effects/VibeEffect.java b/src/main/java/dev/jaxcksn/nanoleafMusic/effects/VibeEffect.java new file mode 100644 index 0000000..b5f8549 --- /dev/null +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/effects/VibeEffect.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020, Jaxcksn + * All rights reserved. + */ + +package dev.jaxcksn.nanoleafMusic.effects; + +import ch.qos.logback.classic.Logger; +import com.wrapper.spotify.model_objects.miscellaneous.AudioAnalysisMeasure; +import dev.jaxcksn.nanoleafMusic.Main; +import dev.jaxcksn.nanoleafMusic.utility.SpecificAudioAnalysis; +import io.github.rowak.nanoleafapi.*; +import io.github.rowak.nanoleafapi.effectbuilder.CustomEffectBuilder; +import org.slf4j.LoggerFactory; + +import java.util.Random; + +/** + * More minimal beat effect, where the colors fade from one to another, and changes in the + * parts of the song changes the color. + */ +public class VibeEffect implements MusicEffect { + public Color[] palette; + public Aurora device; + private Panel[] panels; + private final Random random; + public boolean albumMode = false; + private int paletteIndex = 0; + public final EffectType effectType = EffectType.VIBE; + public boolean songChanged = false; + private AudioAnalysisMeasure currentSection; + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.MusicEffect"); + + public VibeEffect(Color[] palette, Aurora device) { + this.palette = palette; + this.device = device; + try { + this.panels = device.panelLayout().getPanels(); + } catch (StatusCodeException e) { + Main.showException(e); + } + this.random = new Random(); + paletteIndex = random.nextInt(palette.length); + logger.info("Vibe effect was loaded"); + } + + + @Override + public void setSongChanged() { + songChanged = true; + } + + @Override + public void run(SpecificAudioAnalysis analysis) throws StatusCodeException { + if (analysis.getBeat() != null) { + if (currentSection == null) { + currentSection = analysis.getBar(); + } else if (currentSection != analysis.getBar()) { + currentSection = analysis.getBar(); + setNextPaletteColor(); + } + Color color = palette[paletteIndex]; + int panelId = panels[random.nextInt(panels.length)].getId(); + int[] colorRGB = {color.getRed(), color.getGreen(), color.getBlue()}; + java.awt.Color darkerColor = new java.awt.Color(colorRGB[0], colorRGB[1], colorRGB[2]).darker().darker().darker(); + CustomEffectBuilder ceb = new CustomEffectBuilder(device); + ceb.addFrameToAllPanels(new Frame(darkerColor.getRed(), + darkerColor.getGreen(), darkerColor.getBlue(), 0, 2)); + ceb.addFrame(panelId, new Frame(colorRGB[0], colorRGB[1], colorRGB[2], 0, 1)); + + + new Thread(() -> { + try { + device.effects().displayEffect(ceb.build("", false)); + } catch (StatusCodeException e) { + logger.error("Unrecoverable exception was thrown. Shutting down program."); + Main.showException(e); + System.exit(1); + } + }).start(); + + + } + } + + @Override + public Color[] getPalette() { + return palette; + } + + private int[] adjustLuma(int[] color, double brightness) { + float[] hsbColor = java.awt.Color.RGBtoHSB(color[0], color[1], color[2], null); + float newBrightness = (float) (brightness * 100 + random.nextInt(10)) / 100; + hsbColor[2] = newBrightness; + + int rgb = java.awt.Color.HSBtoRGB(hsbColor[0], hsbColor[1], hsbColor[2]); + int r = (rgb >> 16) & 0xFF; + int g = (rgb >> 8) & 0xFF; + int b = rgb & 0xFF; + double luma = 0.2126 * r + 0.7152 * g + 0.0722 * b; + + if (luma < 65 && brightness < 1) { + //if it's still too dark. + return adjustLuma(new int[]{r, g, b}, brightness + 0.1); + } else { + return new int[]{r, g, b}; + } + } + + + public void setPalette(int[][] colors) { + if (!albumMode) { + albumMode = true; + } + Color[] newPalette = new Color[colors.length]; + + + for (int i = 0; i < colors.length; i++) { + int r = colors[i][0]; + int g = colors[i][1]; + int b = colors[i][2]; + + // This mainly to stop colors that are just black, as they kind of ruin the effect. + double luma = 0.2126 * r + 0.7152 * g + 0.0722 * b; + if (luma < 65) { + //Let's lighten it. + int[] bright = adjustLuma(colors[i], 0.5); + newPalette[i] = Color.fromRGB(bright[0], bright[1], bright[2]); + } else { + newPalette[i] = Color.fromRGB(r, g, b); + } + + } + + palette = newPalette; + } + + public EffectType getEffectType() { + return effectType; + } + + //If the palette is manually set + public void setPalette(Color[] colors) { + if (albumMode) { + albumMode = false; + } + palette = colors; + } + + protected void setNextPaletteColor() { + if (paletteIndex == palette.length - 1) { + paletteIndex = 0; + } else { + paletteIndex++; + } + } + +} diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/utility/CallbackServer.java b/src/main/java/dev/jaxcksn/nanoleafMusic/utility/CallbackServer.java index 5a47e15..2e12727 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/utility/CallbackServer.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/utility/CallbackServer.java @@ -5,9 +5,12 @@ package dev.jaxcksn.nanoleafMusic.utility; +import ch.qos.logback.classic.Logger; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; +import dev.jaxcksn.nanoleafMusic.Main; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.InetSocketAddress; @@ -18,10 +21,14 @@ public class CallbackServer { protected static HttpServer server; private final authServerHandler requestHandler = new authServerHandler(); + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.CallbackServer"); static public class authServerHandler implements HttpHandler { private final CountDownLatch tokenLatch = new CountDownLatch(1); private String authCode; + private static final Logger logger + = (Logger) LoggerFactory.getLogger("nanoleafMusic.CallbackServer"); @Override public void handle(HttpExchange httpExchange) throws IOException { @@ -66,6 +73,7 @@ public void handle(HttpExchange httpExchange) throws IOException { " \n" + "\n" + ""; + logger.error("Received fatal response with from callback"); httpExchange.sendResponseHeaders(200, textResponse.length()); httpExchange.getResponseBody().write(textResponse.getBytes()); httpExchange.getResponseBody().flush(); @@ -107,11 +115,11 @@ public void handle(HttpExchange httpExchange) throws IOException { " \n" + "\n" + ""; + logger.info("Received valid response from callback"); httpExchange.sendResponseHeaders(200, textResponse.length()); httpExchange.getResponseBody().write(textResponse.getBytes()); httpExchange.getResponseBody().flush(); httpExchange.getResponseBody().close(); - System.out.println("\u001b[92;1m✔\u001b[0m Spotify Authorization Given"); tokenLatch.countDown(); } } @@ -134,10 +142,12 @@ private String handleGetRequest(HttpExchange httpExchange) { private String fetchAuthCode() { try { + logger.info("Waiting for request to callback server"); tokenLatch.await(); } catch (InterruptedException e) { - e.printStackTrace(); + Main.showException(e); } + logger.info("Passing access code from callback"); return authCode; } @@ -149,11 +159,11 @@ public CallbackServer() { try { server = HttpServer.create(new InetSocketAddress("0.0.0.0", 8001), 0); server.createContext("/connect", requestHandler); - ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10); + ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5, new Main.NamedThreadFactory("callback")); server.setExecutor(threadPoolExecutor); server.start(); } catch (IOException e) { - e.printStackTrace(); + Main.showException(e); } } @@ -163,6 +173,7 @@ public String getAuthCode() { } public void destroy() { + logger.info("Destroying the callback server"); server.stop(0); } diff --git a/src/main/java/dev/jaxcksn/nanoleafMusic/utility/Settings.java b/src/main/java/dev/jaxcksn/nanoleafMusic/utility/Settings.java index 8fe35b5..fc397f0 100644 --- a/src/main/java/dev/jaxcksn/nanoleafMusic/utility/Settings.java +++ b/src/main/java/dev/jaxcksn/nanoleafMusic/utility/Settings.java @@ -9,18 +9,12 @@ public class Settings { public boolean albumColors; - /** - * This is a debug setting, and should only be changed manually in preferences. - * Adjusting can cause performance issues, so do it at your own risk. - */ - public int albumPaletteLength; public String colorPalette; public EffectType activeEffectType; - public Settings(boolean albumColors, int albumPaletteLength, String colorPalette, EffectType activeEffectType) { + public Settings(boolean albumColors, String colorPalette, EffectType activeEffectType) { this.activeEffectType = activeEffectType; this.albumColors = albumColors; - this.albumPaletteLength = albumPaletteLength; this.colorPalette = colorPalette; } } diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..48a1f03 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,30 @@ + + + + + true + + %d{HH:mm:ss} [%thread] %highlight(%level) %logger{36} - %msg%n + + + + + ${HOME:-${USERPROFILE}}/logs/NanoleafMusic.log + false + + true + + + %d{HH:mm:ss} [%thread] %level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/playbackView.fxml b/src/main/resources/playbackView.fxml index 57d532a..6571875 100644 --- a/src/main/resources/playbackView.fxml +++ b/src/main/resources/playbackView.fxml @@ -5,7 +5,6 @@ ~ All rights reserved. --> - @@ -49,6 +48,8 @@ + - @@ -49,6 +48,8 @@ +