Add non-interactive player to next-share.
authorRazvan Deaconescu <razvan.deaconescu@cs.pub.ro>
Sat, 6 Aug 2011 12:49:12 +0000 (15:49 +0300)
committerRazvan Deaconescu <razvan.deaconescu@cs.pub.ro>
Sat, 6 Aug 2011 12:49:48 +0000 (15:49 +0300)
instrumentation/next-share/BaseLib/Video/SimPlayer.py [new file with mode: 0644]

diff --git a/instrumentation/next-share/BaseLib/Video/SimPlayer.py b/instrumentation/next-share/BaseLib/Video/SimPlayer.py
new file mode 100644 (file)
index 0000000..fb0a75e
--- /dev/null
@@ -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