diff options
| author | anand <anand.panchdhari@gmail.com> | 2025-12-17 15:57:55 +0530 |
|---|---|---|
| committer | anand <anand.panchdhari@gmail.com> | 2025-12-17 15:57:55 +0530 |
| commit | b7ef29a8886a57aadb787807a7c6cf74c1f0ed3a (patch) | |
| tree | 366a68240fbc9da6b1d567bd6c46d1350ad814de /rmpc | |
Nixos
Diffstat (limited to 'rmpc')
| -rwxr-xr-x | rmpc/README.md | 102 | ||||
| -rwxr-xr-x | rmpc/assets/adding_to_queue.gif | bin | 0 -> 811296 bytes | |||
| -rwxr-xr-x | rmpc/assets/lyrics.png | bin | 0 -> 950669 bytes | |||
| -rwxr-xr-x | rmpc/assets/rmpc_screenshot.png | bin | 0 -> 2027249 bytes | |||
| -rwxr-xr-x | rmpc/assets/search_demo.gif | bin | 0 -> 1202005 bytes | |||
| -rwxr-xr-x | rmpc/config.ron | 138 | ||||
| -rwxr-xr-x | rmpc/genre_counts.txt | 152 | ||||
| -rwxr-xr-x | rmpc/increment_play_count | 8 | ||||
| -rwxr-xr-x | rmpc/inspect_log.sh | 26 | ||||
| -rwxr-xr-x | rmpc/notify | 26 | ||||
| -rwxr-xr-x | rmpc/themes/catppuccin_mocha.ron | 153 | ||||
| -rwxr-xr-x | rmpc/utils/fetch_album_lyrics.sh | 109 | ||||
| -rwxr-xr-x | rmpc/utils/tag_genres.sh | 38 | ||||
| -rwxr-xr-x | rmpc/utils/tag_music.sh | 38 |
14 files changed, 790 insertions, 0 deletions
diff --git a/rmpc/README.md b/rmpc/README.md new file mode 100755 index 0000000..efc0e35 --- /dev/null +++ b/rmpc/README.md @@ -0,0 +1,102 @@ +# RMPC Configuration + +## 🧠 Overview +This is my personal configuration for rmpc, a command-line client for MPD (Music Player Daemon). It includes custom scripts and MPD settings optimized for a local music library. + +<div align="center"> + <img src="assets/rmpc_screenshot.png" alt="rmpc UI Screenshot" width="800"><br> + <em>A snapshot of the rmpc interface showing the currently playing track.</em> +</div> + +## 📂 Directory Structure +``` +. +├── config.ron # Main rmpc configuration +├── increment_play_count # Script to track song plays +├── notify # Script to send track notifications on song change +├── README.md +└── themes/ + └── catppuccin_mocha.ron # Theme file (RON format) +``` + +## ⚙️ Setup +1. Dependencies + - rmpc (https://mierak.github.io/rmpc/next/overview/) + - mpd (https://mpd.readthedocs.io/en/latest/mpd.conf.5.html) + - Some setup may be required to get mpd up and running, but there are plenty of tutorials available, and the process is relatively simple. + - any terminal notifier if you use the notify script +2. Installation + - Follow rmpc install directions on their website + - Copy config.ron contents to your config file (e.g., `~/.config/rmpc/config.ron`) + - Copy over theme or create your own + - Make scripts executable by running `chmod +x increment_play_count notify` +3. Set up a music directory to store your mp3 files. My config assumes `~/Music/mpd/` is the base directory for rmpc. Within `~/Music/mpd/`, each artist is given a folder containing folders for each of their albums (i.e., `~/Music/mpd/Daft Punk/Discovery/` is the path to a series of mp3's for the album Discovery by Daft Punk). + +## 🎬 Demo + +<div align="center"> + <img src="assets/adding_to_queue.gif" alt="Adding to Queue" width="800"> + <p><em>Adding to Queue</em></p> + <p>A demonstration of how tracks can be quickly added to the play queue using the rmpc interface.</p> +</div> + +<br> + +<div align="center"> + <img src="assets/search_demo.gif" alt="Search Demo" width="800"> + <p><em>Search Demo</em></p> + <p>Using fuzzy search to find artists and albums instantly within a large local library. Assuming .mp3 files are tagged with metadata, you can create advanced searches based on artist, genre, features, etc., using regular expressions.</p> +</div> + +# Utilities For Adding Music & Metadata + +I have a music directory that rmpc pulls from located at `~/Music/mpd/`, so much of the setup will depend on this assummption, but feel free to adjust to your setup. + +## `utils/tag_music.sh` + +I have the `tag_music.sh` script located in my `~/Music/` directory. This script adds standardized ID3 metadata (artist, album, track title, and track number) to each .mp3 file in an album folder using the eyeD3 tool. To use: +- Set value for `ARTIST` variable +- Set value for `ALBUM` variable +- Set value for `DIR` variable, representing the path to the mp3 files for that album +- Fill in the `TRACKS` array with the names of the album's tracks (matching the mp3 filenames) in track-order. +- Run with `./tag_music.sh` from the same directory the script is in and ensure all tracks were found and tagged. + +## Adding Other Metadata + +### Recording Date +To add the album's recording date, which rmpc will use to sort the albums, use one of the following commands (after replacing with the correct date) when in the album's directory: +- `eyed3 --recording-date=2021 *.mp3` +- `eyeD3 --recording-date=2025-01-31 *.mp3` + +### Features +I use the `albumartist` tag (separate from the `artist` tag) to denote the artists who are featured on the album. To speed up the process of tagging features, I include the features in the track name using the following format (`"<song_title> (feat. <featured_artist>).mp3"`) and then run the following command from the album directory: + +```bash +for f in *.mp3; do + if [[ "$f" =~ "\(feat\. ([^)]+)\)" ]]; then + FEATURED="${match[1]}" + echo "Setting album artist to '$FEATURED' for: $f" + eyeD3 --album-artist "$FEATURED" "$f" + fi +done +``` + +### Album Art +To add the album art to rmpc, make sure the album art is stored in the album directory and then run the following command (substituting in the name of the image file). +```bash +for f in *.mp3; do + eyeD3 --add-image=cover.png:FRONT_COVER "$f" +done +``` + +## Adding Genres with `utils/tag_genres.sh` +You can use the script `utils/tag_genres.sh` to add genre tags to each track in an album. Simply navigate to the script and run it as `./tag_genres.sh <path_to_album_directory>`. Then, as you are prompted for each track, enter the genres that describe the track as a list. My preference is to use semicolons as delimiters. + +## Adding Lyrics with `utils/fetch_album_lyrics.sh` +You can download `.lrc` files, and rmpc will display synchronized scrolling lyrics in the Lyrics pane. Use the script `utils/fetch_album_lyrics.sh` to automatically fetch `.lrc` files from [LRCLIB](https://lrclib.net/) for all mp3 files in the album directory. +- Usage: `./fetch_album_lyrics.sh <path_to_album_directory>` + +<div align="center"> + <img src="assets/lyrics.png" alt="rmpc Lyrics Pane" width="800"><br> + <em>The rmpc synchronized scrolling lyrics in the Lyrics pane.</em> +</div> diff --git a/rmpc/assets/adding_to_queue.gif b/rmpc/assets/adding_to_queue.gif Binary files differnew file mode 100755 index 0000000..ae92f42 --- /dev/null +++ b/rmpc/assets/adding_to_queue.gif diff --git a/rmpc/assets/lyrics.png b/rmpc/assets/lyrics.png Binary files differnew file mode 100755 index 0000000..30feb3f --- /dev/null +++ b/rmpc/assets/lyrics.png diff --git a/rmpc/assets/rmpc_screenshot.png b/rmpc/assets/rmpc_screenshot.png Binary files differnew file mode 100755 index 0000000..8db0377 --- /dev/null +++ b/rmpc/assets/rmpc_screenshot.png diff --git a/rmpc/assets/search_demo.gif b/rmpc/assets/search_demo.gif Binary files differnew file mode 100755 index 0000000..3a735c2 --- /dev/null +++ b/rmpc/assets/search_demo.gif diff --git a/rmpc/config.ron b/rmpc/config.ron new file mode 100755 index 0000000..308102f --- /dev/null +++ b/rmpc/config.ron @@ -0,0 +1,138 @@ +#![enable(implicit_some)] +#![enable(unwrap_newtypes)] +#![enable(unwrap_variant_newtypes)] +( + cache_dir: Some("/tmp/rmpc/cache"), + password: None, + theme: "catppuccin_mocha", + on_song_change: ["~/.config/rmpc/increment_play_count"], + volume_step: 5, + max_fps: 30, + scrolloff: 0, + wrap_navigation: false, + enable_mouse: true, + status_update_interval_ms: 1000, + select_current_song_on_change: false, + browser_column_widths: [20, 38, 42], + album_art: ( + method: Auto, + max_size_px: (width: 900, height: 900), + disabled_protocols: ["http://", "https://"], + vertical_align: Top, + horizontal_align: Center, + ), + keybinds: ( + global: { + ":": CommandMode, + ",": VolumeDown, + "s": Stop, + ".": VolumeUp, + "<Tab>": NextTab, + "<S-Tab>": PreviousTab, + "1": SwitchToTab("Queue"), + "2": SwitchToTab("Artists"), + "3": SwitchToTab("Albums"), + "4": SwitchToTab("Search"), + "5": SwitchToTab("Directories"), + "q": Quit, + ">": NextTrack, + "p": TogglePause, + "<": PreviousTrack, + "f": SeekForward, + "z": ToggleRepeat, + "x": ToggleRandom, + "c": ToggleConsume, + "v": ToggleSingle, + "b": SeekBack, + "~": ShowHelp, + "I": ShowCurrentSongInfo, + "O": ShowOutputs, + "P": ShowDecoders, + }, + navigation: { + "k": Up, + "j": Down, + "h": Left, + "l": Right, + "<Up>": Up, + "<Down>": Down, + "<Left>": Left, + "<Right>": Right, + "<C-k>": PaneUp, + "<C-j>": PaneDown, + "<C-h>": PaneLeft, + "<C-l>": PaneRight, + "<C-u>": UpHalf, + "N": PreviousResult, + "a": Add, + "A": AddAll, + "r": Rename, + "n": NextResult, + "g": Top, + "<Space>": Select, + "<C-Space>": InvertSelection, + "G": Bottom, + "<CR>": Confirm, + "i": FocusInput, + "J": MoveDown, + "<C-d>": DownHalf, + "/": EnterSearch, + "<C-c>": Close, + "<Esc>": Close, + "K": MoveUp, + "D": Delete, + }, + queue: { + "D": DeleteAll, + "<CR>": Play, + "<C-s>": Save, + "a": AddToPlaylist, + "d": Delete, + "i": ShowInfo, + "C": JumpToCurrent, + }, + ), + search: ( + case_sensitive: false, + mode: Contains, + tags: [ + (value: "any", label: "Any Tag"), + (value: "artist", label: "Artist"), + (value: "album", label: "Album"), + (value: "title", label: "Title"), + (value: "filename", label: "Filename"), + (value: "genre", label: "Genre"), + (value: "albumartist", label: "Featured"), + ], + ), + artists: ( + album_display_mode: SplitByDate, + album_sort_by: Date, + ), + tabs: [ + ( + name: "Queue", + pane: Split( + direction: Horizontal, + panes: [(size: "20%", pane: Pane(AlbumArt)), (size: "80%", pane: Pane(Queue))], + ), + ), + ( + name: "Artists", + pane: Pane(Artists), + ), + ( + name: "Albums", + pane: Pane(Albums), + ), + ( + name: "Search", + pane: Pane(Search), + ), + ( + name: "Directories", + pane: Pane(Directories), + ), + ], +) + diff --git a/rmpc/genre_counts.txt b/rmpc/genre_counts.txt new file mode 100755 index 0000000..f455533 --- /dev/null +++ b/rmpc/genre_counts.txt @@ -0,0 +1,152 @@ +R&B 101 +Soul 54 +Funk 46 +Trap 36 +Synth-pop 31 +Jazz Rap 26 +Alternative R&B 25 +Pop Rock 21 +Soft Rock 20 +West Coast hip-hop 12 +Neo-Soul 12 +Experimental 12 +Electronic 11 +Alternative Hip Hop 11 +Gospel 10 +Disco 10 +Blues Rock 10 +Adult Contemporary 10 +Singer-Songwriter 9 +Neo Soul 9 +Hip-Hop 9 +Pop 8 +Jazz Fusion 8 +Funk Rock 8 +Dance-pop 8 +Conscious Hip Hop 8 +Ambient Pop 8 +Ambient 8 +Southern Hip Hop 7 +Cloud Rap 7 +Spoken Word 6 +Quiet Storm 6 +G-funk 6 +Experimental Hip Hop 6 +Synthwave 5 +Hip-hop 5 +Hip hop 5 +Folk Rock 5 +Blues 5 +West Coast Hip Hop 4 +Psychedelic Hip Hop 4 +Lo-fi 4 +G-Funk 4 +Country Rock 4 +Americana 4 +Alternative Rock 4 +Techno 3 +Rock 3 +Psychedelic Soul 3 +Progressive Electronic 3 +Post-disco 3 +Mariachi 3 +Jazz rap 3 +Jazz 3 +Indie rock 3 +Funk mandelão 3 +Folk 3 +Experimental R&B 3 +Dream Pop 3 +Contemporary R&B 3 +Brazilian funk 3 +Bebop Jazz 3 +Alternative 3 +Afrobeat 3 +Acoustic Rock 3 +funk 2 +experimental 2 +contemporary R&B 2 +ambient pop 2 +Witch House 2 +West Coast Hip-Hop 2 +Trap Soul 2 +Synth-funk 2 +Synth-Funk 2 +Psychedelic Rap 2 +Psychedelic Funk 2 +Pop Rap 2 +Pop Ballad 2 +Neo soul 2 +Memphis Rap 2 +Instrumental 2 +Industrial Hip Hop 2 +Indie Rock 2 +Hard Rock 2 +Experimental synthpop 2 +Experimental Rap 2 +Eurodisco 2 +Electropop 2 +Electro-Funk 2 +Drill 2 +Conscious hip-hop 2 +Conscious Hip-Hop 2 +Cinematic Pop 2 +Boogie 2 +Ballad 2 +Avant-Garde Hip Hop 2 +Ambient R&B 2 +Acoustic 2 +soul 1 +psychedelic soul 1 +industrial hip hop 1 +funk rock 1 +alternative R&B 1 +acoustic 1 +UK R&B 1 +Trap Metal 1 +Synthpop 1 +Smooth Jazz 1 +Retro Pop 1 +Reggae Fusion 1 +Ratchet music 1 +Rap 1 +Rage Rap 1 +Psychedelic hip hop 1 +Psychedelic funk 1 +Psychedelic Rock 1 +Psychedelic 1 +Protest Rap 1 +Power Pop 1 +Political Rap 1 +Piano Rock 1 +Philadelphia soul 1 +Minimalist soul 1 +Midtempo Funk 1 +Latin Soul 1 +Jazz-Funk 1 +Interlude 1 +Indie 1 +Hyphy 1 +Horror Pop 1 +Hardcore hip-hop 1 +Freestyle 1 +Experimental hip-hop 1 +Electro-funk 1 +Electro-Pop 1 +Downtempo 1 +Disco-funk 1 +Dancehall 1 +Dance 1 +Country Pop 1 +Comedy Rap 1 +Chipmunk Soul 1 +Chillout 1 +Brass-funk 1 +Boom Bap 1 +Baroque Pop 1 +Atmospheric Hip Hop 1 +Ambient Rock 1 +Alternative hip hop 1 +Alternative Hip-Hop 1 +Acoustic Soul 1 +1980s R&B 1 diff --git a/rmpc/increment_play_count b/rmpc/increment_play_count new file mode 100755 index 0000000..6fa8570 --- /dev/null +++ b/rmpc/increment_play_count @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +sticker=$(rmpc sticker get "$FILE" "playCount" | jq -r '.value') +if [ -z "$sticker" ]; then + rmpc sticker set "$FILE" "playCount" "1" +else + rmpc sticker set "$FILE" "playCount" "$((sticker + 1))" +fi diff --git a/rmpc/inspect_log.sh b/rmpc/inspect_log.sh new file mode 100755 index 0000000..660eae6 --- /dev/null +++ b/rmpc/inspect_log.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env sh + +LOGFILE="$HOME/.mpd/log" +OUTFILE="$HOME/.config/rmpc/genre_counts.txt" +TMPFILE="$(mktemp)" + +# Extract and accumulate genres to tmpfile +grep "player: played" "$LOGFILE" | while read -r line; do + filepath=$(echo "$line" | sed -n 's/.*player: played "\(.*\)"/\1/p') + fullpath="$HOME/Music/mpd/$filepath" + + if [ -f "$fullpath" ]; then + genre=$(eyeD3 --no-color "$fullpath" | grep "genre:" | sed -E 's/.*genre: (.*) \(id.*/\1/') + IFS=';' + for g in $genre; do + clean_genre=$(echo "$g" | xargs) + [ -n "$clean_genre" ] && echo "$clean_genre" >> "$TMPFILE" + done + fi +done + +# Count and sort genres +sort "$TMPFILE" | uniq -c | sort -k1 -nr | awk '{ $1=$1; print substr($0, index($0,$2)) " " $1 }' > "$OUTFILE" + +rm "$TMPFILE" +echo "✅ Genre frequency list saved to $OUTFILE" diff --git a/rmpc/notify b/rmpc/notify new file mode 100755 index 0000000..84767d5 --- /dev/null +++ b/rmpc/notify @@ -0,0 +1,26 @@ +#!/usr/bin/env sh + +# Directory where to store temporary data +TMP_DIR="/tmp/rmpc" + +# Ensure the directory is created +mkdir -p "$TMP_DIR" + +# Where to temporarily store the album art received from rmpc +ALBUM_ART_PATH="$TMP_DIR/notification_cover" + +# Path to fallback album art if no album art is found by rmpc/mpd +# Change this to your needs +DEFAULT_ALBUM_ART_PATH="$TMP_DIR/default_album_art.jpg" + +# Save album art of the currently playing song to a file +if ! rmpc albumart --output "$ALBUM_ART_PATH"; then + # Use default album art if rmpc returns non-zero exit code + ALBUM_ART_PATH="${DEFAULT_ALBUM_ART_PATH}" +fi + +MSG_TITLE=$(urlencode "$TITLE") +MSG_ARTIST=$(urlencode "$ARTIST") + +# Integrates with hammerspoon, but use whatever terminal notifier you have set up. +open "hammerspoon://canvasNotify?line1=$TITLE&line2=$ARTIST&duration=3" diff --git a/rmpc/themes/catppuccin_mocha.ron b/rmpc/themes/catppuccin_mocha.ron new file mode 100755 index 0000000..8b9459e --- /dev/null +++ b/rmpc/themes/catppuccin_mocha.ron @@ -0,0 +1,153 @@ +#![enable(implicit_some)] +#![enable(unwrap_newtypes)] +#![enable(unwrap_variant_newtypes)] +( + default_album_art_path: None, + show_song_table_header: true, + draw_borders: true, + browser_column_widths: [20, 38, 42], + background_color: "#1e1e2e", + text_color: "#cdd6f4", + header_background_color: None, + modal_background_color: "#11111b", + tab_bar: ( + enabled: true, + active_style: (fg: "black", bg: "blue", modifiers: "Bold"), + inactive_style: (), + ), + highlighted_item_style: (fg: "#eba0ac", modifiers: "Bold"), + current_item_style: (fg: "black", bg: "blue", modifiers: "Bold"), + borders_style: (fg: "blue"), + highlight_border_style: (fg: "blue"), + symbols: (song: "", dir: "", marker: " ", ellipsis: "..."), + progress_bar: ( + symbols: ["", "", " "], + track_style: (fg: "#1e2030"), + elapsed_style: (fg: "#eba0ac"), + thumb_style: (fg: "#eba0ac", bg: "#1e2030"), + ), + scrollbar: ( + symbols: ["│", "█", "▲", "▼"], + track_style: (), + ends_style: (), + thumb_style: (fg: "blue"), + ), + song_table_format: [ // view in the queue + ( + prop: (kind: Property(Artist), + default: (kind: Text("Unknown")) + ), + width: "15%", + ), + ( + prop: (kind: Property(Title), + default: (kind: Text("Unknown")) + ), + width: "55%", + ), + ( + prop: (kind: Property(Album), style: (fg: "white"), + default: (kind: Text("Unknown Album"), style: (fg: "white")) + ), + width: "20%", + ), + ( + prop: (kind: Sticker("playCount"), default: (kind: Text("0"))), + width: "9", + alignment: Right, + label: "Playcount" + ), + ( + prop: (kind: Property(Duration), + default: (kind: Text("-")) + ), + width: "10%", + alignment: Right, + ), + ], + layout: Split( + direction: Vertical, + panes: [ + ( + pane: Pane(Header), + size: "2", + ), + ( + pane: Pane(Tabs), + size: "3", + ), + ( + pane: Pane(TabContent), + size: "100%", + ), + ( + pane: Pane(ProgressBar), + size: "1", + ), + ], + ), + header: ( + rows: [ + ( + left: [ + (kind: Text("["), style: (fg: "yellow", modifiers: "Bold")), + (kind: Property(Status(StateV2(playing_label: "Playing", paused_label: "Paused", stopped_label: "Stopped"))), style: (fg: "yellow", modifiers: "Bold")), + (kind: Text("]"), style: (fg: "yellow", modifiers: "Bold")) + ], + center: [ + (kind: Property(Song(Title)), style: (modifiers: "Bold"), + default: (kind: Text("No Song"), style: (modifiers: "Bold")) + ) + ], + right: [ + (kind: Property(Widget(Volume)), style: (fg: "blue")) + ] + ), + ( + left: [ + (kind: Property(Status(Elapsed))), + (kind: Text(" / ")), + (kind: Property(Status(Duration))), + (kind: Text(" (")), + (kind: Property(Status(Bitrate))), + (kind: Text(" kbps)")) + ], + center: [ + (kind: Property(Song(Artist)), style: (fg: "yellow", modifiers: "Bold"), + default: (kind: Text("Unknown"), style: (fg: "yellow", modifiers: "Bold")) + ), + (kind: Text(" - ")), + (kind: Property(Song(Album)), + default: (kind: Text("Unknown Album")) + ) + ], + right: [ + ( + kind: Property(Widget(States( + active_style: (fg: "white", modifiers: "Bold"), + separator_style: (fg: "white"))) + ), + style: (fg: "dark_gray") + ), + ] + ), + ], + ), + browser_song_format: [ + ( + kind: Group([ + (kind: Property(Track)), // track # + (kind: Text(" ")), + ]) + ), + ( + kind: Group([ + (kind: Property(Artist)), + (kind: Text(" - ")), + (kind: Property(Title)), + ]), + default: (kind: Property(Filename)) + ), + ], +) + diff --git a/rmpc/utils/fetch_album_lyrics.sh b/rmpc/utils/fetch_album_lyrics.sh new file mode 100755 index 0000000..cfc0d90 --- /dev/null +++ b/rmpc/utils/fetch_album_lyrics.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Usage: fetch_album_lyrics_simple.sh "/path/to/Artist/Album" +# Example: fetch_album_lyrics_simple.sh "$HOME/Music/mpd/Anderson .Paak/Ventura" + +LRCLIB_API="https://lrclib.net/api/get" + +if [ $# -ne 1 ]; then + echo "Usage: $0 \"/path/to/Artist/Album\"" + exit 1 +fi + +ALBUM_DIR="$1" +if [ ! -d "$ALBUM_DIR" ]; then + echo "Error: '$ALBUM_DIR' is not a directory." + exit 1 +fi + +ARTIST="$(basename "$(dirname "$ALBUM_DIR")")" +ALBUM="$(basename "$ALBUM_DIR")" + +# Try to fetch synced lyrics (only the [mm:ss.xx] lines) for a given title +# Arguments: +# $1 = artist +# $2 = album +# $3 = title_to_try +# Returns stdout = JSON .syncedLyrics (or "null"/empty) +get_lyrics_for() { + local artist="$1" + local album="$2" + local title_try="$3" + + curl -sG \ + --data-urlencode "artist_name=${artist}" \ + --data-urlencode "track_name=${title_try}" \ + --data-urlencode "album_name=${album}" \ + "$LRCLIB_API" \ + | jq -r '.syncedLyrics' +} + +# Attempt a single fetch: +# 1) Try with TITLE_RAW (may include “(feat ...)”) +# 2) If that yields "" or "null", strip “(…)" and retry +# 3) If still no lyrics, give up +# 4) If we do get lyrics, write them verbatim to the .lrc file +# +# Arguments: +# $1 = ARTIST +# $2 = ALBUM +# $3 = TITLE_RAW +# $4 = OUTPUT_LRC_FILE (full path, e.g. /.../Song.lrc) +fetch_for_plain() { + local artist="$1" + local album="$2" + local title_try="$3" + local out_lrc="$4" + + # 1. First-pass lookup + local lyrics + lyrics="$(get_lyrics_for "$artist" "$album" "$title_try")" + + # 2. If empty or "null", try stripping "(...)" from title + if [ -z "$lyrics" ] || [ "$lyrics" == "null" ]; then + local stripped + stripped="$(echo "$title_try" | sed -E 's/ *\([^)]*\)//g')" + if [ "$stripped" != "$title_try" ]; then + title_try="$stripped" + lyrics="$(get_lyrics_for "$artist" "$album" "$title_try")" + fi + fi + + # 3. If still empty/null → skip + if [ -z "$lyrics" ] || [ "$lyrics" == "null" ]; then + echo "✗ No lyrics for: \"$title_try\"" + return 1 + fi + + # 4. Write only the synced‐lyrics lines (timestamps + text) + # We drop any existing [ar:], [al:], [ti:] lines from the API payload, + # but typically lrclib returns only timestamped lines anyway. + echo "$lyrics" | sed -E '/^\[(ar|al|ti):/d' > "$out_lrc" + echo "✔ Saved lyrics: $(basename "$out_lrc")" + return 0 +} + +echo "▶ Fetching lyrics for all .mp3 in: $ALBUM_DIR" +echo " Artist: $ARTIST" +echo " Album: $ALBUM" +echo + +shopt -s nullglob +for mp3 in "$ALBUM_DIR"/*.mp3; do + TITLE_RAW="$(basename "$mp3" .mp3)" + LRC_FILE="${mp3%.mp3}.lrc" + + if [ -f "$LRC_FILE" ]; then + echo "– Skipping \"$TITLE_RAW\" (already have .lrc)" + continue + fi + + if ! fetch_for_plain "$ARTIST" "$ALBUM" "$TITLE_RAW" "$LRC_FILE"; then + # a failure just prints the “No lyrics for…” message and moves on + continue + fi +done + +echo +echo "Done." diff --git a/rmpc/utils/tag_genres.sh b/rmpc/utils/tag_genres.sh new file mode 100755 index 0000000..9d1e012 --- /dev/null +++ b/rmpc/utils/tag_genres.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 <path-to-album-directory>" + exit 1 +fi + +DIR="$1" + +cd "$DIR" || { echo "❌ Failed to navigate to $DIR"; exit 1; } + +# Get all mp3s sorted by eyeD3 track number +FILES=() +while IFS= read -r line; do + FILES+=("$line") +done < <( + for f in *.mp3; do + track_num=$(eyeD3 "$f" 2>/dev/null | grep -i "^track:" | awk '{print $2}' | cut -d/ -f1) + printf "%03d|%s\n" "${track_num:-999}" "$f" + done | sort | cut -d'|' -f2 +) + +for file in "${FILES[@]}"; do + [[ -f "$file" ]] || continue + + echo "" + echo "🎵 Now tagging: $file" + eyeD3 "$file" | grep -Ei "title:|track:|genre:" + + read -rp "Enter genre(s) for this track (comma-separated): " genre + + if [[ -n "$genre" ]]; then + eyeD3 --genre="$genre" "$file" + echo "✅ Set genre to: $genre" + else + echo "⏭️ Skipped (no genre entered)" + fi +done diff --git a/rmpc/utils/tag_music.sh b/rmpc/utils/tag_music.sh new file mode 100755 index 0000000..32e3b2c --- /dev/null +++ b/rmpc/utils/tag_music.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +ARTIST="Anderson .Paak" +ALBUM="Oxnard" +DIR="$HOME/Music/mpd/Anderson .Paak/Oxnard" + +declare -a TRACKS=( + "The Chase (feat. Kadhja Bonet)" + "Headlow (feat. Norelle)" + "Tints (feat. Kendrick Lamar)" + "Who R U?" + "6 Summers" + "Saviers Road" + "Smile⧸Petty (feat. Sonyae Elise)" + "Mansa Musa (feat. Dr. Dre & Cocoa Sarai)" + "Brother's Keeper (feat. Pusha T)" + "Anywhere (feat. Snoop Dogg & The Last Artful, Dodgr)" + "Trippy (feat. J. Cole)" + "Cheers (feat. Q-Tip)" + "Sweet Chick (feat. BJ The Chicago Kid)" + "Left To Right" +) + +cd "$DIR" || { echo "❌ Failed to navigate to $DIR"; exit 1; } + +# Iterate over the tracks and apply metadata +for i in "${!TRACKS[@]}"; do + track_num=$((i + 1)) + title="${TRACKS[$i]}" + filename="$title.mp3" + + if [[ -f "$filename" ]]; then + echo "✅ Tagging: $filename" + eyeD3 -a "$ARTIST" -A "$ALBUM" -t "$title" -n "$track_num" "$filename" + else + echo "⚠️ File not found: $filename" + fi +done |
