Zu einem Icecast Server streamen auf der Konsole unter Linux

Irgendwie bin ich in der letzten Zeit durch “Sielenc” auf die Idee gekommen, einen Icecast Server testweise zu betreiben :)

Zum Testen wollte ich natürlich auch meine Musik an den Server senden.

Da ich ja unter Linux arbeite, war die Programmauswahl etwas schwieriger; zum Senden der Daten an den Server benutze ich Ices2. Ausserdem wollte ich das komplett auf der Konsole machen können und eine gute Integration in den Music-On-Console Player haben.

In der Konfiguration von Ices2 stellt man entweder eine Playlist ein oder ein Script, welches genau einen Dateienamen zurückliefert. Ich benutze letzteres und habe mir hierfür ein kleines Hilfs-Script geschrieben.

Aus Sicht von Ices gibt dieses Script jedes mal nur einen Dateinamen aus der aktuellen Wartschlange zurück. Falls die Warteschlange leer ist, wird solange automatisch ein Lied aus der “Notfall-Warteschlange” (manche nennen das AutoDJ) geliefert, bis die Warteschlange wieder was zu bieten hat.

<WRAP center round tip> Mit einem Befehl wie find /Music_Free/Electro/ -iname "*ogg" -exec /bin/feed_ices.sh addtoequeue "{}" \; kann man die eQueue ziemlich einfach mit viel Musik befüllen. </WRAP>

Der größere Teil des Scriptes kümmert sich um das Auffüllen der beiden Warteschlangen (Queues).
Beim Aufruf gibt es folgende Möglichkeiten:

  • showqueue – Zeigt die aktuelle Warteschlange an
  • showequeue – Zeigt die Auto-DJ-Warteschlange an
  • addtoqueue file/dir – fügt die Datei oder das Verzeichnis zur Warteschlange hinzu
  • addtoequeue file/dir – wie oben, aber zur Auto-DJ Warteschlange
  • get – Auf diese Weise ruft Ices2 das Script auf, um eine Datei zu bekommen
  • played – Liefert eine Liste aller bisher gespielter Dateien
  • help – Zeigt eine kurze Hilfe an

Im Script kann man damit sehr einfach eine Datei oder ein ganzes Verzeichnis in die Warteschlange übernehmen. Dabei wird für jede einzelne Datei geprüft, ob sie als Ogg vorliegt und falls nicht, automatisch encodiert (Ices2 mag nur Ogg streamen :)). Sollte die Datei bereits encodiert worden sein (im temporären Feed-Ices Verzeichnis), entfällt das Encodieren.

Es wird auch überprüft, ob die Datei, die aktuell zur Queue hinzugefügt werden soll, mit der letzten auf der Warteschlange übereinstimmt und verweigert dann das Hinzufügen. Dies liegt daran, dass Ices2 sich weigert dieselbe Datei mehrmals hintereinander abzuspielen und sich irgendwann wegen zu vieler Fehler beendet.

Sox macht es richtig und es funktioniert somit auch mit Ices2. Jedoch ist es trotzdem nicht möglich, denselben Song zweimal hintereinander abzuspielen.

Dieses Script ist mal wieder so ein “es reicht mir aus”-Fall, es gibt mit Sicherheit bessere.

Ich werde auch nicht wirklich streamen sondern wollte mich nur mal mit dem Thema auseinandersetzen. Zur Not kann man so mal dem einen oder anderen seine Musik nahe bringen :)

Status

Abgeschlossen

Das Script

Hier die aktuelle Version des Scripts zum Download: feed_ices.zip Oder hier als Quellcode (Bash); wie man sieht sind noch einige Debug Sachen drin …

#!/bin/bash
AUTHOR="Natenom"
VERSION="0.1.5"
LASTEDIT="2010-02-21"

# Ices sends only ogg files; if you give me an NOT-ogg I will encode it.
# Use "$0 help"

FI__IS_RUNNING=/tmp/feed_ices.pid #Create this file while running
FI__QUEUE=/home/ices/queue.list #Ices Queue
FI__EQUEUE=/home/ices/equeue.list #These songs will be played if normal playlist is empty.
FI__PLAYED=/home/ices/played.list #List of played songs taken from any queue.
FI__TMP_DIR=/tmp/feed_ice
#FI__STREAM_CODEC=ogg

NOTIFY="notify-send"
FI__NOTIFY_TIME_MS=5000

#Default Queue is the normal one.
FI__QUEUE_USE="${FI__QUEUE}"

#####################################
#### DO NOT EDIT BELOW THIS LINE ####
#####################################

#If there are not queue files, create them
[ ! -f "${FI__QUEUE}" ] && touch "${FI__QUEUE}"
[ ! -f "${FI__EQUEUE}" ] && touch "${FI__EQUEUE}"
[ ! -f "${FI__PLAYED}" ] && touch "${FI__PLAYED}"

#If Ctrl+C remove pid file.
trap "rm ${FI__IS_RUNNING}" 2 15

__FI__COUNTER=0
while [ -f "${FI__IS_RUNNING}" ]; #remove ! after testing
do
 if [ ${__FI__COUNTER} -gt 5 ];
 then
 echo "Waited 5 times, thats too much, bye"
 exit 15
 else
 echo $0 is already running PID: $(cat "${FI__IS_RUNNING}")
 echo "Sleeping for 2 Seconds ..."
 sleep 2
 fi

 __FI__COUNTER=$((${__FI__COUNTER}+1))
done

#No other script is running
# Write our pid to pid-file.
############################echo $$ > "${FI__IS_RUNNING}" #currently disabled until I find a better way

FI_GIVEN_PATH="${2}"

function popupinfo() {
 if [ "$KDE_FULL_SESSION" == true ]; then
 $NOTIFY -t ${FI__NOTIFY_TIME_MS} "${1}" "${2}"
 else
 #Does not really work <img src="bla.domain.tld/wp-includes/images/smilies/icon_razz.gif" alt=":P" class="wp-smiley">
 $NOTIFY_GNOME -i sound -t ${FI__NOTIFY_TIME_MS} "${1}" "${2}"
 fi
}

function encode_file_to_tmp() {
 #Encodes a File from *.* to *.ogg into $FI__TMP_DIR
 # 1 = SourceFile
 # 2 = DestFile

 [ ! -d "${FI__TMP_DIR}" ] && mkdir -p "${FI__TMP_DIR}" #If temporary directory doesn't exist, create it.

 #ffmpeg here does not work because it uses serial number always 0; second it does not write correct vorbis headers.
 #sox instead does a great job
 #_ENCODE_FILE="${1}"
 clear
 sox -V4 "${1}" "${2}" # & #Do that in background because it needs some time.
 return 0
}

function add_file_to_playlist() {
 #Add a file to playlist; if it is not already .ogg, encode it.

 local _directory="${1}"
 local _file="${2}"
 local _queue_it="1" #0 means to enqueue it <>0 to not enqueue

 #Will contain the suffix of the given file.
 local _in_suffix="0"

 #To get the filetype we first need to get it.
 case $(echo "${_file}" | egrep -o '\.ogg$|\.mp3$|\.flac$|\.wav$') in
 .ogg) #Ogg file
 _in_suffix=.ogg
 _return=0 #if not 0, would not work below
 ;;
 .mp3) #Ogg file
 _in_suffix=.mp3
 ;;
 .flac) #Ogg file
 _in_suffix=.flac
 ;;
 .wav) #Ogg file
 _in_suffix=.flac
 ;;
 *) #anything different
 _in_suffix=UNKNOWN
 ;;
 esac

 #Create the new filename
 local __encode_to_file="${FI__TMP_DIR}/${_file/$_in_suffix/.ogg}"

 #***********************************
 #IFS needed if spaces in files
 IFS="
"
 local _last_on_queue_fullpath=$(tail -n 1 "${FI__QUEUE_USE_NOW}") #Get the last song on queue.
 _last_on_queue_filename=$(basename "${_last_on_queue_fullpath}") #Get only the filename of the last queue entry
 unset IFS
 #***********************************

 #If current file is the same as the last added file, it won't be added because ices2 won't play the same file twice in direct order.
 #Compare always the filename, not the full path.
 if [ "${_file/$_in_suffix/.ogg}" == "${_last_on_queue_filename}" ];
 then
 clear
 echo "ERROR: This is the same file as the last one, ignoring. Ices2 would not play this"
 echo "\"${_file/$_in_suffix/.ogg}\" (you want to add)"
 echo "\"${_last_on_queue_filename}\" (last file in your queue)"
 echo "Press Enter to continue"
 read lala
 rm "${FI__IS_RUNNING}"
 return 0
 else
 #Now we can encode it.
 case "${_in_suffix}" in
 .flac|.mp3|.wav) #If not ogg, we need to encode
 if [ -f "${__encode_to_file}" ];
 then
 clear
 echo "File already exists, no encoding needed ... (May be a previous session)."
 echo "Press enter to enqueue this file"
 read waitforme

 #Without following, you would just get the above message and file would not be added to the queue.
 _queue_it="0"
 else
 encode_file_to_tmp "${_directory}/${_file}" "${__encode_to_file}"
 _queue_it=$?
 fi
 ;;
 .ogg)
 #Is an ogg file, nothing to encode
 _queue_it="0"
 #Encode_to_file must be full path of original file not of /tmp/feed_ice/...
 __encode_to_file="${_directory}/${_file}"

 ;;
 esac

 #If Encoding was correct, or it was ogg ... add to queue.
 [ "${_queue_it}" == "0" ] && echo "${__encode_to_file}" >> "${FI__QUEUE_USE_NOW}" || echo "Error in encoding or not an music file."
 fi
}

function add_directory_to_playlist() {
 #Use add_file_to_playlist to add a complete directory
 local _directory="${1}"
 IFS="
"
 for _filename in $(ls "${_directory}");
 do
 add_file_to_playlist "${_directory}" "${_filename}"
 done
 unset IFS
 return 0
}

function addthis() {
 #Determines if it is a directory or a file and ... .
 # 1 = given path
 local _path="${1}"
 if [ -d "${_path}" ]; then
 #It is not a file but a directory, add some special features
 clear
 echo -e "This is a directory, do you want all content to be added to the playlist?\ny/n"
 read __ask
 [ "${__ask}" == "y" ] && add_directory_to_playlist "${_path}"
 else
 #Is a File, not a directory.
 #Split into dir and filename as add_file_to_playlist() needs it.
 local _dir=$(dirname "${_path}")
 local _file=$(basename "${_path}")
 add_file_to_playlist "${_dir}" "${_file}"
 fi
 return 0
}

function showqueue() {
 #Shows the current queue.
 local _queue="${1}"
 IFS="
"
 clear
 echo "Queue (First will be played first)"
 for i in $(cat "${_queue}")
 do
 #__TITLE=$(ogginfo "${i}" | egrep -i 'TITLE=' | tr -d '\t') #Get title from ogginfo.
 #__ARTIST=$(ogginfo "${i}" | egrep -i 'ARTIST=' | grep -v -i album | tr -d '\t') #Get artist from ogginfo.
 echo "${i}" " - ${__ARTIST} ${__TITLE}" #And show.
 done

 #Show how many files are in the emergency playlist.
 echo "Songs in Emergency Playlist (${FI__EQUEUE}): $(cat "${FI__EQUEUE}" | wc -l)"
 unset IFS
}

case ${1} in
 showqueue) #Show the queue.
 showqueue "${FI__QUEUE}"
 ;;
 showequeue) #Show the queue.
 showqueue "${FI__EQUEUE}"
 ;;
 add|addtoqueue) #Add a file to the queue.
 FI__QUEUE_USE_NOW="${FI__QUEUE}"
 addthis "${FI_GIVEN_PATH}"
 ;;
 addtoequeue) #Add the file to the emergency queue (equeue).
 FI__QUEUE_USE_NOW="${FI__EQUEUE}"
 addthis "${FI_GIVEN_PATH}"
 ;;
addrecursiveogg) #Add only oggfiles recursive from path
addthis "${FI_GIVEN_PATH}"
_ONLYOGG=true
;;
 get) #Get the first file entry from the playlist; if playlist is empty, use the emergency playlist in the same way.
 # If emergency playlist is also empty, ices stops working.
 if [ ! -z "$(cat ${FI__QUEUE})" ];
 then
 #Playlist is not empty.
 #Fetch the first filename from playlist.
 __PLAYLIST="${FI__QUEUE}"
 else
 #Queue is empty; Use Equeue instead.
 popupinfo "" "Queue is empty, using emergency-queue with <b>$(cat "${FI__EQUEUE}" | wc -l)</b> Songs"
 __PLAYLIST="${FI__EQUEUE}"
 fi

 #FIFO
 FI__RETURN_FILE=$(head -n 1 "${__PLAYLIST}")
 #And now delete the first entry from playlist.
 sed -i 1d "${__PLAYLIST}"
 #Write filename to already played filelist.
 echo "${FI__RETURN_FILE}" >> "${FI__PLAYED}"

 #Return filename.
 echo "${FI__RETURN_FILE}"

 __COUNT=$(cat "${__PLAYLIST}" | wc -l)
 popupinfo "Feed-Ices" "Still <b>${__COUNT}</b> titles in the queue"
 ;;
 played) #Shows already played songs
 echo "Playlist (Played from top)"
 cat "${FI__PLAYED}"
 ;;
 help|*)
 cat<<EOF
USAGE
 $0 [ACTION] [FILE]

ACTIONS
 showqueue  Shows current queue.
 showequeue  Shows current emergency queue.
 addtoqueue|add file/directory  Adds the file to the current queue.
 addtoequeue file/directory  Adds the file to the current emergency queue.
 get  Get the next file from queue. If queue is empty, the program returns the first file from emergency queue.
 After that, the first entry in the queue/equeue will be deleted.
 played  Get a list of already played songs.
 help  This help
Have fun
2010 - Natenom
EOF
 ;;
esac

###################################################rm "${FI__IS_RUNNING}"
exit 0
Zuletzt geändert: 2023-01-10 20:08:32 +0100 CET: init