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
e9502052
Commit
e9502052
authored
Aug 21, 2013
by
polesye
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #746 from edx/anton/support_failover_from_yt
Add supporting failover from Youtube.
parents
7fc8fcde
03111cb9
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
143 additions
and
57 deletions
+143
-57
common/lib/xmodule/xmodule/js/spec/helper.coffee
+7
-1
common/lib/xmodule/xmodule/js/spec/video/general_spec.js
+40
-3
common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
+2
-0
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
+94
-53
No files found.
common/lib/xmodule/xmodule/js/spec/helper.coffee
View file @
e9502052
...
...
@@ -90,7 +90,13 @@ jasmine.stubbedHtml5Speeds = ['0.75', '1.0', '1.25', '1.50']
jasmine
.
stubRequests
=
->
spyOn
(
$
,
'ajax'
).
andCallFake
(
settings
)
->
if
match
=
settings
.
url
.
match
/youtube\.com\/.+\/videos\/(.+)\?v=2&alt=jsonc/
settings
.
success
data
:
jasmine
.
stubbedMetadata
[
match
[
1
]]
if
settings
.
success
# match[1] - it's video ID
settings
.
success
data
:
jasmine
.
stubbedMetadata
[
match
[
1
]]
else
{
always
:
(
callback
)
->
callback
.
call
(
window
,
{},
'success'
);
}
else
if
match
=
settings
.
url
.
match
/static(\/.*)?\/subs\/(.+)\.srt\.sjson/
settings
.
success
jasmine
.
stubbedCaption
else
if
settings
.
url
.
match
/.+\/problem_get$/
...
...
common/lib/xmodule/xmodule/js/spec/video/general_spec.js
View file @
e9502052
...
...
@@ -4,8 +4,6 @@
beforeEach
(
function
()
{
jasmine
.
stubRequests
();
oldOTBD
=
window
.
onTouchBasedDevice
;
window
.
onTouchBasedDevice
=
jasmine
.
createSpy
(
'onTouchBasedDevice'
).
andReturn
(
false
);
this
.
videosDefinition
=
'0.75:7tqY6eQzVhE,1.0:cogebirgzzM'
;
this
[
'7tqY6eQzVhE'
]
=
'7tqY6eQzVhE'
;
this
[
'cogebirgzzM'
]
=
'cogebirgzzM'
;
...
...
@@ -16,7 +14,6 @@
window
.
onYouTubePlayerAPIReady
=
undefined
;
window
.
onHTML5PlayerAPIReady
=
undefined
;
$
(
'source'
).
remove
();
window
.
onTouchBasedDevice
=
oldOTBD
;
});
describe
(
'constructor'
,
function
()
{
...
...
@@ -58,6 +55,46 @@
expect
(
this
.
state
.
speed
).
toEqual
(
'0.75'
);
});
});
describe
(
'Check Youtube link existence'
,
function
()
{
var
statusList
=
{
error
:
'html5'
,
timeout
:
'html5'
,
abort
:
'html5'
,
parsererror
:
'html5'
,
success
:
'youtube'
,
notmodified
:
'youtube'
};
function
stubDeffered
(
data
,
status
)
{
return
{
always
:
function
(
callback
)
{
callback
.
call
(
window
,
data
,
status
);
}
}
}
function
checkPlayer
(
videoType
,
data
,
status
)
{
this
.
state
=
new
window
.
Video
(
'#example'
);
spyOn
(
this
.
state
,
'getVideoMetadata'
)
.
andReturn
(
stubDeffered
(
data
,
status
));
this
.
state
.
initialize
(
'#example'
);
expect
(
this
.
state
.
videoType
).
toEqual
(
videoType
);
}
it
(
'if video id is incorrect'
,
function
()
{
checkPlayer
(
'html5'
,
{
error
:
{}
},
'success'
);
});
$
.
each
(
statusList
,
function
(
status
,
mode
){
it
(
'Status:'
+
status
+
', mode:'
+
mode
,
function
()
{
checkPlayer
(
mode
,
{},
status
);
});
});
});
});
describe
(
'HTML5'
,
function
()
{
...
...
common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
View file @
e9502052
...
...
@@ -79,6 +79,8 @@
it
(
'create Youtube player'
,
function
()
{
var
oldYT
=
window
.
YT
;
jasmine
.
stubRequests
();
window
.
YT
=
{
Player
:
function
()
{
},
PlayerState
:
oldYT
.
PlayerState
...
...
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
View file @
e9502052
...
...
@@ -30,8 +30,7 @@ function (VideoPlayer) {
*/
return
function
(
state
,
element
)
{
_makeFunctionsPublic
(
state
);
_initialize
(
state
,
element
);
_renderElements
(
state
);
state
.
initialize
(
element
);
};
// ***************************************************************
...
...
@@ -56,59 +55,12 @@ function (VideoPlayer) {
// Old private functions. Now also public so that can be
// tested by Jasmine.
state
.
initialize
=
_
.
bind
(
initialize
,
state
);
state
.
parseSpeed
=
_
.
bind
(
parseSpeed
,
state
);
state
.
fetchMetadata
=
_
.
bind
(
fetchMetadata
,
state
);
state
.
parseYoutubeStreams
=
_
.
bind
(
parseYoutubeStreams
,
state
);
state
.
parseVideoSources
=
_
.
bind
(
parseVideoSources
,
state
);
}
// function _initialize(element)
// The function set initial configuration and preparation.
function
_initialize
(
state
,
element
)
{
// This is used in places where we instead would have to check if an element has a CSS class 'fullscreen'.
state
.
isFullScreen
=
false
;
// The parent element of the video, and the ID.
state
.
el
=
$
(
element
).
find
(
'.video'
);
state
.
id
=
state
.
el
.
attr
(
'id'
).
replace
(
/video_/
,
''
);
// We store all settings passed to us by the server in one place. These are "read only", so don't
// modify them. All variable content lives in 'state' object.
state
.
config
=
{
element
:
element
,
start
:
state
.
el
.
data
(
'start'
),
end
:
state
.
el
.
data
(
'end'
),
caption_data_dir
:
state
.
el
.
data
(
'caption-data-dir'
),
caption_asset_path
:
state
.
el
.
data
(
'caption-asset-path'
),
show_captions
:
(
state
.
el
.
data
(
'show-captions'
).
toString
().
toLowerCase
()
===
'true'
),
youtubeStreams
:
state
.
el
.
data
(
'streams'
),
sub
:
state
.
el
.
data
(
'sub'
),
mp4Source
:
state
.
el
.
data
(
'mp4-source'
),
webmSource
:
state
.
el
.
data
(
'webm-source'
),
oggSource
:
state
.
el
.
data
(
'ogg-source'
),
fadeOutTimeout
:
1400
,
availableQualities
:
[
'hd720'
,
'hd1080'
,
'highres'
]
};
if
(
!
(
_parseYouTubeIDs
(
state
)))
{
// If we do not have YouTube ID's, try parsing HTML5 video sources.
_prepareHTML5Video
(
state
);
}
_configureCaptions
(
state
);
_setPlayerMode
(
state
);
// Possible value are: 'visible', 'hiding', and 'invisible'.
state
.
controlState
=
'visible'
;
state
.
controlHideTimeout
=
null
;
state
.
captionState
=
'visible'
;
state
.
captionHideTimeout
=
null
;
state
.
getVideoMetadata
=
_
.
bind
(
getVideoMetadata
,
state
);
}
// function _renderElements(state)
...
...
@@ -228,12 +180,83 @@ function (VideoPlayer) {
state
.
setSpeed
(
$
.
cookie
(
'video_speed'
));
}
function
_setConfigurations
(
state
)
{
_configureCaptions
(
state
);
_setPlayerMode
(
state
);
// Possible value are: 'visible', 'hiding', and 'invisible'.
state
.
controlState
=
'visible'
;
state
.
controlHideTimeout
=
null
;
state
.
captionState
=
'visible'
;
state
.
captionHideTimeout
=
null
;
}
// ***************************************************************
// Public functions start here.
// These are available via the 'state' object. Their context ('this' keyword) is the 'state' object.
// The magic private function that makes them available and sets up their context is makeFunctionsPublic().
// ***************************************************************
// function initialize(element)
// The function set initial configuration and preparation.
function
initialize
(
element
)
{
var
_this
=
this
;
// This is used in places where we instead would have to check if an element has a CSS class 'fullscreen'.
this
.
isFullScreen
=
false
;
// The parent element of the video, and the ID.
this
.
el
=
$
(
element
).
find
(
'.video'
);
this
.
id
=
this
.
el
.
attr
(
'id'
).
replace
(
/video_/
,
''
);
// We store all settings passed to us by the server in one place. These are "read only", so don't
// modify them. All variable content lives in 'state' object.
this
.
config
=
{
element
:
element
,
start
:
this
.
el
.
data
(
'start'
),
end
:
this
.
el
.
data
(
'end'
),
caption_data_dir
:
this
.
el
.
data
(
'caption-data-dir'
),
caption_asset_path
:
this
.
el
.
data
(
'caption-asset-path'
),
show_captions
:
(
this
.
el
.
data
(
'show-captions'
).
toString
().
toLowerCase
()
===
'true'
),
youtubeStreams
:
this
.
el
.
data
(
'streams'
),
sub
:
this
.
el
.
data
(
'sub'
),
mp4Source
:
this
.
el
.
data
(
'mp4-source'
),
webmSource
:
this
.
el
.
data
(
'webm-source'
),
oggSource
:
this
.
el
.
data
(
'ogg-source'
),
fadeOutTimeout
:
1400
,
availableQualities
:
[
'hd720'
,
'hd1080'
,
'highres'
]
};
if
(
!
(
_parseYouTubeIDs
(
this
)))
{
// If we do not have YouTube ID's, try parsing HTML5 video sources.
_prepareHTML5Video
(
this
);
_setConfigurations
(
this
);
_renderElements
(
this
);
}
else
{
this
.
getVideoMetadata
()
.
always
(
function
(
json
,
status
)
{
var
err
=
$
.
isPlainObject
(
json
.
error
)
||
(
status
!==
"success"
&&
status
!==
"notmodified"
);
if
(
err
){
// When the youtube link doesn't work for any reason
// (for example, the great firewall in china) any
// alternate sources should automatically play.
_prepareHTML5Video
(
_this
);
_this
.
el
.
find
(
'a.quality_control'
).
hide
();
}
_setConfigurations
(
_this
);
_renderElements
(
_this
);
});
}
}
// function parseYoutubeStreams(state, youtubeStreams)
//
// Take a string in the form:
...
...
@@ -297,9 +320,9 @@ function (VideoPlayer) {
this
.
metadata
=
{};
$
.
each
(
this
.
videos
,
function
(
speed
,
url
)
{
$
.
get
(
'https://gdata.youtube.com/feeds/api/videos/'
+
url
+
'?v=2&alt=jsonc'
,
(
function
(
data
)
{
_this
.
getVideoMetadata
(
url
,
function
(
data
)
{
_this
.
metadata
[
data
.
data
.
id
]
=
data
.
data
;
})
,
'jsonp'
)
;
});
});
}
...
...
@@ -329,6 +352,24 @@ function (VideoPlayer) {
}
}
function
getVideoMetadata
(
url
,
callback
)
{
var
successHandler
,
xhr
;
if
(
typeof
url
!==
'string'
)
{
url
=
this
.
videos
[
'1.0'
]
||
''
;
}
successHandler
=
(
$
.
isFunction
(
callback
))
?
callback
:
null
;
xhr
=
$
.
ajax
({
url
:
'https://gdata.youtube.com/feeds/api/videos/'
+
url
+
'?v=2&alt=jsonc'
,
timeout
:
500
,
dataType
:
'jsonp'
,
success
:
successHandler
});
return
xhr
;
}
function
stopBuffering
()
{
var
video
;
...
...
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