diff --git a/.idea/misc.xml b/.idea/misc.xml
index 001e756..11d3355 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -8,7 +8,5 @@
-
-
-
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index e45147d..df8284a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
UTF-8
-5.10.2
+ 5.10.2
@@ -24,8 +24,13 @@
javafx-fxml
21
+
+ org.openjfx
+ javafx-media
+ 21
+
-
+
org.junit.jupiter
junit-jupiter-api
${junit.version}
diff --git a/src/main/java/sbu/javafx/Launcher.java b/src/main/java/sbu/javafx/Launcher.java
index c288d10..852cc6d 100644
--- a/src/main/java/sbu/javafx/Launcher.java
+++ b/src/main/java/sbu/javafx/Launcher.java
@@ -2,7 +2,11 @@ package sbu.javafx;
import javafx.application.Application;
-public class Launcher{
- public static void main(String[] args) {
- // TODO: Launch the JavaFX application by calling Application.launch(Main.class) }
+public class Launcher
+{
+ public static void main(String[] args)
+ {
+ // TODO: Launch the JavaFX application by calling Application.launch(Main.class)
+ Application.launch(MusicApp.class, args);
+ }
}
diff --git a/src/main/java/sbu/javafx/MusicApp.java b/src/main/java/sbu/javafx/MusicApp.java
index a50badc..022a1c2 100644
--- a/src/main/java/sbu/javafx/MusicApp.java
+++ b/src/main/java/sbu/javafx/MusicApp.java
@@ -3,21 +3,43 @@ package sbu.javafx;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
+import javafx.scene.image.Image;
import javafx.stage.Stage;
import java.io.IOException;
+import java.util.Objects;
-public class MusicApp extends Application {
+public class MusicApp extends Application
+{
@Override
public void start(Stage stage) throws IOException
{
- FXMLLoader fxmlLoader = new FXMLLoader(MusicApp.class.getResource("App-view.fxml"));
- Scene scene = new Scene(fxmlLoader.load());
+
+ FXMLLoader fxmlLoader = new FXMLLoader(
+ Objects.requireNonNull(MusicApp.class.getResource("App-view.fxml"),
+ "Cannot find App-view.fxml in resources (src/main/resources/sbu/javafx/)")
+ );
+
+ Scene scene = new Scene(fxmlLoader.load(), 520, 300);
stage.setTitle("Music Player");
stage.setScene(scene);
- //TODO: add icon to the stage
+
+ try
+ {
+ Image icon = new Image(
+ Objects.requireNonNull(MusicApp.class.getResourceAsStream("icon.png"),
+ "Cannot find icon.png in resources (src/main/resources/sbu/javafx/)")
+ );
+ stage.getIcons().add(icon);
+ }
+ catch (Exception ignored) {}
+
stage.show();
}
-}
+ public static void main(String[] args)
+ {
+ launch(args);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/sbu/javafx/MusicController.java b/src/main/java/sbu/javafx/MusicController.java
index 5da27a9..67205d5 100644
--- a/src/main/java/sbu/javafx/MusicController.java
+++ b/src/main/java/sbu/javafx/MusicController.java
@@ -1,31 +1,381 @@
package sbu.javafx;
-import javafx.fxml.FXML;
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.animation.Timeline;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.Button;
import javafx.scene.control.Label;
-import javafx.scene.input.MouseEvent;
-import javafx.scene.layout.VBox;
+import javafx.scene.control.Slider;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.*;
+import javafx.scene.media.Media;
+import javafx.scene.media.MediaPlayer;
+import javafx.stage.Stage;
+import javafx.util.Duration;
+import java.io.IOException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import javafx.scene.Parent;
-public class MusicController {
+public class MusicController
+{
- // TODO: Define your UI components using the @FXML annotation.
+ @FXML private VBox songContainer;
+ @FXML private Label nowPlayingLabel;
+ @FXML private Label currentTrackTitle;
+ @FXML private Label timeLabel;
+ @FXML private Slider progressSlider;
+ @FXML private Slider volumeSlider;
+ @FXML private Button playPauseButton;
+ @FXML private Button repeatButton;
+ @FXML private Button shuffleButton;
+ @FXML private Button likeButton;
+ @FXML private StackPane visualizerContainer;
- //TODO: Implement the logic to play a song. This method should update the UI to show which song is currently playing.
+ private MediaPlayer mediaPlayer;
+ private int currentSongIndex = 0;
+ private boolean isRepeat = false;
+ private boolean isShuffle = false;
+ private boolean isLiked = false;
+ private final Random random = new Random();
+ private Timeline visualizerAnimation;
+
+ private final String[] songFiles = {
+ "Maison.mp3",
+ "KUSURA_BAKMA.mp3",
+ "Hiting_Motion.mp3",
+ "Lost_in_the_haze.mp3",
+ "North.mp3",
+ "Longing.mp3",
+ "After_Math.mp3",
+ "On_The_Roads.mp3",
+ "Bumpup.mp3",
+ "SEVMEYİ_DENEMEDİN.mp3"
+
+ };
+
+ private final Map songDisplayNames = new HashMap<>();
+ private static final ObservableList playlist = FXCollections.observableArrayList();
+ private static final ObservableList favorites = FXCollections.observableArrayList();
@FXML
- public void handlePlayAction() {
- //Update labels, change button icons, etc.
+ public void initialize()
+ {
+ for (int i = 0; i < songFiles.length; i++)
+ {
+ createSongCard(i);
+ }
+ setupVolumeControl();
+ createVisualizer();
}
- //TODO: Logic to add a specific song to the user's playlist.
+ private void createVisualizer()
+ {
+ HBox bars = new HBox(3);
+ bars.setAlignment(Pos.BOTTOM_CENTER);
+ for (int i = 0; i < 4; i++)
+ {
+ Region bar = new Region();
+ bar.setPrefWidth(4);
+ bar.setPrefHeight(5);
+ bar.setStyle("-fx-background-color: #1DB954; -fx-background-radius: 2;");
+ bars.getChildren().add(bar);
+ }
+ visualizerContainer.getChildren().add(bars);
- public void addToPlaylist(String songName) {
- //Add the song to a list or update a ListView.
+ visualizerAnimation = new Timeline();
+ for (Node node : bars.getChildren())
+ {
+ double randomHeight = 10 + Math.random() * 15;
+ KeyFrame kf = new KeyFrame(Duration.millis(150 + Math.random() * 100),
+ new KeyValue(((Region)node).prefHeightProperty(), randomHeight)
+ );
+ visualizerAnimation.getKeyFrames().add(kf);
+ }
+ visualizerAnimation.setCycleCount(Timeline.INDEFINITE);
+ visualizerAnimation.setAutoReverse(true);
}
- // TODO:This method should open a new window or change the current scene to display the "Playlist" page.
+ private void updateLikeButton(String displayName)
+ {
+ if (displayName == null || likeButton == null)
+ {
+ if (likeButton != null)
+ {
+ likeButton.setText("♡");
+ likeButton.getStyleClass().remove("btn-heart-liked");
+ }
+ return;
+ }
- public void openPlaylistWindow() {
- //Create a new stage and show the playlist UI.
+ if (favorites.contains(displayName))
+ {
+ likeButton.setText("♥");
+ if (!likeButton.getStyleClass().contains("btn-heart-liked"))
+ {
+ likeButton.getStyleClass().add("btn-heart-liked");
+ }
+ } else
+ {
+ likeButton.setText("♡");
+ likeButton.getStyleClass().remove("btn-heart-liked");
+ }
}
-}
+
+ @FXML
+ private void toggleLike()
+ {
+ String currentSongName = nowPlayingLabel.getText();
+ if (currentSongName.equals("None") || currentSongName.equals("Not Playing")) return;
+
+ isLiked = !isLiked;
+ if (isLiked)
+ {
+ likeButton.setText("♥");
+ likeButton.setStyle("-fx-text-fill: #ff4d4d; -fx-font-size: 16px;");
+ if (!favorites.contains(currentSongName)) favorites.add(currentSongName);
+ } else
+ {
+ likeButton.setText("♡");
+ likeButton.setStyle("-fx-text-fill: inherit; -fx-font-size: 16px;");
+ favorites.remove(currentSongName);
+ }
+ }
+
+ private void createSongCard(int index)
+ {
+ String fileName = songFiles[index];
+ String displayName = cleanFileName(fileName);
+
+ HBox card = new HBox(14);
+ card.setAlignment(Pos.CENTER_LEFT);
+ card.getStyleClass().add("song-card");
+
+
+ ImageView coverView = createCoverImageView(fileName);
+
+ VBox infoBox = new VBox(4);
+ Label titleLabel = new Label(displayName);
+ titleLabel.getStyleClass().add("song-title");
+
+
+ Label subtitleLabel = new Label("00:00");
+ subtitleLabel.getStyleClass().add("song-subtitle");
+
+
+ subtitleLabel.getStyleClass().add("song-meta");
+ infoBox.getChildren().addAll(titleLabel, subtitleLabel);
+
+ Region spacer = new Region();
+ HBox.setHgrow(spacer, Priority.ALWAYS);
+
+ Button cardLikeBtn = new Button(favorites.contains(displayName) ? "♥" : "♡");
+ cardLikeBtn.getStyleClass().add("btn-ghost");
+ cardLikeBtn.setStyle("-fx-font-size: 16px; -fx-text-fill: #ff4d6d;");
+ cardLikeBtn.setOnAction(event ->
+ {
+ if (favorites.contains(displayName))
+ {
+ favorites.remove(displayName);
+ cardLikeBtn.setText("♡");
+ } else {
+ favorites.add(displayName);
+ cardLikeBtn.setText("♥");
+ }
+ updateLikeButton(displayName);
+ });
+
+ Button addButton = new Button("+");
+ addButton.getStyleClass().add("btn-ghost");
+ addButton.setStyle("-fx-font-size: 18px;");
+ addButton.setOnAction(event ->
+ {
+ if (!playlist.contains(displayName))
+ {
+ playlist.add(displayName);
+ }
+ });
+
+ Button playButton = new Button("▶");
+ playButton.getStyleClass().add("btn-green-small");
+ playButton.setOnAction(event ->
+ {
+ currentSongIndex = index;
+ playMedia(fileName);
+ });
+
+ cardLikeBtn.setOnMouseClicked(e -> e.consume());
+ addButton.setOnMouseClicked(e -> e.consume());
+ playButton.setOnMouseClicked(e -> e.consume());
+
+ card.getChildren().addAll(coverView, infoBox, spacer, cardLikeBtn, addButton, playButton);
+ songContainer.getChildren().add(card);
+ }
+
+ private void playMedia(String fileName)
+ {
+ if (mediaPlayer != null)
+ {
+ mediaPlayer.stop(); mediaPlayer.dispose();
+ }
+
+ URL resource = getClass().getResource("/music/" + fileName);
+ if (resource == null) return;
+
+ Media media = new Media(resource.toExternalForm());
+ mediaPlayer = new MediaPlayer(media);
+ mediaPlayer.volumeProperty().bind(volumeSlider.valueProperty());
+
+ mediaPlayer.setOnReady(() -> {
+ String name = cleanFileName(fileName);
+ nowPlayingLabel.setText(name);
+ currentTrackTitle.setText(name);
+ playPauseButton.setText("Pause");
+ visualizerAnimation.play();
+
+ isLiked = favorites.contains(name);
+ likeButton.setText(isLiked ? "♥" : "♡");
+ likeButton.setStyle(isLiked ? "-fx-text-fill: #ff4d4d; -fx-font-size: 16px;" : "");
+ });
+
+ mediaPlayer.currentTimeProperty().addListener((obs, old, cur) ->
+ {
+ timeLabel.setText(formatDuration(cur) + " / " + formatDuration(media.getDuration()));
+ if (!progressSlider.isValueChanging())
+ {
+ progressSlider.setValue((cur.toSeconds() / media.getDuration().toSeconds()) * 100);
+ }
+ });
+
+ mediaPlayer.setOnEndOfMedia(this::nextTrack);
+ mediaPlayer.play();
+ }
+
+ private ImageView createCoverImageView(String mp3FileName)
+ {
+ String baseName = mp3FileName.substring(0, mp3FileName.lastIndexOf("."));
+ String[] extensions = {".jpg", ".jpeg", ".png", ".webp"};
+ for (String ext : extensions)
+ {
+ URL url = getClass().getResource("/music/" + baseName + ext);
+ if (url != null)
+ {
+ ImageView iv = new ImageView(new Image(url.toExternalForm()));
+ iv.setFitWidth(50); iv.setFitHeight(50); iv.setPreserveRatio(true);
+ return iv;
+ }
+ }
+ return new ImageView();
+ }
+
+ private void loadMetadata(String fileName, Label metaLabel)
+ {
+ URL resource = getClass().getResource("/music/" + fileName);
+ if (resource == null) return;
+
+ Media media = new Media(resource.toExternalForm());
+ MediaPlayer tempPlayer = new MediaPlayer(media);
+
+ tempPlayer.setOnReady(() ->
+ {
+ metaLabel.setText(formatDuration(media.getDuration()));
+ tempPlayer.dispose();
+ });
+ }
+
+ @FXML private void nextTrack()
+ {
+ if (isShuffle) currentSongIndex = random.nextInt(songFiles.length);
+ else currentSongIndex = (currentSongIndex + 1) % songFiles.length;
+ playMedia(songFiles[currentSongIndex]);
+ }
+
+ @FXML private void previousTrack()
+ {
+ currentSongIndex = (currentSongIndex - 1 + songFiles.length) % songFiles.length;
+ playMedia(songFiles[currentSongIndex]);
+ }
+
+ @FXML private void togglePlayPause()
+ {
+ if (mediaPlayer == null) return;
+ if (mediaPlayer.getStatus() == MediaPlayer.Status.PLAYING)
+ {
+ mediaPlayer.pause();
+ playPauseButton.setText("Play");
+ visualizerAnimation.pause();
+ } else
+ {
+ mediaPlayer.play();
+ playPauseButton.setText("Pause");
+ visualizerAnimation.play();
+ }
+ }
+
+ @FXML private void toggleRepeat()
+ {
+ isRepeat = !isRepeat; repeatButton.setStyle(isRepeat ? "-fx-text-fill: #1DB954;" : "");
+ }
+
+ @FXML private void toggleShuffle()
+ {
+ isShuffle = !isShuffle; shuffleButton.setStyle(isShuffle ? "-fx-text-fill: #1DB954;" : "");
+ }
+
+ private void setupVolumeControl()
+ {
+ volumeSlider.setValue(0.5);
+ }
+
+ private String cleanFileName(String f)
+ {
+ return f.replace(".mp3", "").replace("_", " ").replace("-", " ");
+ }
+
+ private String formatDuration(Duration d)
+ {
+ int s = (int) Math.floor(d.toSeconds()); return String.format("%02d:%02d", s / 60, s % 60);
+ }
+
+
+ @FXML
+ private void goToPlaylistScene(ActionEvent event) throws IOException
+ {
+ FXMLLoader loader = new FXMLLoader(getClass().getResource("Playlist-view.fxml"));
+ Parent root = loader.load();
+
+ PlaylistController controller = loader.getController();
+ controller.setPlaylist(playlist);
+ controller.setPreviousScene(((Node) event.getSource()).getScene());
+
+ Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
+ stage.setTitle("Playlist");
+ stage.setScene(new Scene(root));
+ }
+
+ @FXML
+ private void goToFavoritesScene(ActionEvent event) throws IOException
+ {
+ FXMLLoader loader = new FXMLLoader(getClass().getResource("Playlist-view.fxml"));
+ Parent root = loader.load();
+
+ PlaylistController controller = loader.getController();
+ controller.setPlaylist(favorites);
+ controller.setPreviousScene(((Node) event.getSource()).getScene());
+
+ Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
+ stage.setTitle("Favorites");
+ stage.setScene(new Scene(root));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/sbu/javafx/PlaylistController.java b/src/main/java/sbu/javafx/PlaylistController.java
new file mode 100644
index 0000000..cd39887
--- /dev/null
+++ b/src/main/java/sbu/javafx/PlaylistController.java
@@ -0,0 +1,70 @@
+package sbu.javafx;
+
+import javafx.collections.ObservableList;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.Scene;
+import javafx.scene.control.ListView;
+import javafx.stage.Stage;
+
+public class PlaylistController
+{
+
+ @FXML
+ private ListView playlistListView;
+
+ private ObservableList playlist;
+ private Scene previousScene;
+
+ /**
+ * Called from MusicController after loading Playlist-view.fxml
+ */
+ public void setPlaylist(ObservableList playlist)
+ {
+ this.playlist = playlist;
+
+ if (playlistListView != null)
+ {
+ playlistListView.setItems(playlist);
+ }
+ }
+
+ /**
+ * Save the previous scene so we can go back to it.
+ */
+ public void setPreviousScene(Scene previousScene)
+ {
+ this.previousScene = previousScene;
+ }
+
+ @FXML
+ private void removeSelected(ActionEvent e)
+ {
+ if (playlistListView == null) return;
+
+ String selected = playlistListView.getSelectionModel().getSelectedItem();
+ if (selected == null) return;
+
+ if (playlist != null)
+ {
+ playlist.remove(selected);
+ } else
+ {
+ playlistListView.getItems().remove(selected);
+ }
+ }
+
+ @FXML
+ private void goBack(ActionEvent e)
+ {
+ if (playlistListView == null) return;
+
+ Stage stage = (Stage) playlistListView.getScene().getWindow();
+ if (previousScene != null)
+ {
+ stage.setScene(previousScene);
+ stage.setTitle("Music Player");
+ stage.show();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/music/After_Math.jpg b/src/main/resources/music/After_Math.jpg
new file mode 100644
index 0000000..05b5b87
Binary files /dev/null and b/src/main/resources/music/After_Math.jpg differ
diff --git a/src/main/resources/music/After_Math.mp3 b/src/main/resources/music/After_Math.mp3
new file mode 100644
index 0000000..edd66f0
Binary files /dev/null and b/src/main/resources/music/After_Math.mp3 differ
diff --git a/src/main/resources/music/Bumpup.jpg b/src/main/resources/music/Bumpup.jpg
new file mode 100644
index 0000000..ca83c0c
Binary files /dev/null and b/src/main/resources/music/Bumpup.jpg differ
diff --git a/src/main/resources/music/Bumpup.mp3 b/src/main/resources/music/Bumpup.mp3
new file mode 100644
index 0000000..8a940aa
Binary files /dev/null and b/src/main/resources/music/Bumpup.mp3 differ
diff --git a/src/main/resources/music/Hiting_Motion.jpg b/src/main/resources/music/Hiting_Motion.jpg
new file mode 100644
index 0000000..b96e0cf
Binary files /dev/null and b/src/main/resources/music/Hiting_Motion.jpg differ
diff --git a/src/main/resources/music/Hiting_Motion.mp3 b/src/main/resources/music/Hiting_Motion.mp3
new file mode 100644
index 0000000..43a4031
Binary files /dev/null and b/src/main/resources/music/Hiting_Motion.mp3 differ
diff --git a/src/main/resources/music/KUSURA_BAKMA.jpg b/src/main/resources/music/KUSURA_BAKMA.jpg
new file mode 100644
index 0000000..820fa38
Binary files /dev/null and b/src/main/resources/music/KUSURA_BAKMA.jpg differ
diff --git a/src/main/resources/music/KUSURA_BAKMA.mp3 b/src/main/resources/music/KUSURA_BAKMA.mp3
new file mode 100644
index 0000000..d1308e3
Binary files /dev/null and b/src/main/resources/music/KUSURA_BAKMA.mp3 differ
diff --git a/src/main/resources/music/Longing.jpg b/src/main/resources/music/Longing.jpg
new file mode 100644
index 0000000..26afa7b
Binary files /dev/null and b/src/main/resources/music/Longing.jpg differ
diff --git a/src/main/resources/music/Longing.mp3 b/src/main/resources/music/Longing.mp3
new file mode 100644
index 0000000..917959a
Binary files /dev/null and b/src/main/resources/music/Longing.mp3 differ
diff --git a/src/main/resources/music/Lost_in_the_haze.jpg b/src/main/resources/music/Lost_in_the_haze.jpg
new file mode 100644
index 0000000..a2c0aaa
Binary files /dev/null and b/src/main/resources/music/Lost_in_the_haze.jpg differ
diff --git a/src/main/resources/music/Lost_in_the_haze.mp3 b/src/main/resources/music/Lost_in_the_haze.mp3
new file mode 100644
index 0000000..ff3749d
Binary files /dev/null and b/src/main/resources/music/Lost_in_the_haze.mp3 differ
diff --git a/src/main/resources/music/Maison.jpg b/src/main/resources/music/Maison.jpg
new file mode 100644
index 0000000..31e7eb7
Binary files /dev/null and b/src/main/resources/music/Maison.jpg differ
diff --git a/src/main/resources/music/Maison.mp3 b/src/main/resources/music/Maison.mp3
new file mode 100644
index 0000000..0c84e51
Binary files /dev/null and b/src/main/resources/music/Maison.mp3 differ
diff --git a/src/main/resources/music/North.jpg b/src/main/resources/music/North.jpg
new file mode 100644
index 0000000..c3bac63
Binary files /dev/null and b/src/main/resources/music/North.jpg differ
diff --git a/src/main/resources/music/North.mp3 b/src/main/resources/music/North.mp3
new file mode 100644
index 0000000..dd9d20e
Binary files /dev/null and b/src/main/resources/music/North.mp3 differ
diff --git a/src/main/resources/music/On_The_Roads.jpg b/src/main/resources/music/On_The_Roads.jpg
new file mode 100644
index 0000000..b628b84
Binary files /dev/null and b/src/main/resources/music/On_The_Roads.jpg differ
diff --git a/src/main/resources/music/On_The_Roads.mp3 b/src/main/resources/music/On_The_Roads.mp3
new file mode 100644
index 0000000..8dc92c6
Binary files /dev/null and b/src/main/resources/music/On_The_Roads.mp3 differ
diff --git a/src/main/resources/music/SEVMEYİ_DENEMEDİN.jpg b/src/main/resources/music/SEVMEYİ_DENEMEDİN.jpg
new file mode 100644
index 0000000..eb6b5c5
Binary files /dev/null and b/src/main/resources/music/SEVMEYİ_DENEMEDİN.jpg differ
diff --git a/src/main/resources/music/SEVMEYİ_DENEMEDİN.mp3 b/src/main/resources/music/SEVMEYİ_DENEMEDİN.mp3
new file mode 100644
index 0000000..28f40b5
Binary files /dev/null and b/src/main/resources/music/SEVMEYİ_DENEMEDİN.mp3 differ
diff --git a/src/main/resources/sbu/javafx/App-view.fxml b/src/main/resources/sbu/javafx/App-view.fxml
index 306c8d3..32e2264 100644
--- a/src/main/resources/sbu/javafx/App-view.fxml
+++ b/src/main/resources/sbu/javafx/App-view.fxml
@@ -1,8 +1,65 @@
-
-
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/sbu/javafx/Playlist-view.fxml b/src/main/resources/sbu/javafx/Playlist-view.fxml
new file mode 100644
index 0000000..751f317
--- /dev/null
+++ b/src/main/resources/sbu/javafx/Playlist-view.fxml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/sbu/javafx/spotify.css b/src/main/resources/sbu/javafx/spotify.css
new file mode 100644
index 0000000..6dda7f8
--- /dev/null
+++ b/src/main/resources/sbu/javafx/spotify.css
@@ -0,0 +1,113 @@
+.root {
+ -fx-font-family: "Segoe UI", "Inter", "Roboto", "Arial";
+ -fx-background-color: #121212;
+}
+
+.header {
+ -fx-background-color: #181818;
+ -fx-padding: 14 18 14 18;
+ -fx-border-color: #242424;
+ -fx-border-width: 0 0 1 0;
+}
+
+.h1 {
+ -fx-text-fill: #FFFFFF;
+ -fx-font-size: 20px;
+ -fx-font-weight: 700;
+}
+
+.muted {
+ -fx-text-fill: #B3B3B3;
+}
+
+.song-card {
+ -fx-background-color: #181818;
+ -fx-background-radius: 12;
+ -fx-padding: 12 14 12 14;
+ -fx-border-color: #242424;
+ -fx-border-radius: 12;
+}
+
+.song-card:hover {
+ -fx-background-color: #202020;
+}
+
+.song-title {
+ -fx-text-fill: #FFFFFF;
+ -fx-font-size: 14px;
+ -fx-font-weight: 600;
+}
+
+.song-meta {
+ -fx-text-fill: #B3B3B3;
+ -fx-font-size: 12px;
+}
+
+.button {
+ -fx-background-radius: 999;
+ -fx-padding: 8 14 8 14;
+ -fx-font-weight: 600;
+ -fx-cursor: hand;
+ -fx-background-color: #2A2A2A;
+ -fx-text-fill: #FFFFFF;
+}
+
+.button:hover {
+ -fx-background-color: #333333;
+}
+
+.btn-green {
+ -fx-background-color: #1DB954;
+ -fx-text-fill: #0B0B0B;
+}
+
+.btn-green:hover {
+ -fx-background-color: #22C55E;
+}
+
+.btn-ghost {
+ -fx-background-color: transparent;
+ -fx-border-color: #2A2A2A;
+ -fx-border-width: 1;
+ -fx-text-fill: #FFFFFF;
+}
+
+.btn-ghost:hover {
+ -fx-background-color: #1E1E1E;
+}
+
+.list-view {
+ -fx-background-color: #121212;
+ -fx-control-inner-background: #121212;
+ -fx-background-insets: 0;
+ -fx-padding: 0;
+}
+
+.list-cell {
+ -fx-background-color: #181818;
+ -fx-text-fill: #FFFFFF;
+ -fx-padding: 10 12 10 12;
+ -fx-border-color: #242424;
+ -fx-border-width: 0 0 1 0;
+}
+
+.list-cell:filled:selected {
+ -fx-background-color: #1F1F1F;
+}
+.btn-green-small {
+ -fx-background-color: #1db954;
+ -fx-text-fill: black;
+ -fx-background-radius: 50;
+ -fx-padding: 5 10;
+ -fx-font-weight: bold;
+}
+
+.btn-green-small:hover {
+ -fx-background-color: #1ed760;
+ -fx-scale-x: 1.1;
+ -fx-scale-y: 1.1;
+}
+
+.btn-heart-liked {
+ -fx-text-fill: #ff4d6d !important;
+}
\ No newline at end of file