1 Commits
Author SHA1 Message Date
Saba_frm 97a190a7ba develop 2026-06-07 21:23:47 +03:30
30 changed files with 683 additions and 32 deletions
+1 -3
View File
@@ -8,7 +8,5 @@
</list> </list>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" default="true" project-jdk-name="23" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_25" project-jdk-name="21" project-jdk-type="JavaSDK" />
<output url="file://$PROJECT_DIR$/out" />
</component>
</project> </project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+7 -2
View File
@@ -11,7 +11,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.10.2</junit.version> </properties> <junit.version>5.10.2</junit.version> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -24,8 +24,13 @@
<artifactId>javafx-fxml</artifactId> <artifactId>javafx-fxml</artifactId>
<version>21</version> <version>21</version>
</dependency> </dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>21</version>
</dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId> <artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version> <version>${junit.version}</version>
+7 -3
View File
@@ -2,7 +2,11 @@ package sbu.javafx;
import javafx.application.Application; import javafx.application.Application;
public class Launcher{ public class Launcher
public static void main(String[] args) { {
// TODO: Launch the JavaFX application by calling Application.launch(Main.class) } public static void main(String[] args)
{
// TODO: Launch the JavaFX application by calling Application.launch(Main.class)
Application.launch(MusicApp.class, args);
}
} }
+27 -5
View File
@@ -3,21 +3,43 @@ package sbu.javafx;
import javafx.application.Application; import javafx.application.Application;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.IOException; import java.io.IOException;
import java.util.Objects;
public class MusicApp extends Application { public class MusicApp extends Application
{
@Override @Override
public void start(Stage stage) throws IOException 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.setTitle("Music Player");
stage.setScene(scene); 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(); stage.show();
} }
} public static void main(String[] args)
{
launch(args);
}
}
+365 -15
View File
@@ -1,31 +1,381 @@
package sbu.javafx; 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.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.control.Label;
import javafx.scene.input.MouseEvent; import javafx.scene.control.Slider;
import javafx.scene.layout.VBox; 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<String, String> songDisplayNames = new HashMap<>();
private static final ObservableList<String> playlist = FXCollections.observableArrayList();
private static final ObservableList<String> favorites = FXCollections.observableArrayList();
@FXML @FXML
public void handlePlayAction() { public void initialize()
//Update labels, change button icons, etc. {
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) { visualizerAnimation = new Timeline();
//Add the song to a list or update a ListView. 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() { if (favorites.contains(displayName))
//Create a new stage and show the playlist UI. {
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));
}
}
@@ -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<String> playlistListView;
private ObservableList<String> playlist;
private Scene previousScene;
/**
* Called from MusicController after loading Playlist-view.fxml
*/
public void setPlaylist(ObservableList<String> 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();
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.
+61 -4
View File
@@ -1,8 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?> <?import javafx.scene.control.*?>
<VBox xmlns="http://javafx.com/fxml" alignment="CENTER" spacing="10"> <?import javafx.scene.layout.*?>
<!-- TODO: Add your code here! --> <BorderPane xmlns="http://javafx.com/javafx/21"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="sbu.javafx.MusicController"
stylesheets="@spotify.css">
</VBox> <top>
<HBox styleClass="header" spacing="12" alignment="CENTER_LEFT" style="-fx-padding: 15;">
<Label text="Music Player" styleClass="h1"/>
<Region HBox.hgrow="ALWAYS"/>
<Label text="Now Playing:" styleClass="muted"/>
<Label fx:id="nowPlayingLabel" text="None" styleClass="song-title"/>
</HBox>
</top>
<center>
<VBox spacing="12" style="-fx-padding: 18;">
<ScrollPane fitToWidth="true"
style="-fx-background-color: transparent; -fx-background: transparent;"
VBox.vgrow="ALWAYS">
<VBox fx:id="songContainer"
spacing="12"
style="-fx-background-color: transparent;" />
</ScrollPane>
<VBox spacing="10" styleClass="song-card">
<HBox alignment="CENTER" spacing="20">
<HBox alignment="CENTER_LEFT" spacing="12" prefWidth="250">
<StackPane fx:id="visualizerContainer" prefWidth="30" prefHeight="30" />
<VBox alignment="CENTER_LEFT">
<Label fx:id="currentTrackTitle" text="Not Playing" styleClass="song-title" style="-fx-font-size: 13;"/>
<Label fx:id="timeLabel" text="00:00 / 00:00" styleClass="song-meta"/>
</VBox>
</HBox>
<Slider fx:id="progressSlider" min="0" max="100" value="0" HBox.hgrow="ALWAYS"/>
</HBox>
<HBox spacing="15" alignment="CENTER">
<Button fx:id="shuffleButton" text="Shuffle" onAction="#toggleShuffle" styleClass="btn-ghost"/>
<Button text="Prev" onAction="#previousTrack" styleClass="btn-ghost"/>
<Button fx:id="likeButton" text="♡" onAction="#toggleLike" styleClass="btn-ghost" style="-fx-font-size: 16px;"/>
<Button fx:id="playPauseButton" text="Pause" onAction="#togglePlayPause" styleClass="btn-green" prefWidth="80"/>
<Button text="Next" onAction="#nextTrack" styleClass="btn-ghost"/>
<Button fx:id="repeatButton" text="Repeat" onAction="#toggleRepeat" styleClass="btn-ghost"/>
</HBox>
</VBox>
<HBox spacing="10" alignment="CENTER" style="-fx-padding: 5;">
<Label text="Vol:" styleClass="song-meta"/>
<Slider fx:id="volumeSlider" min="0" max="100" value="50" prefWidth="150"/>
<Region HBox.hgrow="ALWAYS" />
<Button text="View Favorites" styleClass="btn-ghost" onAction="#goToFavoritesScene" style="-fx-margin-right: 10;"/>
<Button text="View Playlist" styleClass="btn-ghost" onAction="#goToPlaylistScene"/>
</HBox>
</VBox>
</center>
</BorderPane>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane xmlns="http://javafx.com/javafx/21"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="sbu.javafx.PlaylistController"
stylesheets="@spotify.css">
<top>
<HBox styleClass="header" spacing="10" alignment="CENTER_LEFT">
<Button text="← Back" styleClass="btn-ghost" onAction="#goBack"/>
<Label text="Playlist" styleClass="h1"/>
</HBox>
</top>
<center>
<VBox spacing="12" style="-fx-padding: 18;">
<ListView fx:id="playlistListView" VBox.vgrow="ALWAYS"/>
<HBox alignment="CENTER_RIGHT">
<Button text="Remove Selected" onAction="#removeSelected"/>
</HBox>
</VBox>
</center>
</BorderPane>
+113
View File
@@ -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;
}