summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bindings/python/Makefile.am6
-rwxr-xr-xbindings/python/examples/add_song.py50
-rwxr-xr-xbindings/python/examples/playwith_ipod_api.py14
-rw-r--r--bindings/python/gpod.i46
-rw-r--r--bindings/python/ipod.py203
5 files changed, 266 insertions, 53 deletions
diff --git a/bindings/python/Makefile.am b/bindings/python/Makefile.am
index 1a6a396..381df7e 100644
--- a/bindings/python/Makefile.am
+++ b/bindings/python/Makefile.am
@@ -3,7 +3,8 @@ SUBDIRS = examples
EXTRA_DIST = \
gpod.i \
ipod.py \
- __init__.py
+ __init__.py \
+ gtkpod.py
CLEANFILES = \
*.py* \
@@ -34,9 +35,12 @@ install-pythonDATA: $(python_DATA)
$(INSTALL_PROGRAM) _gpod.so $(DESTDIR)$(pythondir)/gpod/_gpod.so
$(INSTALL_DATA) __init__.py $(DESTDIR)$(pythondir)/gpod/__init__.py
$(INSTALL_DATA) gpod.py $(DESTDIR)$(pythondir)/gpod/gpod.py
+ $(INSTALL_DATA) gtkpod.py $(DESTDIR)$(pythondir)/gpod/gtkpod.py
$(INSTALL_DATA) ipod.py $(DESTDIR)$(pythondir)/gpod/ipod.py
$(PYTHON) -c 'from py_compile import compile; compile("$(DESTDIR)$(pythondir)/gpod/gpod.py")'
$(PYTHON) -O -c 'from py_compile import compile; compile("$(DESTDIR)$(pythondir)/gpod/gpod.py")'
+ $(PYTHON) -c 'from py_compile import compile; compile("$(DESTDIR)$(pythondir)/gpod/gtkpod.py")'
+ $(PYTHON) -O -c 'from py_compile import compile; compile("$(DESTDIR)$(pythondir)/gpod/gtkpod.py")'
$(PYTHON) -c 'from py_compile import compile; compile("$(DESTDIR)$(pythondir)/gpod/ipod.py")'
$(PYTHON) -O -c 'from py_compile import compile; compile("$(DESTDIR)$(pythondir)/gpod/ipod.py")'
diff --git a/bindings/python/examples/add_song.py b/bindings/python/examples/add_song.py
index acca621..a913d1d 100755
--- a/bindings/python/examples/add_song.py
+++ b/bindings/python/examples/add_song.py
@@ -26,10 +26,10 @@ import os, os.path
import gpod
import sys
from optparse import OptionParser
-import eyeD3
import urlparse, urllib2
import tempfile
import shutil
+import eyeD3
def download(path):
print "Downloading %s" % path
@@ -70,11 +70,7 @@ parser.add_option("-p", "--podcast",
if len(args) == 0:
parser.error("Requires an mp3 to add.")
-itdb = gpod.itdb_parse(options.mountpoint, None)
-if not itdb:
- print "Failed to read iPod at %s" % options.mountpoint
- sys.exit(2)
-itdb.mountpoint = options.mountpoint
+db = gpod.Database(options.mountpoint)
for path in args:
deleteWhenDone = []
@@ -90,42 +86,28 @@ for path in args:
deleteWhenDone.pop()
continue
- track = gpod.itdb_track_new()
- audiofile = eyeD3.Mp3AudioFile(path)
- tag = audiofile.getTag()
-
- track.artist= str(tag.getArtist())
- track.album = str(tag.getAlbum())
- track.title = str(tag.getTitle())
- track.filetype = 'mp3'
- track.tracklen = audiofile.getPlayTime() * 1000 # important to add!, iPod uses ms.
-
+ track = gpod.Track(from_file=path, podcast=options.ispodcast)
+ db.add(track)
+
if options.ispodcast:
- track.flag2 = 0x01 # skip when shuffling
- track.flag3 = 0x01 # remember playback position
- track.flag4 = 0x01 # Show Title/Album on the 'Now Playing' page
- playlists = [gpod.itdb_playlist_podcasts(itdb)]
- print "Adding Podcast %s (Title: %s)" % (path,track.title)
+ playlists = [db.Podcasts]
+ print "Adding Podcast %s (%s)" % (path,track)
else:
- track.flag2 = 0x00 # do not skip when shuffling
- track.flag3 = 0x00 # do not remember playback position
- track.flag4 = 0x00 # Show Title/Album/Artist on the 'New Playing' page
- playlists = [gpod.itdb_playlist_mpl(itdb)]
- print "Adding Song %s (Title: %s)" % (path,track.title)
-
- gpod.itdb_track_add(itdb, track, -1)
+ playlists = [db.Master]
+ print "Adding Song %s (%s)" % (path,track)
for playlist in playlists:
- gpod.itdb_playlist_add_track(playlist, track, -1)
+ print " adding to playlist %s" % playlist
+ playlist.add(track)
+ print " added to playlist %s" % playlist
- if gpod.itdb_cp_track_to_ipod(track, path, None) == 1:
- print "Copied to %s" % gpod.itdb_filename_on_ipod(track)
- else:
- print "Copy failed"
+ print " added Song %s (%s)" % (path,track)
+
+ track.copy_to_ipod()
[os.unlink(f) for f in deleteWhenDone]
-gpod.itdb_write(itdb, None)
+db.close()
print "Saved db"
diff --git a/bindings/python/examples/playwith_ipod_api.py b/bindings/python/examples/playwith_ipod_api.py
index 544de5b..4780f73 100755
--- a/bindings/python/examples/playwith_ipod_api.py
+++ b/bindings/python/examples/playwith_ipod_api.py
@@ -1,14 +1,16 @@
#!/usr/bin/python
-import ipod
+import gpod
-db = ipod.Database()
+db = gpod.Database()
+
+print db
for track in db[4:20]:
print track
print track['title']
-filename = "/mp3/Blondie/No_Exit/Blondie_-_Maria.mp3"
-t = ipod.Track(from_file=filename)
-print t
-
+for pl in db.Playlists:
+ print pl
+ for track in pl:
+ print " ", track
diff --git a/bindings/python/gpod.i b/bindings/python/gpod.i
index 39de9c1..1d5f231 100644
--- a/bindings/python/gpod.i
+++ b/bindings/python/gpod.i
@@ -94,6 +94,48 @@ PyObject* sw_get_playlists(Itdb_iTunesDB *itdb) {
}
return list;
}
+
+ void sw__track_extra_destroy (PyObject *data) {
+ Py_XDECREF(data);
+ }
+
+ PyObject *sw__track_extra_duplicate (PyObject *data) {
+ if (data == Py_None) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ } else {
+ return PyDict_Copy(data);
+ }
+ }
+
+ PyObject *sw_set_track_userdata(Itdb_Track *track, PyObject *data) {
+ Py_INCREF(data);
+ if ((PyDict_Check(data)) || (data == Py_None)) {
+ if (track->userdata) {
+ Py_DECREF((PyObject *)track->userdata);
+ }
+ track->userdata = (gpointer) data;
+ track->userdata_duplicate = (ItdbUserDataDuplicateFunc)sw__track_extra_duplicate;
+ track->userdata_destroy = (ItdbUserDataDestroyFunc)sw__track_extra_destroy;
+ Py_INCREF(Py_None);
+ return Py_None;
+ } else {
+ PyErr_SetString(PyExc_TypeError, "userdata must be a Dictionary");
+ return NULL;
+ }
+ }
+
+ PyObject* sw_get_track_userdata(Itdb_Track *track) {
+ if (track->userdata) {
+ Py_INCREF((PyObject *)track->userdata);
+ return (PyObject *)track->userdata;
+ } else {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ }
+
+
%}
# be nicer to decode these utf8 strings into Unicode objects in the C
@@ -101,6 +143,7 @@ PyObject* sw_get_playlists(Itdb_iTunesDB *itdb) {
# them utf8 encoded Strings.
typedef char gchar;
+
%typemap(in) guint8 {
unsigned long ival;
ival = PyInt_AsUnsignedLongMask($input);
@@ -221,4 +264,7 @@ PyObject* sw_get_rule(GList *list, gint index);
PyObject* sw_get_list_len(GList *list);
PyObject* sw_get_playlists(Itdb_iTunesDB *itdb);
PyObject* sw_get_playlist_tracks(Itdb_Playlist *pl);
+PyObject* sw_set_track_userdata(Itdb_Track *track, PyObject *data);
+PyObject* sw_get_track_userdata(Itdb_Track *track);
+
%include "../../src/itdb.h"
diff --git a/bindings/python/ipod.py b/bindings/python/ipod.py
index 3e98310..020ed20 100644
--- a/bindings/python/ipod.py
+++ b/bindings/python/ipod.py
@@ -3,6 +3,8 @@
import gpod
import types
import eyeD3
+import gtkpod
+import os
class DatabaseException(RuntimeError):
pass
@@ -19,9 +21,29 @@ class Database:
else:
self._itdb.mountpoint = mountpoint
+ self._load_gtkpod_extended_info()
+
+ def __str__(self):
+ return self.__repr__()
+
+ def __repr__(self):
+ return "<Database Filename:%s Playlists:%s Tracks:%s>" % (
+ repr(self._itdb.filename),
+ gpod.sw_get_list_len(self._itdb.playlists),
+ len(self))
+
+ def _load_gtkpod_extended_info(self):
+ self._itdb_file = os.path.join(self._itdb.mountpoint,
+ "iPod_Control","iTunes","iTunesDB")
+ self._itdbext_file = os.path.join(self._itdb.mountpoint,
+ "iPod_Control","iTunes","iTunesDB.ext")
+ if os.path.exists(self._itdb_file) and os.path.exists(self._itdbext_file):
+ gtkpod.parse(self._itdbext_file, self, self._itdb_file)
+
def close(self):
if not gpod.itdb_write(self._itdb, None):
raise DatabaseException("Unable to save iTunes database at %s" % mountpoint)
+ gtkpod.write(self._itdbext_file, self, self._itdb_file)
def __getitem__(self, index):
if type(index) == types.SliceType:
@@ -39,9 +61,37 @@ class Database:
track.copy_to_ipod()
gpod.itdb_playlist_add_track(gpod.itdb_playlist_mpl(self._itdb),
track._track, -1)
-
+
+ def __del__(self):
+ gpod.itdb_free(self._itdb)
+
+ def add(self, track, pos=-1):
+ gpod.itdb_track_add(self._itdb, track._track, pos)
+
+ def get_master(self):
+ return Playlist(self,proxied_playlist=gpod.itdb_playlist_mpl(self._itdb))
+
+ def get_podcasts(self):
+ return Playlist(self,proxied_playlist=gpod.itdb_playlist_podcasts(self._itdb))
+
+ def get_playlists(self):
+ l = []
+ for playlist in gpod.sw_get_playlists(self._itdb):
+ l.append(Playlist(self,proxied_playlist=playlist))
+ return tuple(l)
+
+ Master = property(get_master)
+ Podcasts = property(get_podcasts)
+ Playlists= property(get_playlists)
+
+ def smart_update(self):
+ gpod.itdb_spl_update_all(self._itdb)
class Track:
+ # Note we don't free the underlying structure, as it's still used
+ # by the itdb.
+
+
_proxied_attributes = ("title","ipod_path","album","artist","genre","filetype",
"comment","category","composer","grouping","description",
"podcasturl","podcastrss","chapterdata","subtitle","id",
@@ -56,16 +106,15 @@ class Track:
"time_released","has_artwork","flag1","flag2","flag3","flag4",
"lyrics_flag","movie_flag","mark_unplayed","samplecount",
"chapterdata_raw","chapterdata_raw_length","artwork",
- "usertype","userdata","userdata_duplicate","userdata_destroy")
+ "usertype")
def __init__(self, from_file=None, proxied_track=None, podcast=False):
- self._ondiskfilename = None
if from_file:
- self._ondiskfilename = from_file
self._track = gpod.itdb_track_new()
- audiofile = eyeD3.Mp3AudioFile(self._ondiskfilename)
+ self['userdata'] = {'filename_locale': from_file,
+ 'transferred': 0}
+ audiofile = eyeD3.Mp3AudioFile(self['userdata']['filename_locale'])
tag = audiofile.getTag()
- print dir(tag)
for func, attrib in (('getArtist','artist'),
('getTitle','title'),
('getBPM','BPM'),
@@ -86,6 +135,15 @@ class Track:
if of is not None:
self['tracks'] = of
self['tracklen'] = audiofile.getPlayTime() * 1000
+ if podcast:
+ self['flag2'] = 0x01 # skip when shuffling
+ self['flag3'] = 0x01 # remember playback position
+ self['flag4'] = 0x01 # Show Title/Album on the 'Now Playing' page
+ else:
+ self['flag2'] = 0x00 # do not skip when shuffling
+ self['flag3'] = 0x00 # do not remember playback position
+ self['flag4'] = 0x00 # Show Title/Album/Artist on the 'New Playing' page
+
elif proxied_track:
self._track = proxied_track
else:
@@ -93,9 +151,17 @@ class Track:
self.set_podcast(podcast)
def copy_to_ipod(self):
- if gpod.itdb_cp_track_to_ipod(self._track, self._ondiskfilename, None) != 1:
- raise TrackException('Unable to copy %s to iPod as %s' % (self._ondiskfilename,
- self))
+ self['userdata']['md5_hash'] = gtkpod.sha1_hash(
+ self['userdata']['filename_locale'])
+ self['userdata']['transferred'] = 1
+ if gpod.itdb_cp_track_to_ipod(self._track,
+ self['userdata']['filename_locale'], None) != 1:
+ raise TrackException('Unable to copy %s to iPod as %s' % (
+ self['userdata']['filename_locale'],
+ self))
+
+ def ipod_filename(self):
+ return gpod.itdb_filename_on_ipod(self._track)
def set_podcast(self, value):
if value:
@@ -128,13 +194,18 @@ class Track:
return [(k, self[k]) for k in self._proxied_attributes]
def __getitem__(self, item):
- if item in self._proxied_attributes:
+ if item == "userdata":
+ return gpod.sw_get_track_userdata(self._track)
+ elif item in self._proxied_attributes:
return getattr(self._track, item)
else:
raise KeyError('No such key: %s' % item)
def __setitem__(self, item, value):
#print item, value
+ if item == "userdata":
+ gpod.sw_set_track_userdata(self._track, value)
+ return
if type(value) == types.UnicodeType:
value = value.encode()
if item in self._proxied_attributes:
@@ -142,6 +213,114 @@ class Track:
else:
raise KeyError('No such key: %s' % item)
-
+_playlist_sorting = {
+ 1:'playlist',
+ 2:'unknown2',
+ 3:'songtitle',
+ 4:'album',
+ 5:'artist',
+ 6:'bitrate',
+ 7:'genre',
+ 8:'kind',
+ 9:'modified',
+ 10:'track',
+ 11:'size',
+ 12:'time',
+ 13:'year',
+ 14:'rate',
+ 15:'comment',
+ 16:'added',
+ 17:'equalizer',
+ 18:'composer',
+ 19:'unknown19',
+ 20:'count',
+ 21:'last',
+ 22:'disc',
+ 23:'rating',
+ 24:'release',
+ 25:'BPM',
+ 26:'grouping',
+ 27:'category',
+ 28:'description'}
+
class Playlist:
- pass
+ def __init__(self, parent_db, proxied_playlist=None,
+ title="New Playlist", smart=False, pos=-1):
+ self._db = parent_db
+ if proxied_playlist:
+ self._pl = proxied_playlist
+ else:
+ if smart:
+ smart = 1
+ else:
+ smart = 0
+ self._pl = gpod.itdb_playlist_new(title, smart)
+ gpod.itdb_playlist_add(self._db._itdb, self._pl, pos)
+
+ def smart_update(self):
+ gpod.itdb_spl_update(self._pl)
+
+ def randomize(self):
+ gpod.itdb_playlist_randomize(self._pl)
+
+ def get_name(self):
+ return self._pl.name
+ def set_name(self, name):
+ self._pl.name = name
+ def get_id(self):
+ return self._pl.id
+ def get_smart(self):
+ if self._pl.is_spl == 1:
+ return True
+ return False
+ def get_master(self):
+ if gpod.itdb_playlist_is_mpl(self._pl) == 1:
+ return True
+ return False
+ def get_podcast(self):
+ if gpod.itdb_playlist_is_podcasts(self._pl) == 1:
+ return True
+ return False
+ def get_sort(self):
+ return _playlist_sorting[self._pl.sortorder]
+ def set_sort(self, order):
+ order = order.lower()
+ for k, v in _playlist_sorting.items():
+ if v == order:
+ self._pl.sortorder = v
+ return
+ return ValueError("Unknown playlist sorting '%s'" % order)
+
+ name = property(get_name, set_name)
+ id = property(get_id)
+ smart = property(get_smart)
+ master = property(get_master)
+ podcast= property(get_podcast)
+ order = property(get_sort, set_sort)
+
+ def __str__(self):
+ return self.__repr__()
+
+ def __repr__(self):
+ return "<Playlist Title:%s Sort:%s Smart:%s Master:%s Podcast:%s Tracks:%d>" % (
+ repr(self.name),
+ repr(self.order),
+ repr(self.smart),
+ repr(self.master),
+ repr(self.podcast),
+ len(self))
+
+ def __getitem__(self, index):
+ if type(index) == types.SliceType:
+ return [self[i] for i in xrange(*index.indices(len(self)))]
+ else:
+ if index < 0:
+ index += len(self)
+ return Track(proxied_track=gpod.sw_get_track(self._pl.members, index))
+
+ def __len__(self):
+ #return self._pl.num # Always 0 ?
+ return gpod.sw_get_list_len(self._pl.members)
+
+ def add(self, track, pos=-1):
+ gpod.itdb_playlist_add_track(self._pl, track._track, pos)