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
7c7e0950
Commit
7c7e0950
authored
Jan 30, 2013
by
Valera Rozuvan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Renamed videox to videoalpha, as per Piotr naming scheme request.
parent
9b6cbd6d
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
792 additions
and
0 deletions
+792
-0
common/lib/xmodule/setup.py
+1
-0
common/lib/xmodule/xmodule/css/videoalpha/display.scss
+0
-0
common/lib/xmodule/xmodule/js/src/videoalpha/display.coffee
+63
-0
common/lib/xmodule/xmodule/js/src/videoalpha/display/_subview.coffee
+14
-0
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_caption.coffee
+152
-0
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_control.coffee
+35
-0
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_player.coffee
+180
-0
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_progress_slider.coffee
+49
-0
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_quality_control.coffee
+27
-0
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_speed_control.coffee
+43
-0
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_volume_control.coffee
+40
-0
common/lib/xmodule/xmodule/templates/videoalpha/default.yaml
+7
-0
common/lib/xmodule/xmodule/videoalpha_module.py
+150
-0
lms/templates/videoalpha.html
+31
-0
No files found.
common/lib/xmodule/setup.py
View file @
7c7e0950
...
...
@@ -37,6 +37,7 @@ setup(
"timelimit = xmodule.timelimit_module:TimeLimitDescriptor"
,
"vertical = xmodule.vertical_module:VerticalDescriptor"
,
"video = xmodule.video_module:VideoDescriptor"
,
"videoalpha = xmodule.videoalpha_module:VideoAlphaDescriptor"
,
"videodev = xmodule.backcompat_module:TranslateCustomTagDescriptor"
,
"videosequence = xmodule.seq_module:SequenceDescriptor"
,
"discussion = xmodule.discussion_module:DiscussionDescriptor"
,
...
...
common/lib/xmodule/xmodule/css/videoalpha/display.scss
0 → 100644
View file @
7c7e0950
This diff is collapsed.
Click to expand it.
common/lib/xmodule/xmodule/js/src/videoalpha/display.coffee
0 → 100644
View file @
7c7e0950
class
@
Video
constructor
:
(
element
)
->
@
el
=
$
(
element
).
find
(
'.video'
)
@
id
=
@
el
.
attr
(
'id'
).
replace
(
/video_/
,
''
)
@
start
=
@
el
.
data
(
'start'
)
@
end
=
@
el
.
data
(
'end'
)
@
caption_data_dir
=
@
el
.
data
(
'caption-data-dir'
)
@
caption_asset_path
=
@
el
.
data
(
'caption-asset-path'
)
@
show_captions
=
@
el
.
data
(
'show-captions'
)
==
"true"
window
.
player
=
null
@
el
=
$
(
"#video_
#{
@
id
}
"
)
@
parseVideos
@
el
.
data
(
'streams'
)
@
fetchMetadata
()
@
parseSpeed
()
$
(
"#video_
#{
@
id
}
"
).
data
(
'video'
,
this
).
addClass
(
'video-load-complete'
)
@
hide_captions
=
$
.
cookie
(
'hide_captions'
)
==
'true'
if
YT
.
Player
@
embed
()
else
window
.
onYouTubePlayerAPIReady
=
=>
@
el
.
each
->
$
(
this
).
data
(
'video'
).
embed
()
youtubeId
:
(
speed
)
->
@
videos
[
speed
||
@
speed
]
parseVideos
:
(
videos
)
->
@
videos
=
{}
$
.
each
videos
.
split
(
/,/
),
(
index
,
video
)
=>
video
=
video
.
split
(
/:/
)
speed
=
parseFloat
(
video
[
0
]).
toFixed
(
2
).
replace
/\.00$/
,
'.0'
@
videos
[
speed
]
=
video
[
1
]
parseSpeed
:
->
@
setSpeed
(
$
.
cookie
(
'video_speed'
))
@
speeds
=
(
$
.
map
@
videos
,
(
url
,
speed
)
->
speed
).
sort
()
setSpeed
:
(
newSpeed
)
->
if
@
videos
[
newSpeed
]
!=
undefined
@
speed
=
newSpeed
$
.
cookie
(
'video_speed'
,
"
#{
newSpeed
}
"
,
expires
:
3650
,
path
:
'/'
)
else
@
speed
=
'1.0'
embed
:
->
@
player
=
new
VideoPlayer
video
:
this
fetchMetadata
:
(
url
)
->
@
metadata
=
{}
$
.
each
@
videos
,
(
speed
,
url
)
=>
$
.
get
"https://gdata.youtube.com/feeds/api/videos/
#{
url
}
?v=2&alt=jsonc"
,
((
data
)
=>
@
metadata
[
data
.
data
.
id
]
=
data
.
data
)
,
'jsonp'
getDuration
:
->
@
metadata
[
@
youtubeId
()].
duration
log
:
(
eventName
)
->
Logger
.
log
eventName
,
id
:
@
id
code
:
@
youtubeId
()
currentTime
:
@
player
.
currentTime
speed
:
@
speed
common/lib/xmodule/xmodule/js/src/videoalpha/display/_subview.coffee
0 → 100644
View file @
7c7e0950
class
@
Subview
constructor
:
(
options
)
->
$
.
each
options
,
(
key
,
value
)
=>
@
[
key
]
=
value
@
initialize
()
@
render
()
@
bind
()
$
:
(
selector
)
->
$
(
selector
,
@
el
)
initialize
:
->
render
:
->
bind
:
->
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_caption.coffee
0 → 100644
View file @
7c7e0950
class
@
VideoCaption
extends
Subview
initialize
:
->
@
loaded
=
false
bind
:
->
$
(
window
).
bind
(
'resize'
,
@
resize
)
@
$
(
'.hide-subtitles'
).
click
@
toggle
@
$
(
'.subtitles'
).
mouseenter
(
@
onMouseEnter
).
mouseleave
(
@
onMouseLeave
)
.
mousemove
(
@
onMovement
).
bind
(
'mousewheel'
,
@
onMovement
)
.
bind
(
'DOMMouseScroll'
,
@
onMovement
)
captionURL
:
->
"
#{
@
captionAssetPath
}#{
@
youtubeId
}
.srt.sjson"
render
:
->
# TODO: make it so you can have a video with no captions.
#@$('.video-wrapper').after """
# <ol class="subtitles"><li>Attempting to load captions...</li></ol>
# """
@
$
(
'.video-wrapper'
).
after
"""
<ol class="subtitles"></ol>
"""
@
$
(
'.video-controls .secondary-controls'
).
append
"""
<a href="#" class="hide-subtitles" title="Turn off captions">Captions</a>
"""
#"
@
$
(
'.subtitles'
).
css
maxHeight
:
@
$
(
'.video-wrapper'
).
height
()
-
5
@
fetchCaption
()
fetchCaption
:
->
$
.
getWithPrefix
@
captionURL
(),
(
captions
)
=>
@
captions
=
captions
.
text
@
start
=
captions
.
start
@
loaded
=
true
if
onTouchBasedDevice
()
$
(
'.subtitles li'
).
html
"Caption will be displayed when you start playing the video."
else
@
renderCaption
()
renderCaption
:
->
container
=
$
(
'<ol>'
)
$
.
each
@
captions
,
(
index
,
text
)
=>
container
.
append
$
(
'<li>'
).
html
(
text
).
attr
'data-index'
:
index
'data-start'
:
@
start
[
index
]
@
$
(
'.subtitles'
).
html
(
container
.
html
())
@
$
(
'.subtitles li[data-index]'
).
click
@
seekPlayer
# prepend and append an empty <li> for cosmetic reason
@
$
(
'.subtitles'
).
prepend
(
$
(
'<li class="spacing">'
).
height
(
@
topSpacingHeight
()))
.
append
(
$
(
'<li class="spacing">'
).
height
(
@
bottomSpacingHeight
()))
@
rendered
=
true
search
:
(
time
)
->
if
@
loaded
min
=
0
max
=
@
start
.
length
-
1
while
min
<
max
index
=
Math
.
ceil
((
max
+
min
)
/
2
)
if
time
<
@
start
[
index
]
max
=
index
-
1
if
time
>=
@
start
[
index
]
min
=
index
return
min
play
:
->
if
@
loaded
@
renderCaption
()
unless
@
rendered
@
playing
=
true
pause
:
->
if
@
loaded
@
playing
=
false
updatePlayTime
:
(
time
)
->
if
@
loaded
# This 250ms offset is required to match the video speed
time
=
Math
.
round
(
Time
.
convert
(
time
,
@
currentSpeed
,
'1.0'
)
*
1000
+
250
)
newIndex
=
@
search
time
if
newIndex
!=
undefined
&&
@
currentIndex
!=
newIndex
if
@
currentIndex
@
$
(
".subtitles li.current"
).
removeClass
(
'current'
)
@
$
(
".subtitles li[data-index='
#{
newIndex
}
']"
).
addClass
(
'current'
)
@
currentIndex
=
newIndex
@
scrollCaption
()
resize
:
=>
@
$
(
'.subtitles'
).
css
maxHeight
:
@
captionHeight
()
@
$
(
'.subtitles .spacing:first'
).
height
(
@
topSpacingHeight
())
@
$
(
'.subtitles .spacing:last'
).
height
(
@
bottomSpacingHeight
())
@
scrollCaption
()
onMouseEnter
:
=>
clearTimeout
@
frozen
if
@
frozen
@
frozen
=
setTimeout
@
onMouseLeave
,
10000
onMovement
:
=>
@
onMouseEnter
()
onMouseLeave
:
=>
clearTimeout
@
frozen
if
@
frozen
@
frozen
=
null
@
scrollCaption
()
if
@
playing
scrollCaption
:
->
if
!
@
frozen
&&
@
$
(
'.subtitles .current:first'
).
length
@
$
(
'.subtitles'
).
scrollTo
@
$
(
'.subtitles .current:first'
),
offset
:
-
@
calculateOffset
(
@
$
(
'.subtitles .current:first'
))
seekPlayer
:
(
event
)
=>
event
.
preventDefault
()
time
=
Math
.
round
(
Time
.
convert
(
$
(
event
.
target
).
data
(
'start'
),
'1.0'
,
@
currentSpeed
)
/
1000
)
$
(
@
).
trigger
(
'seek'
,
time
)
calculateOffset
:
(
element
)
->
@
captionHeight
()
/
2
-
element
.
height
()
/
2
topSpacingHeight
:
->
@
calculateOffset
(
@
$
(
'.subtitles li:not(.spacing):first'
))
bottomSpacingHeight
:
->
@
calculateOffset
(
@
$
(
'.subtitles li:not(.spacing):last'
))
toggle
:
(
event
)
=>
event
.
preventDefault
()
if
@
el
.
hasClass
(
'closed'
)
# Captions are "closed" e.g. turned off
@
hideCaptions
(
false
)
else
# Captions are on
@
hideCaptions
(
true
)
hideCaptions
:
(
hide_captions
)
=>
if
hide_captions
@
$
(
'.hide-subtitles'
).
attr
(
'title'
,
'Turn on captions'
)
@
el
.
addClass
(
'closed'
)
else
@
$
(
'.hide-subtitles'
).
attr
(
'title'
,
'Turn off captions'
)
@
el
.
removeClass
(
'closed'
)
@
scrollCaption
()
$
.
cookie
(
'hide_captions'
,
hide_captions
,
expires
:
3650
,
path
:
'/'
)
captionHeight
:
->
if
@
el
.
hasClass
(
'fullscreen'
)
$
(
window
).
height
()
-
@
$
(
'.video-controls'
).
height
()
else
@
$
(
'.video-wrapper'
).
height
()
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_control.coffee
0 → 100644
View file @
7c7e0950
class
@
VideoControl
extends
Subview
bind
:
->
@
$
(
'.video_control'
).
click
@
togglePlayback
render
:
->
@
el
.
append
"""
<div class="slider"></div>
<div>
<ul class="vcr">
<li><a class="video_control" href="#"></a></li>
<li>
<div class="vidtime">0:00 / 0:00</div>
</li>
</ul>
<div class="secondary-controls">
<a href="#" class="add-fullscreen" title="Fill browser">Fill Browser</a>
</div>
</div>
"""
#"
unless
onTouchBasedDevice
()
@
$
(
'.video_control'
).
addClass
(
'play'
).
html
(
'Play'
)
play
:
->
@
$
(
'.video_control'
).
removeClass
(
'play'
).
addClass
(
'pause'
).
html
(
'Pause'
)
pause
:
->
@
$
(
'.video_control'
).
removeClass
(
'pause'
).
addClass
(
'play'
).
html
(
'Play'
)
togglePlayback
:
(
event
)
=>
event
.
preventDefault
()
if
@
$
(
'.video_control'
).
hasClass
(
'play'
)
$
(
@
).
trigger
(
'play'
)
else
if
@
$
(
'.video_control'
).
hasClass
(
'pause'
)
$
(
@
).
trigger
(
'pause'
)
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_player.coffee
0 → 100644
View file @
7c7e0950
class
@
VideoPlayer
extends
Subview
initialize
:
->
# Define a missing constant of Youtube API
YT
.
PlayerState
.
UNSTARTED
=
-
1
@
currentTime
=
0
@
el
=
$
(
"#video_
#{
@
video
.
id
}
"
)
bind
:
->
$
(
@
control
).
bind
(
'play'
,
@
play
)
.
bind
(
'pause'
,
@
pause
)
$
(
@
qualityControl
).
bind
(
'changeQuality'
,
@
handlePlaybackQualityChange
)
$
(
@
caption
).
bind
(
'seek'
,
@
onSeek
)
$
(
@
speedControl
).
bind
(
'speedChange'
,
@
onSpeedChange
)
$
(
@
progressSlider
).
bind
(
'seek'
,
@
onSeek
)
if
@
volumeControl
$
(
@
volumeControl
).
bind
(
'volumeChange'
,
@
onVolumeChange
)
$
(
document
).
keyup
@
bindExitFullScreen
@
$
(
'.add-fullscreen'
).
click
@
toggleFullScreen
@
addToolTip
()
unless
onTouchBasedDevice
()
bindExitFullScreen
:
(
event
)
=>
if
@
el
.
hasClass
(
'fullscreen'
)
&&
event
.
keyCode
==
27
@
toggleFullScreen
(
event
)
render
:
->
@
control
=
new
VideoControl
el
:
@
$
(
'.video-controls'
)
@
qualityControl
=
new
VideoQualityControl
el
:
@
$
(
'.secondary-controls'
)
@
caption
=
new
VideoCaption
el
:
@
el
youtubeId
:
@
video
.
youtubeId
(
'1.0'
)
currentSpeed
:
@
currentSpeed
()
captionAssetPath
:
@
video
.
caption_asset_path
unless
onTouchBasedDevice
()
@
volumeControl
=
new
VideoVolumeControl
el
:
@
$
(
'.secondary-controls'
)
@
speedControl
=
new
VideoSpeedControl
el
:
@
$
(
'.secondary-controls'
),
speeds
:
@
video
.
speeds
,
currentSpeed
:
@
currentSpeed
()
@
progressSlider
=
new
VideoProgressSlider
el
:
@
$
(
'.slider'
)
@
playerVars
=
controls
:
0
wmode
:
'transparent'
rel
:
0
showinfo
:
0
enablejsapi
:
1
modestbranding
:
1
if
@
video
.
start
@
playerVars
.
start
=
@
video
.
start
@
playerVars
.
wmode
=
'window'
if
@
video
.
end
# work in AS3, not HMLT5. but iframe use AS3
@
playerVars
.
end
=
@
video
.
end
@
player
=
new
YT
.
Player
@
video
.
id
,
playerVars
:
@
playerVars
videoId
:
@
video
.
youtubeId
()
events
:
onReady
:
@
onReady
onStateChange
:
@
onStateChange
onPlaybackQualityChange
:
@
onPlaybackQualityChange
@
caption
.
hideCaptions
(
@
[
'video'
].
hide_captions
)
addToolTip
:
->
@
$
(
'.add-fullscreen, .hide-subtitles'
).
qtip
position
:
my
:
'top right'
at
:
'top center'
onReady
:
(
event
)
=>
unless
onTouchBasedDevice
()
$
(
'.video-load-complete:first'
).
data
(
'video'
).
player
.
play
()
onStateChange
:
(
event
)
=>
switch
event
.
data
when
YT
.
PlayerState
.
UNSTARTED
@
onUnstarted
()
when
YT
.
PlayerState
.
PLAYING
@
onPlay
()
when
YT
.
PlayerState
.
PAUSED
@
onPause
()
when
YT
.
PlayerState
.
ENDED
@
onEnded
()
onPlaybackQualityChange
:
(
event
,
value
)
=>
quality
=
@
player
.
getPlaybackQuality
()
@
qualityControl
.
onQualityChange
(
quality
)
handlePlaybackQualityChange
:
(
event
,
value
)
=>
@
player
.
setPlaybackQuality
(
value
)
onUnstarted
:
=>
@
control
.
pause
()
@
caption
.
pause
()
onPlay
:
=>
@
video
.
log
'play_video'
window
.
player
.
pauseVideo
()
if
window
.
player
&&
window
.
player
!=
@
player
window
.
player
=
@
player
unless
@
player
.
interval
@
player
.
interval
=
setInterval
(
@
update
,
200
)
@
caption
.
play
()
@
control
.
play
()
@
progressSlider
.
play
()
onPause
:
=>
@
video
.
log
'pause_video'
window
.
player
=
null
if
window
.
player
==
@
player
clearInterval
(
@
player
.
interval
)
@
player
.
interval
=
null
@
caption
.
pause
()
@
control
.
pause
()
onEnded
:
=>
@
control
.
pause
()
@
caption
.
pause
()
onSeek
:
(
event
,
time
)
=>
@
player
.
seekTo
(
time
,
true
)
if
@
isPlaying
()
clearInterval
(
@
player
.
interval
)
@
player
.
interval
=
setInterval
(
@
update
,
200
)
else
@
currentTime
=
time
@
updatePlayTime
time
onSpeedChange
:
(
event
,
newSpeed
)
=>
@
currentTime
=
Time
.
convert
(
@
currentTime
,
parseFloat
(
@
currentSpeed
()),
newSpeed
)
newSpeed
=
parseFloat
(
newSpeed
).
toFixed
(
2
).
replace
/\.00$/
,
'.0'
@
video
.
setSpeed
(
newSpeed
)
@
caption
.
currentSpeed
=
newSpeed
if
@
isPlaying
()
@
player
.
loadVideoById
(
@
video
.
youtubeId
(),
@
currentTime
)
else
@
player
.
cueVideoById
(
@
video
.
youtubeId
(),
@
currentTime
)
@
updatePlayTime
@
currentTime
onVolumeChange
:
(
event
,
volume
)
=>
@
player
.
setVolume
volume
update
:
=>
if
@
currentTime
=
@
player
.
getCurrentTime
()
@
updatePlayTime
@
currentTime
updatePlayTime
:
(
time
)
->
progress
=
Time
.
format
(
time
)
+
' / '
+
Time
.
format
(
@
duration
())
@
$
(
".vidtime"
).
html
(
progress
)
@
caption
.
updatePlayTime
(
time
)
@
progressSlider
.
updatePlayTime
(
time
,
@
duration
())
toggleFullScreen
:
(
event
)
=>
event
.
preventDefault
()
if
@
el
.
hasClass
(
'fullscreen'
)
@
$
(
'.add-fullscreen'
).
attr
(
'title'
,
'Fill browser'
)
@
el
.
removeClass
(
'fullscreen'
)
else
@
el
.
addClass
(
'fullscreen'
)
@
$
(
'.add-fullscreen'
).
attr
(
'title'
,
'Exit fill browser'
)
@
caption
.
resize
()
# Delegates
play
:
=>
@
player
.
playVideo
()
if
@
player
.
playVideo
isPlaying
:
->
@
player
.
getPlayerState
()
==
YT
.
PlayerState
.
PLAYING
pause
:
=>
@
player
.
pauseVideo
()
if
@
player
.
pauseVideo
duration
:
->
@
video
.
getDuration
()
currentSpeed
:
->
@
video
.
speed
volume
:
(
value
)
->
if
value
?
@
player
.
setVolume
value
else
@
player
.
getVolume
()
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_progress_slider.coffee
0 → 100644
View file @
7c7e0950
class
@
VideoProgressSlider
extends
Subview
initialize
:
->
@
buildSlider
()
unless
onTouchBasedDevice
()
buildSlider
:
->
@
slider
=
@
el
.
slider
range
:
'min'
change
:
@
onChange
slide
:
@
onSlide
stop
:
@
onStop
@
buildHandle
()
buildHandle
:
->
@
handle
=
@
$
(
'.slider .ui-slider-handle'
)
@
handle
.
qtip
content
:
"
#{
Time
.
format
(
@
slider
.
slider
(
'value'
))
}
"
position
:
my
:
'bottom center'
at
:
'top center'
container
:
@
handle
hide
:
delay
:
700
style
:
classes
:
'ui-tooltip-slider'
widget
:
true
play
:
=>
@
buildSlider
()
unless
@
slider
updatePlayTime
:
(
currentTime
,
duration
)
->
if
@
slider
&&
!
@
frozen
@
slider
.
slider
(
'option'
,
'max'
,
duration
)
@
slider
.
slider
(
'value'
,
currentTime
)
onSlide
:
(
event
,
ui
)
=>
@
frozen
=
true
@
updateTooltip
(
ui
.
value
)
$
(
@
).
trigger
(
'seek'
,
ui
.
value
)
onChange
:
(
event
,
ui
)
=>
@
updateTooltip
(
ui
.
value
)
onStop
:
(
event
,
ui
)
=>
@
frozen
=
true
$
(
@
).
trigger
(
'seek'
,
ui
.
value
)
setTimeout
(
=>
@
frozen
=
false
),
200
updateTooltip
:
(
value
)
->
@
handle
.
qtip
(
'option'
,
'content.text'
,
"
#{
Time
.
format
(
value
)
}
"
)
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_quality_control.coffee
0 → 100644
View file @
7c7e0950
class
@
VideoQualityControl
extends
Subview
initialize
:
->
@
quality
=
null
;
bind
:
->
@
$
(
'.quality_control'
).
click
@
toggleQuality
render
:
->
@
el
.
append
"""
<a href="#" class="quality_control" title="HD">HD</a>
"""
#"
onQualityChange
:
(
value
)
->
@
quality
=
value
if
@
quality
in
[
'hd720'
,
'hd1080'
,
'highres'
]
@
el
.
addClass
(
'active'
)
else
@
el
.
removeClass
(
'active'
)
toggleQuality
:
(
event
)
=>
event
.
preventDefault
()
if
@
quality
in
[
'hd720'
,
'hd1080'
,
'highres'
]
newQuality
=
'large'
else
newQuality
=
'hd720'
$
(
@
).
trigger
(
'changeQuality'
,
newQuality
)
\ No newline at end of file
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_speed_control.coffee
0 → 100644
View file @
7c7e0950
class
@
VideoSpeedControl
extends
Subview
bind
:
->
@
$
(
'.video_speeds a'
).
click
@
changeVideoSpeed
if
onTouchBasedDevice
()
@
$
(
'.speeds'
).
click
(
event
)
->
event
.
preventDefault
()
$
(
this
).
toggleClass
(
'open'
)
else
@
$
(
'.speeds'
).
mouseenter
->
$
(
this
).
addClass
(
'open'
)
@
$
(
'.speeds'
).
mouseleave
->
$
(
this
).
removeClass
(
'open'
)
@
$
(
'.speeds'
).
click
(
event
)
->
event
.
preventDefault
()
$
(
this
).
removeClass
(
'open'
)
render
:
->
@
el
.
prepend
"""
<div class="speeds">
<a href="#">
<h3>Speed</h3>
<p class="active"></p>
</a>
<ol class="video_speeds"></ol>
</div>
"""
$
.
each
@
speeds
,
(
index
,
speed
)
=>
link
=
$
(
'<a>'
).
attr
(
href
:
"#"
).
html
(
"
#{
speed
}
x"
)
@
$
(
'.video_speeds'
).
prepend
(
$
(
'<li>'
).
attr
(
'data-speed'
,
speed
).
html
(
link
))
@
setSpeed
(
@
currentSpeed
)
changeVideoSpeed
:
(
event
)
=>
event
.
preventDefault
()
unless
$
(
event
.
target
).
parent
().
hasClass
(
'active'
)
@
currentSpeed
=
$
(
event
.
target
).
parent
().
data
(
'speed'
)
$
(
@
).
trigger
'speedChange'
,
$
(
event
.
target
).
parent
().
data
(
'speed'
)
@
setSpeed
(
parseFloat
(
@
currentSpeed
).
toFixed
(
2
).
replace
/\.00$/
,
'.0'
)
setSpeed
:
(
speed
)
->
@
$
(
'.video_speeds li'
).
removeClass
(
'active'
)
@
$
(
".video_speeds li[data-speed='
#{
speed
}
']"
).
addClass
(
'active'
)
@
$
(
'.speeds p.active'
).
html
(
"
#{
speed
}
x"
)
common/lib/xmodule/xmodule/js/src/videoalpha/display/video_volume_control.coffee
0 → 100644
View file @
7c7e0950
class
@
VideoVolumeControl
extends
Subview
initialize
:
->
@
currentVolume
=
100
bind
:
->
@
$
(
'.volume'
).
mouseenter
->
$
(
this
).
addClass
(
'open'
)
@
$
(
'.volume'
).
mouseleave
->
$
(
this
).
removeClass
(
'open'
)
@
$
(
'.volume>a'
).
click
(
@
toggleMute
)
render
:
->
@
el
.
prepend
"""
<div class="volume">
<a href="#"></a>
<div class="volume-slider-container">
<div class="volume-slider"></div>
</div>
</div>
"""
#"
@
slider
=
@
$
(
'.volume-slider'
).
slider
orientation
:
"vertical"
range
:
"min"
min
:
0
max
:
100
value
:
100
change
:
@
onChange
slide
:
@
onChange
onChange
:
(
event
,
ui
)
=>
@
currentVolume
=
ui
.
value
$
(
@
).
trigger
'volumeChange'
,
@
currentVolume
@
$
(
'.volume'
).
toggleClass
'muted'
,
@
currentVolume
==
0
toggleMute
:
=>
if
@
currentVolume
>
0
@
previousVolume
=
@
currentVolume
@
slider
.
slider
'option'
,
'value'
,
0
else
@
slider
.
slider
'option'
,
'value'
,
@
previousVolume
common/lib/xmodule/xmodule/templates/videoalpha/default.yaml
0 → 100644
View file @
7c7e0950
---
metadata
:
display_name
:
default
data_dir
:
a_made_up_name
data
:
|
<videoalpha youtube="0.75:JMD_ifUUfsU,1.0:OEoXaMPEzfM,1.25:AKqURZnYqpk,1.50:DYpADpL7jAY"/>
children
:
[]
common/lib/xmodule/xmodule/videoalpha_module.py
0 → 100644
View file @
7c7e0950
import
json
import
logging
from
lxml
import
etree
from
pkg_resources
import
resource_string
,
resource_listdir
from
xmodule.x_module
import
XModule
from
xmodule.raw_module
import
RawDescriptor
from
xmodule.modulestore.mongo
import
MongoModuleStore
from
xmodule.modulestore.django
import
modulestore
from
xmodule.contentstore.content
import
StaticContent
import
datetime
import
time
import
datetime
import
time
log
=
logging
.
getLogger
(
__name__
)
class
VideoModule
(
XModule
):
video_time
=
0
icon_class
=
'video'
js
=
{
'coffee'
:
[
resource_string
(
__name__
,
'js/src/time.coffee'
),
resource_string
(
__name__
,
'js/src/videoalpha/display.coffee'
)]
+
[
resource_string
(
__name__
,
'js/src/videoalpha/display/'
+
filename
)
for
filename
in
sorted
(
resource_listdir
(
__name__
,
'js/src/videoalpha/display'
))
if
filename
.
endswith
(
'.coffee'
)]}
css
=
{
'scss'
:
[
resource_string
(
__name__
,
'css/videoalpha/display.scss'
)]}
js_module_name
=
"Video"
def
__init__
(
self
,
system
,
location
,
definition
,
descriptor
,
instance_state
=
None
,
shared_state
=
None
,
**
kwargs
):
XModule
.
__init__
(
self
,
system
,
location
,
definition
,
descriptor
,
instance_state
,
shared_state
,
**
kwargs
)
xmltree
=
etree
.
fromstring
(
self
.
definition
[
'data'
])
self
.
youtube
=
xmltree
.
get
(
'youtube'
)
self
.
position
=
0
self
.
show_captions
=
xmltree
.
get
(
'show_captions'
,
'true'
)
self
.
source
=
self
.
_get_source
(
xmltree
)
self
.
track
=
self
.
_get_track
(
xmltree
)
self
.
start_time
,
self
.
end_time
=
self
.
_get_timeframe
(
xmltree
)
if
instance_state
is
not
None
:
state
=
json
.
loads
(
instance_state
)
if
'position'
in
state
:
self
.
position
=
int
(
float
(
state
[
'position'
]))
def
_get_source
(
self
,
xmltree
):
# find the first valid source
return
self
.
_get_first_external
(
xmltree
,
'source'
)
def
_get_track
(
self
,
xmltree
):
# find the first valid track
return
self
.
_get_first_external
(
xmltree
,
'track'
)
def
_get_first_external
(
self
,
xmltree
,
tag
):
"""
Will return the first valid element
of the given tag.
'valid' means has a non-empty 'src' attribute
"""
result
=
None
for
element
in
xmltree
.
findall
(
tag
):
src
=
element
.
get
(
'src'
)
if
src
:
result
=
src
break
return
result
def
_get_timeframe
(
self
,
xmltree
):
""" Converts 'from' and 'to' parameters in video tag to seconds.
If there are no parameters, returns empty string. """
def
parse_time
(
s
):
"""Converts s in '12:34:45' format to seconds. If s is
None, returns empty string"""
if
s
is
None
:
return
''
else
:
x
=
time
.
strptime
(
s
,
'
%
H:
%
M:
%
S'
)
return
datetime
.
timedelta
(
hours
=
x
.
tm_hour
,
minutes
=
x
.
tm_min
,
seconds
=
x
.
tm_sec
)
.
total_seconds
()
return
parse_time
(
xmltree
.
get
(
'from'
)),
parse_time
(
xmltree
.
get
(
'to'
))
def
handle_ajax
(
self
,
dispatch
,
get
):
'''
Handle ajax calls to this video.
TODO (vshnayder): This is not being called right now, so the position
is not being saved.
'''
log
.
debug
(
u"GET {0}"
.
format
(
get
))
log
.
debug
(
u"DISPATCH {0}"
.
format
(
dispatch
))
if
dispatch
==
'goto_position'
:
self
.
position
=
int
(
float
(
get
[
'position'
]))
log
.
info
(
u"NEW POSITION {0}"
.
format
(
self
.
position
))
return
json
.
dumps
({
'success'
:
True
})
raise
Http404
()
def
get_progress
(
self
):
''' TODO (vshnayder): Get and save duration of youtube video, then return
fraction watched.
(Be careful to notice when video link changes and update)
For now, we have no way of knowing if the video has even been watched, so
just return None.
'''
return
None
def
get_instance_state
(
self
):
#log.debug(u"STATE POSITION {0}".format(self.position))
return
json
.
dumps
({
'position'
:
self
.
position
})
def
videoalpha_list
(
self
):
return
self
.
youtube
def
get_html
(
self
):
if
isinstance
(
modulestore
(),
MongoModuleStore
)
:
caption_asset_path
=
StaticContent
.
get_base_url_path_for_course_assets
(
self
.
location
)
+
'/subs_'
else
:
# VS[compat]
# cdodge: filesystem static content support.
caption_asset_path
=
"/static/{0}/subs/"
.
format
(
self
.
metadata
[
'data_dir'
])
return
self
.
system
.
render_template
(
'videoalpha.html'
,
{
'streams'
:
self
.
videoalpha_list
(),
'id'
:
self
.
location
.
html_id
(),
'position'
:
self
.
position
,
'source'
:
self
.
source
,
'track'
:
self
.
track
,
'display_name'
:
self
.
display_name
,
# TODO (cpennington): This won't work when we move to data that isn't on the filesystem
'data_dir'
:
self
.
metadata
[
'data_dir'
],
'caption_asset_path'
:
caption_asset_path
,
'show_captions'
:
self
.
show_captions
,
'start'
:
self
.
start_time
,
'end'
:
self
.
end_time
})
class
VideoAlphaDescriptor
(
RawDescriptor
):
module_class
=
VideoModule
stores_state
=
True
template_dir_name
=
"videoalpha"
lms/templates/videoalpha.html
0 → 100644
View file @
7c7e0950
% if display_name is not UNDEFINED and display_name is not None:
<h2>
${display_name}
</h2>
% endif
%if settings.MITX_FEATURES['STUB_VIDEO_FOR_TESTING']:
<div
id=
"stub_out_video_for_testing"
></div>
%else:
<div
id=
"video_${id}"
class=
"video"
data-streams=
"${streams}"
data-caption-data-dir=
"${data_dir}"
data-show-captions=
"${show_captions}"
data-start=
"${start}"
data-end=
"${end}"
data-caption-asset-path=
"${caption_asset_path}"
>
<div
class=
"tc-wrapper"
>
<article
class=
"video-wrapper"
>
<section
class=
"video-player"
>
<div
id=
"${id}"
></div>
</section>
<section
class=
"video-controls"
></section>
</article>
</div>
</div>
%endif
% if source:
<div
class=
"video-sources"
>
<p>
Download video
<a
href=
"${source}"
>
here
</a>
.
</p>
</div>
% endif
% if track:
<div
class=
"video-tracks"
>
<p>
Download subtitles
<a
href=
"${track}"
>
here
</a>
.
</p>
</div>
% endif
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