diff --git a/.idea/misc.xml b/.idea/misc.xml index 001e756..0c04b52 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..8306744 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/ReadMe.md b/ReadMe.md index 7be3352..30cca74 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,72 +1,123 @@ -# Seventh Assignment — JavaFX Music Player Project +# 🎵 Music Player -This is a starter JavaFX application for a simple Music Player interface. Your goal is to design the UI and connect it to the provided controller logic to create basic music player interactions. +A minimal JavaFX music player with playlist support and dark mode. -## 📋 Assignment Objectives +--- -### Complete the following features: +## Features -**1. Design the Music Player UI** +- Play, pause, skip, and seek through songs +- Shuffle and repeat modes +- Volume control +- Album art display (loaded from file metadata) +- Library tab — manage all your songs +- Playlists tab — create custom playlists and add songs to them +- Dark mode toggle +- Right-click on any song to add it to a playlist -Create a layout for a simple music player interface. Your UI should display at least **three songs**, each containing: +--- -- Song title -- Artist name -- Duration -- A **Play** button -- An **Add to Playlist** button +## Project Structure -**2. Connect UI Components to the Controller** +``` +MusicPlayer/ +├── src/ +│ └── main/ +│ ├── java/sbu/javafx/ +│ │ ├── MainApp.java +│ │ ├── MusicController.java +│ │ ├── Song.java +│ │ └── Playlist.java +│ └── resources/sbu/javafx/ +│ ├── player.fxml +│ └── style.css +└── pom.xml +``` -Link your UI elements to the provided `MusicController` class: +--- -- Connect buttons to their corresponding controller methods. -- Ensure that clicking buttons triggers the correct actions. +## Requirements -**3. Display the Currently Playing Song** +- Java 17+ +- JavaFX 17+ +- JAudioTagger 3.0.1 (for reading song metadata) -When a user clicks a **Play** button: +--- -- Update a label in the UI showing the currently playing song (e.g., "Now Playing: Song Title"). +## Dependencies (pom.xml) -**4. Implement a Playlist** +```xml + + org.openjfx + javafx-controls + 17 + + + org.openjfx + javafx-fxml + 17 + + + org.openjfx + javafx-media + 17 + + + net.jthink + jaudiotagger + 3.0.1 + +``` -When the **Add to Playlist** button is clicked: +--- -- Add the selected song to a playlist data structure (e.g., an `ArrayList`). +## How to Run -**5. Create a Playlist Page** +```bash +mvn clean javafx:run +``` -Add a **"View Playlist"** button to the main UI: +--- -- When clicked, switch to a **new scene** that displays the playlist. -- Use `stage.setScene(...)` to switch between scenes. -- Show the playlist content using a `ListView` or any other layout. +## How to Use +### Adding Songs +1. Go to the **Library** tab +2. Click **+ Add Song** +3. Select one or more audio files (mp3, wav, aac, flac) +4. Songs will appear in the library with their metadata -## 🎨 UI Design Reference +### Playing Songs +- Double-click any song in the library to start playing +- Use the control buttons to play/pause, skip, or go back +- Drag the progress bar to seek -To help you get started, refer to the layout below. Your application should have a similar, clean, and simple structure: +### Playlists +1. Go to the **Playlists** tab +2. Click **+ New** to create a playlist +3. Go back to the Library tab +4. Right-click any song and select **Add to Playlist** +5. Double-click a playlist to open it +6. Double-click a song inside the playlist to play it -![Music Player UI Layout](https://uploadkon.ir/uploads/85a922_26music.png) +### Dark Mode +- Click the 🌙 button on the top right of the player panel to toggle dark mode -*(Note: This is a wireframe. You are encouraged to keep your implementation simple and focus on functionality.)* +--- +## Supported Formats -## ⭐ Bonus Tasks (Optional) +| Format | Supported | +|--------|-----------| +| MP3 | ✅ | +| WAV | ✅ | +| AAC | ✅ | +| FLAC | ✅ | -**The core requirements above do not include actual audio playback.** The following tasks are optional (Bonus) and should only be attempted if you have completed the core requirements. - -**1. Play Functionality** - -- Implement actual audio playback using JavaFX `Media` and `MediaPlayer` classes. - -**2. Add Visual Feedback** - -- When a song is playing, change the row style or update the **Play** button text to "Playing". - -**3. Dark Mode / Light Mode** - -- Add a toggle button to switch between two CSS stylesheets (e.g., `dark.css` and `light.css`) at runtime. - +--- +## Preview +![Screenshot1](src/main/resources/sbu/images/Screenshot1.png) +![Screenshot2](src/main/resources/sbu/images/Screenshot2.png) +![Screenshot3](src/main/resources/sbu/images/Screenshot3.png) +![Screenshot4](src/main/resources/sbu/images/Screenshot4.png) \ No newline at end of file diff --git a/pom.xml b/pom.xml index e45147d..44c18fe 100644 --- a/pom.xml +++ b/pom.xml @@ -11,32 +11,50 @@ UTF-8 -5.10.2 + 5.10.2 + 23 + 23 + org.openjfx javafx-controls - 21 + ${javafx.version} + org.openjfx javafx-fxml - 21 + ${javafx.version} - + + org.openjfx + javafx-media + ${javafx.version} + + + org.junit.jupiter junit-jupiter-api ${junit.version} test + org.junit.jupiter junit-jupiter-engine ${junit.version} test - + + + + net.jthink + jaudiotagger + 3.0.1 + + @@ -45,30 +63,18 @@ maven-compiler-plugin 3.13.0 - 23 - 23 + ${maven.compiler.release} + org.openjfx javafx-maven-plugin 0.0.8 - - - - default-cli - - sbu.javafx/sbu.javafx.MusicApp - app - app - app - true - true - true - - - + + sbu.javafx/sbu.javafx.MusicApp + - \ No newline at end of file + diff --git a/src/main/java/sbu/javafx/Launcher.java b/src/main/java/sbu/javafx/Launcher.java index c288d10..a3ffd1f 100644 --- a/src/main/java/sbu/javafx/Launcher.java +++ b/src/main/java/sbu/javafx/Launcher.java @@ -4,5 +4,6 @@ import javafx.application.Application; public class Launcher{ public static void main(String[] args) { - // TODO: Launch the JavaFX application by calling Application.launch(Main.class) } + Application.launch(MusicApp.class); + } } diff --git a/src/main/java/sbu/javafx/MusicApp.java b/src/main/java/sbu/javafx/MusicApp.java index a50badc..cdf066a 100644 --- a/src/main/java/sbu/javafx/MusicApp.java +++ b/src/main/java/sbu/javafx/MusicApp.java @@ -3,21 +3,24 @@ 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; - 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()); + public void start(Stage stage) throws Exception { + FXMLLoader loader = new FXMLLoader( + getClass().getResource("App-view.fxml")); + Scene scene = new Scene(loader.load()); stage.setTitle("Music Player"); + stage.getIcons().add(new Image("/sbu/images/default-cover.png")); stage.setScene(scene); - //TODO: add icon to the stage + stage.setResizable(false); 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..752201d 100644 --- a/src/main/java/sbu/javafx/MusicController.java +++ b/src/main/java/sbu/javafx/MusicController.java @@ -1,31 +1,482 @@ package sbu.javafx; -import javafx.fxml.FXML; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.*; +import javafx.scene.control.Button; import javafx.scene.control.Label; -import javafx.scene.input.MouseEvent; +import javafx.scene.control.Menu; +import javafx.scene.control.MenuItem; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.media.Media; +import javafx.scene.media.MediaPlayer; import javafx.scene.layout.VBox; +import javafx.stage.FileChooser; +import javafx.util.Duration; +import org.jaudiotagger.audio.AudioFile; +import org.jaudiotagger.audio.AudioFileIO; +import org.jaudiotagger.audio.AudioHeader; +import org.jaudiotagger.tag.FieldKey; +import org.jaudiotagger.tag.Tag; +import org.jaudiotagger.tag.images.Artwork; +import javafx.scene.shape.Rectangle; + +import java.awt.*; +import java.io.File; +import java.io.FileOutputStream; +import java.util.*; +import java.util.List; public class MusicController { - // TODO: Define your UI components using the @FXML annotation. + // ========================================= fxml Fields ============================================ + // player panel + @FXML private ImageView albumCover; + @FXML private Label songTitle, artistName; + @FXML private Label currentTime, totalTime; + @FXML private Button playPauseBtn, shuffleBtn, repeatBtn; + @FXML private Button libraryTabBtn, playlistTabBtn; + @FXML private Slider progressSlider, volumeSlider; - //TODO: Implement the logic to play a song. This method should update the UI to show which song is currently playing. + // library and playlists panel + @FXML private VBox libraryPane, playlistsPane, playlistsListPane, playlistDetailPane; + @FXML private ListView libraryView; + @FXML private ListView playlistsView; + @FXML private ListView playlistDetailView; + @FXML private Label libraryCount, playlistDetailName; + + // my fields + private MediaPlayer mediaPlayer; + private ObservableList library = FXCollections.observableArrayList(); + private HashMap> playlists = new HashMap<>(); + + private List playQueue = new ArrayList<>(); + private int currentIndex = -1; + private boolean isPlaying = false; + private boolean isShuffle = false; + private boolean isRepeat = false; + + // theme button + @FXML private Button themeBtn; + private boolean isDarkMode = false; + + // ========================================= Methods ============================================ + public void initialize(){ // runs without calling + libraryView.setItems(library); + + // volume + volumeSlider.valueProperty().addListener((obs, oldVal, newVal) -> { + if(mediaPlayer != null){ + mediaPlayer.setVolume(newVal.doubleValue()); + } + }); + + // progress slider - seek functionality + progressSlider.setOnMouseReleased(e -> { + if(mediaPlayer != null){ + mediaPlayer.seek(Duration.seconds(progressSlider.getValue())); + } + }); + progressSlider.setMax(100); + + // library - double click to play + libraryView.setOnMouseClicked(e -> { + if(e.getClickCount() == 2){ // double click + Song selectedSong = libraryView.getSelectionModel().getSelectedItem(); + if(selectedSong != null){ + playQueue = new ArrayList<>(library); + currentIndex = playQueue.indexOf(selectedSong); + playSong(selectedSong); + } + } + }); + + // playlist detail - double click to play + playlistDetailView.setOnMouseClicked(e -> { + if(e.getClickCount() == 2){ // double click + Song selectedSong = playlistDetailView.getSelectionModel().getSelectedItem(); + if(selectedSong != null){ + playQueue = new ArrayList<>(playlistDetailView.getItems()); + currentIndex = playQueue.indexOf(selectedSong); + playSong(selectedSong); + } + } + }); + + // playlists - double click to open + playlistsView.setOnMouseClicked(e -> { + if(e.getClickCount() == 2){ // double click + String selectedPlaylist = playlistsView.getSelectionModel().getSelectedItem(); + if(selectedPlaylist != null){ + openPlayList(selectedPlaylist); + } + } + }); + + setupLibraryItems(); + + Rectangle clip = new Rectangle(160, 160); + clip.setArcWidth(24); + clip.setArcHeight(24); + albumCover.setClip(clip); + + } + + // ========================================= Playback ============================================ + private void playSong(Song song) { + if(mediaPlayer != null){ + mediaPlayer.stop(); + mediaPlayer.dispose(); // file not in use + } + + File file = new File(song.getFilePath()); + if (!file.exists()) { + System.out.println("FILE NOT FOUND!"); + return; + } + + Media media = new Media(file.toURI().toString()); + mediaPlayer = new MediaPlayer(media); + mediaPlayer.setVolume(volumeSlider.getValue()); + + // UI changes + songTitle.setText(song.getTitle()); + artistName.setText(song.getArtist()); + loadCover(song); + + // progress bar + mediaPlayer.currentTimeProperty().addListener((obs, oldVal, newVal) -> { + if(!progressSlider.isValueChanging()){ + progressSlider.setValue(newVal.toSeconds()); + currentTime.setText(formatTime(newVal.toSeconds())); + } + }); + + mediaPlayer.setOnReady(() -> { + double total= mediaPlayer.getTotalDuration().toSeconds(); + progressSlider.setMax(total); + totalTime.setText(formatTime(total)); + }); + + mediaPlayer.setOnEndOfMedia(() -> { + if(isRepeat){ + mediaPlayer.seek(Duration.ZERO); + mediaPlayer.play(); + } + else{ + handleNext(); + } + }); + mediaPlayer.play(); + isPlaying = true; + playPauseBtn.setText("⏸"); + libraryView.refresh(); + } + + private void loadCover(Song song){ + try{ + if(song.getCoverPath() != null && !song.getCoverPath().isEmpty()){ + albumCover.setImage(new Image( + new File(song.getCoverPath()).toURI().toString() + )); + } + else{ + albumCover.setImage(new Image( + Objects.requireNonNull(getClass().getResourceAsStream("/sbu/images/default-cover.png")) + )); + } + } catch(Exception e){ + albumCover.setImage(null); + } + } + + private String formatTime(double seconds) { + int minute = (int) seconds / 60; + int second = (int) seconds % 60; + return String.format("%d:%02d", minute, second); + } + + private Song createSongFromFile(File file){ + String filePath = file.getAbsolutePath(); + String title = file.getName(); + String artist = "Unknown"; + String album = "Unknown"; + String coverPath = ""; + int duration = 0; + + try{ + AudioFile audioFile = AudioFileIO.read(file); + Tag tag = audioFile.getTag(); + AudioHeader header = audioFile.getAudioHeader(); + + if(tag != null){ + // title + String t = tag.getFirst(FieldKey.TITLE); + if(t != null && !t.isBlank()) + title = t; + + // artist + String a = tag.getFirst(FieldKey.ARTIST); + if(a != null && !a.isBlank()) + artist = a; + + // album + String al = tag.getFirst(FieldKey.ALBUM); + if(al != null && !al.isBlank()) + album = al; + + // cover + List artworks = tag.getArtworkList(); + if(artworks != null && !artworks.isEmpty()){ + byte[] imageData = artworks.get(0).getBinaryData(); + File coverFile = new File( + System.getProperty("java.io.tmpdir"), + title.replaceAll("\\s+", "_") + "_cover.jpg" + ); + + try(FileOutputStream fos = new FileOutputStream(coverFile)){ + fos.write(imageData); + coverPath = coverFile.getAbsolutePath(); + } + } + } + + if(header != null) + duration = header.getTrackLength(); + + } catch(Exception e){ + System.out.println("Cannot read metadata, " + e.getMessage()); + } + + return new Song(title, artist, album, filePath, coverPath, duration); + } + + // ========================================= Control buttons ============================================ + @FXML + public void handlePlayPause() { + if(mediaPlayer == null) return; + + if(isPlaying){ + mediaPlayer.pause(); + playPauseBtn.setText("▶"); + } + else{ + mediaPlayer.play(); + playPauseBtn.setText("⏸"); + } + isPlaying = !isPlaying; + } @FXML - public void handlePlayAction() { - //Update labels, change button icons, etc. + public void handleNext() { + if(playQueue.isEmpty()) return; + if(isShuffle){ + currentIndex = (int) (Math.random() * playQueue.size()); + } + else{ + currentIndex = (currentIndex + 1) % playQueue.size(); // when we reach end of the queue it goes to the first one + } + playSong(playQueue.get(currentIndex)); } - //TODO: Logic to add a specific song to the user's playlist. - - public void addToPlaylist(String songName) { - //Add the song to a list or update a ListView. + @FXML + public void handlePrev() { + if(playQueue.isEmpty()) return; + currentIndex = (currentIndex - 1 + playQueue.size()) % playQueue.size(); + playSong(playQueue.get(currentIndex)); } - // TODO:This method should open a new window or change the current scene to display the "Playlist" page. + @FXML + public void handleShuffle() { + isShuffle = !isShuffle; + if(isShuffle) shuffleBtn.getStyleClass().add("active-btn"); + else shuffleBtn.getStyleClass().remove("active-btn"); + } - public void openPlaylistWindow() { - //Create a new stage and show the playlist UI. + @FXML + public void handleRepeat() { + isRepeat = !isRepeat; + if (isRepeat) repeatBtn.getStyleClass().add("active-btn"); + else repeatBtn.getStyleClass().remove("active-btn"); + } + + // ========================================= Library ============================================ + @FXML + public void handleAddSong() { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Add Songs"); + fileChooser.getExtensionFilters().add( + new FileChooser.ExtensionFilter("Audio Files", "*.mp3", "*.wav", "*.aac", "*.flac") + ); + + List files = fileChooser.showOpenMultipleDialog(null); + if(files == null) return; + for(File file : files){ + Song song = createSongFromFile(file); + library.add(song); + } + libraryCount.setText(library.size() + " Songs"); + } + + private void setupLibraryItems() { + libraryView.setCellFactory(lv -> new ListCell<>() { + private final ImageView cover = new ImageView(); + private final Label index = new Label(); + private final Label title = new Label(); + private final Label artist = new Label(); + private final Label duration = new Label(); + private final VBox info = new VBox(2, title, artist); + private final javafx.scene.layout.HBox root = new javafx.scene.layout.HBox(10, index, cover, info, duration); + + // Context Menu + private final ContextMenu contextMenu = new ContextMenu(); + private final Menu addToPlaylistMenu = new Menu("Add to Playlist"); + + { + cover.setFitWidth(36); + cover.setFitHeight(36); + cover.setPreserveRatio(true); + Rectangle coverClip = new Rectangle(36, 36); + coverClip.setArcWidth(8); + coverClip.setArcHeight(8); + cover.setClip(coverClip); + index.setStyle("-fx-text-fill: #9E9E9E; -fx-font-size: 11px; -fx-min-width: 20px;"); + title.setStyle("-fx-font-size: 13px; -fx-text-fill: #212121;"); + artist.setStyle("-fx-font-size: 11px; -fx-text-fill: #757575;"); + duration.setStyle("-fx-font-size: 11px; -fx-text-fill: #9E9E9E;"); + javafx.scene.layout.HBox.setHgrow(info, javafx.scene.layout.Priority.ALWAYS); + root.setAlignment(javafx.geometry.Pos.CENTER_LEFT); + root.setPadding(new javafx.geometry.Insets(4, 8, 4, 8)); + + // Context Menu + contextMenu.getItems().add(addToPlaylistMenu); + contextMenu.setOnShowing(e -> { + addToPlaylistMenu.getItems().clear(); + if (playlists.isEmpty()) { + MenuItem none = new MenuItem("No playlists yet"); + none.setDisable(true); + addToPlaylistMenu.getItems().add(none); + } else { + for (String name : playlists.keySet()) { + MenuItem item = new MenuItem(name); + item.setOnAction(ev -> { + Song song = getItem(); + if (song != null) playlists.get(name).add(song); + }); + addToPlaylistMenu.getItems().add(item); + } + } + }); + } + + @Override + protected void updateItem(Song song, boolean empty) { + super.updateItem(song, empty); + if (empty || song == null) { + setGraphic(null); + setContextMenu(null); + } else { + index.setText(String.valueOf(getIndex() + 1)); + title.setText(song.getTitle()); + artist.setText(song.getArtist()); + duration.setText(song.getFormattedDuration()); + + try { + if (song.getCoverPath() != null && !song.getCoverPath().isEmpty()) { + cover.setImage(new Image(new File(song.getCoverPath()).toURI().toString(), true)); + } else { + cover.setImage(new Image( + Objects.requireNonNull(getClass().getResourceAsStream("/sbu/images/default-cover.png")))); + } + } catch (Exception e) { + cover.setImage(null); + } + + if (getIndex() == currentIndex && playQueue.equals(new ArrayList<>(library))) { + title.setStyle("-fx-font-size: 13px; -fx-text-fill: #1DB954; -fx-font-weight: bold;"); + } else { + title.setStyle("-fx-font-size: 13px; -fx-text-fill: #212121;"); + } + + setGraphic(root); + setContextMenu(contextMenu); + } + } + }); + } + + // ========================================= Playlists ============================================ + @FXML + public void handleNewPlaylist() { + TextInputDialog dialog = new TextInputDialog(); + dialog.setTitle("New Playlist"); + dialog.setHeaderText(null); + dialog.setContentText("Playlist name: "); + dialog.showAndWait().ifPresent(name -> { + if(!name.isBlank() && !playlists.containsKey(name)){ + playlists.put(name, FXCollections.observableArrayList()); + playlistsView.setItems( FXCollections.observableArrayList(playlists.keySet()) ); + } + }); + } + + @FXML + private void openPlayList(String name) { + playlistDetailName.setText(name); + playlistDetailView.setItems(playlists.get(name)); + playlistsListPane.setVisible(false); + playlistsListPane.setManaged(false); + playlistDetailPane.setVisible(true); + playlistDetailPane.setManaged(true); + } + + @FXML + public void handleBackToPlaylists() { + playlistDetailPane.setVisible(false); + playlistDetailPane.setManaged(false); + playlistsListPane.setVisible(true); + playlistsListPane.setManaged(true); + } + + // switching between tabs (library and playlists) + @FXML + public void showLibrary() { + libraryPane.setVisible(true); + libraryPane.setManaged(true); + playlistsPane.setVisible(false); + playlistsPane.setManaged(false); + + libraryTabBtn.getStyleClass().add("tab-active"); + playlistTabBtn.getStyleClass().remove("tab-active"); + } + + @FXML + private void showPlaylists() { + playlistsPane.setVisible(true); + playlistsPane.setManaged(true); + libraryPane.setVisible(false); + libraryPane.setManaged(false); + + playlistTabBtn.getStyleClass().add("tab-active"); + libraryTabBtn.getStyleClass().remove("tab-active"); + } + + // theme controller + @FXML + public void handleThemeToggle() { + isDarkMode = !isDarkMode; + javafx.scene.Scene scene = themeBtn.getScene(); + + if (isDarkMode) { + scene.getRoot().getStyleClass().add("dark"); + themeBtn.setText("☀️"); + } else { + scene.getRoot().getStyleClass().remove("dark"); + themeBtn.setText("🌙"); + } } } diff --git a/src/main/java/sbu/javafx/Playlist.java b/src/main/java/sbu/javafx/Playlist.java new file mode 100644 index 0000000..8ddc989 --- /dev/null +++ b/src/main/java/sbu/javafx/Playlist.java @@ -0,0 +1,47 @@ +package sbu.javafx; + +import java.util.ArrayList; +import java.util.List; + +public class Playlist { + + private String name; + private List songs = new ArrayList<>(); + + // Constructor + public Playlist(String name){ + this.name = name; + } + + // ========================================= getters & setters ============================================ + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getSongs() { + return songs; + } + + // ========================================= Methods ============================================ + public void addSong(Song song){ + if(song != null && !songs.contains(song)) + songs.add(song); + } + + public void removeSong(Song song){ + songs.remove(song); + } + + public int size(){ + return songs.size(); + } + + @Override + public String toString() { + return name + " (" + songs.size() + " songs"; + } +} diff --git a/src/main/java/sbu/javafx/Song.java b/src/main/java/sbu/javafx/Song.java new file mode 100644 index 0000000..7f4dc26 --- /dev/null +++ b/src/main/java/sbu/javafx/Song.java @@ -0,0 +1,73 @@ +package sbu.javafx; + +public class Song { + + private String title; + private String artist; + private String album; + private String filePath; + private String coverPath; + private int duration; // seconds + + // Constructor + public Song(String title, String artist, String album, String filePath, String coverPath, int duration){ + this.title = title; + this.artist = artist; + this.album = album; + this.filePath = filePath; + this.coverPath = coverPath; + this.duration = duration; + } + + // ========================================= Getters ============================================ + public int getDuration() { + return duration; + } + public String getCoverPath() { + return coverPath; + } + public String getFilePath() { + return filePath; + } + public String getAlbum() { + return album; + } + public String getArtist() { + return artist; + } + public String getTitle() { + return title; + } + + // ========================================= Setters ============================================ + public void setTitle(String title) { + this.title = title; + } + public void setArtist(String artist) { + this.artist = artist; + } + public void setAlbum(String album) { + this.album = album; + } + public void setFilePath(String filePath) { + this.filePath = filePath; + } + public void setCoverPath(String coverPath) { + this.coverPath = coverPath; + } + public void setDuration(int duration) { + this.duration = duration; + } + + // ========================================= Methods ============================================ + public String getFormattedDuration(){ + int minutes = duration / 60; + int seconds = duration % 60; + return String.format("%d:%02d)", minutes, seconds); + } + + @Override + public String toString() { + return title + " - " + artist; + } +} diff --git a/src/main/resources/sbu/images/Screenshot1.png b/src/main/resources/sbu/images/Screenshot1.png new file mode 100644 index 0000000..c143a12 Binary files /dev/null and b/src/main/resources/sbu/images/Screenshot1.png differ diff --git a/src/main/resources/sbu/images/Screenshot2.png b/src/main/resources/sbu/images/Screenshot2.png new file mode 100644 index 0000000..96f7e34 Binary files /dev/null and b/src/main/resources/sbu/images/Screenshot2.png differ diff --git a/src/main/resources/sbu/images/Screenshot3.png b/src/main/resources/sbu/images/Screenshot3.png new file mode 100644 index 0000000..8c7594e Binary files /dev/null and b/src/main/resources/sbu/images/Screenshot3.png differ diff --git a/src/main/resources/sbu/images/Screenshot4.png b/src/main/resources/sbu/images/Screenshot4.png new file mode 100644 index 0000000..60608b5 Binary files /dev/null and b/src/main/resources/sbu/images/Screenshot4.png differ diff --git a/src/main/resources/sbu/images/default-cover.png b/src/main/resources/sbu/images/default-cover.png new file mode 100644 index 0000000..2f4b39a Binary files /dev/null and b/src/main/resources/sbu/images/default-cover.png differ diff --git a/src/main/resources/sbu/javafx/App-view.fxml b/src/main/resources/sbu/javafx/App-view.fxml index 306c8d3..ec85ecd 100644 --- a/src/main/resources/sbu/javafx/App-view.fxml +++ b/src/main/resources/sbu/javafx/App-view.fxml @@ -1,8 +1,200 @@ - + + + + + + + + - + - + + + + + +