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
fc32820f
Commit
fc32820f
authored
Jun 23, 2015
by
muzaffaryousaf
Committed by
Sarina Canelake
Jul 24, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Using youtube api (v3) instead of v2 to get the video duration .
TNL-2413
parent
82f937b9
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
222 additions
and
149 deletions
+222
-149
cms/envs/acceptance.py
+2
-2
cms/envs/aws.py
+1
-0
cms/envs/bok_choy.py
+2
-2
cms/envs/common.py
+7
-4
cms/templates/ux/reference/container.html
+1
-1
common/djangoapps/terrain/setup_prereqs.py
+1
-2
common/djangoapps/terrain/stubs/youtube.py
+12
-10
common/lib/xmodule/xmodule/js/fixtures/video.html
+1
-1
common/lib/xmodule/xmodule/js/fixtures/video_all.html
+1
-1
common/lib/xmodule/xmodule/js/fixtures/video_html5.html
+1
-1
common/lib/xmodule/xmodule/js/fixtures/video_no_captions.html
+1
-1
common/lib/xmodule/xmodule/js/fixtures/video_with_bumper.html
+1
-1
common/lib/xmodule/xmodule/js/fixtures/video_yt_multiple.html
+3
-3
common/lib/xmodule/xmodule/js/js_test.yml
+1
-0
common/lib/xmodule/xmodule/js/spec/helper.js
+31
-19
common/lib/xmodule/xmodule/js/spec/video/general_spec.js
+6
-0
common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js
+2
-2
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
+104
-75
common/lib/xmodule/xmodule/js/src/video/10_main.js
+2
-0
common/lib/xmodule/xmodule/tests/test_video.py
+2
-2
common/lib/xmodule/xmodule/video_module/video_module.py
+13
-1
common/test/acceptance/tests/helpers.py
+1
-2
lms/djangoapps/courseware/features/video.feature
+4
-4
lms/djangoapps/courseware/tests/test_video_mongo.py
+12
-8
lms/envs/acceptance.py
+2
-2
lms/envs/aws.py
+1
-0
lms/envs/bok_choy.py
+2
-2
lms/envs/common.py
+4
-3
lms/static/js_test.yml
+1
-0
No files found.
cms/envs/acceptance.py
View file @
fc32820f
...
...
@@ -118,8 +118,8 @@ except ImportError:
pass
# Point the URL used to test YouTube availability to our stub YouTube server
YOUTUBE
[
'API'
]
=
"127.0.0.1:{0}/get_youtube_api/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'
TEST_URL'
]
=
"
127.0.0.1:{0}/test_youtube/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'API'
]
=
"
http://
127.0.0.1:{0}/get_youtube_api/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'
METADATA_URL'
]
=
"http://
127.0.0.1:{0}/test_youtube/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'TEXT_API'
][
'url'
]
=
"127.0.0.1:{0}/test_transcripts_youtube/"
.
format
(
YOUTUBE_PORT
)
# Generate a random UUID so that different runs of acceptance tests don't break each other
...
...
cms/envs/aws.py
View file @
fc32820f
...
...
@@ -348,3 +348,4 @@ if FEATURES['ENABLE_COURSEWARE_INDEX'] or FEATURES['ENABLE_LIBRARY_INDEX']:
XBLOCK_SETTINGS
=
ENV_TOKENS
.
get
(
'XBLOCK_SETTINGS'
,
{})
XBLOCK_SETTINGS
.
setdefault
(
"VideoDescriptor"
,
{})[
"licensing_enabled"
]
=
FEATURES
.
get
(
"LICENSING"
,
False
)
XBLOCK_SETTINGS
.
setdefault
(
"VideoModule"
,
{})[
'YOUTUBE_API_KEY'
]
=
AUTH_TOKENS
.
get
(
'YOUTUBE_API_KEY'
,
YOUTUBE_API_KEY
)
cms/envs/bok_choy.py
View file @
fc32820f
...
...
@@ -103,8 +103,8 @@ FEATURES['ENTRANCE_EXAMS'] = True
# Point the URL used to test YouTube availability to our stub YouTube server
YOUTUBE_PORT
=
9080
YOUTUBE
[
'API'
]
=
"127.0.0.1:{0}/get_youtube_api/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'
TEST_URL'
]
=
"
127.0.0.1:{0}/test_youtube/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'API'
]
=
"
http://
127.0.0.1:{0}/get_youtube_api/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'
METADATA_URL'
]
=
"http://
127.0.0.1:{0}/test_youtube/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'TEXT_API'
][
'url'
]
=
"127.0.0.1:{0}/test_transcripts_youtube/"
.
format
(
YOUTUBE_PORT
)
FEATURES
[
'ENABLE_COURSEWARE_INDEX'
]
=
True
...
...
cms/envs/common.py
View file @
fc32820f
...
...
@@ -44,7 +44,7 @@ from lms.envs.common import (
PROFILE_IMAGE_SECRET_KEY
,
PROFILE_IMAGE_MIN_BYTES
,
PROFILE_IMAGE_MAX_BYTES
,
# The following setting is included as it is used to check whether to
# display credit eligibility table on the CMS or not.
ENABLE_CREDIT_ELIGIBILITY
ENABLE_CREDIT_ELIGIBILITY
,
YOUTUBE_API_KEY
)
from
path
import
path
from
warnings
import
simplefilter
...
...
@@ -652,10 +652,10 @@ CELERY_QUEUES = {
YOUTUBE
=
{
# YouTube JavaScript API
'API'
:
'www.youtube.com/iframe_api'
,
'API'
:
'
https://
www.youtube.com/iframe_api'
,
# URL to
test YouTube availability
'
TEST_URL'
:
'gdata.youtube.com/feeds/api/videos/
'
,
# URL to
get YouTube metadata
'
METADATA_URL'
:
'https://www.googleapis.com/youtube/v3/videos
'
,
# Current youtube api for requesting transcripts.
# For example: http://video.google.com/timedtext?lang=en&v=j_jEn79vS3g.
...
...
@@ -994,6 +994,9 @@ ELASTIC_FIELD_MAPPINGS = {
XBLOCK_SETTINGS
=
{
"VideoDescriptor"
:
{
"licensing_enabled"
:
FEATURES
.
get
(
"LICENSING"
,
False
)
},
'VideoModule'
:
{
'YOUTUBE_API_KEY'
:
YOUTUBE_API_KEY
}
}
...
...
cms/templates/ux/reference/container.html
View file @
fc32820f
...
...
@@ -137,7 +137,7 @@
<h2>
Video
</h2>
<div
id=
"video_i4x-AndyA-ABT101-video-72b5a0d74e8c4ed4a4d4e6bf67837c09"
class=
"video closed is-initialized"
data-streams=
"1.00:OEoXaMPEzfM"
data-save-state-url=
"/preview/xblock/i4x:;_;_AndyA;_ABT101;_video;_72b5a0d74e8c4ed4a4d4e6bf67837c09/handler/xmodule_handler/save_user_state"
data-caption-data-dir=
"None"
data-show-captions=
"true"
data-general-speed=
"1.0"
data-speed=
"null"
data-start=
"0.0"
data-end=
"0.0"
data-caption-asset-path=
"/c4x/AndyA/ABT101/asset/subs_"
data-autoplay=
"False"
data-yt-test-timeout=
"1500"
data-yt-test-url=
"https://
gdata.youtube.com/feeds/api
/videos/"
data-autohide-html5=
"False"
tabindex=
"-1"
>
<div
id=
"video_i4x-AndyA-ABT101-video-72b5a0d74e8c4ed4a4d4e6bf67837c09"
class=
"video closed is-initialized"
data-streams=
"1.00:OEoXaMPEzfM"
data-save-state-url=
"/preview/xblock/i4x:;_;_AndyA;_ABT101;_video;_72b5a0d74e8c4ed4a4d4e6bf67837c09/handler/xmodule_handler/save_user_state"
data-caption-data-dir=
"None"
data-show-captions=
"true"
data-general-speed=
"1.0"
data-speed=
"null"
data-start=
"0.0"
data-end=
"0.0"
data-caption-asset-path=
"/c4x/AndyA/ABT101/asset/subs_"
data-autoplay=
"False"
data-yt-test-timeout=
"1500"
data-yt-test-url=
"https://
www.googleapis.com/youtube/v3
/videos/"
data-autohide-html5=
"False"
tabindex=
"-1"
>
<div
class=
"focus_grabber first"
tabindex=
"-1"
></div>
<div
class=
"tc-wrapper"
>
...
...
common/djangoapps/terrain/setup_prereqs.py
View file @
fc32820f
...
...
@@ -28,8 +28,7 @@ SERVICES = {
YOUTUBE_API_URLS
=
{
'main'
:
'https://www.youtube.com/'
,
'player'
:
'http://www.youtube.com/iframe_api'
,
'metadata'
:
'http://gdata.youtube.com/feeds/api/videos/'
,
'player'
:
'https://www.youtube.com/iframe_api'
,
# For transcripts, you need to check an actual video, so we will
# just specify our default video and see if that one is available.
'transcript'
:
'http://video.google.com/timedtext?lang=en&v=OEoXaMPEzfM'
,
...
...
common/djangoapps/terrain/stubs/youtube.py
View file @
fc32820f
...
...
@@ -95,6 +95,9 @@ class StubYouTubeHandler(StubHttpRequestHandler):
if
self
.
server
.
config
.
get
(
'youtube_api_blocked'
):
self
.
send_response
(
404
,
content
=
''
,
headers
=
{
'Content-type'
:
'text/plain'
})
else
:
# Delay the response to simulate network latency
time
.
sleep
(
self
.
server
.
config
.
get
(
'time_to_response'
,
self
.
DEFAULT_DELAY_SEC
))
# Get the response to send from YouTube.
# We need to do this every time because Google sometimes sends different responses
# as part of their own experiments, which has caused our tests to become "flaky"
...
...
@@ -117,17 +120,16 @@ class StubYouTubeHandler(StubHttpRequestHandler):
# Construct the response content
callback
=
self
.
get_params
[
'callback'
]
youtube_metadata
=
json
.
loads
(
requests
.
get
(
"http://gdata.youtube.com/feeds/api/videos/{id}?v=2&alt=jsonc"
.
format
(
id
=
youtube_id
)
)
.
text
)
data
=
OrderedDict
({
'data'
:
OrderedDict
({
'id'
:
youtube_id
,
'message'
:
message
,
'duration'
:
youtube_metadata
[
'data'
][
'duration'
],
})
'items'
:
list
(
OrderedDict
({
'contentDetails'
:
OrderedDict
({
'id'
:
youtube_id
,
'duration'
:
'PT2M20S'
,
})
})
)
})
response
=
"{cb}({data})"
.
format
(
cb
=
callback
,
data
=
json
.
dumps
(
data
))
...
...
common/lib/xmodule/xmodule/js/fixtures/video.html
View file @
fc32820f
...
...
@@ -4,7 +4,7 @@
<div
id=
"video_id"
class=
"video closed"
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": "[]", "speed": "1.5", "startTime": "", "streams": "0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
TestUrl": "gdata.youtube.com/feeds/api
/videos/"}'
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": "[]", "speed": "1.5", "startTime": "", "streams": "0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
MetadataUrl": "www.googleapis.com/youtube/v3
/videos/"}'
>
<div
class=
"focus_grabber first"
></div>
...
...
common/lib/xmodule/xmodule/js/fixtures/video_all.html
View file @
fc32820f
...
...
@@ -4,7 +4,7 @@
<div
id=
"video_id"
class=
"video closed"
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
TestUrl": "gdata.youtube.com/feeds/api
/videos/"}'
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
MetadataUrl": "www.googleapis.com/youtube/v3
/videos/"}'
>
<div
class=
"focus_grabber first"
></div>
...
...
common/lib/xmodule/xmodule/js/fixtures/video_html5.html
View file @
fc32820f
...
...
@@ -4,7 +4,7 @@
<div
id=
"video_id"
class=
"video closed"
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
TestUrl": "gdata.youtube.com/feeds/api
/videos/", "source": "", "html5_sources": ["http://youtu.be/3_yD_cEKoCk.mp4"]}'
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
MetadataUrl": "www.googleapis.com/youtube/v3
/videos/", "source": "", "html5_sources": ["http://youtu.be/3_yD_cEKoCk.mp4"]}'
>
<div
class=
"focus_grabber first"
></div>
...
...
common/lib/xmodule/xmodule/js/fixtures/video_no_captions.html
View file @
fc32820f
...
...
@@ -4,7 +4,7 @@
<div
id=
"video_id"
class=
"video closed"
data-metadata=
'{"streams":"0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl", "showCaptions": false, "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "speed": "1.5", "startTime": "", "end": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytTestTimeout": "1500", "yt
TestUrl": "gdata.youtube.com/feeds/api
/videos/"}'
data-metadata=
'{"streams":"0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl", "showCaptions": false, "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "speed": "1.5", "startTime": "", "end": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytTestTimeout": "1500", "yt
MetadataUrl": "www.googleapis.com/youtube/v3
/videos/"}'
>
<div
class=
"focus_grabber first"
></div>
...
...
common/lib/xmodule/xmodule/js/fixtures/video_with_bumper.html
View file @
fc32820f
...
...
@@ -4,7 +4,7 @@
<div
id=
"video_id"
class=
"video closed"
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
TestUrl": "gdata.youtube.com/feeds/api
/videos/"}'
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "", "sub": "Z5KLxerq05Y", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
MetadataUrl": "www.googleapis.com/youtube/v3
/videos/"}'
data-bumper-metadata=
'{"transcriptLanguage": "en", "showCaptions": "true", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "transcriptTranslationUrl": "/transcript/translation/__lang__/?is_bumper=1", "transcriptAvailableTranslationsUrl": "/transcript/available_translations/?is_bumper=1", "streams": "", "saveStateUrl": "/save_user_state"}'
data-poster=
'{"url": "xmodule/include/fixtures/poster.jpg", "type": "youtube"}'
>
...
...
common/lib/xmodule/xmodule/js/fixtures/video_yt_multiple.html
View file @
fc32820f
...
...
@@ -4,7 +4,7 @@
<div
id=
"video_id1"
class=
"video closed"
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl", "sub": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
TestUrl": "gdata.youtube.com/feeds/api
/videos/"}'
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.5", "startTime": "", "streams": "0.5:7tqY6eQzVhE,1.0:cogebirgzzM,1.5:abcdefghijkl", "sub": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
MetadataUrl": "www.googleapis.com/youtube/v3
/videos/"}'
>
<div
class=
"focus_grabber first"
></div>
...
...
@@ -38,7 +38,7 @@
<div
id=
"video_id2"
class=
"video"
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.0", "startTime": "", "streams": "0.75:7tqY6eQzVhE,1.0:cogebirgzzM", "sub": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
TestUrl": "gdata.youtube.com/feeds/api
/videos/"}'
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.0", "startTime": "", "streams": "0.75:7tqY6eQzVhE,1.0:cogebirgzzM", "sub": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
MetadataUrl": "www.googleapis.com/youtube/v3
/videos/"}'
>
<div
class=
"tc-wrapper"
>
<article
class=
"video-wrapper"
>
...
...
@@ -68,7 +68,7 @@
<div
id=
"video_id3"
class=
"video"
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.0", "startTime": "", "streams": "0.75:7tqY6eQzVhE,1.0:cogebirgzzM", "sub": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
TestUrl": "gdata.youtube.com/feeds/api
/videos/"}'
data-metadata=
'{"autohideHtml5": "true", "autoplay": "false", "captionDataDir": "", "endTime": "", "generalSpeed": "1.0", "saveStateUrl": "/save_user_state", "savedVideoPosition": "0", "showCaptions": "true", "sources": ["xmodule/include/fixtures/test.mp4","xmodule/include/fixtures/test.webm","xmodule/include/fixtures/test.ogv"], "speed": "1.0", "startTime": "", "streams": "0.75:7tqY6eQzVhE,1.0:cogebirgzzM", "sub": "", "transcriptAvailableTranslationsUrl": "/transcript/available_translations", "transcriptLanguage": "en", "transcriptLanguages": {"en": "English", "de": "Deutsch", "zh": "普通话"}, "transcriptTranslationUrl": "/transcript/translation/__lang__", "ytApiUrl": "www.youtube.com/iframe_api", "ytImageUrl": "", "ytTestTimeout": "1500", "yt
MetadataUrl": "www.googleapis.com/youtube/v3
/videos/"}'
>
<div
class=
"tc-wrapper"
>
<article
class=
"video-wrapper"
>
...
...
common/lib/xmodule/xmodule/js/js_test.yml
View file @
fc32820f
...
...
@@ -59,6 +59,7 @@ lib_paths:
-
common_static/js/src/utility.js
-
public/js/split_test_staff.js
-
common_static/js/src/accessibility_tools.js
-
common_static/js/vendor/moment.min.js
# Paths to spec (test) JavaScript files
spec_paths
:
...
...
common/lib/xmodule/xmodule/js/spec/helper.js
View file @
fc32820f
...
...
@@ -36,7 +36,7 @@
return
f
();
}
};
jasmine
.
YT
=
stubbedYT
;
// Stub YouTube API.
window
.
YT
=
stubbedYT
;
...
...
@@ -76,19 +76,27 @@
jasmine
.
stubbedMetadata
=
{
'7tqY6eQzVhE'
:
{
id
:
'7tqY6eQzVhE'
,
duration
:
300
contentDetails
:
{
id
:
'7tqY6eQzVhE'
,
duration
:
'PT5M0S'
}
},
'cogebirgzzM'
:
{
id
:
'cogebirgzzM'
,
duration
:
200
contentDetails
:
{
id
:
'cogebirgzzM'
,
duration
:
'PT3M20S'
}
},
'abcdefghijkl'
:
{
id
:
'abcdefghijkl'
,
duration
:
400
contentDetails
:
{
id
:
'abcdefghijkl'
,
duration
:
'PT6M40S'
}
},
bogus
:
{
duration
:
100
contentDetails
:
{
duration
:
'PT1M40S'
}
}
};
...
...
@@ -122,7 +130,7 @@
}
return
spy
.
andCallFake
(
function
(
settings
)
{
var
match
=
settings
.
url
.
match
(
/
youtube
\.
com
\/
.+
\/
videos
\/(
.+
)\?
v=2&alt=jsonc
/
),
.
match
(
/
googleapis
\.
com
\/
.+
\/
videos
\/\?
id=
(
.+
)
&part=contentDetails
/
),
status
,
callCallback
;
if
(
match
)
{
status
=
match
[
1
].
split
(
'_'
);
...
...
@@ -138,7 +146,7 @@
};
}
else
if
(
settings
.
success
)
{
return
settings
.
success
({
data
:
jasmine
.
stubbedMetadata
[
match
[
1
]]
items
:
jasmine
.
stubbedMetadata
[
match
[
1
]]
});
}
else
{
return
{
...
...
@@ -168,15 +176,6 @@
return
;
}
else
if
(
settings
.
url
===
'/save_user_state'
)
{
return
{
success
:
true
};
}
else
if
(
settings
.
url
===
'http://www.youtube.com/iframe_api'
)
{
// Stub YouTube API.
window
.
YT
=
stubbedYT
;
// Call the callback that must be called when YouTube API is
// loaded. By specification.
window
.
onYouTubeIframeAPIReady
();
return
{
success
:
true
};
}
else
{
throw
'External request attempted for '
+
settings
.
url
+
...
...
@@ -224,6 +223,19 @@
// Stub jQuery.scrollTo module.
$
.
fn
.
scrollTo
=
jasmine
.
createSpy
(
'jQuery.scrollTo'
);
// Stub window.Video.loadYouTubeIFrameAPI()
window
.
Video
.
loadYouTubeIFrameAPI
=
jasmine
.
createSpy
(
'window.Video.loadYouTubeIFrameAPI'
).
andReturn
(
function
(
scriptTag
)
{
var
event
=
document
.
createEvent
(
'Event'
);
if
(
fixture
===
"video.html"
)
{
event
.
initEvent
(
'load'
,
false
,
false
);
}
else
{
event
.
initEvent
(
'error'
,
false
,
false
);
}
scriptTag
.
dispatchEvent
(
event
);
}
);
jasmine
.
initializePlayer
=
function
(
fixture
,
params
)
{
var
state
;
...
...
common/lib/xmodule/xmodule/js/spec/video/general_spec.js
View file @
fc32820f
...
...
@@ -115,6 +115,12 @@
return
state
.
youtubeApiAvailable
===
true
;
},
'YouTube API is loaded'
,
3000
);
window
.
YT
=
jasmine
.
YT
;
// Call the callback that must be called when YouTube API is
// loaded. By specification.
window
.
onYouTubeIframeAPIReady
();
runs
(
function
()
{
// If YouTube API is not loaded, then the code will should create
// a global callback that will be called by API once it is loaded.
...
...
common/lib/xmodule/xmodule/js/spec/video/initialize_spec.js
View file @
fc32820f
...
...
@@ -71,10 +71,10 @@ function (Initialize) {
speed
:
'1.50'
,
metadata
:
{
'testId'
:
{
duration
:
400
duration
:
'PT6M40S'
},
'videoId'
:
{
duration
:
100
duration
:
'PT1M40S'
}
},
videos
:
{
...
...
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
View file @
fc32820f
...
...
@@ -16,6 +16,8 @@ define(
'video/01_initialize.js'
,
[
'video/03_video_player.js'
,
'video/00_i18n.js'
],
function
(
VideoPlayer
,
i18n
)
{
var
moment
=
window
.
moment
;
/**
* @function
*
...
...
@@ -31,6 +33,9 @@ function (VideoPlayer, i18n) {
state
.
initialize
(
element
)
.
done
(
function
()
{
if
(
state
.
isYoutubeType
())
{
state
.
parseSpeed
();
}
// On iPhones and iPods native controls are used.
if
(
/iP
(
hone|od
)
/i
.
test
(
state
.
isTouch
[
0
]))
{
_hideWaitPlaceholder
(
state
);
...
...
@@ -75,7 +80,10 @@ function (VideoPlayer, i18n) {
setSpeed
:
setSpeed
,
speedToString
:
speedToString
,
trigger
:
trigger
,
youtubeId
:
youtubeId
youtubeId
:
youtubeId
,
loadHtmlPlayer
:
loadHtmlPlayer
,
loadYoutubePlayer
:
loadYoutubePlayer
,
loadYouTubeIFrameAPI
:
loadYouTubeIFrameAPI
},
_youtubeApiDeferred
=
null
,
...
...
@@ -126,6 +134,9 @@ function (VideoPlayer, i18n) {
onYTApiReady
=
function
()
{
console
.
log
(
'[Video info]: YouTube API is available and is loaded.'
);
if
(
state
.
htmlPlayerLoaded
)
{
return
;
}
console
.
log
(
'[Video info]: Starting YouTube player.'
);
video
=
VideoPlayer
(
state
);
state
.
modules
.
push
(
video
);
...
...
@@ -176,7 +187,6 @@ function (VideoPlayer, i18n) {
if
(
!
_youtubeApiDeferred
)
{
_youtubeApiDeferred
=
$
.
Deferred
();
setupOnYouTubeIframeAPIReady
();
_loadYoutubeApi
(
state
);
}
else
if
(
!
window
.
onYouTubeIframeAPIReady
||
!
window
.
onYouTubeIframeAPIReady
.
done
)
{
// The Deferred object could have been already defined in a previous
// initialization of the video module. However, since then the global variable
...
...
@@ -196,24 +206,32 @@ function (VideoPlayer, i18n) {
state
.
modules
.
push
(
video
);
state
.
__dfd__
.
resolve
();
state
.
htmlPlayerLoaded
=
true
;
}
}
function
_
load
YoutubeApi
(
state
)
{
console
.
log
(
'[Video info]:
YouTube API is not loaded. Will try to load..
.'
);
function
_
waitFor
YoutubeApi
(
state
)
{
console
.
log
(
'[Video info]:
Starting to wait for YouTube API to load
.'
);
window
.
setTimeout
(
function
()
{
// If YouTube API will load OK, it will run `onYouTubeIframeAPIReady`
// callback, which will set `state.youtubeApiAvailable` to `true`.
// If something goes wrong at this stage, `state.youtubeApiAvailable` is
// `false`.
if
(
!
state
.
youtube
Is
Available
)
{
if
(
!
state
.
youtube
Api
Available
)
{
console
.
log
(
'[Video info]: YouTube API is not available.'
);
if
(
!
state
.
htmlPlayerLoaded
)
{
state
.
loadHtmlPlayer
();
}
}
state
.
el
.
trigger
(
'youtube_availability'
,
[
state
.
youtube
Is
Available
]);
state
.
el
.
trigger
(
'youtube_availability'
,
[
state
.
youtube
Api
Available
]);
},
state
.
config
.
ytTestTimeout
);
$
.
getScript
(
document
.
location
.
protocol
+
'//'
+
state
.
config
.
ytApiUrl
);
}
function
loadYouTubeIFrameAPI
(
scriptTag
)
{
var
firstScriptTag
=
document
.
getElementsByTagName
(
'script'
)[
0
];
firstScriptTag
.
parentNode
.
insertBefore
(
scriptTag
,
firstScriptTag
);
}
// function _configureCaptions(state)
...
...
@@ -454,6 +472,50 @@ function (VideoPlayer, i18n) {
});
}
function
loadYoutubePlayer
()
{
if
(
this
.
htmlPlayerLoaded
)
{
return
;
}
console
.
log
(
'[Video info]: Fetch metadata for YouTube video.'
);
this
.
fetchMetadata
();
this
.
parseSpeed
();
}
function
loadHtmlPlayer
()
{
// When the youtube link doesn't work for any reason
// (for example, firewall) any
// alternate sources should automatically play.
if
(
!
_prepareHTML5Video
(
this
))
{
console
.
log
(
'[Video info]: Continue loading '
+
'YouTube video.'
);
// Non-YouTube sources were not found either.
this
.
el
.
find
(
'.video-player div'
)
.
removeClass
(
'hidden'
);
this
.
el
.
find
(
'.video-player h3'
)
.
addClass
(
'hidden'
);
// If in reality the timeout was to short, try to
// continue loading the YouTube video anyways.
this
.
loadYoutubePlayer
();
}
else
{
console
.
log
(
'[Video info]: Start HTML5 player.'
);
// In-browser HTML5 player does not support quality
// control.
this
.
el
.
find
(
'a.quality_control'
).
hide
();
_renderElements
(
this
);
}
}
// function initialize(element)
// The function set initial configuration and preparation.
...
...
@@ -484,7 +546,7 @@ function (VideoPlayer, i18n) {
// jQuery .data() return object with keys in lower camelCase format.
this
.
config
=
$
.
extend
({},
_getConfiguration
(
this
.
metadata
,
this
.
storage
),
{
element
:
element
,
fadeOutTimeout
:
1400
,
fadeOutTimeout
:
1400
,
captionsFreezeTime
:
10000
,
mode
:
$
.
cookie
(
'edX_video_player_mode'
),
// Available HD qualities will only be accessible once the video has
...
...
@@ -500,6 +562,9 @@ function (VideoPlayer, i18n) {
this
.
speed
=
this
.
speedToString
(
this
.
config
.
speed
||
this
.
config
.
generalSpeed
);
this
.
htmlPlayerLoaded
=
false
;
_setConfigurations
(
this
);
if
(
!
(
_parseYouTubeIDs
(
this
)))
{
...
...
@@ -512,67 +577,30 @@ function (VideoPlayer, i18n) {
}
console
.
log
(
'[Video info]: Start player in HTML5 mode.'
);
_setConfigurations
(
this
);
_renderElements
(
this
);
}
else
{
if
(
!
this
.
youtubeXhr
)
{
this
.
youtubeXhr
=
this
.
getVideoMetadata
();
}
_renderElements
(
this
);
this
.
youtubeXhr
.
always
(
function
(
json
,
status
)
{
// It will work for both if statusCode is 200 or 410.
var
didSucceed
=
(
json
.
error
&&
json
.
error
.
code
===
410
)
||
status
===
'success'
||
status
===
'notmodified'
;
if
(
!
didSucceed
)
{
console
.
log
(
'[Video info]: YouTube returned an error for '
+
'video with id "'
+
id
+
'".'
);
// When the youtube link doesn't work for any reason
// (for example, the great firewall in china) any
// alternate sources should automatically play.
if
(
!
_prepareHTML5Video
(
self
))
{
console
.
log
(
'[Video info]: Continue loading '
+
'YouTube video.'
);
// Non-YouTube sources were not found either.
el
.
find
(
'.video-player div'
)
.
removeClass
(
'hidden'
);
el
.
find
(
'.video-player h3'
)
.
addClass
(
'hidden'
);
// If in reality the timeout was to short, try to
// continue loading the YouTube video anyways.
self
.
fetchMetadata
();
self
.
parseSpeed
();
}
else
{
console
.
log
(
'[Video info]: Change player mode to HTML5.'
);
_waitForYoutubeApi
(
this
);
// In-browser HTML5 player does not support quality
// control.
el
.
find
(
'a.quality_control'
).
hide
();
}
}
else
{
console
.
log
(
'[Video info]: Start player in YouTube mode.'
);
var
scriptTag
=
document
.
createElement
(
'script'
);
self
.
fetchMetadata
();
self
.
parseSpeed
();
}
scriptTag
.
src
=
this
.
config
.
ytApiUrl
;
scriptTag
.
async
=
true
;
_setConfigurations
(
self
);
_renderElements
(
self
);
});
}
$
(
scriptTag
).
on
(
'load'
,
function
()
{
self
.
loadYoutubePlayer
();
});
$
(
scriptTag
).
on
(
'error'
,
function
()
{
console
.
log
(
'[Video info]: YouTube returned an error for '
+
'video with id "'
+
self
.
id
+
'".'
);
self
.
loadHtmlPlayer
();
});
window
.
Video
.
loadYouTubeIFrameAPI
(
scriptTag
);
}
return
__dfd__
.
promise
();
}
...
...
@@ -619,8 +647,9 @@ function (VideoPlayer, i18n) {
metadataXHRs
=
_
.
map
(
this
.
videos
,
function
(
url
,
speed
)
{
return
self
.
getVideoMetadata
(
url
,
function
(
data
)
{
if
(
data
.
data
)
{
self
.
metadata
[
data
.
data
.
id
]
=
data
.
data
;
if
(
data
.
items
.
length
>
0
)
{
var
metaDataItem
=
data
.
items
[
0
];
self
.
metadata
[
metaDataItem
.
id
]
=
metaDataItem
.
contentDetails
;
}
});
});
...
...
@@ -671,16 +700,16 @@ function (VideoPlayer, i18n) {
if
(
!
(
_
.
isString
(
url
)))
{
url
=
this
.
videos
[
'1.0'
]
||
''
;
}
return
$
.
ajax
(
{
url
:
[
document
.
location
.
protocol
,
'//'
,
this
.
config
.
ytTestUrl
,
url
,
'?v=2&alt=jsonc'
].
join
(
''
),
dataType
:
'jsonp'
,
timeout
:
this
.
config
.
ytTestTimeout
,
success
:
_
.
isFunction
(
callback
)
?
callback
:
null
}
);
// Will hit the API URL iF YT key is defined in settings.
if
(
this
.
config
.
ytKey
)
{
return
$
.
ajax
({
url
:
[
this
.
config
.
ytMetadataUrl
,
'?id='
,
url
,
'&part=contentDetails&key='
,
this
.
config
.
ytKey
].
join
(
''
)
,
timeout
:
this
.
config
.
ytTestTimeout
,
success
:
_
.
isFunction
(
callback
)
?
callback
:
null
});
}
else
{
return
$
.
Deferred
().
reject
().
promise
();
}
}
function
youtubeId
(
speed
)
{
...
...
@@ -693,7 +722,7 @@ function (VideoPlayer, i18n) {
function
getDuration
()
{
try
{
return
this
.
metadata
[
this
.
youtubeId
()].
duration
;
return
moment
.
duration
(
this
.
metadata
[
this
.
youtubeId
()].
duration
,
moment
.
ISO_8601
).
asSeconds
()
;
}
catch
(
err
)
{
return
_
.
result
(
this
.
metadata
[
this
.
youtubeId
(
'1.0'
)],
'duration'
)
||
0
;
}
...
...
common/lib/xmodule/xmodule/js/src/video/10_main.js
View file @
fc32820f
...
...
@@ -160,6 +160,8 @@
youtubeXhr
=
null
;
};
window
.
Video
.
loadYouTubeIFrameAPI
=
initialize
.
prototype
.
loadYouTubeIFrameAPI
;
// Invoke the mock Video constructor so that the elements stored within it can be processed by the real
// `window.Video` constructor.
oldVideo
(
null
,
true
);
...
...
common/lib/xmodule/xmodule/tests/test_video.py
View file @
fc32820f
...
...
@@ -736,8 +736,8 @@ class VideoDescriptorIndexingTestCase(unittest.TestCase):
# YouTube JavaScript API
'API'
:
'www.youtube.com/iframe_api'
,
# URL to
test YouTube availability
'
TEST_URL'
:
'gdata.youtube.com/feeds/api
/videos/'
,
# URL to
get YouTube metadata
'
METADATA_URL'
:
'www.googleapis.com/youtube/v3
/videos/'
,
# Current youtube api for requesting transcripts.
# For example: http://video.google.com/timedtext?lang=en&v=j_jEn79vS3g.
...
...
common/lib/xmodule/xmodule/video_module/video_module.py
View file @
fc32820f
...
...
@@ -86,6 +86,7 @@ log = logging.getLogger(__name__)
_
=
lambda
text
:
text
@XBlock.wants
(
'settings'
)
class
VideoModule
(
VideoFields
,
VideoTranscriptsMixin
,
VideoStudentViewHandlers
,
XModule
,
LicenseMixin
):
"""
XML source example:
...
...
@@ -261,6 +262,15 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
cdn_exp_group
=
None
self
.
youtube_streams
=
youtube_streams
or
create_youtube_string
(
self
)
# pylint: disable=W0201
settings_service
=
self
.
runtime
.
service
(
self
,
'settings'
)
yt_api_key
=
None
if
settings_service
:
xblock_settings
=
settings_service
.
get_settings_bucket
(
self
)
if
xblock_settings
and
'YOUTUBE_API_KEY'
in
xblock_settings
:
yt_api_key
=
xblock_settings
[
'YOUTUBE_API_KEY'
]
metadata
=
{
'saveStateUrl'
:
self
.
system
.
ajax_url
+
'/save_user_state'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
False
),
...
...
@@ -286,7 +296,9 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
'ytTestTimeout'
:
1500
,
'ytApiUrl'
:
settings
.
YOUTUBE
[
'API'
],
'ytTestUrl'
:
settings
.
YOUTUBE
[
'TEST_URL'
],
'ytMetadataUrl'
:
settings
.
YOUTUBE
[
'METADATA_URL'
],
'ytKey'
:
yt_api_key
,
'transcriptTranslationUrl'
:
self
.
runtime
.
handler_url
(
self
,
'transcript'
,
'translation/__lang__'
)
.
rstrip
(
'/?'
),
...
...
common/test/acceptance/tests/helpers.py
View file @
fc32820f
...
...
@@ -67,8 +67,7 @@ def is_youtube_available():
youtube_api_urls
=
{
'main'
:
'https://www.youtube.com/'
,
'player'
:
'http://www.youtube.com/iframe_api'
,
'metadata'
:
'http://gdata.youtube.com/feeds/api/videos/'
,
'player'
:
'https://www.youtube.com/iframe_api'
,
# For transcripts, you need to check an actual video, so we will
# just specify our default video and see if that one is available.
'transcript'
:
'http://video.google.com/timedtext?lang=en&v=3_yD_cEKoCk'
,
...
...
lms/djangoapps/courseware/features/video.feature
View file @
fc32820f
...
...
@@ -3,7 +3,7 @@ Feature: LMS.Video component
As a student, I want to view course videos in LMS
# 1
Scenario
:
Verify that each video in
each sub-section includes a transcript for
non-Youtube countries
Scenario
:
Verify that each video in
sub-section includes a transcript for Youtube and
non-Youtube countries
Given
youtube server is up and response time is 2 seconds
And
I am registered for the course
"test_course"
And
I have a
"subs_3_yD_cEKoCk.srt.sjson"
transcript file in assets
...
...
@@ -24,9 +24,9 @@ Feature: LMS.Video component
|
Welcome
to
edX.
|
|
Equal
transcripts
|
When
I open video
"C"
Then
the video has rendered in
"
HTML5
"
mode
Then
the video has rendered in
"
YOUTUBE
"
mode
And
I make sure captions are opened
And
I see
"好 各位同学"
text in the captions
When
I open video
"D"
Then
the video has rendered in
"
HTML5
"
mode
And
the video does not show the captions
Then
the video has rendered in
"
YOUTUBE
"
mode
And
I make sure captions are opened
lms/djangoapps/courseware/tests/test_video_mongo.py
View file @
fc32820f
...
...
@@ -62,8 +62,9 @@ class TestVideoYouTube(TestVideo):
"transcriptLanguage"
:
"en"
,
"transcriptLanguages"
:
OrderedDict
({
"en"
:
"English"
,
"uk"
:
u"Українська"
}),
"ytTestTimeout"
:
1500
,
"ytApiUrl"
:
"www.youtube.com/iframe_api"
,
"ytTestUrl"
:
"gdata.youtube.com/feeds/api/videos/"
,
"ytApiUrl"
:
"https://www.youtube.com/iframe_api"
,
"ytMetadataUrl"
:
"https://www.googleapis.com/youtube/v3/videos/"
,
"ytKey"
:
None
,
"transcriptTranslationUrl"
:
self
.
item_descriptor
.
xmodule_runtime
.
handler_url
(
self
.
item_descriptor
,
'transcript'
,
'translation/__lang__'
)
.
rstrip
(
'/?'
),
...
...
@@ -139,8 +140,9 @@ class TestVideoNonYouTube(TestVideo):
"transcriptLanguage"
:
"en"
,
"transcriptLanguages"
:
OrderedDict
({
"en"
:
"English"
}),
"ytTestTimeout"
:
1500
,
"ytApiUrl"
:
"www.youtube.com/iframe_api"
,
"ytTestUrl"
:
"gdata.youtube.com/feeds/api/videos/"
,
"ytApiUrl"
:
"https://www.youtube.com/iframe_api"
,
"ytMetadataUrl"
:
"https://www.googleapis.com/youtube/v3/videos/"
,
"ytKey"
:
None
,
"transcriptTranslationUrl"
:
self
.
item_descriptor
.
xmodule_runtime
.
handler_url
(
self
.
item_descriptor
,
'transcript'
,
'translation/__lang__'
)
.
rstrip
(
'/?'
),
...
...
@@ -192,8 +194,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
"transcriptLanguage"
:
"en"
,
"transcriptLanguages"
:
OrderedDict
({
"en"
:
"English"
}),
"ytTestTimeout"
:
1500
,
"ytApiUrl"
:
"www.youtube.com/iframe_api"
,
"ytTestUrl"
:
"gdata.youtube.com/feeds/api/videos/"
,
"ytApiUrl"
:
"https://www.youtube.com/iframe_api"
,
"ytMetadataUrl"
:
"https://www.googleapis.com/youtube/v3/videos/"
,
"ytKey"
:
None
,
"transcriptTranslationUrl"
:
self
.
item_descriptor
.
xmodule_runtime
.
handler_url
(
self
.
item_descriptor
,
'transcript'
,
'translation/__lang__'
)
.
rstrip
(
'/?'
),
...
...
@@ -1181,8 +1184,9 @@ class TestVideoWithBumper(TestVideo):
"transcriptLanguage"
:
"en"
,
"transcriptLanguages"
:
OrderedDict
({
"en"
:
"English"
,
"uk"
:
u"Українська"
}),
"ytTestTimeout"
:
1500
,
"ytApiUrl"
:
"www.youtube.com/iframe_api"
,
"ytTestUrl"
:
"gdata.youtube.com/feeds/api/videos/"
,
"ytApiUrl"
:
"https://www.youtube.com/iframe_api"
,
"ytMetadataUrl"
:
"https://www.googleapis.com/youtube/v3/videos/"
,
"ytKey"
:
None
,
"transcriptTranslationUrl"
:
self
.
item_descriptor
.
xmodule_runtime
.
handler_url
(
self
.
item_descriptor
,
'transcript'
,
'translation/__lang__'
)
.
rstrip
(
'/?'
),
...
...
lms/envs/acceptance.py
View file @
fc32820f
...
...
@@ -175,8 +175,8 @@ XQUEUE_INTERFACE = {
}
# Point the URL used to test YouTube availability to our stub YouTube server
YOUTUBE
[
'API'
]
=
"127.0.0.1:{0}/get_youtube_api/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'
TEST_URL'
]
=
"
127.0.0.1:{0}/test_youtube/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'API'
]
=
"
http://
127.0.0.1:{0}/get_youtube_api/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'
METADATA_URL'
]
=
"http://
127.0.0.1:{0}/test_youtube/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'TEXT_API'
][
'url'
]
=
"127.0.0.1:{0}/test_transcripts_youtube/"
.
format
(
YOUTUBE_PORT
)
if
FEATURES
.
get
(
'ENABLE_COURSEWARE_SEARCH'
)
or
\
...
...
lms/envs/aws.py
View file @
fc32820f
...
...
@@ -625,6 +625,7 @@ FACEBOOK_APP_ID = AUTH_TOKENS.get("FACEBOOK_APP_ID")
XBLOCK_SETTINGS
=
ENV_TOKENS
.
get
(
'XBLOCK_SETTINGS'
,
{})
XBLOCK_SETTINGS
.
setdefault
(
"VideoDescriptor"
,
{})[
"licensing_enabled"
]
=
FEATURES
.
get
(
"LICENSING"
,
False
)
XBLOCK_SETTINGS
.
setdefault
(
"VideoModule"
,
{})[
'YOUTUBE_API_KEY'
]
=
AUTH_TOKENS
.
get
(
'YOUTUBE_API_KEY'
,
YOUTUBE_API_KEY
)
##### CDN EXPERIMENT/MONITORING FLAGS #####
CDN_VIDEO_URLS
=
ENV_TOKENS
.
get
(
'CDN_VIDEO_URLS'
,
CDN_VIDEO_URLS
)
...
...
lms/envs/bok_choy.py
View file @
fc32820f
...
...
@@ -115,8 +115,8 @@ FEATURES['ENTRANCE_EXAMS'] = True
# Point the URL used to test YouTube availability to our stub YouTube server
YOUTUBE_PORT
=
9080
YOUTUBE
[
'API'
]
=
"127.0.0.1:{0}/get_youtube_api/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'
TEST_URL'
]
=
"
127.0.0.1:{0}/test_youtube/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'API'
]
=
"
http://
127.0.0.1:{0}/get_youtube_api/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'
METADATA_URL'
]
=
"http://
127.0.0.1:{0}/test_youtube/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'TEXT_API'
][
'url'
]
=
"127.0.0.1:{0}/test_transcripts_youtube/"
.
format
(
YOUTUBE_PORT
)
############################# SECURITY SETTINGS ################################
...
...
lms/envs/common.py
View file @
fc32820f
...
...
@@ -1737,10 +1737,10 @@ EMAIL_OPTIN_MINIMUM_AGE = PARENTAL_CONSENT_AGE_LIMIT
YOUTUBE
=
{
# YouTube JavaScript API
'API'
:
'www.youtube.com/iframe_api'
,
'API'
:
'
https://
www.youtube.com/iframe_api'
,
# URL to
test YouTube availability
'
TEST_URL'
:
'gdata.youtube.com/feeds/api
/videos/'
,
# URL to
get YouTube metadata
'
METADATA_URL'
:
'https://www.googleapis.com/youtube/v3
/videos/'
,
# Current youtube api for requesting transcripts.
# For example: http://video.google.com/timedtext?lang=en&v=j_jEn79vS3g.
...
...
@@ -1754,6 +1754,7 @@ YOUTUBE = {
'IMAGE_API'
:
'http://img.youtube.com/vi/{youtube_id}/0.jpg'
,
# /maxresdefault.jpg for 1920*1080
}
YOUTUBE_API_KEY
=
None
################################### APPS ######################################
INSTALLED_APPS
=
(
...
...
lms/static/js_test.yml
View file @
fc32820f
...
...
@@ -61,6 +61,7 @@ lib_paths:
-
xmodule_js/common_static/js/vendor/edxnotes/annotator-full.min.js
-
xmodule_js/common_static/js/test/i18n.js
-
xmodule_js/common_static/js/vendor/date.js
-
xmodule_js/common_static/js/vendor/moment.min.js
# Paths to source JavaScript files
src_paths
:
...
...
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