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
cafbe74d
Commit
cafbe74d
authored
Jun 23, 2015
by
muzaffaryousaf
Committed by
David Baumgold
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
57b9d8dc
Hide whitespace changes
Inline
Side-by-side
Showing
28 changed files
with
245 additions
and
163 deletions
+245
-163
cms/envs/acceptance.py
+2
-2
cms/envs/aws.py
+3
-0
cms/envs/bok_choy.py
+2
-2
cms/envs/common.py
+12
-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_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
+111
-86
common/lib/xmodule/xmodule/js/src/video/10_main.js
+2
-0
common/lib/xmodule/xmodule/tests/test_video.py
+1
-0
common/lib/xmodule/xmodule/video_module/video_module.py
+12
-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
+22
-14
lms/envs/acceptance.py
+2
-2
lms/envs/aws.py
+3
-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 @
cafbe74d
...
@@ -118,6 +118,6 @@ except ImportError:
...
@@ -118,6 +118,6 @@ except ImportError:
pass
pass
# Point the URL used to test YouTube availability to our stub YouTube server
# 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
[
'API'
]
=
"
http://
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
[
'
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
)
YOUTUBE
[
'TEXT_API'
][
'url'
]
=
"127.0.0.1:{0}/test_transcripts_youtube/"
.
format
(
YOUTUBE_PORT
)
cms/envs/aws.py
View file @
cafbe74d
...
@@ -312,3 +312,6 @@ VIDEO_UPLOAD_PIPELINE = ENV_TOKENS.get('VIDEO_UPLOAD_PIPELINE', VIDEO_UPLOAD_PIP
...
@@ -312,3 +312,6 @@ VIDEO_UPLOAD_PIPELINE = ENV_TOKENS.get('VIDEO_UPLOAD_PIPELINE', VIDEO_UPLOAD_PIP
#date format the api will be formatting the datetime values
#date format the api will be formatting the datetime values
API_DATE_FORMAT
=
'
%
Y-
%
m-
%
d'
API_DATE_FORMAT
=
'
%
Y-
%
m-
%
d'
API_DATE_FORMAT
=
ENV_TOKENS
.
get
(
'API_DATE_FORMAT'
,
API_DATE_FORMAT
)
API_DATE_FORMAT
=
ENV_TOKENS
.
get
(
'API_DATE_FORMAT'
,
API_DATE_FORMAT
)
XBLOCK_SETTINGS
=
ENV_TOKENS
.
get
(
'XBLOCK_SETTINGS'
,
{})
XBLOCK_SETTINGS
.
setdefault
(
"VideoModule"
,
{})[
'YOUTUBE_API_KEY'
]
=
AUTH_TOKENS
.
get
(
'YOUTUBE_API_KEY'
,
YOUTUBE_API_KEY
)
cms/envs/bok_choy.py
View file @
cafbe74d
...
@@ -73,8 +73,8 @@ DEBUG = True
...
@@ -73,8 +73,8 @@ DEBUG = True
# Point the URL used to test YouTube availability to our stub YouTube server
# Point the URL used to test YouTube availability to our stub YouTube server
YOUTUBE_PORT
=
9080
YOUTUBE_PORT
=
9080
YOUTUBE
[
'API'
]
=
"127.0.0.1:{0}/get_youtube_api/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'API'
]
=
"
http://
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
[
'
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
)
YOUTUBE
[
'TEXT_API'
][
'url'
]
=
"127.0.0.1:{0}/test_transcripts_youtube/"
.
format
(
YOUTUBE_PORT
)
#####################################################################
#####################################################################
...
...
cms/envs/common.py
View file @
cafbe74d
...
@@ -36,7 +36,8 @@ import lms.envs.common
...
@@ -36,7 +36,8 @@ import lms.envs.common
# Although this module itself may not use these imported variables, other dependent modules may.
# Although this module itself may not use these imported variables, other dependent modules may.
from
lms.envs.common
import
(
from
lms.envs.common
import
(
USE_TZ
,
TECH_SUPPORT_EMAIL
,
PLATFORM_NAME
,
BUGS_EMAIL
,
DOC_STORE_CONFIG
,
ALL_LANGUAGES
,
WIKI_ENABLED
,
MODULESTORE
,
USE_TZ
,
TECH_SUPPORT_EMAIL
,
PLATFORM_NAME
,
BUGS_EMAIL
,
DOC_STORE_CONFIG
,
ALL_LANGUAGES
,
WIKI_ENABLED
,
MODULESTORE
,
update_module_store_settings
,
ASSET_IGNORE_REGEX
,
COPYRIGHT_YEAR
update_module_store_settings
,
ASSET_IGNORE_REGEX
,
COPYRIGHT_YEAR
,
YOUTUBE_API_KEY
,
)
)
from
path
import
path
from
path
import
path
from
warnings
import
simplefilter
from
warnings
import
simplefilter
...
@@ -594,10 +595,10 @@ CELERY_QUEUES = {
...
@@ -594,10 +595,10 @@ CELERY_QUEUES = {
YOUTUBE
=
{
YOUTUBE
=
{
# YouTube JavaScript API
# YouTube JavaScript API
'API'
:
'www.youtube.com/iframe_api'
,
'API'
:
'
https://
www.youtube.com/iframe_api'
,
# URL to
test YouTube availability
# URL to
get YouTube metadata
'
TEST_URL'
:
'gdata.youtube.com/feeds/api/videos/
'
,
'
METADATA_URL'
:
'https://www.googleapis.com/youtube/v3/videos
'
,
# Current youtube api for requesting transcripts.
# Current youtube api for requesting transcripts.
# For example: http://video.google.com/timedtext?lang=en&v=j_jEn79vS3g.
# For example: http://video.google.com/timedtext?lang=en&v=j_jEn79vS3g.
...
@@ -868,3 +869,10 @@ FILES_AND_UPLOAD_TYPE_FILTERS = {
...
@@ -868,3 +869,10 @@ FILES_AND_UPLOAD_TYPE_FILTERS = {
'application/vnd.ms-powerpoint'
,
'application/vnd.ms-powerpoint'
,
],
],
}
}
XBLOCK_SETTINGS
=
{
'VideoModule'
:
{
'YOUTUBE_API_KEY'
:
YOUTUBE_API_KEY
}
}
cms/templates/ux/reference/container.html
View file @
cafbe74d
...
@@ -137,7 +137,7 @@
...
@@ -137,7 +137,7 @@
<h2>
Video
</h2>
<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=
"focus_grabber first"
tabindex=
"-1"
></div>
<div
class=
"tc-wrapper"
>
<div
class=
"tc-wrapper"
>
...
...
common/djangoapps/terrain/setup_prereqs.py
View file @
cafbe74d
...
@@ -28,8 +28,7 @@ SERVICES = {
...
@@ -28,8 +28,7 @@ SERVICES = {
YOUTUBE_API_URLS
=
{
YOUTUBE_API_URLS
=
{
'main'
:
'https://www.youtube.com/'
,
'main'
:
'https://www.youtube.com/'
,
'player'
:
'http://www.youtube.com/iframe_api'
,
'player'
:
'https://www.youtube.com/iframe_api'
,
'metadata'
:
'http://gdata.youtube.com/feeds/api/videos/'
,
# For transcripts, you need to check an actual video, so we will
# For transcripts, you need to check an actual video, so we will
# just specify our default video and see if that one is available.
# just specify our default video and see if that one is available.
'transcript'
:
'http://video.google.com/timedtext?lang=en&v=OEoXaMPEzfM'
,
'transcript'
:
'http://video.google.com/timedtext?lang=en&v=OEoXaMPEzfM'
,
...
...
common/djangoapps/terrain/stubs/youtube.py
View file @
cafbe74d
...
@@ -95,6 +95,9 @@ class StubYouTubeHandler(StubHttpRequestHandler):
...
@@ -95,6 +95,9 @@ class StubYouTubeHandler(StubHttpRequestHandler):
if
self
.
server
.
config
.
get
(
'youtube_api_blocked'
):
if
self
.
server
.
config
.
get
(
'youtube_api_blocked'
):
self
.
send_response
(
404
,
content
=
''
,
headers
=
{
'Content-type'
:
'text/plain'
})
self
.
send_response
(
404
,
content
=
''
,
headers
=
{
'Content-type'
:
'text/plain'
})
else
:
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.
# Get the response to send from YouTube.
# We need to do this every time because Google sometimes sends different responses
# 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"
# as part of their own experiments, which has caused our tests to become "flaky"
...
@@ -117,17 +120,16 @@ class StubYouTubeHandler(StubHttpRequestHandler):
...
@@ -117,17 +120,16 @@ class StubYouTubeHandler(StubHttpRequestHandler):
# Construct the response content
# Construct the response content
callback
=
self
.
get_params
[
'callback'
]
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
({
'data'
:
OrderedDict
({
'items'
:
list
(
'id'
:
youtube_id
,
OrderedDict
({
'message'
:
message
,
'contentDetails'
:
OrderedDict
({
'duration'
:
youtube_metadata
[
'data'
][
'duration'
],
'id'
:
youtube_id
,
})
'duration'
:
'PT2M20S'
,
})
})
)
})
})
response
=
"{cb}({data})"
.
format
(
cb
=
callback
,
data
=
json
.
dumps
(
data
))
response
=
"{cb}({data})"
.
format
(
cb
=
callback
,
data
=
json
.
dumps
(
data
))
...
...
common/lib/xmodule/xmodule/js/fixtures/video.html
View file @
cafbe74d
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
data-autoplay=
"False"
data-autoplay=
"False"
data-yt-test-timeout=
"1500"
data-yt-test-timeout=
"1500"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-test-url=
"
gdata.youtube.com/feeds/api
/videos/"
data-yt-test-url=
"
www.googleapis.com/youtube/v3
/videos/"
data-autohide-html5=
"True"
data-autohide-html5=
"True"
>
>
<div
class=
"focus_grabber first"
></div>
<div
class=
"focus_grabber first"
></div>
...
...
common/lib/xmodule/xmodule/js/fixtures/video_all.html
View file @
cafbe74d
...
@@ -19,7 +19,7 @@
...
@@ -19,7 +19,7 @@
data-autoplay=
"False"
data-autoplay=
"False"
data-yt-test-timeout=
"1500"
data-yt-test-timeout=
"1500"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-test-url=
"
gdata.youtube.com/feeds/api
/videos/"
data-yt-test-url=
"
www.googleapis.com/youtube/v3
/videos/"
data-autohide-html5=
"True"
data-autohide-html5=
"True"
>
>
<div
class=
"focus_grabber first"
></div>
<div
class=
"focus_grabber first"
></div>
...
...
common/lib/xmodule/xmodule/js/fixtures/video_html5.html
View file @
cafbe74d
...
@@ -19,7 +19,7 @@
...
@@ -19,7 +19,7 @@
data-autoplay=
"False"
data-autoplay=
"False"
data-yt-test-timeout=
"1500"
data-yt-test-timeout=
"1500"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-test-url=
"
gdata.youtube.com/feeds/api
/videos/"
data-yt-test-url=
"
www.googleapis.com/youtube/v3
/videos/"
data-autohide-html5=
"True"
data-autohide-html5=
"True"
>
>
<div
class=
"focus_grabber first"
></div>
<div
class=
"focus_grabber first"
></div>
...
...
common/lib/xmodule/xmodule/js/fixtures/video_no_captions.html
View file @
cafbe74d
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
data-autoplay=
"False"
data-autoplay=
"False"
data-yt-test-timeout=
"1500"
data-yt-test-timeout=
"1500"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-test-url=
"
gdata.youtube.com/feeds/api
/videos/"
data-yt-test-url=
"
www.googleapis.com/youtube/v3
/videos/"
data-autohide-html5=
"True"
data-autohide-html5=
"True"
>
>
<div
class=
"focus_grabber first"
></div>
<div
class=
"focus_grabber first"
></div>
...
...
common/lib/xmodule/xmodule/js/fixtures/video_yt_multiple.html
View file @
cafbe74d
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
data-autoplay=
"False"
data-autoplay=
"False"
data-yt-test-timeout=
"1500"
data-yt-test-timeout=
"1500"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-api-url=
"www.youtube.com/iframe_api"
data-yt-test-url=
"
gdata.youtube.com/feeds/api
/videos/"
data-yt-test-url=
"
www.googleapis.com/youtube/v3
/videos/"
data-autohide-html5=
"True"
data-autohide-html5=
"True"
>
>
<div
class=
"focus_grabber first"
></div>
<div
class=
"focus_grabber first"
></div>
...
@@ -88,7 +88,7 @@
...
@@ -88,7 +88,7 @@
data-transcript-available-translations-url=
"/transcript/available_translations"
data-transcript-available-translations-url=
"/transcript/available_translations"
data-autoplay=
"False"
data-autoplay=
"False"
data-yt-test-timeout=
"1500"
data-yt-test-timeout=
"1500"
data-yt-test-url=
"
https://gdata.youtube.com/feeds/api
/videos/"
data-yt-test-url=
"
www.googleapis.com/youtube/v3
/videos/"
data-autohide-html5=
"True"
data-autohide-html5=
"True"
>
>
...
@@ -153,7 +153,7 @@
...
@@ -153,7 +153,7 @@
data-transcript-available-translations-url=
"/transcript/available_translations"
data-transcript-available-translations-url=
"/transcript/available_translations"
data-autoplay=
"False"
data-autoplay=
"False"
data-yt-test-timeout=
"1500"
data-yt-test-timeout=
"1500"
data-yt-test-url=
"
https://gdata.youtube.com/feeds/api
/videos/"
data-yt-test-url=
"
www.googleapis.com/youtube/v3
/videos/"
data-autohide-html5=
"True"
data-autohide-html5=
"True"
>
>
...
...
common/lib/xmodule/xmodule/js/js_test.yml
View file @
cafbe74d
...
@@ -59,6 +59,7 @@ lib_paths:
...
@@ -59,6 +59,7 @@ lib_paths:
-
common_static/js/src/utility.js
-
common_static/js/src/utility.js
-
public/js/split_test_staff.js
-
public/js/split_test_staff.js
-
common_static/js/src/accessibility_tools.js
-
common_static/js/src/accessibility_tools.js
-
common_static/js/vendor/moment.min.js
# Paths to spec (test) JavaScript files
# Paths to spec (test) JavaScript files
spec_paths
:
spec_paths
:
...
...
common/lib/xmodule/xmodule/js/spec/helper.js
View file @
cafbe74d
...
@@ -36,7 +36,7 @@
...
@@ -36,7 +36,7 @@
return
f
();
return
f
();
}
}
};
};
jasmine
.
YT
=
stubbedYT
;
// Stub YouTube API.
// Stub YouTube API.
window
.
YT
=
stubbedYT
;
window
.
YT
=
stubbedYT
;
...
@@ -76,19 +76,27 @@
...
@@ -76,19 +76,27 @@
jasmine
.
stubbedMetadata
=
{
jasmine
.
stubbedMetadata
=
{
'7tqY6eQzVhE'
:
{
'7tqY6eQzVhE'
:
{
id
:
'7tqY6eQzVhE'
,
contentDetails
:
{
duration
:
300
id
:
'7tqY6eQzVhE'
,
duration
:
'PT5M0S'
}
},
},
'cogebirgzzM'
:
{
'cogebirgzzM'
:
{
id
:
'cogebirgzzM'
,
contentDetails
:
{
duration
:
200
id
:
'cogebirgzzM'
,
duration
:
'PT3M20S'
}
},
},
'abcdefghijkl'
:
{
'abcdefghijkl'
:
{
id
:
'abcdefghijkl'
,
contentDetails
:
{
duration
:
400
id
:
'abcdefghijkl'
,
duration
:
'PT6M40S'
}
},
},
bogus
:
{
bogus
:
{
duration
:
100
contentDetails
:
{
duration
:
'PT1M40S'
}
}
}
};
};
...
@@ -122,7 +130,7 @@
...
@@ -122,7 +130,7 @@
}
}
return
spy
.
andCallFake
(
function
(
settings
)
{
return
spy
.
andCallFake
(
function
(
settings
)
{
var
match
=
settings
.
url
var
match
=
settings
.
url
.
match
(
/
youtube
\.
com
\/
.+
\/
videos
\/(
.+
)\?
v=2&alt=jsonc
/
),
.
match
(
/
googleapis
\.
com
\/
.+
\/
videos
\/\?
id=
(
.+
)
&part=contentDetails
/
),
status
,
callCallback
;
status
,
callCallback
;
if
(
match
)
{
if
(
match
)
{
status
=
match
[
1
].
split
(
'_'
);
status
=
match
[
1
].
split
(
'_'
);
...
@@ -138,7 +146,7 @@
...
@@ -138,7 +146,7 @@
};
};
}
else
if
(
settings
.
success
)
{
}
else
if
(
settings
.
success
)
{
return
settings
.
success
({
return
settings
.
success
({
data
:
jasmine
.
stubbedMetadata
[
match
[
1
]]
items
:
jasmine
.
stubbedMetadata
[
match
[
1
]]
});
});
}
else
{
}
else
{
return
{
return
{
...
@@ -168,15 +176,6 @@
...
@@ -168,15 +176,6 @@
return
;
return
;
}
else
if
(
settings
.
url
===
'/save_user_state'
)
{
}
else
if
(
settings
.
url
===
'/save_user_state'
)
{
return
{
success
:
true
};
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
{
}
else
{
throw
'External request attempted for '
+
throw
'External request attempted for '
+
settings
.
url
+
settings
.
url
+
...
@@ -221,6 +220,19 @@
...
@@ -221,6 +220,19 @@
// Stub jQuery.scrollTo module.
// Stub jQuery.scrollTo module.
$
.
fn
.
scrollTo
=
jasmine
.
createSpy
(
'jQuery.scrollTo'
);
$
.
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
)
{
jasmine
.
initializePlayer
=
function
(
fixture
,
params
)
{
var
state
;
var
state
;
...
...
common/lib/xmodule/xmodule/js/spec/video/general_spec.js
View file @
cafbe74d
...
@@ -122,6 +122,12 @@
...
@@ -122,6 +122,12 @@
return
state
.
youtubeApiAvailable
===
true
;
return
state
.
youtubeApiAvailable
===
true
;
},
'YouTube API is loaded'
,
3000
);
},
'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
()
{
runs
(
function
()
{
// If YouTube API is not loaded, then the code will should create
// 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.
// 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 @
cafbe74d
...
@@ -223,10 +223,10 @@ function (Initialize) {
...
@@ -223,10 +223,10 @@ function (Initialize) {
speed
:
'1.50'
,
speed
:
'1.50'
,
metadata
:
{
metadata
:
{
'testId'
:
{
'testId'
:
{
duration
:
400
duration
:
'PT6M40S'
},
},
'videoId'
:
{
'videoId'
:
{
duration
:
100
duration
:
'PT1M40S'
}
}
},
},
videos
:
{
videos
:
{
...
...
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
View file @
cafbe74d
...
@@ -16,6 +16,8 @@ define(
...
@@ -16,6 +16,8 @@ define(
'video/01_initialize.js'
,
'video/01_initialize.js'
,
[
'video/03_video_player.js'
,
'video/00_video_storage.js'
,
'video/00_i18n.js'
],
[
'video/03_video_player.js'
,
'video/00_video_storage.js'
,
'video/00_i18n.js'
],
function
(
VideoPlayer
,
VideoStorage
,
i18n
)
{
function
(
VideoPlayer
,
VideoStorage
,
i18n
)
{
var
moment
=
window
.
moment
;
/**
/**
* @function
* @function
*
*
...
@@ -31,6 +33,9 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -31,6 +33,9 @@ function (VideoPlayer, VideoStorage, i18n) {
state
.
initialize
(
element
)
state
.
initialize
(
element
)
.
done
(
function
()
{
.
done
(
function
()
{
if
(
state
.
isYoutubeType
())
{
state
.
parseSpeed
();
}
// On iPhones and iPods native controls are used.
// On iPhones and iPods native controls are used.
if
(
/iP
(
hone|od
)
/i
.
test
(
state
.
isTouch
[
0
]))
{
if
(
/iP
(
hone|od
)
/i
.
test
(
state
.
isTouch
[
0
]))
{
_hideWaitPlaceholder
(
state
);
_hideWaitPlaceholder
(
state
);
...
@@ -76,7 +81,10 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -76,7 +81,10 @@ function (VideoPlayer, VideoStorage, i18n) {
setSpeed
:
setSpeed
,
setSpeed
:
setSpeed
,
speedToString
:
speedToString
,
speedToString
:
speedToString
,
trigger
:
trigger
,
trigger
:
trigger
,
youtubeId
:
youtubeId
youtubeId
:
youtubeId
,
loadHtmlPlayer
:
loadHtmlPlayer
,
loadYoutubePlayer
:
loadYoutubePlayer
,
loadYouTubeIFrameAPI
:
loadYouTubeIFrameAPI
},
},
_youtubeApiDeferred
=
null
,
_youtubeApiDeferred
=
null
,
...
@@ -127,6 +135,9 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -127,6 +135,9 @@ function (VideoPlayer, VideoStorage, i18n) {
onYTApiReady
=
function
()
{
onYTApiReady
=
function
()
{
console
.
log
(
'[Video info]: YouTube API is available and is loaded.'
);
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
);
video
=
VideoPlayer
(
state
);
state
.
modules
.
push
(
video
);
state
.
modules
.
push
(
video
);
...
@@ -179,7 +190,6 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -179,7 +190,6 @@ function (VideoPlayer, VideoStorage, i18n) {
if
(
!
_youtubeApiDeferred
)
{
if
(
!
_youtubeApiDeferred
)
{
_youtubeApiDeferred
=
$
.
Deferred
();
_youtubeApiDeferred
=
$
.
Deferred
();
setupOnYouTubeIframeAPIReady
();
setupOnYouTubeIframeAPIReady
();
_loadYoutubeApi
(
state
);
}
else
if
(
!
window
.
onYouTubeIframeAPIReady
||
!
window
.
onYouTubeIframeAPIReady
.
done
)
{
}
else
if
(
!
window
.
onYouTubeIframeAPIReady
||
!
window
.
onYouTubeIframeAPIReady
.
done
)
{
// The Deferred object could have been already defined in a previous
// The Deferred object could have been already defined in a previous
// initialization of the video module. However, since then the global variable
// initialization of the video module. However, since then the global variable
...
@@ -201,21 +211,32 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -201,21 +211,32 @@ function (VideoPlayer, VideoStorage, i18n) {
state
.
modules
.
push
(
video
);
state
.
modules
.
push
(
video
);
state
.
__dfd__
.
resolve
();
state
.
__dfd__
.
resolve
();
state
.
htmlPlayerLoaded
=
true
;
}
}
}
}
function
_
load
YoutubeApi
(
state
)
{
function
_
waitFor
YoutubeApi
(
state
)
{
console
.
log
(
'[Video info]:
YouTube API is not loaded. Will try to load..
.'
);
console
.
log
(
'[Video info]:
Starting to wait for YouTube API to load
.'
);
window
.
setTimeout
(
function
()
{
window
.
setTimeout
(
function
()
{
// If YouTube API will load OK, it will run `onYouTubeIframeAPIReady`
// If YouTube API will load OK, it will run `onYouTubeIframeAPIReady`
// callback, which will set `state.youtubeApiAvailable` to `true`.
// callback, which will set `state.youtubeApiAvailable` to `true`.
// If something goes wrong at this stage, `state.youtubeApiAvailable` is
// If something goes wrong at this stage, `state.youtubeApiAvailable` is
// `false`.
// `false`.
_reportToServer
(
state
,
state
.
youtubeApiAvailable
);
if
(
!
state
.
youtubeApiAvailable
)
{
console
.
log
(
'[Video info]: YouTube API is not available.'
);
if
(
!
state
.
htmlPlayerLoaded
)
{
state
.
loadHtmlPlayer
();
}
}
state
.
el
.
trigger
(
'youtube_availability'
,
[
state
.
youtubeApiAvailable
]);
},
state
.
config
.
ytTestTimeout
);
},
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
_reportToServer
(
state
,
youtubeIsAvailable
)
{
function
_reportToServer
(
state
,
youtubeIsAvailable
)
{
...
@@ -458,6 +479,50 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -458,6 +479,50 @@ function (VideoPlayer, VideoStorage, 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)
// function initialize(element)
// The function set initial configuration and preparation.
// The function set initial configuration and preparation.
...
@@ -494,7 +559,7 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -494,7 +559,7 @@ function (VideoPlayer, VideoStorage, i18n) {
// jQuery .data() return object with keys in lower camelCase format.
// jQuery .data() return object with keys in lower camelCase format.
this
.
config
=
$
.
extend
({},
_getConfiguration
(
el
.
data
(),
storage
),
{
this
.
config
=
$
.
extend
({},
_getConfiguration
(
el
.
data
(),
storage
),
{
element
:
element
,
element
:
element
,
fadeOutTimeout
:
1400
,
fadeOutTimeout
:
1400
,
captionsFreezeTime
:
10000
,
captionsFreezeTime
:
10000
,
mode
:
$
.
cookie
(
'edX_video_player_mode'
),
mode
:
$
.
cookie
(
'edX_video_player_mode'
),
// Available HD qualities will only be accessible once the video has
// Available HD qualities will only be accessible once the video has
...
@@ -510,6 +575,9 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -510,6 +575,9 @@ function (VideoPlayer, VideoStorage, i18n) {
this
.
speed
=
this
.
speedToString
(
this
.
speed
=
this
.
speedToString
(
this
.
config
.
speed
||
this
.
config
.
generalSpeed
this
.
config
.
speed
||
this
.
config
.
generalSpeed
);
);
this
.
htmlPlayerLoaded
=
false
;
_setConfigurations
(
this
);
if
(
!
(
_parseYouTubeIDs
(
this
)))
{
if
(
!
(
_parseYouTubeIDs
(
this
)))
{
...
@@ -522,70 +590,30 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -522,70 +590,30 @@ function (VideoPlayer, VideoStorage, i18n) {
}
}
console
.
log
(
'[Video info]: Start player in HTML5 mode.'
);
console
.
log
(
'[Video info]: Start player in HTML5 mode.'
);
_setConfigurations
(
this
);
_renderElements
(
this
);
_renderElements
(
this
);
}
else
{
}
else
{
if
(
!
this
.
youtubeXhr
)
{
_renderElements
(
this
);
this
.
youtubeXhr
=
this
.
getVideoMetadata
();
}
this
.
youtubeXhr
_waitForYoutubeApi
(
this
);
.
always
(
function
(
json
,
status
)
{
var
err
=
$
.
isPlainObject
(
json
.
error
)
||
(
status
!==
'success'
&&
status
!==
'notmodified'
);
if
(
err
)
{
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.'
);
// In-browser HTML5 player does not support quality
var
scriptTag
=
document
.
createElement
(
'script'
);
// control.
el
.
find
(
'a.quality_control'
).
hide
();
}
}
else
{
console
.
log
(
'[Video info]: Start player in YouTube mode.'
);
self
.
fetchMetadata
();
scriptTag
.
src
=
this
.
config
.
ytApiUrl
;
self
.
parseSpeed
();
scriptTag
.
async
=
true
;
}
_setConfigurations
(
self
);
$
(
scriptTag
).
on
(
'load'
,
function
()
{
_renderElements
(
self
);
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
();
return
__dfd__
.
promise
();
}
}
...
@@ -633,23 +661,22 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -633,23 +661,22 @@ function (VideoPlayer, VideoStorage, i18n) {
// example the length of the video can be determined from the meta
// example the length of the video can be determined from the meta
// data.
// data.
function
fetchMetadata
()
{
function
fetchMetadata
()
{
var
_this
=
this
,
var
self
=
this
,
metadataXHRs
=
[];
metadataXHRs
=
[];
this
.
metadata
=
{};
this
.
metadata
=
{};
$
.
each
(
this
.
videos
,
function
(
speed
,
url
)
{
metadataXHRs
=
_
.
map
(
this
.
videos
,
function
(
url
,
speed
)
{
var
xhr
=
_this
.
getVideoMetadata
(
url
,
function
(
data
)
{
return
self
.
getVideoMetadata
(
url
,
function
(
data
)
{
if
(
data
.
data
)
{
if
(
data
.
items
.
length
>
0
)
{
_this
.
metadata
[
data
.
data
.
id
]
=
data
.
data
;
var
metaDataItem
=
data
.
items
[
0
];
self
.
metadata
[
metaDataItem
.
id
]
=
metaDataItem
.
contentDetails
;
}
}
});
});
metadataXHRs
.
push
(
xhr
);
});
});
$
.
when
.
apply
(
this
,
metadataXHRs
).
done
(
function
()
{
$
.
when
.
apply
(
this
,
metadataXHRs
).
done
(
function
()
{
_this
.
el
.
trigger
(
'metadata_received'
);
self
.
el
.
trigger
(
'metadata_received'
);
// Not only do we trigger the "metadata_received" event, we also
// Not only do we trigger the "metadata_received" event, we also
// set a flag to notify that metadata has been received. This
// set a flag to notify that metadata has been received. This
...
@@ -657,7 +684,7 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -657,7 +684,7 @@ function (VideoPlayer, VideoStorage, i18n) {
// to know that metadata has been received. This is important in
// to know that metadata has been received. This is important in
// cases when some code will subscribe to the "metadata_received"
// cases when some code will subscribe to the "metadata_received"
// event after it has been triggered.
// event after it has been triggered.
_this
.
youtubeMetadataReceived
=
true
;
self
.
youtubeMetadataReceived
=
true
;
});
});
}
}
...
@@ -703,18 +730,16 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -703,18 +730,16 @@ function (VideoPlayer, VideoStorage, i18n) {
if
(
typeof
url
!==
'string'
)
{
if
(
typeof
url
!==
'string'
)
{
url
=
this
.
videos
[
'1.0'
]
||
''
;
url
=
this
.
videos
[
'1.0'
]
||
''
;
}
}
successHandler
=
(
$
.
isFunction
(
callback
))
?
callback
:
null
;
// Will hit the API URL iF YT key is defined in settings.
xhr
=
$
.
ajax
({
if
(
this
.
config
.
ytKey
)
{
url
:
[
return
$
.
ajax
({
document
.
location
.
protocol
,
'//'
,
this
.
config
.
ytTestUrl
,
url
,
url
:
[
this
.
config
.
ytTestUrl
,
'?id='
,
url
,
'&part=contentDetails&key='
,
this
.
config
.
ytKey
].
join
(
''
),
'?v=2&alt=jsonc'
timeout
:
this
.
config
.
ytTestTimeout
,
].
join
(
''
),
success
:
_
.
isFunction
(
callback
)
?
callback
:
null
dataType
:
'jsonp'
,
});
timeout
:
this
.
config
.
ytTestTimeout
,
}
else
{
success
:
successHandler
return
$
.
Deferred
().
reject
().
promise
();
});
}
return
xhr
;
}
}
function
saveState
(
async
,
data
)
{
function
saveState
(
async
,
data
)
{
...
@@ -754,7 +779,7 @@ function (VideoPlayer, VideoStorage, i18n) {
...
@@ -754,7 +779,7 @@ function (VideoPlayer, VideoStorage, i18n) {
function
getDuration
()
{
function
getDuration
()
{
try
{
try
{
return
this
.
metadata
[
this
.
youtubeId
()].
duration
;
return
moment
.
duration
(
this
.
metadata
[
this
.
youtubeId
()].
duration
,
moment
.
ISO_8601
).
asSeconds
()
;
}
catch
(
err
)
{
}
catch
(
err
)
{
return
_
.
result
(
this
.
metadata
[
this
.
youtubeId
(
'1.0'
)],
'duration'
)
||
0
;
return
_
.
result
(
this
.
metadata
[
this
.
youtubeId
(
'1.0'
)],
'duration'
)
||
0
;
}
}
...
...
common/lib/xmodule/xmodule/js/src/video/10_main.js
View file @
cafbe74d
...
@@ -113,6 +113,8 @@
...
@@ -113,6 +113,8 @@
youtubeXhr
=
null
;
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
// Invoke the mock Video constructor so that the elements stored within it can be processed by the real
// `window.Video` constructor.
// `window.Video` constructor.
oldVideo
(
null
,
true
);
oldVideo
(
null
,
true
);
...
...
common/lib/xmodule/xmodule/tests/test_video.py
View file @
cafbe74d
...
@@ -582,3 +582,4 @@ class VideoCdnTest(unittest.TestCase):
...
@@ -582,3 +582,4 @@ class VideoCdnTest(unittest.TestCase):
cdn_response
.
return_value
=
Mock
(
status_code
=
404
)
cdn_response
.
return_value
=
Mock
(
status_code
=
404
)
fake_cdn_url
=
'http://fake_cdn.com/'
fake_cdn_url
=
'http://fake_cdn.com/'
self
.
assertIsNone
(
get_video_from_cdn
(
fake_cdn_url
,
original_video_url
))
self
.
assertIsNone
(
get_video_from_cdn
(
fake_cdn_url
,
original_video_url
))
common/lib/xmodule/xmodule/video_module/video_module.py
View file @
cafbe74d
...
@@ -26,6 +26,7 @@ from pkg_resources import resource_string
...
@@ -26,6 +26,7 @@ from pkg_resources import resource_string
from
django.conf
import
settings
from
django.conf
import
settings
from
xblock.core
import
XBlock
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
from
xblock.runtime
import
KvsFieldData
from
xblock.runtime
import
KvsFieldData
...
@@ -77,6 +78,7 @@ log = logging.getLogger(__name__)
...
@@ -77,6 +78,7 @@ log = logging.getLogger(__name__)
_
=
lambda
text
:
text
_
=
lambda
text
:
text
@XBlock.wants
(
'settings'
)
class
VideoModule
(
VideoFields
,
VideoTranscriptsMixin
,
VideoStudentViewHandlers
,
XModule
):
class
VideoModule
(
VideoFields
,
VideoTranscriptsMixin
,
VideoStudentViewHandlers
,
XModule
):
"""
"""
XML source example:
XML source example:
...
@@ -230,6 +232,14 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
...
@@ -230,6 +232,14 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
track_url
,
transcript_language
,
sorted_languages
=
self
.
get_transcripts_for_student
()
track_url
,
transcript_language
,
sorted_languages
=
self
.
get_transcripts_for_student
()
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'
]
return
self
.
system
.
render_template
(
'video.html'
,
{
return
self
.
system
.
render_template
(
'video.html'
,
{
'ajax_url'
:
self
.
system
.
ajax_url
+
'/save_user_state'
,
'ajax_url'
:
self
.
system
.
ajax_url
+
'/save_user_state'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
False
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
False
),
...
@@ -254,7 +264,8 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
...
@@ -254,7 +264,8 @@ class VideoModule(VideoFields, VideoTranscriptsMixin, VideoStudentViewHandlers,
# configuration setting field.
# configuration setting field.
'yt_test_timeout'
:
1500
,
'yt_test_timeout'
:
1500
,
'yt_api_url'
:
settings
.
YOUTUBE
[
'API'
],
'yt_api_url'
:
settings
.
YOUTUBE
[
'API'
],
'yt_test_url'
:
settings
.
YOUTUBE
[
'TEST_URL'
],
'yt_test_url'
:
settings
.
YOUTUBE
[
'METADATA_URL'
],
'yt_key'
:
yt_api_key
,
'transcript_download_format'
:
transcript_download_format
,
'transcript_download_format'
:
transcript_download_format
,
'transcript_download_formats_list'
:
self
.
descriptor
.
fields
[
'transcript_download_format'
]
.
values
,
'transcript_download_formats_list'
:
self
.
descriptor
.
fields
[
'transcript_download_format'
]
.
values
,
'transcript_language'
:
transcript_language
,
'transcript_language'
:
transcript_language
,
...
...
common/test/acceptance/tests/helpers.py
View file @
cafbe74d
...
@@ -49,8 +49,7 @@ def is_youtube_available():
...
@@ -49,8 +49,7 @@ def is_youtube_available():
youtube_api_urls
=
{
youtube_api_urls
=
{
'main'
:
'https://www.youtube.com/'
,
'main'
:
'https://www.youtube.com/'
,
'player'
:
'http://www.youtube.com/iframe_api'
,
'player'
:
'https://www.youtube.com/iframe_api'
,
'metadata'
:
'http://gdata.youtube.com/feeds/api/videos/'
,
# For transcripts, you need to check an actual video, so we will
# For transcripts, you need to check an actual video, so we will
# just specify our default video and see if that one is available.
# just specify our default video and see if that one is available.
'transcript'
:
'http://video.google.com/timedtext?lang=en&v=OEoXaMPEzfM'
,
'transcript'
:
'http://video.google.com/timedtext?lang=en&v=OEoXaMPEzfM'
,
...
...
lms/djangoapps/courseware/features/video.feature
View file @
cafbe74d
...
@@ -3,7 +3,7 @@ Feature: LMS.Video component
...
@@ -3,7 +3,7 @@ Feature: LMS.Video component
As a student, I want to view course videos in LMS
As a student, I want to view course videos in LMS
# 1
# 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
Given
youtube server is up and response time is 2 seconds
And
I am registered for the course
"test_course"
And
I am registered for the course
"test_course"
And
I have a
"subs_OEoXaMPEzfM.srt.sjson"
transcript file in assets
And
I have a
"subs_OEoXaMPEzfM.srt.sjson"
transcript file in assets
...
@@ -24,9 +24,9 @@ Feature: LMS.Video component
...
@@ -24,9 +24,9 @@ Feature: LMS.Video component
|
Hi,
welcome
to
Edx.
|
|
Hi,
welcome
to
Edx.
|
|
Equal
transcripts
|
|
Equal
transcripts
|
When
I open video
"C"
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 make sure captions are opened
And
I see
"好 各位同学"
text in the captions
And
I see
"好 各位同学"
text in the captions
When
I open video
"D"
When
I open video
"D"
Then
the video has rendered in
"
HTML5
"
mode
Then
the video has rendered in
"
YOUTUBE
"
mode
And
the video does not show the captions
And
I make sure captions are opened
lms/djangoapps/courseware/tests/test_video_mongo.py
View file @
cafbe74d
...
@@ -55,8 +55,9 @@ class TestVideoYouTube(TestVideo):
...
@@ -55,8 +55,9 @@ class TestVideoYouTube(TestVideo):
'track'
:
None
,
'track'
:
None
,
'youtube_streams'
:
create_youtube_string
(
self
.
item_descriptor
),
'youtube_streams'
:
create_youtube_string
(
self
.
item_descriptor
),
'yt_test_timeout'
:
1500
,
'yt_test_timeout'
:
1500
,
'yt_api_url'
:
'www.youtube.com/iframe_api'
,
'yt_api_url'
:
'https://www.youtube.com/iframe_api'
,
'yt_test_url'
:
'gdata.youtube.com/feeds/api/videos/'
,
'yt_test_url'
:
'https://www.googleapis.com/youtube/v3/videos/'
,
'yt_key'
:
None
,
'transcript_download_format'
:
'srt'
,
'transcript_download_format'
:
'srt'
,
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_language'
:
u'en'
,
'transcript_language'
:
u'en'
,
...
@@ -119,8 +120,9 @@ class TestVideoNonYouTube(TestVideo):
...
@@ -119,8 +120,9 @@ class TestVideoNonYouTube(TestVideo):
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'yt_test_timeout'
:
1500
,
'yt_test_timeout'
:
1500
,
'yt_api_url'
:
'www.youtube.com/iframe_api'
,
'yt_api_url'
:
'https://www.youtube.com/iframe_api'
,
'yt_test_url'
:
'gdata.youtube.com/feeds/api/videos/'
,
'yt_test_url'
:
'https://www.googleapis.com/youtube/v3/videos/'
,
'yt_key'
:
None
,
'transcript_download_format'
:
'srt'
,
'transcript_download_format'
:
'srt'
,
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_language'
:
u'en'
,
'transcript_language'
:
u'en'
,
...
@@ -221,8 +223,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -221,8 +223,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'yt_test_timeout'
:
1500
,
'yt_test_timeout'
:
1500
,
'yt_api_url'
:
'www.youtube.com/iframe_api'
,
'yt_api_url'
:
'https://www.youtube.com/iframe_api'
,
'yt_test_url'
:
'gdata.youtube.com/feeds/api/videos/'
,
'yt_test_url'
:
'https://www.googleapis.com/youtube/v3/videos/'
,
'yt_key'
:
None
,
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
}
}
...
@@ -337,8 +340,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -337,8 +340,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'yt_test_timeout'
:
1500
,
'yt_test_timeout'
:
1500
,
'yt_api_url'
:
'www.youtube.com/iframe_api'
,
'yt_api_url'
:
'https://www.youtube.com/iframe_api'
,
'yt_test_url'
:
'gdata.youtube.com/feeds/api/videos/'
,
'yt_test_url'
:
'https://www.googleapis.com/youtube/v3/videos/'
,
'yt_key'
:
None
,
'transcript_download_format'
:
'srt'
,
'transcript_download_format'
:
'srt'
,
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_language'
:
u'en'
,
'transcript_language'
:
u'en'
,
...
@@ -476,8 +480,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -476,8 +480,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'yt_test_timeout'
:
1500
,
'yt_test_timeout'
:
1500
,
'yt_api_url'
:
'www.youtube.com/iframe_api'
,
'yt_api_url'
:
'https://www.youtube.com/iframe_api'
,
'yt_test_url'
:
'gdata.youtube.com/feeds/api/videos/'
,
'yt_test_url'
:
'https://www.googleapis.com/youtube/v3/videos/'
,
'yt_key'
:
None
,
'transcript_download_format'
:
'srt'
,
'transcript_download_format'
:
'srt'
,
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_language'
:
u'en'
,
'transcript_language'
:
u'en'
,
...
@@ -593,8 +598,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -593,8 +598,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'yt_test_timeout'
:
1500
,
'yt_test_timeout'
:
1500
,
'yt_api_url'
:
'www.youtube.com/iframe_api'
,
'yt_api_url'
:
'https://www.youtube.com/iframe_api'
,
'yt_test_url'
:
'gdata.youtube.com/feeds/api/videos/'
,
'yt_test_url'
:
'https://www.googleapis.com/youtube/v3/videos/'
,
'yt_key'
:
None
,
'transcript_download_format'
:
'srt'
,
'transcript_download_format'
:
'srt'
,
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_language'
:
u'en'
,
'transcript_language'
:
u'en'
,
...
@@ -696,8 +702,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
...
@@ -696,8 +702,9 @@ class TestGetHtmlMethod(BaseTestXmodule):
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'youtube_streams'
:
'1.00:OEoXaMPEzfM'
,
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'autoplay'
:
settings
.
FEATURES
.
get
(
'AUTOPLAY_VIDEOS'
,
True
),
'yt_test_timeout'
:
1500
,
'yt_test_timeout'
:
1500
,
'yt_api_url'
:
'www.youtube.com/iframe_api'
,
'yt_api_url'
:
'https://www.youtube.com/iframe_api'
,
'yt_test_url'
:
'gdata.youtube.com/feeds/api/videos/'
,
'yt_test_url'
:
'https://www.googleapis.com/youtube/v3/videos/'
,
'yt_key'
:
None
,
'transcript_download_format'
:
'srt'
,
'transcript_download_format'
:
'srt'
,
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_download_formats_list'
:
[{
'display_name'
:
'SubRip (.srt) file'
,
'value'
:
'srt'
},
{
'display_name'
:
'Text (.txt) file'
,
'value'
:
'txt'
}],
'transcript_language'
:
u'en'
,
'transcript_language'
:
u'en'
,
...
@@ -891,3 +898,4 @@ class VideoDescriptorTest(VideoDescriptorTestBase):
...
@@ -891,3 +898,4 @@ class VideoDescriptorTest(VideoDescriptorTestBase):
]
]
rendered_context
=
self
.
descriptor
.
get_context
()
rendered_context
=
self
.
descriptor
.
get_context
()
self
.
assertListEqual
(
rendered_context
[
'tabs'
],
correct_tabs
)
self
.
assertListEqual
(
rendered_context
[
'tabs'
],
correct_tabs
)
lms/envs/acceptance.py
View file @
cafbe74d
...
@@ -176,6 +176,6 @@ XQUEUE_INTERFACE = {
...
@@ -176,6 +176,6 @@ XQUEUE_INTERFACE = {
}
}
# Point the URL used to test YouTube availability to our stub YouTube server
# 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
[
'API'
]
=
"
http://
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
[
'
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
)
YOUTUBE
[
'TEXT_API'
][
'url'
]
=
"127.0.0.1:{0}/test_transcripts_youtube/"
.
format
(
YOUTUBE_PORT
)
lms/envs/aws.py
View file @
cafbe74d
...
@@ -500,3 +500,6 @@ PDF_RECEIPT_LOGO_HEIGHT_MM = ENV_TOKENS.get('PDF_RECEIPT_LOGO_HEIGHT_MM', PDF_RE
...
@@ -500,3 +500,6 @@ PDF_RECEIPT_LOGO_HEIGHT_MM = ENV_TOKENS.get('PDF_RECEIPT_LOGO_HEIGHT_MM', PDF_RE
PDF_RECEIPT_COBRAND_LOGO_HEIGHT_MM
=
ENV_TOKENS
.
get
(
PDF_RECEIPT_COBRAND_LOGO_HEIGHT_MM
=
ENV_TOKENS
.
get
(
'PDF_RECEIPT_COBRAND_LOGO_HEIGHT_MM'
,
PDF_RECEIPT_COBRAND_LOGO_HEIGHT_MM
'PDF_RECEIPT_COBRAND_LOGO_HEIGHT_MM'
,
PDF_RECEIPT_COBRAND_LOGO_HEIGHT_MM
)
)
XBLOCK_SETTINGS
=
ENV_TOKENS
.
get
(
'XBLOCK_SETTINGS'
,
{})
XBLOCK_SETTINGS
.
setdefault
(
"VideoModule"
,
{})[
'YOUTUBE_API_KEY'
]
=
AUTH_TOKENS
.
get
(
'YOUTUBE_API_KEY'
,
YOUTUBE_API_KEY
)
lms/envs/bok_choy.py
View file @
cafbe74d
...
@@ -103,8 +103,8 @@ FEATURES['ENTRANCE_EXAMS'] = True
...
@@ -103,8 +103,8 @@ FEATURES['ENTRANCE_EXAMS'] = True
# Point the URL used to test YouTube availability to our stub YouTube server
# Point the URL used to test YouTube availability to our stub YouTube server
YOUTUBE_PORT
=
9080
YOUTUBE_PORT
=
9080
YOUTUBE
[
'API'
]
=
"127.0.0.1:{0}/get_youtube_api/"
.
format
(
YOUTUBE_PORT
)
YOUTUBE
[
'API'
]
=
"
http://
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
[
'
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
)
YOUTUBE
[
'TEXT_API'
][
'url'
]
=
"127.0.0.1:{0}/test_transcripts_youtube/"
.
format
(
YOUTUBE_PORT
)
############################# SECURITY SETTINGS ################################
############################# SECURITY SETTINGS ################################
...
...
lms/envs/common.py
View file @
cafbe74d
...
@@ -1459,10 +1459,10 @@ EMAIL_OPTIN_MINIMUM_AGE = 13
...
@@ -1459,10 +1459,10 @@ EMAIL_OPTIN_MINIMUM_AGE = 13
YOUTUBE
=
{
YOUTUBE
=
{
# YouTube JavaScript API
# YouTube JavaScript API
'API'
:
'www.youtube.com/iframe_api'
,
'API'
:
'
https://
www.youtube.com/iframe_api'
,
# URL to
test YouTube availability
# URL to
get YouTube metadata
'
TEST_URL'
:
'gdata.youtube.com/feeds/api
/videos/'
,
'
METADATA_URL'
:
'https://www.googleapis.com/youtube/v3
/videos/'
,
# Current youtube api for requesting transcripts.
# Current youtube api for requesting transcripts.
# For example: http://video.google.com/timedtext?lang=en&v=j_jEn79vS3g.
# For example: http://video.google.com/timedtext?lang=en&v=j_jEn79vS3g.
...
@@ -1474,6 +1474,7 @@ YOUTUBE = {
...
@@ -1474,6 +1474,7 @@ YOUTUBE = {
},
},
},
},
}
}
YOUTUBE_API_KEY
=
None
################################### APPS ######################################
################################### APPS ######################################
INSTALLED_APPS
=
(
INSTALLED_APPS
=
(
...
...
lms/static/js_test.yml
View file @
cafbe74d
...
@@ -58,6 +58,7 @@ lib_paths:
...
@@ -58,6 +58,7 @@ lib_paths:
-
xmodule_js/common_static/js/vendor/edxnotes/annotator-full.min.js
-
xmodule_js/common_static/js/vendor/edxnotes/annotator-full.min.js
-
xmodule_js/common_static/js/test/i18n.js
-
xmodule_js/common_static/js/test/i18n.js
-
xmodule_js/common_static/js/vendor/date.js
-
xmodule_js/common_static/js/vendor/date.js
-
xmodule_js/common_static/js/vendor/moment.min.js
# Paths to source JavaScript files
# Paths to source JavaScript files
src_paths
:
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