Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
faab4cae
Commit
faab4cae
authored
Feb 13, 2014
by
polesye
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
BLD-837: Fix download transcript behavior.
parent
989a9796
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
130 additions
and
142 deletions
+130
-142
cms/djangoapps/contentstore/features/video-editor.py
+1
-1
common/lib/xmodule/xmodule/tests/test_video.py
+30
-0
common/lib/xmodule/xmodule/video_module.py
+81
-49
lms/djangoapps/courseware/tests/test_video_mongo.py
+18
-92
No files found.
cms/djangoapps/contentstore/features/video-editor.py
View file @
faab4cae
...
...
@@ -45,7 +45,7 @@ def correct_video_settings(_step):
[
'HTML5 Transcript'
,
''
,
False
],
[
'Show Transcript'
,
'True'
,
False
],
[
'Start Time'
,
'00:00:00'
,
False
],
#
['Transcript Download Allowed', 'False', False],
[
'Transcript Download Allowed'
,
'False'
,
False
],
[
'Video Download Allowed'
,
'False'
,
False
],
[
'Video Sources'
,
''
,
False
],
[
'Youtube ID'
,
'OEoXaMPEzfM'
,
False
],
...
...
common/lib/xmodule/xmodule/tests/test_video.py
View file @
faab4cae
...
...
@@ -277,6 +277,36 @@ class VideoDescriptorImportTestCase(unittest.TestCase):
'data'
:
''
})
def
test_from_xml_missing_download_track
(
self
):
"""
Ensure that attributes have the right values if they aren't
explicitly set in XML.
"""
module_system
=
DummySystem
(
load_error_modules
=
True
)
xml_data
=
'''
<video display_name="Test Video"
youtube="1.0:p2Q6BrNhdh8,1.25:1EeWXzPdhSA"
show_captions="true">
<source src="http://www.example.com/source.mp4"/>
<track src="http://www.example.com/track"/>
</video>
'''
output
=
VideoDescriptor
.
from_xml
(
xml_data
,
module_system
,
Mock
())
self
.
assert_attributes_equal
(
output
,
{
'youtube_id_0_75'
:
''
,
'youtube_id_1_0'
:
'p2Q6BrNhdh8'
,
'youtube_id_1_25'
:
'1EeWXzPdhSA'
,
'youtube_id_1_5'
:
''
,
'show_captions'
:
True
,
'start_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'end_time'
:
datetime
.
timedelta
(
seconds
=
0.0
),
'track'
:
'http://www.example.com/track'
,
'download_track'
:
True
,
'download_video'
:
True
,
'html5_sources'
:
[
'http://www.example.com/source.mp4'
],
'data'
:
''
})
def
test_from_xml_no_attributes
(
self
):
"""
Make sure settings are correct if none are explicitly set in XML.
...
...
common/lib/xmodule/xmodule/video_module.py
View file @
faab4cae
...
...
@@ -13,12 +13,12 @@ in XML.
import
json
import
logging
from
HTMLParser
import
HTMLParser
from
lxml
import
etree
from
pkg_resources
import
resource_string
import
datetime
import
copy
from
webob
import
Response
from
pysrt
import
SubRipTime
,
SubRipItem
from
django.conf
import
settings
...
...
@@ -116,8 +116,6 @@ class VideoFields(object):
display_name
=
"Video Sources"
,
scope
=
Scope
.
settings
,
)
# `track` is deprecated field and should not be used in future.
# `download_track` is used instead.
track
=
String
(
help
=
"The external URL to download the timed transcript track. This appears as a link beneath the video."
,
display_name
=
"Download Transcript"
,
...
...
@@ -199,8 +197,6 @@ class VideoModule(VideoFields, XModule):
if
key
==
'speed'
:
self
.
global_speed
=
self
.
speed
log
.
debug
(
u"Test."
)
return
json
.
dumps
({
'success'
:
True
})
log
.
debug
(
u"GET {0}"
.
format
(
data
))
...
...
@@ -221,14 +217,11 @@ class VideoModule(VideoFields, XModule):
elif
self
.
html5_sources
:
sources
[
'main'
]
=
self
.
html5_sources
[
0
]
# Commented due to the reason described in BLD-811.
# if self.download_track:
# if self.track:
# track_url = self.track
# elif self.sub:
# track_url = self.runtime.handler_url(self, 'download_transcript')
track_url
=
self
.
track
if
self
.
download_track
:
if
self
.
track
:
track_url
=
self
.
track
elif
self
.
sub
:
track_url
=
self
.
runtime
.
handler_url
(
self
,
'download_transcript'
)
return
self
.
system
.
render_template
(
'video.html'
,
{
'ajax_url'
:
self
.
system
.
ajax_url
+
'/save_user_state'
,
...
...
@@ -257,14 +250,14 @@ class VideoModule(VideoFields, XModule):
def
get_transcript
(
self
,
subs_id
):
'''
Returns transcript
without timecodes
.
Returns transcript
in *.srt format
.
Args:
`subs_id`: str, subtitles id
Raises:
- NotFoundError if cannot find transcript file in storage.
- ValueError if transcript file is incorrect JSON.
- ValueError if transcript file is
empty or
incorrect JSON.
- KeyError if transcript file has incorrect format.
'''
...
...
@@ -272,11 +265,13 @@ class VideoModule(VideoFields, XModule):
content_location
=
StaticContent
.
compute_location
(
self
.
location
.
org
,
self
.
location
.
course
,
filename
)
sjson_transcripts
=
contentstore
()
.
find
(
content_location
)
str_subs
=
_generate_srt_from_sjson
(
json
.
loads
(
sjson_transcripts
.
data
),
speed
=
1.0
)
if
not
str_subs
:
log
.
debug
(
'generate_srt_from_sjson produces no subtitles'
)
raise
ValueError
data
=
contentstore
()
.
find
(
content_location
)
.
data
text
=
json
.
loads
(
data
)[
'text'
]
return
HTMLParser
()
.
unescape
(
"
\n
"
.
join
(
text
))
return
str_subs
@XBlock.handler
...
...
@@ -296,9 +291,9 @@ class VideoModule(VideoFields, XModule):
response
=
Response
(
subs
,
headerlist
=
[
(
'Content-Disposition'
,
'attachment; filename="{0}.
tx
t"'
.
format
(
self
.
sub
)),
(
'Content-Disposition'
,
'attachment; filename="{0}.
sr
t"'
.
format
(
self
.
sub
)),
])
response
.
content_type
=
"
text/plain; charset=utf-8
"
response
.
content_type
=
"
application/x-subrip
"
return
response
...
...
@@ -325,15 +320,6 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
'''
Mostly handles backward compatibility issues.
Track was deprecated field, but functionality was reverted,
this is commented out because might be used in future.
###
`track` is deprecated field.
If `track` field exists show `track` field on front-end as not-editable
but clearable. Dropdown `download_track` is a new field and it has value
True.
###
`source` is deprecated field.
a) If `source` exists and `source` is not `html5_sources`: show `source`
field on front-end as not-editable but clearable. Dropdown is a new
...
...
@@ -352,14 +338,6 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
editable_fields
=
self
.
editable_metadata_fields
# Commented due to the reason described in BLD-811.
# self.track_visible = False
# if self.track:
# self.track_visible = True
# download_track = editable_fields['download_track']
# if not download_track['explicitly_set']:
# self.download_track = True
self
.
source_visible
=
False
if
self
.
source
:
# If `source` field value exist in the `html5_sources` field values,
...
...
@@ -377,16 +355,6 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
def
editable_metadata_fields
(
self
):
editable_fields
=
super
(
VideoDescriptor
,
self
)
.
editable_metadata_fields
# Commented due to the reason described in BLD-811.
# if hasattr(self, 'track_visible'):
# if self.track_visible:
# editable_fields['track']['non_editable'] = True
# else:
# editable_fields.pop('track')
if
'download_track'
in
editable_fields
:
editable_fields
.
pop
(
'download_track'
)
if
hasattr
(
self
,
'source_visible'
):
if
self
.
source_visible
:
editable_fields
[
'source'
][
'non_editable'
]
=
True
...
...
@@ -585,10 +553,17 @@ class VideoDescriptor(VideoFields, TabsEditingDescriptor, EmptyDataRawDescriptor
value
=
deserialize_field
(
cls
.
fields
[
attr
],
value
)
field_data
[
attr
]
=
value
# Add `source` for backwards compatibility if xml doesn't have `download_video`.
# For backwards compatibility: Add `source` if XML doesn't have `download_video`
# attribute.
if
'download_video'
not
in
field_data
and
sources
:
field_data
[
'source'
]
=
field_data
[
'html5_sources'
][
0
]
# For backwards compatibility: if XML doesn't have `download_track` attribute,
# it means that it is an old format. So, if `track` has some value,
# `download_track` needs to have value `True`.
if
'download_track'
not
in
field_data
and
track
is
not
None
:
field_data
[
'download_track'
]
=
True
return
field_data
...
...
@@ -610,3 +585,60 @@ def _create_youtube_string(module):
for
pair
in
zip
(
youtube_speeds
,
youtube_ids
)
if
pair
[
1
]])
def
_generate_subs
(
speed
,
source_speed
,
source_subs
):
"""
Generate transcripts from one speed to another speed.
Args:
`speed`: float, for this speed subtitles will be generated,
`source_speed`: float, speed of source_subs
`soource_subs`: dict, existing subtitles for speed `source_speed`.
Returns:
`subs`: dict, actual subtitles.
"""
if
speed
==
source_speed
:
return
source_subs
coefficient
=
1.0
*
speed
/
source_speed
subs
=
{
'start'
:
[
int
(
round
(
timestamp
*
coefficient
))
for
timestamp
in
source_subs
[
'start'
]
],
'end'
:
[
int
(
round
(
timestamp
*
coefficient
))
for
timestamp
in
source_subs
[
'end'
]
],
'text'
:
source_subs
[
'text'
]}
return
subs
def
_generate_srt_from_sjson
(
sjson_subs
,
speed
):
"""Generate transcripts with speed = 1.0 from sjson to SubRip (*.srt).
:param sjson_subs: "sjson" subs.
:param speed: speed of `sjson_subs`.
:returns: "srt" subs.
"""
output
=
''
equal_len
=
len
(
sjson_subs
[
'start'
])
==
len
(
sjson_subs
[
'end'
])
==
len
(
sjson_subs
[
'text'
])
if
not
equal_len
:
return
output
sjson_speed_1
=
_generate_subs
(
speed
,
1
,
sjson_subs
)
for
i
in
range
(
len
(
sjson_speed_1
[
'start'
])):
item
=
SubRipItem
(
index
=
i
,
start
=
SubRipTime
(
milliseconds
=
sjson_speed_1
[
'start'
][
i
]),
end
=
SubRipTime
(
milliseconds
=
sjson_speed_1
[
'end'
][
i
]),
text
=
sjson_speed_1
[
'text'
][
i
]
)
output
+=
(
unicode
(
item
))
output
+=
'
\n
'
return
output
lms/djangoapps/courseware/tests/test_video_mongo.py
View file @
faab4cae
...
...
@@ -5,7 +5,6 @@ from mock import patch, PropertyMock
import
os
import
tempfile
import
textwrap
import
unittest
from
functools
import
partial
from
xmodule.contentstore.content
import
StaticContent
...
...
@@ -20,7 +19,6 @@ from xmodule.exceptions import NotFoundError
class
TestVideo
(
BaseTestXmodule
):
"""Integration tests: web client + mongo."""
CATEGORY
=
"video"
DATA
=
SOURCE_XML
METADATA
=
{}
...
...
@@ -72,7 +70,7 @@ class TestVideoYouTube(TestVideo):
'start'
:
3603.0
,
'saved_video_position'
:
0.0
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'track'
:
''
,
'track'
:
None
,
'youtube_streams'
:
_create_youtube_string
(
self
.
item_module
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
False
),
'yt_test_timeout'
:
1500
,
...
...
@@ -128,7 +126,7 @@ class TestVideoNonYouTube(TestVideo):
'start'
:
3603.0
,
'saved_video_position'
:
0.0
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'track'
:
''
,
'track'
:
None
,
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'yt_test_timeout'
:
1500
,
...
...
@@ -147,7 +145,6 @@ class TestGetHtmlMethod(BaseTestXmodule):
'''
CATEGORY
=
"video"
DATA
=
SOURCE_XML
maxDiff
=
None
METADATA
=
{}
def
setUp
(
self
):
...
...
@@ -225,14 +222,13 @@ class TestGetHtmlMethod(BaseTestXmodule):
)
self
.
initialize_module
(
data
=
DATA
)
#
track_url = self.item_descriptor.xmodule_runtime.handler_url(self.item_module, 'download_transcript')
track_url
=
self
.
item_descriptor
.
xmodule_runtime
.
handler_url
(
self
.
item_module
,
'download_transcript'
)
context
=
self
.
item_module
.
render
(
'student_view'
)
.
content
expected_context
.
update
({
'ajax_url'
:
self
.
item_descriptor
.
xmodule_runtime
.
ajax_url
+
'/save_user_state'
,
# 'track': track_url if data['expected_track_url'] == u'a_sub_file.srt.sjson' else data['expected_track_url'],
'track'
:
u'http://www.example.com/track'
if
data
[
'track'
]
else
''
,
'track'
:
track_url
if
data
[
'expected_track_url'
]
==
u'a_sub_file.srt.sjson'
else
data
[
'expected_track_url'
],
'sub'
:
data
[
'sub'
],
'id'
:
self
.
item_module
.
location
.
html_id
(),
})
...
...
@@ -316,7 +312,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
'start'
:
3603.0
,
'saved_video_position'
:
0.0
,
'sub'
:
u'a_sub_file.srt.sjson'
,
'track'
:
''
,
'track'
:
None
,
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'yt_test_timeout'
:
1500
,
...
...
@@ -430,7 +426,7 @@ class TestVideoDescriptorInitialization(BaseTestXmodule):
},
}
metadata
=
{
'track'
:
''
,
'track'
:
None
,
'source'
:
'http://example.org/video.mp4'
,
'html5_sources'
:
[
'http://youtu.be/OEoXaMPEzfM.mp4'
],
}
...
...
@@ -454,85 +450,6 @@ class TestVideoDescriptorInitialization(BaseTestXmodule):
self
.
assertNotIn
(
'source'
,
fields
)
self
.
assertFalse
(
self
.
item_module
.
download_video
)
@unittest.skip
(
'Skipped due to the reason described in BLD-811'
)
def
test_track_is_not_empty
(
self
):
metatdata
=
{
'track'
:
'http://example.org/track'
,
}
self
.
initialize_module
(
metadata
=
metatdata
)
fields
=
self
.
item_descriptor
.
editable_metadata_fields
self
.
assertIn
(
'track'
,
fields
)
self
.
assertEqual
(
self
.
item_module
.
track
,
'http://example.org/track'
)
self
.
assertTrue
(
self
.
item_module
.
download_track
)
self
.
assertTrue
(
self
.
item_module
.
track_visible
)
@unittest.skip
(
'Skipped due to the reason described in BLD-811'
)
@patch
(
'xmodule.x_module.XModuleDescriptor.editable_metadata_fields'
,
new_callable
=
PropertyMock
)
def
test_download_track_is_explicitly_set
(
self
,
mock_editable_fields
):
mock_editable_fields
.
return_value
=
{
'download_track'
:
{
'default_value'
:
False
,
'explicitly_set'
:
True
,
'display_name'
:
'Transcript Download Allowed'
,
'help'
:
'Show a link beneath the video to allow students to download the transcript.'
,
'type'
:
'Boolean'
,
'value'
:
False
,
'field_name'
:
'download_track'
,
'options'
:
[
{
'display_name'
:
"True"
,
"value"
:
True
},
{
'display_name'
:
"False"
,
"value"
:
False
}
],
},
'track'
:
{
'default_value'
:
''
,
'explicitly_set'
:
False
,
'display_name'
:
'Download Transcript'
,
'help'
:
'The external URL to download the timed transcript track.'
,
'type'
:
'Generic'
,
'value'
:
u'http://example.org/track'
,
'field_name'
:
'track'
,
'options'
:
[],
},
'source'
:
{
'default_value'
:
''
,
'explicitly_set'
:
False
,
'display_name'
:
'Download Video'
,
'help'
:
'The external URL to download the video.'
,
'type'
:
'Generic'
,
'value'
:
u''
,
'field_name'
:
'source'
,
'options'
:
[],
},
}
metadata
=
{
'source'
:
''
,
'track'
:
'http://example.org/track'
,
}
self
.
initialize_module
(
metadata
=
metadata
)
fields
=
self
.
item_descriptor
.
editable_metadata_fields
self
.
assertIn
(
'track'
,
fields
)
self
.
assertEqual
(
self
.
item_module
.
track
,
'http://example.org/track'
)
self
.
assertFalse
(
self
.
item_module
.
download_track
)
self
.
assertTrue
(
self
.
item_module
.
track_visible
)
@unittest.skip
(
'Skipped due to the reason described in BLD-811'
)
def
test_track_is_empty
(
self
):
metatdata
=
{
'track'
:
''
,
}
self
.
initialize_module
(
metadata
=
metatdata
)
fields
=
self
.
item_descriptor
.
editable_metadata_fields
self
.
assertNotIn
(
'track'
,
fields
)
self
.
assertEqual
(
self
.
item_module
.
track
,
''
)
self
.
assertFalse
(
self
.
item_module
.
download_track
)
self
.
assertFalse
(
self
.
item_module
.
track_visible
)
class
TestVideoGetTranscriptsMethod
(
TestVideo
):
"""
...
...
@@ -556,7 +473,7 @@ class TestVideoGetTranscriptsMethod(TestVideo):
self
.
item_module
.
render
(
'student_view'
)
item
=
self
.
item_descriptor
.
xmodule_runtime
.
xmodule_instance
good_sjson
=
_create_file
(
content
=
"""
good_sjson
=
_create_file
(
content
=
textwrap
.
dedent
(
"""
\
{
"start": [
270,
...
...
@@ -571,13 +488,22 @@ class TestVideoGetTranscriptsMethod(TestVideo):
"Let's start with what is on your screen right now."
]
}
"""
)
"""
)
)
_upload_file
(
good_sjson
,
self
.
item_module
.
location
)
subs_id
=
_get_subs_id
(
good_sjson
.
name
)
text
=
item
.
get_transcript
(
subs_id
)
expected_text
=
"Hi, welcome to Edx.
\n
Let's start with what is on your screen right now."
expected_text
=
textwrap
.
dedent
(
"""
\
0
00:00:00,270 --> 00:00:02,720
Hi, welcome to Edx.
1
00:00:02,720 --> 00:00:05,430
Let's start with what is on your screen right now.
"""
)
self
.
assertEqual
(
text
,
expected_text
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment