Skip to content

Commit

Permalink
feat: [MAJOR] CHANGELOG v9.6 + lot of verbose output
Browse files Browse the repository at this point in the history
  • Loading branch information
nots1dd committed Jul 9, 2024
1 parent 3277d17 commit a1c714d
Show file tree
Hide file tree
Showing 14 changed files with 352 additions and 206 deletions.
37 changes: 32 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ A fast and interactive console music player written in C++ for UNIX Systems.

Supports any `.mp3`, `.wav` and `.flac` files! (with or without embedded metadata)

Great TUI with great metadata parsing along with custom keybinds all wrapped in one!

## Features

### Insanely Fast
Expand Down Expand Up @@ -32,6 +34,8 @@ Supports any `.mp3`, `.wav` and `.flac` files! (with or without embedded metadat

* Songs with or without metadata are all displayed with equal song controls!

* Custom keybinds that fit ever user's needs!!

## Demo

https://github.com/nots1dd/Litemus/assets/140317709/253cc58f-7034-46ae-9020-5881612d36da
Expand All @@ -55,12 +59,15 @@ This has only been tried and tested on Arch Linux 6.9 kernel (x86-64)

### Steps:

If you want to do this the hard way (without `build.sh`):

1. Clone this repository: `https://github.com/nots1dd/Litemus.git`
2. Install the required C++ libraries: `ncurses`, `SFML`, `nlohmann-json` (and `ffmpeg in your system`)
2. Install the required C++ libraries: `ncurses`, `SFML`, `nlohmann-json` (and `ffmpeg` in your system)
3. Run `cmake -S . -B build/`
4. Run `cmake --build build/`
5. If the build is successful, copy `keybinds.json` to `$HOME/.config/litemus/` directory (create it if needed)

This will generate a `./build/Litemus` executable. Run it to get the Litemus experience!
This will generate a `./build/Litemus` executable and will be able to read your custom keybinds. Run it to get the Litemus experience!

> <span style="color: green;"><strong>Tip:</strong></span>
>
Expand All @@ -69,6 +76,26 @@ This will generate a `./build/Litemus` executable. Run it to get the Litemus exp
> <span style="color: white;">NOTE: Run <code>chmod +x build.sh</code> in order for it to execute. ALWAYS BE CAREFUL OF WHAT YOU ARE EXECUTING!!</span>

## Configuration

### Keybinds

Custom keybinds are fully implemented within LiteMus, with an efficient system of parsing them (via `nlohmann-json` of course)

-> When building using `build.sh`, a `$HOME/.config/litemus/` directory is created where the current directory's `keybinds.json` is copied to

-> The `$HOME/.config/litemus/keybinds.json` file can be updated as you need!

-> There is verbose output as well that lets the user know if anything went wrong while parsing

> **NOTE:**
>
> The code checks for the TAB, ESCAPE and ENTER special characters only!
>
> Adding unknown fields or any special characters that might pose a security risk while parsing are taken care of
>
> However, if there are still any form of vulnerabilities or unexpected results, feel free to open an issue!
## Installation

There is currently no means of installing this on any Linux distro other than building it from source.
Expand All @@ -79,17 +106,17 @@ If you encounter any issues with building Litemus from this repository in any di
>
> LiteMus can only dynamically window based on the initial window sizes
>
> Hence, it is noted that it still lacks some major dynamic resizing issues
> Hence, it is noted that it still has some major dynamic resizing issues
>
> BEST RESULTS are seen on: 1920x1080 screen size
## Future

- [x] Have clean windows for showing session details, lyrics view
- [ ] Possibly have an audio visualizer (text based) integrated
- [ ] Possibly have an audio visualizer (text based) integrated (POSSIBLY ANOTHER PROJECT)
- [x] Make the code more modular and easy to read, modify
- [x] Add other audio formats (.wav, .flac)
- [ ] Have custom keybinds
- [x] Have custom keybinds

## LICENSE

Expand Down
4 changes: 4 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ cmake --build build/
# Add alias to the appropriate shell rc file
echo "alias lmus=$(pwd)/build/Litemus" >> "$RC_FILE"

mkdir $HOME/.config/litemus/ >/dev/null

cp keybinds.json $HOME/.config/litemus/

echo -e "\n[SUCCESS] Alias added to $RC_FILE. Please restart your terminal or source the rc file to apply the changes."
9 changes: 9 additions & 0 deletions changelog/v9_6.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Change type: MAJOR

Summary

-> Custom keybinds is now implemented (with proper error handling and testing)

-> More verbose output regarding keybinds parse errors

-> Readme, build.sh update regarding `keybinds.json`
1 change: 1 addition & 0 deletions headers/directoryUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extern const string RESET;
extern const string GREEN;
extern const string YELLOW;
extern const string BOLD;
extern const string BLUE;

// Function to create a directory if it doesn't exist
inline void createDirectory(const string& path) {
Expand Down
7 changes: 5 additions & 2 deletions headers/keyHandlers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
#include <menu.h>
#include <cstring>
#include <string>
#include <map>
#include <unordered_map>
#include <thread>
#include <chrono>
#include <SFML/Audio.hpp>

void loadKeybinds(const std::string& filepath, std::unordered_map<std::string, int>& keybinds);
void handleKeyEvent_1(MENU* artistMenu, MENU* songMenu, WINDOW* artist_menu_win, bool showingArtists, int menu_height, int menu_width);
void handleKeyEvent_tab(MENU* artistMenu, MENU* songMenu, WINDOW* artist_menu_win, WINDOW* song_menu_win, bool showingArtists, int menu_height, int menu_width);
void handleKeyEvent_slash(MENU* artistMenu, MENU* songMenu, bool showingArtists);
void displayLyricsWindow(WINDOW *artist_menu_win, std::string& currentLyrics, std::string& currentSong, std::string& currentArtist, int menu_height, int menu_width, sf::Music &music, WINDOW *status_win, bool firstEnterPressed, bool showingLyrics, WINDOW *song_menu_win, MENU *songMenu, std::string& currentGenre, bool showingArtists);
void displayLyricsWindow(WINDOW *artist_menu_win, std::string& currentLyrics, std::string& currentSong, std::string& currentArtist, int menu_height, int menu_width, sf::Music &music, WINDOW *status_win, bool firstEnterPressed, bool showingLyrics, WINDOW *song_menu_win, MENU *songMenu, std::string& currentGenre, bool showingArtists, std::unordered_map<std::string, int>& keybinds);
void quitFunc(sf::Music& music, std::vector<std::string>& allArtists, std::vector<std::string>& songTitles, ITEM** artistItems, ITEM** songItems, MENU* artistMenu, MENU* songMenu);
void printSessionDetails(WINDOW* menu_win, const std::string& songsDirectory, const std::string& cacheDir, const std::string& cacheDebugFile, int artistsSize, int songsSize);
void printSessionDetails(WINDOW* menu_win, const std::string& songsDirectory, const std::string& cacheDir, const std::string& cacheDebugFile, const std::string& keybindsFilePath, int artistsSize, int songsSize);

#endif
2 changes: 1 addition & 1 deletion headers/lmus_cache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ void printArtists(const json& artistsArray);
void storeSongCountAndInodes(const string& infoDirectory, int songCount, const vector<string>& inodes, const vector<string>& songNames, const json& songsInfoArray);
void storeSongsJSON(const string& filePath, const vector<string>& songNames);
bool compareInodeVectors(const vector<string>& vec1, const vector<string>& vec2);
int lmus_cache_main(std::string& songDirectory, const std::string homeDir, const std::string cacheLitemusDirectory, const std::string cacheInfoDirectory, const std::string songCacheInfoFile, const std::string artistsFilePath, const std::string songDirPathCache, const std::string debugFile);
int lmus_cache_main(std::string& songDirectory, const std::string homeDir, const std::string cacheLitemusDirectory, const std::string configLitemusDirectory, const std::string cacheInfoDirectory, const std::string songCacheInfoFile, const std::string artistsFilePath, const std::string songDirPathCache, const std::string debugFile);

#endif // MAIN_HPP
3 changes: 2 additions & 1 deletion headers/ncurses_helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
#include <menu.h>
#include <string>
#include <sstream>
#include <unordered_map>
#include <iomanip>
#include <SFML/Audio.hpp>

void ncursesSetup();
void updateWindowDimensions(int& menu_height, int& menu_width, int& title_height, int& title_width);
void ncursesWinControl(WINDOW* artist_menu_win, WINDOW* song_menu_win, WINDOW* status_win, WINDOW* title_win, const std::string& choice);
void ncursesWinLoop(MENU* artistMenu, MENU* songMenu, WINDOW* artist_menu_win, WINDOW* song_menu_win, WINDOW* status_win, WINDOW* title_win, const char* title_content, bool showingArtMen);
void displayWindow(WINDOW* menu_win, const std::string window);
void displayWindow(WINDOW* menu_win, const std::string window, const std::unordered_map<std::string, int>& keybinds);
void updateStatusBar(WINDOW* status_win, const std::string& songName, const std::string& artistName, const std::string& songGenre, const sf::Music& music, bool firstEnterPressed, bool showingLyrics);
bool showExitConfirmation(WINDOW* parent_win);
void highlightFocusedWindow(MENU* menu, bool focused);
Expand Down
3 changes: 3 additions & 0 deletions headers/parsers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <cctype>
#include <nlohmann/json.hpp>

using namespace std;

std::vector<std::string> parseArtists(const std::string& artistsFile);
std::tuple<std::vector<std::string>, std::vector<std::string>, std::vector<std::string>, std::vector<std::string>, std::vector<std::string>> listSongs(const std::string& cacheFile, const std::string& artistName, const std::string& songsDirectory);
std::pair<std::string, std::string> findCurrentGenreArtist(const std::string& cacheFile, const std::string& currentSong, std::string& currentLyrics);
Expand All @@ -20,5 +22,6 @@ void litemusHelper(const std::string& NC);
std::vector<std::string> getTitlesWithWhiteSpaces(const std::vector<std::string>& songTitles, const std::vector<std::string>& songDurations, size_t maxLength);
size_t getMaxSongTitleLength(const std::vector<std::string>& songTitles, const std::vector<std::string>& songDurations);
std::string removeWhitespace(const std::string& str);
void verboseQuit(const std::string& NC, const std::string& BLUE, const std::string& BOLD);

#endif
79 changes: 72 additions & 7 deletions headers/src/keyHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,71 @@
#include "../ncurses_helpers.hpp"
#include "../parsers.hpp"
#include "../sfml_helpers.hpp"
#include "../directoryUtils.hpp"

using json = nlohmann::json;

std::string toLower(const std::string& str) {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(),
[](unsigned char c){ return std::tolower(c); });
return result;
}

int translateKey(const std::string& key) {
std::string lowerkey = toLower(key);
if (lowerkey == "tab") return 9;
if (lowerkey == "enter") return 10;
if (lowerkey == "escape") return 27;
if (lowerkey == "right") return KEY_RIGHT;
if (lowerkey == "left") return KEY_LEFT;
if (key.length() == 1) return key[0]; // ASCII character
return -1; // Invalid key
}

void loadKeybinds(const std::string& filepath, std::unordered_map<std::string, int>& keybinds) {
std::ifstream keybindsFile(filepath);
if (!keybindsFile.is_open()) {
std::cerr << "Failed to open keybinds file: " << filepath << std::endl;
return;
}

std::cout << YELLOW << BOLD << "---------------------- KEYBINDS -- SETUP ----------------------" << RESET << std::endl;
std::cout << "Parsing keybinds from " << filepath << std::endl;

try {
json json;
keybindsFile >> json;

for (const auto& item : json.items()) {
std::string key = item.key();
std::string value = item.value().get<std::string>();
// Check if key is valid
if (item.key().empty() || !item.value().is_string()) {
std::cerr << "Invalid key or value in keybinds file: " << filepath << std::endl;
std::cout << YELLOW << BOLD << "------------------ KEYBINDS -- SETUP -- END -------------------" << RESET << std::endl;
exit(EXIT_FAILURE);
}
int translatedKey = translateKey(item.value().get<std::string>());
if (translatedKey == -1) {
std::cerr << "Invalid key value in keybinds file: " << filepath << std::endl;
std::cout << YELLOW << BOLD << "------------------ KEYBINDS -- SETUP -- END -------------------" << RESET << std::endl;
exit(EXIT_FAILURE);
}
keybinds[item.key()] = translatedKey;
}
} catch (const json::parse_error& e) {
std::cerr << "JSON parse error in keybinds file " << filepath << ": " << e.what() << std::endl;
std::cout << YELLOW << BOLD << "------------------ KEYBINDS -- SETUP -- END -------------------" << RESET << std::endl;
exit(EXIT_FAILURE);
} catch (const std::exception& e) {
std::cerr << "Error loading keybinds file " << filepath << ": " << e.what() << std::endl;
std::cout << YELLOW << BOLD << "------------------ KEYBINDS -- SETUP -- END -------------------" << RESET << std::endl;
exit(EXIT_FAILURE);
}
std::cout << GREEN << "[SUCCESS] All keybinds parsed without an issue." << RESET << std::endl;
std::cout << YELLOW << BOLD << "------------------ KEYBINDS -- SETUP -- END -------------------" << RESET << std::endl;
}

void handleKeyEvent_1(MENU* artistMenu, MENU* songMenu, WINDOW* artist_menu_win, bool showingArtists, int menu_height, int menu_width) {
werase(artist_menu_win);
Expand Down Expand Up @@ -128,19 +193,19 @@ void handleKeyEvent_slash(MENU* artistMenu, MENU* songMenu, bool showingArtists)
}


void displayLyricsWindow(WINDOW *artist_menu_win, std::string& currentLyrics, std::string& currentSong, std::string& currentArtist, int menu_height, int menu_width, sf::Music &music, WINDOW *status_win, bool firstEnterPressed, bool showingLyrics, WINDOW *song_menu_win, MENU *songMenu, std::string& currentGenre, bool showingArtists) {
void displayLyricsWindow(WINDOW *artist_menu_win, std::string& currentLyrics, std::string& currentSong, std::string& currentArtist, int menu_height, int menu_width, sf::Music &music, WINDOW *status_win, bool firstEnterPressed, bool showingLyrics, WINDOW *song_menu_win, MENU *songMenu, std::string& currentGenre, bool showingArtists, std::unordered_map<std::string, int>& keybinds) {
mvwprintw(artist_menu_win, 0, 2, " Lyrics: ");
wrefresh(artist_menu_win);
werase(artist_menu_win);
int x, y;
getmaxyx(stdscr, y, x); // get the screen dimensions
int warning_width = 75; // adjust this to your liking
int warning_height = 15;
int warning_width = x * 0.34; // adjust this to your liking
int warning_height = y * 0.38;
int warning_y = (LINES - warning_height) / 2;
int warning_x = (COLS - warning_width) / 2 + 10;

WINDOW *warning_win = newwin(warning_height, warning_width, warning_y, warning_x);
displayWindow(warning_win, "warning");
displayWindow(warning_win, "warning", keybinds);

WINDOW *lyrics_win = derwin(artist_menu_win, menu_height - 2, menu_width - 2, 1, 1);
mvwprintw(artist_menu_win, 0, 2, " Lyrics: ");
Expand Down Expand Up @@ -190,7 +255,7 @@ void displayLyricsWindow(WINDOW *artist_menu_win, std::string& currentLyrics, st
wrefresh(lyrics_win);
mvwprintw(song_menu_win, 0, 2, " Songs: ");
wrefresh(song_menu_win);
displayWindow(warning_win, "warning");
displayWindow(warning_win, "warning", keybinds);
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
}
Expand All @@ -208,11 +273,11 @@ void quitFunc(sf::Music& music, std::vector<std::string>& allArtists, std::vecto
free_menu(artistMenu);
}

void printSessionDetails(WINDOW* menu_win, const std::string& songsDirectory, const std::string& cacheDir, const std::string& cacheDebugFile, int artistsSize, int songsSize) {
void printSessionDetails(WINDOW* menu_win, const std::string& songsDirectory, const std::string& cacheDir, const std::string& cacheDebugFile, const std::string& keybindsFilePath, int artistsSize, int songsSize) {
werase(menu_win);
mvwprintw(menu_win, 2, 10, "LiteMus Session Details");
std::stringstream artStr;
artStr << "Directory: " << songsDirectory << std::endl << std::endl << " Cache Directory: " << cacheDir << std::endl << std::endl << " Debug File: " << cacheDebugFile << std::endl
artStr << "Directory: " << songsDirectory << std::endl << std::endl << " Cache Directory: " << cacheDir << std::endl << std::endl << " Debug File: " << cacheDebugFile << std::endl << std::endl << " keybinds.json Path: " << keybindsFilePath << std::endl
<< std::endl << std::endl << " No of artists: " << artistsSize << std::endl << std::endl << " No of songs: " << songsSize;
std::string newartStr = artStr.str();
mvwprintw(menu_win, 4, 4, newartStr.c_str());
Expand Down
3 changes: 2 additions & 1 deletion headers/src/lmus_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,14 +338,15 @@ void drawProgressBar(WINDOW* win, int y, int x, float progress) {
wrefresh(win);
}

int lmus_cache_main(std::string& songDirectory, const std::string homeDir, const std::string cacheLitemusDirectory, const std::string cacheInfoDirectory, const std::string songCacheInfoFile, const std::string artistsFilePath, const std::string songDirPathCache, const std::string debugFile) {
int lmus_cache_main(std::string& songDirectory, const std::string homeDir, const std::string cacheLitemusDirectory, const std::string configLitemusDirectory, const std::string cacheInfoDirectory, const std::string songCacheInfoFile, const std::string artistsFilePath, const std::string songDirPathCache, const std::string debugFile) {

// DIRECTORY VARIABLES
const string cacheDirectory = homeDir + "/.cache/";
cout << BLUE << BOLD << "----------------- LITEMUS -- CACHE -- START ------------------" << RESET << endl;
changeDirectory(songDirectory);
createDirectory(cacheDirectory);
createDirectory(cacheLitemusDirectory);
createDirectory(configLitemusDirectory);
createDirectory(cacheInfoDirectory);

vector<string> inodes = getInodes();
Expand Down
Loading

0 comments on commit a1c714d

Please sign in to comment.