- Abstraction work within the Music module.

This commit is contained in:
noisymime 2008-03-19 13:06:29 +00:00
parent 086704cb5e
commit 9f8995cd25
9 changed files with 183 additions and 92 deletions

View File

@ -0,0 +1,87 @@
import time
from modules.music_player.music_objects.song import song
from modules.music_player.music_objects.artist import artist
from modules.music_player.music_objects.album import album
#############################################################
# This is the backend for the regular MythMusic backend.
# Information is pulled from the 'mythconverg' mysql db
#############################################################
class Backend:
def __init__(self, music_player):
self.music_player = music_player
self.dbMgr = music_player.dbMgr
#Returns a list of artist objects
def get_artists(self):
#Generate some SQL to retrieve videos that were in the final_file_list
#Load the videos into the cover viewer
sql = "SELECT * FROM music_artists"
if self.music_player.glossMgr.debug: print "Music Artist SQL: " + sql
results = self.dbMgr.run_sql(sql)
#Check for null return
if results == None:
print "MusicPlayer: No connection to DB or no artists found in DB"
return None
pixbuf = None
artists = []
#Else add the entries in
for record in results:
tempArtist = artist(self.music_player)
tempArtist.import_from_mythObject(record)
artists.append(tempArtist)
#self.artistImageRow.add_object(tempArtist)
time.sleep(0.01) #Arbitary sleep time to avoid CPU bound status
return artists
#Given an artistID, returns a list of albums for them
def get_albums_by_artistID(self, id):
#Generate some SQL
sql = "SELECT * FROM music_albums where artist_id='%s'" % (str(id))
if self.music_player.glossMgr.debug: print "Music Album SQL: " + sql
results = self.dbMgr.run_sql(sql)
#Check for null return
if results == None:
print "MusicPlayer: No connection to DB or no albums found with ID %s" % (str(id))
return None
pixbuf = None
artists = []
#Else add the entries in
for record in results:
tempAlbum = album(self.music_player)
tempAlbum.import_from_mythObject(record)
artists.append(tempAlbum)
#self.artistImageRow.add_object(tempArtist)
return artists
#Given an albumID, returns a list of songs on the album
def get_songs_by_albumID(self, id):
#Generate some SQL
sql = "SELECT * FROM music_songs where album_id='%s'" % (str(id))
if self.music_player.glossMgr.debug: print "Music Song SQL: " + sql
results = self.dbMgr.run_sql(sql)
#Check for null return
if results == None:
print "MusicPlayer: No connection to DB or no songs found with ID %s" % (str(id))
return None
pixbuf = None
songs = []
#Else add the entries in
for record in results:
tempSong = song(self.music_player)
tempSong.import_from_mythObject(record)
songs.append(tempSong)
#self.artistImageRow.add_object(tempArtist)
return songs

View File

@ -0,0 +1,51 @@
import time
import thread
import clutter
from ui_elements.image_row import ImageRow
from ui_elements.image_frame import ImageFrame
class MusicObjectRow(ImageRow):
def __init__(self, glossMgr, width, height, columns, music_player):
ImageRow.__init__(self, glossMgr, width, height, columns)
self.music_player = music_player
self.objectLibrary = []
def add_object(self, object):
self.objectLibrary.append(object)
def load_image_range(self, start, end, thread_data = None):
for i in range(start, end):
object = self.objectLibrary[i]
print "loading: " + object.name
pixbuf = object.get_image()
time.sleep(self.music_player.sleep_time)
tmpImage = clutter.Texture()
if pixbuf == object.PENDING_DOWNLOAD:
object.connect("image-found", self.set_image_cb, object, tmpImage)
elif not pixbuf is None:
#tmpImage.set_pixbuf(pixbuf)
tmpImage = ImageFrame(pixbuf, self.image_size)
self.add_texture_group(tmpImage)
#clutter.threads_leave()
print "Finished threads"
#Just a callback function to call 'load_image_range()' in a new thread
def load_image_range_cb(self, timeline):
thread.start_new_thread(self.load_image_range, (self.num_columns, len(self.objectLibrary)-1))
#A simple callback funtion to set the image of an artist/album after it has completed a download
def set_image_cb(self, data, music_object, tmpImage):
#if self.glossMgr.debug:
print "Image for music_object '%s' downloaded" % (music_object.name)
pixbuf = music_object.get_image()
if not pixbuf is None:
clutter.threads_init()
tmpImage.set_pixbuf(pixbuf)
clutter.threads_leave()

View File

@ -13,4 +13,12 @@ class album:
except IndexError, e:
print "Music_Player: Found difference in DB structure for albums. Attempting to continue."
def get_image(self):
#First way to get an image is via the songs on this album
songs = self.music_player.backend.get_songs_by_albumID(self.albumID)
for song in songs:
pixbuf = song.get_image_from_ID3()
if not pixbuf is None:
return pixbuf

View File

@ -6,7 +6,6 @@ import thread
from modules.music_player.music_objects.music_object import MusicObject
class artist(MusicObject):
PENDING_DOWNLOAD = range(1)
artistID = None
name = None

View File

@ -2,6 +2,7 @@ import gobject
# An abstract class that simply serves to help emit a common signal
class MusicObject(gobject.GObject):
PENDING_DOWNLOAD = range(1)
#Setup signals
__gsignals__ = {

View File

@ -1,5 +1,7 @@
import eyeD3
import os
import pygtk
import gtk
class song:
filename = None
@ -46,6 +48,8 @@ class song:
def get_image(self):
return self.get_image_from_ID3()
#Tries to retrieve an image (pixbuf) from a song
#Returns None if nothing found
def get_image_from_ID3(self):
#Basic check first up to make sure the filename is set
if self.filename is None:
@ -74,6 +78,9 @@ class song:
print "Image Mine Type: " + str(img.mimeType)
data = img.imageData
return data
loader = gtk.gdk.PixbufLoader()
loader.write(pixbuf)
loader.close()
return loader.get_pixbuf()

View File

@ -2,16 +2,15 @@ import pygtk
import gtk
import clutter
import thread, time
from modules.music_player.music_objects.song import song
from modules.music_player.music_objects.artist import artist
from modules.music_player.music_objects.album import album
from modules.music_player.backends.myth_music import Backend
from modules.music_player.lastFM_interface import lastFM_interface
from ui_elements.image_row import ImageRow
from ui_elements.image_frame import ImageFrame
from modules.music_player.music_object_row import MusicObjectRow
class Module:
title = "Music"
num_columns = 6
sleep_time = 0.2
def __init__(self, glossMgr, dbMgr):
self.stage = glossMgr.get_stage()
@ -22,7 +21,9 @@ class Module:
self.artists = []
self.songs = []
self.imageRow = ImageRow(self.glossMgr, self.stage.get_width(), 200, self.num_columns)
self.backend = Backend(self)
self.artistImageRow = MusicObjectRow(self.glossMgr, self.stage.get_width(), 200, self.num_columns, self)
self.lastFM = lastFM_interface()
self.base_dir = self.dbMgr.get_setting("MusicLocation")
@ -31,8 +32,8 @@ class Module:
self.is_playing = False
#self.load_albums()
#self.load_artists()
thread.start_new_thread(self.load_artists, ())
self.artists = self.backend.get_artists()
#thread.start_new_thread(self.load_artists, ())
def setup_ui(self):
@ -66,7 +67,9 @@ class Module:
clutter.main_quit()
if (event.keyval == clutter.keysyms.Left) or (event.keyval == clutter.keysyms.Right):
self.imageRow.input_queue.input(event)
#calculate a period of time the loading threads should sleep for when a timline is in progress
self.sleep_time = float(MusicObjectRow.frames) / float(MusicObjectRow.fps)
self.artistImageRow.input_queue.input(event)
def begin(self, glossMgr):
@ -81,60 +84,25 @@ class Module:
self.alpha = clutter.Alpha(self.timeline_backdrop, clutter.ramp_inc_func)
self.backdrop_behaviour = clutter.BehaviourOpacity(opacity_start=0, opacity_end=255, alpha=self.alpha)
self.backdrop_behaviour.apply(self.backdrop)
#Load in the initial images:
self.load_image_range(0, self.num_columns)
self.artistImageRow.objectLibrary = self.artists
self.artistImageRow.load_image_range(0, self.num_columns)
self.stage.add(self.imageRow)
self.imageRow.set_opacity(0)
self.imageRow.show()
self.backdrop_behaviour.apply(self.imageRow)
self.stage.add(self.artistImageRow)
self.artistImageRow.set_opacity(0)
self.artistImageRow.show()
self.backdrop_behaviour.apply(self.artistImageRow)
self.timeline_backdrop.start()
#Load the rest of the images
#thread.start_new_thread(self.load_image_range, (self.num_columns, len(self.artists)-1))
self.timeline_backdrop.connect("completed", self.load_image_range_cb)
self.timeline_backdrop.connect("completed", self.artistImageRow.load_image_range_cb)
#self.load_image_range(self.num_columns, len(self.artists)-1)
self.imageRow.select_first()
self.artistImageRow.select_first()
#Just a callback function to call 'load_image_range()'
def load_image_range_cb(self, timeline):
#self.load_image_range(self.num_columns, len(self.artists)-1)
#clutter.threads_enter()
thread.start_new_thread(self.load_image_range, (self.num_columns, len(self.artists)-1))
def load_image_range(self, start, end, thread_data = None):
for i in range(start, end):
artist = self.artists[i]
print "loading: " + artist.name
pixbuf = artist.get_image()
if end > 6:
time.sleep(0.5)
tmpImage = clutter.Texture()
if pixbuf == artist.PENDING_DOWNLOAD:
artist.connect("image-found", self.set_image_cb, artist, tmpImage)
elif not pixbuf is None:
#tmpImage.set_pixbuf(pixbuf)
tmpImage = ImageFrame(pixbuf, self.imageRow.image_size)
self.imageRow.add_texture_group(tmpImage)
#clutter.threads_leave()
print "Finished threads"
#A simple callback funtion to set the image of an artist/album after it has completed a download
def set_image_cb(self, data, music_object, tmpImage):
#if self.glossMgr.debug:
print "Image for music_object '%s' downloaded" % (music_object.name)
pixbuf = music_object.get_image()
if not pixbuf is None:
clutter.threads_init()
tmpImage.set_pixbuf(pixbuf)
clutter.threads_leave()
def stop(self):
pass
@ -145,7 +113,7 @@ class Module:
def unpause(self):
pass
def load_albums(self):
def load_songs(self):
#Generate some SQL to retrieve videos that were in the final_file_list
#Load the videos into the cover viewer
sql = "SELECT * FROM music_songs" # WHERE filename IN ("
@ -179,36 +147,4 @@ class Module:
pixbuf = loader.get_pixbuf()
self.tmpImage = clutter.Texture()
self.tmpImage.set_pixbuf(pixbuf)
def load_artists(self):
#Generate some SQL to retrieve videos that were in the final_file_list
#Load the videos into the cover viewer
sql = "SELECT * FROM music_artists" # WHERE filename IN ("
if self.glossMgr.debug: print "Music Artist SQL: " + sql
results = self.dbMgr.run_sql(sql)
#Check for null return
if results == None:
print "MusicPlayer: No connection to DB or no artists found in DB"
return None
pixbuf = None
#Else add the entries in
for record in results:
tempArtist = artist(self)
tempArtist.import_from_mythObject(record)
self.artists.append(tempArtist)
"""
if not tempArtist.get_image()is None:
pixbuf = tempArtist.get_image()
break
"""
#print filename
#tempSong.set_file(filename)
"""
if not pixbuf is None:
self.tmpImage = clutter.Texture()
self.tmpImage.set_pixbuf(pixbuf)
"""

View File

@ -16,6 +16,8 @@ class ImageRow(clutter.Group):
inactiveOpacity = 150
images_size_percent = 0.90 #This is the percentage of the total group size that the covers will take
fps = 35
frames = 25
def __init__(self, glossMgr, width, height, columns):
clutter.Group.__init__(self)
@ -97,7 +99,7 @@ class ImageRow(clutter.Group):
def select_item(self, incomingItem, outgoingItem):
self.timeline = clutter.Timeline(10,35)
self.timeline = clutter.Timeline(self.frames, self.fps)
alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func)
self.input_queue.set_timeline(self.timeline)
@ -158,7 +160,7 @@ class ImageRow(clutter.Group):
self.timeline.start()
def select_first(self):
self.timeline = clutter.Timeline(20,80)
self.timeline = clutter.Timeline(self.frames, self.fps)
self.input_queue.set_timeline(self.timeline)
incomingItem = 0
@ -181,7 +183,7 @@ class ImageRow(clutter.Group):
if self.currentSelection is None:
return
self.timeline = clutter.Timeline(10,35)
self.timeline = clutter.Timeline(self.frames, self.fps)
alpha = clutter.Alpha(self.timeline, clutter.smoothstep_inc_func)
self.behaviourOld_scale = clutter.BehaviourScale(x_scale_start=self.scaleFactor, y_scale_start=self.scaleFactor, x_scale_end=1, y_scale_end=1, alpha=alpha)