gloss-mc/modules/video_player/video_player.py

370 lines
16 KiB
Python

import pygtk
import gtk
import pygst
import gst
import gobject
import pango
import clutter
import os
from clutter import cluttergst
from VideoController import VideoController
from modules.video_player.elements.cover_viewer import coverViewer
from modules.video_player.elements.video_details import video_details
from modules.video_player.elements.folder_menu import folderMenu
from modules.video_player.video_object import videoItem
#===============================================================================
#This module displays a basic cover viewer for videos within myth's Mythvideo table
#It defines the following theme elements:
#video_menu_image (Texture): The graphic used on the menu to represent this module
#video_cover_bg_image (Texture): Graphic that serves as the background for the cover viewer grid
#===============================================================================
class Module():
title = "Videos"
STATE_FOLDERS, STATE_COVERS, STATE_VIDEO = range(3)
#The are just some default values to be used in case of an emergency
coverViewerWidth = 750
coverViewerHeight = 600
coverViewerPosX = 300
coverViewerPosY = 50
coverViewerRows = 3
coverViewerColumns = 4
coverDetailsWidth = 750
foldersPosX = 50
foldersPosX = 50
cover_size = int(coverViewerWidth / coverViewerColumns)
#Basic UI elements
currentViewer = None
backdrop = None
video_details = None
covers_background = None
def __init__(self, glossMgr, dbMgr):
self.stage = glossMgr.get_stage()
self.glossMgr = glossMgr
self.dbMgr = dbMgr
self.setup_ui()
self.viewerLibrary = []
self.folderLibrary = []
self.videoController = VideoController(glossMgr)
#Setup initial vars
self.is_playing = False
self.controlState = self.STATE_FOLDERS
#This block can be moved to begin() but causes a performance hit when loading the module *shrug*
#All it does is load the initial folders into the viewers
self.baseDir = dbMgr.get_setting("VideoStartupDir")
if self.glossMgr.debug: print "VideoPlayer: Base directory=" + str(self.baseDir)
self.cwd = self.baseDir
self.folder_level = 0
self.base_folder_menu = folderMenu(glossMgr, self.coverViewerRows, self.folders_cover_size)
self.base_folder_menu.set_position(self.foldersPosX, self.foldersPosY)
self.folderLibrary.append(self.base_folder_menu)
self.load_base_folders(self.baseDir, self.base_folder_menu)
#Set the current viewer
self.currentViewer = self.base_folder_menu.get_current_viewer()
#self.currentViewer.set_depth(self.currentViewer.cover_size)
#Create the details group
self.video_details = video_details(self.coverDetailsWidth)
self.video_details.set_position(self.coverDetailsPosX, self.coverDetailsPosY)
def setup_ui(self):
self.menu_image = self.glossMgr.themeMgr.get_texture("video_menu_image", None, None)
#Add the background
self.covers_background = self.glossMgr.themeMgr.get_texture("video_covers_background", self.stage, None)
#Set the number of rows / cols for the viewer (These come from the MythVideo settings in the backend DB)
tmp_rows = self.dbMgr.get_setting("VideoGalleryRowsPerPage")
tmp_cols = self.dbMgr.get_setting("VideoGalleryColsPerPage")
if not tmp_rows is None: self.coverViewerRows = int(tmp_rows)
if not tmp_cols is None: self.coverViewerColumns = int(tmp_cols)
#Set the size / position of the cover_viewer
element = self.glossMgr.themeMgr.search_docs("video_cover_viewer", "main").childNodes
(self.coverViewerWidth, self.coverViewerHeight) = self.glossMgr.themeMgr.get_dimensions(element, self.stage)
(self.coverViewerPosX, self.coverViewerPosY) = self.glossMgr.themeMgr.get_position(element, self.stage)
#Set the all important cover size
self.cover_size = int(self.coverViewerWidth / self.coverViewerColumns)
#set the size / position of the details group
element = self.glossMgr.themeMgr.search_docs("video_cover_details", "main").childNodes
(self.coverDetailsWidth, self.coverDetailsHeight) = self.glossMgr.themeMgr.get_dimensions(element, self.stage)
(self.coverDetailsPosX, self.coverDetailsPosY) = self.glossMgr.themeMgr.get_position(element, self.stage)
#set the size / position of the folder menu
element = self.glossMgr.themeMgr.search_docs("video_folder_menu", "main").childNodes
(self.foldersWidth, self.foldersHeight) = self.glossMgr.themeMgr.get_dimensions(element, self.stage)
(self.foldersPosX, self.foldersPosY) = self.glossMgr.themeMgr.get_position(element, self.stage)
self.folders_cover_size = self.foldersWidth #int(self.foldersWidth / self.coverViewerColumns)
def load_base_folders(self, dirPath, folder_menu):
try:
new_file_list = os.listdir(dirPath)
except os.error, (errno, errstr):
self.glossMgr.display_msg("VideoPlayer File Error", "Could not load Video directory")
return
#Videos and Directories
for dir_entry in new_file_list:
tempPath = dirPath + "/" + dir_entry
if os.path.isdir(tempPath) and not ( dir_entry[0] == "."):
tempViewer = coverViewer(self.glossMgr, self.coverViewerWidth, self.coverViewerHeight, self.coverViewerRows, self.coverViewerColumns)
self.loadDir(tempPath, tempViewer)
folder_menu.add_base_dir(dir_entry, tempViewer)
def loadDir(self, dirPath, cover_viewer):
if not os.path.isdir(dirPath):
print "ERROR VideoPlayer: Invalid video path"
return None
final_file_list = []
new_file_list = os.listdir(dirPath)
#Videos and Directories
for dir_entry in new_file_list:
if os.path.isdir(dirPath + "/" + dir_entry) and not ( dir_entry[0] == "."):
cover_viewer.add_folder(dir_entry)
#print dir_entry
else:
final_file_list.append(dir_entry)
#Check if we're empty
if len(final_file_list) == 0:
return
#Make sure the dirPath ends in "/"
if not dirPath[-1] == "/":
dirPath = dirPath + "/"
#Generate some SQL to retrieve videos that were in the final_file_list
#Load the videos into the cover viewer
sql = "SELECT * FROM videometadata WHERE filename IN ("
for filename in final_file_list:
filename = dirPath + filename
sql = sql + "\"" + filename + "\", "
sqlLength = int(len(sql) - 2)
sql = sql[:sqlLength]
sql = sql + ")"
if self.glossMgr.debug: print "Video SQL: " + sql
results = self.dbMgr.run_sql(sql)
#Check for null return
if results == None:
print "VideoPlayer: No connection to DB or no videos found in DB"
return None
#Else add the entries in
for record in results:
tempVideo = videoItem()
tempVideo.importFromMythObject(record)
cover_viewer.add_video(tempVideo)
#Action to take when menu item is selected
def action(self):
return self
def on_key_press_event (self, stage, event):
up = clutter.keysyms.Up
down = clutter.keysyms.Down
left = clutter.keysyms.Left
right= clutter.keysyms.Right
if event.keyval == clutter.keysyms.q:
clutter.main_quit()
#*****************************************************************
#"State based input handling
if self.controlState == self.STATE_FOLDERS:
if event.keyval == clutter.keysyms.Escape:
return True
elif event.keyval == right:
self.controlState = self.STATE_COVERS
self.currentViewer = self.folderLibrary[self.folder_level].get_current_viewer()
self.currentViewer.select_first()
self.folderLibrary[self.folder_level].on_key_press_event(event)
self.currentViewer = self.folderLibrary[self.folder_level].get_current_viewer()
#**********************************************************
elif self.controlState == self.STATE_VIDEO:
if event.keyval == clutter.keysyms.Escape:
self.videoController.stop_video()
self.is_playing = False
self.controlState = self.STATE_COVERS
else:
self.videoController.on_key_press_event(event)
return False
if event.keyval == clutter.keysyms.p:
if self.paused:
self.unpause()
else:
self.pause()
#**********************************************************
elif self.controlState == self.STATE_COVERS:
if (event.keyval == up) or (event.keyval == down) or (event.keyval == left) or (event.keyval == right):
timeline = self.currentViewer.on_key_press_event(event)
video = self.currentViewer.get_current_video()
#Do a check to see if the input queue is currently processing
#
if not self.currentViewer.input_queue.is_in_queue():
if not video is None:
self.video_details.set_video_bare(video)
self.currentViewer.set_details_update(False, None)
else:
self.video_details.set_folder(self.currentViewer.folderLibrary[(self.currentViewer.currentSelection-len(self.currentViewer.folderLibrary))])
#self.video_details.clear()
else:
self.currentViewer.set_details_update(True, self.video_details)
if event.keyval == clutter.keysyms.Return:
#Find whether the current item is a folder or video
item = self.currentViewer.get_current_item()
if item.isFolder:
self.glossMgr.display_msg("Msg", "Its a folder")
else:
self.play_video()
if event.keyval == clutter.keysyms.Escape:
self.currentViewer.select_none()
self.controlState = self.STATE_FOLDERS
def begin(self, glossMgr):
#Check that the library actually contains something
#if len(self.currentViewer.textureLibrary) == 0:
if self.currentViewer is None:
self.glossMgr.display_msg("Error: No videos", "There are no videos available in the library. This maybe caused by an empty library or a failed connection to the server.")
self.stop()
return
#Create a backdrop for the player. In this case we just use the same background as the menus
self.backdrop = glossMgr.get_themeMgr().get_texture("background", None, None) #clutter.CloneTexture(glossMgr.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)
#Add the covers background
self.covers_background.set_opacity(0)
self.covers_background.show()
self.stage.add(self.covers_background)
#Add the folders menu
self.folderLibrary[0].set_opacity(0)
self.folderLibrary[0].show()
self.stage.add(self.folderLibrary[0])
#Add the cover viewer
self.currentViewer.set_opacity(0)
self.currentViewer.show_all()
self.currentViewer.show()
self.currentViewer.set_position(self.coverViewerPosX, self.coverViewerPosY)
self.stage.add(self.currentViewer)
#Add the video details
self.video_details.set_opacity(0)
self.video_details.show_all()
self.video_details.show()
self.stage.add(self.video_details)
#Fade everything in
timeline_begin = clutter.Timeline(10,40)
alpha = clutter.Alpha(timeline_begin, clutter.ramp_inc_func)
self.begin_behaviour = clutter.BehaviourOpacity(opacity_start=0, opacity_end=255, alpha=alpha)
self.begin_behaviour.apply(self.backdrop)
self.begin_behaviour.apply(self.folderLibrary[0])
self.begin_behaviour.apply(self.covers_background)
self.begin_behaviour.apply(self.currentViewer)
self.begin_behaviour.apply(self.video_details)
#self.viewerLibrary[0].set_position(50, 40)
#self.currentViewer.toggle_details() #Turns the details group on
#self.currentViewer.select_first()
timeline_begin.start()
#self.folder_menu = folderMenu(self.stage, self.currentViewer.num_visible_rows, self.currentViewer.cover_size)
#self.folder_menu.set_dir_cover_viewer(self.currentViewer)
def stop(self):
self.glossMgr.currentPlugin = None
#Fade everything out
timeline_stop = clutter.Timeline(10,30)
alpha = clutter.Alpha(timeline_stop, clutter.ramp_inc_func)
self.stop_behaviour = clutter.BehaviourOpacity(opacity_start=255, opacity_end=0, alpha=alpha)
if not self.currentViewer is None: self.stop_behaviour.apply(self.currentViewer)
if not self.backdrop is None: self.stop_behaviour.apply(self.backdrop)
if not self.folderLibrary[self.folder_level] is None: self.stop_behaviour.apply(self.folderLibrary[self.folder_level])
if not self.video_details is None: self.stop_behaviour.apply(self.video_details)
if not self.covers_background is None: self.stop_behaviour.apply(self.covers_background)
timeline_stop.connect('completed', self.destroyPlugin)
timeline_stop.start()
def destroyPlugin(self, data):
self.stage.remove(self.currentViewer)
self.stage.remove(self.folderLibrary[self.folder_level])
self.stage.remove(self.video_details)
self.stage.remove(self.covers_background)
self.backdrop.hide()
#self.stage.remove(self.overlay)
def play_video(self):
vid = self.currentViewer.get_current_item().video
uri = "file://" + str(vid.filename)
self.videoController.play_video(uri, self)
self.is_playing = True
self.controlState = self.STATE_VIDEO
self.stage.remove(self.currentViewer)
self.stage.remove(self.base_folder_menu)
def stop_video(self):
if not self.is_playing:
return
self.is_playing = False
timeline = clutter.Timeline(15, 25)
self.currentViewer.set_opacity(0)
self.base_folder_menu.set_opacity(0)
alpha = clutter.Alpha(timeline, clutter.ramp_inc_func)
self.behaviour = clutter.BehaviourOpacity(opacity_start=0, opacity_end=255, alpha=alpha)
self.behaviour.apply(self.currentViewer)
self.behaviour.apply(self.base_folder_menu)
self.stage.add(self.currentViewer)
self.stage.add(self.base_folder_menu)
self.currentViewer.show()
timeline.start()
def pause(self):
pass
def unpause(self):
pass