#!/bin/bash # pspcp VER="v1.3 (08/26/05)" COPYRIGHT="(c) Olle Johansson 26 Aug 2005" DESCR="Copy and possibly re-encode mp3 files to Sony PSP." # LICENSE # This script is release under the GNU General Public License. # http://www.gnu.org/copyleft/gpl.html # REQUIREMENTS # This scripts requires the following external programs, or their equivalent: # lame - Needed to re-encode mp3:s # id3cp - To retain id3 tags of re-encoded mp3:s # Some way of returning a random list of mp3 files to use the random option. # DESCRIPTION # To use this with your Sony PSP you will first have to make sure it # is mounted, or that it gets automounted when it is accessed. # This script can actually be used on any directory, so it should work # with an iPod or USB memory dongle as well, just as long as it can be # mounted. # Before you start using it you need to edit the configuration variables # below. Most important is the PSPPATH which needs to be set to the # directory where files will be copied to. # BUGS # * Adds an empty file with the name "0" in cwd. # TODO # * Option to clean up file names # * Make sure quality of mp3 is higher than target quality before re-encoding. # * Select what type of data to copy to automatically insert in right dir. # * Possibly find type of data automatically. # * Make it possible to have more than one file to copy as arguments. # * Scale images to best size for viewing on PSP. # * Copy html files to Common directory and create index file with a list of links. # * Restore backed up information. # * Cache information about songs to make it quicker to show the list. # * Listing of movies, pictures and gamesaves on the memory stick. # * Separate Memory Usage information from info, or speed it up. Possibly cache info. # CHANGES # v1.3 (26 Aug 2005) # - Added b option to backup to backup SERVER directory. # - Added -i flag to list available memory on the PSP. # - Added -V flag to show version information. # - Moved lame encoding quality settings to config options. # - Added -m flag to list all songs on memory stick (requires mp3info program or similar) # - Added -h option to print usage information. # # v1.2 (29 Jan 2005) # - Better check to see if anything was done. # - Added option to backup data from the PSP. # # v1.1 (28 Jan 2005) # - Added option to empty the directory. # - Added option to change target directory. # - Added option to select quality of re-encoding. # - Added a check to make sure the file will fit before copying. # - Added a check that a file with the same name doesn't already exist. # - If no re-encoding quality is given, the mp3 file is copied as-is. # - Script exits cleanly if target directory doesn't exist. # - Only run randomizing script if actually necessary. # - Only randomize as many files as necessary. # - Exits with an error message if nothing could be done. # - Added some script information. ############################################################################## # Configurable values, change these to fit your setup. # Set to 1 if you want debug information DEBUG=0 # Temporary path to save converted files in before they're moved to the PSP TMPPATH=/tmp/pspcp # Path where psp is mounted PSPPATH=/mnt/psp # Path to place music files on PSP PSPMUSIC= # Path where songs are placed on the PSP MUSICPATH=$PSPPATH/PSP/MUSIC/$PSPMUSIC # Path to backup info on PSP BACKUPPATH=$HOME/.psp/psp # Command to convert mp3 files, lame options for quality settings are appended. LAME="lame --mp3input -S -v -h -V 0" # LAME encoding quality settings LAMEQ1="-m m -b 32 -B 96" LAMEQ2="-m j -b 64 -B 160" LAMEQ3="-m s -b 96 -B 256" LAMEQ4="-m s -b 128 -B 320" # Command that returns a list of (random) mp3 files. RANDOMSCRIPT='/usr/local/otto-2.0/bin/otto pick $COUNT paths' # Command to copy the id3 tag from one file to another. ID3CP=/usr/bin/id3cp # Command to get block size, number of blocks and available blocks on PSP STATCMD="stat -f -c \"%s %b %a\"" # MP3INFO script (http://www.ibiblio.org/mp3info/) # Should return information about the mp3 file in the following format: # ArtistAlbumTrack NumberTrack TitleBitrateLength MP3INFO="mp3info -r m -p %a\t%l\t%n\t%t\t%r\t%m:%02s" # End of configurable values, don't change anything below this line. ############################################################################## # Some standard values BINPATH=$0 SCRIPTNAME=`basename $0` COUNT=0 DUMMY=0 VERBOSE=0 QUALITY=0 EMPTYDIR=0 THINGSDONE=0 declare -a STATS # List of error codes E_NOARG=33 # No arguments given E_NOTMPDIR=44 # Couldn't create temporary save directory. E_NOPSP=55 # Couldn't find PSP path. E_NOTHINGTODO=66 # Nothing to be done. E_NOBACKUPDIR=77 # Couldn't create backup destination directory. # The functions used in this script # ---------------------------------------------------------------------------- # Prints usage information print_usage () { echo -e "${SCRIPTNAME} $VER - $DESCR\n$COPYRIGHT" echo -e "Usage:\n${SCRIPTNAME} [-himdve] [-t ] [-r ] [-q ] []" echo "Pass name of mp3 to copy as argument or use the -r option to copy random" echo "songs. If no reencoding quality is given, the mp3 is copied as-is." echo -e "-h\tPrint this usage information." echo -e "-i\tPrint information about your PSP." echo -e "-m\tShow information about all songs on your PSP." echo -e "-r X\tRandom, choose X random mp3:s" echo -e "-q X\tQuality of re-encoding, 1 is lowest and 4 is highest quality." echo -e "-t X\tChange target music directory (DEFAULT: $MUSICPATH)" echo -e "-b X\tBackup data\n\tX=g(ame)|m(usic)|p(hoto)|s(avedata)|v(ideo)|b(rowser)|a(ll)" echo -e "-e\tErase content of music directory on Sony PSP before adding new files." echo -e "-v\tVerbose, outputs information on what the script is doing." echo -e "-V\tPrint version information." echo -e "-d\tDummy mode, print commands instead of executing." } # Prints debugging info if $DEBUG is set. debug () { if [[ $DEBUG > 0 ]]; then echo "${1}" fi } # Print only if VERBOSE is set. vecho () { if [[ $VERBOSE > 0 ]]; then echo "${1}" fi } # If DUMMY is 1 just echo the command in $1, otherwise run it. ddo () { if [[ $DUMMY > 0 ]]; then eval echo $1 else eval $1 fi } # Function to return the closes integer value equal to or greater than $1 ceil () { if [ $(( $1 % $2 )) > 0 ]; then RETVAL=$(( $1 / $2 + 1 )) else RETVAL=$(( $1 / $2 )) fi echo $RETVAL } # Check if file fits on file system filefits () { if [ -z "$2" ]; then FILESYSTEM=$MUSICPATH else FILESYSTEM=$2 fi if [[ -e "$FILESYSTEM" && -e "$1" ]]; then FILESIZE=`stat -c "%s" "$1"` STATS=( `eval $STATCMD "$FILESYSTEM"` ) FILESIZEINBLOCKS=`ceil $FILESIZE ${STATS[0]}` if [ $FILESIZEINBLOCKS -le ${STATS[2]} ]; then echo 1 else echo 0 fi else echo 0 fi } # Prints a size in human readable form hrsize () { let "VAL=$1" KB=1024 let "MB=1024*$KB" let "GB=1024*$MB" let "TB=1024*$GB" if [ "$VAL" -lt "$KB" ]; then echo "$VAL bytes" elif [ "$VAL" -lt "$MB" ]; then let "RETSIZE=$VAL/$KB" echo "$RETSIZE KB" elif [ "$VAL" -lt "$GB" ]; then let "RETSIZE=$VAL/$MB" echo "$RETSIZE MB" elif [ "$VAL" -lt "$TB" ]; then let "RETSIZE=$VAL/$GB" echo "$RETSIZE GB" else let "RETSIZE=$VAL/$TB" echo "$VAL TB" fi } # Print information about a directory dirinfo () { DIRNAME=$1 DIR=$2 DSIZE=`du -bs "$DIR" | awk '{ print $1}'` DSIZE=`hrsize $DSIZE` DFILES=`find "$DIR" -type f | wc -l | awk '{print $1}'` if [ "$DFILES" -eq "0" ]; then echo "$DIRNAME: empty" else echo "$DIRNAME: $DSIZE in $DFILES files" fi } # Print size of Memory Stick in PSP an available memory. mssize () { FILESYSTEM=$PSPPATH/PSP # blocksize totalblocks freeblocks STATS=( `eval $STATCMD "$FILESYSTEM"` ) FSSIZE=`hrsize ${STATS[1]}*${STATS[0]}` FREESIZE=`hrsize ${STATS[2]}*${STATS[0]}` # PSIZE=`du -bs "$FILESYSTEM/PHOTO" | awk '{ print $1}'` # PSIZE=`hrsize $PSIZE` # PFILES=`find /mnt/psp/PSP/PHOTO -type f | wc -l | awk '{print $1}'` echo "Information about your PSP" echo "--------------------------" echo "Memory Stick size: $FSSIZE" echo "Available Memory: $FREESIZE" echo "Memory Usage:" dirinfo "Photos" "$FILESYSTEM/PHOTO" dirinfo "Music" "$FILESYSTEM/MUSIC" dirinfo "Movies" "$PSPPATH/MP_ROOT" dirinfo "Game Saves" "$FILESYSTEM/SAVEDATA" dirinfo "Games" "$FILESYSTEM/GAME" dirinfo "System" "$FILESYSTEM/SYSTEM" dirinfo "Common" "$FILESYSTEM/COMMON" } # Convert and copy mp3 file convertmp3 () { FILENAME=`basename "$1"` if [ ! -e "$MUSICPATH/$FILENAME" ]; then if [[ $QUALITY > 0 ]]; then vecho "Converting and copying mp3: $1" # Decide what quality to re-encode to. if [[ $QUALITY -eq "1" ]]; then CMD1='$LAME $LAMEQ1 "$1" "$TMPPATH/$FILENAME" >/dev/null 2>/dev/null' elif [[ $QUALITY -eq "2" ]]; then CMD1='$LAME $LAMEQ2 "$1" "$TMPPATH/$FILENAME" >/dev/null 2>/dev/null' elif [[ $QUALITY -eq "3" ]]; then CMD1='$LAME $LAMEQ3 "$1" "$TMPPATH/$FILENAME" >/dev/null 2>/dev/null' elif [[ $QUALITY -eq "4" ]]; then CMD1='$LAME $LAMEQ4 "$1" "$TMPPATH/$FILENAME" >/dev/null 2>/dev/null' fi # Re-encode the file to the temp dir. debug $CMD1 if [[ $DUMMY > 0 ]]; then eval echo $CMD1 else eval $CMD1 fi # Copy the id3tag from the original file debug "$ID3CP \"$1\" \"$TMPPATH/$FILENAME\" >/dev/null" if [[ $DUMMY > 0 ]]; then echo "$ID3CP \"$1\" \"$TMPPATH/$FILENAME\" >/dev/null" else $ID3CP "$1" "$TMPPATH/$FILENAME" >/dev/null fi # Check if the file fits on the PSP debug "Checking if file fits on PSP." FILEFITS=`filefits "$TMPPATH/$FILENAME" "$MUSICPATH"` if [[ $FILEFITS > 0 ]]; then # Move the re-encoded file to the Sony PSP debug "mv \"$TMPPATH/$FILENAME\" \"$MUSICPATH/$FILENAME\"" if [[ $DUMMY > 0 ]]; then echo "mv \"$TMPPATH/$FILENAME\" \"$MUSICPATH/$FILENAME\"" else mv "$TMPPATH/$FILENAME" "$MUSICPATH/$FILENAME" fi else vecho "File doesn't fit: $TMPPATH/$FILENAME" debug "Removing re-encoded file." if [[ $DUMMY > 0 ]]; then echo "rm \"$TMPPATH/$FILENAME\"" else rm "$TMPPATH/$FILENAME" fi fi else vecho "Copying mp3: $1" # Check if the file fits on the PSP debug "Checking if file fits on PSP." FILEFITS=`filefits "$1" "$MUSICPATH"` if [[ $FILEFITS > 0 ]]; then if [[ $DUMMY > 0 ]]; then echo "cp \"$1\" \"$MUSICPATH/$FILENAME\"" else cp "$1" "$MUSICPATH/$FILENAME" fi else vecho "File doesn't fit: $TMPPATH/$FILENAME" fi fi else vecho "File already exists: $MUSICPATH/$FILENAME" fi } backupfiles () { ARG=${1:0:1} if [[ $ARG == "p" || $ARG == "P" ]]; then BACKUPSOURCE=$PSPPATH/PSP/PHOTO BACKUPDEST=$BACKUPPATH/PSP/PHOTO elif [[ $ARG == "m" || $ARG == "M" ]]; then BACKUPSOURCE=$PSPPATH/PSP/MUSIC BACKUPDEST=$BACKUPPATH/PSP/MUSIC elif [[ $ARG == "g" || $ARG == "G" ]]; then BACKUPSOURCE=$PSPPATH/PSP/GAME BACKUPDEST=$BACKUPPATH/PSP/GAME elif [[ $ARG == "s" || $ARG == "S" ]]; then BACKUPSOURCE=$PSPPATH/PSP/SAVEDATA BACKUPDEST=$BACKUPPATH/PSP/SAVEDATA elif [[ $ARG == "v" || $ARG == "V" ]]; then BACKUPSOURCE=$PSPPATH/MP_ROOT/100MNV01 BACKUPDEST=$BACKUPPATH/MP_ROOT/100MNV01 elif [[ $ARG == "b" || $ARG == "B" ]]; then BACKUPSOURCE=$PSPPATH/PSP/SYSTEM BACKUPDEST=$BACKUPPATH/PSP/SYSTEM else BACKUPSOURCE=$PSPPATH BACKUPDEST=$BACKUPPATH fi vecho "Backing up $BACKUPSOURCE to $BACKUPDEST" debug "Checking that backup destination directory exists." if [ ! -d "$BACKUPDEST" ]; then mkdir "$BACKUPDEST" if [ ! -d "$BACKUPDEST" ]; then echo "ERROR: Couldn't create temporary directory: $BACKUPDEST" exit $E_NOBACKUPDIR fi fi debug "Checking if there are any files to copy." FILECOUNT=`ls -1 $BACKUPSOURCE | wc -l` if [[ $FILECOUNT -gt 0 ]]; then if [[ $DUMMY > 0 ]]; then echo "cp -udpr \"$BACKUPSOURCE/\"* \"$BACKUPDEST\"" else cp -udpr "$BACKUPSOURCE/"* "$BACKUPDEST" fi else vecho "Nothing to backup, directory empty." fi } # Print information about mp3 file showmp3info () { debug "Finding mp3 info for: $1" MP3FILEINFO="" if [ -f "$1" ]; then THISMP3=`$MP3INFO "$1" 2>/dev/null` MP3ARTIST=`echo "$THISMP3" | gawk -F'\t' '{print $1}' -` MP3ALBUM=`echo "$THISMP3" | gawk -F'\t' '{print $2}' -` MP3TRACK=`echo "$THISMP3" | gawk -F'\t' '{print $3}' -` MP3TITLE=`echo "$THISMP3" | gawk -F'\t' '{print $4}' -` MP3BITRATE=`echo "$THISMP3" | gawk -F'\t' '{print $5}' -` MP3LENGTH=`echo "$THISMP3" | gawk -F'\t' '{print $6}' -` if [ -n "$MP3ARTIST" ]; then MP3FILEINFO="$MP3ARTIST" fi if [ -n "$MP3ALBUM" ]; then MP3FILEINFO="$MP3FILEINFO / $MP3ALBUM" fi if [ -n "$MP3TRACK" ]; then MP3FILEINFO="$MP3FILEINFO / $MP3TRACK - " else MP3FILEINFO="$MP3FILEINFO / " fi if [ -n "$MP3TITLE" ]; then MP3FILEINFO="${MP3FILEINFO}${MP3TITLE}" else MP3FILEINFO=`basename "$1" .mp3` fi MP3FILEINFO="$MP3FILEINFO ($MP3BITRATE kbps) $MP3LENGTH" echo $MP3FILEINFO fi } # List all mp3 files and print information about each. musicinfo () { MUSICPATH=$PSPPATH/PSP/MUSIC if [ -n "$1" ]; then MYPATH=$1 else MYPATH=$MUSICPATH fi #find "$MUSICPATH" -type f -exec showmp3info '{}' \; for i in "$MYPATH"/*; do if [ -f "$i" ]; then showmp3info "$i" elif [ -d "$i" ]; then DIRNAME=`basename "$i"` vecho "Directory: $DIRNAME" musicinfo "$i" fi done } # Start of the script. # ---------------------------------------------------------------------------- # Check if we have any arguments, otherwise exit. if [ -z "$1" ]; then echo "ERROR: No arguments given." print_usage exit $E_NOARG fi # Read the command line argument flags. while getopts ":dvehimVb:t:r:q:" Option do case $Option in d ) DUMMY=1;; v ) VERBOSE=1;; e ) EMPTYDIR=1;; h ) SHOWUSAGE=1;; i ) INFO=1;; m ) MUSICINFO=1;; V ) VERINFO=1;; b ) BACKUP=$OPTARG;; t ) MUSICPATH=$OPTARG;; r ) COUNT=$OPTARG;; q ) QUALITY=$OPTARG;; esac done # Decrements the argument pointer so it points to next argument. shift $(($OPTIND - 1)) # Print usage information if [[ $SHOWUSAGE > 0 ]]; then print_usage exit 1 fi # Print version info if [[ $VERINFO > 0 ]]; then echo "pspcp $VER $COPYRIGHT" exit 1 fi # Make sure the PSP is connected and directory exists if [ ! -e "$MUSICPATH" ]; then echo "ERROR: PSP not connected/Destination directory not found." exit $E_NOPSP fi # Print PSP info if [[ $INFO > 0 ]]; then mssize let "THINGSDONE+=1" fi # Print music info if [[ $MUSICINFO > 0 ]]; then musicinfo let "THINGSDONE+=1" fi # Create temporary save directory if it doesn't exist, but only if we need it. if [[ $QUALITY > 0 ]]; then if [ ! -d "$TMPPATH" ]; then mkdir "$TMPPATH" if [ ! -d "$TMPPATH" ]; then echo "ERROR: Couldn't create temporary directory: $TMPPATH" exit $E_NOTMPDIR fi fi fi # Start by backing up data from the PSP if [[ -n "$BACKUP" ]]; then backupfiles "$BACKUP" let "THINGSDONE+=1" fi # Erase the contents of the directory on the Sony PSP first. if [[ $EMPTYDIR > 0 ]]; then vecho "Removing all current songs from the Sony PSP." if [[ $DUMMY > 0 ]]; then echo "rm -rf \"$MUSICPATH/\"*" else rm -rf "$MUSICPATH/"* fi let "THINGSDONE+=1" fi # If a filename was given we start by converting that. if [ -n "$1" ]; then if [ -f "$1" ]; then convertmp3 "$1" let "THINGSDONE+=1" fi fi # Only randomize files if necessary if [[ $COUNT -gt 0 ]]; then # Insane code necessary to get each line as an element in an array. vecho "Randomizing mp3 files..." declare -a RANDOMFILES OIFS=$IFS; IFS=$'\n' RANDOMFILES=( `eval $RANDOMSCRIPT` ) IFS=$OIFS # For each file in list, call convertmp3, quit when COUNT is reached. i=0 for element in $(seq 0 $((${#RANDOMFILES[@]} - 1))); do mp3file=${RANDOMFILES[$element]} if [ $i -eq $COUNT ]; then vecho "All files converted." break fi if [ -f "$mp3file" ]; then convertmp3 "$mp3file" fi let "i+=1" done let "THINGSDONE+=1" fi # Print usage information if nothing was done. if [ $THINGSDONE == 0 ]; then echo "ERROR: Nothing to be done." print_usage exit $E_NOTHINGTODO fi # Quit with exit value 1 if we have printed information. if [[ $VERBOSE > 0 ]]; then exit 1 fi exit 0