From b24f63e82643df889d2cb887567a9c645bdcd855 Mon Sep 17 00:00:00 2001 From: Razvan Deaconescu Date: Sat, 6 Aug 2011 15:49:12 +0300 Subject: [PATCH] Add non-interactive player to next-share. --- .../next-share/BaseLib/Video/SimPlayer.py | 998 ++++++++++++++++++ 1 file changed, 998 insertions(+) create mode 100644 instrumentation/next-share/BaseLib/Video/SimPlayer.py diff --git a/instrumentation/next-share/BaseLib/Video/SimPlayer.py b/instrumentation/next-share/BaseLib/Video/SimPlayer.py new file mode 100644 index 0000000..fb0a75e --- /dev/null +++ b/instrumentation/next-share/BaseLib/Video/SimPlayer.py @@ -0,0 +1,998 @@ +# Written by Arno Bakker +# see LICENSE.txt for license information +from threading import currentThread +from traceback import print_exc +import inspect +import os +import re +import sys +import urllib +import urlparse +import wx + +from BaseLib.Video.defs import * +from BaseLib.Video.VideoServer import VideoHTTPServer,VideoRawVLCServer +from BaseLib.Video.utils import win32_retrieve_video_play_command,win32_retrieve_playcmd_from_mimetype,quote_program_path,videoextdefaults + +from BaseLib.Core.simpledefs import * +from BaseLib.Core.Utilities.unicode import unicode2str,bin2unicode + +from BaseLib.Video.CachingStream import SmartCachingStream +from BaseLib.Video.Ogg import is_ogg,OggMagicLiveStream + +DEBUG = False + +if sys.platform == "linux2" or sys.platform == "darwin": + USE_VLC_RAW_INTERFACE = False +else: + USE_VLC_RAW_INTERFACE = False # False for Next-Share + + +class SimPlayer(VideoPlayer): + + __single = None + + def __init__(self,httpport=6880): + if SimPlayer.__single: + raise RuntimeError, "SimPlayer is singleton" + SimPlayer.__single = self + self.videoframe = None + self.extprogress = None + self.vod_download = None + self.playbackmode = None + self.preferredplaybackmode = None + self.other_downloads = None + self.closeextplayercallback = None + + self.videohttpservport = httpport + self.videohttpserv = None + # Must create the instance here, such that it won't get garbage collected + # TODO: have to substitute this call + #self.videorawserv = VideoRawVLCServer.getInstance() + + self.resume_by_system = 1 + self.user_download_choice = None + + def getInstance(*args, **kw): + if SimPlayer.__single is None: + SimPlayer(*args, **kw) + return SimPlayer.__single + getInstance = staticmethod(getInstance) + + def register(self,utility,preferredplaybackmode=None,closeextplayercallback=None): + + self.utility = utility # TEMPARNO: make sure only used for language strings + + self.preferredplaybackmode = preferredplaybackmode + self.determine_playbackmode() + + if self.playbackmode == PLAYBACKMODE_INTERNAL: + # use local wrapper + self.vlcwrap = None + self.supportedvodevents = [VODEVENT_START,VODEVENT_PAUSE,VODEVENT_RESUME] + else: + self.vlcwrap = None + # Can't pause when external player + self.supportedvodevents = [VODEVENT_START] + + if closeextplayercallback is not None: + self.closeextplayercallback = closeextplayercallback + + def set_other_downloads(self, other_downloads): + """A boolean indicating whether there are other downloads running at this time""" + self.other_downloads = other_downloads + + def get_vlcwrap(self): + return self.vlcwrap + + def get_supported_vod_events(self): + return self.supportedvodevents + + def set_videoframe(self,videoframe): + self.videoframe = videoframe + + + def play_file(self,dest): + """ Play video file from disk """ + if DEBUG: + print >>sys.stderr,"videoplay: Playing file from disk",dest + + (prefix,ext) = os.path.splitext(dest) + [mimetype,cmd] = self.get_video_player(ext,dest) + + if DEBUG: + print >>sys.stderr,"videoplay: play_file: cmd is",cmd + + self.launch_video_player(cmd) + + def play_file_via_httpserv(self,dest): + """ Play a file via our internal HTTP server. Needed when the user + selected embedded VLC as player and the filename contains Unicode + characters. + """ + if DEBUG: + print >>sys.stderr,"videoplay: Playing file with Unicode filename via HTTP" + + (prefix,ext) = os.path.splitext(dest) + videourl = self.create_url(self.videohttpserv,'/'+os.path.basename(prefix+ext)) + [mimetype,cmd] = self.get_video_player(ext,videourl) + + stream = open(dest,"rb") + stats = os.stat(dest) + length = stats.st_size + streaminfo = {'mimetype':mimetype,'stream':stream,'length':length} + self.videohttpserv.set_inputstream(streaminfo) + + self.launch_video_player(cmd) + + + + def play_url(self,url): + """ Play video file from network or disk """ + if DEBUG: + print >>sys.stderr,"videoplay: Playing file from url",url + + self.determine_playbackmode() + + t = urlparse.urlsplit(url) + dest = t[2] + + # VLC will play .flv files, but doesn't like the URLs that YouTube uses, + # so quote them + if self.playbackmode != PLAYBACKMODE_INTERNAL: + if sys.platform == 'win32': + x = [t[0],t[1],t[2],t[3],t[4]] + n = urllib.quote(x[2]) + if DEBUG: + print >>sys.stderr,"videoplay: play_url: OLD PATH WAS",x[2],"NEW PATH",n + x[2] = n + n = urllib.quote(x[3]) + if DEBUG: + print >>sys.stderr,"videoplay: play_url: OLD QUERY WAS",x[3],"NEW PATH",n + x[3] = n + url = urlparse.urlunsplit(x) + elif url[0] != '"' and url[0] != "'": + # to prevent shell escape problems + # TODO: handle this case in escape_path() that now just covers spaces + url = "'"+url+"'" + + (prefix,ext) = os.path.splitext(dest) + [mimetype,cmd] = self.get_video_player(ext,url) + + if DEBUG: + print >>sys.stderr,"videoplay: play_url: cmd is",cmd + + self.launch_video_player(cmd) + + + def play_stream(self,streaminfo): + if DEBUG: + print >>sys.stderr,"videoplay: play_stream" + + self.determine_playbackmode() + + if self.playbackmode == PLAYBACKMODE_INTERNAL: + if USE_VLC_RAW_INTERFACE: + # Play using direct callbacks from the VLC C-code + self.launch_video_player(None,streaminfo=streaminfo) + else: + # Play via internal HTTP server + self.videohttpserv.set_inputstream(streaminfo,'/') + url = self.create_url(self.videohttpserv,'/') + + self.launch_video_player(url,streaminfo=streaminfo) + else: + # External player, play stream via internal HTTP server + path = '/' + self.videohttpserv.set_inputstream(streaminfo,path) + url = self.create_url(self.videohttpserv,path) + + [mimetype,cmd] = self.get_video_player(None,url,mimetype=streaminfo['mimetype']) + self.launch_video_player(cmd) + + + def launch_video_player(self,cmd,streaminfo=None): + if self.playbackmode == PLAYBACKMODE_INTERNAL: + + if cmd is not None: + # Play URL from network or disk + self.videoframe.get_videopanel().Load(cmd,streaminfo=streaminfo) + else: + # Play using direct callbacks from the VLC C-code + self.videoframe.get_videopanel().Load(cmd,streaminfo=streaminfo) + + self.videoframe.show_videoframe() + self.videoframe.get_videopanel().StartPlay() + else: + # Launch an external player + # Play URL from network or disk + self.exec_video_player(cmd) + + + def stop_playback(self,reset=False): + """ Stop playback in current video window """ + if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None: + self.videoframe.get_videopanel().Stop() + if reset: + self.videoframe.get_videopanel().Reset() + self.set_vod_download(None) + + def show_loading(self): + if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None: + self.videoframe.get_videopanel().ShowLoading() + + def close(self): + """ Stop playback and close current video window """ + if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None: + self.videoframe.hide_videoframe() + self.set_vod_download(None) + + def play(self,ds, selectedinfilename=None): + """ Used by Tribler Main """ + self.determine_playbackmode() + + d = ds.get_download() + tdef = d.get_def() + videofiles = d.get_dest_files(exts=videoextdefaults) + + if len(videofiles) == 0: + print >>sys.stderr,"videoplay: play: No video files found! Let user select" + # Let user choose any file + videofiles = d.get_dest_files(exts=None) + + + selectedoutfilename= None + if selectedinfilename is None: + # User didn't select file to play, select if there is a single, or ask + if len(videofiles) > 1: + infilenames = [] + for infilename,diskfilename in videofiles: + infilenames.append(infilename) + selectedinfilename = self.ask_user_to_select_video(infilenames) + print >> sys.stderr , "selectedinfilename == None" , selectedinfilename , len(selectedinfilename) + if selectedinfilename is None: + print >>sys.stderr,"videoplay: play: User selected no video" + return + for infilename,diskfilename in videofiles: + if infilename == selectedinfilename: + selectedoutfilename = diskfilename + else: + selectedinfilename = videofiles[0][0] + selectedoutfilename = videofiles[0][1] + else: + #print >> sys.stderr , "videoplay: play: selectedinfilename not None" , selectedinfilename , len(selectedinfilename) + for infilename,diskfilename in videofiles: + if infilename == selectedinfilename: + selectedoutfilename = diskfilename + if self.videoframe is not None: + self.videoframe.get_videopanel().SetLoadingText(selectedinfilename) + + # 23/02/10 Boudewijn: This Download does not contain the + # selectedinfilename in the available files. It is likely + # that this is a multifile torrent and that another file was + # previously selected for download. + if selectedoutfilename is None: + return self.play_vod(ds, selectedinfilename) + + print >> sys.stderr , "videoplay: play: PROGRESS" , ds.get_progress() + complete = ds.get_progress() == 1.0 or ds.get_status() == DLSTATUS_SEEDING + + bitrate = tdef.get_bitrate(selectedinfilename) + if bitrate is None and not complete: + video_analyser_path = self.utility.config.Read('videoanalyserpath') + if not os.access(video_analyser_path,os.F_OK): + self.onError(self.utility.lang.get('videoanalysernotfound'),video_analyser_path,self.utility.lang.get('videoanalyserwhereset')) + return + + # The VLC MediaControl API's playlist_add_item() doesn't accept unicode filenames. + # So if the file to play is unicode we play it via HTTP. The alternative is to make + # Tribler save the data in non-unicode filenames. + # + flag = self.playbackmode == PLAYBACKMODE_INTERNAL and not self.is_ascii_filename(selectedoutfilename) + + if complete: + print >> sys.stderr, 'videoplay: play: complete' + if flag: + self.play_file_via_httpserv(selectedoutfilename) + else: + self.play_file(selectedoutfilename) + + self.manage_others_when_playing_from_file(d) + # Fake it, to get DL status reporting for right Download + self.set_vod_download(d) + else: + print >> sys.stderr, 'videoplay: play: not complete' + self.play_vod(ds,selectedinfilename) + + + def play_vod(self,ds,infilename): + """ Called by GUI thread when clicking "Play ASAP" button """ + + d = ds.get_download() + tdef = d.get_def() + # For multi-file torrent: when the user selects a different file, play that + oldselectedfile = None + if not tdef.get_live() and ds.is_vod() and tdef.is_multifile_torrent(): + oldselectedfiles = d.get_selected_files() + oldselectedfile = oldselectedfiles[0] # Should be just one + + # 1. (Re)Start torrent in VOD mode + switchfile = (oldselectedfile is not None and oldselectedfile != infilename) + + print >> sys.stderr, ds.is_vod() , switchfile , tdef.get_live() + if not ds.is_vod() or switchfile or tdef.get_live(): + + + if switchfile: + if self.playbackmode == PLAYBACKMODE_INTERNAL: + self.videoframe.get_videopanel().Reset() + + #[proceed,othertorrentspolicy] = self.warn_user(ds,infilename) + proceed = True + othertorrentspolicy = OTHERTORRENTS_STOP_RESTART + + if not proceed: + # User bailing out + return + + if DEBUG: + print >>sys.stderr,"videoplay: play_vod: Enabling VOD on torrent",`d.get_def().get_name()` + + self.manage_other_downloads(othertorrentspolicy,targetd = d) + + # Restart download + d.set_video_event_callback(self.sesscb_vod_event_callback) + d.set_video_events(self.get_supported_vod_events()) + if d.get_def().is_multifile_torrent(): + d.set_selected_files([infilename]) + print >>sys.stderr,"videoplay: play_vod: Restarting existing Download",`ds.get_download().get_def().get_infohash()` + self.set_vod_download(d) + d.restart() + + def restart_other_downloads(self, download_state_list): + def get_vod_download_status(default): + for download_state in download_state_list: + if self.vod_download == download_state.get_download(): + return download_state.get_status() + return default + + if self.resume_by_system: + # resume when there is no VOD download + if self.vod_download is None: + self.resume_by_system += 1 + if DEBUG: print >> sys.stderr, "VideoPlayer: restart_other_downloads: Resume because vod_download is None", "(%d)" % self.resume_by_system + + # resume when the VOD download is not part of download_state_list + elif not self.vod_download in [download_state.get_download() for download_state in download_state_list]: + self.resume_by_system += 1 + if DEBUG: + print >> sys.stderr, "VideoPlayer: restart_other_downloads: Resume because", `self.vod_download.get_def().get_name()`, "not in list", "(%d)" % self.resume_by_system + print >> sys.stderr, "VideoPlayer: list:", `[download_state.get_download().get_def().get_name() for download_state in download_state_list]` + + # resume when the VOD download has finished downloading + elif not get_vod_download_status(DLSTATUS_ALLOCATING_DISKSPACE) in (DLSTATUS_ALLOCATING_DISKSPACE, DLSTATUS_WAITING4HASHCHECK, DLSTATUS_HASHCHECKING, DLSTATUS_DOWNLOADING): + self.resume_by_system += 1 + if DEBUG: + print >> sys.stderr, "VideoPlayer: restart_other_downloads: Resume because vod_download_status is inactive", "(%d)" % self.resume_by_system + print >> sys.stderr, "VideoPlayer: status:", dlstatus_strings[get_vod_download_status(DLSTATUS_ALLOCATING_DISKSPACE)] + + # otherwise we do not resume + else: + self.resume_by_system = 1 + + # because of threading issues it is possible that we have + # false positives. therefore we will only resume torrents + # after we checked 2 times (once every second) + if self.resume_by_system > 2: + self.resume_by_system = 0 + + # sometimes the self.vod_download stays set to a + # download class that is no longer downloading + self.set_vod_download(None) + + for download_state in download_state_list: + download = download_state.get_download() + torrent_def = download.get_def() + infohash = torrent_def.get_infohash() + + from BaseLib.Main.vwxGUI.UserDownloadChoice import UserDownloadChoice + self.user_download_choice = UserDownloadChoice.get_singleton() + user_state = self.user_download_choice.get_download_state(infohash) + + # resume a download unless the user explisitly + # stopped the download + if not user_state == "stop": + if DEBUG: print >> sys.stderr, "VideoPlayer: restart_other_downloads: Restarting", `download.get_def().get_name()` + download.set_mode(DLMODE_NORMAL) + download.restart() + + def manage_other_downloads(self,othertorrentspolicy, targetd = None): + self.resume_by_system = 1 + if DEBUG: print >> sys.stderr, "VideoPlayer: manage_other_downloads" + + policy_stop = othertorrentspolicy == OTHERTORRENTS_STOP or \ + othertorrentspolicy == OTHERTORRENTS_STOP_RESTART + + for download in self.utility.session.get_downloads(): + if download.get_def().get_live(): + # Filter out live torrents, they are always + # removed. They stay in myPreferenceDB so can be + # restarted. + if DEBUG: print >>sys.stderr,"VideoPlayer: manage_other_downloads: Remove live", `download.get_def().get_name()` + self.utility.session.remove_download(download) + + elif download == targetd: + if DEBUG: print >>sys.stderr,"VideoPlayer: manage_other_downloads: Leave", `download.get_def().get_name()` + download.stop() + + elif policy_stop: + if DEBUG: print >>sys.stderr,"VideoPlayer: manage_other_downloads: Stop", `download.get_def().get_name()` + download.stop() + + else: + if DEBUG: print >>sys.stderr,"VideoPlayer: manage_other_downloads: Ignore", `download.get_def().get_name()` + + def manage_others_when_playing_from_file(self,targetd): + """ When playing from file, make sure all other Downloads are no + longer in VOD mode, so they won't interrupt the playback. + """ + activetorrents = self.utility.session.get_downloads() + for d in activetorrents: + if d.get_mode() == DLMODE_VOD: + if d.get_def().get_live(): + #print >>sys.stderr,"videoplay: manage_when_file_play: Removing live",`d.get_def().get_name()` + self.utility.session.remove_download(d) + else: + #print >>sys.stderr,"videoplay: manage_when_file_play: Restarting in NORMAL mode",`d.get_def().get_name()` + d.stop() + d.set_mode(DLMODE_NORMAL) + d.restart() + + + + + def start_and_play(self,tdef,dscfg, selectedinfilename = None): + """ Called by GUI thread when Tribler started with live or video torrent on cmdline """ + + # ARNO50: > Preview1: TODO: make sure this works better when Download already existed. + + + if selectedinfilename == None: + if not tdef.get_live(): + videofiles = tdef.get_files(exts=videoextdefaults) + if len(videofiles) == 1: + selectedinfilename = videofiles[0] + elif len(videofiles) > 1: + selectedinfilename = self.ask_user_to_select_video(videofiles) + + if selectedinfilename or tdef.get_live(): + if tdef.is_multifile_torrent(): + dscfg.set_selected_files([selectedinfilename]) + + othertorrentspolicy = OTHERTORRENTS_STOP_RESTART + self.manage_other_downloads(othertorrentspolicy,targetd = None) + + # Restart download + dscfg.set_video_event_callback(self.sesscb_vod_event_callback) + dscfg.set_video_events(self.get_supported_vod_events()) + print >>sys.stderr,"videoplay: Starting new VOD/live Download",`tdef.get_name()` + + download = self.utility.session.start_download(tdef,dscfg) + + if self.videoframe is not None: + self.videoframe.get_videopanel().SetLoadingText(selectedinfilename) + + + self.set_vod_download(download) + return download + else: + return None + + + def sesscb_vod_event_callback(self,d,event,params): + """ Called by the Session when the content of the Download is ready + + Called by Session thread """ + + print >>sys.stderr,"videoplay: sesscb_vod_event_callback called",currentThread().getName(),"###########################################################" + wx.CallAfter(self.gui_vod_event_callback,d,event,params) + + def gui_vod_event_callback(self,d,event,params): + """ Also called by SwarmPlayer """ + + print >>sys.stderr,"videoplay: gui_vod_event:",event + if event == VODEVENT_START: + filename = params["filename"] + mimetype = params["mimetype"] + stream = params["stream"] + length = params["length"] + + if filename: + self.play_file(filename) + else: + if d.get_def().get_live(): + cachestream = stream + blocksize = d.get_def().get_piece_length() + else: + piecelen = d.get_def().get_piece_length() + if piecelen > 2 ** 17: + # Arno, 2010-01-21: + # Workaround for streams with really large piece + # sizes. For some content/containers, VLC can do + # GET X-, GET X+10K-, GET X+20K HTTP requests + # and we would answer these by putting megabytes + # into the stream buffer, of which only 10K would be + # used. This kills performance. Hence I add a caching + # stream that tries to resolve answers from its internal + # buffer, before reading the engine's stream. + # This works, but only if the HTTP server doesn't + # read too aggressively, i.e., uses small blocksize. + # + cachestream = SmartCachingStream(stream) + + blocksize = max(32768,piecelen/8) + else: + cachestream = stream + blocksize = piecelen + + if d.get_def().get_live() and is_ogg(d.get_def().get_name_as_unicode()): + # Live Ogg stream. To support this we need to do + # two things: + # 1. Write Ogg headers (stored in .tstream) + # 2. Find first Ogg page in stream. + cachestream = OggMagicLiveStream(d.get_def(),stream) + + + # Estimate duration. Video player (e.g. VLC) often can't tell + # when streaming. + estduration = None + if d.get_def().get_live(): + # Set correct Ogg MIME type + if is_ogg(d.get_def().get_name_as_unicode()): + params['mimetype'] = 'application/ogg' + else: + file = None + if d.get_def().is_multifile_torrent(): + file = d.get_selected_files()[0] + bitrate = d.get_def().get_bitrate(file) + if bitrate is not None: + estduration = float(length) / float(bitrate) + + # Set correct Ogg MIME type + if file is None: + if is_ogg(d.get_def().get_name_as_unicode()): + params['mimetype'] = 'application/ogg' + else: + if is_ogg(file): + params['mimetype'] = 'application/ogg' + + + + streaminfo = {'mimetype':mimetype,'stream':cachestream,'length':length,'blocksize':blocksize,'estduration':estduration} + self.play_stream(streaminfo) + + elif event == VODEVENT_PAUSE: + if self.videoframe is not None: + self.videoframe.get_videopanel().PlayPause() + self.set_player_status("Buffering...") + elif event == VODEVENT_RESUME: + if self.videoframe is not None: + self.videoframe.get_videopanel().PlayPause() + self.set_player_status("") + + def ask_user_to_select_video(self,videofiles): + dlg = VideoChooser(self.videoframe.get_window(),self.utility,videofiles,title='Tribler',expl='Select which file to play') + result = dlg.ShowModal() + if result == wx.ID_OK: + index = dlg.getChosenIndex() + filename = videofiles[index] + else: + filename = None + dlg.Destroy() + return filename + + def is_ascii_filename(self,filename): + if isinstance(filename,str): + return True + try: + filename.encode('ascii','strict') + return True + except: + print_exc() + return False + + def warn_user(self,ds,infilename): + + islive = ds.get_download().get_def().get_live() + if islive and not self.other_downloads: + # If it's the only download and live, don't warn. + return + + dlg = VODWarningDialog(self.videoframe.get_window(),self.utility,ds,infilename,self.other_downloads,islive) + result = dlg.ShowModal() + othertorrentspolicy = dlg.get_othertorrents_policy() + dlg.Destroy() + return [result == wx.ID_OK,othertorrentspolicy] + + def create_url(self,videoserver,upath): + schemeserv = 'http://127.0.0.1:'+str(videoserver.get_port()) + asciipath = unicode2str(upath) + return schemeserv+urllib.quote(asciipath) + + + + def get_video_player(self,ext,videourl,mimetype=None): + + video_player_path = self.utility.config.Read('videoplayerpath') + if DEBUG: + print >>sys.stderr,"videoplay: Default player is",video_player_path + + if mimetype is None: + if sys.platform == 'win32': + # TODO: Use Python's mailcap facility on Linux to find player + [mimetype,playcmd] = win32_retrieve_video_play_command(ext,videourl) + if DEBUG: + print >>sys.stderr,"videoplay: Win32 reg said playcmd is",playcmd + + if mimetype is None: + if ext == '.avi': + # Arno, 2010-01-08: Hmmm... video/avi is not official registered at IANA + mimetype = 'video/avi' + elif ext == '.mpegts' or ext == '.ts': + mimetype = 'video/mp2t' + else: + mimetype = 'video/mpeg' + else: + if sys.platform == 'win32': + [mimetype,playcmd] = win32_retrieve_playcmd_from_mimetype(mimetype,videourl) + + if self.playbackmode == PLAYBACKMODE_INTERNAL: + if DEBUG: + print >>sys.stderr,"videoplay: using internal player" + return [mimetype,videourl] + elif self.playbackmode == PLAYBACKMODE_EXTERNAL_MIME and sys.platform == 'win32': + if playcmd is not None: + cmd = 'start /B "TriblerVideo" '+playcmd + return [mimetype,cmd] + + if DEBUG: + print >>sys.stderr,"videoplay: Defaulting to default player",video_player_path + qprogpath = quote_program_path(video_player_path) + #print >>sys.stderr,"videoplay: Defaulting to quoted prog",qprogpath + if qprogpath is None: + return [None,None] + qvideourl = self.escape_path(videourl) + playcmd = qprogpath+' '+qvideourl + if sys.platform == 'win32': + cmd = 'start /B "TriblerVideo" '+playcmd + elif sys.platform == 'darwin': + cmd = 'open -a '+playcmd + else: + cmd = playcmd + if DEBUG: + print >>sys.stderr,"videoplay: using external user-defined player by executing ",cmd + return [mimetype,cmd] + + + + def exec_video_player(self,cmd): + if DEBUG: + print >>sys.stderr,"videoplay: Command is @"+cmd+"@" + # I get a weird problem on Linux. When doing a + # os.popen2("vlc /tmp/file.wmv") I get the following error: + #[00000259] main interface error: no suitable interface module + #[00000001] main private error: interface "(null)" initialization failed + # + # The only thing that appears to work is + # os.system("vlc /tmp/file.wmv") + # but that halts Tribler, as it waits for the created shell to + # finish. Hmmmm.... + # + try: + if sys.platform == 'win32': + #os.system(cmd) + (self.player_out,self.player_in) = os.popen2( cmd, 'b' ) + else: + (self.player_out,self.player_in) = os.popen2( cmd, 'b' ) + except Exception, e: + print_exc() + self.onError(self.utility.lang.get('videoplayerstartfailure'),cmd,str(e.__class__)+':'+str(e)) + + + + def escape_path(self,path): + if path[0] != '"' and path[0] != "'" and path.find(' ') != -1: + if sys.platform == 'win32': + # Add double quotes + path = "\""+path+"\"" + else: + path = "\'"+path+"\'" + return path + + + def onError(self,action,value,errmsg=u''): + self.onMessage(wx.ICON_ERROR,action,value,errmsg) + + def onWarning(self,action,value,errmsg=u''): + self.onMessage(wx.ICON_INFORMATION,action,value,errmsg) + + def onMessage(self,icon,action,value,errmsg=u''): + # Don't use language independence stuff, self.utility may not be + # valid. + msg = action + msg += '\n' + msg += value + msg += '\n' + msg += errmsg + msg += '\n' + dlg = wx.MessageDialog(None, msg, self.utility.lang.get('videoplayererrortitle'), wx.OK|icon) + result = dlg.ShowModal() + dlg.Destroy() + + def set_vod_download(self,d): + self.vod_download = d + + def get_vod_download(self): + return self.vod_download + + # + # Set information about video playback progress that is displayed + # to the user. + # + def set_content_name(self,name): + if self.videoframe is not None: + self.videoframe.get_videopanel().SetContentName(name) + + def set_content_image(self,wximg): + if self.videoframe is not None: + self.videoframe.get_videopanel().SetContentImage(wximg) + + def set_player_status(self,msg): + if self.videoframe is not None: + self.videoframe.get_videopanel().SetPlayerStatus(msg) + + def set_player_status_and_progress(self,msg,pieces_complete): + if self.videoframe is not None: + self.videoframe.get_videopanel().UpdateStatus(msg,pieces_complete) + + def set_save_button(self,enable,savebutteneventhandler): + if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None: + self.videoframe.get_videopanel().EnableSaveButton(enable,savebutteneventhandler) + + def get_state(self): + if self.playbackmode == PLAYBACKMODE_INTERNAL and self.videoframe is not None: + return self.videoframe.get_videopanel().GetState() + else: + return MEDIASTATE_PLAYING + + def determine_playbackmode(self): + feasible = return_feasible_playback_modes(self.utility.getPath()) + if self.preferredplaybackmode in feasible: + self.playbackmode = self.preferredplaybackmode + else: + self.playbackmode = feasible[0] + + def get_playbackmode(self): + return self.playbackmode + + #def set_preferredplaybackmode(self,mode): + # This is a bit complex: If there is no int. player avail we change + # the VideoFrame to contain some minimal info. Would have to dynamically + # change that back if we allow dynamic switching of video player. + # self.preferredplaybackmode = mode + + # + # Internal methods + # + def videohttpserver_error_callback(self,e,url): + """ Called by HTTP serving thread """ + wx.CallAfter(self.videohttpserver_error_guicallback,e,url) + + def videohttpserver_error_guicallback(self,e,url): + print >>sys.stderr,"videoplay: Video HTTP server reported error",str(e) + # if e[0] == ECONNRESET and self.closeextplayercallback is not None: + if self.closeextplayercallback is not None: + self.closeextplayercallback() + + def videohttpserver_set_status_callback(self,status): + """ Called by HTTP serving thread """ + wx.CallAfter(self.videohttpserver_set_status_guicallback,status) + + def videohttpserver_set_status_guicallback(self,status): + self.videoframe.get_videopanel().SetPlayerStatus(status) + + + + +class VideoChooser(wx.Dialog): + + def __init__(self,parent,utility,filelist,title=None,expl=None): + + self.utility = utility + self.filelist = [] + + # Convert to Unicode for display + for file in filelist: + u = bin2unicode(file) + self.filelist.append(u) + + if DEBUG: + print >>sys.stderr,"VideoChooser: filelist",self.filelist + + style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER + if title is None: + title = self.utility.lang.get('selectvideofiletitle') + wx.Dialog.__init__(self,parent,-1,title,style=style) + + sizer = wx.BoxSizer(wx.VERTICAL) + filebox = wx.BoxSizer(wx.VERTICAL) + self.file_chooser=wx.Choice(self, -1, wx.Point(-1, -1), wx.Size(300, -1), self.filelist) + self.file_chooser.SetSelection(0) + + if expl is None: + self.utility.lang.get('selectvideofile') + filebox.Add(wx.StaticText(self, -1, expl), 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) + filebox.Add(self.file_chooser) + sizer.Add(filebox, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) + + buttonbox = wx.BoxSizer(wx.HORIZONTAL) + okbtn = wx.Button(self, wx.ID_OK, label=self.utility.lang.get('ok'), style = wx.BU_EXACTFIT) + buttonbox.Add(okbtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + cancelbtn = wx.Button(self, wx.ID_CANCEL, label=self.utility.lang.get('cancel'), style = wx.BU_EXACTFIT) + buttonbox.Add(cancelbtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + sizer.Add(buttonbox, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) + + self.SetSizerAndFit(sizer) + + def getChosenIndex(self): + return self.file_chooser.GetSelection() + + + +class VODWarningDialog(wx.Dialog): + + def __init__(self, parent, utility, ds, infilename, other_downloads, islive): + self.parent = parent + self.utility = utility + + style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER + if islive: + title = self.utility.lang.get('livewarntitle') + else: + title = self.utility.lang.get('vodwarntitle') + + wx.Dialog.__init__(self,parent,-1,title,style=style) + + if islive: + msg = self.utility.lang.get('livewarngeneral') + else: + msg = self.utility.lang.get('vodwarngeneral') + + """ + if bitrate is None: + msg += self.utility.lang.get('vodwarnbitrateunknown') + msg += self.is_mov_file(videoinfo) + msg += self.utility.lang.get('vodwarnconclusionno') + elif bitrate > maxuploadrate and maxuploadrate != 0: + s = self.utility.lang.get('vodwarnbitrateinsufficient') % (str(bitrate/1024),str(maxuploadrate)+" KB/s") + msg += s + msg += self.is_mov_file(videoinfo) + msg += self.utility.lang.get('vodwarnconclusionno') + elif bitrate > maxmeasureduploadrate and maxuploadrate == 0: + s = self.utility.lang.get('vodwarnbitrateinsufficientmeasured') % (str(bitrate/1024),str(maxuploadrate)+" KB/s") + msg += s + msg += self.is_mov_file(videoinfo) + msg += self.utility.lang.get('vodwarnconclusionno') + + else: + if maxuploadrate == 0: + rate = self.utility.lang.get('unlimited') + else: + rate = str(maxuploadrate)+" KB/s" + s = self.utility.lang.get('vodwarnbitratesufficient') % (str(bitrate/1024),rate) + msg += s + extra = self.is_mov_file(videoinfo) + if extra == '': + msg += self.utility.lang.get('vodwarnconclusionyes') + else: + msg += extra + msg += self.utility.lang.get('vodwarnconclusionno') + + """ + sizer = wx.BoxSizer(wx.VERTICAL) + text = wx.StaticText(self, -1, msg) + text.Wrap(500) + sizer.Add(text, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND, 5) + + # 22/08/08 boudewijn: only show the selectbox when there are + # torrents that are actively downloading + if other_downloads: + otherslist = [self.utility.lang.get('vodrestartothertorrents'), + self.utility.lang.get('vodstopothertorrents'), + self.utility.lang.get('vodleaveothertorrents')] + + othersbox = wx.BoxSizer(wx.VERTICAL) + self.others_chooser=wx.Choice(self, -1, wx.Point(-1, -1), wx.Size(-1, -1), otherslist) + self.others_chooser.SetSelection(OTHERTORRENTS_STOP_RESTART) + + othersbox.Add(wx.StaticText(self, -1, self.utility.lang.get('vodwhataboutothertorrentspolicy')), 1, wx.ALIGN_CENTER_VERTICAL) + othersbox.Add(self.others_chooser) + sizer.Add(othersbox, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) + else: + self.others_chooser = None + + sizer.Add(wx.StaticText(self, -1, self.utility.lang.get('vodwarnprompt')), 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) + + + buttonbox = wx.BoxSizer(wx.HORIZONTAL) + okbtn = wx.Button(self, wx.ID_OK, label=self.utility.lang.get('yes'), style = wx.BU_EXACTFIT) + buttonbox.Add(okbtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + cancelbtn = wx.Button(self, wx.ID_CANCEL, label=self.utility.lang.get('no'), style = wx.BU_EXACTFIT) + buttonbox.Add(cancelbtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 5) + sizer.Add(buttonbox, 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) + + self.SetSizerAndFit(sizer) + + def get_othertorrents_policy(self): + if self.others_chooser: + idx = self.others_chooser.GetSelection() + else: + idx = OTHERTORRENTS_STOP_RESTART + if DEBUG: + print >>sys.stderr,"videoplay: Other-torrents-policy is",idx + return idx + + def is_mov_file(self,videoinfo): + orig = videoinfo['inpath'] + (prefix,ext) = os.path.splitext(orig) + low = ext.lower() + if low == '.mov': + return self.utility.lang.get('vodwarnmov') + else: + return '' + + +def parse_playtime_to_secs(hhmmss): + if DEBUG: + print >>sys.stderr,"videoplay: Playtime is",hhmmss + r = re.compile("([0-9]+):*") + occ = r.findall(hhmmss) + t = None + if len(occ) > 0: + if len(occ) == 3: + # hours as well + t = int(occ[0])*3600 + int(occ[1])*60 + int(occ[2]) + elif len(occ) == 2: + # minutes and seconds + t = int(occ[0])*60 + int(occ[1]) + elif len(occ) == 1: + # seconds + t = int(occ[0]) + return t + +def return_feasible_playback_modes(syspath): + l = [] + try: + import vlc + + if USE_VLC_RAW_INTERFACE: + # check if the special raw interface is available + # pylint: disable-msg=E1101 + if not inspect.ismethoddescriptor(vlc.MediaControl.set_raw_callbacks): + raise Exception("Incorrect vlc plugin. This does not provide the set_raw_callbacks method") + # pylint: enable-msg=E1101 + vlcpath = os.path.join(syspath,"vlc") + if sys.platform == 'win32': + if os.path.isdir(vlcpath): + l.append(PLAYBACKMODE_INTERNAL) + else: + l.append(PLAYBACKMODE_INTERNAL) + except Exception: + print_exc() + + if sys.platform == 'win32': + l.append(PLAYBACKMODE_EXTERNAL_MIME) + l.append(PLAYBACKMODE_EXTERNAL_DEFAULT) + else: + l.append(PLAYBACKMODE_EXTERNAL_DEFAULT) + return l -- 2.20.1