Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Predefined player parameters #257

Merged
merged 7 commits into from
Nov 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/app/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import update from "root/config/update.json";
import themes from "root/config/themes.json";
import langs from "root/config/langs.json";
import livestreamer from "root/config/livestreamer.json";
import players from "root/config/players.json";
import twitch from "root/config/twitch.json";
import notification from "root/config/notification.json";
import chat from "root/config/chat.json";
Expand All @@ -20,6 +21,7 @@ export {
themes,
langs,
livestreamer,
players,
twitch,
notification,
chat
Expand Down
38 changes: 37 additions & 1 deletion src/app/controllers/SettingsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,25 @@ import {
Controller
} from "Ember";
import {
players,
langs,
themes
} from "config";
import RetryTransitionMixin from "mixins/RetryTransitionMixin";
import { playerSubstitutions } from "models/LivestreamerParameters";
import qualities from "models/LivestreamerQualities";
import Settings from "models/localstorage/Settings";
import platform from "utils/node/platform";
import platform, {
platform as platformName
} from "utils/node/platform";


const { alias, equal } = computed;
const { service } = inject;
const { themes: themesList } = themes;

const kPlayers = Object.keys( players );


function settingsAttrMeta( attr, prop ) {
return computed(function() {
Expand Down Expand Up @@ -57,6 +62,37 @@ export default Controller.extend( RetryTransitionMixin, {
retryOpenMin : settingsAttrMeta( "retry_open", "min" ),
retryOpenMax : settingsAttrMeta( "retry_open", "max" ),

playerPresets: computed(function() {
let presetList = kPlayers
.filter(function( key ) {
return players[ key ][ "exec" ][ platformName ]
&& players[ key ][ "disabled" ] !== true;
})
.map(function( key ) {
return {
id: key,
label: players[ key ][ "name" ]
};
});

presetList.unshift({
id : "default",
label: "No preset"
});

return presetList;
}),

playerPresetFields: computed(function() {
return kPlayers.reduce(function( list, preset ) {
list.push( ...players[ preset ].params.map(function( param ) {
param.preset = preset;
return param;
}) );
return list;
}, [] );
}),


substitutionsPlayer: playerSubstitutions,
substitutionsChatCustom: alias( "chat.substitutionsCustom" ),
Expand Down
36 changes: 35 additions & 1 deletion src/app/initializers/localstorage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { langs } from "config";
import {
players,
langs
} from "config";
import qualities from "models/LivestreamerQualities";


Expand Down Expand Up @@ -39,6 +42,37 @@ function upgradeSettings() {
settings.gui_homepage = "/user/followedStreams";
}

// translate old player data into the player presets format
if ( typeof settings.player === "string" ) {
settings.player = {
"default": {
"exec": settings[ "player" ] || "",
"args": settings[ "player_params" ] || ""
}
};
delete settings[ "player_params" ];
}

// make sure that default player params are set/updated once a new one gets added
if ( typeof settings.player === "object" ) {
Object.keys( players ).forEach(function( name ) {
if ( !settings.player.hasOwnProperty( name ) ) {
settings.player[ name ] = {
"exec": "",
"args": "",
"params": {}
};
}
let playerParams = settings.player[ name ].params;
// iterate player preset params
players[ name ].params.forEach(function( param ) {
// don't overwrite already existing values
if ( playerParams.hasOwnProperty( param.name ) ) { return; }
playerParams[ param.name ] = param.default;
});
});
}

var renamedProps = {
"gui_flagsvisible": "stream_show_flag",
"gui_gamevisible" : "stream_show_info",
Expand Down
29 changes: 22 additions & 7 deletions src/app/models/Livestreamer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import {
import {
attr,
belongsTo,
PromiseObject,
Model
} from "EmberData";
import { parameters } from "models/LivestreamerParameters";
import {
getPlayerExec,
getPlayerParams
} from "models/LivestreamerPlayerParameters";
import qualities from "models/LivestreamerQualities";
import Parameter from "utils/Parameter";
import { twitch } from "config";
Expand Down Expand Up @@ -71,13 +76,23 @@ export default Model.extend({
this.kill();
}),

parameters: computed(function() {
return Parameter.getParameters(
this,
parameters,
get( this, "settings.advanced" )
);
}).volatile(),
getParameters() {
// wait for all parameter promises to resolve first
return Promise.all([
get( this, "playerExec.promise" )
])
.then( Parameter.getParameters.bind( null, this, parameters ) );
},

playerExec: computed( "settings.player", "settings.player_preset", function() {
return PromiseObject.create({
promise: getPlayerExec( get( this, "settings" ) )
});
}),

playerParams: computed( "settings.player", "settings.player_preset", function() {
return getPlayerParams( get( this, "settings" ) );
}),

streamquality: computed( "quality", "settings.quality_presets", function() {
let quality = get( this, "quality" );
Expand Down
6 changes: 3 additions & 3 deletions src/app/models/LivestreamerParameters.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ export const parameters = [
new Parameter(
"--player",
null,
"settings.player",
"playerExec.exec",
playerSubstitutions
),
new Parameter(
"--player-args",
[ "settings.advanced", "settings.player" ],
"settings.playerParamsCorrected",
null,
"playerParams",
playerSubstitutions
),
new Parameter(
Expand Down
96 changes: 96 additions & 0 deletions src/app/models/LivestreamerPlayerParameters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { get } from "Ember";
import { players } from "config";
import whichFallback from "utils/node/fs/whichFallback";


/**
* @param {Settings} settings
* @returns {Object[]}
*/
function getPlayerData( settings ) {
let player = get( settings, "player" );
let preset = get( settings, "player_preset" );

if ( !preset || !player.hasOwnProperty( preset ) ) {
preset = "default";
}

return [
// player settings
get( player, preset ) || {},
// player preset data
get( players, preset ) || {}
];
}


/**
* Resolve the player executable path and use fallbacks
* @param {Settings} settings
* @returns {Promise.<Object>}
*/
export function getPlayerExec( settings ) {
let [ playerSettings, playerPreset ] = getPlayerData( settings );
let exec = get( playerSettings, "exec" ) || get( playerPreset, "exec" ) || "";

// no player path or preset was selected
if ( exec === "" ) {
return Promise.resolve();
}

return whichFallback( exec, playerPreset.fallback )
.then(function( exec ) {
// return an object with an exec property for the EmberData.PromiseObject
return { exec };
})
.catch(function() {
return Promise.reject( new Error( "Unable to find player executable" ) );
});
}


/**
* Build the player parameter string out of the preset and custom params
* @param {Settings} settings
* @returns {String}
*/
export function getPlayerParams( settings ) {
let [ playerData, { params: playerPresetParams } ] = getPlayerData( settings );

let args = get( playerData, "args" ) || "";
let params = get( playerData, "params" ) || {};

// get the content of an ObjectBuffer
if ( params.getContent instanceof Function ) {
params = params.getContent();
}

// build the parameter preset string
let paramlist = Object.keys( params )
.map(function( param ) {
let paramObj = playerPresetParams.findBy( "name", param );
if ( !paramObj ) { return ""; }

switch ( paramObj.type ) {
case "boolean":
return params[ param ]
? paramObj.args
: null;
default:
return null;
}
})
.filter(function( item ) {
return item !== null;
});

// combine preset and custom params
let res = [ ...paramlist, args ].join( " " );

// fix missing {filename} variable
if ( res && res.indexOf( "{filename}" ) === -1 ) {
res = `${res} {filename}`;
}

return res;
}
48 changes: 37 additions & 11 deletions src/app/models/localstorage/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import {
attr,
Model
} from "EmberData";
import { langs } from "config";
import {
players,
langs
} from "config";
import qualities from "models/LivestreamerQualities";
import { isWin } from "utils/node/platform";

Expand All @@ -22,6 +25,34 @@ function defaultQualityPresets() {
}, {} );
}

function defaultPlayerData() {
return Object.keys( players )
.map(function( player ) {
let params = players[ player ][ "params" ]
.reduce(function( obj, param ) {
obj[ param.name ] = param.default;
return obj;
}, {} );

return {
key: player,
exec: "",
args: "",
params: params
};
})
.reduce( function( obj, player ) {
obj[ player.key ] = player;
delete player.key;
return obj;
}, {
"default": {
exec: "",
args: ""
}
} );
}

function defaultLangFilterValue() {
return langCodes.reduce(function( obj, code ) {
if ( !langs[ code ].disabled ) {
Expand All @@ -32,15 +63,18 @@ function defaultLangFilterValue() {
}


/**
* @class Settings
*/
export default Model.extend({
advanced : attr( "boolean", { defaultValue: false } ),
livestreamer : attr( "string", { defaultValue: "" } ),
livestreamer_params : attr( "string", { defaultValue: "" } ),
livestreamer_oauth : attr( "boolean", { defaultValue: true } ),
quality : attr( "string", { defaultValue: "source" } ),
quality_presets : attr( "", { defaultValue: defaultQualityPresets } ),
player : attr( "string", { defaultValue: "" } ),
player_params : attr( "string", { defaultValue: "" } ),
player : attr( "", { defaultValue: defaultPlayerData } ),
player_preset : attr( "string", { defaultValue: "default" } ),
player_passthrough : attr( "string", { defaultValue: "http" } ),
player_reconnect : attr( "boolean", { defaultValue: true } ),
player_no_close : attr( "boolean", { defaultValue: false } ),
Expand Down Expand Up @@ -85,14 +119,6 @@ export default Model.extend({

isVisibleInTray: computed( "gui_integration", function() {
return ( get( this, "gui_integration" ) & 2 ) > 0;
}),

playerParamsCorrected: computed( "player_params", function() {
let params = get( this, "player_params" );

return params.length && params.indexOf( "{filename}" ) === -1
? `${params} {filename}`
: params;
})

}).reopenClass({
Expand Down
Loading