2015-05-17 19:02:57 -04:00
var FFZ = window . FrankerFaceZ ,
utils = require ( '../utils' ) ,
constants = require ( '../constants' ) ;
// --------------------
// Initialization
// --------------------
FFZ . prototype . setup _channel = function ( ) {
2015-07-04 17:06:36 -04:00
// Style Stuff!
this . log ( "Creating channel style element." ) ;
var s = this . _channel _style = document . createElement ( "style" ) ;
s . id = "ffz-channel-css" ;
document . head . appendChild ( s ) ;
2015-06-05 03:59:28 -04:00
// Settings stuff!
document . body . classList . toggle ( "ffz-hide-view-count" , ! this . settings . channel _views ) ;
2015-11-07 22:56:15 -05:00
document . body . classList . toggle ( 'ffz-theater-stats' , this . settings . theater _stats ) ;
2016-10-01 13:43:08 -04:00
var banner _hidden = this . settings . hide _channel _banner ;
banner _hidden = banner _hidden === 1 ? this . settings . channel _bar _bottom : banner _hidden > 0 ;
utils . toggle _cls ( 'ffz-hide-channel-banner' ) ( banner _hidden ) ;
utils . toggle _cls ( 'ffz-channel-bar-bottom' ) ( this . settings . channel _bar _bottom ) ;
2016-09-30 13:09:03 -04:00
utils . toggle _cls ( 'ffz-minimal-channel-title' ) ( this . settings . channel _title _top === 2 ) ;
utils . toggle _cls ( 'ffz-channel-title-top' ) ( this . settings . channel _title _top > 0 ) ;
utils . toggle _cls ( 'ffz-minimal-channel-bar' ) ( this . settings . channel _bar _collapse ) ;
2015-05-17 19:02:57 -04:00
2016-09-30 13:09:03 -04:00
this . log ( "Hooking the Ember Channel Index redesign." ) ;
this . update _views ( 'component:channel-redesign' , this . modify _channel _redesign ) ;
2016-10-01 13:43:08 -04:00
this . update _views ( 'component:channel-redesign/live' , this . modify _channel _live ) ;
2016-09-30 13:09:03 -04:00
this . log ( "Hooking the Ember Channel Index component." ) ;
if ( ! this . update _views ( 'component:legacy-channel' , this . modify _channel _index ) )
2015-05-17 19:02:57 -04:00
return ;
2016-07-13 02:06:50 -04:00
var f = this ,
2016-10-01 13:43:08 -04:00
Channel = utils . ember _lookup ( 'controller:channel' ) ;
2015-07-13 21:52:44 -04:00
if ( ! Channel )
2016-10-01 13:43:08 -04:00
return f . error ( "Unable to find the Ember Channel controller" ) ;
2015-06-05 03:59:28 -04:00
this . log ( "Hooking the Ember Channel controller." ) ;
2015-05-17 19:02:57 -04:00
Channel . reopen ( {
2016-08-12 14:25:19 -04:00
/ * i s E d i t a b l e : f u n c t i o n ( ) {
var channel _id = this . get ( 'content.id' ) ,
user = this . get ( 'login.userData' ) ;
if ( ! user || ! user . login )
return false ;
else if ( user . login === channel _id || user . is _admin || user . is _staff )
return true ;
// Okay, have we loaded this user's editor status? Try that.
if ( f . _editor _of )
return f . _editor _of . indexOf ( channel _id ) !== - 1 ;
var t = this ;
f . get _user _editor _of ( ) . then ( function ( result ) {
// Once editor status is loaded, if the user does have editor
// status for this channel, update this property.
if ( result . indexOf ( channel _id ) !== - 1 )
Ember . propertyDidChange ( t , 'isEditable' ) ;
} ) ;
return false ;
} . property ( 'content.id' , 'login.userData' , 'login.userData.login' ) , * /
2016-09-30 13:09:03 -04:00
/ * f f z U p d a t e U p t i m e : f u n c t i o n ( ) {
2015-06-05 03:59:28 -04:00
if ( f . _cindex )
f . _cindex . ffzUpdateUptime ( ) ;
2015-05-17 19:02:57 -04:00
2016-09-30 13:09:03 -04:00
} . observes ( "isLive" , "channel.id" ) , * /
2015-05-17 19:02:57 -04:00
2015-07-29 01:03:10 -04:00
ffzUpdateInfo : function ( ) {
if ( this . _ffz _update _timer )
clearTimeout ( this . _ffz _update _timer ) ;
2015-08-04 01:43:08 -04:00
2016-10-02 18:01:40 -04:00
if ( ! this . get ( 'channelModel.id' ) )
2015-07-29 01:03:10 -04:00
return ;
2015-08-04 01:43:08 -04:00
2016-07-13 02:06:50 -04:00
this . _ffz _update _timer = setTimeout ( this . ffzCheckUpdate . bind ( this ) , 55000 + ( Math . random ( ) * 10000 ) ) ;
2016-10-02 18:01:40 -04:00
} . observes ( "channelModel" ) ,
2015-08-04 01:43:08 -04:00
2015-07-29 01:03:10 -04:00
ffzCheckUpdate : function ( ) {
var t = this ,
2016-10-02 18:01:40 -04:00
channel _id = t . get ( 'channelModel.id' ) ;
2015-07-29 01:03:10 -04:00
2016-10-02 18:01:40 -04:00
channel _id && utils . api . get ( "streams/" + channel _id , { } , { version : 3 } )
2015-07-29 01:03:10 -04:00
. done ( function ( data ) {
if ( ! data || ! data . stream ) {
// If the stream is offline, clear its created_at time and set it to zero viewers.
2016-10-02 18:01:40 -04:00
t . set ( 'channelModel.stream.createdAt' , null ) ;
t . set ( 'channelModel.stream.viewers' , 0 ) ;
2015-07-29 01:03:10 -04:00
return ;
}
2016-10-02 18:01:40 -04:00
t . set ( 'channelModel.stream.createdAt' , utils . parse _date ( data . stream . created _at ) || null ) ;
t . set ( 'channelModel.stream.viewers' , data . stream . viewers || 0 ) ;
2015-07-29 01:03:10 -04:00
var game = data . stream . game || ( data . stream . channel && data . stream . channel . game ) ;
if ( game ) {
2016-10-02 18:01:40 -04:00
t . set ( 'channelModel.game' , game ) ;
2015-07-29 01:03:10 -04:00
}
2015-08-04 01:43:08 -04:00
2015-07-29 01:03:10 -04:00
if ( data . stream . channel ) {
if ( data . stream . channel . status )
2016-10-02 18:01:40 -04:00
t . set ( 'channelModel.status' , data . stream . channel . status ) ;
2015-08-04 01:43:08 -04:00
2015-07-29 01:03:10 -04:00
if ( data . stream . channel . views )
2016-10-02 18:01:40 -04:00
t . set ( 'channelModel.views' , data . stream . channel . views ) ;
2015-07-29 01:03:10 -04:00
2016-10-02 18:01:40 -04:00
if ( data . stream . channel . followers && t . get ( 'channelModel.followers.isFulfilled' ) )
t . set ( 'channelModel.followers.content.meta.total' , data . stream . channel . followers ) ;
2015-07-29 01:03:10 -04:00
}
} )
. always ( function ( data ) {
t . ffzUpdateInfo ( ) ;
} ) ;
} ,
2015-06-10 18:46:04 -04:00
ffzHostTarget : function ( ) {
2016-10-02 18:01:40 -04:00
var target = this . get ( 'channelModel.hostModeTarget' ) ,
2015-06-05 03:59:28 -04:00
name = target && target . get ( 'name' ) ,
2015-07-04 17:06:36 -04:00
id = target && target . get ( 'id' ) ,
2015-06-05 03:59:28 -04:00
display _name = target && target . get ( 'display_name' ) ;
2015-05-17 19:02:57 -04:00
2016-10-02 18:01:40 -04:00
/ * i f ( i d ! = = f . _ _ o l d _ h o s t _ t a r g e t ) {
2015-07-04 17:06:36 -04:00
if ( f . _ _old _host _target )
2015-10-26 12:13:28 -07:00
f . ws _send ( "unsub" , "channel." + f . _ _old _host _target ) ;
2015-07-04 17:06:36 -04:00
if ( id ) {
2015-10-26 12:13:28 -07:00
f . ws _send ( "sub" , "channel." + id ) ;
2015-07-04 17:06:36 -04:00
f . _ _old _host _target = id ;
} else
delete f . _ _old _host _target ;
2016-10-02 18:01:40 -04:00
} * /
2015-07-04 17:06:36 -04:00
2015-06-05 03:59:28 -04:00
if ( display _name )
FFZ . capitalization [ name ] = [ display _name , Date . now ( ) ] ;
2015-05-17 19:02:57 -04:00
2015-12-12 13:28:35 -05:00
if ( f . _chatv )
f . _chatv . ffzUpdateHost ( target ) ;
2015-07-04 17:06:36 -04:00
if ( f . settings . follow _buttons )
f . rebuild _following _ui ( ) ;
if ( f . settings . srl _races )
f . rebuild _race _ui ( ) ;
2016-10-02 18:01:40 -04:00
} . observes ( "channelModel.hostModeTarget" )
2015-06-05 03:59:28 -04:00
} ) ;
2015-08-04 01:43:08 -04:00
2015-07-29 01:03:10 -04:00
Channel . ffzUpdateInfo ( ) ;
2015-06-05 03:59:28 -04:00
}
2015-05-17 19:02:57 -04:00
2016-10-01 13:43:08 -04:00
FFZ . prototype . modify _channel _live = function ( view ) {
2016-09-30 13:09:03 -04:00
var f = this ;
utils . ember _reopen _view ( view , {
2016-10-01 13:43:08 -04:00
ffz _host : null ,
2016-09-30 13:09:03 -04:00
ffz _init : function ( ) {
var channel _id = this . get ( "channel.id" ) ,
el = this . get ( "element" ) ;
f . _cindex = this ;
2016-10-01 13:43:08 -04:00
f . ws _sub ( "channel." + channel _id ) ;
2016-09-30 13:09:03 -04:00
2016-10-01 13:43:08 -04:00
this . ffzUpdateAttributes ( ) ;
2016-09-30 13:09:03 -04:00
this . ffzFixTitle ( ) ;
this . ffzUpdateUptime ( ) ;
this . ffzUpdateChatters ( ) ;
this . ffzUpdateHostButton ( ) ;
this . ffzUpdatePlayerStats ( ) ;
if ( f . settings . auto _theater ) {
var player = f . players && f . players [ channel _id ] && f . players [ channel _id ] . get ( 'player' ) ;
if ( player )
player . setTheatre ( true ) ;
}
this . $ ( ) . on ( "click" , ".ffz-creative-tag-link" , function ( e ) {
if ( e . button !== 0 || e . altKey || e . ctrlKey || e . shiftKey || e . metaKey )
return ;
utils . ember _lookup ( "router:main" ) . transitionTo ( 'creative.hashtag.index' , this . getAttribute ( 'data-tag' ) ) ;
e . preventDefault ( ) ;
return false ;
} ) ;
} ,
2016-10-01 13:43:08 -04:00
ffzUpdateAttributes : function ( ) {
var channel _id = this . get ( "channel.id" ) ,
hosted _id = this . get ( "channel.hostModeTarget.id" ) ,
el = this . get ( "element" ) ;
2016-09-30 13:09:03 -04:00
2016-10-01 13:43:08 -04:00
if ( hosted _id !== this . ffz _host ) {
if ( this . ffz _host )
f . ws _unsub ( "channel." + this . ffz _host ) ;
2016-09-30 13:09:03 -04:00
2016-10-01 13:43:08 -04:00
if ( hosted _id )
f . ws _sub ( "channel." + hosted _id ) ;
this . ffz _host = hosted _id ;
}
2016-09-30 13:09:03 -04:00
2016-10-01 13:43:08 -04:00
el . classList . add ( 'ffz-channel' ) ;
el . classList . toggle ( 'ffz-host' , hosted _id || false ) ;
el . setAttribute ( 'data-channel' , channel _id || '' ) ;
el . setAttribute ( 'data-hosted' , hosted _id || '' ) ;
} . observes ( 'channel.id' , 'channel.hostModeTarget' ) ,
ffz _destroy : function ( ) {
var channel _id = this . get ( "channel.id" ) ,
el = this . get ( "element" ) ;
if ( channel _id )
f . ws _unsub ( "channel." + channel _id ) ;
if ( this . ffz _host ) {
f . ws _unsub ( "channel." + this . ffz _host ) ;
this . ffz _host = null ;
}
if ( f . _cindex === this )
f . _cindex = null ;
if ( this . _ffz _update _uptime )
clearTimeout ( this . _ffz _update _uptime ) ;
if ( this . _ffz _update _stats )
clearTimeout ( this . _ffz _update _stats ) ;
document . body . classList . remove ( 'ffz-small-player' ) ;
utils . update _css ( f . _channel _style , channel _id , null ) ;
} ,
2016-09-30 13:09:03 -04:00
ffzFixTitle : function ( ) {
if ( ! f . settings . stream _title )
return ;
var channel _id = this . get ( "channel.id" ) ,
status = this . get ( "channel.status" ) ,
game = this . get ( "channel.game" ) ,
tokens = f . tokenize _line ( channel _id , channel _id , status , true ) ;
if ( game === 'Creative' )
tokens = f . tokenize _ctags ( tokens ) ;
var el = this . $ ( ".cn-metabar__title .card__title" ) ;
el && el . html ( f . render _tokens ( tokens ) ) ;
} . observes ( 'channel.id' , 'channel.status' , 'channel.game' ) ,
ffzUpdateUptime : function ( ) {
if ( this . _ffz _update _uptime ) {
clearTimeout ( this . _ffz _update _uptime ) ;
delete this . _ffz _update _uptime ;
}
var container = this . get ( 'element' ) ;
if ( this . isDestroyed || ! container || ! f . settings . stream _uptime || ! this . get ( 'isLiveAccordingToKraken' ) )
return container && this . $ ( "#ffz-uptime-display" ) . remove ( ) ;
// Schedule an update.
this . _ffz _update _uptime = setTimeout ( this . ffzUpdateUptime . bind ( this ) , 1000 ) ;
// Determine when the channel last went live.
var online = this . get ( "channel.stream.createdAt" ) ,
now = Date . now ( ) - ( f . _ws _server _offset || 0 ) ;
var uptime = online && Math . floor ( ( now - online . getTime ( ) ) / 1000 ) || - 1 ;
if ( uptime < 0 )
return this . $ ( "#ffz-uptime-display" ) . remove ( ) ;
var el = container . querySelector ( '#ffz-uptime-display span' ) ;
if ( ! el ) {
var cont = container . querySelector ( '.cn-metabar__more' ) ;
if ( ! cont )
return ;
var stat = utils . createElement ( 'span' ) ,
figure = utils . createElement ( 'figure' , 'icon cn-metabar__icon' , constants . CLOCK + ' ' ) ,
balloon = utils . createElement ( 'div' , 'balloon balloon--tooltip balloon--down balloon--center' ) ,
balloon _wrapper = utils . createElement ( 'div' , 'balloon-wrapper' , figure ) ,
stat _wrapper = utils . createElement ( 'div' , 'cn-metabar__ffz flex__item mg-l-1' , balloon _wrapper ) ;
balloon _wrapper . appendChild ( stat ) ;
balloon _wrapper . appendChild ( balloon ) ;
stat _wrapper . id = 'ffz-uptime-display' ;
balloon . innerHTML = 'Stream Uptime <nobr>(since ' + online . toLocaleString ( ) + ')</nobr>' ;
var viewers = cont . querySelector ( ".cn-metabar__livecount" ) ;
if ( viewers )
cont . insertBefore ( stat _wrapper , viewers . nextSibling ) ;
else
cont . appendChild ( stat _wrapper ) ;
el = stat ;
}
el . innerHTML = utils . time _to _string ( uptime , false , false , false , f . settings . stream _uptime === 1 || f . settings . stream _uptime === 3 ) ;
} . observes ( 'channel.stream.createdAt' , 'isLiveAccordingToKraken' ) ,
ffzUpdatePlayerStats : function ( ) {
if ( this . _ffz _update _stats ) {
clearTimeout ( this . _ffz _update _stats ) ;
this . _ffz _update _stats = null ;
}
// Stop scheduling this so it can die.
if ( this . isDestroyed )
return ;
// Schedule an update.
if ( f . settings . player _stats )
this . _ffz _update _stats = setTimeout ( this . ffzUpdatePlayerStats . bind ( this ) , 1000 ) ;
var channel _id = this . get ( "channel.id" ) ,
container = this . get ( "element" ) ,
player _cont = f . players && f . players [ channel _id ] ,
player , stats ;
try {
player = player _cont && player _cont . get ( 'player' ) ;
stats = player && player . getVideoInfo ( ) ;
2016-10-01 13:43:08 -04:00
} catch ( err ) { } // This gets spammy if we try logging it.
2016-09-30 13:09:03 -04:00
if ( ! container || ! f . settings . player _stats || ! stats || ! stats . hls _latency _broadcaster )
return container && this . $ ( "#ffz-player-stats" ) . remove ( ) ;
var el = container . querySelector ( "#ffz-player-stats" ) ;
if ( ! el ) {
var cont = container . querySelector ( '.cn-metabar__more' ) ;
if ( ! cont )
return ;
var stat = utils . createElement ( 'span' ) ,
figure = utils . createElement ( 'figure' , 'icon cn-metabar__icon' , constants . GRAPH + ' ' ) ,
balloon = utils . createElement ( 'div' , 'balloon balloon--tooltip balloon--up balloon--center' ) ,
balloon _wrapper = utils . createElement ( 'div' , 'balloon-wrapper' , figure ) ;
el = utils . createElement ( 'div' , 'cn-metabar__ffz flex__item mg-l-1' , balloon _wrapper ) ;
balloon _wrapper . appendChild ( stat ) ;
balloon _wrapper . appendChild ( balloon ) ;
el . id = 'ffz-player-stats' ;
var viewers = cont . querySelector ( '#ffz-uptime-display' ) || cont . querySelector ( ".cn-metabar__livecount" ) ;
if ( viewers )
cont . insertBefore ( el , viewers . nextSibling ) ;
else
cont . appendChild ( el ) ;
}
var stat = el . querySelector ( 'span' ) ,
balloon = el . querySelector ( '.balloon' ) ;
var delay = Math . round ( stats . hls _latency _broadcaster / 10 ) / 100 ,
dropped = utils . number _commas ( stats . dropped _frames || 0 ) ,
bitrate ;
if ( stats . playback _bytes _per _second )
bitrate = Math . round ( stats . playback _bytes _per _second * 8 / 10.24 ) / 100 ;
else
bitrate = Math . round ( stats . current _bitrate * 100 ) / 100 ;
var is _old = delay > 180 ;
if ( is _old ) {
delay = Math . floor ( delay ) ;
stat . textContent = utils . time _to _string ( delay , true , delay > 172800 ) + ' old' ;
} else {
delay = delay . toString ( ) ;
var ind = delay . indexOf ( '.' ) ;
delay += ( ind === - 1 ? '.00' : ( ind >= delay . length - 2 ? '0' : '' ) ) + 's' ;
stat . textContent = delay ;
}
balloon . innerHTML = ( is _old ? 'Video Information<br>' +
'Broadcast ' + utils . time _to _string ( delay , true ) + ' Ago<br><br>' : 'Stream Latency<br>' ) +
'Video: ' + stats . vid _width + 'x' + stats . vid _height + 'p ' + stats . current _fps + ' fps<br>' +
'Playback Rate: ' + bitrate + ' Kbps<br>' +
'Dropped Frames: ' + dropped ;
} ,
ffzUpdateChatters : function ( ) {
var channel _id = this . get ( "channel.id" ) ,
room = f . rooms && f . rooms [ channel _id ] ,
container = this . get ( 'element' ) ;
if ( ! container || ! room || ! f . settings . chatter _count )
return container && this . $ ( "#ffz-chatter-display" ) . remove ( ) ;
var chatter _count = Object . keys ( room . room . get ( 'ffz_chatters' ) || { } ) . length ,
el = container . querySelector ( '#ffz-chatter-display span' ) ;
if ( ! el ) {
var cont = container . querySelector ( '.cn-metabar__more' ) ;
if ( ! cont )
return ;
var stat = utils . createElement ( 'span' ) ,
figure = utils . createElement ( 'figure' , 'icon cn-metabar__icon' , constants . ROOMS + ' ' ) ,
balloon = utils . createElement ( 'div' , 'balloon balloon--tooltip balloon--down balloon--center' , 'Currently in Chat' ) ,
balloon _wrapper = utils . createElement ( 'div' , 'balloon-wrapper' , figure ) ,
stat _wrapper = utils . createElement ( 'div' , 'cn-metabar__ffz flex__item mg-l-1' , balloon _wrapper ) ;
balloon _wrapper . appendChild ( stat ) ;
balloon _wrapper . appendChild ( balloon ) ;
stat _wrapper . id = 'ffz-chatter-display' ;
var viewers = cont . querySelector ( '#ffz-player-stats' ) || cont . querySelector ( '#ffz-uptime-display' ) || cont . querySelector ( ".cn-metabar__livecount" ) || cont . querySelector ( ".cn-metabar__viewcount" ) ;
if ( viewers )
cont . insertBefore ( stat _wrapper , viewers . nextSibling ) ;
else
cont . appendChild ( stat _wrapper ) ;
el = stat ;
}
el . innerHTML = utils . number _commas ( chatter _count ) ;
} . observes ( 'channel.id' ) ,
ffzUpdateHostButton : function ( ) {
var t = this ,
channel _id = this . get ( "channel.id" ) ,
hosted _id = this . get ( "channel.hostModeTarget.id" ) ,
user = f . get _user ( ) ,
room = user && f . rooms && f . rooms [ user . login ] && f . rooms [ user . login ] . room ,
now _hosting = room && room . ffz _host _target ,
hosts _left = room && room . ffz _hosts _left ,
el = this . get ( "element" ) ,
update _button = function ( channel , container , before ) {
if ( ! container )
return ;
var btn = container . querySelector ( '#ffz-ui-host-button' ) ;
if ( ! f . settings . stream _host _button || ! user || user . login === channel ) {
if ( btn )
btn . parentElement . removeChild ( btn ) ;
return ;
}
if ( ! btn ) {
btn = utils . createElement ( 'button' , 'button button--hollow mg-l-1' ) ,
btn . id = 'ffz-ui-host-button' ;
btn . addEventListener ( 'click' , t . ffzClickHost . bind ( t , channel !== channel _id ) ) ;
if ( before )
container . insertBefore ( btn , before ) ;
else
container . appendChild ( btn ) ;
jQuery ( btn ) . tipsy ( { html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
}
btn . classList . remove ( 'disabled' ) ;
btn . innerHTML = channel === now _hosting ? 'Unhost' : 'Host' ;
if ( now _hosting ) {
var name = FFZ . get _capitalization ( now _hosting ) ;
btn . title = 'You are now hosting ' + f . format _display _name ( name , now _hosting , true ) [ 0 ] + '.' ;
} else
btn . title = 'You are not hosting any channel.' ;
if ( typeof hosts _left === 'number' )
btn . title += ' You have ' + hosts _left + ' host command' + utils . pluralize ( hosts _left ) + ' remaining this half hour.' ;
} ;
if ( ! el )
return ;
this . set ( "ffz_host_updating" , false ) ;
if ( channel _id ) {
var container = el . querySelector ( '.cn-metabar__more' ) ,
share = container && container . querySelector ( '.js-share-box' ) ;
update _button ( channel _id , container , share ? share . parentElement : null ) ;
}
if ( hosted _id )
update _button ( hosted _id , el . querySelector ( '.cn-hosting--bottom' ) ) ;
} . observes ( 'channel.id' , 'channel.hostModeTarget.id' ) ,
ffzClickHost : function ( is _host , e ) {
var btn = e . target ,
target = this . get ( is _host ? 'channel.hostModeTarget.id' : 'channel.id' ) ,
user = f . get _user ( ) ,
room = user && f . rooms && f . rooms [ user . login ] && f . rooms [ user . login ] . room ,
now _hosting = room && room . ffz _host _target ;
if ( ! room || this . get ( 'ffz_host_updating' ) )
return ;
btn . classList . add ( 'disabled' ) ;
btn . title = 'Updating...' ;
this . set ( 'ffz_host_updating' , true ) ;
if ( now _hosting === target )
room . send ( '/unhost' , true ) ;
else
room . send ( '/host ' + target , true ) ;
}
2016-10-01 13:43:08 -04:00
} ) ;
}
FFZ . prototype . modify _channel _redesign = function ( view ) {
var f = this ;
utils . ember _reopen _view ( view , {
ffz _init : function ( ) {
// Twitch y u make me do dis
// (If this isn't the outer channel-redesign abort)
if ( this . parentView instanceof view )
return ;
var channel _id = this . get ( "channel.id" ) ,
el = this . get ( "element" ) ;
f . _credesign = this ;
this . ffzUpdateCoverHeight ( ) ;
el . setAttribute ( 'data-channel' , channel _id ) ;
el . classList . add ( 'ffz-channel-container' ) ;
} ,
ffz _destroy : function ( ) {
var channel _id = this . get ( "channel.id" ) ,
el = this . get ( "element" ) ;
el . setAttribute ( 'data-channel' , '' ) ;
el . classList . remove ( 'ffz-channel-container' ) ;
if ( f . _credesign === this )
f . _credesign = null ;
} ,
ffzUpdateCoverHeight : function ( ) {
var old _height = this . get ( 'channelCoverHeight' ) ,
setting = f . settings . hide _channel _banner ,
banner _hidden = setting === 1 ? f . settings . channel _bar _bottom : setting > 0 ,
new _height = banner _hidden ? 0 : 380 ;
this . set ( 'channelCoverHeight' , new _height ) ;
this . $ ( "#channel" ) . toggleClass ( 'ffz-bar-fixed' , this . get ( 'isFixed' ) ) ;
if ( old _height !== new _height )
this . scrollTo ( this . $scrollContainer . scrollTop ( ) + ( new _height - old _height ) ) ;
} . observes ( 'isFixed' )
2016-09-30 13:09:03 -04:00
} )
}
2016-07-13 02:06:50 -04:00
FFZ . prototype . modify _channel _index = function ( view ) {
var f = this ;
utils . ember _reopen _view ( view , {
ffz _init : function ( ) {
2016-09-30 13:09:03 -04:00
var channel _id = this . get ( 'model.id' ) ,
2015-07-04 17:06:36 -04:00
el = this . get ( 'element' ) ;
2015-06-05 03:59:28 -04:00
f . _cindex = this ;
2016-09-30 13:09:03 -04:00
f . ws _send ( "sub" , "channel." + channel _id ) ;
2015-07-04 17:06:36 -04:00
2016-09-30 13:09:03 -04:00
el . setAttribute ( 'data-channel' , channel _id ) ;
2015-07-04 17:06:36 -04:00
el . classList . add ( 'ffz-channel' ) ;
2015-06-05 03:59:28 -04:00
this . ffzFixTitle ( ) ;
this . ffzUpdateUptime ( ) ;
this . ffzUpdateChatters ( ) ;
2015-07-13 21:52:44 -04:00
this . ffzUpdateHostButton ( ) ;
2015-07-29 01:03:10 -04:00
this . ffzUpdatePlayerStats ( ) ;
2015-06-05 03:59:28 -04:00
2016-07-13 02:06:50 -04:00
// Listen to scrolling.
this . _ffz _scroller = this . ffzOnScroll . bind ( this ) ;
jQuery ( el ) . parents ( '.tse-scroll-content' ) . on ( 'scroll' , this . _ffz _scroller ) ;
2015-07-04 17:06:36 -04:00
var views = this . get ( 'element' ) . querySelector ( '.svg-glyph_views:not(.ffz-svg)' )
if ( views )
views . parentNode . classList . add ( 'twitch-channel-views' ) ;
if ( f . settings . follow _buttons )
f . rebuild _following _ui ( ) ;
if ( f . settings . srl _races )
f . rebuild _race _ui ( ) ;
2015-07-31 17:44:20 -04:00
if ( f . settings . auto _theater ) {
2016-09-30 13:09:03 -04:00
var player = f . players && f . players [ channel _id ] && f . players [ channel _id ] . get ( 'player' ) ;
2016-06-22 14:23:03 -04:00
if ( player )
player . setTheatre ( true ) ;
2015-07-31 17:44:20 -04:00
}
2016-05-06 02:23:12 -04:00
this . $ ( ) . on ( "click" , ".ffz-creative-tag-link" , function ( e ) {
if ( e . button !== 0 || e . altKey || e . ctrlKey || e . shiftKey || e . metaKey )
return ;
utils . ember _lookup ( "router:main" ) . transitionTo ( 'creative.hashtag.index' , this . getAttribute ( 'data-tag' ) ) ;
e . preventDefault ( ) ;
return false ;
} ) ;
2015-06-05 03:59:28 -04:00
} ,
2016-07-13 02:06:50 -04:00
ffz _destroy : function ( ) {
2016-09-30 13:09:03 -04:00
var channel _id = this . get ( 'model.id' ) ;
if ( channel _id )
f . ws _send ( "unsub" , "channel." + channel _id ) ;
2016-07-13 02:06:50 -04:00
this . get ( 'element' ) . setAttribute ( 'data-channel' , '' ) ;
2016-09-30 13:09:03 -04:00
if ( f . _cindex === this )
f . _cindex = null ;
2016-07-13 02:06:50 -04:00
if ( this . _ffz _update _uptime )
clearTimeout ( this . _ffz _update _uptime ) ;
if ( this . _ffz _update _stats )
clearTimeout ( this . _ffz _update _stats ) ;
if ( this . _ffz _scroller ) {
jQuery ( this . get ( 'element' ) ) . parents ( '.tse-scroll-content' ) . off ( 'scroll' , this . _ffz _scroller ) ;
this . _ffz _scroller = null ;
}
document . body . classList . remove ( 'ffz-small-player' ) ;
2016-09-30 13:09:03 -04:00
utils . update _css ( f . _channel _style , channel _id , null ) ;
2016-07-13 02:06:50 -04:00
} ,
ffzOnScroll : function ( event ) {
// When we scroll past the bottom of the player, do stuff!
var top = event && event . target && event . target . scrollTop ,
height = this . get ( 'layout.playerSize.1' ) ;
2016-07-13 02:31:26 -04:00
if ( ! top )
top = jQuery ( this . get ( 'element' ) ) . parents ( '.tse-scroll-content' ) . scrollTop ( ) ;
2016-07-13 02:06:50 -04:00
document . body . classList . toggle ( 'ffz-small-player' , f . settings . small _player && top >= height ) ;
} ,
2015-06-05 03:59:28 -04:00
ffzFixTitle : function ( ) {
if ( f . has _bttv || ! f . settings . stream _title )
return ;
2016-09-30 13:09:03 -04:00
var channel _id = this . get ( 'model.id' ) ,
status = this . get ( 'model.status' ) ,
game = this . get ( 'model.game' ) ,
2015-06-05 03:59:28 -04:00
2016-09-30 13:09:03 -04:00
tokens = f . tokenize _line ( channel _id , channel _id , status , true ) ;
2015-06-05 03:59:28 -04:00
2016-05-06 02:23:12 -04:00
if ( game === 'Creative' )
tokens = f . tokenize _ctags ( tokens ) ;
this . $ ( "#broadcast-meta .title" ) . html ( f . render _tokens ( tokens ) ) ;
2016-09-30 13:09:03 -04:00
status = this . get ( 'hostModeTarget.status' ) ;
channel _id = this . get ( 'hostModeTarget.id' ) ;
game = this . get ( 'hostModeTarget.game' ) ;
2016-05-06 02:23:12 -04:00
2016-09-30 13:09:03 -04:00
if ( channel _id ) {
tokens = f . tokenize _line ( channel _id , channel _id , status , true ) ;
2016-05-06 02:23:12 -04:00
if ( game === 'Creative' )
tokens = f . tokenize _ctags ( tokens ) ;
this . $ ( ".target-meta .target-title" ) . html ( f . render _tokens ( tokens ) ) ;
}
2015-06-05 03:59:28 -04:00
} ,
2015-05-17 19:02:57 -04:00
2015-07-13 21:52:44 -04:00
ffzUpdateHostButton : function ( ) {
2016-09-30 13:09:03 -04:00
var channel _id = this . get ( 'model.id' ) ,
hosted _id = this . get ( 'hostModeTarget.id' ) ,
2015-07-13 21:52:44 -04:00
user = f . get _user ( ) ,
room = user && f . rooms && f . rooms [ user . login ] && f . rooms [ user . login ] . room ,
now _hosting = room && room . ffz _host _target ,
2015-08-04 01:43:08 -04:00
hosts _left = room && room . ffz _hosts _left ,
2015-07-13 21:52:44 -04:00
el = this . get ( 'element' ) ;
this . set ( 'ffz_host_updating' , false ) ;
if ( channel _id ) {
var container = el && el . querySelector ( '.stats-and-actions .channel-actions' ) ,
btn = container && container . querySelector ( '#ffz-ui-host-button' ) ;
if ( ! container || ! f . settings . stream _host _button || ! user || user . login === channel _id ) {
if ( btn )
btn . parentElement . removeChild ( btn ) ;
} else {
if ( ! btn ) {
btn = document . createElement ( 'span' ) ;
btn . id = 'ffz-ui-host-button' ;
2016-06-22 14:23:03 -04:00
btn . className = 'button button--text' ;
2015-08-04 01:43:08 -04:00
2016-05-25 01:06:27 -04:00
btn . addEventListener ( 'click' , this . ffzClickHost . bind ( this , false ) ) ;
2015-08-04 01:43:08 -04:00
2015-07-31 17:44:20 -04:00
var before ;
try { before = container . querySelector ( ':scope > .theatre-button' ) ; }
catch ( err ) { before = undefined ; }
2015-08-04 01:43:08 -04:00
2015-07-13 21:52:44 -04:00
if ( before )
container . insertBefore ( btn , before ) ;
else
container . appendChild ( btn ) ;
2015-11-14 23:52:49 -05:00
jQuery ( btn ) . tipsy ( { html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
2015-07-13 21:52:44 -04:00
}
btn . classList . remove ( 'disabled' ) ;
btn . innerHTML = channel _id === now _hosting ? 'Unhost' : 'Host' ;
if ( now _hosting )
btn . title = 'You are now hosting ' + utils . sanitize ( FFZ . get _capitalization ( now _hosting ) ) + '.' ;
else
btn . title = 'You are not hosting any channel.' ;
if ( typeof hosts _left === "number" )
btn . title += ' You have ' + hosts _left + ' host command' + utils . pluralize ( hosts _left ) + ' remaining this half hour.' ;
}
}
2015-08-04 01:43:08 -04:00
2015-07-13 21:52:44 -04:00
if ( hosted _id ) {
var container = el && el . querySelector ( '#hostmode .channel-actions' ) ,
btn = container && container . querySelector ( '#ffz-ui-host-button' ) ;
if ( ! container || ! f . settings . stream _host _button || ! user || user . login === hosted _id ) {
if ( btn )
btn . parentElement . removeChild ( btn ) ;
} else {
if ( ! btn ) {
btn = document . createElement ( 'span' ) ;
btn . id = 'ffz-ui-host-button' ;
2016-06-22 14:23:03 -04:00
btn . className = 'button button--text' ;
2015-08-04 01:43:08 -04:00
2016-05-25 01:06:27 -04:00
btn . addEventListener ( 'click' , this . ffzClickHost . bind ( this , true ) ) ;
2015-08-04 01:43:08 -04:00
2015-07-31 17:44:20 -04:00
var before ;
try { before = container . querySelector ( ':scope > .theatre-button' ) ; }
catch ( err ) { before = undefined ; }
2015-07-13 21:52:44 -04:00
if ( before )
container . insertBefore ( btn , before ) ;
else
container . appendChild ( btn ) ;
2015-11-14 23:52:49 -05:00
jQuery ( btn ) . tipsy ( { html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
2015-07-13 21:52:44 -04:00
}
btn . classList . remove ( 'disabled' ) ;
btn . innerHTML = hosted _id === now _hosting ? 'Unhost' : 'Host' ;
if ( now _hosting )
btn . title = 'You are currently hosting ' + utils . sanitize ( FFZ . get _capitalization ( now _hosting ) ) + '. Click to ' + ( hosted _id === now _hosting ? 'unhost' : 'host' ) + ' this channel.' ;
else
btn . title = 'You are not currently hosting any channel. Click to host this channel.' ;
if ( typeof hosts _left === "number" )
btn . title += ' You have ' + hosts _left + ' host command' + utils . pluralize ( hosts _left ) + ' remaining this half hour.' ;
}
2015-08-04 01:43:08 -04:00
}
2015-07-13 21:52:44 -04:00
} ,
2015-08-04 01:43:08 -04:00
2016-05-25 01:06:27 -04:00
ffzClickHost : function ( is _host , e ) {
var btn = e . target ,
2016-09-30 13:09:03 -04:00
target = is _host ? this . get ( 'hostModeTarget.id' ) : this . get ( 'model.id' ) ,
2015-07-13 21:52:44 -04:00
user = f . get _user ( ) ,
room = user && f . rooms && f . rooms [ user . login ] && f . rooms [ user . login ] . room ,
now _hosting = room && room . ffz _host _target ;
2016-05-25 01:06:27 -04:00
if ( ! room || this . get ( 'ffz_host_updating' ) )
2015-07-13 21:52:44 -04:00
return ;
2016-05-25 01:06:27 -04:00
btn . classList . add ( 'disabled' ) ;
btn . title = 'Updating...' ;
2015-07-13 21:52:44 -04:00
2016-05-25 01:06:27 -04:00
this . set ( 'ffz_host_updating' , true ) ;
2015-07-13 21:52:44 -04:00
if ( now _hosting === target )
2015-10-17 18:05:44 -04:00
room . send ( "/unhost" , true ) ;
2015-07-13 21:52:44 -04:00
else
2015-10-17 18:05:44 -04:00
room . send ( "/host " + target , true ) ;
2015-07-13 21:52:44 -04:00
} ,
2015-06-05 03:59:28 -04:00
ffzUpdateChatters : function ( ) {
// Get the counts.
2016-09-30 13:09:03 -04:00
var room _id = this . get ( 'model.id' ) ,
2015-06-05 03:59:28 -04:00
room = f . rooms && f . rooms [ room _id ] ;
if ( ! room || ! f . settings . chatter _count ) {
var el = this . get ( 'element' ) . querySelector ( '#ffz-chatter-display' ) ;
el && el . parentElement . removeChild ( el ) ;
el = this . get ( 'element' ) . querySelector ( '#ffz-ffzchatter-display' ) ;
el && el . parentElement . removeChild ( el ) ;
2015-05-17 19:02:57 -04:00
return ;
2015-06-05 03:59:28 -04:00
}
2015-05-17 19:02:57 -04:00
2015-06-05 03:59:28 -04:00
var chatter _count = Object . keys ( room . room . get ( 'ffz_chatters' ) || { } ) . length ,
2015-07-04 17:06:36 -04:00
ffz _chatters = room . ffz _chatters || 0 ,
ffz _viewers = room . ffz _viewers || 0 ;
2015-05-17 19:02:57 -04:00
2015-06-05 03:59:28 -04:00
var el = this . get ( 'element' ) . querySelector ( '#ffz-chatter-display span' ) ;
if ( ! el ) {
var cont = this . get ( 'element' ) . querySelector ( '.stats-and-actions .channel-stats' ) ;
if ( ! cont )
return ;
2015-05-17 19:02:57 -04:00
2015-06-05 03:59:28 -04:00
var stat = document . createElement ( 'span' ) ;
stat . className = 'ffz stat' ;
stat . id = 'ffz-chatter-display' ;
2015-07-04 17:06:36 -04:00
stat . title = "Currently in Chat" ;
2015-05-17 19:02:57 -04:00
2015-06-05 03:59:28 -04:00
stat . innerHTML = constants . ROOMS + " " ;
el = document . createElement ( "span" ) ;
stat . appendChild ( el ) ;
2015-05-17 19:02:57 -04:00
2015-06-05 03:59:28 -04:00
var other = cont . querySelector ( "#ffz-ffzchatter-display" ) ;
if ( other )
cont . insertBefore ( stat , other ) ;
else
cont . appendChild ( stat ) ;
2015-05-17 19:02:57 -04:00
2015-11-14 23:52:49 -05:00
jQuery ( stat ) . tipsy ( { html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
2015-06-05 03:59:28 -04:00
}
2015-05-17 19:02:57 -04:00
2015-06-05 03:59:28 -04:00
el . innerHTML = utils . number _commas ( chatter _count ) ;
2015-05-17 19:02:57 -04:00
2015-07-04 17:06:36 -04:00
if ( ! ffz _chatters && ! ffz _viewers ) {
2015-06-05 03:59:28 -04:00
el = this . get ( 'element' ) . querySelector ( '#ffz-ffzchatter-display' ) ;
el && el . parentNode . removeChild ( el ) ;
return ;
}
2015-05-17 19:02:57 -04:00
2015-06-05 03:59:28 -04:00
el = this . get ( 'element' ) . querySelector ( '#ffz-ffzchatter-display span' ) ;
if ( ! el ) {
var cont = this . get ( 'element' ) . querySelector ( '.stats-and-actions .channel-stats' ) ;
if ( ! cont )
return ;
2015-05-17 19:02:57 -04:00
2015-06-05 03:59:28 -04:00
var stat = document . createElement ( 'span' ) ;
stat . className = 'ffz stat' ;
stat . id = 'ffz-ffzchatter-display' ;
2015-07-04 17:06:36 -04:00
stat . title = "Viewers (In Chat) with FrankerFaceZ" ;
2015-05-17 19:02:57 -04:00
2015-06-05 03:59:28 -04:00
stat . innerHTML = constants . ZREKNARF + " " ;
el = document . createElement ( "span" ) ;
stat . appendChild ( el ) ;
2015-05-17 19:02:57 -04:00
2015-06-05 03:59:28 -04:00
var other = cont . querySelector ( "#ffz-chatter-display" ) ;
if ( other )
cont . insertBefore ( stat , other . nextSibling ) ;
else
cont . appendChild ( stat ) ;
2015-05-17 19:02:57 -04:00
2015-11-14 23:52:49 -05:00
jQuery ( stat ) . tipsy ( { html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
2015-06-05 03:59:28 -04:00
}
2015-05-17 19:02:57 -04:00
2015-07-04 17:06:36 -04:00
el . innerHTML = utils . number _commas ( ffz _viewers ) + " (" + utils . number _commas ( ffz _chatters ) + ")" ;
2015-06-05 03:59:28 -04:00
} ,
2015-05-17 19:02:57 -04:00
2015-07-29 01:03:10 -04:00
ffzUpdatePlayerStats : function ( ) {
2016-07-13 02:06:50 -04:00
if ( this . _ffz _update _stats ) {
clearTimeout ( this . _ffz _update _stats ) ;
this . _ffz _update _stats = null ;
}
// Schedule an update.
if ( f . settings . player _stats )
this . _ffz _update _stats = setTimeout ( this . ffzUpdatePlayerStats . bind ( this ) , 1000 ) ;
2016-09-30 13:09:03 -04:00
var channel _id = this . get ( 'model.id' ) ,
hosted _id = this . get ( 'hostModeTarget.id' ) ,
2015-07-29 01:03:10 -04:00
el = this . get ( 'element' ) ;
if ( channel _id ) {
var container = el && el . querySelector ( '.stats-and-actions .channel-stats' ) ,
stat _el = container && container . querySelector ( '#ffz-ui-player-stats' ) ,
el = stat _el && stat _el . querySelector ( 'span' ) ,
2016-07-13 03:54:42 -04:00
je ,
2015-08-04 01:43:08 -04:00
2015-07-29 01:03:10 -04:00
player _cont = f . players && f . players [ channel _id ] ,
2015-11-01 17:28:19 -05:00
player = undefined , stats = undefined ;
try {
2016-03-23 19:28:22 -04:00
player = player _cont && player _cont . get && player _cont . get ( 'player' ) ;
2016-07-13 02:06:50 -04:00
stats = player && player . getVideoInfo ( ) ;
2015-11-01 17:28:19 -05:00
} catch ( err ) {
2016-07-13 02:06:50 -04:00
f . error ( "Channel ffzUpdatePlayerStats: player.getVideoInfo: " + err ) ;
2015-11-01 17:28:19 -05:00
}
2015-07-29 01:03:10 -04:00
2016-07-13 02:06:50 -04:00
if ( ! container || ! f . settings . player _stats || ! stats || ! stats . hls _latency _broadcaster || Number . isNaN ( stats . hls _latency _broadcaster ) ) {
2015-07-29 01:03:10 -04:00
if ( stat _el )
stat _el . parentElement . removeChild ( stat _el ) ;
} else {
if ( ! stat _el ) {
stat _el = document . createElement ( 'span' ) ;
stat _el . id = 'ffz-ui-player-stats' ;
2015-11-14 23:52:49 -05:00
stat _el . className = 'ffz stat' ;
2015-08-04 01:43:08 -04:00
2015-07-29 01:03:10 -04:00
stat _el . innerHTML = constants . GRAPH + " " ;
el = document . createElement ( 'span' ) ;
stat _el . appendChild ( el ) ;
2015-08-04 01:43:08 -04:00
2015-07-29 01:03:10 -04:00
var other = container . querySelector ( '#ffz-uptime-display' ) ;
if ( other )
container . insertBefore ( stat _el , other . nextSibling ) ;
else
container . appendChild ( stat _el ) ;
2015-11-14 23:52:49 -05:00
2016-07-13 03:54:42 -04:00
je = jQuery ( stat _el ) ;
je . hover (
function ( ) { je . data ( "hover" , true ) . tipsy ( "show" ) } ,
function ( ) { je . data ( "hover" , false ) . tipsy ( "hide" ) } )
. data ( "hover" , false )
. tipsy ( { trigger : 'manual' , html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
} else
je = jQuery ( stat _el ) ;
2015-08-04 01:43:08 -04:00
2016-07-13 02:06:50 -04:00
var delay = Math . round ( stats . hls _latency _broadcaster / 10 ) / 100 ,
2016-08-09 20:45:28 -04:00
dropped = utils . number _commas ( stats . dropped _frames || 0 ) ,
bitrate ;
if ( stats . playback _bytes _per _second )
2016-07-13 03:54:42 -04:00
bitrate = Math . round ( stats . playback _bytes _per _second * 8 / 10.24 ) / 100 ;
2016-08-09 20:45:28 -04:00
else
bitrate = Math . round ( stats . current _bitrate * 100 ) / 100 ;
2015-10-17 18:05:44 -04:00
if ( delay > 180 ) {
delay = Math . floor ( delay ) ;
2016-08-09 20:45:28 -04:00
stat _el . setAttribute ( 'original-title' , 'Video Information<br>Broadcast ' + utils . time _to _string ( delay , true ) + ' Ago<br><br>Video: ' + stats . vid _width + 'x' + stats . vid _height + 'p @ ' + stats . current _fps + '<br>Playback Rate: ' + bitrate + ' Kbps<br>Dropped Frames: ' + dropped ) ;
2015-10-17 18:05:44 -04:00
el . textContent = utils . time _to _string ( Math . floor ( delay ) , true , delay > 172800 ) + ' old' ;
} else {
2016-08-09 20:45:28 -04:00
stat _el . setAttribute ( 'original-title' , 'Stream Latency<br>Video: ' + stats . vid _width + 'x' + stats . vid _height + 'p @ ' + stats . current _fps + '<br>Playback Rate: ' + bitrate + ' Kbps<br>Dropped Frames: ' + dropped ) ;
2016-07-13 02:06:50 -04:00
delay = delay . toString ( ) ;
var ind = delay . indexOf ( '.' ) ;
if ( ind === - 1 )
2015-10-17 18:05:44 -04:00
delay = delay + '.00' ;
2016-07-13 02:06:50 -04:00
else if ( ind >= delay . length - 2 )
2015-10-17 18:05:44 -04:00
delay = delay + '0' ;
el . textContent = delay + 's' ;
}
2016-07-13 03:54:42 -04:00
if ( je . data ( "hover" ) )
je . tipsy ( "hide" ) . tipsy ( "show" ) ;
2015-07-29 01:03:10 -04:00
}
}
2015-08-04 01:43:08 -04:00
2015-07-29 01:03:10 -04:00
if ( hosted _id ) {
var container = el && el . querySelector ( '#hostmode .channel-stats' ) ,
stat _el = container && container . querySelector ( '#ffz-ui-player-stats' ) ,
el = stat _el && stat _el . querySelector ( 'span' ) ,
2016-07-13 03:54:42 -04:00
je ,
2015-08-04 01:43:08 -04:00
2015-07-29 01:03:10 -04:00
player _cont = f . players && f . players [ hosted _id ] ,
2015-11-01 17:28:19 -05:00
player = undefined , stats = undefined ;
try {
player = player _cont && player _cont . ffz _player ;
2016-07-13 03:54:42 -04:00
stats = player && player . getVideoInfo ( ) ;
2015-11-01 17:28:19 -05:00
} catch ( err ) {
2016-07-13 03:54:42 -04:00
f . error ( "Channel ffzUpdatePlayerStats: player.getVideoInfo: " + err ) ;
2015-11-01 17:28:19 -05:00
}
2015-07-29 01:03:10 -04:00
2016-07-13 02:06:50 -04:00
if ( ! container || ! f . settings . player _stats || ! stats || ! stats . hls _latency _broadcaster || Number . isNaN ( stats . hls _latency _broadcaster ) ) {
2015-07-29 01:03:10 -04:00
if ( stat _el )
stat _el . parentElement . removeChild ( stat _el ) ;
} else {
if ( ! stat _el ) {
stat _el = document . createElement ( 'span' ) ;
stat _el . id = 'ffz-ui-player-stats' ;
2015-11-14 23:52:49 -05:00
stat _el . className = 'ffz stat' ;
2015-08-04 01:43:08 -04:00
2015-07-29 01:03:10 -04:00
stat _el . innerHTML = constants . GRAPH + " " ;
el = document . createElement ( 'span' ) ;
stat _el . appendChild ( el ) ;
2015-08-04 01:43:08 -04:00
2015-07-29 01:03:10 -04:00
var other = container . querySelector ( '#ffz-uptime-display' ) ;
if ( other )
container . insertBefore ( stat _el , other . nextSibling ) ;
else
container . appendChild ( stat _el ) ;
2015-11-14 23:52:49 -05:00
2016-07-13 03:54:42 -04:00
je = jQuery ( stat _el ) ;
je . hover (
function ( ) { je . data ( "hover" , true ) . tipsy ( "show" ) } ,
function ( ) { je . data ( "hover" , false ) . tipsy ( "hide" ) } )
. data ( "hover" , false )
. tipsy ( { trigger : 'manual' , html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
} else
je = jQuery ( stat _el ) ;
2015-08-04 01:43:08 -04:00
2016-07-13 02:06:50 -04:00
var delay = Math . round ( stats . hls _latency _broadcaster / 10 ) / 100 ,
2016-08-09 20:45:28 -04:00
dropped = utils . number _commas ( stats . dropped _frames || 0 ) ,
bitrate ;
if ( stats . playback _bytes _per _second )
2016-07-13 03:54:42 -04:00
bitrate = Math . round ( stats . playback _bytes _per _second * 8 / 10.24 ) / 100 ;
2016-08-09 20:45:28 -04:00
else
bitrate = Math . round ( stats . current _bitrate * 100 ) / 100 ;
2015-10-17 18:05:44 -04:00
if ( delay > 180 ) {
delay = Math . floor ( delay ) ;
2016-08-09 20:45:28 -04:00
stat _el . setAttribute ( 'original-title' , 'Video Information<br>Broadcast ' + utils . time _to _string ( delay , true ) + ' Ago<br><br>Video: ' + stats . vid _width + 'x' + stats . vid _height + 'p @ ' + stats . current _fps + '<br>Playback Rate: ' + bitrate + ' Kbps<br>Dropped Frames: ' + dropped ) ;
2015-10-17 18:05:44 -04:00
el . textContent = utils . time _to _string ( Math . floor ( delay ) , true , delay > 172800 ) + ' old' ;
} else {
2016-08-09 20:45:28 -04:00
stat _el . setAttribute ( 'original-title' , 'Stream Latency<br>Video: ' + stats . vid _width + 'x' + stats . vid _height + 'p @ ' + stats . current _fps + '<br>Playback Rate: ' + bitrate + ' Kbps<br>Dropped Frames: ' + dropped ) ;
2016-07-13 02:06:50 -04:00
delay = delay . toString ( ) ;
var ind = delay . indexOf ( '.' ) ;
if ( ind === - 1 )
2015-10-17 18:05:44 -04:00
delay = delay + '.00' ;
2016-07-13 02:06:50 -04:00
else if ( ind >= delay . length - 2 )
2015-10-17 18:05:44 -04:00
delay = delay + '0' ;
el . textContent = delay + 's' ;
}
2016-07-13 03:54:42 -04:00
if ( je . data ( "hover" ) )
je . tipsy ( "hide" ) . tipsy ( "show" ) ;
2015-07-29 01:03:10 -04:00
}
2015-08-04 01:43:08 -04:00
}
2015-07-29 01:03:10 -04:00
} ,
2015-06-05 03:59:28 -04:00
ffzUpdateUptime : function ( ) {
if ( this . _ffz _update _uptime ) {
clearTimeout ( this . _ffz _update _uptime ) ;
delete this . _ffz _update _uptime ;
}
2016-09-30 13:09:03 -04:00
var controller = utils . ember _lookup ( 'controller:channel' ) ;
if ( ! f . settings . stream _uptime || ! ( controller && controller . get ( 'isLiveAccordingToKraken' ) ) ) {
2015-06-05 03:59:28 -04:00
var el = this . get ( 'element' ) . querySelector ( '#ffz-uptime-display' ) ;
if ( el )
el . parentElement . removeChild ( el ) ;
return ;
2015-05-17 19:02:57 -04:00
}
2015-06-05 03:59:28 -04:00
// Schedule an update.
this . _ffz _update _uptime = setTimeout ( this . ffzUpdateUptime . bind ( this ) , 1000 ) ;
// Determine when the channel last went live.
2016-09-30 13:09:03 -04:00
var online = this . get ( "model.stream.created_at" ) ,
2015-11-19 02:45:56 -05:00
now = Date . now ( ) - ( f . _ws _server _offset || 0 ) ;
2015-07-29 01:03:10 -04:00
online = online && utils . parse _date ( online ) ;
2015-06-05 03:59:28 -04:00
2015-11-19 02:45:56 -05:00
var uptime = online && Math . floor ( ( now - online . getTime ( ) ) / 1000 ) || - 1 ;
2015-07-29 01:03:10 -04:00
if ( uptime < 0 ) {
var el = this . get ( 'element' ) . querySelector ( '#ffz-uptime-display' ) ;
if ( el )
el . parentElement . removeChild ( el ) ;
2015-06-05 03:59:28 -04:00
return ;
2015-07-29 01:03:10 -04:00
}
2015-06-05 03:59:28 -04:00
var el = this . get ( 'element' ) . querySelector ( '#ffz-uptime-display span' ) ;
if ( ! el ) {
var cont = this . get ( 'element' ) . querySelector ( '.stats-and-actions .channel-stats' ) ;
if ( ! cont )
return ;
var stat = document . createElement ( 'span' ) ;
stat . className = 'ffz stat' ;
stat . id = 'ffz-uptime-display' ;
stat . title = "Stream Uptime <nobr>(since " + online . toLocaleString ( ) + ")</nobr>" ;
stat . innerHTML = constants . CLOCK + " " ;
el = document . createElement ( "span" ) ;
stat . appendChild ( el ) ;
var viewers = cont . querySelector ( ".live-count" ) ;
if ( viewers )
cont . insertBefore ( stat , viewers . nextSibling ) ;
else {
try {
viewers = cont . querySelector ( "script:nth-child(0n+2)" ) ;
cont . insertBefore ( stat , viewers . nextSibling ) ;
} catch ( err ) {
cont . insertBefore ( stat , cont . childNodes [ 0 ] ) ;
}
}
2015-11-14 23:52:49 -05:00
jQuery ( stat ) . tipsy ( { html : true , gravity : utils . tooltip _placement ( constants . TOOLTIP _DISTANCE , 'n' ) } ) ;
2015-06-05 03:59:28 -04:00
}
2015-10-17 18:05:44 -04:00
el . innerHTML = utils . time _to _string ( uptime , false , false , false , f . settings . stream _uptime === 1 || f . settings . stream _uptime === 3 ) ;
2015-05-17 19:02:57 -04:00
}
2015-06-05 03:59:28 -04:00
} ) ;
}
2015-05-17 19:02:57 -04:00
2015-06-05 03:59:28 -04:00
// ---------------
// Settings
// ---------------
2015-07-31 17:44:20 -04:00
FFZ . settings _info . auto _theater = {
type : "boolean" ,
value : false ,
category : "Appearance" ,
no _mobile : true ,
no _bttv : true ,
name : "Automatic Theater Mode" ,
help : "Automatically enter theater mode when opening a channel."
} ;
2016-07-13 02:06:50 -04:00
FFZ . settings _info . small _player = {
type : "boolean" ,
value : false ,
no _mobile : true ,
no _bttv : true ,
category : "Appearance" ,
name : "Mini-Player on Scroll" ,
help : "When you scroll down on the page, shrink the player and put it in the upper right corner so you can still watch." ,
on _update : function ( val ) {
if ( ! val )
return document . body . classList . remove ( 'ffz-small-player' ) ;
else if ( this . _vodc )
this . _vodc . ffzOnScroll ( ) ;
else if ( this . _cindex )
this . _cindex . ffzOnScroll ( ) ;
}
}
2015-06-05 03:59:28 -04:00
FFZ . settings _info . chatter _count = {
type : "boolean" ,
value : false ,
2015-07-18 21:10:27 -04:00
no _mobile : true ,
2015-06-05 03:59:28 -04:00
category : "Channel Metadata" ,
name : "Chatter Count" ,
help : "Display the current number of users connected to chat beneath the channel." ,
on _update : function ( val ) {
if ( this . _cindex )
this . _cindex . ffzUpdateChatters ( ) ;
if ( ! val || ! this . rooms )
return ;
// Refresh the data.
for ( var room _id in this . rooms )
this . rooms . hasOwnProperty ( room _id ) && this . rooms [ room _id ] . room && this . rooms [ room _id ] . room . ffzInitChatterCount ( ) ;
}
} ;
FFZ . settings _info . channel _views = {
type : "boolean" ,
value : true ,
2015-07-18 21:10:27 -04:00
no _mobile : true ,
2015-06-05 03:59:28 -04:00
category : "Channel Metadata" ,
name : "Channel Views" ,
help : 'Display the number of times the channel has been viewed beneath the stream.' ,
on _update : function ( val ) {
document . body . classList . toggle ( "ffz-hide-view-count" , ! val ) ;
}
} ;
2015-07-13 21:52:44 -04:00
FFZ . settings _info . hosted _channels = {
type : "boolean" ,
value : true ,
2015-07-18 21:10:27 -04:00
no _mobile : true ,
2015-07-13 21:52:44 -04:00
category : "Channel Metadata" ,
name : "Channel Hosting" ,
help : "Display other channels that have been featured by the current channel." ,
on _update : function ( val ) {
var cb = document . querySelector ( 'input.ffz-setting-hosted-channels' ) ;
if ( cb )
cb . checked = val ;
2015-08-04 01:43:08 -04:00
2016-10-01 13:43:08 -04:00
var Chat = utils . ember _lookup ( 'controller:chat' ) ,
room = Chat && Chat . get ( 'currentChannelRoom' ) ;
2015-08-04 01:43:08 -04:00
2016-10-01 13:43:08 -04:00
if ( room )
room . setHostMode ( {
hostTarget : room . ffz _host _target ,
recentlyJoined : true
} ) ;
2015-07-13 21:52:44 -04:00
}
} ;
FFZ . settings _info . stream _host _button = {
type : "boolean" ,
value : true ,
2015-07-18 21:10:27 -04:00
no _mobile : true ,
2015-07-13 21:52:44 -04:00
category : "Channel Metadata" ,
name : "Host This Channel Button" ,
help : "Display a button underneath streams that make it easy to host them with your own channel." ,
on _update : function ( val ) {
if ( this . _cindex )
this . _cindex . ffzUpdateHostButton ( ) ;
}
} ;
2015-06-05 03:59:28 -04:00
FFZ . settings _info . stream _uptime = {
2015-10-17 18:05:44 -04:00
type : "select" ,
options : {
0 : "Disabled" ,
1 : "Enabled" ,
2 : "Enabled (with Seconds)" ,
3 : "Enabled (Channel Only)" ,
4 : "Enabled (Channel Only with Seconds)"
} ,
value : 1 ,
2016-10-01 14:11:49 -04:00
process _value : utils . process _int ( 1 , 0 , 2 ) ,
2015-06-05 03:59:28 -04:00
2015-10-17 18:05:44 -04:00
no _mobile : true ,
2015-06-05 03:59:28 -04:00
category : "Channel Metadata" ,
name : "Stream Uptime" ,
help : 'Display the stream uptime under a channel by the viewer count.' ,
on _update : function ( val ) {
if ( this . _cindex )
this . _cindex . ffzUpdateUptime ( ) ;
}
} ;
FFZ . settings _info . stream _title = {
type : "boolean" ,
value : true ,
no _bttv : true ,
2015-07-18 21:10:27 -04:00
no _mobile : true ,
2015-06-05 03:59:28 -04:00
category : "Channel Metadata" ,
name : "Title Links" ,
help : "Make links in stream titles clickable." ,
on _update : function ( val ) {
if ( this . _cindex )
this . _cindex . ffzFixTitle ( ) ;
}
2015-10-17 18:05:44 -04:00
} ;
2016-09-30 13:09:03 -04:00
FFZ . settings _info . channel _bar _bottom = {
type : "boolean" ,
value : false ,
no _bttv : true ,
no _mobile : true ,
category : "Appearance" ,
name : "Channel Bar on Bottom" ,
help : "Hide the profile banner and position the channel bar at the bottom of the screen." ,
on _update : function ( val ) {
if ( this . has _bttv )
return ;
2016-10-01 13:43:08 -04:00
var banner _hidden = this . settings . hide _channel _banner ;
banner _hidden = banner _hidden === 1 ? val : banner _hidden > 0 ;
2016-09-30 13:09:03 -04:00
utils . toggle _cls ( 'ffz-channel-bar-bottom' ) ( val ) ;
2016-10-01 13:43:08 -04:00
utils . toggle _cls ( 'ffz-hide-channel-banner' ) ( banner _hidden ) ;
if ( this . _credesign )
this . _credesign . ffzUpdateCoverHeight ( ) ;
2016-09-30 13:09:03 -04:00
var Layout = utils . ember _lookup ( 'service:layout' ) ;
if ( Layout )
2016-10-02 18:01:40 -04:00
Ember . propertyDidChange ( Layout , 'ffzExtraHeight' ) ;
2016-09-30 13:09:03 -04:00
}
}
2016-10-01 13:43:08 -04:00
FFZ . settings _info . hide _channel _banner = {
type : "select" ,
options : {
0 : "Never" ,
1 : "When Channel Bar is on Bottom" ,
2 : "Always"
} ,
value : 1 ,
process _value : utils . process _int ( 1 ) ,
no _bttv : true ,
no _mobile : true ,
category : "Appearance" ,
name : "Hide Channel Banner" ,
help : "Hide the banner at the top of channel pages." ,
on _update : function ( val ) {
if ( this . has _bttv )
return ;
var is _hidden = val === 1 ? this . settings . channel _bar _bottom : val > 0 ;
utils . toggle _cls ( 'ffz-hide-channel-banner' ) ( is _hidden ) ;
if ( this . _credesign )
this . _credesign . ffzUpdateCoverHeight ( ) ;
var Layout = utils . ember _lookup ( 'service:layout' ) ;
if ( Layout )
2016-10-02 18:01:40 -04:00
Ember . propertyDidChange ( Layout , 'ffzExtraHeight' ) ;
2016-10-01 13:43:08 -04:00
}
}
2016-09-30 13:09:03 -04:00
FFZ . settings _info . channel _bar _collapse = {
type : "boolean" ,
value : false ,
no _bttv : true ,
no _mobile : true ,
category : "Appearance" ,
name : "Minimal Channel Bar" ,
help : "Slide the channel bar mostly out of view when it's not being used." ,
on _update : function ( val ) {
if ( this . has _bttv )
return ;
utils . toggle _cls ( 'ffz-minimal-channel-bar' ) ( val ) ;
var Layout = utils . ember _lookup ( 'service:layout' ) ;
if ( Layout )
2016-10-02 18:01:40 -04:00
Ember . propertyDidChange ( Layout , 'ffzExtraHeight' ) ;
2016-09-30 13:09:03 -04:00
}
}
FFZ . settings _info . channel _title _top = {
type : "select" ,
options : {
0 : "Disabled" ,
1 : "On Top" ,
2 : "On Top, Minimal"
} ,
value : 0 ,
2016-10-01 14:11:49 -04:00
process _value : utils . process _int ( 0 ) ,
2016-09-30 13:09:03 -04:00
no _bttv : true ,
no _mobile : true ,
category : "Appearance" ,
name : "Channel Title on Top" ,
help : "Display the channel title and game above the player rather than below." ,
on _update : function ( val ) {
if ( this . has _bttv )
return ;
document . body . classList . toggle ( 'ffz-minimal-channel-title' , val === 2 ) ;
document . body . classList . toggle ( 'ffz-channel-title-top' , val > 0 ) ;
var Layout = utils . ember _lookup ( 'service:layout' ) ;
if ( Layout )
2016-10-02 18:01:40 -04:00
Ember . propertyDidChange ( Layout , 'ffzExtraHeight' ) ;
2016-09-30 13:09:03 -04:00
}
}
2015-11-07 22:56:15 -05:00
FFZ . settings _info . theater _stats = {
type : "boolean" ,
value : true ,
no _mobile : true ,
category : "Channel Metadata" ,
name : "Display on Theater Mode Hover" ,
help : "Show the channel metadata and actions over the video player in theater mode when you hover it with your mouse." ,
on _update : function ( val ) {
document . body . classList . toggle ( 'ffz-theater-stats' , val ) ;
}
} ;
2015-10-17 18:05:44 -04:00
FFZ . basic _settings . channel _info = {
type : "select" ,
options : {
0 : "Disabled" ,
1 : "Enabled" ,
2 : "Enabled (with Seconds)" ,
3 : "Enabled (Channel Only)" ,
4 : "Enabled (Channel Only with Seconds)"
} ,
category : "General" ,
name : "Stream Uptime" ,
help : "Display the current stream's uptime under the player." ,
get : function ( ) {
return this . settings . stream _uptime ;
} ,
set : function ( val ) {
if ( typeof val === 'string' )
val = parseInt ( val || "0" ) ;
this . settings . set ( 'stream_uptime' , val ) ;
}
}