Merge lp:~mblayman/entertainer/pythonize-musiclib into lp:entertainer/future

Proposed by Matt Layman
Status: Merged
Merge reported by: Matt Layman
Merged at revision: not available
Proposed branch: lp:~mblayman/entertainer/pythonize-musiclib
Merge into: lp:entertainer/future
Prerequisite: lp:~mblayman/entertainer/clean-up-1.0
Diff against target: 1694 lines (+309/-837)
13 files modified
entertainerlib/client/medialibrary/music.py (+149/-516)
entertainerlib/client/medialibrary/playable.py (+6/-25)
entertainerlib/gui/screens/album.py (+11/-12)
entertainerlib/gui/screens/artist.py (+1/-1)
entertainerlib/gui/screens/audio_play.py (+2/-2)
entertainerlib/gui/screens/disc.py (+5/-5)
entertainerlib/gui/screens/music.py (+1/-1)
entertainerlib/gui/tabs/albums_tab.py (+4/-4)
entertainerlib/gui/tabs/lyrics_tab.py (+1/-1)
entertainerlib/gui/tabs/playing_tab.py (+16/-19)
entertainerlib/gui/tabs/tracks_tab.py (+5/-7)
entertainerlib/tests/mock.py (+1/-15)
entertainerlib/tests/test_music.py (+107/-229)
To merge this branch: bzr merge lp:~mblayman/entertainer/pythonize-musiclib
Reviewer Review Type Date Requested Status
Matt Layman Approve
Review via email: mp+16600@code.launchpad.net

Commit message

Made the music library code follow more pythonic behavior.

To post a comment you must log in.
Revision history for this message
Matt Layman (mblayman) wrote :

This is a chunk of work to eliminate some of the Java-ness of the MusicLibrary classes. It cleans up a lot of getters and setters and makes the code look much cleaner.

Here is the diffstat for the curious:

 client/client.py | 2
 client/medialibrary/music.py | 678 ++++++++--------------------------------
 client/medialibrary/playable.py | 31 -
 gui/screens/album.py | 23 -
 gui/screens/artist.py | 2
 gui/screens/audio_play.py | 4
 gui/screens/disc.py | 10
 gui/screens/music.py | 2
 gui/tabs/albums_tab.py | 8
 gui/tabs/lyrics_tab.py | 2
 gui/tabs/playing_tab.py | 35 --
 gui/tabs/tracks_tab.py | 12
 tests/mock.py | 18 -
 tests/test_music.py | 346 ++++++--------------
 14 files changed, 315 insertions(+), 858 deletions(-)

452. By Matt Layman

Merged from parent and resolved conflicts.

Revision history for this message
Matt Layman (mblayman) wrote :

Discussed on IRC that I can use my discretion for merges for the time being to bootstrap some contribution.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'entertainerlib/client/medialibrary/music.py'
2--- entertainerlib/client/medialibrary/music.py 2009-09-08 02:40:16 +0000
3+++ entertainerlib/client/medialibrary/music.py 2010-01-03 23:00:31 +0000
4@@ -2,19 +2,15 @@
5 '''Music Library - Interface for Entertainer music library cache'''
6
7 import os
8+
9 import CDDB, DiscID
10 from pysqlite2 import dbapi2 as sqlite
11
12+from entertainerlib.client.medialibrary.playable import Playable
13 from entertainerlib.configuration import Configuration
14-
15-from entertainerlib.client.medialibrary.playable import Playable
16 from entertainerlib.download import LyricsDownloader
17
18
19-class MusicLibraryException(Exception):
20- '''Exception to handle MusicLibrary issues'''
21- pass
22-
23 class AlbumHasNoTracks(Exception):
24 '''Exception to handle Albums with no tracks'''
25 pass
26@@ -23,12 +19,8 @@
27 '''Exception to handle track errors'''
28 pass
29
30-class TrackRatingOutOfRange(Exception):
31- '''Exception to handle track rating problems'''
32- pass
33-
34 class MusicLibrary:
35- """Interface for Entertainer's music cache."""
36+ '''Interface for Entertainer's music cache.'''
37
38 def __init__(self):
39 self.config = Configuration()
40@@ -39,171 +31,66 @@
41 self.cursor = self.db_connection.cursor()
42
43 def __del__(self):
44- """
45- Close connection when this object is destroyed
46- """
47+ '''Close the connection when this object is destroyed.'''
48 self.db_connection.close()
49
50 def get_compact_disc_information(self):
51- """
52- Get the CompactDisc object of the current disc in drive. This
53- method queries CD information from Internet database (CDDB).
54- @return: CompactDisc
55- """
56+ '''Get the CompactDisc object of the current disc in drive by querying
57+ CD information from an Internet database (CDDB).'''
58 cdrom = DiscID.open()
59 disc_id = DiscID.disc_id(cdrom)
60 return CompactDisc(disc_id)
61
62- def get_playlists(self):
63- """
64- Get list of playlist names.
65- @return: String list
66- """
67- self.cursor.execute("SELECT title FROM playlist")
68- playlists = []
69- for row in self.cursor:
70- playlists.append(row[0])
71- return playlists
72-
73- def get_num_of_tracks_on_playlist(self, name):
74- """
75- Get list of playlist names.
76- @param name: Playlist name (see: get_playlists)
77- @return: Integer (number of tracks)
78- """
79- self.cursor.execute("""SELECT COUNT(filename)
80- FROM playlist_relation
81- WHERE title=:name""", { "name" : name })
82- result = self.cursor.fetchall()
83- return result[0][0]
84-
85- def get_tracks_on_playlist(self, name):
86- """
87- Retrun list of tracks on given playlist.
88- @param name: Playlist name
89- @return: List of Track objects
90- """
91- self.cursor.execute(
92- """SELECT filename, title, tracknumber, artist, album,
93- genre, bitrate, year, rating, length, comment, lyrics
94- FROM track, playlist_relation
95- WHERE playlist_relation.title=:name
96- AND playlist_relation.filename=track.filename
97- ORDER BY title""", { "name" : name })
98- tracks = []
99- for row in self.cursor:
100- tracks.append(Track(row[0], row[1], row[2], row[3], row[4],
101- row[5], row[6], row[7], row[8], row[9],
102- row[10], row[11]))
103- return tracks
104-
105 def get_all_artists(self):
106- """
107- Get a list of all artists.
108- @return: list of Strings
109- """
110- self.cursor.execute(
111- "SELECT DISTINCT artist FROM track ORDER BY artist")
112+ '''Return all the artists names.'''
113+ self.cursor.execute("SELECT DISTINCT artist FROM track ORDER BY artist")
114
115 artists = []
116 for row in self.cursor:
117 artists.append(row[0])
118 return artists
119
120- def get_all_tracks(self):
121- """
122- Get all tracks in music cache.
123- @return: List of Track objects
124- """
125- self.cursor.execute(
126- """SELECT filename, title, tracknumber, artist, album,
127- genre, bitrate, year, rating, length, comment, lyrics
128- FROM track
129- ORDER BY title""")
130- tracks = []
131- for row in self.cursor:
132- tracks.append(Track(row[0], row[1], row[2], row[3], row[4],
133- row[5], row[6], row[7], row[8], row[9],
134- row[10], row[11]))
135- return tracks
136-
137- def get_tracks_by_genre(self, genre):
138- """
139- Get albums from music cache by genre.
140- @param genre: Genre (string)
141- @return: List of Track objects
142- """
143- self.cursor.execute(
144- """SELECT filename, title, tracknumber, artist, album,
145- genre, bitrate, year, rating, length, comment, lyrics
146- FROM track
147- WHERE genre=:genre
148- ORDER BY title""", { "genre" : genre})
149- tracks = []
150- for row in self.cursor:
151- tracks.append(Track(row[0], row[1], row[2], row[3], row[4],
152- row[5], row[6], row[7], row[8], row[9],
153- row[10], row[11]))
154- return tracks
155-
156 def get_tracks_by_artist(self, artist):
157- """
158- Get tracks from music cache by artist.
159- @param artist: Name of the artist (string)
160- """
161+ '''Return track objects from the given artist.'''
162 self.cursor.execute(
163 """SELECT filename, title, tracknumber, artist, album,
164- genre, bitrate, year, rating, length, comment, lyrics
165+ year, length, lyrics
166 FROM track
167 WHERE artist=:artist
168 ORDER BY title""", { "artist" : artist})
169 tracks = []
170 for row in self.cursor:
171- tracks.append(Track(row[0], row[1], row[2], row[3], row[4],
172- row[5], row[6], row[7], row[8], row[9],
173- row[10], row[11]))
174+ tracks.append(Track(row[0], row[1], row[2], row[3], row[4], row[5],
175+ row[6], row[7], self._create_album))
176 return tracks
177
178 def get_all_albums(self):
179- """
180- Get all albums in music cache.
181- @return: List of Album objects
182- """
183+ '''Return all the albums in the library.'''
184 self.cursor.execute("SELECT DISTINCT album FROM track ORDER BY album")
185 albums = []
186 for row in self.cursor.fetchall():
187- albums.append(Album(row[0], self.cursor))
188+ albums.append(self._create_album(row[0]))
189 return albums
190
191 def get_albums_by_artist(self, artist):
192- """
193- Get albums from music cache by artist.
194- @param artist: Artist name (string)
195- @return: List of Album objects
196- """
197+ '''Return all albums from the given artist.'''
198 self.cursor.execute("""SELECT DISTINCT album
199 FROM track
200 WHERE artist=:artist
201 ORDER BY album""", { "artist" : artist})
202 albums = []
203 for row in self.cursor.fetchall():
204- albums.append(Album(row[0], self.cursor))
205+ albums.append(self._create_album(row[0]))
206 return albums
207
208 def number_of_tracks(self):
209- """
210- Get the number of tracks in music cache.
211- @return: Integer (number of tracks)
212- """
213+ '''Return the number of tracks.'''
214 self.cursor.execute("SELECT COUNT(filename) FROM track")
215 result = self.cursor.fetchall()
216 return result[0][0]
217
218 def number_of_tracks_by_artist(self, artist):
219- """
220- Get the number of tracks made by given artist
221- @param artist: Artist name (string)
222- """
223+ '''Return the number of tracks from the given artist.'''
224 self.cursor.execute("""SELECT COUNT(filename)
225 FROM track
226 WHERE artist=:artist""", { "artist" : artist})
227@@ -211,10 +98,7 @@
228 return result[0][0]
229
230 def number_of_albums_by_artist(self, artist):
231- """
232- Get the number of albums made by given artist
233- @param artist: Artist name (string)
234- """
235+ '''Return the number of albums from the given artist.'''
236 self.cursor.execute("""SELECT DISTINCT album
237 FROM track
238 WHERE artist=:artist
239@@ -223,363 +107,173 @@
240 return len(result)
241
242 def save_lyrics(self, track, lyrics):
243- """
244- Save lyrics to database.
245- @param track: Track which lyrics we are saving
246- @param lyrics: Lyrics string
247- """
248- track.set_lyrics(lyrics) # Be certain to save for this track instance,
249- # not just persistent storage
250- self.cursor.execute("""UPDATE track
251- SET lyrics=:lyrics
252- WHERE filename=:fn""", {
253- "lyrics" : lyrics,
254- "fn" : track.get_filename()})
255+ '''Save the lyrics to the database.'''
256+ track.lyrics = lyrics # Be certain to save for this track instance,
257+ # not just persistent storage
258+ self.cursor.execute(
259+ """UPDATE track SET lyrics=:lyrics WHERE filename=:fn""",
260+ { "lyrics" : lyrics, "fn" : track.filename})
261 self.db_connection.commit()
262
263-
264-class Album(object):
265- """
266- Objects from this class represents albums. Album contains tracks.
267- """
268-
269- def __init__(self, title, cursor=None):
270- """Initialize album (Fetch data from music cache)"""
271- self.config = Configuration()
272- self.tracks = []
273- self.title = title
274- self.total_length = 0
275-
276- if not cursor:
277- self.db_connection = sqlite.connect(self.config.MUSIC_DB)
278- self.cursor = self.db_connection.cursor()
279- else:
280- self.cursor = cursor
281+ def _create_album(self, title):
282+ '''Factory method to create an Album object.'''
283 self.cursor.execute(
284 """SELECT filename, title, tracknumber, artist, album,
285- genre, bitrate, year, rating, length, comment, lyrics
286+ year, length, lyrics
287 FROM track
288 WHERE album=:title
289 ORDER BY tracknumber""", {"title" : title})
290
291+ length = 0
292+ tracks = []
293 for row in self.cursor:
294- self.tracks.append(Track(row[0], row[1], row[2], row[3], self,
295- row[5], row[6], row[7], row[8], row[9],
296- row[10], row[11]))
297- self.total_length += int(row[9])
298- if len(self.tracks) == 0:
299+ tracks.append(Track(row[0], row[1], row[2], row[3], row[4], row[5],
300+ row[6], row[7], self._create_album))
301+ length += int(row[6])
302+ if len(tracks) == 0:
303 raise AlbumHasNoTracks()
304- self.artist = self.tracks[0].artist
305-
306- def __str__(self):
307- return self.title
308-
309- def get_title(self):
310- """
311- Get title ot the album.
312- @return: String
313- """
314- return self.title
315+
316+ artist = tracks[0].artist
317+
318+ for track in tracks:
319+ if track.artist != artist:
320+ artist = _("Various")
321+ break
322+
323+ encoded_path = artist + ' - ' + title
324+ encoded_path = encoded_path.encode('base64')
325+ album_art_url = os.path.join(self.config.ALBUM_ART_DIR,
326+ encoded_path + '.jpg')
327+
328+ return Album(artist, title, length, tracks[0].year, album_art_url,
329+ tracks)
330+
331+
332+class Album(object):
333+ '''Representation of music album which contains tracks.'''
334+
335+ def __init__(self, artist, title, length, year, album_art_url, tracks):
336+ self.config = Configuration()
337+ self.artist = artist
338+ self.title = title
339+ self.length = length
340+ self.year = year
341+ self.album_art_url = album_art_url
342+ self.tracks = tracks
343
344 def has_album_art(self):
345- """
346- Has this album album art graphics.
347- @return: Boolean
348- """
349- artist_album = self.artist + " - " + self.title
350- artist_album = artist_album.encode("base64")
351- album_art = os.path.join(self.config.ALBUM_ART_DIR,
352- artist_album + ".jpg")
353-
354- return os.path.exists(album_art)
355-
356- def get_album_art_url(self):
357- """
358- Get absolute path of album art file or raises Exception.
359- @return: String
360- """
361- artist_album = self.artist + " - " + self.title
362- artist_album = artist_album.encode("base64")
363- return os.path.join(self.config.ALBUM_ART_DIR, artist_album + ".jpg")
364-
365- def get_tracks(self):
366- """
367- Get tracks from this Album.
368- @return: List of Track object
369- """
370- return self.tracks
371-
372- def get_number_of_tracks(self):
373- """
374- Get the number of tracks on this album.
375- @return: Integer
376- """
377- return len(self.tracks)
378-
379- def get_year(self):
380- """
381- Get release year of this album. This is taken from the first track.
382- @return: String
383- """
384- return self.tracks[0].get_year()
385-
386- def get_genre(self):
387- """
388- Get genre of this album. This is taken from the first track.
389- @return: String
390- """
391- return self.tracks[0].get_genre()
392-
393- def get_artist(self):
394- """
395- Get the artist of this album. This is taken from the first track.
396- @return: String
397- """
398- artist = self.tracks[0].get_artist()
399- text = self.tracks[0].get_artist()
400-
401- for track in self.get_tracks():
402- if track.get_artist() != artist:
403- text = _("Various")
404- break
405- else:
406- artist = track.get_artist()
407-
408- return text
409-
410- def get_total_length(self):
411- """
412- Get total length of the album.
413- @return: Integer (Length in seconds)
414- """
415- return self.total_length
416+ '''Test if the album has album art.'''
417+ return os.path.exists(self.album_art_url)
418
419
420 class Track(Playable):
421- """
422- Track is a Playable object that represents one song in Music library.
423- """
424-
425- def __init__(self, filename, title, tracknumber, artist, album, genre,
426- bitrate, year, rating, length, comment, lyrics):
427- """
428- Initialize track. Notice that album parameter can be either
429- Album name as string or Album object! getAlbum() returns always object.
430- """
431-
432- # Check that these four fields are integers
433- for field in [tracknumber, year, rating, length]:
434- if rating == None: # By default, there is no rating for a track
435- continue
436+ '''Representation of a music track.'''
437+
438+ def __init__(self, filename, title, number, artist, album, year, length,
439+ lyrics, create_album_callback):
440+
441+ # Check that these fields are integers
442+ for field in [number, year, length]:
443 if type(field) != int:
444 raise TrackTypeError("%s is not an integer" % field)
445
446- # Check that the rating is in range 1-5
447- if rating != None and rating not in range(1, 6):
448- raise TrackRatingOutOfRange()
449-
450- self.filename = str(filename) # Filename of the track
451- self.title = str(title) # Track title
452- self.tracknumber = tracknumber # Number of the track
453- self.artist = str(artist) # Artist of the track
454- if isinstance(album, Album):
455- # Album that contains this track (str OR Album object!)
456- self.album = album
457- else:
458- self.album = str(album)
459- self.genre = str(genre) # Genre of the track
460- self.bitrate = str(bitrate) # Bitrate of the track
461- self.year = year # Release year
462- self.rating = rating # Rating of the track (1-5)
463+ self.filename = filename
464+ self.title = title
465+ self.number = number
466+ self.artist = artist
467+ self._album = album
468+ self.year = year
469 # Length of the track in seconds (example 240)
470 self.length = length
471- self.comment = str(comment) # Comment about this track
472- self.lyrics = str(lyrics) # Lyrics of the track
473-
474- def get_filename(self):
475- """
476- Get filename of the track
477- @return: String
478- """
479- return self.filename
480-
481- def get_title(self):
482- """
483- Get title ot the track__MUSIC_DB
484- @return: String
485- """
486- return self.title
487-
488- def get_tracknumber(self):
489- """
490- Get track number of the track
491- @return: Interger
492- """
493- return self.tracknumber
494-
495- def get_artist(self):
496- """
497- Get artist of the track
498- @return: String
499- """
500- return self.artist
501-
502- def get_album(self, cursor=False):
503- """
504- Get album that contains this Track.
505- @return: Album object
506- """
507- if not isinstance(self.album, Album):
508- album = Album(self.album, cursor)
509- self.album = album
510+ self._lyrics = lyrics
511+ self.create_album_callback = create_album_callback
512+
513+ @property
514+ def album(self):
515+ '''Get the album object and use the callback if it's not an instance.'''
516+ if not isinstance(self._album, Album):
517+ album = self.create_album_callback(self._album)
518+ self._album = album
519 return album
520 else:
521- return self.album
522-
523- def get_album_art_url(self, cursor=False):
524- """
525- Get album art URL of this Track
526- @return: String (or None if there is no album art for this track)
527- """
528- self.get_album(cursor) # Need to have an album object
529-
530- if self.album.has_album_art():
531- return self.album.get_album_art_url()
532- else:
533- return None
534-
535- def get_genre(self):
536- """
537- Get genre of the track
538- @return: String
539- """
540- return self.genre
541-
542- def get_bitrate(self):
543- """
544- Get bitrate of the track
545- @return: String
546- """
547- return self.bitrate
548-
549- def get_year(self):
550- """
551- Get year of the track
552- @return: Integer
553- """
554- return self.year
555-
556- def get_rating(self):
557- """
558- Return rating of the track
559- @return: Integer
560- """
561- return self.rating
562-
563- def get_length(self):
564- """
565- Get length of the track in seconds
566- @return: Integer
567- """
568- return self.length
569-
570- def get_length_string(self):
571- """
572- Get length of the track as human readable string. Example: 02:46
573- @return: Timestamp as string
574- """
575+ return self._album
576+
577+ @property
578+ def length_string(self):
579+ '''Return length as a human readable string. Example: 2:46.'''
580 return str(self.length / 60) + ":" + str.zfill(str(self.length % 60), 2)
581
582- def get_comment(self):
583- """
584- Get comment of the track
585- @return: String
586- """
587- return self.comment
588+ def _get_lyrics(self):
589+ '''Get the track lyrics.'''
590+ return self._lyrics
591+
592+ def _set_lyrics(self, lyrics):
593+ '''Set the lyrics. LyricsDownloader returns None if it can't find
594+ anything so ensure that using lyrics is always a string.'''
595+ if lyrics is None:
596+ self._lyrics = ''
597+ else:
598+ self._lyrics = lyrics
599+
600+ lyrics = property(_get_lyrics, _set_lyrics)
601
602 def has_lyrics(self):
603- """
604- Is there lyrics for this track in music cache
605- @return: Boolean, true if lyrics exists
606- """
607+ '''Test if the track has any lyrics.'''
608 if self.lyrics == "":
609 return False
610 else:
611 return True
612
613 def fetch_lyrics(self, callback=False):
614- """
615- Fetch lyrics from the Internet using the LyricsDownloader, use
616+ '''Fetch lyrics from the Internet using the LyricsDownloader, use a
617 callback function to indicate completion since LyricsDownloader is
618 asynchronous. Callback function must take lyrics as only input
619- parameter.
620- @param callback: Function called when fetch_lyrics completes
621- """
622+ parameter.'''
623 if not callback:
624 callback = self.set_lyrics
625 if not self.has_lyrics():
626- self.ld = LyricsDownloader(self.get_title(), self.get_artist(),
627- callback)
628+ self.ld = LyricsDownloader(self.title, self.artist, callback)
629 self.ld.search()
630
631- def get_lyrics(self):
632- """
633- Get lyrics of the track
634- @return: String
635- """
636- return self.lyrics
637-
638- def set_lyrics(self, lyrics):
639- """
640- Set lyrics to track object instance, use save lyrics for persistent
641- storage of lyrics
642- @param lyrics: Lyrics of track
643- """
644- if lyrics is None:
645- self.lyrics = ""
646+ def get_album_art_url(self):
647+ '''Get the album art location if it exists.'''
648+ if self.album.has_album_art():
649+ return self.album.album_art_url
650 else:
651- self.lyrics = lyrics
652+ return None
653
654 # Implement playable interface
655+ def get_title(self):
656+ '''Get the title.'''
657+ return self.title
658+
659 def get_type(self):
660- """
661- Track is an audio stream.
662- @return: Integer
663- """
664+ '''Get the type.'''
665 return Playable.AUDIO_STREAM
666
667 def get_uri(self):
668- """
669- Get URI of the audio file
670- @return: String
671- """
672+ '''Get the URI.'''
673 return "file://" + self.filename
674
675
676 class CompactDisc(object):
677- """
678- CompactDisc
679-
680- Represents one CD. This object is a simple container that includes CD
681- artist, album name and track listing.
682- """
683+ '''Representation of a CD.'''
684
685 def __init__(self, disc_id):
686- """
687- Create a new CompactDisc object. Initialization queries track
688- information from the Internet.
689+ # Assume that no results will be found.
690+ self.artist = _("Unknown artist")
691+ self.length = 0
692+ self.title = _("Unknown title")
693+ self.tracks = []
694
695- @param disc_id: DiscID
696- """
697- self.tracks = []
698 try:
699 (query_status, query_info) = CDDB.query(disc_id)
700 except IOError:
701 # Set query_status to 0 to act like an unknown CD.
702 query_status = 0
703
704- #See CDDB documentation for more information.
705- #http://cddb-py.sourceforge.net/CDDB/README
706+ # See CDDB documentation for more information.
707+ # http://cddb-py.sourceforge.net/CDDB/README
708 # query_info variable's type depends on query_status
709
710 if query_status == 200:
711@@ -592,11 +286,9 @@
712 self._get_information_from_result_element(query_info[0], disc_id)
713 else:
714 # No metadata found for this disc
715- self.album = _("Unknown title")
716- self.artist = _("Unknown artist")
717 for i in range(disc_id[1]):
718 self.tracks.append(CompactDiscTrack(i + 1,
719- _("Unknown track %(num)s.") % {'num': str(i+1)}, 0))
720+ _("Unknown track %(num)s.") % {'num': str(i + 1)}, 0))
721
722 def _get_information_from_result_element(self, result, disc_id):
723 """Catch any information from a CDDB result dictionnary"""
724@@ -605,105 +297,46 @@
725 disc = unicode(result['disc_id'], "iso-8859-1")
726
727 self.artist = title[:title.index(' / ')]
728- self.album = title[title.index(' / ') + 3:]
729+ self.title = title[title.index(' / ') + 3:]
730
731 # Get track titles
732 info = CDDB.read(category, disc)[1]
733- cumulative_length = 0
734 for i in range(disc_id[1]):
735 if i + 4 == len(disc_id):
736- # We must calculate last track length different way
737- length = disc_id[len(disc_id) - 1] - cumulative_length
738+ # We must calculate last track length in a different way
739+ length = disc_id[len(disc_id) - 1] - self.length
740 else:
741 # Calculate track length in seconds
742 length = (disc_id[i + 3] - disc_id[ i + 2]) / 75
743- cumulative_length = cumulative_length + length
744
745+ self.length += length
746 track_title = unicode(info['TTITLE' + str(i)], "iso-8859-1")
747 self.tracks.append(CompactDiscTrack(i + 1, track_title, length))
748
749- def get_artist(self):
750- """
751- Get the name of the artist
752- @return: Artist name as string
753- """
754- return self.artist
755-
756- def get_title(self):
757- """
758- Get name of the album.
759- @return: name as string
760- """
761- return self.album
762-
763- def get_tracks(self):
764- """
765- Get a list of track names.
766- @return: Tracknames in a string list
767- """
768- return self.tracks
769-
770- def get_length(self):
771- """
772- Get length of the album. This is the sum of the track lengths.
773- @return: Integer, length in seconds
774- """
775- length = 0
776- for track in self.tracks:
777- length = length + track.get_length()
778- return length
779-
780
781 class CompactDiscTrack(Playable):
782- """
783- CompactDiscTrack is a Playable object that is a one track of the Audio CD.
784- """
785+ '''Representation of a CD track.'''
786
787- def __init__(self, track_number, title, track_length):
788- """
789- Create a new CD track.
790- @param track_number: Number of the track on the Audio CD.
791- @param title: Title of the track
792- @param track_length: Track length in seconds (integer)
793- """
794+ def __init__(self, number, title, length):
795 self.title = title
796- self.uri = "cdda://" + str(track_number)
797- self.track_length = track_length
798-
799+ self.uri = "cdda://" + str(number)
800+ self.length = length
801+
802+ @property
803+ def length_string(self):
804+ '''Return length as a human readable string. Example: 2:46.'''
805+ return str(self.length / 60) + ":" + str.zfill(str(self.length % 60), 2)
806+
807+ # Implement playable interface
808 def get_title(self):
809- """
810- Get title of the track
811- @return: String
812- """
813+ '''Get the title.'''
814 return self.title
815
816+ def get_type(self):
817+ '''Get the type.'''
818+ return Playable.AUDIO_STREAM
819+
820 def get_uri(self):
821- """
822- Get URI of the playable stream.
823- @return: String
824- """
825+ '''Get the URI.'''
826 return self.uri
827
828- def get_length(self):
829- """
830- Get track length in seconds
831- @return: Integer, length in seconds
832- """
833- return self.track_length
834-
835- def get_length_string(self):
836- """
837- Get length of the track as human readable string. Example: 02:46
838- @return: Timestamp as string
839- """
840- return str.zfill(str(self.track_length / 60), 2) + ":" + \
841- str.zfill(str(self.track_length % 60), 2)
842-
843- def get_type(self):
844- """
845- Get type of the stream. This is one of the
846- constants defined in Playable class.
847- @return: Integer
848- """
849- return Playable.AUDIO_STREAM
850-
851
852=== modified file 'entertainerlib/client/medialibrary/playable.py'
853--- entertainerlib/client/medialibrary/playable.py 2009-05-06 02:58:08 +0000
854+++ entertainerlib/client/medialibrary/playable.py 2010-01-03 23:00:32 +0000
855@@ -1,41 +1,22 @@
856 # Copyright (c) 2009 Entertainer Developers - See COPYING - GPLv2
857 '''Playable - Interface for playable streams. MediaPlayer plays Playables'''
858
859-class Playable:
860- """
861- Interface for playable media.
862-
863- This is an interface for all of those objects that can be played with the
864- MediaPlayer object. MediaPlayer playes only files that implement this
865- interface.
866-
867- At the moment following classes implement this:
868- - Track
869- - VideoItem
870- """
871+class Playable(object):
872+ '''An interface for all objects that can be played with the MediaPlayer.
873+ MediaPlayer playes only files that implement this interface.'''
874
875 VIDEO_STREAM = 0
876 AUDIO_STREAM = 1
877
878 def get_uri(self):
879- """
880- Get URI of the playable stream.
881- @return: String
882- """
883+ '''Get the URI.'''
884 return None
885
886 def get_type(self):
887- """
888- Get type of the stream. This is one of the
889- constants defined in Playable class.
890- @return: Integer
891- """
892+ '''Get the type (as defined by the Playable constants.'''
893 return None
894
895 def get_title(self):
896- """
897- Gets the title of the stream.
898- @return: String
899- """
900+ '''Get the title.'''
901 return None
902
903
904=== modified file 'entertainerlib/gui/screens/album.py'
905--- entertainerlib/gui/screens/album.py 2009-07-29 03:09:34 +0000
906+++ entertainerlib/gui/screens/album.py 2010-01-03 23:00:32 +0000
907@@ -38,7 +38,7 @@
908
909 #List indicator
910 self.li = ListIndicator(0.74, 0.85, 0.2, 0.045, ListIndicator.VERTICAL)
911- self.li.set_maximum(len(self.album.get_tracks()))
912+ self.li.set_maximum(len(self.album.tracks))
913 self.add(self.li)
914
915 self.track_menu.active = True
916@@ -51,8 +51,7 @@
917 displays album cover art.
918 """
919 if(self.album.has_album_art()):
920- pixbuf = gtk.gdk.pixbuf_new_from_file(
921- self.album.get_album_art_url())
922+ pixbuf = gtk.gdk.pixbuf_new_from_file(self.album.album_art_url)
923 else:
924 pixbuf = gtk.gdk.pixbuf_new_from_file(
925 self.theme.getImage("default_album_art"))
926@@ -64,19 +63,19 @@
927 """
928 Create album info labels.
929 """
930- if self.album.get_year() != 0:
931- album_text = self.album.get_title() + ", " + \
932- str(self.album.get_year())
933+ if self.album.year != 0:
934+ album_text = self.album.title + ", " + str(self.album.year)
935 else:
936- album_text = self.album.get_title()
937+ album_text = self.album.title
938 album = Label(0.0416, "text", 0.5, 0.13, album_text, font_weight="bold")
939 album.set_ellipsize(pango.ELLIPSIZE_END)
940+ album.set_line_wrap(False)
941 album.width = 0.45
942 self.add(album)
943
944- length = str(self.album.get_total_length() / 60)
945+ length = str(self.album.length / 60)
946 num_of_tracks_text = _("%(total)s tracks, %(time)s minutes") % \
947- {'total': self.album.get_number_of_tracks(), 'time': length}
948+ {'total': len(self.album.tracks), 'time': length}
949 num_of_tracks = Label(0.028, "subtitle", 0.5, 0.18,
950 num_of_tracks_text, font_weight="bold")
951 self.add(num_of_tracks)
952@@ -87,8 +86,8 @@
953 """
954 menu = TextMenu(0.4978, 0.2344, 0.4393, 0.0781)
955
956- tracks = self.album.get_tracks()
957- tracks_list = [[track.get_title(), track.get_length_string(), track] \
958+ tracks = self.album.tracks
959+ tracks_list = [[track.title, track.length_string, track] \
960 for track in tracks]
961 menu.async_add(tracks_list)
962
963@@ -132,6 +131,6 @@
964 '''Update of the list indicator and the screen's title'''
965 self.li.set_current(self.track_menu.selected_index + 1)
966 track = self.track_menu.selected_userdata
967- self.screen_title.set_text(track.get_artist())
968+ self.screen_title.set_text(track.artist)
969 self.screen_title.show()
970
971
972=== modified file 'entertainerlib/gui/screens/artist.py'
973--- entertainerlib/gui/screens/artist.py 2009-06-17 02:49:44 +0000
974+++ entertainerlib/gui/screens/artist.py 2010-01-03 23:00:32 +0000
975@@ -51,6 +51,6 @@
976 better documentation.
977 """
978 album = self.tab_group.tab("albums").selected_album
979- self.media_player.set_playlist(Playlist(album.get_tracks()))
980+ self.media_player.set_playlist(Playlist(album.tracks))
981 self.media_player.play()
982
983
984=== modified file 'entertainerlib/gui/screens/audio_play.py'
985--- entertainerlib/gui/screens/audio_play.py 2009-07-12 17:02:32 +0000
986+++ entertainerlib/gui/screens/audio_play.py 2010-01-03 23:00:32 +0000
987@@ -18,11 +18,11 @@
988 Screen.__init__(self, 'AudioPlay', has_tabs=True)
989
990 self.theme = self.config.theme
991- album = track.get_album()
992+ album = track.album
993
994 # Create album art (this is displayed on all tab pages
995 if(album.has_album_art()):
996- pixbuf = gtk.gdk.pixbuf_new_from_file(album.get_album_art_url())
997+ pixbuf = gtk.gdk.pixbuf_new_from_file(album.album_art_url)
998 else:
999 pixbuf = gtk.gdk.pixbuf_new_from_file(
1000 self.theme.getImage("default_album_art"))
1001
1002=== modified file 'entertainerlib/gui/screens/disc.py'
1003--- entertainerlib/gui/screens/disc.py 2009-07-29 03:09:34 +0000
1004+++ entertainerlib/gui/screens/disc.py 2010-01-03 23:00:32 +0000
1005@@ -60,12 +60,12 @@
1006 try:
1007 disc = self.music_library.get_compact_disc_information()
1008
1009- title = disc.get_title()
1010- artist = disc.get_artist()
1011- tracks = disc.get_tracks()
1012+ title = disc.title
1013+ artist = disc.artist
1014+ tracks = disc.tracks
1015
1016 self.playlist = Playlist(tracks)
1017- self._create_album_info(title, artist, tracks, disc.get_length())
1018+ self._create_album_info(title, artist, tracks, disc.length)
1019 self.track_menu = self._create_track_menu(tracks)
1020 self.add(self.track_menu)
1021 self._create_album_cover_texture(artist, title)
1022@@ -177,7 +177,7 @@
1023 menu = TextMenu(0.4978, 0.2344, 0.4393, 0.0781)
1024 menu.visible_rows = 7
1025
1026- tracks_list = [[track.get_title(), track.get_length_string(), index] \
1027+ tracks_list = [[track.title, track.length_string, index] \
1028 for index, track in enumerate(tracks)]
1029 menu.async_add(tracks_list)
1030
1031
1032=== modified file 'entertainerlib/gui/screens/music.py'
1033--- entertainerlib/gui/screens/music.py 2009-06-17 02:49:44 +0000
1034+++ entertainerlib/gui/screens/music.py 2010-01-03 23:00:32 +0000
1035@@ -85,7 +85,7 @@
1036 self.media_player.play()
1037 elif self.tab_group.tab("albums").active:
1038 album = self.tab_group.tab("albums").selected_album
1039- self.media_player.set_playlist(Playlist(album.get_tracks()))
1040+ self.media_player.set_playlist(Playlist(album.tracks))
1041 self.media_player.play()
1042
1043 def handle_user_event(self, event):
1044
1045=== modified file 'entertainerlib/gui/tabs/albums_tab.py'
1046--- entertainerlib/gui/tabs/albums_tab.py 2010-01-03 23:00:30 +0000
1047+++ entertainerlib/gui/tabs/albums_tab.py 2010-01-03 23:00:32 +0000
1048@@ -42,7 +42,7 @@
1049 self.menu.items_per_col = self.menu.visible_rows
1050 self.add(self.menu)
1051
1052- albums_list = [[album.get_album_art_url(), album] for album in albums]
1053+ albums_list = [[album.album_art_url, album] for album in albums]
1054 self.menu.async_add_albums(albums_list)
1055
1056 self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
1057@@ -83,10 +83,10 @@
1058 '''Update the album information labels.'''
1059 if self.active:
1060 album = self.menu.selected_userdata
1061- self.album_title.set_text(album.get_title())
1062- self.album_artist.set_text(album.get_artist())
1063+ self.album_title.set_text(album.title)
1064+ self.album_artist.set_text(album.artist)
1065 self.album_tracks.set_text(_("%(total)s tracks") % \
1066- {'total': album.get_number_of_tracks()})
1067+ {'total': len(album.tracks)})
1068 self.li.show()
1069 self.li.set_current(self.menu.selected_index + 1)
1070 else:
1071
1072=== modified file 'entertainerlib/gui/tabs/lyrics_tab.py'
1073--- entertainerlib/gui/tabs/lyrics_tab.py 2009-08-04 01:09:03 +0000
1074+++ entertainerlib/gui/tabs/lyrics_tab.py 2010-01-03 23:00:32 +0000
1075@@ -22,7 +22,7 @@
1076 self.lyrics_text = ""
1077
1078 if self.track.has_lyrics():
1079- self.lyrics_text = self.track.get_lyrics()
1080+ self.lyrics_text = self.track.lyrics
1081 lyrics = Label(0.037, "subtitle", 0, 0, self.lyrics_text)
1082 lyrics.set_line_wrap_mode(pango.WRAP_WORD)
1083 lyrics.width = 0.366
1084
1085=== modified file 'entertainerlib/gui/tabs/playing_tab.py'
1086--- entertainerlib/gui/tabs/playing_tab.py 2009-06-17 02:49:44 +0000
1087+++ entertainerlib/gui/tabs/playing_tab.py 2010-01-03 23:00:32 +0000
1088@@ -14,14 +14,14 @@
1089 tab_title=_("Currently playing")):
1090 Tab.__init__(self, name, tab_title)
1091
1092- album = track.get_album()
1093+ album = track.album
1094
1095 # Track name
1096- if track.get_tracknumber() == 0:
1097- track_label_text = str(track.get_title())
1098+ if track.number == 0:
1099+ track_label_text = track.title
1100 else:
1101 track_label_text = _("From %(number)d. %(title)s") % \
1102- {'number':track.get_tracknumber(), 'title': track.get_title()}
1103+ {'number': track.number, 'title': track.title}
1104
1105 self.track_label = Label(0.05, "text", 0.5, 0.33, track_label_text)
1106 self.track_label.set_ellipsize(pango.ELLIPSIZE_END)
1107@@ -29,12 +29,11 @@
1108 self.add(self.track_label)
1109
1110 # Album name
1111- if album.get_year() == 0:
1112- album_label_text = _("From %(title)s") % \
1113- {'title': album.get_title()}
1114+ if album.year == 0:
1115+ album_label_text = _("From %(title)s") % {'title': album.title}
1116 else:
1117 album_label_text = _("From %(title)s, %(year)s") % \
1118- {'title': album.get_title(), 'year': album.get_year()}
1119+ {'title': album.title, 'year': album.year}
1120
1121 self.album_label = Label(0.042, "subtitle", 0.5, 0.43, album_label_text)
1122 self.album_label.set_ellipsize(pango.ELLIPSIZE_END)
1123@@ -42,7 +41,7 @@
1124 self.add(self.album_label)
1125
1126 # Artist name
1127- artist_text = _("By %(artist)s") % {'artist': track.get_artist()}
1128+ artist_text = _("By %(artist)s") % {'artist': track.artist}
1129 self.artist_label = Label(0.042, "subtitle", 0.5, 0.53, artist_text)
1130 self.artist_label.set_ellipsize(pango.ELLIPSIZE_END)
1131 self.artist_label.width = 0.4
1132@@ -57,23 +56,21 @@
1133 Called when currently playing changes tracks. The provided track
1134 is used to update all the necessary labels.
1135 '''
1136- track = track
1137- album = track.get_album()
1138+ album = track.album
1139
1140- if track.get_tracknumber() == 0:
1141- self.track_label.set_text(str(track.get_title()))
1142+ if track.number == 0:
1143+ self.track_label.set_text(track.title)
1144 else:
1145- self.track_label.set_text(str(track.get_tracknumber()) + \
1146- ". " + track.get_title())
1147+ self.track_label.set_text(str(track.number) + ". " + track.title)
1148
1149- if album.get_year() != 0:
1150+ if album.year != 0:
1151 self.album_label.set_text(_("From %(title)s, %(year)s") % \
1152- {'title': album.get_title(), 'year': album.get_year()})
1153+ {'title': album.title, 'year': album.year})
1154 else:
1155 self.album_label.set_text(_("From %(album)s") % \
1156- {'album': album.get_title()})
1157+ {'album': album.title})
1158 self.artist_label.set_text(_("By %(artist)s") % \
1159- {'artist': track.get_artist()})
1160+ {'artist': track.artist})
1161
1162 def can_activate(self):
1163 '''No interaction is available on the PlayingTab.'''
1164
1165=== modified file 'entertainerlib/gui/tabs/tracks_tab.py'
1166--- entertainerlib/gui/tabs/tracks_tab.py 2010-01-03 23:00:31 +0000
1167+++ entertainerlib/gui/tabs/tracks_tab.py 2010-01-03 23:00:33 +0000
1168@@ -29,7 +29,7 @@
1169 self.menu.cursor = None
1170 self.add(self.menu)
1171
1172- tracks_list = [[track.get_title(), None, track] for track in tracks]
1173+ tracks_list = [[track.title, None, track] for track in tracks]
1174 self.menu.async_add_artists(tracks_list)
1175
1176 self.track_title = Label(0.045, "title", 0.22, 0.79, "")
1177@@ -69,12 +69,10 @@
1178 '''Update the track information labels'''
1179 if self.active:
1180 track = self.menu.selected_userdata
1181- self.track_title.set_text(track.get_title())
1182- self.track_length.set_text(str(track.get_length() / 60) + ":" + \
1183- str(track.get_length() % 60).zfill(2))
1184- self.track_number.set_text(_("Track %(track)s from %(album)s") % \
1185- {'track': track.get_tracknumber(),
1186- 'album': track.get_album().get_title()})
1187+ self.track_title.set_text(track.title)
1188+ self.track_length.set_text(track.length_string)
1189+ self.track_number.set_text(_("Track %(track)d from %(album)s") % \
1190+ {'track': track.number, 'album': track.album.title})
1191 self.li.show()
1192 self.li.set_current(self.menu.selected_index + 1)
1193 else:
1194
1195=== modified file 'entertainerlib/tests/mock.py'
1196--- entertainerlib/tests/mock.py 2009-09-08 02:40:16 +0000
1197+++ entertainerlib/tests/mock.py 2010-01-03 23:00:33 +0000
1198@@ -23,17 +23,7 @@
1199 if make_track:
1200 self.mock_track = MockTrack(make_album=False)
1201
1202- def get_number_of_tracks(self):
1203- '''See `Album.get_number_of_tracks`.'''
1204- return 1
1205-
1206- def get_tracks(self):
1207- '''See `Album.get_tracks`.'''
1208- return [self.mock_track]
1209-
1210- def get_year(self):
1211- '''See `Album.get_year`.'''
1212- return '2009'
1213+ self.tracks = [self.mock_track]
1214
1215 def has_album_art(self):
1216 '''See `Album.has_album_art`.'''
1217@@ -284,10 +274,6 @@
1218 if make_album:
1219 self.album = MockAlbum(make_track=False)
1220
1221- def get_album(self, cursor=False):
1222- '''See `Track.get_album`.'''
1223- return self.album
1224-
1225
1226 class MockVideoLibrary(VideoLibrary):
1227 '''Mock entertainerlib.client.medialibrary.videos.VideoLibrary'''
1228
1229=== modified file 'entertainerlib/tests/test_music.py'
1230--- entertainerlib/tests/test_music.py 2009-09-08 02:40:16 +0000
1231+++ entertainerlib/tests/test_music.py 2010-01-03 23:00:33 +0000
1232@@ -6,8 +6,10 @@
1233 from pysqlite2 import dbapi2 as sqlite
1234
1235 from entertainerlib.client.medialibrary.music import (
1236- Album, AlbumHasNoTracks, MusicLibrary, Track, CompactDisc,
1237- TrackRatingOutOfRange, TrackTypeError)
1238+ Album, AlbumHasNoTracks,
1239+ CompactDisc, CompactDiscTrack,
1240+ MusicLibrary,
1241+ Track, TrackTypeError)
1242 from entertainerlib.client.medialibrary.playable import Playable
1243 from entertainerlib.tests import EntertainerTest
1244
1245@@ -16,7 +18,6 @@
1246 classes that need to access the music cache"""
1247
1248 def setUp(self):
1249- """See unittest.TestCase"""
1250 EntertainerTest.setUp(self)
1251
1252 self.debug = False
1253@@ -85,161 +86,83 @@
1254
1255 def setUp(self):
1256 TestMusic.setUp(self)
1257+ self.music_library = MusicLibrary()
1258 self.track = Track('/path/to/track.mp3', # filename
1259 'title',
1260 1, # tracknumber
1261 'artist0',
1262 'album0',
1263- 'genre',
1264- '128', # bitrate
1265 2008, # year
1266- 5, # rating
1267 240, # length
1268- 'comment',
1269- 'lyrics')
1270-
1271- def tearDown(self):
1272- TestMusic.tearDown(self)
1273-
1274- def testTrackConstructor(self):
1275- """testTrackConstructor - Ensures that a Track object is created"""
1276+ 'lyrics',
1277+ self.music_library._create_album)
1278+
1279+ def test_constructor(self):
1280+ '''Test that a Track object is created.'''
1281 self.assertTrue(isinstance(self.track, Track))
1282+ self.assertEqual(self.track.artist, 'artist0')
1283+ self.assertEqual(self.track.filename, '/path/to/track.mp3')
1284+ self.assertEqual(self.track.length, 240)
1285+ self.assertEqual(self.track.length_string, '4:00')
1286+ self.assertEqual(self.track.lyrics, 'lyrics')
1287+ self.assertEqual(self.track.number, 1)
1288+ self.assertEqual(self.track.title, 'title')
1289+ self.assertEqual(self.track.year, 2008)
1290
1291- def testTrackBadConstructor(self):
1292- """testTrackBadConstructor - Ensures that bad track construction
1293- raises an exception for the integer fields"""
1294- for i in [2, 7, 8, 9]:
1295- t = ['a', 'b', 1, 'c', 'd', 'e', 'f', 2, 3, 4, 'g', 'h']
1296+ def test_bad_constructor(self):
1297+ '''Test that bad track construction raises an exception for the integer
1298+ fields.'''
1299+ for i in [2, 5, 6]:
1300+ t = ['', '', 1, '', '', 2, 3, '']
1301 t[i] = str(t[i])
1302 self.assertRaises(TrackTypeError, Track, t[0], t[1], t[2], t[3],
1303- t[4], t[5], t[6], t[7], t[8], t[9], t[10], t[11])
1304-
1305- def testTrackRatingInRange(self):
1306- """testTrackRatingInRange - Ensures that the ratings in range create
1307- valid Track objects"""
1308- for i in range(1, 6):
1309- self.track = Track('a', 'b', 1, 'c', 'd', 'e', 'f', 2, i, 3, 'g',
1310- 'h')
1311- self.assertTrue(isinstance(self.track, Track))
1312- self.assertEqual(self.track.get_rating(), i)
1313-
1314- def testTrackRatingOutOfRange(self):
1315- """testTrackRatingOutOfRange - Ensures that the rating raises an
1316- exception for something out of range in construction"""
1317- for i in [0, 6]:
1318- self.assertRaises(TrackRatingOutOfRange, Track, 'a', 'b', 1, 'c',
1319- 'd', 'e', 'f', 2, i, 3, 'g', 'h')
1320-
1321- def testTrackGetFilename(self):
1322- """testTrackGetFilename - Ensures that the filename is returned"""
1323- result = self.track.get_filename()
1324- self.assertEqual(result, '/path/to/track.mp3')
1325-
1326- def testTrackGetTitle(self):
1327- """testTrackGetTitle - Ensures that the title is returned"""
1328- result = self.track.get_title()
1329- self.assertEqual(result, 'title')
1330-
1331- def testTrackGetTrackNumber(self):
1332- """testTrackGetTrackNumber - Ensures that the tracknumber is
1333- returned"""
1334- result = self.track.get_tracknumber()
1335- self.assertEqual(result, 1)
1336-
1337- def testTrackGetArtist(self):
1338- """testTrackGetArtist - Ensures that the artist is returned"""
1339- result = self.track.get_artist()
1340- self.assertEqual(result, 'artist0')
1341-
1342- def testTrackGetAlbum(self):
1343- """testTrackGetAlbum - Ensures that an album object is returned"""
1344- result = self.track.get_album(self.cursor)
1345+ t[4], t[5], t[6], t[7], None)
1346+
1347+ def test_album_property(self):
1348+ '''Test that an album object is returned.'''
1349+ result = self.track.album
1350 self.assertTrue(isinstance(result, Album))
1351- self.assertEqual(result.get_title(), 'album0')
1352-
1353- def testTrackGetAlbumNot(self):
1354- """testTrackGetAlbumNot - Ensures that a bad album in the track
1355- returns AlbumHasNoTracks"""
1356- self.badTrack = Track('path', 'title', 1, 'artist',
1357- 'foo-bar-baz**', # Here is the bad input
1358- 'genre', '128', 2008, 5, 240, 'comment',
1359- 'lyrics')
1360- self.assertRaises(AlbumHasNoTracks, self.badTrack.get_album,
1361- self.cursor)
1362-
1363- def testTrackGetAlbumArtUrlExists(self):
1364- """testTrackGetAlbumArtUrl - Ensures that the album art url is
1365- returned"""
1366+ self.assertEqual(result.title, 'album0')
1367+
1368+ def test_bad_album(self):
1369+ '''Test that a bad album in the track returns AlbumHasNoTracks.'''
1370+ bad_track = Track('', '', 1, '', 'foo-bar-baz**', 2, 3, '',
1371+ self.music_library._create_album)
1372+ try:
1373+ album = bad_track.album
1374+ self.fail()
1375+ except AlbumHasNoTracks:
1376+ pass
1377+
1378+ def test_get_album_art_url(self):
1379+ '''Test that the album art url is returned.'''
1380 album_artist = "artist0 - album0"
1381 album_artist = album_artist.encode("base64")
1382 album_art = os.path.join(self.art_path, album_artist + ".jpg")
1383 open(album_art, "wb").close()
1384- result = self.track.get_album_art_url(self.cursor)
1385+ result = self.track.get_album_art_url()
1386 self.assertEqual(result, album_art)
1387 if os.path.exists(album_art):
1388 os.remove(album_art)
1389
1390- def testTrackGetAlbumArtUrlNotExists(self):
1391- """testTrackGetAlbumArtUrlNotExists - Ensures that when art does
1392- not exist, None is returned"""
1393- result = self.track.get_album_art_url(self.cursor)
1394+ result = self.track.get_album_art_url()
1395 self.assertEqual(result, None)
1396
1397- def testTrackGetGenre(self):
1398- """testTrackGetGenre - Ensures that the genre is returned"""
1399- result = self.track.get_genre()
1400- self.assertEqual(result, 'genre')
1401-
1402- def testTrackGetBitrate(self):
1403- """testTrackGetBitrate - Ensures that the bitrate is returned"""
1404- result = self.track.get_bitrate()
1405- self.assertEqual(result, '128')
1406-
1407- def testTrackGetYear(self):
1408- """testTrackGetYear - Ensures that the year is returned"""
1409- result = self.track.get_year()
1410- self.assertEqual(result, 2008)
1411-
1412- def testTrackGetRating(self):
1413- """testTrackGetRating - Ensures that the rating is returned"""
1414- result = self.track.get_rating()
1415- self.assertEqual(result, 5)
1416-
1417- def testTrackGetLength(self):
1418- """testTrackGetLength - Ensures that the length is returned"""
1419- result = self.track.get_length()
1420- self.assertEqual(result, 240)
1421-
1422- def testTrackGetComment(self):
1423- """testTrackGetComment - Ensures that the comment is returned"""
1424- result = self.track.get_comment()
1425- self.assertEqual(result, 'comment')
1426-
1427- def testTrackGetLyrics(self):
1428- """testTrackGetLyrics - Ensures that the lyrics are returned"""
1429- result = self.track.get_lyrics()
1430- self.assertEqual(result, 'lyrics')
1431-
1432- def testTrackSetLyrics(self):
1433- """testTrackSetLyrics - Ensures that lyrics are properly set"""
1434- self.track.set_lyrics('some new lyrics')
1435- self.assertEqual(self.track.get_lyrics(), 'some new lyrics')
1436-
1437- def testTrackSetNoneLyrics(self):
1438- """testTrackSetNoneLyrics - Ensures that lyrics are set to empty
1439- string when None is passed as lyrics input"""
1440- self.track.set_lyrics(None)
1441- self.assertEqual(self.track.get_lyrics(), '')
1442-
1443- def testTrackGetType(self):
1444- """testTrackGetType - Ensures that the type is returned"""
1445- result = self.track.get_type()
1446- self.assertEqual(result, Playable.AUDIO_STREAM)
1447-
1448- def testTrackGetUri(self):
1449- """testTrackGetUri - Ensures that the uri is returned"""
1450- result = self.track.get_uri()
1451- self.assertEqual(result, 'file:///path/to/track.mp3')
1452+ def test_lyrics_property(self):
1453+ '''Test the lyrics property.'''
1454+ self.track.lyrics = 'some new lyrics'
1455+ self.assertEqual(self.track.lyrics, 'some new lyrics')
1456+
1457+ self.track.lyrics = None
1458+ self.assertEqual(self.track.lyrics, '')
1459+
1460+ def test_get_type(self):
1461+ '''Test that the type is returned.'''
1462+ self.assertEqual(self.track.get_type(), Playable.AUDIO_STREAM)
1463+
1464+ def test_get_uri(self):
1465+ '''Test that the uri is returned.'''
1466+ self.assertEqual(self.track.get_uri(), 'file:///path/to/track.mp3')
1467
1468
1469 class TestMusicLibrary(TestMusic):
1470@@ -249,9 +172,8 @@
1471 TestMusic.setUp(self)
1472 self.musiclibrary = MusicLibrary()
1473
1474- def testMusicLibraryConstructor(self):
1475- """testMusicLibraryContructor - Ensures instantiation of MusicLibrary
1476- class"""
1477+ def test_music_library_constructor(self):
1478+ '''Test that the music library contructor works.'''
1479 self.assertTrue(isinstance(self.musiclibrary, MusicLibrary))
1480
1481 def testGetAllArtists(self):
1482@@ -260,34 +182,13 @@
1483 result = self.musiclibrary.get_all_artists()
1484 self.assertEqual(result, ['artist0'])
1485
1486- def testGetAllTracks(self):
1487- """testGetAllTracks - Ensures that all tracks are returned"""
1488- result = self.musiclibrary.get_all_tracks()
1489- for i in result:
1490- self.assertTrue(isinstance(i, Track))
1491- self.assertEqual(len(result), 8)
1492-
1493- def testGetTracksByGenre(self):
1494- """testGetTracksByGenre - Ensures tracks of a certain genre are
1495- returned"""
1496- result = self.musiclibrary.get_tracks_by_genre('genre0')
1497- self.assertEqual(len(result), 4)
1498- for i in result:
1499- self.assertEqual(i.get_genre(), 'genre0')
1500-
1501- def testGetTracksByMissingGenre(self):
1502- """testGetTracksByMissingGenre - Ensures proper handling of a missing
1503- genre"""
1504- self.assertEquals(
1505- len(self.musiclibrary.get_tracks_by_genre('foo-bar-baz**')), 0)
1506-
1507 def testGetTracksByArtist(self):
1508 """testGetTracksByArtist - Ensure tracks by a certain artist are
1509 returned"""
1510 result = self.musiclibrary.get_tracks_by_artist('artist0')
1511 self.assertEqual(len(result), 8)
1512 for i in result:
1513- self.assertEqual(i.get_artist(), 'artist0')
1514+ self.assertEqual(i.artist, 'artist0')
1515
1516 def testGetTracksByUnknownArtist(self):
1517 """testGetTracksByUnknownArtist - Ensure proper handling of an missing
1518@@ -308,7 +209,7 @@
1519 result = self.musiclibrary.get_albums_by_artist('artist0')
1520 self.assertEqual(len(result), 2)
1521 for i in result:
1522- self.assertEqual(i.get_artist(), 'artist0')
1523+ self.assertEqual(i.artist, 'artist0')
1524
1525 def testGetAlbumsByUnknownArtist(self):
1526 """testGetAlbumsByUnknownArtist - Ensures proper handling of an
1527@@ -350,42 +251,32 @@
1528 database"""
1529 # Only need a filename that matches something in the database, the rest
1530 # of the track is for construction purposes only
1531- self.track = Track('/filename/000', '', 0, '', '', '', '', 0, 1, 0, '',
1532- '')
1533- self.musiclibrary.save_lyrics(self.track, 'some lyrics here')
1534- self.assertEqual(self.track.get_lyrics(), 'some lyrics here')
1535+ track = Track('/filename/000', '', 0, '', '', 0, 1, '', None)
1536+ self.musiclibrary.save_lyrics(track, 'some lyrics here')
1537+ self.assertEqual(track.lyrics, 'some lyrics here')
1538
1539
1540 class TestAlbum(TestMusic):
1541 '''Tests Album'''
1542
1543 def setUp(self):
1544- """See unittest.TestCase"""
1545 TestMusic.setUp(self)
1546- self.album = Album('album1', self.cursor)
1547-
1548- def tearDown(self):
1549- """Clean up after the test"""
1550- TestMusic.tearDown(self)
1551+ self.music_library = MusicLibrary()
1552+ self.album = self.music_library._create_album('album1')
1553
1554 def testAlbumConstructor(self):
1555 """Test that an Album object is properly constructed"""
1556 self.assertTrue(isinstance(self.album, Album))
1557+ self.assertEqual(self.album.artist, 'artist0')
1558+ self.assertEqual(self.album.length, 8)
1559+ self.assertEqual(self.album.title, 'album1')
1560+ self.assertEqual(self.album.year, 0)
1561
1562 def testAlbumConstructorNot(self):
1563 """Test that an AlbumHasNoTracks exception is raised when the created
1564 album doesn't exist in the cache"""
1565- self.assertRaises(AlbumHasNoTracks, Album, 'foo', self.cursor)
1566-
1567- def testAlbumStr(self):
1568- """Test that title is returned in string conversion"""
1569- result = str(self.album)
1570- self.assertEqual(result, 'album1')
1571-
1572- def testAlbumGetTitle(self):
1573- """Test that the album title is returned"""
1574- result = self.album.get_title()
1575- self.assertEqual(result, 'album1')
1576+ self.assertRaises(AlbumHasNoTracks, self.music_library._create_album,
1577+ 'foo')
1578
1579 def testAlbumHasAlbumArt(self):
1580 """Test that album art exists for the file"""
1581@@ -399,12 +290,12 @@
1582
1583 def testAlbumHasAlbumArtNot(self):
1584 """Test that missing album art is reported back"""
1585- otherAlbum = Album('album1', self.cursor)
1586- self.assertFalse(otherAlbum.has_album_art())
1587+ other_album = self.music_library._create_album('album1')
1588+ self.assertFalse(other_album.has_album_art())
1589
1590 def testAlbumGetAlbumArtUrl(self):
1591 """Test that the path to the album's art is returned"""
1592- result = self.album.get_album_art_url()
1593+ result = self.album.album_art_url
1594 album_artist = "artist0 - album1"
1595 album_artist = album_artist.encode("base64")
1596 album_art = os.path.join(self.art_path, album_artist + ".jpg")
1597@@ -412,60 +303,30 @@
1598
1599 def testAlbumGetTracks(self):
1600 """Test that all tracks for an album are returned"""
1601- result = self.album.get_tracks()
1602+ result = self.album.tracks
1603 self.assertEqual(len(result), 4)
1604 for i in result:
1605 self.assertTrue(isinstance(i, Track))
1606
1607- def testAlbumGetNumberOfTracks(self):
1608- """Test correct number of tracks from album is returned"""
1609- self.assertEqual(self.album.get_number_of_tracks(), 4)
1610-
1611- def testAlbumGetYear(self):
1612- """Test that year of album is returned"""
1613- self.assertEqual(self.album.get_year(), 0)
1614-
1615- def testAlbumGetGenre(self):
1616- """Test that genre of album is returned"""
1617- self.assertEqual(self.album.get_genre(), 'genre0')
1618-
1619- def testAlbumGetArtist(self):
1620- """Test that artist of album is returned"""
1621- self.assertEqual(self.album.get_artist(), 'artist0')
1622-
1623- def testAlbumGetTotalLength(self):
1624- """Test that total length of album is returned"""
1625- self.assertEqual(self.album.get_total_length(), 8)
1626-
1627
1628 class TestCompactDisc(TestMusic):
1629 '''Tests CompactDisc'''
1630
1631 def setUp(self):
1632- """See unittest.TestCase"""
1633 TestMusic.setUp(self)
1634 self.cd = CompactDisc([2299253003L, 11, 150, 25433, 46436, 68737, 89292,
1635 108536, 130667, 144099, 160174, 181568, 203695, 3027])
1636
1637- def tearDown(self):
1638- """Clean up after the test"""
1639- TestMusic.tearDown(self)
1640-
1641- def testCompactDiscConstructor(self):
1642- """Test that a CompactDisc object is properly constructed"""
1643+ def test_constructor(self):
1644+ '''Test that a CompactDisc object is properly constructed.'''
1645 self.assertTrue(isinstance(self.cd, CompactDisc))
1646-
1647- def testCompactDiscGetTitle(self):
1648- """Test that album title is returned from CDDB"""
1649- self.assertEqual(self.cd.get_title(), 'The Joshua Tree')
1650-
1651- def testCompactDiscGetArtist(self):
1652- """Test that artist is returned from CDDB"""
1653- self.assertEqual(self.cd.get_artist(), 'U2')
1654-
1655- def testCompactDiscGetTracks(self):
1656- """Test that tracks names are returned from CDDB"""
1657- tracks = [track.get_title() for track in self.cd.get_tracks()]
1658+ self.assertEqual(self.cd.artist, 'U2')
1659+ self.assertEqual(self.cd.length, 3027)
1660+ self.assertEqual(self.cd.title, 'The Joshua Tree')
1661+
1662+ def test_tracks(self):
1663+ '''Test that track names are returned from CDDB.'''
1664+ tracks = [track.title for track in self.cd.tracks]
1665
1666 self.assertEqual(tracks, ['Where the Streets Have No Name',
1667 "I Still haven't Found What I'm Looking For", 'With or Without You',
1668@@ -474,7 +335,24 @@
1669 'Trip Through Your Wires', 'One Tree Hill', 'Exit',
1670 'Mothers of the Disappeared'])
1671
1672- def testCompactDiscGetLength(self):
1673- """Test that the sum of the track lengths is returned"""
1674- self.assertEqual(self.cd.get_length(), 3027)
1675+class TestCompactDiscTrack(TestMusic):
1676+ '''Tests CompactDiscTrack'''
1677+
1678+ def setUp(self):
1679+ TestMusic.setUp(self)
1680+ self.track = CompactDiscTrack(1, 'test', 120)
1681+
1682+ def test_constructor(self):
1683+ '''Test that a CompactDiscTrack object is properly constructed.'''
1684+ self.assertTrue(isinstance(self.track, CompactDiscTrack))
1685+ self.assertEqual(self.track.length, 120)
1686+ self.assertEqual(self.track.title, 'test')
1687+
1688+ def test_get_uri(self):
1689+ '''Test that the URI is in the proper CD format.'''
1690+ self.assertEqual(self.track.uri, 'cdda://1')
1691+
1692+ def test_length_string_property(self):
1693+ '''Test the length_string property.'''
1694+ self.assertEqual(self.track.length_string, '2:00')
1695

Subscribers

People subscribed via source and target branches