Compare commits
3 Commits
dcc50fa9f2
...
076e3c0c5e
| Author | SHA1 | Date | |
|---|---|---|---|
| 076e3c0c5e | |||
| acb6ff7e1d | |||
| e1aaf14814 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1 @@
|
||||
demo
|
||||
fuzic
|
||||
|
||||
7
LICENSE
Normal file
7
LICENSE
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright © 2025 Ämin Baumeler
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
131
README.md
Normal file
131
README.md
Normal file
@@ -0,0 +1,131 @@
|
||||
A terminal-based music browser and player.
|
||||
|
||||
🔍 Key Features
|
||||
---------------
|
||||
- [Fuzzily](https://github.com/junegunn/fzf) browse your music library
|
||||
- Treat your local library as part of the global [MusicBrainz universe](https://musicbrainz.org)
|
||||
- Search the MusicBrainz database and discover music artists, and releases
|
||||
- Handle metadata without touching your audio files
|
||||
- Fast and Hackable application with [vim](https://www.vim.org)-like keybindings
|
||||
|
||||
🎧 Description and Use Case
|
||||
---------------------------
|
||||
This application allows for a keyboard-controlled maneuvering of your music
|
||||
library and the MusicBrainz database. All metadata is fetched from the
|
||||
MusicBrainz database without tagging your audio files. Effectively, your local
|
||||
library becomes "embedded" in MusicBrainz. You can control playback while
|
||||
browsing the vast world of music releases.
|
||||
|
||||
🎞️ Screenshot
|
||||
-------------
|
||||

|
||||
|
||||
🛠️ Requirements and Installation
|
||||
--------------------------------
|
||||
### Requirements
|
||||
This is an almost[^1] POSIX-compliant shell script with inline `awk` elements (GNU flavored, we couldn't resist) that makes calls to:
|
||||
- the command-line fuzzy finder [fzf](https://junegunn.github.io/fzf/),
|
||||
- the command-line data transferrer [curl](https://curl.se),
|
||||
- the command-line JSON processor [jq](https://jqlang.org),
|
||||
- the command-line media player [mpv](https://mpv.io), and
|
||||
- the command-line multipurpose relay [socat](http://www.dest-unreach.org/socat/).
|
||||
|
||||
[^1]: It is written in POSIX style, but we make use of zero terminated strings.
|
||||
|
||||
The following may improve your experience:
|
||||
- the command-line multimedia prober [ffprobe](https://ffmpeg.org/ffprobe.html) _(optional, for automatic release detection)_,
|
||||
- the command-line clipboard [xsel](http://www.kfish.org/software/xsel/) _(optional, for yanking urls)_,
|
||||
- Noto fonts _(optional, for emojis in your terminal)_.
|
||||
|
||||
### Installation
|
||||
Run `./scripts/build.sh`, then copy `fuzic` to your preferred location, e.g., `~/.local/bin`, and make it executable.
|
||||
|
||||
🧩 Configuration
|
||||
-----------------
|
||||
Although no configuration is needed, this application may be customized using environment variables.
|
||||
If the file `$XDG_CONFIG_HOME/fuzic/config` (defaults to `$HOME/.config/fuzic/config`) exists, then it will be sourced.
|
||||
You may also specify an alternative location of the configuration file with the environment variable `CONFIGFILE`.
|
||||
|
||||
The **appearance** of the application is controlled using the environment variables defined in `src/sh/theme.sh`.
|
||||
The directory `share/theme` contains example themes.
|
||||
You may execute `CONFIGFILE=share/theme/plain.sh fuzic` to launch `fuzic` using an emojiless and colorless theme.
|
||||
|
||||
Custom **filters** can be defined using the environment variables from `src/sh/filter.sh`.
|
||||
For instance, when you launch `F_2_LIST_ARTISTS="jazz" fuzic`, then the artist list can be queried for the word `jazz` by pressing `alt-2`.
|
||||
|
||||
Also the *key bindings* can be reconfigured to your liking.
|
||||
For that, adjust the environment variables defined in `src/sh/keys.sh`.
|
||||
|
||||
Finally, you can sort the artists according to their [sort name](https://musicbrainz.org/doc/Style/Artist/Sort_Name) by specifying `SORT_ARTIST_DEFAULT=sort-artist-sortname`,
|
||||
and the switch to alphabetical release-group sorting via `SORT_RG_DEFAULT=sort-rg-title`.
|
||||
|
||||
|
||||
▶️ Usage
|
||||
--------
|
||||
For a quick start, run the application and hit `alt-?` to display the available key-bindings.
|
||||
You may also open the application in one of the following predefined modes:
|
||||
```
|
||||
Usage: fuzic [OPTION]
|
||||
|
||||
GENERAL OPTIONS:
|
||||
--help Show this help and exit
|
||||
--artists Default options, list artists of local music
|
||||
--albums List albums of local music
|
||||
--search-artist Search artist on MusicBrainz
|
||||
--search-album Search album on MusicBrainz
|
||||
--artist <mbid> List release groups of given artist <mbid>
|
||||
--releasegroup <mbid> List releases in given release group <mbid>
|
||||
--release <mbid> Show release given by <mbid>
|
||||
|
||||
MANAGE LOCAL MUSIC:
|
||||
--decorate <path> Decorate directory containing a tagged release
|
||||
--decorate-as <path> <mbid> Decorate directory as the relase <mbid>
|
||||
--reload-database <path> Populate database with decorated local music from <path>
|
||||
```
|
||||
|
||||
|
||||
🗄️ Local Music Library
|
||||
----------------------
|
||||
Instead of reading the tags from audio files, this application uses **decorations.**
|
||||
The decoration of a folder is a JSON files that stores the relevant MusicBrainz IDs of the release the folder corresponds to.
|
||||
This assumes that complete releases are stored within separate folders.
|
||||
The application then fetches the relevant metadata, which is displayed to the user.
|
||||
Folders can be decorated using `fuzic --decorate <path>` or `fuzic --decorate-as <path> <mbid>` commands.
|
||||
The first version may be used on tagged files (using, e.g., [MusicBrainz Picard](https://picard.musicbrainz.org/)).
|
||||
With this command, the folder gets decorated with the relevant MusicBrainz IDs are extracted from the files.
|
||||
The latter version does not require the files to be tagged.
|
||||
Instead, it decorates the files found in that folder as the release given by the [MusicBrainz Release ID](https://musicbrainz.org/doc/MusicBrainz_Identifier) `<mbid>`.
|
||||
|
||||
After decorating your folders, you may run `fuzic --reload-database <path>` to build your music library.
|
||||
This command searches the path specified for decorated folders.
|
||||
Once the local music library is built, it will display automatically when `fuzic` is launched.
|
||||
|
||||
⌨️ Basic Keys
|
||||
-------------
|
||||
As mentioned above, you may hit `alt-?` anytime to display the possible keys.
|
||||
Note that `fuzic` allows for two modes: _normal mode_ and _insert mode._
|
||||
Similar to `vim`, keys pressed in the _normal mode_ do not directly write to the input field, but are instead bound to other actions.
|
||||
For instance, the keys `j` and `k` can be used to navigate the list down and up.
|
||||
In the insert mode, triggered using `i`, `a`, or `/`, the keys directly modify the input field.
|
||||
|
||||
Some central keys are `<enter>` to start playback of the selected entry, `<space>` to pause playback, and `ctrl-p` to open the currently loaded playlist.
|
||||
|
||||
🧭 Planned Features
|
||||
-------------------
|
||||
The following features are planned:
|
||||
- offline mode
|
||||
- saving and loading playlists
|
||||
- cover art and artist images
|
||||
- lyrics support
|
||||
- showing current position in track
|
||||
- hooks when played track changes
|
||||
|
||||
🌐 Information Sources
|
||||
----------------------
|
||||
Main metadata is fetched from the MusicBrainz database.
|
||||
This application also access [wikidata](https://wikidata.org), [wikipedia](https://en.wikipedia.org), and [discogs](https://www.discogs.com).
|
||||
In the future, more sources may be added.
|
||||
|
||||
License
|
||||
-------
|
||||
This project is licensed under the [MIT License](./LICENSE).
|
||||
@@ -184,3 +184,7 @@ KBF_GROUP="%s"
|
||||
KBF_KEY="%s"
|
||||
# Format description
|
||||
KBF_DESC="%s"
|
||||
|
||||
# Playlist title
|
||||
# ==============
|
||||
TITLE_PLYLST=" Playlist "
|
||||
|
||||
16
src/main.sh
16
src/main.sh
@@ -601,7 +601,7 @@ while true; do
|
||||
--reverse \
|
||||
--no-sort \
|
||||
--border=double \
|
||||
--border-label="╢ Playlist ╟" \
|
||||
--border-label="$TITLE_PLYLST" \
|
||||
--no-input \
|
||||
--margin="2%,10%" \
|
||||
--bind="$KEYS_DOWN,$KEYS_N_DOWN:down" \
|
||||
@@ -623,6 +623,8 @@ while true; do
|
||||
--bind="$KEYS_KEYBINDINGS:preview:$0 --show-keybindings $VIEW_PLAYLIST" \
|
||||
--bind="$KEYS_SCROLL_PREVIEW_DOWN:preview-down" \
|
||||
--bind="$KEYS_SCROLL_PREVIEW_UP:preview-up" \
|
||||
--bind="$KEYS_PREVIEW_TOGGLE_WRAP:toggle-preview-wrap" \
|
||||
--bind="$KEYS_PREVIEW_TOGGLE_SIZE:change-preview-window(right,90%,border-line,nowrap|$FZF_DEFAULT_PREVIEW_WINDOW)" \
|
||||
--bind="$KEYS_PREVIEW_CLOSE:hide-preview" \
|
||||
--bind="$KEYS_PLAYBACK,$KEYS_N_PLAYBACK:transform($0 --playback $VIEW_PLAYLIST {3} {4} {5})+$FZF_RELOAD_PLAYLIST+$FZF_POS_PLAYLIST" \
|
||||
--bind="$KEYS_PLAYLIST_RELOAD:$FZF_RELOAD_PLAYLIST+$FZF_POS_PLAYLIST" \
|
||||
@@ -635,6 +637,8 @@ while true; do
|
||||
--bind="$KEYS_PLAYLIST_SHUFFLE:execute-silent($0 --playlist $PLAYLIST_CMD_SHUFFLE)+$FZF_RELOAD_PLAYLIST" \
|
||||
--bind="$KEYS_PLAYLIST_UNSHUFFLE:execute-silent($0 --playlist $PLAYLIST_CMD_UNSHUFFLE)+$FZF_RELOAD_PLAYLIST" \
|
||||
--bind="$KEYS_PLAYLIST_GOTO_RELEASE:print($VIEW_RELEASE)+accept" \
|
||||
--preview-window="hidden" \
|
||||
--wrap-sign="" \
|
||||
--delimiter="\t" \
|
||||
--with-nth="{1}" \
|
||||
--accept-nth="{3}" || true
|
||||
@@ -668,7 +672,7 @@ while true; do
|
||||
--info="inline-right" \
|
||||
--header-first \
|
||||
--header-border="bottom" \
|
||||
--bind="start:transform:$0 --action-draw $MODE $VIEW "0" $MBID" \
|
||||
--bind="start:transform:$0 --action-draw $MODE $VIEW 0 $MBID" \
|
||||
--bind="$KEYS_I_NORMAL:transform:$IN_NORMAL_MODE || echo hide-input" \
|
||||
--bind="$KEYS_N_INSERT:transform:$IN_NORMAL_MODE && echo show-input || $PUT_FZF_KEY_LOGIC" \
|
||||
--bind="$KEYS_DOWN:down" \
|
||||
@@ -683,7 +687,7 @@ while true; do
|
||||
--bind="$KEYS_OUT:transform:$0 --action-draw $FZF_CURRENT_MODE $FZF_CURRENT_VIEW \"-1\" $FZF_CURRENT_MBID" \
|
||||
--bind="$KEYS_N_IN:transform:$IN_NORMAL_MODE && ([ {4} ] && $0 --action-draw $FZF_CURRENT_MODE $FZF_CURRENT_VIEW \"+1\" {4}) || $PUT_FZF_KEY_LOGIC" \
|
||||
--bind="$KEYS_N_OUT:transform:$IN_NORMAL_MODE && ($0 --action-draw $FZF_CURRENT_MODE $FZF_CURRENT_VIEW \"-1\" $FZF_CURRENT_MBID) || $PUT_FZF_KEY_LOGIC" \
|
||||
--bind="$KEYS_SELECT_ARTIST:transform:$0 --action-gotoartist $FZF_CURRENT_MODE $FZF_CURRENT_VIEW $FZF_CURRENT_MBID {4}" \
|
||||
--bind="$KEYS_SELECT_ARTIST:transform:$0 --action-gotoartist $FZF_CURRENT_MODE $FZF_CURRENT_VIEW \"$FZF_CURRENT_MBID\" {4}" \
|
||||
--bind="$KEYS_LIST_ARTISTS:transform:$0 --action-draw \$FZF_INPUT_STATE $VIEW_LIST_ARTISTS \"0\"" \
|
||||
--bind="$KEYS_LIST_ALBUMS:transform:$0 --action-draw \$FZF_INPUT_STATE $VIEW_LIST_ALBUMS \"0\"" \
|
||||
--bind="$KEYS_SEARCH_ARTIST:transform:$0 --action-draw $MODE_INSERT $VIEW_SEARCH_ARTIST \"0\"" \
|
||||
@@ -717,7 +721,7 @@ open \"\$(dirname {5})\"" \
|
||||
--bind="$KEYS_YANK_CURRENT:execute-silent:printf $FZF_CURRENT_MBID | $CLIP" \
|
||||
--bind="$KEYS_SHOW_PLAYLIST:transform:echo \"print($VIEW_PLAYLIST)+print()+print($FZF_CURRENT_VIEW)+print($FZF_CURRENT_MBID)+accept\"" \
|
||||
--bind="$KEYS_KEYBINDINGS:preview:$0 --show-keybindings $FZF_CURRENT_VIEW" \
|
||||
--bind="$KEYS_REFRESH:execute-silent($0 --remove-from-cache $FZF_CURRENT_VIEW $FZF_CURRENT_MBID {4})+transform:$0 --action-draw $FZF_CURRENT_MODE $FZF_CURRENT_VIEW \"0\" $FZF_CURRENT_MBID" \
|
||||
--bind="$KEYS_REFRESH:execute-silent($0 --remove-from-cache $FZF_CURRENT_VIEW \"$FZF_CURRENT_MBID\" {4})+transform:$0 --action-draw $FZF_CURRENT_MODE $FZF_CURRENT_VIEW \"0\" $FZF_CURRENT_MBID" \
|
||||
--bind="$KEYS_QUIT:print($VIEW_QUIT)+accept" \
|
||||
--bind="$KEYS_N_QUIT:transform:$IN_NORMAL_MODE && ($IN_LIST_ARTISTS_VIEW && echo \"print($VIEW_QUIT)+accept\" || $0 --action-draw $MODE_NORMAL $VIEW_LIST_ARTISTS \"0\") || $PUT_FZF_KEY_LOGIC" \
|
||||
--bind="$KEYS_SCROLL_PREVIEW_DOWN:preview-down" \
|
||||
@@ -726,8 +730,8 @@ open \"\$(dirname {5})\"" \
|
||||
--bind="$KEYS_PREVIEW_TOGGLE_SIZE:change-preview-window(right,90%,border-line,nowrap|$FZF_DEFAULT_PREVIEW_WINDOW)" \
|
||||
--bind="$KEYS_PREVIEW_OPEN:show-preview" \
|
||||
--bind="$KEYS_PREVIEW_CLOSE:hide-preview" \
|
||||
--bind="$KEYS_PLAYBACK:transform:$0 --playback $FZF_CURRENT_VIEW $FZF_CURRENT_MBID {4} {5}" \
|
||||
--bind="$KEYS_N_PLAYBACK:transform:$IN_NORMAL_MODE && $0 --playback $FZF_CURRENT_VIEW $FZF_CURRENT_MBID {4} {5} || $PUT_FZF_KEY_LOGIC" \
|
||||
--bind="$KEYS_PLAYBACK:transform:$0 --playback $FZF_CURRENT_VIEW \"$FZF_CURRENT_MBID\" {4} {5}" \
|
||||
--bind="$KEYS_N_PLAYBACK:transform:$IN_NORMAL_MODE && $0 --playback $FZF_CURRENT_VIEW \"$FZF_CURRENT_MBID\" {4} {5} || $PUT_FZF_KEY_LOGIC" \
|
||||
--bind="change:execute-silent($0 --mbsearch $FZF_CURRENT_VIEW &)+reload:$0 --lines $FZF_CURRENT_VIEW" \
|
||||
--preview-window="$FZF_DEFAULT_PREVIEW_WINDOW" \
|
||||
--wrap-sign="" \
|
||||
|
||||
@@ -387,5 +387,10 @@ if [ ! "${THEME_LOADED:-}" ]; then
|
||||
KBF_DESC="${KBF_DESC:-"${CKB}%s${OFF}"}"
|
||||
export KBF_GROUP KBF_KEY KBF_DESC
|
||||
|
||||
# Playlist title
|
||||
# ==============
|
||||
TITLE_PLYLST="${TITLE_PLYLST:-" 🎶 ${CARTIST}Playlist${OFF} "}"
|
||||
export TITLE_PLYLST
|
||||
|
||||
export THEME_LOADED=1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user