SD card / MP3 management tool


#1

Hallo,

während ich auf alle bestellten Teile für meinen ersten TonUINO warte habe ich mir immer wieder die Frage gestellt: Wie macht ihr das eigentlich mit dem “Betanken” und dem Verwalten des Inhaltes Eurer SD-Karte(n)?

Vielleicht habe ich es bisher überlesen, aber die Vorstellung, Ordner und Dateien von Hand anzulegen und umzubenennen finde ich persönlich wenig praktikabel… :wink:

Deshalb habe ich die Wartezeit genutzt, um ein kleines Shell Script zu kreieren und hoffe, es findet Anwendung.

Bitte gebt gerne Feedback und/oder Verbesserungsvorschläge :+1:

Michi

#!/bin/bash
##############################################################################
#
# DESC: manage TonUINO sdcard
#
# INFO: https://www.voss.earth/tonuino/, Struktur auf der SD-Karte
#
# DEPS: depends on id3v2 tool/command
#
# VERS: v1.0 - 20190111 - Michi
#
##############################################################################

# check dependencies
[ ! -x id3v2 ] || { echo "ERROR: command \"id3v2\" is not not found, please install dependency!" >&2; exit 1; }

# print out help/usage
function usage {
  echo "Usage: $(basename "$0") [-h] || [-v] -l <path to sdcard> || [-v] [-n] -c <music folder> <path to sdcard>" >&2
  echo -e "\t-h|--help      show help/usage" >&2
  echo -e "\t-v|--verbose   show detailed information about executed commands" >&2
  echo -e "\t-l|--list      list content of sdcard based on ID3v2 tags" >&2
  echo -e "\t-c|--copy      copy content of music folder to sdcard, reflecting folder structure and naming convention" >&2
  echo -e "\t-n|--num       enumerate files mannually (e.g. in case of missing ID3v2 tags or mixed files" >&2
  exit 1
}

# copy content of music folder to sdcard, reflecting folder structure and naming convention
function copy {
  # validate source (directory, containing MP3 files) and destination (sdcard mount point)
  [ ! -d "$SRC" ] || [ -z "$(find "$SRC" -type f -iname \*.mp3 2>/dev/null)" ] && { echo "ERROR: please specify a valid path to your MP3 files!" >&2; exit 1; }
  [ ! -d "$DST" ] && { echo "ERROR: please specify a valid path to your sdcard!" >&2; exit 1; }

  # determine next "free" directory on sdcard and try to create it
  DN="$(printf "%02d" "$(find "$DST" -type d -name "[0-9][0-9]" 2>/dev/null | wc -l)")"
  mkdir "$DST"/"$DN" >/dev/null 2>&1 || { echo "ERROR: can't create directory $DST/$DN" >&2; exit 1; }

  # variable used for manual enumeration of files or files without ID3 tags
  CNT=1

  # loop over all MP3 files, convert filenames based on ID3 tag track number (if exists) or dedicated counter and copy to sdcard
  find "$SRC" -type f -iname \*.mp3 -print0 | while read -d $'\0' -r FILE ; do
    # try to get a valid track number and eliminate left hand zeros
    # https://stackoverflow.com/questions/8078167/bizarre-issue-with-printf-in-bash-script09-and-08-are-invalid-numbers-07/11804275
    FN="$(printf "%03d" "$(( 10#$(id3v2 -l "$FILE" | LC_ALL=C sed -ne '/TRCK/s/.*:[^0-9]*\([0-9]*\).*/\1/p') ))").mp3"
    # use dedicated counter for filenames if the above command didn't succeed or manual enumeration has been forced (-n)
    [[ $FN == "000.mp3" ]] || [[ -n $ENUM ]] && FN="$(printf "%03d" $CNT).mp3"
    # be verbose
    [[ -n $VERBOSE ]] && echo -n "cp $FILE $DST/$DN/$FN ... "
    # try to copy file
    cp "$FILE" "$DST"/"$DN"/"$FN" >/dev/null 2>&1 || { echo "ERROR: can't copy file $FILE to $DST/$DN/$FN" >&2; exit 1; }
    # be verbose
    [[ -n $VERBOSE ]] && echo "DONE"
    # increase dediacted counter
    ((CNT++))
  done

  exit 0
}

# copy content of music folder to sdcard, reflecting folder structure and naming convention
function list {
  # validate source (sdcard mount point, containing MP3 files)
  [ ! -d "$SRC" ] || [ -z "$(find "$SRC" -type f -iname \*.mp3 2>/dev/null)" ] && { echo "ERROR: please specify a valid path to your sdcard!" >&2; exit 1; }

  # loop over all sdcard folders
  for DIR in $(find "$SRC" -type d -name "[0-9][0-9]" | sort -n); do
    # in verbose mode print detailed information for each file (artists, album, title if ID3 tags are available)
    # or a summary per folder (artist, album if ID3 tags are available) otherwise
                if [[ -n $VERBOSE ]]; then
      # print directory name
                        echo "$DIR"
      # loop over MP3 files
                        for FILE in "$DIR"/*.mp3; do
                                echo -ne "\t$(basename "$FILE") => "
        # determine ID3 information and ensure output is ordered accordingly: artist, album, title
        # ordering: prepend a number per tag, sort nummerically, remove prefixed numbers
        # print it out finally
        id3v2 -l "$FILE" | LC_ALL=C sed -ne '/TPE1/s/.*: \(.*\)/1Artist: \1/p;/TALB/s/.*: \(.*\)/2, Album: \1/p;/TIT2/s/.*: \(.*\)/3, Title: \1/p;s/.*\(No ID3.* tag\)/3\1/p' | \
          LC_ALL=C sort -n | LC_ALL=C cut -c2- | LC_ALL=C tr -d '\n'
        # line break after each file
        echo
                        done
      # line break after each folder
                        echo
                else
      # initialize array for ID3 information
      ID3=()
      # determine ID3 information for all MP3 files in the directory at once and ensure output is ordered accordingly: artist, album
      # ordering: prepend a number per tag, sort nummerically, keep uniqe information only, remove prefixed numbers
      # store the result in an array
      while IFS=$'\n' read -r LINE; do
        ID3+=("$LINE")
      done < <(id3v2 -l "$DIR"/*.mp3 | LC_ALL=C sed -ne '/TPE1/s/.*: \(.*\)/1Artist: \1/p;/TALB/s/.*: \(.*\)/2Album: \1/p;s/.*\(No ID3.* tag\)/3\1/p' | \
        LC_ALL=C sort -n | LC_ALL=C uniq | LC_ALL=C cut -c2-)
      # if array holds exactly 2 elements, it has to be artist and album information, so print it out
      # if array holds more than 2 elements, the directory contains songs of various artists and/or albums
      # otherwise no ID3 tags were found
      if [[ ${#ID3[*]} -eq "2" ]]; then
                                echo "$DIR: ${ID3[0]}, ${ID3[1]}"
                        elif [[ ${#ID3[*]} -gt "2" ]]; then
                                echo "$DIR: various artists/albums"
                        else
                                echo "$DIR: no ID3 tags available"
                        fi
                fi
  done

  exit 0
}

# evaluate arguments provided on the command line
while [ "$#" -gt 0 ]; do
  case "$1" in
    -v|--verbose) VERBOSE=1;;
    -n|--num) ENUM=1;;
    -l|--list) CMD="list"; SRC="$2"; shift;;
    -c|--copy) CMD="copy"; SRC="$2"; DST="$3"; shift 2;;
    -h|*) usage;;
  esac
  shift
done

# MAIN - this is where the real work starts...
[[ $CMD == "list" ]] && list
[[ $CMD == "copy" ]] && copy

# print usage instruction if script has been called without any arguments
usage

Nützliche Programme unter Linux
#2

Super Idee! Danke fürs teilen. Kannst du vielleicht noch etwas beschreiben was das Tool genau macht und wie man es anwendet? Beim überfliegen habe ich gelesen, daß IDv3 tags ausgewertet werden - sind diese zwingend Voraussetzung?


#3

Wow, sieht super aus. Werd ich definitiv mal ausprobieren.
Bisher hat es bei mir gereicht die Ordner mit for zu durchlaufen und einen counter mitlaufen zu lassen. Bei find hatte ich z.T. Probleme mit der Reihenfolge der Dateien.


#4

Es gab dieses Thema schonmal:


#5

Oje, warum bin ich da nicht früher drauf gekommen… :thinking: Danke für den Hinweis!


#6

Ich hoffe, der nachfolgende output bringt etwas Licht ins Dunkel… ID3 Tags sind nicht zwingend notwendig, aber durchaus sinnvoll. Das war auch einer der Gründe für die Entwicklung des Scriptes. Ich finde es gut, wenn ich später mal schnell/einfach sehe, was auf der Karte wo abgelegt ist. Mann/Frau könnte das Script im nächsten Schritt entsprechend erweitern, um alte Inhalte zu löschen/ersetzen und die Struktur dann zu reorganisieren.

Ohne ID3 Tags werden die Dateien auf Basis des Schleifenzählers durch nummeriert. Dies kann auch mit dem Parameter -n forciert werden, falls man einen Ordner mit gemischten Inhalten (mit und ohne Tags) hat. Denn sonst werden ggf. schon kopierte Dateien überschrieben, wenn deren Name aus einem vorhandenem Tag übernommen wurde und gerade zufällig dem Schleifenzähler gleicht der für eine Datei ohne Tag benutzt würde. Der Zähler selbst läuft stur hoch, während die vom find Kommando gelieferten Dateien in nicht sortierter Reihenfolge abgearbeitet werden. Eine Sortierung auf Basis von Dateinamen erschien mir an dieser Stelle nicht sinnvoll, da jeder Benutzer / jedes Programm die Namen beliebig vergeben kann.

Ist nicht optimal gelöst, zumindest wenn die Reihenfolge wichtig ist, z.B. bei Hörbüchern. Deshalb die klare Tendenz zu den ID3 Tags.

# Hilfe zum Script
$ tonuino-sdcard
Usage: tonuino-sdcard [-h] || [-v] -l <path to sdcard> || [-v] [-n] -c <music folder> <path to sdcard>
	-h|--help      show help/usage
	-v|--verbose   show detailed information about executed commands
	-l|--list      list content of sdcard based on ID3v2 tags
	-c|--copy      copy content of music folder to sdcard, reflecting folder structure and naming convention
	-n|--num       enumerate files mannually (e.g. in case of missing ID3v2 tags or mixed files

# Dateien aus Music/tmp, ... auf die SD-Karte übertragen, -v ist etwas geschwätziger
$ tonuino-sdcard -c Music/tmp /tmp/sdcard
$ tonuino-sdcard -c Music/Die\ Fantastischen\ Vier/Captain\ Fantastic /tmp/sdcard
$ tonuino-sdcard -c Music/Judith\ Holofernes/Ein\ leichtes\ Schwert /tmp/sdcard -v
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_12_-_John_Irving.mp3 /tmp/sdcard/02/012.mp3 ... DONE
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_09_-_Hasenherz_.mp3 /tmp/sdcard/02/009.mp3 ... DONE
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_06_-_Opossum.mp3 /tmp/sdcard/02/006.mp3 ... DONE
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_11_-_Havarie.mp3 /tmp/sdcard/02/011.mp3 ... DONE
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_07_-_M.I.L.F.mp3 /tmp/sdcard/02/007.mp3 ... DONE
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_02_-_Pechmarie.mp3 /tmp/sdcard/02/002.mp3 ... DONE
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_10_-_Platz_da.mp3 /tmp/sdcard/02/010.mp3 ... DONE
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_05_-_Liebe_Teil_2_-_Jetzt_erst_recht_.mp3 /tmp/sdcard/02/005.mp3 ... DONE
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_03_-_Ein_leichtes_Schwert__.mp3 /tmp/sdcard/02/003.mp3 ... DONE
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_04_-_Danke,_ich_hab_schon.mp3 /tmp/sdcard/02/004.mp3 ... DONE
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_01_-_Nichtsnutz.mp3 /tmp/sdcard/02/001.mp3 ... DONE
cp Music/Judith Holofernes/Ein leichtes Schwert/Judith_Holofernes_-_Ein_leichtes_Schwert_-_08_-_Brennende_Brücken.mp3 /tmp/sdcard/02/008.mp3 ... DONE

# Struktur auf der Karte nach obigen Aktionen
$ find /tmp/sdcard
/tmp/sdcard
/tmp/sdcard/02
/tmp/sdcard/02/008.mp3
/tmp/sdcard/02/009.mp3
/tmp/sdcard/02/001.mp3
/tmp/sdcard/02/002.mp3
/tmp/sdcard/02/003.mp3
/tmp/sdcard/02/007.mp3
/tmp/sdcard/02/012.mp3
/tmp/sdcard/02/006.mp3
/tmp/sdcard/02/010.mp3
/tmp/sdcard/02/004.mp3
/tmp/sdcard/02/005.mp3
/tmp/sdcard/02/011.mp3
/tmp/sdcard/00
/tmp/sdcard/00/001.mp3
/tmp/sdcard/00/002.mp3
/tmp/sdcard/00/003.mp3
/tmp/sdcard/00/004.mp3
/tmp/sdcard/00/005.mp3
/tmp/sdcard/01
/tmp/sdcard/01/008.mp3
/tmp/sdcard/01/009.mp3
/tmp/sdcard/01/015.mp3
/tmp/sdcard/01/001.mp3
/tmp/sdcard/01/014.mp3
/tmp/sdcard/01/002.mp3
/tmp/sdcard/01/016.mp3
/tmp/sdcard/01/003.mp3
/tmp/sdcard/01/007.mp3
/tmp/sdcard/01/013.mp3
/tmp/sdcard/01/012.mp3
/tmp/sdcard/01/006.mp3
/tmp/sdcard/01/010.mp3
/tmp/sdcard/01/004.mp3
/tmp/sdcard/01/005.mp3
/tmp/sdcard/01/011.mp3

# Zusammenfassung des Karteninhaltes unter Verwendung von ID3 tags auflisten (falls verfügbar)
$ tonuino-sdcard -l /tmp/sdcard
/tmp/sdcard/00: various artists/albums
/tmp/sdcard/01: various artists/albums
/tmp/sdcard/02: Artist: Judith Holofernes, Album: Ein leichtes Schwert

# Details zum Karteninhalt unter Verwendung von ID3 tags auflisten (falls verfügbar)
$ tonuino-sdcard -l /tmp/sdcard -v
/tmp/sdcard/00
	001.mp3 => No ID3 tag
	002.mp3 => No ID3 tag
	003.mp3 => Artist: Ennio Morricone, Album: Il Etait une Fois dans L'Ouest, Title: The Man with the Harmonica
	004.mp3 => No ID3 tag
	005.mp3 => No ID3 tag

/tmp/sdcard/01
	001.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Captain Fantastic
	002.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Tunnel
	003.mp3 => Artist: Die Fantastischen Vier feat. Clueso, Album: Captain Fantastic, Title: Zusammen
	004.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Fantanamera
	005.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Moduland.Y
	006.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Hitisn
	007.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Watchmen
	008.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Alle (Skit)
	009.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Endzeitstimmung
	010.mp3 => Artist: Die Fantastischen Vier feat. Flo Mega, Album: Captain Fantastic, Title: Hot
	011.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Henson J.J. Barkley (Skit)
	012.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Aller Anfang ist Yeah
	013.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Das ist mein Ding
	014.mp3 => Artist: Die Fantastischen Vier, Album: Captain Fantastic, Title: Affen mit Waffen
	015.mp3 => Artist: Die Fantastischen Vier feat. Tom Gaebel, Album: Captain Fantastic, Title: Hitisn Reprise
	016.mp3 => Artist: Die Fantastischen Vier feat. Damion, Album: Captain Fantastic, Title: Weitermachen

/tmp/sdcard/02
	001.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: Nichtsnutz
	002.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: Pechmarie
	003.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: Ein leichtes Schwert
	004.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: Danke, ich hab schon
	005.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: Liebe Teil 2 - Jetzt erst recht
	006.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: Opossum
	007.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: M.I.L.F
	008.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: Brennende Br�cken
	009.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: Hasenherz
	010.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: Platz da
	011.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: Havarie
	012.mp3 => Artist: Judith Holofernes, Album: Ein leichtes Schwert, Title: John Irving

#7

Am besten würde man mal 'ne GUI bauen um die SD-Karte zu verwalten :wink:

Aber das Script ist schon echt super und sinnvoll.


#8

Hallo Zusammen,

ich habe gleichzeitig ein ähnliches Script entwickelt (hab @Michi 's Skript nicht gesehen). Allerdings in Python. Das ist hier zu finden:

Es verwendet allerdings (noch) keine ID3-Tags.


#9

Mittlerweile sind die wichtigsten Teile angekommen und mein erster TonUINO läuft auf dem Steckbrett… :+1:

Deshalb mal ein Update zum Script (jetzt auch auf GitHub):