LinuxQuestions.org
Support LQ: Use code LQ3 and save $3 on Domain Registration
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Newbie
User Name
Password
Linux - Newbie This Linux forum is for members that are new to Linux.
Just starting out and have a question? If it is not in the man pages or the how-to's this is the place!

Notices

Reply
 
Search this Thread
Old 05-05-2010, 10:26 PM   #1
thiemster
LQ Newbie
 
Registered: Jan 2010
Location: Ohio, USA
Distribution: Arch Linux w/ Gnome, Ubuntu 9.10, Windows Vista
Posts: 24

Rep: Reputation: 15
Autofill iPod Shuffle with a certain amount of megabytes of music


I have a first generation iPod Shuffle (yes, I know it's old) with 512 megabytes of space (feel free to laugh). I also have arch linux installed on my computer, with a music folder containing about 30 folders full of music. This totals to about 2 gigabytes of music.

What I want to do is: create a script that moves no more than 512 megabytes (it can be slightly under this amount, but NOT over) of random songs from my music folder (and it's subsequent artist/album folders) to a separate folder. I will then use gtkpod to move those random songs in the new folder to my iPod Shuffle.

The problem is, I have no clue how to do this. Help is appreciated, either in suggesting a linux command that might do this or writing out a whole script. Either way, I would be VERY satisfied
 
Old 05-07-2010, 09:20 AM   #2
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949
Dunno why, but I found this an interesting challenge. So much so that I actually went and wrote a whole script for it. You'll have to modify it as necessary for your needs. It's fully commented so it should be rather self-explanatory.
Code:
#!/bin/bash

#break strings on newlines only
#needed if filenames could contain spaces.
IFS=$'\n'   

#what directory should you load from?
musicdir=$HOME/music_directory

#what file types do you want to load?
#comma separated list, without periods
types=mp3,aac,mp4

#maximum amount of files to be copied, in megabytes
max=512

#convert types format for use in the find command
types=${types//,/\\|}

#convert max number to bytes
max2=$max
max=$(( max * 1024 * 1024 ))

#load an array with all available files
declare -a filelist
filelist=( $(find $musicdir -type f -iregex ".+\.\(${types}\)" -print) )

#get total number of files
tnum=${#filelist[@]}

#grab random files until size total equals max amount.
#save the result in the variable "$exportlist".

declare total=0
declare exportlist
while true; do

     #get filename of random index value
     rand=$(( $RANDOM % $tnum ))
     file="${filelist[$rand]}"

     #if filename is already in the list, skip it
     [[ "$exportlist" =~ "$file" ]] && continue

     #increment the total size count
     filesize=$(stat -c %s $file)
     total=$(( total + filesize ))

     #if total size exceeds max, exit loop before copying
     #but gotta remove the last file's size again first
     [[ $total -gt $max ]] && total=$(( total - filesize )) && break

     #otherwise add the file to the exportlist,
     #inserting a newline in front of it as a separator
     exportlist+=${file/#/$'\n'}
     
     unset rand file filesize

done

#Output display

echo "Loading approximately $max2 megabytes from $tnum files."

#enable these if you want to display a verbose list.
#(looping through list confirms that each entry is separate.
#also allows reducing it to just the basename for legibility)
#echo "=========="
#for file in $exportlist; do echo "${file##*/}" ; done
#echo "=========="

echo "Files total approximately $(( total / 1024 / 1024 )) megabytes."
[[ $total -gt $max ]] && echo "Maximum size exceeded!"
#The above line should never show.

#The command to copy the files goes here.
#(but commented out for safety)

#cp -v -t /temp/ipod $exportlist

exit 0
Of course, now that I did this, some expert will probably come along and show us how to do it in two lines.

Edit: Hold on, just discovered a fairly big mistake. It won't properly handle duplicate entries. I'll post the revised version when I'm done.

Edit2: I've updated the script to be able to handle duplicates. At least I hope. I'd appreciate having any mistakes pointed out.

Edit3: Fixed a couple more things. It was still outputting duplicates, until I quoted the variables in the test. Now it seems to be working ok, but it's hard to test it when the input is random.

Edit4: Corrected another bug. See my follow-up post below for details.

Last edited by David the H.; 05-09-2010 at 07:55 AM. Reason: as above
 
1 members found this post helpful.
Old 05-07-2010, 03:06 PM   #3
thiemster
LQ Newbie
 
Registered: Jan 2010
Location: Ohio, USA
Distribution: Arch Linux w/ Gnome, Ubuntu 9.10, Windows Vista
Posts: 24

Original Poster
Rep: Reputation: 15
I modified it for my specific folders, AND IT WORKED!
Thank you very much. This happens to be (obviously) exactly what I was looking for!
You have inspired me to try to figure out how to do this in python (the only programming language I have at least an intermediate grasp on), and I will post the results of that whenever I figure it out.
 
Old 05-07-2010, 10:34 PM   #4
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949
Glad to help out. I rather enjoy working out scripting challenges like this. I really just knocked it out fairly quickly though. I'm sure it could be improved with some thought.

I don't know much about python myself, but I'm sure the basic pattern of the script should be translatable. Looking forward to seeing what you come up with.
 
Old 05-09-2010, 08:01 AM   #5
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949
Eyballing my script again, I just noticed another small bug. This line needs to be changed:
Code:
#get total number of files
tnum=$(( ${#filelist[@]} + 1 ))
It needs to read simply
Code:
tnum=${#filelist[@]}
I'd gotten myself a bit confused over the output of the above string and using it in the random number calculation later on. The ($RANDOM % $tnum) calculation outputs a number between 0 and $tnum-1, so I mistakenly thought I needed to add one in order to get it to work correctly, but since arrays start at index 0, no incrementation is actually necessary, and it also affects the output at the upper and lower ends of the range. Not to mention that I shouldn't have added it where I did in any case, since it also changes the number of files displayed in the output.

I've edited the original post to reflect this fix.
 
Old 05-09-2010, 08:55 AM   #6
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Debian sid + kde 3.5 & 4.4
Posts: 6,823

Rep: Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949Reputation: 1949
I decided to make one more modification to the script, and this time I'll post the whole thing again.

I got to thinking about how there often seems to be 4-5MB of wasted space at the end of the cycle, so I've added a routine to try to fill that space in. It's really just a test that forces it to iterate a few more times before giving up. If the space is large enough then it might just find one last track to squeeze into it.

In my tests it seems to reduce the wasted space to about 2MB or so usually. You can adjust how many times you want it to keep trying.

Code:
#!/bin/bash

IFS=$'\n'   #separate on newlines only; needed if filenames could contain spaces.

#what directory should you load from?
musicdir=$HOME/music_directory

#what file types do you want to load?
#comma separated list, without periods
types=mp3,aac,mp4

#maximum amount of files to be copied, in megabytes
max=512

#convert types format for use in the find command
types=${types//,/\\|}

#convert max number to bytes
max2=$max
max=$(( max * 1024 * 1024 ))

#load an array with all available files
declare -a filelist
filelist=( $(find $musicdir -type f -iregex ".+\.\(${types}\)" -print) )

#get total number of files
tnum=${#filelist[@]}

#grab random files until size total equals max amount.
#save the result in the variable "$exportlist".

#set the first number below to tell it
#how many times to try to fill in the final gap.
declare -i tryagain=20
declare -i total=0
declare exportlist
while true; do

     #get filename of random index value
     rand=$(( $RANDOM % $tnum ))
     file="${filelist[$rand]}"

     #if filename is already in the list, skip it
	#it seeems you must quote the variables here
     [[ "$exportlist" =~ "$file" ]] && continue

     #increment the total size count
     filesize=$(stat -c %s $file)
     total=$(( total + filesize ))

     #if total size exceeds max
     #skip the file and remove it's size from the total
	if [[ $total -gt $max ]]; then

		total=$(( total - filesize ))

		#continue trying until $tryagain equals zero, then exit
		if [[ $tryagain -gt 0 ]] ; then
			tryagain=$(( tryagain - 1 ))
			continue
		else
			break
		fi
	fi

     #otherwise add the file to the exportlist, adding a newline as a spacer
     exportlist+=${file:+$'\n'}${file}

     unset rand file filesize

done

#Output display

echo "Loading approximately $max2 megabytes from $tnum files."

#enable these if you want to display a verbose list.
#(looping through list confirms that each entry is separate.
#also allows reducing it to just the basename for legibility)
#finally, I've added a sort to the list.

#echo "=========="
#for file in $exportlist; do echo "${file##*/}" ; done | sort
#echo "=========="

echo "Files total approximately $(( total / 1024 / 1024 )) megabytes."
[[ $total -gt $max ]] && echo "Maximum size exceeded!"
#The above line should never show.

#The command to copy the files goes here.
#(but commented out for safety)

#cp -v -t /home/david/test/test $exportlist

exit 0
 
1 members found this post helpful.
Old 05-18-2010, 08:34 AM   #7
thiemster
LQ Newbie
 
Registered: Jan 2010
Location: Ohio, USA
Distribution: Arch Linux w/ Gnome, Ubuntu 9.10, Windows Vista
Posts: 24

Original Poster
Rep: Reputation: 15
Thank you very much for the script and all of the nice modifications. I unfortunately gave up on the python script, as it was just basically going to use "os.system(command)" to run all of the commands in your script. This would make it fairly pointless...
 
  


Reply

Tags
bash, ipod, random, script, shell, shuffle


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Cannot listen to music put on 3rd Gen iPod Shuffle using gtkpod in Arch Linux cantab Linux - Hardware 3 03-08-2010 06:31 PM
Help on iPod Shuffle Autofill BASH Script dgeronimo Programming 3 01-04-2006 05:23 PM
Ipod Shuffle help ziggis-soft General 8 12-29-2005 10:47 PM
ipod shuffle speel *BSD 2 06-10-2005 06:09 PM
ipod/ipod shuffle woes (lack of permissions to edit files) Lechium Linux - Software 1 04-13-2005 03:14 PM


All times are GMT -5. The time now is 01:29 AM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration