summaryrefslogtreecommitdiffstats
path: root/bindings/python
diff options
context:
space:
mode:
authorNicholas Piper <nicholas@users.sourceforge.net>2007-01-14 20:29:38 +0000
committerNicholas Piper <nicholas@users.sourceforge.net>2007-01-14 20:29:38 +0000
commitce9e6169a993e38fb7b8edaed05c105fa4450e27 (patch)
treeeea0ee523668ffa655784c5044391aa5ac69672a /bindings/python
parenta2379f519b851a68db57ac4d7c0615564cabaa9c (diff)
downloadlibgpod-ce9e6169a993e38fb7b8edaed05c105fa4450e27.tar.gz
libgpod-ce9e6169a993e38fb7b8edaed05c105fa4450e27.tar.xz
libgpod-ce9e6169a993e38fb7b8edaed05c105fa4450e27.zip
Use gtkdoc documentation for python docstrings, topped up with docstrings by Todd Zullinger.
git-svn-id: https://gtkpod.svn.sf.net/svnroot/gtkpod/libgpod/trunk@1366 f01d2545-417e-4e96-918e-98f8d0dbbcb6
Diffstat (limited to 'bindings/python')
-rw-r--r--bindings/python/Makefile.am13
-rw-r--r--bindings/python/__init__.py8
-rw-r--r--bindings/python/gpod.i.in14
-rw-r--r--bindings/python/gtkpod.py23
-rw-r--r--bindings/python/ipod.py239
5 files changed, 279 insertions, 18 deletions
diff --git a/bindings/python/Makefile.am b/bindings/python/Makefile.am
index e65cf0d..0951303 100644
--- a/bindings/python/Makefile.am
+++ b/bindings/python/Makefile.am
@@ -4,6 +4,8 @@ EXTRA_DIST = \
README.in \
__init__.py \
gpod.i.in \
+ gpod_doc.i.in \
+ gtkdoc-to-swig.xsl \
gtkpod.py \
ipod.py
@@ -12,6 +14,7 @@ CLEANFILES = \
*.pyo \
_gpod.so \
gpod.py \
+ gpod_doc.i \
gpod_wrap.*
DISTCLEANFILES = \
@@ -28,7 +31,7 @@ README: README.in gpod.i
if HAVE_PYTHON
BUILT_SOURCES = gpod_wrap.c
-SWIG_INTERFACES = gpod.i
+SWIG_INTERFACES = gpod.i gpod_doc.i
LIBGPOD_CFLAGS += -fno-strict-aliasing
INCLUDES = -I$(top_srcdir)/src
@@ -41,6 +44,14 @@ _gpod_la_CPPFLAGS = $(PYTHON_INCLUDES) $(LIBGPOD_CFLAGS) $(INCLUDES)
_gpod_la_LDFLAGS = -module -avoid-version
_gpod_la_LIBADD = $(LIBGPOD_LIBS) $(top_builddir)/src/libgpod.la
+gpod_doc.i: $(srcdir)/gpod_doc.i.in $(srcdir)/gtkdoc-to-swig.xsl
+ cat $< > $@
+if ENABLE_GTK_DOC
+ -for xml in $(top_srcdir)/docs/reference/xml/*.xml; do \
+ xsltproc $(srcdir)/gtkdoc-to-swig.xsl $$xml; \
+ done >> $@
+endif
+
gpod_wrap.c: $(SWIG_INTERFACES) $(nodist_gpod_PYTHON)
$(SWIG) -python $(INCLUDES) -o $@ $<
diff --git a/bindings/python/__init__.py b/bindings/python/__init__.py
index 05a8159..b0640cf 100644
--- a/bindings/python/__init__.py
+++ b/bindings/python/__init__.py
@@ -1,6 +1,12 @@
+"""Manage tracks and playlists on an iPod.
+
+The gpod module allows you to add and remove tracks, create and edit
+playlists, and other iPod tasks.
+
+"""
+
from gpod import *
from ipod import *
__all__ = ["DatabaseException", "TrackException",
"Database","Track","Playlist"]
-
diff --git a/bindings/python/gpod.i.in b/bindings/python/gpod.i.in
index ef6e65a..a908e02 100644
--- a/bindings/python/gpod.i.in
+++ b/bindings/python/gpod.i.in
@@ -28,7 +28,17 @@ Please send any fixes, improvements or suggestions to
*/
-%module gpod
+%define DOCSTRING
+"This module gives access to an iPod's content. It provides an easy to
+use API to retrieve the list of files and playlists stored on an iPod,
+modify them, and save them back to the iPod.
+
+This module implements the libgpod C API as directly as possible in
+Python. See the main gpod module for a more traditional Python
+interface."
+%enddef
+
+%module(docstring=DOCSTRING) gpod
%{
#include "db-artwork-debug.h"
#include "db-artwork-parser.h"
@@ -163,6 +173,8 @@ PyObject* sw_get_playlists(Itdb_iTunesDB *itdb) {
%}
+%include "gpod_doc.i"
+
# be nicer to decode these utf8 strings into Unicode objects in the C
# layer. Here we are leaving it to the Python side, and just giving
# them utf8 encoded Strings.
diff --git a/bindings/python/gtkpod.py b/bindings/python/gtkpod.py
index 9eebf36..54a68dc 100644
--- a/bindings/python/gtkpod.py
+++ b/bindings/python/gtkpod.py
@@ -1,3 +1,5 @@
+"""Read and write Gtkpod extended info files."""
+
import sha
import os
import socket
@@ -9,12 +11,15 @@ import socket
hostname = socket.gethostname()
class ParseError(Exception):
+ """Exception for parse errors."""
pass
class SyncError(Exception):
+ """Exception for sync errors."""
pass
def sha1_hash(filename):
+ """Return an SHA1 hash on the first 16k of a file."""
import struct
# only hash the first 16k
hash_len = 4*4096
@@ -25,6 +30,15 @@ def sha1_hash(filename):
return hash.hexdigest()
def write(filename, db, itunesdb_file):
+ """Save extended info to a file.
+
+ db is a gpod.Database instance
+
+ Extended info is written for the iTunesDB specified in
+ itunesdb_file
+
+ """
+
file = open(filename, "w")
def write_pair(name, value):
@@ -53,6 +67,15 @@ def write(filename, db, itunesdb_file):
write_pair("id", "xxx")
def parse(filename, db, itunesdb_file=None):
+ """Load extended info from a file.
+
+ db is a gpod.Database instance
+
+ If itunesdb_file is set and it's hash is valid some expensive
+ checks are skipped.
+
+ """
+
tracks_by_id = {}
tracks_by_sha = {}
id = 0
diff --git a/bindings/python/ipod.py b/bindings/python/ipod.py
index a3ac1c0..4c0d14a 100644
--- a/bindings/python/ipod.py
+++ b/bindings/python/ipod.py
@@ -1,6 +1,8 @@
-"""Documentation for this module.
+"""Manage tracks and playlists on an iPod.
+
+You should normally just import the gpod module which will import the
+classes and methods provided here.
-More details.
"""
import gpod
@@ -10,13 +12,41 @@ import gtkpod
import os
class DatabaseException(RuntimeError):
+ """Exception for database errors."""
pass
class TrackException(RuntimeError):
+ """Exception for track errors."""
pass
class Database:
+ """An iTunes database.
+
+ A database contains playlists and tracks.
+
+ """
+
def __init__(self, mountpoint="/mnt/ipod", local=False, localdb=None):
+ """Create a Database object.
+
+ You can create the object from a mounted iPod or from a local
+ database file.
+
+ To use a mounted iPod:
+
+ db = gpod.Database('/mnt/ipod')
+
+ To use a local database file:
+
+ db = gpod.Database(localdb='/path/to/iTunesDB')
+
+ If you specify local=True then the default local database from
+ gtkpod will be used (~/.gtkpod/local_0.itdb):
+
+ db = gpod.Database(local=True)
+
+ """
+
if local or localdb:
if localdb:
self._itdb_file = localdb
@@ -37,7 +67,7 @@ class Database:
def __str__(self):
return self.__repr__()
-
+
def __repr__(self):
return "<Database Filename:%s Playlists:%s Tracks:%s>" % (
repr(self._itdb.filename),
@@ -45,12 +75,21 @@ class Database:
len(self))
def _load_gtkpod_extended_info(self):
+ """Read extended information from a gtkpod .ext file."""
itdbext_file = "%s.ext" % (self._itdb_file)
if os.path.exists(self._itdb_file) and os.path.exists(itdbext_file):
gtkpod.parse(itdbext_file, self, self._itdb_file)
def close(self):
+ """Save and close the database.
+
+ Note: If you are working with an iPod you should normally call
+ copy_delayed_files() prior to close() to ensure that newly
+ added or updated tracks are transferred to the iPod.
+
+ """
+
if not gpod.itdb_write_file(self._itdb, self._itdb_file, None):
raise DatabaseException("Unable to save iTunes database %s" % self)
itdbext_file = "%s.ext" % (self._itdb_file)
@@ -68,6 +107,11 @@ class Database:
return gpod.sw_get_list_len(self._itdb.tracks)
def import_file(self, filename):
+ """Import a file.
+
+ filename is added to the end of the master playlist.
+
+ """
track = Track(filename)
track.copy_to_ipod()
gpod.itdb_playlist_add_track(gpod.itdb_playlist_mpl(self._itdb),
@@ -77,9 +121,26 @@ class Database:
gpod.itdb_free(self._itdb)
def add(self, track, pos=-1):
+ """Add a track to a database.
+
+ If pos is set the track will be inserted at that position. By
+ default the track will be added at the end of the database.
+
+ """
+
gpod.itdb_track_add(self._itdb, track._track, pos)
def remove(self, item, harddisk=False, ipod=True):
+ """Remove a playlist or track from a database.
+
+ item is either a playlist or track object.
+
+ If harddisk is True the item will be removed from the the hard drive.
+
+ If ipod is True the item will be removed from the iPod.
+
+ """
+
if isinstance(item, Playlist):
if ipod or harddisk:
# remove all the tracks that were in this playlist
@@ -109,12 +170,15 @@ class Database:
raise DatabaseException("Unable to remove a %s from database" % type(item))
def get_master(self):
+ """Get the Master playlist."""
return Playlist(self,proxied_playlist=gpod.itdb_playlist_mpl(self._itdb))
def get_podcasts(self):
+ """Get the podcasts playlist."""
return Playlist(self,proxied_playlist=gpod.itdb_playlist_podcasts(self._itdb))
def get_playlists(self):
+ """Get all playlists."""
return _Playlists(self)
Master = property(get_master)
@@ -122,12 +186,25 @@ class Database:
Playlists= property(get_playlists)
def smart_update(self):
+ """Update all smart playlists."""
gpod.itdb_spl_update_all(self._itdb)
def new_Playlist(self,*args,**kwargs):
+ """Create a new Playlist.
+
+ See Playlist.__init__() for details.
+
+ """
+
return Playlist(self, *args,**kwargs)
def new_Track(self,**kwargs):
+ """Create a new Track.
+
+ See Track.__init__() for details.
+
+ """
+
track = Track(**kwargs)
self.add(track)
if kwargs.has_key('podcast') and kwargs['podcast'] == True:
@@ -137,6 +214,19 @@ class Database:
return track
def copy_delayed_files(self,callback=False):
+ """Copy files not marked as transferred to the iPod.
+
+ callback is an optional function that will be called for each
+ track that is copied. It will be passed the following
+ arguments:
+
+ database -> the database object
+ track -> the track being copied
+ iterator -> the current track number being copied
+ total -> the total tracks to be copied
+
+ """
+
if not gpod.itdb_get_mountpoint(self._itdb):
# we're not working with a real ipod.
return
@@ -157,10 +247,22 @@ class Database:
track.copy_to_ipod()
class Track:
+ """A track in an iTunes database.
+
+ A track contains information like the artist, title, album, etc.
+ It also contains data like the location on the iPod, whether there
+ is artwork stored, the track has lyrics, etc.
+
+ Information from a gtkpod extended info file (if one exists for
+ the iTunesDB) is also available in Track['userdata'].
+
+ The information is stored in a dictionary.
+
+ """
+
# 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",
@@ -179,6 +281,20 @@ class Track:
def __init__(self, filename=None, from_file=None,
proxied_track=None, podcast=False):
+ """Create a Track object.
+
+ If from_file or filename is set, the file specified will be
+ used to create the track.
+
+ If proxied_track is set, it is expected to be an Itdb_Track
+ object.
+
+ If podcast is True then the track will be setup as a Podcast,
+ unless proxied_track is set.
+
+ """
+
+ # XXX couldn't from_file and filename be merged?
if from_file:
filename = from_file
if filename:
@@ -218,9 +334,10 @@ class Track:
self._track = proxied_track
else:
self._track = gpod.itdb_track_new()
- self.set_podcast(podcast)
+ self.set_podcast(podcast)
def copy_to_ipod(self):
+ """Copy the track to the iPod."""
self['userdata']['md5_hash'] = gtkpod.sha1_hash(
self['userdata']['filename_locale'])
if not gpod.itdb_get_mountpoint(self._track.itdb):
@@ -234,21 +351,37 @@ class Track:
return True
def ipod_filename(self):
+ """Get the full path to the track on the iPod.
+
+ Note (from the libgpod documentation): This code works around
+ a problem on some systems and might return a filename with
+ different case than the original filename. Don't copy it back
+ to track unless you must.
+
+ """
+
return gpod.itdb_filename_on_ipod(self._track)
def set_podcast(self, value):
+ """Mark the track as a podcast.
+
+ If value is True flags appropriate for podcasts are set,
+ otherwise those flags are unset.
+
+ """
+
if value:
self['skip_when_shuffling'] = 0x01
self['remember_playback_position'] = 0x01
- self['flag4'] = 0x01 # Show Title/Album on the 'Now Playing' page
+ self['flag4'] = 0x01 # Show Title/Album on the 'Now Playing' page
else:
self['skip_when_shuffling'] = 0x00
self['remember_playback_position'] = 0x00
- self['flag4'] = 0x00 # Show Title/Album/Artist on the 'New Playing' page
+ self['flag4'] = 0x00 # Show Title/Album/Artist on the 'Now Playing' page
def __str__(self):
return self.__repr__()
-
+
def __repr__(self):
return "<Track Artist:%s Title:%s Album:%s>" % (
repr(self['artist']),
@@ -284,6 +417,8 @@ class Track:
else:
raise KeyError('No such key: %s' % item)
+# XXX would this be better as a public variable so that the docs would
+# list valid sort order values?
_playlist_sorting = {
1:'playlist',
2:'unknown2',
@@ -323,7 +458,7 @@ class _Playlists:
def __nonzero__(self):
return True
-
+
def __getitem__(self, index):
if type(index) == types.SliceType:
return [self[i] for i in xrange(*index.indices(len(self)))]
@@ -336,7 +471,7 @@ class _Playlists:
def __repr__(self):
return "<Playlists from %s>" % self._db
-
+
def __call__(self, id=None, number=None, name=None):
if ((id and (number or name)) or (number and name)):
raise ValueError("Only specify id, number OR name")
@@ -373,10 +508,30 @@ class _Playlists:
proxied_playlist=pl)
else:
raise KeyError("Playlist with number %s not found." % repr(number))
-
+
class Playlist:
+ """A playlist in an iTunes database."""
+
def __init__(self, parent_db, title="New Playlist",
smart=False, pos=-1, proxied_playlist=None):
+ """Create a playlist object.
+
+ parent_db is the database object to which the playlist
+ belongs.
+
+ title is a string that provides a name for the playlist.
+
+ If smart is true the playlist will be a smart playlist.
+
+ If pos is set the track will be inserted at that position. By
+ default the playlist will be added at the end of the database.
+
+ If proxied_playlist is set it is expected to be an
+ Itdb_Playlist object (as returned by gpod.sw_get_playlist() or
+ similar functions).
+
+ """
+
self._db = parent_db
if proxied_playlist:
self._pl = proxied_playlist
@@ -389,32 +544,71 @@ class Playlist:
gpod.itdb_playlist_add(self._db._itdb, self._pl, pos)
def smart_update(self):
+ """Update the content of the smart playlist."""
gpod.itdb_spl_update(self._pl)
def randomize(self):
+ """Randomizes the playlist."""
gpod.itdb_playlist_randomize(self._pl)
def get_name(self):
+ """Get the name of the playlist."""
return self._pl.name
+
def set_name(self, name):
+ """Set the name for the playlist."""
self._pl.name = name
+
def get_id(self):
+ """Get the id of the playlist."""
return self._pl.id
+
+ # XXX would this be more aptly named is_smart? If it was, I think
+ # the docstring could skip the explanation of the return value.
+ # (Same question for get_master and get_podcast.)
def get_smart(self):
+ """Check if the playlist is smart or not.
+
+ Returns True for a smart playlist, False for a regular
+ playlist.
+
+ """
+
if self._pl.is_spl == 1:
return True
return False
+
def get_master(self):
+ """Check if the playlist is the master playlist (MPL).
+
+ Returns True if the playlist is the MPL, False if not.
+
+ """
+
if gpod.itdb_playlist_is_mpl(self._pl) == 1:
return True
return False
+
def get_podcast(self):
+ """Check if the playlist is the podcasts playlist.
+
+ Returns True if the playlist is the podcasts playlist, False
+ if not.
+
+ """
+
if gpod.itdb_playlist_is_podcasts(self._pl) == 1:
return True
return False
+
def get_sort(self):
+ """Get the sort order for the playlist."""
return _playlist_sorting[self._pl.sortorder]
+
+ # XXX how does one find out what values are allowed for order without
+ # poking into this file? (See similar question @ _playlist_order)
def set_sort(self, order):
+ """Set the sort order for the playlist."""
order = order.lower()
for k, v in _playlist_sorting.items():
if v == order:
@@ -431,7 +625,7 @@ class Playlist:
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),
@@ -440,7 +634,7 @@ class Playlist:
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)))]
@@ -461,11 +655,26 @@ class Playlist:
return True
else:
return False
-
+
def add(self, track, pos=-1):
+ """Add a track to the playlist.
+
+ track is a track object to add.
+
+ If pos is set the track will be inserted at that position. By
+ default the track will be added at the end of the playlist.
+
+ """
+
gpod.itdb_playlist_add_track(self._pl, track._track, pos)
def remove(self, track):
+ """Remove a track from the playlist.
+
+ track is a track object to remove.
+
+ """
+
if self.__contains__(track):
gpod.itdb_playlist_remove_track(self._pl, track._track)
else: