gloss-mc/VideoPlayer.py

425 lines
17 KiB
Python

import pygtk
import gtk
import pygst
import gst
import gobject
import pango
import clutter
from clutter import cluttergst
from VideoController import VideoController
class VideoPlayer():
def __init__(self, stage, dbMgr):
self.stage = stage
self.cover_viewer = coverViewer(self.stage, 600, 600)
self.videoController = VideoController(stage)
self.is_playing = False
#This block can be moved to begin() but causes a performance hit when loading the module *shrug*
results = dbMgr.get_videos()
if results == None:
print "VideoPlayer: No connection to DB or no videos found in DB"
return None
#Load the videos into the cover viewer
for record in results:
tempVideo = videoItem()
tempVideo.importFromMythObject(record)
self.cover_viewer.add_video(tempVideo)
def on_key_press_event (self, stage, event):
if self.is_playing:
if event.keyval == clutter.keysyms.Escape:
self.videoController.stop_video()
self.is_playing = False
else:
self.videoController.on_key_press_event(event)
return False
if event.keyval == clutter.keysyms.p:
if self.paused:
self.unpause()
else:
self.pause()
if event.keyval == clutter.keysyms.q:
clutter.main_quit()
up = clutter.keysyms.Up
down = clutter.keysyms.Down
left = clutter.keysyms.Left
right= clutter.keysyms.Right
if (event.keyval == up) or (event.keyval == down) or (event.keyval == left) or (event.keyval == right):
self.cover_viewer.on_cursor_press_event(event)
if event.keyval == clutter.keysyms.Return:
vid = self.cover_viewer.get_current_video()
uri = "file://" + str(vid.filename)
self.videoController.play_video(uri)
self.is_playing = True
if event.keyval == clutter.keysyms.Escape:
return True
def begin(self, MenuMgr):
#Create a backdrop for the player. In this case we just use the same background as the menus
self.backdrop = clutter.CloneTexture(MenuMgr.get_skinMgr().get_Background())
self.backdrop.set_size(self.stage.get_width(), self.stage.get_height())
self.backdrop.set_opacity(0)
self.backdrop.show()
self.stage.add(self.backdrop)
#Fade the backdrop in
timeline_begin = clutter.Timeline(10,40)
alpha = clutter.Alpha(timeline_begin, clutter.ramp_inc_func)
begin_behaviour = clutter.BehaviourOpacity(alpha, 0, 255)
begin_behaviour.apply(self.backdrop)
self.cover_viewer.set_opacity(0)
self.cover_viewer.show_all()
self.cover_viewer.show()
self.stage.add(self.cover_viewer)
cover_x = self.stage.get_width() - self.cover_viewer.get_width()
self.cover_viewer.set_position(cover_x, 40)
self.cover_viewer.toggle_details() #Turns the details group on
self.cover_viewer.select_first()
begin_behaviour.apply(self.cover_viewer)
timeline_begin.start()
def stop(self):
#Fade everything out
timeline_stop = clutter.Timeline(10,30)
alpha = clutter.Alpha(timeline_stop, clutter.ramp_inc_func)
stop_behaviour = clutter.BehaviourOpacity(alpha, 255, 0)
stop_behaviour.apply(self.cover_viewer)
stop_behaviour.apply(self.backdrop)
timeline_stop.connect('completed', self.destroyPlugin)
timeline_stop.start()
def destroyPlugin(self, data):
self.stage.remove(self.cover_viewer)
self.backdrop.hide()
#self.stage.remove(self.overlay)
def pause(self):
pass
def unpause(self):
pass
class videoItem():
def __init(self):
self.mythID = None
def importFromMythObject(self, mythObject):
self.mythID = mythObject[0]
self.title = mythObject[1]
self.director = mythObject[2]
self.plot = mythObject[3]
self.rating = mythObject[4]
self.inetRef = mythObject[5]
self.year = mythObject[6]
self.userRating = mythObject[7]
self.length = mythObject[8]
self.showLevel = mythObject[9]
self.filename = mythObject[10]
self.coverfile = mythObject[11]
self.childID = mythObject[12]
self.browse = mythObject[13]
self.playCommand = mythObject[14]
self.category = mythObject[15]
def getTitle(self):
return self.title
def getCoverfile(self):
return self.coverfile
class coverViewer(clutter.Group):
scaleFactor = 1.4
inactiveOpacity = 150
def __init__(self, stage, width, height):
clutter.Group.__init__(self)
self.stage = stage
self.videoLibrary = []
self.textureLibrary = []
self.current_video_details = video_details_group(width)
self.covers_group = clutter.Group()
self.num_covers = 0
self.cover_gap = 1
self.num_visible_rows = 3
self.num_columns = 4
self.cover_size = int(width / self.num_columns) #200 #A cover will be cover_size * cover_size (X * Y)
#Setup the current min and max viewable rows
self.min_visible_rows = 0
self.max_visible_rows = self.num_visible_rows
self.currentSelection = 0
self.add(self.covers_group)
self.covers_group.show()
#self.stage.add(self.current_video_description)
self.current_video_details.show()
self.current_video_details.show_all()
#Turns the description group off and on
def toggle_details(self):
if self.current_video_details.get_parent() == None:
self.add(self.current_video_details)
#Set the position of the details group
(pos_x, pos_y) = self.get_abs_position()
#The next two lines are horribly ugly, but all they do is position the details viewer in the middle of the gap between the bottom of the visible cover viewer and the bottom of the stage
viewer_lower_y = int(pos_y + (self.max_visible_rows * self.cover_size))
pos_y = viewer_lower_y# + int( (self.stage.get_height() - viewer_lower_y) / 2 - int(self.current_video_details.get_height()/2))
self.current_video_details.set_position(20, pos_y)
else:
self.stage.remove(self.current_video_details)
def add_video(self, video):
self.videoLibrary.append(video)
imagePath = video.getCoverfile()
tempTexture = clutter.Texture()
pixbuf = gtk.gdk.pixbuf_new_from_file(imagePath)
tempTexture.set_pixbuf(pixbuf)
xy_ratio = float(tempTexture.get_width()) / tempTexture.get_height()
width = int(self.cover_size * xy_ratio)
tempTexture.set_width(width)
tempTexture.set_height(self.cover_size)
tempTexture.set_opacity(self.inactiveOpacity)
tempTexture.set_position( (self.num_covers * self.cover_size), 0)
tempTexture.set_depth(1)
self.textureLibrary.append(tempTexture)
#x = (self.cover_gap + self.cover_size) * (self.num_covers/self.num_rows)
#y = (self.num_covers % self.num_rows) * self.cover_size + ( (self.num_covers % self.num_rows) * self.cover_gap)
x = (self.num_covers % self.num_columns) * self.cover_size + ( (self.num_covers % self.num_columns) * self.cover_gap)
y = (self.cover_gap + self.cover_size) * (self.num_covers/self.num_columns)
#Center the cover
if width < self.cover_size:
x = x + (self.cover_size - width)/2
tempTexture.set_position(x, y)
#If we're past the maximum rows, make the pics invistible
if self.num_covers > (self.num_columns * self.num_visible_rows)-1:
tempTexture.set_opacity(0)
self.covers_group.add(tempTexture)
tempTexture.show()
self.num_covers = self.num_covers +1
def select_item(self, incomingItem, outgoingItem):
self.current_video_details.set_video(self.videoLibrary[incomingItem])
self.timeline = clutter.Timeline(20,80)
#Check if the cover is currently not visible
rolling = False
if incomingItem > (self.num_columns * self.max_visible_rows-1):
self.rollViewer(True, self.timeline)
rolling = True
if incomingItem < (self.num_columns * self.min_visible_rows):
self.rollViewer(False, self.timeline)
rolling = True
outgoingTexture = self.textureLibrary[outgoingItem]
incomingTexture = self.textureLibrary[incomingItem]
alpha = clutter.Alpha(self.timeline, clutter.ramp_inc_func)
behaviourNew_scale = clutter.BehaviourScale(alpha, 1, self.scaleFactor, clutter.GRAVITY_CENTER)
behaviourNew_z = clutter.BehaviourDepth(alpha, 1, 2)
#If we're performing a roll (See above) then the incoming opacity should start at 0 rather than the normal inactive opacity
if rolling:
behaviourNew_opacity = clutter.BehaviourOpacity(alpha, 0, 255)
else:
behaviourNew_opacity = clutter.BehaviourOpacity(alpha, self.inactiveOpacity, 255)
behaviourOld_scale = clutter.BehaviourScale(alpha, self.scaleFactor, 1, clutter.GRAVITY_CENTER)
behaviourOld_z = clutter.BehaviourDepth(alpha, 2, 1)
behaviourOld_opacity = clutter.BehaviourOpacity(alpha, 255, self.inactiveOpacity)
behaviourNew_scale.apply(incomingTexture)
behaviourNew_z.apply(incomingTexture)
behaviourNew_opacity.apply(incomingTexture)
behaviourOld_scale.apply(outgoingTexture)
behaviourOld_z.apply(outgoingTexture)
behaviourOld_opacity.apply(outgoingTexture)
self.currentSelection = incomingItem
self.timeline.start()
def select_first(self):
self.current_video_details.set_video(self.videoLibrary[0])
self.timeline = clutter.Timeline(20,80)
incomingItem = 0
incomingTexture = self.textureLibrary[incomingItem]
alpha = clutter.Alpha(self.timeline, clutter.ramp_inc_func)
behaviourNew_scale = clutter.BehaviourScale(alpha, 1, self.scaleFactor, clutter.GRAVITY_CENTER)
behaviourNew_z = clutter.BehaviourDepth(alpha, 1, 2)
behaviourNew_opacity = clutter.BehaviourOpacity(alpha, self.inactiveOpacity, 255)
behaviourNew_scale.apply(incomingTexture)
behaviourNew_z.apply(incomingTexture)
behaviourNew_opacity.apply(incomingTexture)
self.currentSelection = incomingItem
self.timeline.start()
#This moves the visible row of covers up and down
# moveUp: True if the covers are to come up, false if they're to go down
def rollViewer(self, moveUp, timeline):
if moveUp:
new_y = self.covers_group.get_y() - self.cover_size
self.max_visible_rows = self.max_visible_rows + 1
self.min_visible_rows = self.min_visible_rows + 1
#Define the row of covers that now needs to disapear / appear
min_outgoing = (self.min_visible_rows-1) * self.num_columns
max_outgoing = min_outgoing + self.num_columns
min_incoming = (self.max_visible_rows-1) * self.num_columns
max_incoming = min_incoming + self.num_columns
else:
new_y = self.covers_group.get_y() + self.cover_size
self.max_visible_rows = self.max_visible_rows - 1
self.min_visible_rows = self.min_visible_rows - 1
#Define the row of covers that now needs to disapear / appear
min_incoming = (self.min_visible_rows) * self.num_columns
max_incoming = min_incoming + self.num_columns
min_outgoing = (self.max_visible_rows) * self.num_columns
max_outgoing = min_outgoing + self.num_columns
knots = (\
(self.covers_group.get_x(), self.covers_group.get_y()),\
(self.covers_group.get_x(), new_y) \
)
alpha = clutter.Alpha(timeline, clutter.ramp_inc_func)
behaviour_path = clutter.BehaviourPath(alpha, knots)
behaviour_incoming = clutter.BehaviourOpacity(alpha, 0, self.inactiveOpacity)
behaviour_outgoing = clutter.BehaviourOpacity(alpha, self.inactiveOpacity, 0)
behaviour_path.apply(self.covers_group)
#Also need to change a few opacities - This is really messy, but works
for i in range(min_outgoing, max_outgoing):
behaviour_outgoing.apply(self.textureLibrary[i])
for i in range(min_incoming, max_incoming):
behaviour_incoming.apply(self.textureLibrary[i])
def get_current_video(self):
return self.videoLibrary[self.currentSelection]
def on_cursor_press_event(self, event):
newItem = None
if event.keyval == clutter.keysyms.Left:
#Make sure we're not already on the first cover
if not self.currentSelection == 0:
newItem = self.currentSelection - 1
if event.keyval == clutter.keysyms.Right:
#This check makes sure that we're not on the last cover already
if not self.currentSelection == (self.get_n_children()-1):
newItem = self.currentSelection + 1
if event.keyval == clutter.keysyms.Down:
#Check if we're already on the bottom row
if not (self.currentSelection > (len(self.textureLibrary)-1 - self.num_columns)):
newItem = self.currentSelection + self.num_columns
if event.keyval == clutter.keysyms.Up:
#Check if we're already on the top row
if not (self.currentSelection < self.num_columns):
newItem = self.currentSelection - self.num_columns
#Final sanity check
if (newItem < 0) and (not newItem == None):
newItem = self.currentSelection
#If there is movement, make the scale happen
if not newItem == None:
self.select_item(newItem, self.currentSelection)
class video_details_group(clutter.Group):
font = "Lucida Grande "
title_font_size = 30
main_font_size = 22
plot_font_size = 18
def __init__(self, desired_width):
clutter.Group.__init__(self)
self.width = desired_width
#Add the various labels
self.title = clutter.Label()
self.title.set_font_name(self.font + str(self.title_font_size))
self.title.set_color(clutter.color_parse('White'))
self.title.set_text("")
self.title.set_ellipsize(pango.ELLIPSIZE_END)
self.add(self.title)
#Not sure how to get the row height before the text is set :(
self.row_gap = self.title.get_height()
self.year = clutter.Label()
self.year.set_font_name(self.font + str(self.main_font_size))
self.year.set_color(clutter.color_parse('White'))
self.year.set_text("")
self.year.set_opacity(220)
self.year.set_ellipsize(pango.ELLIPSIZE_END)
self.year.set_position(0, self.row_gap)
self.add(self.year)
self.director = clutter.Label()
self.director.set_font_name(self.font + str(self.main_font_size))
self.director.set_color(clutter.color_parse('White'))
self.director.set_text("")
self.director.set_opacity(220)
self.director.set_ellipsize(pango.ELLIPSIZE_END)
self.director.set_position(int(self.year.get_width()), self.row_gap)
self.add(self.director)
self.plot = clutter.Label()
self.plot.set_font_name(self.font + str(self.plot_font_size))
self.plot.set_color(clutter.color_parse('White'))
self.plot.set_text("")
self.plot.set_opacity(220)
#self.plot.set_ellipsize(pango.ELLIPSIZE_END)
self.plot.set_position(0, int(self.row_gap*2))
self.add(self.plot)
self.show_all()
def set_video(self, video):
self.video = video
self.title.set_text(video.title)
self.title.set_width(self.width)
self.year.set_text("Year: " + str(video.year))
self.director.set_text(" Director: " + str(video.director))
self.director.set_position(int(self.year.get_width()), self.row_gap)
self.director.set_width(int(self.width - self.year.get_width()))
self.plot.set_text(video.plot)
self.plot.set_width(self.width)