Commit 4415fb4c by Peter Fogg

Started removing XML from video editor.

TODO: This breaks the 1.5x and .75x speeds. I'm still looking into
why.

TODO: VideoDescriptor inherits from RawDescriptor in order to
use the from_xml and export_to_xml methods. This seems really ugly,
though; I'd rather find a better way to do this.
parent 468dfe34
......@@ -4,7 +4,7 @@ Feature: Video Component Editor
Scenario: User can view metadata
Given I have created a Video component
And I edit and select Settings
Then I see only the Video display name setting
Then I see the correct settings and default values
Scenario: User can modify display name
Given I have created a Video component
......
......@@ -4,6 +4,14 @@
from lettuce import world, step
@step('I see only the video display name setting$')
def i_see_only_the_video_display_name(step):
world.verify_all_setting_entries([['Display Name', "default", True]])
@step('I see the correct settings and default values$')
def i_see_the_correct_settings_and_values(step):
world.verify_all_setting_entries([['.75x', 'JMD_ifUUfsU', False],
['1.25x', 'AKqURZnYqpk', False],
['1.5x', 'DYpADpL7jAY', False],
['Display Name', "default", True],
['External Source', '', False],
['External Track', '', False],
['Normal Speed', 'OEoXaMPEzfM', False],
['Show Captions', 'True', False],
])
......@@ -8,7 +8,7 @@ class @Video
@show_captions = @el.data('show-captions') == "true"
window.player = null
@el = $("#video_#{@id}")
@parseVideos @el.data('streams')
@parseVideos()
@fetchMetadata()
@parseSpeed()
$("#video_#{@id}").data('video', this).addClass('video-load-complete')
......@@ -27,10 +27,11 @@ class @Video
parseVideos: (videos) ->
@videos = {}
$.each videos.split(/,/), (index, video) =>
video = video.split(/:/)
speed = parseFloat(video[0]).toFixed(2).replace /\.00$/, '.0'
@videos[speed] = video[1]
@videos['.75'] = @el.data('youtube-id-0-75')
@videos['1.0'] = @el.data('normal-speed-video-id')
@videos['1.25'] = @el.data('youtube-id-1-25')
@videos['1.5'] = @el.data('youtube-id-1-5')
alert @videos['1.5']
parseSpeed: ->
@setSpeed($.cookie('video_speed'))
......
......@@ -8,18 +8,25 @@ from django.http import Http404
from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor
from xmodule.contentstore.content import StaticContent
from xblock.core import Integer, Scope, String
import datetime
import time
from xmodule.editing_module import MetadataOnlyEditingDescriptor
from xblock.core import Integer, Scope, String, Boolean, Float
log = logging.getLogger(__name__)
YOUTUBE_SPEEDS = ['.75', '1.0', '1.25', '1.5']
class VideoFields(object):
data = String(help="XML data for the problem", scope=Scope.content)
position = Integer(help="Current position in the video", scope=Scope.user_state, default=0)
show_captions = Boolean(help="Whether or not captions are shown", display_name="Show Captions", scope=Scope.settings, default=True)
youtube_id_1_0 = String(help="Youtube ID for normal speed video", display_name="Normal Speed", scope=Scope.settings, default="OEoXaMPEzfM")
youtube_id_0_75 = String(help="Youtube ID for .75x speed video", display_name=".75x", scope=Scope.settings, default="JMD_ifUUfsU")
youtube_id_1_25 = String(help="Youtube ID for 1.25x speed video", display_name="1.25x", scope=Scope.settings, default="AKqURZnYqpk")
youtube_id_1_5 = String(help="Youtube ID for 1.5x speed video", display_name="1.5x", scope=Scope.settings, default="DYpADpL7jAY")
start_time = Float(help="Time the video starts", display_name="Start Time", scope=Scope.settings, default=0.0)
end_time = Float(help="Time the video ends", display_name="End Time", scope=Scope.settings, default=0.0)
source = String(help="External source to download video", display_name="External Source", scope=Scope.settings, default="")
track = String(help="External source to download subtitle strack", display_name="External Track", scope=Scope.settings, default="")
class VideoModule(VideoFields, XModule):
......@@ -39,52 +46,6 @@ class VideoModule(VideoFields, XModule):
def __init__(self, *args, **kwargs):
XModule.__init__(self, *args, **kwargs)
xmltree = etree.fromstring(self.data)
self.youtube = xmltree.get('youtube')
self.show_captions = xmltree.get('show_captions', 'true')
self.source = self._get_source(xmltree)
self.track = self._get_track(xmltree)
self.start_time, self.end_time = self._get_timeframe(xmltree)
def _get_source(self, xmltree):
# find the first valid source
return self._get_first_external(xmltree, 'source')
def _get_track(self, xmltree):
# find the first valid track
return self._get_first_external(xmltree, 'track')
def _get_first_external(self, xmltree, tag):
"""
Will return the first valid element
of the given tag.
'valid' means has a non-empty 'src' attribute
"""
result = None
for element in xmltree.findall(tag):
src = element.get('src')
if src:
result = src
break
return result
def _get_timeframe(self, xmltree):
""" Converts 'from' and 'to' parameters in video tag to seconds.
If there are no parameters, returns empty string. """
def parse_time(s):
"""Converts s in '12:34:45' format to seconds. If s is
None, returns empty string"""
if s is None:
return ''
else:
x = time.strptime(s, '%H:%M:%S')
return datetime.timedelta(hours=x.tm_hour,
minutes=x.tm_min,
seconds=x.tm_sec).total_seconds()
return parse_time(xmltree.get('from')), parse_time(xmltree.get('to'))
def handle_ajax(self, dispatch, get):
'''
Handle ajax calls to this video.
......@@ -113,37 +74,92 @@ class VideoModule(VideoFields, XModule):
#log.debug(u"STATE POSITION {0}".format(self.position))
return json.dumps({'position': self.position})
def video_list(self):
return self.youtube
def get_html(self):
# We normally let JS parse this, but in the case that we need a hacked
# out <object> player because YouTube has broken their <iframe> API for
# the third time in a year, we need to extract it server side.
normal_speed_video_id = None # The 1.0 speed video
# video_list() example:
# "0.75:nugHYNiD3fI,1.0:7m8pab1MfYY,1.25:3CxdPGXShq8,1.50:F-D7bOFCnXA"
for video_id_str in self.video_list().split(","):
if video_id_str.startswith("1.0:"):
normal_speed_video_id = video_id_str.split(":")[1]
return self.system.render_template('video.html', {
'streams': self.video_list(),
'youtube_id_0_75': self.youtube_id_0_75,
'normal_speed_video_id': self.youtube_id_1_0,
'youtube_id_1_25': self.youtube_id_1_25,
'youtube_id_1_5': self.youtube_id_1_5,
'id': self.location.html_id(),
'position': self.position,
'source': self.source,
'track': self.track,
'display_name': self.display_name_with_default,
'caption_asset_path': "/static/subs/",
'show_captions': self.show_captions,
'show_captions': 'true' if self.show_captions else 'false',
'start': self.start_time,
'end': self.end_time,
'normal_speed_video_id': normal_speed_video_id
'end': self.end_time
})
# TODO: (pfogg) I am highly uncertain about inheriting from
# RawDescriptor for the xml-related methods. This makes LMS unit tests
# pass, but this really shouldn't be a RawDescriptor if users can't
# see raw xml.
class VideoDescriptor(VideoFields, RawDescriptor):
# also if it's just return super(...)... then we can just remove these methods, ha
class VideoDescriptor(VideoFields,
MetadataOnlyEditingDescriptor,
RawDescriptor):
module_class = VideoModule
stores_state = True
template_dir_name = "video"
@property
def non_editable_metadata_fields(self):
non_editable_fields = super(MetadataOnlyEditingDescriptor, self).non_editable_metadata_fields
non_editable_fields.extend([VideoModule.start_time,
VideoModule.end_time])
return non_editable_fields
@classmethod
def from_xml(cls, xml_data, system, org=None, course=None):
"""
Creates an instance of this descriptor from the supplied xml_data.
This may be overridden by subclasses
xml_data: A string of xml that will be translated into data and children for
this module
system: A DescriptorSystem for interacting with external resources
org and course are optional strings that will be used in the generated modules
url identifiers
"""
return super(RawDescriptor, cls).from_xml(xml_data, system, org, course)
def export_to_xml(self, resource_fs):
"""
Returns an xml string representign this module, and all modules
underneath it. May also write required resources out to resource_fs
Assumes that modules have single parentage (that no module appears twice
in the same course), and that it is thus safe to nest modules as xml
children as appropriate.
The returned XML should be able to be parsed back into an identical
XModuleDescriptor using the from_xml method with the same system, org,
and course
resource_fs is a pyfilesystem object (from the fs package)
"""
return super(RawDescriptor, self).export_to_xml(resource_fs)
# xml = etree.Element('video')
# xml.set('youtube', self.video_list())
# xml.set('show_captions', self.show_captions)
# xml.set('from', self.start_time)
# xml.set('to', self.end_time)
# source_tag = etree.SubElement(xml, 'source')
# source_tag.set('src', self.source)
# track_tag = etree.SubElement(xml, 'track')
# track_tag.set('src', self.track)
# return etree.tostring(xml, pretty_print=True, encoding='utf-8')
# def video_list(self):
# videos = [self.youtube_id_0_75, self.youtube_id_1_0,
# self.youtube_id_1_25, self.youtube_id_1_5]
# streams = [':'.join((video, youtube_id))
# for video, youtube_id
# in zip(YOUTUBE_SPEEDS, videos)]
# return ','.join(streams)
......@@ -32,7 +32,17 @@
width="640" height="390"></embed>
</object>
%else:
<div id="video_${id}" class="video" data-streams="${streams}" data-show-captions="${show_captions}" data-start="${start}" data-end="${end}" data-caption-asset-path="${caption_asset_path}" data-autoplay="${settings.MITX_FEATURES['AUTOPLAY_VIDEOS']}">
<div id="video_${id}"
class="video"
data-youtube-id-0-75="${youtube_id_0_75}"
data-normal-speed-video-id="${normal_speed_video_id}"
data-youtube-id-1-25="${youtube_id_1_25}"
data-youtube-id-1-5="${youtube_id_1_5}"
data-show-captions="${show_captions}"
data-start="${start}"
data-end="${end}"
data-caption-asset-path="${caption_asset_path}"
data-autoplay="${settings.MITX_FEATURES['AUTOPLAY_VIDEOS']}">
<div class="tc-wrapper">
<article class="video-wrapper">
<section class="video-player">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment