While the N900 is AWESOME, it is really new and some things are just not quite perfect yet! One of these things is the playlist editor for the default Media Player. That is to say that there ISN'T a playlist editor!!
Now my computer at home has an many, many great playlist editors... So why not use them??
This program will sync playlists between your N900 and your home machine. (Actually it will sync playlists between any two machines)
This is a work in progress so nothing is guaranteed! Right now I am using my Wiki as a means of transferring code between different sites.
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
PlaylistSync by Bob Brandt (projects@brandt.ie).
Licensed under GPL version 3
Created on 10 March 2010
"""
import sys, os, optparse, re
version = "0.1"
class playlistsync(object):
'''
This class is used to sync a playlist from one device to another and all relevant files
'''
def __init__(self, sourceRoot, destMountPoint, destRoot = None, hierarchical = False, debug = False):
if os.path.isdir(sourceRoot):
self.__sourceRoot = sourceRoot
else:
raise IOError, "There is no such directory: '%s'" % sourceRoot
if os.path.isdir(destMountPoint):
self.__destMountPoint = destMountPoint
else:
raise IOError, "There is no such directory: '%s'" % destMountPoint
if not destRoot: destRoot = destMountPoint
self.__destRoot = destRoot
self.__hierarchical = bool(hierarchical)
self.__sourcePlaylists = self.__loadplaylists(self.__sourceRoot)
self.__destPlaylists = self.__loadplaylists(self.__destMountPoint)
self.__playlists = {}
for playlist in self.__sourcePlaylists.keys():
if playlist in self.__destPlaylists.keys():
self.__playlists[playlist] = {"source":self.__sourcePlaylists[playlist], "dest":self.__destPlaylists[playlist]}
playlistextenstions = property(lambda self: (".m3u",".pls") )
sourcePlaylists = property(lambda self: self.__sourcePlaylists )
destPlaylists = property(lambda self: self.__destPlaylists )
playlists = property(lambda self: self.__playlists )
def __loadplaylists(self, root):
playlists = {}
def checknames(junk, dirpath, namelist):
for name in namelist:
if os.path.splitext(name)[1].lower() in self.playlistextenstions:
playlists[os.path.splitext(name)[0].lower()] = os.path.join(dirpath,name)
os.path.walk(root, checknames, None)
return playlists
def __isInPlaylist(self, Playlist, PlayLists):
Playlist = str(Playlist).lower()
if Playlist in PlayLists.keys():
return {"name":Playlist, "path":PlayLists[Playlist] }
for tmp in PlayLists.keys():
if Playlist == str(PlayLists[tmp]).lower():
return {"name":Playlist, "path":PlayLists[tmp] }
return None
def addPlaylist(self, Playlist):
tmpPlaylist = str(Playlist).lower()
path = ""
if self.__isInPlaylist(tmpPlaylist, self.__playlists):
raise ValueError, "%s is already being synced." % Playlist
self.__convertplaylist(tmpPlaylist)
def __convertplaylist(self, Playlist):
if not self.__isInPlaylist(Playlist, self.__sourcePlaylists):
raise IOError, "There is no such playlist: '%s' underneath: %s" % ( Playlist , self.__sourceRoot )
f = open(self.__isInPlaylist(Playlist, self.__sourcePlaylists)["path"], "r")
playlisttext = f.read()
f.close()
pathseperator = os.path.normpath("/")
extension = os.path.splitext(self.__isInPlaylist(Playlist, self.__sourcePlaylists)["path"])[1].lower()
if extension == ".m3u" and self.__hierarchical:
search = re.compile("^" + self.__sourceRoot + pathseperator, re.IGNORECASE | re.MULTILINE | re.UNICODE)
replace = self.__destRoot + pathseperator
elif extension == ".m3u" and not self.__hierarchical:
search = re.compile("^" + self.__sourceRoot + ".*" + pathseperator, re.IGNORECASE | re.MULTILINE | re.UNICODE)
replace = self.__destRoot + pathseperator
elif extension == ".pls" and self.__hierarchical:
search = re.compile("=" + self.__sourceRoot + pathseperator, re.IGNORECASE | re.MULTILINE | re.UNICODE)
replace = "=" + self.__destRoot + pathseperator
elif extension == ".pls" and not self.__hierarchical:
search = re.compile("=" + self.__sourceRoot + ".*" + pathseperator, re.IGNORECASE | re.MULTILINE | re.UNICODE)
replace = "=" + self.__destRoot + pathseperator
else:
search = None
replace = None
if not (search and replace) :
raise ValueError, "Invalid Extension or Hierarchical Setting: %s, %s" % ( str(extension), str(self.__hierarchical) )
playlisttext = search.sub(replace, playlisttext)
if self.__hierarchical:
search = re.compile("^/home/BrandtB/Desktop/playlists-orig.*" + pathseperator, re.IGNORECASE | re.MULTILINE | re.UNICODE)
else:
search = re.compile("^/home/BrandtB/Desktop/playlists-orig" + pathseperator, re.IGNORECASE | re.MULTILINE | re.UNICODE)
replace = "/home/BrandtB/Desktop/playlists-dest" + pathseperator
filename = search.sub(replace, self.__isInPlaylist(Playlist, self.__sourcePlaylists)["path"])
f = open(filename, "w")
f.write(playlisttext)
f.flush()
f.close()
def removePlaylist(self, Playlist):
tmpPlaylist = str(Playlist).lower()
path = ""
if not self.__isInPlaylist(tmpPlaylist, self.__playlists):
raise ValueError, "%s is not being synced." % Playlist
os.remove(self.__destPlaylists[tmpPlaylist])
def listPlayLists(self):
allplaylists = {}
for playlist in self.__sourcePlaylists.keys():
allplaylists[playlist] = {"source":True, "dest":False}
for playlist in self.__destPlaylists.keys():
if allplaylists.has_key(playlist):
allplaylists[playlist]["dest"] = True
else:
allplaylists[playlist] = {"dest":True, "source":False}
length = 0
for playlist in allplaylists.keys():
if len(playlist) > length: length = len(playlist)
print "Playlists:"
for playlist in allplaylists.keys():
if allplaylists[playlist]["source"] and allplaylists[playlist]["dest"]:
print playlist + " " * (length - len(playlist)) + "\t" + "Sync"
else:
print playlist + " " * (length - len(playlist)) + "\t" + "Available"
print "\n"
def syncPlaylists(self):
pass
def cleanupPlaylists(self):
pass
if __name__ == '__main__':
usageText="usage: %prog [options] Source Destination RemotePath"
versionText = "\n" + " ".join(["GNU","%prog",str(version)]) + "\n"
versionText += "This program is distributed in the hope that it will be useful,\n"
versionText += "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
versionText += "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
versionText += "GNU General Public License for more details.\n\n"
versionText += "Originally written by Bob Brandt <projects@brandt.ie>.\n"
parser = optparse.OptionParser(usage=usageText, version=versionText)
parser.add_option("-a", "--add", dest="add", default="", type="string", help="Add playlist to sync list.")
parser.add_option("-r", "--remove", dest="remove", default="", type="string", help="Remove playlist from sync list.")
parser.add_option("-l", "--list", dest="list", action="store_true", default=False, help="List all playlists and whether or not they will be synced.")
parser.add_option("-c", "--cleanup", dest="cleanup", action="store_true", default=False, help="Remove all media not specified in the playlists.")
parser.add_option("-t", "--tree", dest="tree", action="store_true", default=False, help="Keep tree structure when copying files.")
parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False, help="To run the command in Debug mode.")
(options, args) = parser.parse_args()
# Source = args[0]
# Destination = args[1]
# RemotePath = args[2]
Source = "/home/BrandtB/Desktop/playlists-orig"
Destination = "/home/BrandtB/Desktop/playlists-dest"
RemotePath = "/home/user/MyDocs/Music"
options.tree = False
options.list = True
temp = playlistsync(Source, Destination, RemotePath, options.tree, options.debug)
if options.add:
temp.addPlaylist(options.add)
if options.remove:
temp.removePlaylist(options.remove)
if options.list:
temp.listPlayLists()
if not (options.add or options.remove or options.list or options.cleanup):
temp.syncPlaylists()
if options.cleanup:
temp.cleanupPlaylists()
sys.exit()