iriverlist versión 0.9

Enviado por SD0625 el 8 Febrero, 2010 - 16:57.

He estado un poco ocioso y me puse a trabajar en un script en bash para crear las listas de reproducción para mi mp4, un iriver E100. Me puse picar código, pues a pesar de que encontré algunas soluciones la verdad nada supera a la herramienta que uno mismo construyó.

Una pequeña introducción. Los reproductores iriver usan un extraño formato (.PLA) para sus listas de reproducción, que hasta lo que tengo entendido solo la usan ellos. El script toma una lista de reproducción en formato m3u y la procesa para convertirla en una lista .pla

Sin más palabras, el script:

#! /bin/bash

################################################################################
# Este script transforma una lista de música de formato m3u a pla. El formato  #
# pla es usado exclusivamente (hasta lo que yo sé) por los reproductores        #
# portátiles fabricados por iRiver. Por esta razón el script filtrará los      #
# tracks, incluyendo en el archivo de salida solo los que se encuentran en el  #
# dispositivo. Además, la lista pla se guardará siempre en el directorio        #
# ${iriver_mount_dir}/${Plist_Dir}. Por defecto ${Plist_Dir}="Playlist" que es #
# donde el iRiver e100 busca los playlists. Si tu reproductor ocupa otra ruta  #
# cambia el valor de ${Plist_Dir} al correcto.                                 #
#                                                                              #
# Diseñado para crear listas de música para mi iRiver E100. He de suponer que  #
# servirá en otros...                                                          #
#                                                                              #
#                                           Licencia: GPLv2                    #
#                                              Autor: Marcelo (sd0625) Vergara #
#                                             e-mail: sd0625@gmail.com         #
#                                última modificación: lun 8 feb 2010           #
################################################################################

Plist_Dir="Playlists"
Version="0.9"

################################################################################
# Verifica las opciones auxiliares, y efectúa las acciones correspondientes
while getopts ":Vho:fv" opt; do
    case $opt in
        V)  #--> Version
            #${0##*/} muestra el nombre del script eliminando la ruta a éste
            echo "${0##*/} Versión $Version: El convertidor de listas de música para los"\
                "reproductores iRiver."
            exit 0
            ;;

        h ) #--> Ayuda
            echo -e "modo de uso: ${0##*/} [-Vhvf] [-o file] m3u_list iRiver_mount_point"\
                "\n\n${0##*/} convierte listas de reproducción m3u al formato pla usado en los"\
                "reproductores iRiver.\n\nOpciones:\n\n\t-V\tMuestra la versión del script y"\
                "termina.\n\t-h\tMuestra esta ayuda y termina.\n\t-v\tMuestra información"\
                "mientras se ejecuta.\n\t-o file\tNombre del archivo de salida. Si no se"\
                "especifica se ocupa el nombre del archivo de entrada como patrón.\n\t-f\tSe"\
                "sobreescribe el archivo de salida.\n\nObservaciones:\n\n\t- El script supone que"\
                "ruta_reproductor es efectivamente donde está montado el dispositivo. La única"\
                "revisión es verificar que en ese directorio está montado un disco vfat.\n\t- El"\
                "script no agrega a la lista aquellos tracks que no se encuentren en el"\
                "reproductor.\n\t- La lista se escribirá en la carpeta $Plist_Dir del Reproductor."\
                "Si el tuyo ocupa otra, edita el valor de $Plist_Dir en el script\n\nBugs y"\
                "mejoras reportarlas a sd0625@gmail.com."
            exit 0
            ;;

        o ) #--> Nombre del archivo de salida. Si se omite, se ocupa el nombre del archivo de
            #--> entrada cambiando la extensión a .pla. Error si se ingresa más de uno o una ruta
            #--> a éste
            if [[ -n $pla_list ]]; then   
                echo "ERROR: ingresaste más de un archivo de salida." >&2
                exit 1
            fi

            pla_list=$OPTARG

            if [[ $pla_list != ${pla_list##*/} ]]; then
                echo "ERROR: Debes ingresar solo el nombre del archivo."\
                    "de salida; éste se guarda automáticamente en el"\
                    "directorio $Plist_Dir de tu reproductor" >&2
                exit 1
            fi

            # Si $pla_list no termina en .pla se le añade
            ext=${pla_list##*.}
            [[ ${#ext} != 3 || `expr $ext : [pP][lL][aA]` != 3 ]] && pla_list=$pla_list.pla
            ;;

        f)  #--> Sobreescribir el archivo si existe
            force=" "
            ;;

        v)  #--> mostrar información
            verbose=" "
            ;;

        :)  #--> falta argumento para -o
            echo -e "Error\n-o: No indicaste archivo de salida." >&2
            exit 1
            ;;
           
        ?)  #--> Opción desconocida
            echo -e "ERROR:\nOpción -$OPTARG desconocida. Consulta $0"\
                 "-h para ver la ayuda" >&2
            exit 1
            ;;
    esac
done

shift $(($OPTIND- 1))
# fin procesamiento de opciones
################################################################################

################################################################################
# procesamiento y verficación de argumentos
m3u_list=$1
iRiver_mount_point=$2

if [[  $# != 2 ]]; then
    echo -e "ERROR:\nNo indicaste correctamente el archivo de entrada y el"\
         "directorio de montaje del reproductor." >&2
    exit 1
fi

if ! [[ -f "$m3u_list" && -d "$iRiver_mount_point" ]]; then
    echo -e "ERROR:\n\"$m3u_list\" no es un fichero y/o \"$iRiver_mount_point\" no"\
         "es un directorio." >&2
    exit 1;
fi

ext=${m3u_list##*.}
if [[ ${#ext} != 3 || $(expr $ext : [mM]3[uU]) != 3 ]]; then
    echo -e "ERROR:\n\"$m3u_list\" no es una lista de reproducción m3u." >&2
    exit 1;
fi

# usado para transformar una ruta relativa a una absoluta sin / al final
cd "$iRiver_mount_point"
iRiver_mount_point="$PWD"
cd "$OLDPWD"

mount -t vfat | grep -q " $iRiver_mount_point "
if [[ $? != 0 ]]; then
    echo -e "ERROR:\n\"$iRiver_mount_point\" no es un punto de montaje de un disco vfat." >&2
    exit 1
fi
# fin argumentos
####################################################################################################

####################################################################################################
# comienzo del procesamiento
if [[ -z "$pla_list" ]]; then     # si no se especificó archivo de salida
    pla_list=${m3u_list##*/}
    pla_list=${pla_list%.*}.pla
fi

pla_list="$iRiver_mount_point/$Plist_Dir/$pla_list"
m3u_list_dir="${m3u_list%/*}"
[[ "$m3u_list" == "$m3u_list_dir" ]] && m3u_list_dir=.
cd "$m3u_list_dir"
m3u_list_dir=$PWD
cd "$OLDPWD"

if [[ -f "$pla_list"  && -z $force ]]; then
    echo -e "ERROR:\nYa existe un archivo llamado \"$pla_list\". Usa -f para sobreescribirlo." >&2
    exit 1
fi

( : > "$pla_list" ) 2> /dev/null
if [[ $? != 0 ]]; then
    echo -e "ERROR:\nNo se pudo crear el archivo \"$pla_list\"." >&2
    exit 1;
fi

#filtra la lista m3u quedándose solo con la ruta de los tracks y eliminando lo demás
tracks=$(grep -E "^/|^.[./]" "$m3u_list")
IFS=$'\x0d\x0a'

tmp_file_head=$(mktemp -p /tmp iriverlist.XXXXXX)
tmp_file_body=$(mktemp -p /tmp iriverlist.XXXXXX)
declare -i j=0 track_ok=0 intaux=0 offset_mount=${#iRiver_mount_point}
(( offset_mount-- ))

for i in $tracks; do
    cd "$m3u_list_dir"    # util solo para determinar si la pista está en el reproductor
    # en listas con rutas relativas

    if ! [[ -f "$i" ]]; then
        [[ -n $verbose ]] && echo "\"$i\" no existe" >&2
        continue
    fi

    cd "${i%/*}"
    track_path="$PWD/"
    if [[ $track_path == ${track_path#$iRiver_mount_point/} ]]; then
        [[ -n $verbose ]] && echo -e "\"$i\" no está en el reproductor." >&2
        continue
    fi
   
    win_path=${track_path#$iRiver_mount_point}${i##*/}
    # cambia los separadores de directorio usados en Unix (/) por los de Windows (\)
    win_path=${win_path//\//\\}
    #convierte desde la codificación UTF-8 a latin1 para nombres de archivo con letras acentuadas, eñes, etc
    win_path=$( echo $win_path | iconv --from UTF-8 --to ISO_8859-1 )
    j=1

    #por la estructura de los archivos *.pla, parece que no soportan rutas de mas de 255 caracteres.
    #PD: las rutas siempre son relativas al directorio raiz del reproductor
    if (( ${#win_path} > 255 )); then
        [[ -n $verbose ]] && echo "\"$win_path\" tiene más de 255 carácteres." >&2
        continue
    fi

    offset=${#track_path}
    (( offset=offset-offset_mount ))
    printf -v offset "%02x" $offset
    printf "%b%b" "\x00" "\x$offset" >> "$tmp_file_body"

    #en cada ciclo añade al final del archivo $tmp_file_body un byte 0x00 y la primera letra de la
    #variable $win_path para luego eliminarla de la cadena
    while [[ -n $win_path ]]; do
        printf "%b%c" '\x00' $win_path >> "$tmp_file_body"
        win_path=${win_path:1}
        ((j++))
    done
   
    while (( $j < 256 )); do
        printf "%b%b" '\x00' '\x00' >> "$tmp_file_body"
        ((j++))
    done

    ((track_ok++))
done

if (( track_ok == 0 )); then
    echo "No se encontraron tracks validos..." >&2
    rm "$tmp_file_head" "$tmp_file_body" "$pla_list"
    exit 1
fi

#mask se usa para convertir el número $track_ok en su representacion binaria de bytes little endian
declare -i mask=16777216
j=0;

while (( j < 4 )); do
    ((intaux =  track_ok / mask))
    ((track_ok = track_ok % mask))
    ((mask = mask / 256))
    printf -v aux "%02X" $intaux
    printf "%b" "\x$aux" >> "$tmp_file_head"
    ((j++))
done

echo "iriver UMS PLA" >> "$tmp_file_head"
j=19
while (( $j < 512 )); do
    printf "%b"  '\x00' >> "$tmp_file_head"
    ((j++))
done

cat "$tmp_file_head" "$tmp_file_body"  > "$pla_list"
rm "$tmp_file_head" "$tmp_file_body"
exit 0

El script trabaja de la siguiente manera:

irivelist [-vVhf] [-o output_file] mi_lista.m3u punto_de_montaje_reproductor

donde:

  • -V
  • muestra la versión y termina

  • -v
  • muestra los mensajes de error

  • -h
  • muestra la ayuda y termina

  • -f
  • fuerza la sobreescritura del fichero de salida

  • -o file
  • indica el nombre del fichero de salida. Es necesario solo el nombre, pues el script guardará la lista en la carpeta Playlists del reproductor. Si no se indica, el nombre del archivo de salida será el mismo que el de entrada, cambiando solo la extensión a .pla

  • mi_lista.m3u
  • la lista de entrada la cual se convertirá a PLA. Puede tener tanto listas con rutas relativas (que empiezen con ./) o absolutas (empiezan con /)

  • punto de montaje_reproductor
  • es el directorio donde está montado el reproductor.

Como ya se mencionó antes, como la el formato parece ser usado exclusivamete por iriver .Inc, el script se encargará de verificar si cada track se encuentra efectivamente en el reproductor. También se encarga de guardar las listas de salida en el directorio usado por el reproductor para este fin.

Traté de que el script verificará automáticamente si había un reproductor iriver y donde estaba montado. Lo primero lo pude hacer usando lsusb -d 4102: (4102 es el ID de la compañía que manufactura los reproductores), pero no encontré la manera de saber en qué carpeta está montado, y si es que está montado. Es por eso que me vi en la obligación de que se le indique al script la carpeta donde se monta el dispositivo. Si alguien sabe como hacer esto último que me ayude...

Cualquier duda, ayuda o lo que sea relevante en la mejora del iriverlist es bienvenida

EDITO: para quien lo necesite, solo tiene que hacer un copy-paste del script y guardarlo como iriverlist (o como se les de la gana). Despues tienen que darle permisos de ejecución, moverlo a alguna de las carpetas de $PATH para llamarlo como cualquier comando (yo me creee mi ~/.bin para esos casos) y listo

Imagen de shaola
Enviado por shaola el 8 Febrero, 2010 - 17:40.

Buen script!

para mirar en que directorio está montado talvez puedes hacerlo con el archivo /etc/mtab.

Imagen de SD0625
Enviado por SD0625 el 8 Febrero, 2010 - 17:49.

el problema es que no me sé el dispositivo, recuerda que es plug & play. Además a mi reproductor se le puede agregar una tarjeta microSD, aunque no esté instalada, el SO detecta tanto la memoria interna (gralmnete /dev/sdb) y la externa (generlamente /dev/sdc)... igual vamos a chequear

Imagen de shaola
Enviado por shaola el 8 Febrero, 2010 - 17:58.

se me ocurre una posibilidad. Lanzar el script antes de conectarlo. El script lee el mtab, pide que conectes el dispositivo, vuelve a leer el mtab y compara. Si compruebas que siempre conecta antes la memoria interna que la sd (por ejemplo) supongo que las dos ultimas lineas en orden serán los puntos de montaje para cada cosa.

No sé... como no tengo ningún aparatito de esos no puedo comprobarlo ahora.

Otra opción, podría ser ponerle una etiqueta (label) a las particiones del dispositivo.

Por ejemplo DISPOSITIVO para la memoria interna y SD para la externa. Siendo así, el sistema montará siempre en /media/DISPOSITIVO y en /media/SD.

Al menos mis usb los tengo con etiquetas y siempre los montan en esos puntos de montaje.

Imagen de SD0625
Enviado por SD0625 el 8 Febrero, 2010 - 18:06.

se me olvidaba decirte... el script solo ve los tracks en la memoria interna... la externa no la he probado porque no he deshuesado listas que contengan tracks en la otra memoria, asi que no se como funciona en ese caso. ademas lo estoy tratando de hacer lo mas portable que pueda ... supongo que para mi uso personal lo modificaré un poco para que si no le indico la ruta, use por defecto /media/e100.. no se me habia ocurrido... gracias