# ContentsHandler.py
#
# Copyright (c) 2006 INdT (Instituto Nokia de Technologia)
#
# Author: Kenneth Rohde Christiansen <kenneth.christiansen@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
import urllib
import ID3
import M4ATags
import os
import sys
import gtk
import Utils
import PlaylistParserPLS
def remove_playlist_if_exists(playlist_name, device_path):
pathname = Utils.append_os_path(device_path, "Music/")
if os.path.exists(os.path.join(pathname, playlist_name + ".pls")):
## Code that removes existing playlist and deleted the files
## not in use by other playlists on the device.
##
## The algorithm is as follows:
##
## o read files from playlist - put in dictionary
## o delete playlist
## o read remote playlists again (not without that deleted playlist)
## o iterate through the playlists and look up in the hash and remove if found
##
## Now we will have a hash only consisting of the songs that are not in other
## playlists and that we can safely delete.
old_pls = open(os.path.join(pathname, playlist_name + ".pls"), "r")
## 1: read files from playlist - put in dictionary
songs_to_delete = {}
lines = old_pls.readlines()[:]
for line in lines:
if line.startswith ("File"):
song_location = pathname + line.split("=")[1].strip("\n")
if not os.path.exists(song_location):
continue
songs_to_delete[song_location] = True
## 2: delete playlist
os.remove (pathname + playlist_name + ".pls")
os.remove (pathname + playlist_name + ".m3u")
## 3: read remote playlists again (not without that deleted playlist)
playlists_left = PlaylistParserPLS.parse_playlists(device_path)
## 4: iterate through the playlists and look up in the hash and remove if found
for remote_playlist in playlists_left:
for song_location in remote_playlist[2]:
songs_to_delete[song_location] = False
for song_location in songs_to_delete.keys():
if songs_to_delete[song_location] == True:
os.remove(song_location)
album_dir = os.path.dirname(song_location)
artist_dir = os.path.split(album_dir)[0]
#print album_dir, artist_dir
# if empty
if not os.listdir(album_dir):
os.rmdir(album_dir)
if not os.listdir(artist_dir):
os.rmdir(artist_dir)
def get_song_info_as_text(song_path):
temp1 = song_path.rsplit("/", 1)[-1]
temp2 = temp1.rsplit(".", 1)
filename = temp2[0]
fileext = temp2[-1]
(artist, title, album, track_no, length) = get_song_info(song_path)
return "%s - %s" % (artist, title)
def add_playlist(parent, view, playlist, playlist_name, device_path):
print "Transferring playlist %s" % playlist_name
songnumber = 0
playlist_error = False;
pathname = Utils.append_os_path(device_path, "Music/")
if not os.path.exists(pathname):
os.makedirs (pathname)
remove_playlist_if_exists(playlist_name, device_path)
# create new playlist to fill:
playlist_data = []
for song in playlist[2]:
if view.model.stop_transfer == True:
break
location = song[0]
songnumber += 1
temp1 = location.rsplit("/", 1)[-1]
temp2 = temp1.rsplit(".", 1)
filename = temp2[0]
fileext = temp2[-1]
(artist, title, album, track_no, length) = get_song_info(location)
if track_no != -1:
remote_filename = u"%s/%s/%s. %s.%s" % (artist, album, track_no, title, fileext)
else:
remote_filename = u"%s/%s/%s.%s" % (artist, album, title, fileext)
remote_filename = Utils.to_os_path(remote_filename)
playlist_data_file = remote_filename
playlist_data_title = "%s - %s\n" % (artist, title)
playlist_data_length = length
rel_pathname = "Music/%s/%s" % (artist, album)
pathname = Utils.append_os_path(device_path, rel_pathname)
rel_filename = "Music/%s" % (remote_filename)
filename = Utils.append_os_path(device_path, rel_filename)
# Remove illegal chars
filename = filename.replace(":", "_")
if not os.path.exists(pathname):
os.makedirs (pathname)
try:
if os.path.getsize(location) / 1024 < Utils.freespace(device_path):
view.model.transferred_data += (os.path.getsize(location) / 1024) + 100
#gtk.gdk.threads_enter()
view.progress_dialog.set_theme_icon("audio-x-generic")
view.progress_dialog.set_text("Transferring <i>" + playlist_name + "</i>...")
#gtk.gdk.threads_leave()
print "Transfering %s (%s)" % (filename, location.split("/")[-1])
Utils.transfer_file(location, filename)
#gtk.gdk.threads_enter()
view.progress_dialog.set_progress(view.model.transferred_data, view.model.space_required)
#gtk.gdk.threads_leave()
view.refresh_freespace()
else:
print " WARNING: Ran out of space, ignoring current song (%s - %s)" % (artist, title)
songnumber -= 1
except IOError, e:
raise e
songnumber -= 1
break
playlist_data.append( (playlist_data_file, playlist_data_title, playlist_data_length) )
playlist_error = write_pls_playlist(playlist_name, playlist_data, device_path)
if not playlist_error:
playlist_error = write_m3u_playlist(playlist_name, playlist_data, device_path)
if playlist_error:
dialog = gtk.MessageDialog(parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
"Device ran out of space, playlist not updated")
dialog.set_title("No space left on device")
dialog.show()
def write_m3u_playlist(playlist_name, playlist_data, device_path):
pathname = Utils.append_os_path(device_path, "Music/")
playlist_error = False
try:
m3u = open(pathname + ".tmp-" + playlist_name + ".m3u", "w")
m3u.write("#EXTM3U\n")
songnumber = 1
for playlist_entry in playlist_data:
line = "#EXTINF:%s,%s\n" % ("-1", playlist_entry[1].strip())
line += "%s\n" % (playlist_entry[0].strip())
m3u.write(line)
m3u.flush()
songnumber += 1
m3u.close();
if not os.path.exists(pathname + ".tmp-" + playlist_name + ".m3u"):
playlist_error = True
if not Utils.BUFFER_SIZE < Utils.freespace (device_path):
playlist_error = True
if not os.path.getsize(pathname + ".tmp-" + playlist_name + ".m3u") < Utils.freespace (device_path):
playlist_error = True
Utils.transfer_file(pathname + ".tmp-" + playlist_name + ".m3u", pathname + playlist_name + ".m3u")
except IOError, e:
playlist_error = True
return playlist_error
def write_pls_playlist(playlist_name, playlist_data, device_path):
pathname = Utils.append_os_path(device_path, "Music/")
playlist_error = False
try:
pls = open(pathname + ".tmp-" + playlist_name + ".pls", "w")
pls.write("[playlist]\n")
songnumber = 1
for playlist_entry in playlist_data:
line = "File%s=%s\n" % (songnumber, playlist_entry[0].strip())
line += "Title%s=%s\n" % (songnumber, playlist_entry[1].strip())
line += "Length%s:%s\n" % (songnumber, playlist_entry[2])
pls.write(line)
pls.flush()
songnumber += 1
pls.write("NumberOfEntries=%s\nVersion=2" % songnumber)
pls.close();
if not os.path.exists(pathname + ".tmp-" + playlist_name + ".pls"):
playlist_error = True
if not Utils.BUFFER_SIZE < Utils.freespace (device_path):
playlist_error = True
if not os.path.getsize(pathname + ".tmp-" + playlist_name + ".pls") < Utils.freespace (device_path):
playlist_error = True
Utils.transfer_file(pathname + ".tmp-" + playlist_name + ".pls", pathname + playlist_name + ".pls")
except IOError, e:
playlist_error = True
return playlist_error
def remove_photoalbum_if_exists(photoalbum_name, device_path):
pathname = Utils.append_os_path(device_path, "Photos/")
if os.path.exists(pathname + photoalbum_name):
for current_file in os.listdir(pathname + photoalbum_name):
os.remove(pathname + photoalbum_name + "/" + current_file)
os.rmdir(pathname + photoalbum_name)
def add_photoalbum(parent, view, photoalbum, photoalbum_name, device_path):
songnumber = 0
pathname = Utils.append_os_path(device_path, "Photos/")
if not os.path.exists(pathname):
os.makedirs (pathname)
if os.path.exists(pathname + photoalbum_name):
remove_photoalbum_if_exists(photoalbum_name, device_path)
os.makedirs(pathname + photoalbum_name)
else:
os.makedirs(pathname + photoalbum_name)
for basepath in os.listdir(photoalbum[2]):
if view.model.stop_transfer == True:
break
current_path = os.path.join(photoalbum[2], basepath)
# F-Spot uses subdirs with the day number
# code should be made more general
if os.path.isdir(current_path):
for basepath in os.listdir(current_path):
if view.model.stop_transfer == True:
break
source = os.path.join(current_path, basepath)
target = os.path.join(pathname + photoalbum_name, basepath)
copy_photo(view, photoalbum_name, source, target, device_path)
else:
source = current_path
target = os.path.join(pathname + photoalbum_name, basepath)
copy_photo(view, photoalbum_name, source, target, device_path)
def copy_photo(view, photoalbum_name, source, target, device_path):
try:
if os.path.getsize(source) / 1024 < Utils.freespace(device_path):
view.model.transferred_data += (os.path.getsize(source) / 1024)
#gtk.gdk.threads_enter()
view.progress_dialog.set_theme_icon("image-x-generic")
view.progress_dialog.set_text("Transferring <i>" + photoalbum_name + "</i>...")
#gtk.gdk.threads_leave()
Utils.transfer_file(source, target)
#gtk.gdk.threads_enter()
view.progress_dialog.set_progress(view.model.transferred_data, view.model.space_required)
#gtk.gdk.threads_leave()
view.refresh_freespace()
else:
print " WARNING: Ran out of space, ignoring current photo (%s - %s)" % (artist, title)
except IOError, e:
print "error"
def get_song_info(location):
tracknumber = -1
length = -1
if location.endswith('m4a'):
info = M4ATags.M4ATags(location)
artist = info.get('Artist')
title = info.get('Title')
album = info.get('Album')
else:
try:
info = ID3.ID3(location)
try:
artist = Utils.unicodify(info.artist)
except AttributeError, e:
artist = u"Unknown"
try:
album = Utils.unicodify(info.album)
except AttributeError, e:
album = u"Unknown"
try:
title = Utils.unicodify(info.title)
except AttributeError, e:
title = Utils.unicodify(filename)
except ID3.InvalidTagError, e:
artist = u"Unknown"
album = u"Unknown"
title = u"Unknown"
if artist == "":
artist = u"Unknown"
if album == "":
album = u"Unknown"
if title == "":
title = u"Unknown"
return (artist, title, album, tracknumber, length)