Music Fetch Tool

Presenting my two scripts to manage my music collection.

I used to run cmus in my terminal as music player, but after a while it started to get really slow when I got lot of musics.
So I decided to try something else, that’s how I found ncmpcpp.
It is a music player that is a client of the MPD (Music Player Daemon) server.

I have a very basic system to handle my music which is a simple script that allows me to:

My server is more like a backup than something else here.

Here is the bash script I made before:

#!/bin/bash

dir="$HOME/Music/mine"
srv="homelab:/DATA/Media/Musics"
txtgrn='\e[0;32m' # Green
txtwht='\e[0;37m' # White

add() {
  [ "$1" = "" ] && echo "no url given" && exit 1
  url="$1"
  echo -e "${txtgrn}Downloading" "$txtwht" "$url"
  yt-dlp --extract-audio "$url" 2> /dev/null
  music=$(ls ./*.opus)
  echo -e "${txtgrn}Converting" "$txtwht" "$music"
  ffmpeg -i "$music" "${music%%.opus}.mp3" 2> /dev/null
  echo -e "${txtgrn}Deleting" "$txtwht" "$music"
  rm "$music"
  echo -e "${txtgrn}Moving" "$txtwht" "${music%%.opus}.mp3"
  mv "${music%%.opus}.mp3" "$dir"
}

rem() {
  del="$(find "$dir" -type f | fzf)"
  echo -e "${txtgrn}Deleting" "$txtwht" "$del"
  rm "$del"
}

sync() {
  [ "$1" = "" ] && echo "no action given" && exit 2
  action="$1"
  case "$action" in
    "get") scp -r "$srv/*" "$dir/.." ;;
    "give") scp -r "$dir/" "$srv" ;;
    *) echo "not right action" && exit 3
  esac
}

usage() {
  echo "
usage: ${0##*/} [OPTION]

OPTIONS:
  play            start the music player
  add  [url]      download a music and add it to the library
  rem             remove a music from library
  sync [get/give] synchronize between client and server"
}

[ $# -lt 1 ] && usage && exit 0
case "$1" in
  "play") ncmpcpp ;;
  "add") add "$2" ;;
  "rem") rem ;;
  "sync") sync "$2" ;;
  *) echo "$1: unknown option";;
esac

But after I discovered thor which allows to make nice command line tools easily, I decided to rewrite it in ruby.
Here is how it looks:

#!/bin/ruby

require 'thor'
require 'fileutils'
require 'net/ssh'
require 'net/scp'

MUSICS = File.expand_path "~/Music/Musics"

class Ydl < Thor
  descs = {
    play:   ["play",            "start the music player"],
    search: ["search",          "search for music in the library"],
    add:    ["add  [URL]",      "add music from URL"],
    rem:    ["rem",             "remove music by name using fzf"],
    sync:   ["sync [get/give]", "sync the music with the server"]
  }

  desc(*descs[:play])
  def play
    exec("ncmpcpp")
  end

  desc(*descs[:search])
  def search
    `ls #{MUSICS} | fzf`.chomp
  end

  desc(*descs[:add])
  def add(url)
    `yt-dlp -x --audio-format mp3 #{url}`
    file = `ls *.mp3`.chomp
    mp3 = "#{File.basename(file, File.extname(file))}.mp3"
    FileUtils.mv(mp3, MUSICS)
  rescue => e
    abort "couldn't download the music: #{e}"
  end

  desc(*descs[:rem])
  def rem
    to_remove = `ls #{MUSICS} | fzf`.chomp
    File.delete("#{MUSICS}/#{to_remove}") unless to_remove == ""
  rescue => e
    abort "couldn't remove the file: #{e}"
  end

  desc(*descs[:sync])
  def sync(action)
    abort "sync: missing argument" if action.nil?
    abort "sync #{action}: wrong argument" if !action =~ /get|give/
    Net::SSH.start("homelab") do |ssh|
      ssh.scp.download!("/DATA/Media/Musics", "#{Dir.home}/Music", recursive: true) if action == "get"
      ssh.scp.upload!(MUSICS, "/DATA/Media/", recursive: true) if action == "give"
    end
  rescue => e
    abort "couldn't sync: #{e}"
  end

  def self.exit_on_failure?
    true
  end
end

Ydl.start

It allows me to easily upload and download the new music between my laptop, server and phone.
For those using an iPhone, you can use vlc and iSH. iSH is a small alpine linux shell allowing to run some scripts and commands, I can run my ruby script like this. It requires more work but I can download the music to my iSH container, then using the file manager of iOS move the music to the vlc music folder and it is good to go :)