Source code for player_content

# -*- coding: utf-8 -*-
"""
.. module:: player_content
   :platform: Unix, Windows
   :synopsis: Behavior of the player related to the content

.. moduleauthor:: Anton Konyshev <anton.konyshev@gmail.com>

"""

import sys

import wx

import pysrt
from pysrt.srtitem import SubRipItem

import events as ev
import ids
from defaults import Defaults


[docs]class PlayerContentMixin(object): """Interaction with video and subtitles. """
[docs] def show_subtitle(self, subtitle=None): """Shows subtitle text in "preview" mode. :param subtitle: Subtitle to showing :type subtitle: :class:`pysrt.srtitem.SubRipItem` """ if ( isinstance(subtitle, SubRipItem) and subtitle.text != self.answer_edit.GetValue() ): self.answer_edit.ChangeValue(subtitle.text) if self.need_translation(): wx.PostEvent(self, ev.TranslateRequest(src=subtitle.text)) elif subtitle is None: self.answer_edit.ChangeValue(u'') self.subtitle_translate.Clear()
[docs] def edit_answer(self, output): """Prints content of an answer after verification and ephasizes its words with appropriate colors. :param output: Result of verification. List of pairs where first value is a symbol and second is an estimation of its correctness :type output: list of tuples """ answer = u''.join([symbol for symbol, _ in output]) self.answer_edit.ChangeValue(answer) start = end = value = None for index in xrange(len(output) + 1): try: charvalue = output[index][1] except IndexError: charvalue = None if start is None: start = index value = charvalue if value == charvalue: end = index + 1 else: self.answer_edit.SetStyle(start, end, wx.TextAttr( self.correct_highlighting() if value else self.incorrect_highlighting())) start = index value = charvalue end = index + 1 self.answer_edit.SetInsertionPointEnd() self.progress_gauge.SetValue(len([sym for sym, correctness in output if correctness])) if self.need_translation(): wx.PostEvent(self, ev.TranslateAnswer(answer=answer))
[docs] def load_video(self, event): """Loads video file. :param event: Event that contains path to video file :type event: events.LoadVideo """ wx.PostEvent(self, wx.PyCommandEvent(wx.EVT_BUTTON.typeId, ids.PAUSE)) self._video_loading_handled = False if not self.video.Load(event.filepath): wx.MessageBox( Defaults.VIDEO_LOADING_ERROR_MESSAGE.format(event.filepath), Defaults.VIDEO_LOADING_ERROR_TITLE, style=wx.ICON_ERROR | wx.OK, ) else: # Different platforms and versions of wx framework show different # behavior here, because the framework uses platform dependent # backends in order to play media. For example, under Windows 7 # with DirectShow backend the EVT_MEDIA_LOADED event isn't # generated, but under XP, where WMP10 backend is used, or Linux, # where GStreamer backend is used, EVT_MEDIA_LOADED # generates well. Handling of the media loading by catching the # EVT_MEDIA_LOADED event is the preferred way, but we should handle # it in any case, even if the event will never be generated. # Therefore we use timeout, if EVT_MEDIA_LOADED was generated # the timeout callback will do nothing, else it will try to run # the handling without the backend response receiving. wx.CallLater(Defaults.VIDEO_BACKEND_LOADING_TIMEOUT, self.on_video_backend_timeout)
[docs] def on_video_backend_timeout(self): """Runs the video handling without backend's response. """ if not getattr(self, u'_video_loading_handled', False): self.on_video_backend_response(None)
[docs] def on_video_backend_response(self, event): """Notifies the player of the successful loading of a video. """ self._video_loading_handled = True wx.PostEvent(self, ev.ContentLoadingState(video=True))
[docs] def load_subtitles(self, event): """Loads subtitles file. :param event: Event that contains path to subtitles file :type event: events.LoadSubtitles """ try: try: self.subtitles = pysrt.open(event.filepath) except UnicodeDecodeError: default_encoding = self.app.get_setting( u'encoding', Defaults.DEFAULT_ENCODING) try: self.subtitles = pysrt.open( event.filepath, encoding=default_encoding) except UnicodeDecodeError: msg = Defaults.SUBTITLES_DECODE_ERROR_MESSAGE wx.MessageBox(msg.format(event.filepath, default_encoding), Defaults.SUBTITLES_DECODE_ERROR_TITLE, style=wx.ICON_ERROR | wx.OK) return if isinstance(getattr(self, u'subtitles', None), pysrt.srtfile.SubRipFile): wx.PostEvent(self, ev.SubtitlesShift( shift=self.delay_spin.GetValue())) wx.PostEvent(self, ev.ContentLoadingState(subtitles=True)) except IOError as err: wx.MessageBox( Defaults.SUBTITLES_LOADING_ERROR_MESSAGE.format(event.filepath, err.message), Defaults.SUBTITLES_LOADING_ERROR_TITLE, style=wx.ICON_ERROR | wx.OK, )
[docs] def find_subtitle(self, first=False): """Find an actual subtitle. Method searches for a subtitle which is appropriate to a current position in the video stream. If there isn't such subtitle, None will be returned. :param bool first: If True first subtitle will be returned :returns: An actual subtitle or None if there isn't subtitles :rtype: :class:`pysrt.srtitem.SubRipItem` """ if isinstance(getattr(self, 'subtitles', None), pysrt.srtfile.SubRipFile): position = int(self.video.Tell() / 1000) try: return self.subtitles.slice( starts_before={'seconds': position}, ends_after={'seconds': position}, )[0] except IndexError: pass if first: try: return self.subtitles.slice( starts_after={'seconds': position}, )[0] except IndexError: pass return None
[docs] def _total_subtitle_fragments(self): """Calculates the total number of subtitle. :returns: Number of subtitles :rtype: int """ if isinstance(getattr(self, 'subtitles', None), pysrt.srtfile.SubRipFile): return len(self.subtitles) return 0
[docs] def get_statistics(self): """Calculates a part of generic statistical values. This values are used with values received from :meth:`verificator.Verificator.get_statistics` to show lesson statistics for user at the end of a lesson. :returns: Several statistical values :rtype: dict """ return { u'total_fragments': self._total_subtitle_fragments(), }
[docs] def _seconds_to_readable(self, src): """Converts an interval in seconds into a human readable interval value. :param src: Value of time interval in seconds :type src: int or float :returns: Human readable interval value :rtype: unicode """ if isinstance(src, (int, float)): mins, secs = divmod(src, 60) hour, mins = divmod(mins, 60) result = u':'.join([unicode(int(val)).zfill(2) for val in [mins, secs]]) if hour: result = u':'.join([unicode(int(hour)).zfill(2), result]) return result else: return u''
[docs] def show_statistics_dialog(self): """Shows dialog with statistics of the lesson. """ if getattr(self, 'verificator', None): stats = self.get_statistics() stats.update(self.verificator.get_statistics()) for value in (u'learning_time', u'seek_position'): stats[value] = self._seconds_to_readable(stats.get(value, None)) msg = Defaults.LESSON_STATISTICS_REPORT wx.MessageBox(msg.format(**stats), Defaults.LESSON_STATISTICS_TITLE, wx.OK | wx.ICON_INFORMATION)