#!/usr/bin/python ''' SVPlayer - simple video and TV player (c) 2011-2015 Jan ONDREJ (SAL) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Usage: svplayer [options] [URL] Options: -h|--help show this help message -f|--fullscreen start fullscreen -p|--profile X start with profile X -c|--channel X switch to channel X after startup -b|--browser start file browser -y|--youtube X start youtube and search for X -0|--movetopleft move/resize player to it's default size after startup ''' import sys, os, re, pidfile, time, getopt, atexit, json from config import * from sgtk import * import players from ui import TopMenu from browser import file_browser, set_menu, iptv_date_menu, settings_menu import mobile.server, select try: import pylirc except ImportError: pylirc = None try: from youtube import youtube_player except ImportError, e: print "WARNING!!! Unable to import youtube player modules:", e youtube_player = None try: import tvguide except ImportError: pass reg_geometry = re.compile('^([0-9]*)x?([0-9]*)([+-][0-9]*)?([+-][0-9]*)?$') class ControlBar(Screen): def __init__(self, parent): Screen.__init__(self) self._parent = parent self.modify_bg(Gtk.STATE_NORMAL, COLOR_BLACK) self.modify_fg(Gtk.STATE_NORMAL, COLOR_GREEN) self.timeout = None self.sy = 0 self.connect("draw", self.redraw) def set_value(self, name, value): self.label = name self.value = value if self.timeout is not None: GObject.source_remove(self.timeout) self.sy = 8 self.show() self.draw( self.get_property('window').cairo_create(), *self._parent.window.get_size() ) def draw(self, cr, width, height): # clear widget Screen.draw(self, cr, width, height) sx = width sy = self.sy self.set_size_request(sx, sy) if sy>4: cr.set_source_rgb(0, 0.9, 0) x0 = (sx - sx/6*6)/2 + 1 w = sx*int(self.value)/100 for x in range(min(w/6+1, sx/6)): xx = x0 + x*6 cr.rectangle(xx, 2, 3, sy-4) cr.fill() cr.set_source_rgb(0, 0, 0) cr.rectangle( x0 + w, 0, sx - x0 - w, sy-1 ) cr.fill() def redraw(self, x, y): self.draw( self.get_property('window').cairo_create(), *self._parent.window.get_size() ) def ahide(self): '''Animated hide.''' # Animated hide blinks screen if player width is smaller than # window width. Disabled until it will be fixed. return self.hide() self.sy -= 2 wx = self._parent.window.get_size()[0] px = self._parent.player.get_size_request()[0] if self.sy>0: self.draw( self.get_property('window').cairo_create(), *self.get_property('window').get_size() ) self.timeout = GObject.timeout_add(40, self.ahide) else: self.timeout = None self.hide() class player_window: changing_channel = 0 channel = 0 last_channel = None last_motion = 0 last_cursor_position = (-10, -10) last_url = None status_timeout = None commit_channel_timeout = None geometries = geometries status_next_update = 0 last_message = "" last_pos = None content_length = None windowed_size = None next_screensaver_poke = 0 file_browser = None youtube = None pressed_keys = [] def __init__(self, urls, default_channel='', default_volume=50, default_size=None, default_position=None, fullscreen=False, profile=0, press_keys=[], web_remote_control=None): self.profile = profile if profile>0: self.urls = PROFILES[profile] else: self.urls = urls self.source = 0 self.autoswitch_source = True self.volume = default_volume self.fullscreen = fullscreen self.window = Gtk.Window(Gtk.WINDOW_TOPLEVEL) self.window.modify_bg(Gtk.STATE_NORMAL, COLOR_BLACK) self.window.connect("destroy", self.destroy) self.window.connect("delete_event", self.destroy) self.window.connect("key_press_event", self.keyboard_press) self.window.connect("key_release_event", self.keyboard) self.window.connect("focus_in_event", self.focus_event) self.window.connect("focus_out_event", self.focus_event) self.window.connect("button_press_event", self.mouse_press) self.window.connect("button_release_event", self.mouse_release) self.window.connect("scroll_event", self.mouse_scroll) self.window.connect("motion_notify_event", self.mouse_motion) self.drag_init(self.window) self.last_motion = time.time() self.window.set_border_width(0) # top line self.vbox = Gtk.VBox(spacing=0) # main menu self.uimanager = TopMenu(self) self.menubar = self.uimanager.widget self.vbox.pack_start(self.menubar, False, True, 0) # info row self.hbox = Gtk.HBox(homogeneous=False, spacing=0) self.label_left = Gtk.Label() self.label_left.modify_font(FONT_SANS24) self.hbox.pack_start(self.label_left, padding=0) self.label_center = Gtk.Label() self.label_center.modify_font(FONT_SANS20) self.hbox.pack_start(self.label_center, padding=0) self.entry = Gtk.Entry() self.entry.set_visible(False) self.hbox.pack_start(self.entry, padding=0) self.label_right = Gtk.Label() self.label_right.modify_font(FONT_SANS24) self.hbox.pack_start(self.label_right, padding=0) self.label_left.modify_fg(Gtk.STATE_NORMAL, COLOR_DARK_YELLOW) self.label_center.modify_fg(Gtk.STATE_NORMAL, COLOR_DARK_YELLOW) self.label_right.modify_fg(Gtk.STATE_NORMAL, COLOR_DARK_YELLOW) self.vbox.pack_start(self.hbox, expand=False, padding=0) self.control_bar = ControlBar(self) self.vbox.pack_start(self.control_bar, expand=False, fill=True) # Middle box if youtube_player: self.youtube = youtube_player(self.play_files) self.vbox.pack_start(self.youtube.mainbox, expand=False) else: self.youtube = None # Video player widget self.player = players.BasePlayerWidget() self.audio_player = players.BasePlayerWidget() # realize window self.window.add(self.vbox) self.button_press = None self.vbox.show() self.window.show() # screensaver if screensaver: print "Screensaver configuration:", screensaver screensaver.startup(self.window.get_property('window')) # web remote control if web_remote_control: #mobile.server.handler_class.CHANNELS = dict(enumerate([ # x.name # for x in urls #])) mobile.server.handler_class.player = self try: self.web_remote = mobile.server.server_class( web_remote_control, mobile.server.handler_class ) except socket.error, error: print "Unable to start web remote server", str(error) self.web_remote = None else: self.web_remote = None # change fullscreen after screensaver was started self.change_fullscreen(0) # resize if default_size: self.window.resize(*default_size) self.windowed_size = default_size else: self.windowed_size = self.window.get_size() if default_position: print "Default position:", default_position self.window.move(*default_position) # set mute if requested in OS environment self.mute = bool(os.environ.get("MUTE", 0)) self.change_channel( channel=int(default_channel), player=players.default ) self.refresh_status() # start status refreshing self.check_lirc() # change mode for key in press_keys: if key=="\n": self.keyboard_press(key=key) elif len(key)==1: self.keyboard(key=key) else: self.entry.set_text(key) def destroy(self, widget=None, data=None, save_size=False): if config_file: # save config cdir = os.path.dirname(config_file) if not os.path.isdir(cdir): os.makedirs(cdir) if not self.fullscreen and save_size: self.windowed_size = self.window.get_size() f = open(config_file, "wt") save_parameters = dict([ (x, getattr(self, x)) for x in [ 'channel', 'volume', 'windowed_size' ] ]) f.write(json.dumps(save_parameters)) f.close() self.player.stop() self.player.destroy() if self.last_url: self.last_url.stop() Gtk.main_quit() def change_profile(self, profile): if profile<=len(PROFILES): if PROFILES[profile]: self.profile = profile self.urls = PROFILES[profile] self.profile_name = PROFILES.name(profile) before = self.channel self.channel = sys.maxint if self.urls[0](0).url.lower().endswith('.jpg'): player = players.ImagePlayerWidget else: player = players.default self.change_channel(channel=before, player=player) self.set_status("Profile: %d. %s" % (profile, self.profile_name)) self.uimanager.update() else: print "Empty profile:", PROFILES.name(profile) def change_channel(self, direction=0, channel=None, player=None): before = self.channel if channel is not None: self.channel = channel self.channel += direction if self.channel>len(self.urls): self.channel = 1 if self.channel<1: self.channel = len(self.urls) url = self.urls[self.channel-1] print "Channel:", self.channel, url.name # reset source if channel changed if before!=self.channel: self.source = 0 while True: if self.source<0: self.source = url.source_count()-1 if self.source>=url.source_count(): self.source = 0 break # skip all archive and guide sources if url(self.source).archive_only or url(self.source).guide_only: self.source += 1 else: break if player: surl = url(self.source, player().name) else: surl = url(self.source, self.player.name) print "URL:", surl.url, surl.options while self.changing_channel>0: time.sleep(0.1) # do nothing if it's same channel as before if before!=self.channel: self.last_channel = before if self.last_url==surl: print "Same channel:", self.channel, self.source, surl else: self.last_url = surl self.last_pos = None self.content_length = None self.last_message = "" # stop player, change media and start again self.changing_channel += 1 self.player.stop() self.audio_player.stop() if self.last_url: self.last_url.stop() if player is not None: print "Player change: %s -> %s" % (self.player, player) try: self.vbox.remove(self.player) self.player.destroy() except TypeError: pass self.player = player( surl.url, subtitles=surl.subtitles, options=surl.options ) self.vbox.pack_start(self.player, expand=True) else: self.player.set_media(surl.url, surl.options) self.player.show_all() self.player.play() self.changing_channel -= 1 # audio player if url.background_audio: self.audio_player = players.GSTPlayer( url.background_audio, options={'audio_only': True} ) self.audio_player.play() # set parameters self.change_volume() if surl.deinterlace=="DEFAULT": # DEFAULT self.player.set_deinterlace("yadif2x") # replaced bob else: print "Deinterlace:", surl.deinterlace self.player.set_deinterlace(surl.deinterlace) self.error_counter = error_counter() # new channel set self.channel_req = "" self.onscreen = self.player self.refresh_status(all=True, timeout=0) self.mouse_motion() # set language self.player.language_priority(surl.lang_priority) self.audio_track = 1 # track 1, will be changed automatically later self.mute_volume = 0 # hide ui menu self.uimanager.hide() def refresh_status(self, all=False, timeout=1000): url = self.urls[self.channel-1] surl = url(self.source) if all or time.time()>self.status_next_update: self.status_next_update = sys.maxint # disable self.label_left.set_text(str(self.channel)) if url==surl: xname = url.name else: xname = (url.name + ' ' + surl.name).strip() self.label_center.modify_font(FONT_SANS20) self.label_center.set_text( "%s [%s]" % (xname, surl.source) ) self.window.set_title( "%d. %s [%s]" % (self.channel, xname, surl.source) ) self.control_bar.ahide() self.label_right.set_text(time.strftime("%H:%M:%S")) pos = self.player.get_position() if pos[0]>=1 and pos[0]<=99: self.last_pos = pos[0] self.content_length = pos[2] self.label_left.set_text("%4.2f" % (self.channel+pos[0]/100.0)) elif url.source=="multi": source = url.find_guide() if source: prog = tvguide.profi_program.current(source.name) if prog and 0.0<=prog.progress()<1.0: self.last_pos = prog.progress()*100 self.content_length = prog.seconds self.label_left.set_text("%4.2f" % (self.channel+prog.progress())) # poke (disable) screensaver activation if self.fullscreen and screensaver: if self.next_screensaver_poke5 if self.fullscreen and self.player.handle_events and \ self.last_motion>0 and time.time()-self.last_motion>5: self.last_cursor_position = self.window.get_pointer() self.window.get_property('window').set_cursor( Gdk.Cursor(Gdk.CursorType.BLANK_CURSOR) ) # is video still playing? if self.player.was_ended(): if not self.onscreen==self.youtube \ and self.urls[self.channel-1].source=="YouTube": self.youtube.search_related() self.onscreen = self.youtube elif self.urls[self.channel-1].restart: print "Stream ended, restarting ..." self.player.stop() url = self.urls[self.channel-1] surl = url(self.source, self.player.name) self.player.set_media(surl.url, surl.options) self.player.play() elif self.channel0: self.status_timeout = GObject.timeout_add(timeout, self.refresh_status) def set_status(self, text, delay=1.0, large=False): print text self.window.set_title(text) if large: self.label_center.modify_font(FONT_SANS28) self.label_center.set_text(text) self.status_next_update = time.time()+delay def change_volume(self, direction=0, volume=None, mute=None): if volume is not None: self.volume = volume if mute is not None: self.mute = mute self.volume += direction if self.volume>self.player.max_volume: self.volume = self.player.max_volume if self.volume<0: self.volume = 0 surl = self.urls[self.channel-1](self.source) self.player.set_volume(int(self.volume*surl.volume)) self.player.set_mute(self.mute) self.audio_player.set_volume(int(self.volume*surl.volume)) self.audio_player.set_mute(self.mute) if self.mute: self.set_status("Mute: on") else: self.set_status("Volume: %d%%" % self.player.get_volume()) if direction!=0 or volume is not None: self.control_bar.set_value( 'Volume', self.player.get_volume()*100/self.player.max_volume ) def change_track(self, increment=0): # reload audio tracks if not present if not self.player.audio_tracks: surl = self.urls[self.channel-1](self.source) self.player.get_tracks(surl.lang_priority) self.audio_track += increment if self.audio_track>=len(self.player.audio_tracks): self.audio_track = 0 self.player.set_track(self.player.audio_tracks[self.audio_track][0]) self.set_status( "Language: %s" % self.player.audio_tracks[self.audio_track][1] ) def change_subtitle(self): self.player.next_sub() if self.player.get_sub() in self.player.sub_list(): self.set_status( "Sub: %s" % self.player.sub_list()[self.player.get_sub()][1] ) def change_position(self, forward=0, seek=None): if seek is None: #print "Forward:", forward if forward!=0: self.player.forward(forward) else: self.player.seek(seek) # unpause self.player.pause(False) self.audio_player.pause(False) pos = self.player.get_position() # display position only if this stream has non zero length if pos[2]: self.set_status( "Position: %s/%s, %3.1f%%" % (totime(pos[1]), totime(pos[2]), pos[0]) ) self.control_bar.set_value('Position', int(pos[0])) def change_fullscreen(self, value=None): if value==1: self.fullscreen = not self.fullscreen elif value==None or value==0: pass # no change else: self.fullscreen = value if self.fullscreen: self.control_bar.hide() self.windowed_size = self.window.get_size() self.window.fullscreen() self.hbox.show() self.label_left.show() self.label_center.show() self.label_right.show() self.entry.hide() self.uimanager.hide() if screensaver: screensaver.inhibit() else: self.control_bar.hide() self.hbox.hide() self.uimanager.hide() self.window.unfullscreen() if self.windowed_size is not None: self.window.resize(*self.windowed_size) if screensaver: screensaver.uninhibit() def change_aspect(self): try: next_aspect = self.geometries.index( (self.player.get_aspect(), self.player.get_crop()))+1 except ValueError, e: print "ValueError:", e next_aspect = 0 if next_aspect>=len(self.geometries): next_aspect = 0 self.player.set_aspect(self.geometries[next_aspect][0]) self.player.set_crop(self.geometries[next_aspect][1]) self.set_status( "Aspect ratio: %s, Crop: %s" % self.geometries[next_aspect] ) def change_engine(self): if players.default==players.GSTWidget: players.default = players.VLCWidget self.set_status("Engine: VLC") elif players.default==players.VLCWidget: players.default = players.GSTWidget self.set_status("Engine: GStreamer") print "Current engine:", players.default self.last_url = None self.change_channel(0, player=players.default) def play_files(self, names, filenames, subtitles=[], media=media, player=None, options={}): for name, filename in zip(names, filenames): self.urls.append(media(name, filename)) self.player.stop() if player: try: self.vbox.remove(self.player) except TypeError: pass self.player.destroy() if type(filenames[0])==int: self.player = player( options=options or self.urls[-1](0, player().name).options ) else: self.player = player( subtitles=subtitles, options=options or self.urls[-1](0, player().name).options, events=not filenames[0].startswith("dvd://") ) self.vbox.pack_start(self.player, expand=True) self.change_channel(channel=len(self.urls)-len(filenames)+1) # add subtitles for subtitle in subtitles: self.player.add_subtitle(subtitle) if self.file_browser: self.vbox.remove(self.file_browser.mainbox) self.onscreen = self.player self.player.show_all() # hide hbox if not fullscreen if not self.fullscreen: self.hbox.hide() def check_lirc(self): if pylirc is not None: events = pylirc.nextcode() else: events = [] if events: for event in events: print "LIRC:", event if self.onscreen==self.youtube and event!="Escape": self.youtube.pushkey(event) elif self.keyboard_press(key=event)==False: self.keyboard(key=event) # remove all pressed keys after window lost focus if not self.window.has_focus: self.pressed_keys = [] # hide menu after timeout if there is no mouse motion if self.uimanager.visible and self.last_motion+301: if self.error_counter.sum()>switch_source_errors: print "TOO MANY ERRORS [%d], switching source..." \ % self.error_counter.sum() self.error_counter.reset() self.source += 1 self.change_channel() elif self.player.read_bytes()==0 and self.player.run_time()>switch_source_no_signal: print "ERROR: NO INPUT, switching source..." print self.urls[self.channel-1].source_count() self.source += 1 self.change_channel() # display messages from player or guide msg = self.player.get_message() if msg is None: guide = self.urls[self.channel-1].find_guide() if guide: msg = tvguide.profi_program.current_name(guide.name) if msg and self.last_message!=msg: self.set_status(msg, 2.0, large=True) self.last_message = msg if self.web_remote: try: self.web_remote.handle_request() except select.error: pass GObject.timeout_add(100, self.check_lirc) def commit_channel(self): if not self.channel_req.isdigit(): self.channel_req = "" self.last_message = "" self.change_position() return self.change_channel( channel=int(self.channel_req), player=players.default ) if self.commit_channel_timeout is not None: GObject.source_remove(self.commit_channel_timeout) self.commit_channel_timeout = None def hide_browser(self): self.vbox.remove(self.file_browser.mainbox) self.player.show() self.onscreen = self.player def focus_event(self, widget, event): self.pressed_keys = [] return True def keyboard_press(self, widget=None, event=None, key=None): if key is None: key = Gtk.gdk.keyval_name(event.keyval) if not key in self.pressed_keys: self.pressed_keys.append(key) if self.onscreen==self.player: if key in ["+", "KP_Add", "plus", "equal"]: if "Control_L" in self.pressed_keys or "Control_R" in self.pressed_keys: self.change_volume(1, mute=False) else: self.change_volume(2, mute=False) return True elif key in ["-", "KP_Subtract", "minus"]: if "Control_L" in self.pressed_keys or "Control_R" in self.pressed_keys: self.change_volume(-1, mute=False) else: self.change_volume(-2, mute=False) return True elif key=='bracketleft' or key=="Left": self.change_position(-10) return True elif key=='bracketright' or key=="Right": self.change_position(10) return True elif key=='apostrophe' or key=="Down": self.change_position(-60) return True elif key=='backslash' or key=="Up": self.change_position(60) return True elif key=='End': self.change_position(seek=99.9) return True elif key=='Home': self.change_position(seek=0) return True elif self.onscreen==self.youtube: if key in ["Enter", "Return", "\n"] and not self.youtube.vbox: print "YOUTUBE request:", self.entry.get_text() found = self.youtube.search(self.entry.get_text()) if len(found)==0: self.set_status("NOT FOUND!") self.youtube.mainbox.hide() self.onscreen = self.player return True if key in ["Escape", "Enter", "Return", "\n"]: return False if event: return self.youtube.keyboard(widget, event) return False elif self.onscreen==self.file_browser: if key=="Escape": return False if key=='b' or key=='n': self.hide_browser() return True # send all keys to browser for GTK3 return self.file_browser.pushkey(key) return False def keyboard(self, widget=None, event=None, key=None): if key is None: key = Gtk.gdk.keyval_name(event.keyval) print "KEY:", key, event.keyval, self.onscreen if key in self.pressed_keys: self.pressed_keys.remove(key) # ignore some special keys pressed mostly as mistakes if "Alt_L" in self.pressed_keys: return False # remap keys if key in key_remap: key = key_remap[key] if key.startswith("KP_"): key = key[3:] if self.entry.get_visible() and len(key)==1 and key!="\n": # ignore characters from text input box return False if self.onscreen==self.youtube: if key in ["Escape", "Enter", "Return", "\n"]: self.entry.hide() self.label_center.show() if key=="Escape": self.youtube.remove_vbox() self.onscreen = self.player #self.change_channel(1) return True return False if self.onscreen==self.file_browser: if key=="Escape": self.hide_browser() return True return False # Profiles if key.startswith('F') and key[1:].isdigit(): self.change_profile(int(key[1:])) return True # Channels elif key=="Page_Down": self.change_channel(-1, player=players.default) return True elif key=="Page_Up": self.change_channel(1, player=players.default) return True elif key in '1234567890': self.channel_req += key print "CHANNEL REQ:", self.channel_req self.label_left.set_text(self.channel_req+"_") self.commit_channel_timeout = \ GObject.timeout_add(2000, self.commit_channel) return True elif key in ["Return", "Enter", "\n"]: self.commit_channel() return True elif key=='BackSpace' and (self.last_channel is not None): self.change_channel(channel=self.last_channel, player=players.default) return True # Source (DVB-T, Multicast, ...) elif key=='s': self.source += 1 self.change_channel() return True # lock on this source, disable autoswitch elif key=='L': self.autoswitch_source = not self.autoswitch_source print "Auto source switch:", self.autoswitch_source return True # Subtitle elif key=='t': self.change_subtitle() return True # Engine elif key=='E': self.change_engine() return True # Pause, FF, REW elif key=='p': print "Player state:", self.player.get_state() self.player.pause() self.audio_player.pause() pos = self.player.get_position() self.set_status( "%s: %s/%s, %3.1f%%" % (self.player.get_state(), totime(pos[1]), totime(pos[2]), pos[0]) ) if pos[0]: self.control_bar.set_value('Position', int(pos[0])) time.sleep(0.1) return True elif key=="REW_s": self.change_position(-10) return True elif key=="FF_s": self.change_position(10) return True elif key=="REW_m": self.change_position(-60) return True elif key=="FF_m": self.change_position(60) return True # Volume return True elif key=="VOL_UP": self.change_volume(2, mute=False) return True elif key=="VOL_DOWN": self.change_volume(-2, mute=False) return True elif key=='m': # switch volumes self.volume, self.mute_volume = self.mute_volume, self.volume if self.volume==0: self.player.toggle_mute() self.mute = self.player.get_mute() self.set_status("Mute: %s" % ["off", "on"][self.mute]) else: self.change_volume(volume=self.volume, mute=False) return True elif key=='a': self.change_track(1) # +1 return True # Fullscreen elif key=='f': self.change_fullscreen(1) return True elif key=='h': self.hbox.hide() elif key=='W' and self.onscreen==self.player and not self.fullscreen: # resize x, y = self.player.get_size() if x>0 and y>0: self.window.resize(x, y) self.set_status("Window size: %d x %d" % (x, y)) return True elif key=='w' and self.onscreen==self.player and not self.fullscreen: vx, vy = self.player.get_size() wx, wy = self.window.get_size() if vy==0 or wy==0: return True # do not divide by zero vaspect = float(vx)/float(vy) waspect = float(wx)/float(wy) if vaspect' or key=='greater': self.player.set_rate(inc=2.0) # snapshot elif key=='S': self.player.snapshot(snapshot_path) # debug elif key=="D": import IPython IPython.embed() return True return False def mouse_press(self, widget, event): #print event.button, event.type if event.button==3: if event.type==Gtk.gdk._2BUTTON_PRESS: self.keyboard(key='f') return True elif self.uimanager.visible: self.uimanager.hide() else: self.uimanager.show() return False elif event.button==1: if event.type==Gtk.gdk._2BUTTON_PRESS: self.keyboard(key='f') return True self.button_press = event.button #print event, "FALSE" #self.player.emit("button_press_event", event) return False def mouse_release(self, widget, event): self.button_press = None #print event, "FALSE" #self.player.emit("button_release_event", event) return False def mouse_scroll(self, widget, event): if event.direction==Gtk.gdk.SCROLL_UP: if self.button_press==2: self.change_channel(1, player=players.default) elif "Shift_L" in self.pressed_keys or "Shift_R" in self.pressed_keys: self.change_position(10) elif "Control_L" in self.pressed_keys or "Control_R" in self.pressed_keys: self.change_volume(2, mute=False) else: self.change_volume(1, mute=False) return True elif event.direction==Gtk.gdk.SCROLL_DOWN: if self.button_press==2: self.change_channel(-1, player=players.default) elif "Shift_L" in self.pressed_keys or "Shift_R" in self.pressed_keys: self.change_position(-10) elif "Control_L" in self.pressed_keys or "Control_R" in self.pressed_keys: self.change_volume(-1, mute=False) else: self.change_volume(-2, mute=False) return True return False def mouse_motion(self, widget=None, event=None): def delta2(a, b): return (a[0]-b[0])**2 + (a[1]-b[1])**2 if delta2(self.window.get_pointer(), self.last_cursor_position)>=50: #print delta2(self.window.get_pointer(), self.last_cursor_position) # display mouse curses (reset to default shape) self.window.get_property('window').set_cursor(None) self.last_motion = time.time() self.last_cursor_position = self.window.get_pointer() return False def drag_init(self, widget): drags = [ ('text/plain', 0, 0), ('TEXT', 0, 1), ('STRING', 0, 2), ('text/uri-list', 0, 3) ] widget.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY) widget.drag_dest_add_text_targets() widget.connect("drag_data_received", self.drag_data) def drag_data(self, widget, context, x, y, data, info, time): url = data.get_text() if url.startswith("http://") or url.startswith("ftp://"): self.play_files([url], [url]) context.finish(True, False, time) if __name__ == "__main__": # load config try: f = open(config_file, "rt") defaults = json.loads(f.read()) f.close() except (IOError, ValueError), e: print "Config error:", e defaults = dict( volume = 50, channel = 0 ) # parse options try: opts, files = getopt.gnu_getopt(sys.argv[1:], 'hfby:p:c:g:m0', [ 'help', 'fullscreen', 'browser', 'youtube=', 'profile=', 'channel=', 'geometry=', 'multi', 'movetopleft' ]) except getopt.GetoptError, (msg, opt): print "Error:",msg sys.exit(1) # start player press_keys = [] profile = 1 for key, value in opts: if key in ['-h', '--help']: print __doc__.strip() sys.exit() elif key in ['-f', '--fullscreen']: defaults["fullscreen"] = True elif key in ['-p', '--profile']: if value.isdigit(): profile = int(value) elif key in ['-c', '--channel']: defaults['channel'] = int(value) elif key in ['-g', '--geometry']: value = reg_geometry.search(value).groups() print "Geometry:", value if value[0] and value[1]: defaults["windowed_size"] = (int(value[0]), int(value[1])) if value[2] and value[3]: defaults["window_position"] = (int(value[2]), int(value[3])) elif key in ['-b', '--browser']: press_keys = ['b'] elif key in ['-y', '--youtube']: press_keys.extend(['y', value, "\n"]) elif key in ['-0', '--movetopleft']: press_keys.append("~") if files: defaults['channel'] = len(CHANNELS)+1 for fn in files: subtitles = [ os.path.abspath(fn.rsplit(".", 1)[0] + ".srt") ] CHANNELS.append(media(fn, fn, subtitles=subtitles)) # initialize lirc if pylirc is not None: try: pylirc.init('svplayer') atexit.register(pylirc.exit) except RuntimeError, e: print "PyLIRC init error:", e pylirc = None player = player_window( CHANNELS, defaults.get('channel', 0), defaults.get('volume', 50), defaults.get('windowed_size', (640, 480)), defaults.get('window_position', None), defaults.get('fullscreen', False), press_keys=press_keys, profile=profile, web_remote_control=web_remote_control ) # run remote thread #mobile_thread = mobile.server.thread( # ('', 9008), # dict(enumerate([ # x.name # for x in CHANNELS # ])), # player #) # run main program Gtk.main()