Skip to main content

Notice

Please note that most of the software linked on this forum is likely to be safe to use. If you are unsure, feel free to ask in the relevant topics, or send a private message to an administrator or moderator. To help curb the problems of false positives, or in the event that you do find actual malware, you can contribute through the article linked here.
Topic: Syncing a Playlist to a Separate Device (Read 1510 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

Syncing a Playlist to a Separate Device

I recently got myself a Sansa Clip+. Not only does it support FLAC and ReplayGain out of the box, the thing is tiny and can take a 16GB microSD card. Only problem: no support in everyone's favorite music player for syncing to non-iPod devices! So, I fixed that.

Find enclosed one (1) (uno) script, quickly hacked together, to sync files from any one playlist in foobar2000 to a device. This script requires [a href=\'index.php?showtopic=39946\']foo_comserver2[/a] to be installed, along with Python itself. If the Python example that comes with foo_comserver2 works, so will this script.

VERY IMPORTANT: Before you use this, you will need to change three things in the script to suit your setup!
1. playlist_to_sync: add the full name of the playlist or autoplaylist you wish to sync here, between the quotes.
2. base_dir: replace 'E:\\Music\\FLAC\\' with the full path of where your files are located. For example, my files are in E:\Music\FLAC\%album artist%\%album%\etc. Be sure to double-up your backslashes here, otherwise this will not work.
3. target_dir: again, replace 'G:\\MUSIC\\' with where you want your files to wind up. My Sansa's microSD slot shows up at G:\, hence what I have filled in here.

Once run, the script polls foobar2000 for the full path of each track on the named playlist. It then steps through the list of paths, to see if a matching file exists on the target; if it does, and it's the same file, it continues gracefully. Otherwise, it copies the file to the target.

Note: this script does not have a GUI, because I simply don't care enough to make one right now. Maybe later. If you want to, feel free. Run this script in a command prompt. (ie. 'python sync.py' or similar)

I assign all rights to this little hackjob to the community that's made listening to music on Windows so much more fun. Keep up the good work, everyone.

Code: [Select]
# playlist-to-device sync script for foobar2000
# tested on Windows XP SP3, Python 2.6.2, foobar2000 1.0, and foo_comserver2 0.7a6
# progress bar code shamelessly cribbed from [url=http://stackoverflow.com/questions/274493/how-to-copy-a-file-in-python-with-a-progress-bar]http://stackoverflow.com/questions/274493/...-a-progress-bar[/url]
# questions? kerobaros@gmail.com

import win32com.client
import os, filecmp, shutil, sys

# --- CONFIGURATION STARTS HERE ---
playlist_to_sync = 'Rating > 3'
base_dir = 'E:\\Music\\FLAC\\'
target_dir = 'G:\\MUSIC\\'
# --- CONFIGURATION ENDS HERE ---

class ProgressBar:
    """From [url=http://code.activestate.com/recipes/168639/"""]http://code.activestate.com/recipes/168639...ot;""[/url]
    def __init__(self, minValue = 0, maxValue = 10, totalWidth=12):
        self.progBar = "[]" # This holds the progress bar string
        self.min = minValue
        self.max = maxValue
        self.span = maxValue - minValue
        self.width = totalWidth
        self.amount = 0 # When amount == max, we are 100% done
        self.updateAmount(0) # Build progress bar string
 
    def updateAmount(self, newAmount = 0):
        if newAmount < self.min: newAmount = self.min
        if newAmount > self.max: newAmount = self.max
        self.amount = newAmount
 
        # Figure out the new percent done, round to an integer
        diffFromMin = float(self.amount - self.min)
        percentDone = (diffFromMin / float(self.span)) * 100.0
        percentDone = round(percentDone)
        percentDone = int(percentDone)
 
        # Figure out how many hash bars the percentage should be
        allFull = self.width - 2
        numHashes = (percentDone / 100.0) * allFull
        numHashes = int(round(numHashes))
 
        # build a progress bar with hashes and spaces
        self.progBar = "[" + '#'*numHashes + ' '*(allFull-numHashes) + "]"
 
        # figure out where to put the percentage, roughly centered
        percentPlace = (len(self.progBar) / 2) - len(str(percentDone))
        percentString = str(percentDone) + "%"
 
        # slice the percentage into the bar
        self.progBar = (self.progBar[0:percentPlace] + percentString
                        + self.progBar[percentPlace+len(percentString):])
 
    def __str__(self):
        return str(self.progBar)
 
def copy_with_prog(src_file, dest_file, overwrite = False, block_size = 512):
    if not overwrite:
        if os.path.isfile(dest_file):
            raise IOError("File exists, not overwriting")
   
    # Open src and dest files, get src file size
    src = open(src_file, "rb")
    dest = open(dest_file, "wb")
 
    src_size = os.stat(src_file).st_size
   
    # Set progress bar
    prgb = ProgressBar(totalWidth = 79, maxValue = src_size)
   
    # Start copying file
    cur_block_pos = 0 # a running total of current position
    while True:
        cur_block = src.read(block_size)
       
        # Update progress bar
        prgb.updateAmount(cur_block_pos)
        cur_block_pos += block_size
       
        sys.stdout.write(
            '\r%s\r' % str(prgb)
        )
       
        # If it's the end of file
        if not cur_block:
            # ..write new line to prevent messing up terminal
            sys.stderr.write('\n')
            break
        else:
            # ..if not, write the block and continue
            dest.write(cur_block)
    #end while
 
    # Close files
    src.close()
    dest.close()
 
    # Check output file is same size as input one!
    dest_size = os.stat(dest_file).st_size
 
    if dest_size != src_size:
        raise IOError(
            "New file-size does not match original (src: %s, dest: %s)" % (
            src_size, dest_size)
        )

def get_tracks():
    try:
        ProgID = "Foobar2000.Application.0.7"
        fb2k = win32com.client.Dispatch(ProgID)
    except: pass
   
    pls = fb2k.Playlists
   
    tracks = []
   
    for playlist in pls:
        if playlist.Name == playlist_to_sync:
            for track in playlist.GetTracks(): tracks.append(track.FormatTitle("%path%"))
   
    return tracks

def from_base_to_target(old_name):
    return old_name.replace(base_dir, target_dir).encode('latin-1')
   
def copy_track(base_name, target_name):
    if os.path.exists(base_name):
        if os.path.exists(target_name):
            if filecmp.cmp(base_name, target_name): print '%s skipped, already synced.' % (target_name.replace(target_dir,''))
            else:
                os.remove(target_name)
                #shutil.copy2(base_name, target_name)
                copy_with_prog(base_name, target_name)
                print '%s out-of-date, resynced.' % (target_name.replace(target_dir,''))
        else:
            if not os.path.exists(os.path.dirname(target_name)): os.makedirs(os.path.dirname(target_name))
            #shutil.copy2(base_name, target_name)
            copy_with_prog(base_name, target_name)
            print '%s synced.' % (target_name.replace(target_dir,''))

def main():
    tracks = get_tracks()
    for track in tracks:
        copy_track(track, from_base_to_target(track))

main()