Mercurial > libervia-web
view libervia/pages/_browser/alt_media_player.py @ 1337:f0648005cd11
browser: alternative media player:
the new `alt_media_player` module implements an `ogv.js` based media player which is able
to play WebM (VP8/VP9) or Ogg (Theora, Vorbis) formats (+ other codecs not integrated
yet) on browser that don't do it natively.
The module imports `ogv.js` only when it's necessary (incompatible browser + videos
needing it).
This for now used in blog, and will be used in other places when it's suitable.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 15 Aug 2020 16:45:23 +0200 |
parents | |
children | 472267dcd4d8 |
line wrap: on
line source
#!/usr/bin/env python3 """This module implement an ogv.js based alternative media player This is useful to play libre video/audio formats on browser that don't do it natively. """ from browser import document, timer class VideoPlayer: TIMER_MODES = ("timer", "remaining") imports_done = False def __init__(self, ori_video_elt, sources): self.video_player_elt = video_player_elt = video_player_tpl.get_elt() self.player = player = self.ogv.OGVPlayer.new() # {"debug": True}) ori_video_elt.parentNode.replaceChild(video_player_elt, ori_video_elt) overlay_play_elt = self.video_player_elt.select_one(".video_overlay_play") overlay_play_elt.bind("click", self.on_overlay_play_elt_click) self.progress_elt = video_player_elt.select_one("progress") self.progress_elt.bind("click", self.on_progress_click) self.timer_elt = video_player_elt.select_one(".timer") self.timer_mode = "timer" self.controls_elt = video_player_elt.select_one(".video_controls") player_wrapper_elt = video_player_elt.select_one(".video_elt") player.src = sources[0] player_wrapper_elt <= player self.hide_controls_timer = None # a click on the video itself is like click on play icon player_wrapper_elt.bind("click", self.on_play_click) # buttons for handler in ("play", "change_timer_mode", "change_volume", "fullscreen"): for elt in video_player_elt.select(f".click_to_{handler}"): elt.bind("click", getattr(self, f"on_{handler}_click")) # events # FIXME: progress is not implemented in OGV.js, update when available for event in ("play", "pause", "timeupdate", "ended", "volumechange"): player.bind(event, getattr(self, f"on_{event}")) def on_overlay_play_elt_click(self, evt): evt.stopPropagation() evt.target.remove() self.player.play() def on_play_click(self, evt): if self.player.paused: print("playing") self.player.play() else: self.player.pause() print("paused") def on_change_timer_mode_click(self, evt): self.timer_mode = self.TIMER_MODES[ (self.TIMER_MODES.index(self.timer_mode) + 1) % len(self.TIMER_MODES) ] def on_change_volume_click(self, evt): self.player.muted = not self.player.muted def on_fullscreen_click(self, evt): try: fullscreen_elt = document.fullscreenElement request_fullscreen = self.video_player_elt.requestFullscreen except AttributeError: print("fullscreen is not available on this browser") else: if fullscreen_elt == None: print("requesting fullscreen") request_fullscreen() else: print(f"leaving fullscreen: {fullscreen_elt}") try: document.exitFullscreen() except AttributeError: print("exitFullscreen not available on this browser") def on_progress_click(self, evt): position = evt.offsetX / evt.target.width new_time = self.player.duration * position self.player.currentTime = new_time def on_play(self, evt): self.video_player_elt.classList.add("playing") self.show_controls() self.video_player_elt.bind("mousemove", self.on_mouse_move) def on_pause(self, evt): self.video_player_elt.classList.remove("playing") self.show_controls() self.video_player_elt.unbind("mousemove") def on_timeupdate(self, evt): self.update_progress() def on_ended(self, evt): self.update_progress() def on_volumechange(self, evt): if self.player.muted: self.video_player_elt.classList.add("muted") else: self.video_player_elt.classList.remove("muted") def on_mouse_move(self, evt): self.show_controls() def update_progress(self): duration = self.player.duration current_time = duration if self.player.ended else self.player.currentTime self.progress_elt.max = duration self.progress_elt.value = current_time self.progress_elt.text = f"{current_time/duration*100:.02f}" current_time, duration = int(current_time), int(duration) if self.timer_mode == "timer": cur_min, cur_sec = divmod(current_time, 60) tot_min, tot_sec = divmod(duration, 60) self.timer_elt.text = f"{cur_min}:{cur_sec:02d}/{tot_min}:{tot_sec:02d}" elif self.timer_mode == "remaining": rem_min, rem_sec = divmod(duration - current_time, 60) self.timer_elt.text = f"{rem_min}:{rem_sec:02d}" else: print(f"ERROR: unknown timer mode: {self.timer_mode}") def hide_controls(self): self.controls_elt.classList.add("hidden") self.video_player_elt.style.cursor = "none" if self.hide_controls_timer is not None: timer.clear_timeout(self.hide_controls_timer) self.hide_controls_timer = None def show_controls(self): self.controls_elt.classList.remove("hidden") self.video_player_elt.style.cursor = "" if self.hide_controls_timer is not None: timer.clear_timeout(self.hide_controls_timer) if self.player.paused: self.hide_controls_timer = None else: self.hide_controls_timer = timer.set_timeout(self.hide_controls, 3000) @classmethod def do_imports(cls): # we do imports (notably for ogv.js) if they are necessary if cls.imports_done: return from js_modules import ogv cls.ogv = ogv if not ogv.OGVCompat.supported('OGVPlayer'): print("Can't use OGVPlayer with this browser") raise NotImplementedError import template global video_player_tpl video_player_tpl = template.Template("components/video_player.html") cls.imports_done = True @classmethod def install(cls, cant_play): ext_list = set() for data in cant_play.values(): ext_list.update(data['ext']) for ori_video_elt in document.body.select('video'): sources = [] src = (ori_video_elt.src or '').strip() if src: sources.append(src) for source_elt in ori_video_elt.select('source'): src = (source_elt.src or '').strip() if src: sources.append(src) # FIXME: we only use first found source try: source = sources[0] except IndexError: print(f"Can't find any source for following elt:\n{ori_video_elt.html}") continue try: ext = f".{source.rsplit('.', 1)[1]}" except IndexError: print( f"No extension found for source of following elt:\n{ori_video_elt.html}") continue if ext and ext in ext_list: cls.do_imports() print(f"alternative player will be used for {source!r}") cls(ori_video_elt, sources) def install_if_needed(): CONTENT_TYPES = { "ogg_theora": {"type": 'video/ogg; codecs="theora"', "ext": [".ogg"]}, "webm_vp8": {"type": 'video/webm; codecs="vp8, vorbis"', "ext": [".webm"]}, "webm_vp9": {"type": 'video/webm; codecs="vp9"', "ext": [".webm"]}, # FIXME: handle audio # "ogg_vorbis": {"type": 'audio/ogg; codecs="vorbis"', "ext": ".ogg"}, } test_video_elt = document.createElement("video") cant_play = {k:d for k,d in CONTENT_TYPES.items() if test_video_elt.canPlayType(d['type']) != "probably"} if cant_play: cant_play_list = '\n'.join(f"- {k} ({d['type']})" for k, d in cant_play.items()) print( "This browser is incompatible with following content types, using alternative:\n" f"{cant_play_list}" ) try: VideoPlayer.install(cant_play) except NotImplementedError: pass else: print("This browser can play natively all requested open video/audio formats")