I have upgrade the RofiBeats into a full fledge Music Player but i am facing some issues and errors can someone help me. #335
Md-Hasan-Hamid
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
#!/bin/bash
────────────────────────────────────────────────────────────────────
🎵 RofiBEATS — Advanced Music Player (v3.6.0)
────────────────────────────────────────────────────────────────────
set -o pipefail
═══════════════════════════════════════════════════════════════════
🔧 CONFIGURATION SECTION
═══════════════════════════════════════════════════════════════════
readonly mDIR="${HOME}/Music"
readonly iDIR="${HOME}/.config/swaync/icons"
readonly rofi_theme="${HOME}/.config/rofi/config-rofi-Beats.rasi"
readonly rofi_theme_menu="${HOME}/.config/rofi/config-rofi-Beats-menu.rasi"
readonly cache_dir="${HOME}/.cache/rofi-beats"
readonly history_file="${cache_dir}/youtube_history.txt"
readonly favorites_file="${cache_dir}/favorites.txt"
readonly queue_file="${cache_dir}/queue.txt"
readonly downloads_dir="${mDIR}/Downloaded"
readonly video_downloads_dir="${mDIR}/Videos"
readonly config_file="${cache_dir}/config.conf"
readonly mpv_socket="${XDG_RUNTIME_DIR:-/tmp}/mpv-rofi-beats.sock"
readonly pid_file="${cache_dir}/rofi-beats.pid"
readonly log_file="${cache_dir}/rofi-beats.log"
readonly download_log="${cache_dir}/download_log.txt"
readonly cache_url_file="${cache_dir}/.last_url"
readonly cache_title_file="${cache_dir}/.last_title"
mkdir -p "$cache_dir" "$downloads_dir" "$video_downloads_dir" 2>/dev/null || true
readonly SCRIPT_VERSION="3.6.0"
readonly MAX_HISTORY_ENTRIES=150
readonly MAX_QUEUE_SIZE=500
readonly YT_DLP_TIMEOUT=30
readonly IPC_TIMEOUT=3
readonly NOTIFICATION_DURATION=4
readonly SOCKET_WAIT_TIME=10
readonly MAX_RETRIES=3
─── Browser Detection ───────────────────────────────────────────────
detect_browser() {
if command -v brave &>/dev/null; then
echo "brave"
elif command -v chromium &>/dev/null; then
echo "chromium"
elif command -v google-chrome &>/dev/null; then
echo "google-chrome"
elif command -v firefox &>/dev/null; then
echo "firefox"
else
echo ""
fi
}
readonly DEFAULT_BROWSER=$(detect_browser)
─── Global Variables ────────────────────────────────────────────────
AUTOPLAY=1
SHUFFLE_ENABLED=0
REPEAT_MODE="none"
VOLUME_LEVEL=100
CURRENT_PLAYLIST=()
DOWNLOAD_QUALITY="best"
LAST_SELECTED_URL=""
LAST_SELECTED_TITLE=""
Cache last selected URL
cache_selection() {
local title="$1"
local url="$2"
echo "$url" > "$cache_url_file"
echo "$title" > "$cache_title_file"
LAST_SELECTED_URL="$url"
LAST_SELECTED_TITLE="$title"
log_debug "Cached: $title | $url"
}
Load cached URL
load_cached_url() {
if [[ -f "$cache_url_file" ]]; then
LAST_SELECTED_URL=$(cat "$cache_url_file" 2>/dev/null)
LAST_SELECTED_TITLE=$(cat "$cache_title_file" 2>/dev/null)
fi
}
═══════════════════════════════════════════════════════════════════
🎵 ONLINE STATIONS ARRAY
═══════════════════════════════════════════════════════════════════
declare -A online_music=(
["FM - Easy Rock 96.3 📻🎶"]="https://radio-stations-philippines.com/easy-rock"
["FM - Easy Rock - Baguio 91.9 📻🎶"]="https://radio-stations-philippines.com/easy-rock-baguio"
["FM - Love Radio 90.5 📻🎶"]="https://radio-stations-philippines.com/pinoy-love-radio"
["FM - WRock - CEBU 96.3 📻🎶"]="https://onlineradio.ph/126-96-3-wrock.html"
["FM - Fresh Philippines 📻🎶"]="https://onlineradio.ph/553-fresh-fm.html"
["Radio - Lofi Girl 🎧🎶"]="https://play.streamafrica.net/lofiradio"
["Radio - Chillhop 🎧🎶"]="http://stream.zeno.fm/fyn8eh3h5f8uv"
["Radio - Ibiza Global 🎧🎶"]="https://filtermusic.net/ibiza-global"
["Radio - Metal Music 🎧🎶"]="https://tunein.com/radio/mETaLmuSicRaDio-s119867/"
["YT - Wish 107.5 YT Pinoy HipHop 📻🎶"]="https://youtube.com/playlist?list=PLkrzfEDjeYJnmgMYwCKid4XIFqUKBVWEs&si=vahW_noh4UDJ5d37"
["YT - Youtube Top 100 Songs Global 📹🎶"]="https://youtube.com/playlist?list=PL4fGSI1pDJn6puJdseH2Rt9sMvt9E2M4i&si=5jsyfqcoUXBCSLeu"
["YT - Wish 107.5 YT Wishclusives 📹🎶"]="https://youtube.com/playlist?list=PLkrzfEDjeYJn5B22H9HOWP3Kxxs-DkPSM&si=d_Ld2OKhGvpH48WO"
["YT - Relaxing Piano Music 🎹🎶"]="https://youtu.be/6H7hXzjFoVU?si=nZTPREC9lnK1JJUG"
["YT - Youtube Remix 📹🎶"]="https://youtube.com/playlist?list=PLeqTkIUlrZXlSNn3tcXAa-zbo95j0iN-0"
["YT - Korean Drama OST 📹🎶"]="https://youtube.com/playlist?list=PLUge_o9AIFp4HuA-A3e3ZqENh63LuRRlQ"
["YT - lofi hip hop radio beats 📹🎶"]="https://www.youtube.com/live/jfKfPfyJRdk?si=PnJIA9ErQIAw6-qd"
["YT - BollyWood 90s 📹🎶"]="https://zeno.fm/radio/retro-bollywood-90s/"
["YT - Relaxing Piano Jazz Music 🎹🎶"]="https://youtu.be/85UEqRat6E4?si=jXQL1Yp2VP_G6NSn"
["YT - Phunks 🎹🎶"]="https://youtu.be/rKKLKzb5Ld4?si=fxDXp30U8J_zGnkZ"
["YT - Unnakul Naane🎶"]="https://youtu.be/661K4XN_egs?si=RCnfaGTCqNiXCi7r"
["FM - Tamil Mirchi 98.3 📻🎶"]="http://radios.crabdance.com:8002/1"
["FM - Suryan 93.5 📻🎶"]="http://radios.crabdance.com:8002/2"
["FM - Hello 106.4 📻🎶"]="http://radios.crabdance.com:8002/3"
["FM - Shoutout 📻🎶"]="https://orf-live.ors-shoutcast.at/oe3-q2a"
["FM - Mirchi 📻🎶"]="https://listen.openstream.co/4603/audio"
["FM - Md.Rafi 📻🎶"]="https://stream.zeno.fm/2xx62x8ztm0uv"
["FM - Ms Lata 💖📻🎶"]="https://stream.zeno.fm/87xam8pf7tzuv"
["FM - Mr.Kishore Sir 📻🎶"]="https://stream.zeno.fm/0ghtfp8ztm0uv"
["FM - Ishq 99.9 📻🎶"]="https://drive.uber.radio/uber/bollywoodlove/icecast.audio"
["FM - Radio City 📻🎶"]="https://stream.zeno.fm/pxc55r5uyc9uv"
["FM - Ghazal City 📻🎶"]="https://stream.zeno.fm/yo0kzyzedittv"
["Arijit Sir 🎤🎶"]="https://stream.zeno.fm/wn6muavgfr2tv"
["Today's Hit 🔥🎧"]="https://ice1.streeemer.com:8030/radio.aac"
["Tamil 100 🎵💯"]="https://stream.zeno.fm/ex1yqu2gsh1tv"
["Bhojpuriya 💃🎶"]="https://stream.zeno.fm/zqyhigwwo5mvv"
["Radio Vrishti 🌧️📻"]="https://stream.zeno.fm/un3qjmd4stbtv"
["Mixify Eng 🇬🇧🎶"]="https://server.mixify.in/listen/english/radio.mp3"
["Kiss Fm 💋📻"]="https://srv01.onlineradio.voaplus.com/kissfm"
["YT - Best Hindi Songs 2025 🎶"]="https://www.youtube.com/playlist?list=PLRZlMhcYkA2FYuTGWiVTkSz18o2pK8Hv4"
["YT - Latest Hindi Songs 2025 🎶"]="https://www.youtube.com/playlist?list=PL3oW2tjiIxvTSdJ4zqjL9dijeZ0LjcuGS"
["YT - New Hindi Hits 2025 🎶"]="https://www.youtube.com/playlist?list=PLw-VjHDlEOgvh3fIZ8VSWWE09eYGO8uFI"
["YT - Top Tamil Hits 2025 🎶"]="https://www.youtube.com/playlist?list=PLdPQQOxV3l0_11bBrgSX5CCb0YtcBGhhA"
["YT - Latest Tamil Hits 2025 🎶"]="https://www.youtube.com/playlist?list=PL3oW2tjiIxvTaC6caIGR55W3ssqGvb_LR"
["YT - New Tamil Songs 2025 🎶"]="https://www.youtube.com/playlist?list=PLinS5uF49IBpPnKzwrk8nu29EQiK4fuQs"
["YT - Top 100 English Songs 2025 🎶"]="https://www.youtube.com/playlist?list=PLx0sYbCqOb8QTF1DCJVfQrtWknZFzuoAE"
["YT - Top English Hits 2025 🎶"]="https://www.youtube.com/playlist?list=PLDIoUOhQQPlXr63I_vwF9GD8sAKh77dWU"
["YT - Top English Music Videos 2025 📹🎶"]="https://www.youtube.com/watch?v=0rYKbMjtnMo"
["YT - Sixties Greatest Hits 🎶"]="https://www.youtube.com/playlist?list=PLGBuKfnErZlCkRRgt06em8nbXvcV5Sae7"
["YT - Best 60s Songs Playlist 🎶"]="https://www.youtube.com/playlist?list=PLxA687tYuMWhsg1ZOb7dctduaJu4yUPUI"
["YT - Golden Oldies 60s Playlist 🎶"]="https://www.youtube.com/playlist?list=PLeqdIr58xuT3FqvbW1ThebL8LRoyGX3Dz"
["YT - 80s Music Hits Playlist 🎶"]="https://www.youtube.com/playlist?list=PLmXxqSJJq-yXrCPGIT2gn8b34JjOrl4Xf"
["YT - Greatest 80s Hits 🎶"]="https://www.youtube.com/playlist?list=PLCD0445C57F2B7F41"
["YT - Nonstop 80s Greatest Hits 📹🎶"]="https://www.youtube.com/watch?v=qRmTW6ruKy0"
["YT - Greatest 90s Music Hits 🎶"]="https://www.youtube.com/playlist?list=PL7DA3D097D6FDBC02"
["YT - 90s Radio Mix [Live] 📻🎶"]="https://www.youtube.com/watch?v=Ukyd5pBfJjM"
["YT - Best 90s Music Mix 📹🎶"]="https://www.youtube.com/watch?v=LvTQ6LLhlxQ"
["YT - 60s Hindi Hits (Rafi, Kishore, Lata) 🎶"]="https://www.youtube.com/watch?v=40L252sVd-c"
["YT - 80s Hindi Hits (Kishore Kumar) 🎶"]="https://www.youtube.com/watch?v=q1ErQ_TIi6w"
["YT - 90s Best Hindi Hits Collection 🎶"]="https://www.youtube.com/watch?v=8u1Bnxn9PTY"
["YT - 90s Tamil Hits (AR Rahman) 🎶"]="https://www.youtube.com/watch?v=nlc7bSWTtP4"
)
═══════════════════════════════════════════════════════════════════
═══════════════════════════════════════════════════════════════════
trap 'error_handler $? $LINENO' ERR
trap 'cleanup_exit' EXIT INT TERM
error_handler() {
local exit_code=$1
local line_number=$2
log_error "Error on line $line_number (exit code: $exit_code)"
}
cleanup_exit() {
rm -f "$mpv_socket" 2>/dev/null || true
log_info "RofiBEATS v$SCRIPT_VERSION exited cleanly"
}
log_info() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] [INFO] $*" >> "$log_file"
}
log_error() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] [ERROR] $*" >> "$log_file"
}
log_debug() {
[[ "${DEBUG:-0}" == "1" ]] && echo "[$(date +'%Y-%m-%d %H:%M:%S')] [DEBUG] $*" >> "$log_file"
}
═══════════════════════════════════════════════════════════════════
🔔 NOTIFICATION SYSTEM
═══════════════════════════════════════════════════════════════════
notification() {
local urgency="${1:-normal}"
local title="${2:-RofiBEATS}"
local message="${3:-}"
notify-send -u "$urgency" -t $((NOTIFICATION_DURATION * 1000))
-i "$iDIR/music.png" "$title" "$message" 2>/dev/null || true
log_info "Notification: $title - $message"
}
notify_error() {
notification critical "🎵 Error" "$1"
}
notify_success() {
notification normal "✅ Success" "$1"
}
notify_info() {
notification low "ℹ️ Info" "$1"
}
═══════════════════════════════════════════════════════════════════
🎵 MUSIC STATE FUNCTIONS
═══════════════════════════════════════════════════════════════════
music_playing() {
pgrep -x "mpv" >/dev/null 2>&1
return $?
}
is_socket_alive() {
if [[ ! -S "$mpv_socket" ]]; then
return 1
fi
Direct socat ping test instead of bash exec
local resp=$(echo '{"command": ["get_property", "playback-time"]}' | timeout 1 socat - "UNIX-CONNECT:$mpv_socket" 2>/dev/null)
if [[ -n "$resp" && "$resp" =~ "data" ]]; then
return 0
else
return 1
fi
}
ensure_socket_clean() {
if [[ -S "$mpv_socket" ]] && ! music_playing; then
rm -f "$mpv_socket" 2>/dev/null || true
sleep 0.1
fi
}
wait_for_socket() {
local counter=0
while [[ ! -S "$mpv_socket" ]] && [[ $counter -lt $SOCKET_WAIT_TIME ]]; do
sleep 0.2
((counter++))
done
if [[ -S "$mpv_socket" ]]; then
sleep 0.5
# Verify socket is actually responsive
if is_socket_alive; then
log_debug "Socket ready and responsive"
return 0
fi
fi
log_error "Socket creation or responsiveness timeout"
return 1
}
stop_music() {
pkill -x mpv 2>/dev/null || true
sleep 0.3
pkill -9 -x mpv 2>/dev/null || true
sleep 0.3
rm -f "$mpv_socket" 2>/dev/null || true
notification low "⏹️ Music" "Playback stopped"
log_info "Music playback stopped"
}
═══════════════════════════════════════════════════════════════════
🔊 IPC COMMUNICATION
═══════════════════════════════════════════════════════════════════
mpv_ipc_send() {
local cmd="$1"
local timeout="${2:-$IPC_TIMEOUT}"
if ! is_socket_alive; then
log_debug "IPC socket not alive, attempting reconnect..."
return 1
fi
{
echo "$cmd"
sleep 0.1
} | timeout "$timeout" socat - "UNIX-CONNECT:$mpv_socket" 2>/dev/null
local result=$?
if [[ $result -eq 0 ]]; then
log_debug "IPC command succeeded"
else
log_debug "IPC command failed (timeout or socket error)"
fi
return $result
}
mpv_ipc_append() {
local url="$1"
[[ -z "$url" ]] && return 1
url=$(echo "$url" | sed 's/\/\\/g; s/"/\"/g')
local cmd="{"command": ["loadfile", "$url", "append-play"]}"
mpv_ipc_send "$cmd" >/dev/null 2>&1
return $?
}
mpv_ipc_load() {
local url="$1"
[[ -z "$url" ]] && return 1
url=$(echo "$url" | sed 's/\/\\/g; s/"/\"/g')
local cmd="{"command": ["loadfile", "$url", "replace"]}"
mpv_ipc_send "$cmd" >/dev/null 2>&1
return $?
}
get_current_track_info() {
if ! is_socket_alive; then
# Return cached URL if socket dies
if [[ -n "$LAST_SELECTED_URL" ]]; then
echo "$LAST_SELECTED_TITLE|$LAST_SELECTED_URL"
return 0
fi
echo "No track playing|"
return 1
fi
local title_cmd="{"command": ["get_property", "media-title"]}"
local title_resp=$(mpv_ipc_send "$title_cmd" 2>/dev/null)
local title=$(echo "$title_resp" | grep -oP '"data":"?\K[^",}]+' | head -1)
local path_cmd="{"command": ["get_property", "path"]}"
local path_resp=$(mpv_ipc_send "$path_cmd" 2>/dev/null)
local path=$(echo "$path_resp" | grep -oP '"data":"?\K[^",}]+' | head -1)
if [[ -n "$title" && "$title" != "null" ]]; then
# Cache it
cache_selection "$title" "$path"
echo "$title|$path"
else
# Return fallback
if [[ -n "$LAST_SELECTED_URL" ]]; then
echo "$LAST_SELECTED_TITLE|$LAST_SELECTED_URL"
else
echo "Unknown track|"
fi
fi
}
get_playlist_length() {
if ! is_socket_alive; then
echo 0
return 1
fi
local cmd="{"command": ["get_property", "playlist-count"]}"
local resp=$(mpv_ipc_send "$cmd" 2>/dev/null)
echo "$resp" | grep -oP '"data":\K[0-9]+' | head -1 || echo 0
}
get_current_position() {
if ! is_socket_alive; then
echo "0/0"
return 1
fi
local pos_cmd="{"command": ["get_property", "playlist-pos"]}"
local len_cmd="{"command": ["get_property", "playlist-count"]}"
local pos=$(mpv_ipc_send "$pos_cmd" 2>/dev/null | grep -oP '"data":\K[0-9]+' | head -1)
local len=$(mpv_ipc_send "$len_cmd" 2>/dev/null | grep -oP '"data":\K[0-9]+' | head -1)
pos=${pos:-0}
len=${len:-0}
echo "$((pos + 1))/${len}"
}
get_playback_time() {
if ! is_socket_alive; then
echo "0:00|0:00"
return 1
fi
local time_cmd="{"command": ["get_property", "playback-time"]}"
local duration_cmd="{"command": ["get_property", "duration"]}"
local time_resp=$(mpv_ipc_send "$time_cmd" 2>/dev/null)
local duration_resp=$(mpv_ipc_send "$duration_cmd" 2>/dev/null)
local time_sec=$(echo "$time_resp" | grep -oP '"data":\K[0-9.]+' | cut -d. -f1 | head -1)
local duration_sec=$(echo "$duration_resp" | grep -oP '"data":\K[0-9.]+' | cut -d. -f1 | head -1)
time_sec=${time_sec:-0}
duration_sec=${duration_sec:-0}
local time_fmt=$(printf "%d:%02d"$((time_sec / 60)) $ ((time_sec % 60)))$((duration_sec / 60)) $ ((duration_sec % 60)))
local duration_fmt=$(printf "%d:%02d"
echo "$time_fmt|$duration_fmt"
}
set_volume() {
local volume="$1"
[[ $volume -lt 0 ]] && volume=0
[[ $volume -gt 100 ]] && volume=100
if ! is_socket_alive; then
VOLUME_LEVEL=$volume
return 1
fi
local cmd="{"command": ["set_property", "volume", $volume]}"
mpv_ipc_send "$cmd" >/dev/null 2>&1
VOLUME_LEVEL=$volume
log_debug "Volume set to $volume"
}
toggle_pause() {
if ! is_socket_alive; then
notify_error "Cannot control - IPC socket unavailable"
return 1
fi
local cmd="{"command": ["cycle", "pause"]}"
mpv_ipc_send "$cmd" >/dev/null 2>&1
log_debug "Play/Pause toggled"
sleep 0.3
return 0
}
next_track() {
if ! is_socket_alive; then
notify_error "Cannot skip - IPC socket unavailable"
return 1
fi
local cmd="{"command": ["playlist-next"]}"
mpv_ipc_send "$cmd" >/dev/null 2>&1
log_debug "Next track"
sleep 0.3
return 0
}
previous_track() {
if ! is_socket_alive; then
notify_error "Cannot go back - IPC socket unavailable"
return 1
fi
local cmd="{"command": ["playlist-prev"]}"
mpv_ipc_send "$cmd" >/dev/null 2>&1
log_debug "Previous track"
sleep 0.3
return 0
}
seek_to_position() {
local seconds="$1"
if ! is_socket_alive; then
return 1
fi
local cmd="{"command": ["set_property", "playback-time", $seconds]}"
mpv_ipc_send "$cmd" >/dev/null 2>&1
}
set_repeat_mode() {
local mode="$1"
if ! is_socket_alive; then
return 1
fi
case "$mode" in
none)
local cmd="{"command": ["set_property", "loop-playlist", false]}"
mpv_ipc_send "$cmd" >/dev/null 2>&1
REPEAT_MODE="none"
;;
one)
local cmd="{"command": ["set_property", "loop-file", "inf"]}"
mpv_ipc_send "$cmd" >/dev/null 2>&1
REPEAT_MODE="one"
;;
all)
local cmd="{"command": ["set_property", "loop-playlist", "inf"]}"
mpv_ipc_send "$cmd" >/dev/null 2>&1
REPEAT_MODE="all"
;;
esac
}
═══════════════════════════════════════════════════════════════════
📺 VIEW VIDEO FEATURE
═══════════════════════════════════════════════════════════════════
extract_youtube_url() {
local path="$1"
if [[ "$path" =~ v=([^&]+) ]]; then
echo "https://www.youtube.com/watch?v=${BASH_REMATCH}"
elif [[ "$path" =~ youtu.be/([^?&/]+) ]]; then
echo "https://www.youtube.com/watch?v=${BASH_REMATCH}"
elif [[ "$path" =~ youtube.com/watch ]]; then
echo "$path"
elif [[ "$path" =~ youtu.be ]]; then
echo "$path"
else
echo "$path"
fi
}
is_youtube_url() {
local url="$1"
[[ "$url" =~ youtube.com|youtu.be ]]
return $?
}
view_playing_video() {
if [[ -z "$DEFAULT_BROWSER" ]]; then
notify_error "No browser found (Firefox, Chromium, Chrome, or Brave required)"
log_error "No browser detected"
return 1
fi
local track_info=$(get_current_track_info)
local path="${track_info##*|}"
if [[ -z "$path" || "$path" == "No track playing" ]]; then
notify_error "No video currently playing"
return 1
fi
if ! is_youtube_url "$path"; then
# Try cached URL
if is_youtube_url "$LAST_SELECTED_URL"; then
path="$LAST_SELECTED_URL"
else
notify_error "Current track is not from YouTube"
return 1
fi
fi
local video_url=$(extract_youtube_url "$path")
notification normal "🌐 Browser" "Opening video..."
nohup "$DEFAULT_BROWSER" "$video_url" >/dev/null 2>&1 &
log_info "Opened video in browser: $video_url"
sleep 1
notify_success "Video opened in browser"
}
═══════════════════════════════════════════════════════════════════
📹 DOWNLOAD VIDEO FEATURE
═══════════════════════════════════════════════════════════════════
select_download_quality() {
local quality=$(printf "🎬 Best Quality (auto)\n1080p Full HD\n720p HD\n480p SD\n360p Mobile\n◄ Cancel" |
rofi -i -dmenu -p "📹 Select Video Quality" -config "$rofi_theme_menu")
case "$quality" in
"🎬 Best Quality (auto)")
echo "best"
;;
"1080p Full HD")
echo "bestvideo[height<=1080]+bestaudio/best"
;;
"720p HD")
echo "bestvideo[height<=720]+bestaudio/best"
;;
"480p SD")
echo "bestvideo[height<=480]+bestaudio/best"
;;
"360p Mobile")
echo "bestvideo[height<=360]+bestaudio/best"
;;
*)
echo ""
;;
esac
}
download_video() {
local title="$1"
local url="$2"
if ! is_youtube_url "$url"; then
notify_error "Only YouTube videos can be downloaded"
return 1
fi
local quality=$(select_download_quality)
[[ -z "$quality" ]] && return 1
notification normal "⬇️ Download" "Starting: $title"
log_info "Download started - Title: $title | URL: $url | Quality: $quality"
(
local start_time=$(date +%s)
cd "$video_downloads_dir" || return 1
) &
}
═══════════════════════════════════════════════════════════════════
🎵 AUDIO DOWNLOAD
═══════════════════════════════════════════════════════════════════
download_track() {
local title="$1"
local url="$2"
notification normal "⬇️ Download" "Starting: $title (Audio)"
log_info "Audio download started - Title: $title"
(
local start_time=$(date +%s)
cd "$downloads_dir" || return 1
) &
}
═══════════════════════════════════════════════════════════════════
📜 HISTORY MANAGEMENT
═══════════════════════════════════════════════════════════════════
add_to_history() {
local title="$1"
local url="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M')
title=$(echo "$title" | sed 's/|/║/g')
if [[ -f "$history_file" ]]; then
grep -v "^${title}|" "$history_file" > "${history_file}.tmp" 2>/dev/null || true
mv "${history_file}.tmp" "$history_file" 2>/dev/null || true
fi
echo "$title|$url|$timestamp" >> "$history_file"
tail -n "$MAX_HISTORY_ENTRIES" "$history_file" > "${history_file}.tmp"
mv "${history_file}.tmp" "$history_file"
log_info "Added to history: $title"
}
clear_history() {
local response=$(printf "Yes\nNo" | rofi -i -dmenu -p "🗑️ Clear History?" -config "$rofi_theme_menu")
if [[ "$response" == "Yes" ]]; then
rm -f "$history_file"
notification normal "📜 History" "Cleared successfully"
log_info "History cleared"
fi
}
show_history() {
[[ ! -f "$history_file" ]] && { notification low "📜 History" "No history found"; return; }
while true; do
local entries=()
local urls=()
done
}
═══════════════════════════════════════════════════════════════════
⭐ FAVORITES MANAGEMENT (USE CACHED URL)
═══════════════════════════════════════════════════════════════════
add_to_favorites() {
local title="$1"
local url="$2"
If URL is empty, try cached URL
if [[ -z "$url" ]] && [[ -n "$LAST_SELECTED_URL" ]]; then
url="$LAST_SELECTED_URL"
title="${title:-$LAST_SELECTED_TITLE}"
fi
Validate it's YouTube
if ! is_youtube_url "$url"; then
notify_error "Only YouTube tracks can be favorited"
return 1
fi
title=$(echo "$title" | sed 's/|/║/g')
if [[ -f "$favorites_file" ]] && grep -q "^${title}|" "$favorites_file"; then
notification low "⭐ Favorites" "Already in favorites"
return 0
fi
echo "$title|$url" >> "$favorites_file"
notification normal "⭐ Favorites" "Added successfully: $title"
log_info "Added to favorites: $title | $url"
}
remove_from_favorites() {
local title="$1"
title=$(echo "$title" | sed 's/|/║/g')
if [[ -f "$favorites_file" ]]; then
grep -v "^${title}|" "$favorites_file" > "${favorites_file}.tmp"
mv "${favorites_file}.tmp" "$favorites_file"
notification normal "⭐ Favorites" "Removed from favorites"
log_info "Removed from favorites: $title"
fi
}
show_favorites() {
[[ ! -f "$favorites_file" ]] && { notification low "⭐ Favorites" "No favorites found"; return; }
while true; do
local entries=()
local urls=()
done
}
═══════════════════════════════════════════════════════════════════
📋 QUEUE MANAGEMENT
═══════════════════════════════════════════════════════════════════
add_to_queue() {
local title="$1"
local url="$2"
local queue_size=$(wc -l < "$queue_file" 2>/dev/null || echo 0)
if [[ $queue_size -ge $MAX_QUEUE_SIZE ]]; then
notify_error "Queue is full (max $MAX_QUEUE_SIZE tracks)"
return 1
fi
title=$(echo "$title" | sed 's/|/║/g')
echo "$title|$url" >> "$queue_file"
notification normal "📋 Queue" "Added: $title"
log_info "Added to queue: $title"
}
show_queue() {
[[ ! -f "$queue_file" ]] || [[ ! -s "$queue_file" ]] && { notification low "📋 Queue" "Queue is empty"; return; }
while true; do
local entries=()
local urls=()
local line_num=1
done
}
play_from_queue() {
[[ ! -f "$queue_file" ]] || [[ ! -s "$queue_file" ]] && { notify_error "Queue is empty"; return; }
local first_entry=$(head -n 1 "$queue_file")
IFS='|' read -r title url <<< "$first_entry"
title=$(echo "$title" | sed 's/║/|/g')
tail -n +2 "$queue_file" > "${queue_file}.tmp"
mv "${queue_file}.tmp" "$queue_file"
if music_playing && mpv_ipc_append "$url"; then▶️ Playing" "From queue: $title"
notification normal "
else
ensure_socket_clean
stop_music
notification normal "🎵 Playing" "$title"
cache_selection "$title" "$url"
mpv --profile=audio --vid=no "$url" --input-ipc-server="$mpv_socket" >/dev/null 2>&1 &
wait_for_socket
start_endfile_watcher
fi
}
═══════════════════════════════════════════════════════════════════
🎵 NOW PLAYING INTERFACE
═══════════════════════════════════════════════════════════════════
show_now_playing() {
while true; do
if ! music_playing; then
notification low "🎵 Now Playing" "No music playing"
return
fi
done
}
show_playlist_info() {
if ! is_socket_alive; then
notification low "📊 Playlist" "No playlist available"
return
fi
local len=$(get_playlist_length)
local pos=$(get_current_position)
local time_info=$(get_playback_time)
local repeat_mode_display="$REPEAT_MODE"
local volume_display="$VOLUME_LEVEL%"
local info="Playlist Length: $len tracks\nCurrent Position:$pos\nPlayback Time: $ {time_info%%|} / ${time_info##|}\nRepeat Mode: $repeat_mode_display\nVolume: $volume_display\nAutoplay: $ ([ $AUTOPLAY -eq 1 ] && echo 'Enabled' || echo 'Disabled')"
echo -e "$info" | rofi -i -dmenu -p "📊 Playlist Info" -config "$rofi_theme_menu" >/dev/null
}
show_keyboard_shortcuts() {
local shortcuts="🎵 RofiBEATS - Keyboard Shortcuts
⚡ Main Features:
🔍 Search YouTube → Find and play songs
🎵 Now Playing → Control playback
⭐ Favorites → Save your loved tracks
📜 History → Browse recently played
📋 Queue → Manage playlist
🌐 Online Stations → Stream radio
🎧 Local Music → Play local files
🔀 Shuffle → Random playback
⬇️ Downloads → View downloaded files
🎮 Now Playing Controls:
▶️ Play/Pause → Resume or pause
⏭️ Next → Skip to next track
⏮️ Previous → Go back
🔊 Volume → Adjust 0-100%
📊 Seek → Jump to specific time
📹 Video Features (YouTube only):
👁️ View Video → Open in browser
📹 Download Video → Save full video
⬇️ Download Audio → Save as MP3
⚙️ Settings:
🔁 Autoplay → Auto-play next track
🗑️ Clear → History/Queue/Cache
📝 Logs → View debug information"
echo -e "$shortcuts" | rofi -i -dmenu -p "📋 Shortcuts" -config "$rofi_theme_menu" >/dev/null
}
═══════════════════════════════════════════════════════════════════
🌐 ONLINE STREAMING
═══════════════════════════════════════════════════════════════════
play_online_music() {
while true; do
local choice=$(printf "%s\n" "${!online_music[@]}" "◄ Back to Main Menu" | sort | rofi -i -dmenu -p "🌐 Online Stations" -config "$rofi_theme")
[[ -z "$choice" || "$choice" == "◄ Back to Main Menu" ]] && return
done
}
═══════════════════════════════════════════════════════════════════
📚 LOCAL MUSIC
═══════════════════════════════════════════════════════════════════
populate_local_music() {
local -a local_music_temp
mapfile -t local_music_temp < <(find -L "$mDIR" -maxdepth 3 -type f
( -iname ".mp3" -o -iname ".flac" -o -iname ".wav" -o -iname ".ogg" -o -iname "*.m4a" ) 2>/dev/null | sort)
CURRENT_PLAYLIST=("${local_music_temp[@]}")
log_info "Populated ${#CURRENT_PLAYLIST[@]} local tracks"
}
play_local_music() {
populate_local_music
[[ ${#CURRENT_PLAYLIST[@]} -eq 0 ]] && { notify_error "No music files found in $mDIR"; return; }
local -a display_names
for file in "${CURRENT_PLAYLIST[@]}"; do
display_names+=("${file##*/}")
done
local choice=$(printf "%s\n" "${display_names[@]}" | rofi -i -dmenu -p "🎧 Local Music (${#display_names[@]})" -config "$rofi_theme")
[[ -z "$choice" ]] && return
local idx=0
for ((i=0; i<${#display_names[@]}; i++)); do
if [[ "${display_names[$i]}" == "$choice" ]]; then
idx=$i
break
fi
done
ensure_socket_clean
stop_music
notification normal "🎵 Playing" "${choice%.}"
cache_selection "${choice%.}" "${CURRENT_PLAYLIST[$idx]}"
mpv --profile=audio --vid=no --playlist-start="$idx" --loop-playlist
"${CURRENT_PLAYLIST[@]}" --input-ipc-server="$mpv_socket" >/dev/null 2>&1 &
wait_for_socket
start_endfile_watcher
}
shuffle_local_music() {
populate_local_music
[[ ${#CURRENT_PLAYLIST[@]} -eq 0 ]] && { notify_error "No music files found in $mDIR"; return; }
ensure_socket_clean
stop_music
notification normal "🎵 Playing" "🔀 Shuffle - ${#CURRENT_PLAYLIST[@]} tracks"
{
printf "%s\n" "${CURRENT_PLAYLIST[@]}" | shuf | mpv --profile=audio --vid=no
--shuffle --loop-playlist --playlist=- --input-ipc-server="$mpv_socket"
} >/dev/null 2>&1 &
wait_for_socket
start_endfile_watcher
}
═══════════════════════════════════════════════════════════════════
🔍 YOUTUBE SEARCH & PLAYBACK
═══════════════════════════════════════════════════════════════════
search_youtube_safe() {
local query="$1"
local cache_file="${cache_dir}/yt_$(echo "$query" | md5sum | cut -d' ' -f1).txt"
if [[ -f "$cache_file" ]] && [[ $(find "$cache_file" -mmin -60 2>/dev/null) ]]; then
mapfile -t results < "$cache_file"
else
notification normal "🔎 Searching" "YouTube for: $query"
fi
return 0
}
search_and_play_youtube() {
local query=$(rofi -dmenu -p "🔍 Search YouTube" -config "$rofi_theme_menu")
[[ -z "$query" ]] && return
if ! search_youtube_safe "$query"; then
return
fi
local -a results
local cache_file="${cache_dir}/yt_$(echo "$query" | md5sum | cut -d' ' -f1).txt"
mapfile -t results < "$cache_file"
while true; do
local -a titles
local -a display_lines
done
}
═══════════════════════════════════════════════════════════════════
🎬 AUTOPLAY & WATCHER
═══════════════════════════════════════════════════════════════════
start_endfile_watcher() {
pgrep -f "endfile_watcher.*$$" >/dev/null && return 0
(
if ! wait_for_socket; then
log_error "Socket never appeared for end-file watcher"
return 1
fi
) >/dev/null 2>&1 &
}
═══════════════════════════════════════════════════════════════════
📁 DOWNLOADED FILES MANAGEMENT
═══════════════════════════════════════════════════════════════════
list_downloads() {
while true; do
local -a audio_files
local -a video_files
mapfile -t audio_files < <(find "$downloads_dir" -maxdepth 1 -type f ( -name ".mp3" -o -name ".m4a" -o -name ".flac" ) 2>/dev/null | sort)
mapfile -t video_files < <(find "$video_downloads_dir" -maxdepth 1 -type f ( -name ".mp4" -o -name ".mkv" -o -name ".webm" ) 2>/dev/null | sort)
done
}
═══════════════════════════════════════════════════════════════════
⚙️ SETTINGS
═══════════════════════════════════════════════════════════════════
show_settings() {
while true; do
local autoplay_status="$([ $AUTOPLAY -eq 1 ] && echo '✅ On' || echo '❌ Off')"
Advanced Music Player for Rofi
✨ Features:
✓ YouTube & Online Streaming
✓ Local Music Library
✓ Favorites & History
✓ Queue Management
✓ Video Download Support
✓ Audio Download (MP3)
✓ Browser Integration
✓ Seek Control
✓ Quality Selection
📍 Directories:
Music: $mDIR
Videos: $video_downloads_dir
Cache: $cache_dir"
echo -e "$about_info" | rofi -i -dmenu -p "ℹ️ About" -config "$rofi_theme_menu" >/dev/null
;;
esac
done
}
═══════════════════════════════════════════════════════════════════
🎼 MAIN MENU & LOOP
═══════════════════════════════════════════════════════════════════
main_menu() {
while true; do
local music_status=""
if music_playing; then
local track_info=$(get_current_track_info)
local current_title="${track_info%%|*}"
current_title="${current_title:0:25}"
[[ ${#current_title} -gt 25 ]] && current_title="${current_title}..."
music_status="🎵 $current_title"
else
music_status="🎵 Not Playing"
fi
done
}
═══════════════════════════════════════════════════════════════════
🚀 STARTUP & INITIALIZATION
═══════════════════════════════════════════════════════════════════
initialize_system() {
log_info "RofiBEATS v$SCRIPT_VERSION started"
local required_tools=(rofi mpv yt-dlp socat notify-send)
for tool in "${required_tools[@]}"; do
if ! command -v "$tool" &>/dev/null; then
notify_error "Missing dependency: $tool"
log_error "Missing dependency: $tool"
exit 1
fi
done
Load cached URL/title
load_cached_url
if [[ ! -f "$config_file" ]]; then
cat > "$config_file" << 'EOF'
AUTOPLAY=1
SHUFFLE_ENABLED=0
REPEAT_MODE="none"
VOLUME_LEVEL=100
DOWNLOAD_QUALITY="best"
EOF
fi
source "$config_file" 2>/dev/null || true
log_info "System initialized successfully"
log_info "Browser detected: ${DEFAULT_BROWSER:-None}"
}
═══════════════════════════════════════════════════════════════════
⏁ ENTRY POINT
═══════════════════════════════════════════════════════════════════
if [[ "${BASH_SOURCE}" == "${0}" ]]; then
initialize_system
main_menu
fi
End of RofiBEATS v3.6.0
Beta Was this translation helpful? Give feedback.
All reactions