Removing Icecast; Adding Snapcast websocket streaming (from SnapWeb); Adding new snapcast broadcast events, fixes #612
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
35
mopidy_iris/static/app.min.js
vendored
35
mopidy_iris/static/app.min.js
vendored
File diff suppressed because one or more lines are too long
@ -106,7 +106,7 @@
|
||||
|
||||
// Release details
|
||||
// These are automatically injected to built HTML
|
||||
var build = "1601538533";
|
||||
var build = "1601627320";
|
||||
var version = "3.52.4";
|
||||
|
||||
// Construct the script tag
|
||||
|
||||
@ -153,42 +153,8 @@ class OutputControl extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
localStreaming() {
|
||||
const {
|
||||
http_streaming_enabled,
|
||||
http_streaming_volume,
|
||||
http_streaming_mute,
|
||||
coreActions,
|
||||
} = this.props;
|
||||
|
||||
if (!http_streaming_enabled) return null;
|
||||
|
||||
return (
|
||||
<div className="output-control__item outputs__item--icecast">
|
||||
<div className="output-control__item__name">
|
||||
Local browser
|
||||
</div>
|
||||
<div className="output-control__item__controls">
|
||||
<MuteControl
|
||||
className="output-control__item__mute"
|
||||
noTooltip
|
||||
mute={http_streaming_mute}
|
||||
onMuteChange={(mute) => coreActions.set({ http_streaming_mute: mute })}
|
||||
/>
|
||||
<VolumeControl
|
||||
className="output-control__item__volume"
|
||||
volume={http_streaming_volume}
|
||||
mute={http_streaming_mute}
|
||||
onVolumeChange={(percent) => coreActions.set({ http_streaming_volume: percent })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderOutputs() {
|
||||
const snapcastGroups = this.snapcastGroups();
|
||||
const localStreaming = this.localStreaming();
|
||||
const commands = this.commands();
|
||||
|
||||
if (!snapcastGroups && !localStreaming && !commands) {
|
||||
@ -220,7 +186,7 @@ class OutputControl extends React.Component {
|
||||
}
|
||||
|
||||
// No customisable outputs
|
||||
if (!this.props.http_streaming_enabled && !this.props.snapcast_enabled && !this.props.pusher_commands) {
|
||||
if (!this.props.snapcast_enabled && !this.props.pusher_commands) {
|
||||
return (
|
||||
<span className="output-control disabled">
|
||||
<button className="control speakers"><Icon name="speaker" /></button>
|
||||
@ -235,10 +201,7 @@ class OutputControl extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
http_streaming_enabled: state.core.http_streaming_enabled,
|
||||
http_streaming_volume: parseInt(state.core.http_streaming_volume) || 50,
|
||||
http_streaming_mute: state.core.http_streaming_mute,
|
||||
const mapStateToProps = (state) => ({
|
||||
pusher_connected: state.pusher.connected,
|
||||
snapcast_enabled: (state.pusher.config ? state.pusher.config.snapcast_enabled : null),
|
||||
show_disconnected_clients: (state.ui.snapcast_show_disconnected_clients !== undefined ? state.ui.snapcast_show_disconnected_clients : false),
|
||||
|
||||
@ -278,47 +278,6 @@ class Services extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderIcecast() {
|
||||
const { core, coreActions } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<div className="field checkbox">
|
||||
<div className="name">
|
||||
<I18n path="settings.services.icecast.enable.label" />
|
||||
</div>
|
||||
<div className="input">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="ssl"
|
||||
checked={core.http_streaming_enabled}
|
||||
onChange={() => coreActions.set({ http_streaming_enabled: !core.http_streaming_enabled })}
|
||||
/>
|
||||
<span className="label">
|
||||
<I18n path="settings.services.icecast.enable.description" />
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<label className="field">
|
||||
<div className="name">
|
||||
<I18n path="settings.services.icecast.location.label" />
|
||||
</div>
|
||||
<div className="input">
|
||||
<TextField
|
||||
onChange={(value) => coreActions.set({ http_streaming_url: value })}
|
||||
value={core.http_streaming_url}
|
||||
autosave
|
||||
/>
|
||||
<div className="description">
|
||||
<I18n path="settings.services.icecast.location.description" />
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderMenu() {
|
||||
if (this.props.spotify.me && this.props.core.users[this.props.spotify.me.uri]) {
|
||||
var spotify_icon = <Thumbnail className="menu-item__thumbnail" circle size="small" images={this.props.core.users[this.props.spotify.me.uri].images} />;
|
||||
@ -415,23 +374,6 @@ class Services extends React.Component {
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
<Link history={this.props.history} className="menu-item menu-item--icecast" activeClassName="menu-item--active" to="/settings/services/icecast" scrollTo="#services-menu">
|
||||
<div className="menu-item__inner">
|
||||
<Icon className="menu-item__icon" name="wifi_tethering" />
|
||||
<div className="menu-item__title">
|
||||
<I18n path="services.icecast.title" />
|
||||
</div>
|
||||
{this.props.core.http_streaming_enabled ? (
|
||||
<span className="status green-text">
|
||||
<I18n path="settings.services.enabled" />
|
||||
</span>
|
||||
) : (
|
||||
<span className="status mid_grey-text">
|
||||
<I18n path="settings.services.disabled" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -446,8 +388,6 @@ class Services extends React.Component {
|
||||
return <div className="sub-tabs__content">{this.renderLastfm()}</div>;
|
||||
case 'genius':
|
||||
return <div className="sub-tabs__content">{this.renderGenius()}</div>;
|
||||
case 'icecast':
|
||||
return <div className="sub-tabs__content">{this.renderIcecast()}</div>;
|
||||
case 'snapcast':
|
||||
return <div className="sub-tabs__content">{<Snapcast match={this.props.match} />}</div>;
|
||||
default:
|
||||
@ -465,7 +405,7 @@ class Services extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => state;
|
||||
const mapStateToProps = (state) => state;
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
coreActions: bindActionCreators(coreActions, dispatch),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -72,7 +72,7 @@ const SnapcastClients = ({ actions, group, clients, groups }) => {
|
||||
</div>
|
||||
<div className="input">
|
||||
<LatencyControl
|
||||
max="150"
|
||||
max="500"
|
||||
value={client.latency}
|
||||
onChange={(value) => actions.setClientLatency(client.id, parseInt(value))}
|
||||
/>
|
||||
|
||||
@ -9,31 +9,79 @@ class Stream extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
console.log(props);
|
||||
|
||||
if (props.enabled && props.streaming_enabled) {
|
||||
this.snapstream = new SnapStream('192.168.1.201', 1780);
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
|
||||
start = () => {
|
||||
const {
|
||||
host,
|
||||
port,
|
||||
ssl,
|
||||
username,
|
||||
} = this.props;
|
||||
|
||||
if (this.snapstream) {
|
||||
this.snapstream.play();
|
||||
} else {
|
||||
const protocol = (ssl ? 'wss' : 'ws');
|
||||
console.log(protocol, host, port, username);
|
||||
this.snapstream = new SnapStream(protocol, host, port, 'Iris');
|
||||
}
|
||||
}
|
||||
|
||||
stop = () => {
|
||||
if (this.snapstream) {
|
||||
this.snapstream.stop();
|
||||
this.snapstream = null;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate = () => {
|
||||
const { enabled, streaming_enabled } = this.props;
|
||||
componentDidUpdate = ({
|
||||
streaming_enabled: prevStreamingEnabled,
|
||||
}) => {
|
||||
const {
|
||||
enabled,
|
||||
streaming_enabled,
|
||||
} = this.props;
|
||||
|
||||
if (enabled && streaming_enabled && !this.snapstream) {
|
||||
this.snapstream = new SnapStream('192.168.1.201', 1780);
|
||||
if (!prevStreamingEnabled && enabled && streaming_enabled) {
|
||||
this.start();
|
||||
}
|
||||
if ((!enabled || !streaming_enabled) && this.snapstream) {
|
||||
this.snapstream = null;
|
||||
if (!enabled || !streaming_enabled) {
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
render = () => null;
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
enabled: state.snapcast.enabled,
|
||||
streaming_enabled: state.snapcast.streaming_enabled,
|
||||
});
|
||||
const mapStateToProps = (state) => {
|
||||
const {
|
||||
snapcast: {
|
||||
enabled,
|
||||
streaming_enabled,
|
||||
host,
|
||||
port,
|
||||
ssl,
|
||||
},
|
||||
pusher: {
|
||||
username,
|
||||
},
|
||||
} = state;
|
||||
|
||||
return {
|
||||
enabled,
|
||||
streaming_enabled,
|
||||
host,
|
||||
port,
|
||||
ssl,
|
||||
username,
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
coreActions: bindActionCreators(coreActions, dispatch),
|
||||
|
||||
@ -118,8 +118,6 @@ services:
|
||||
want_lyrics: 'Wollen Sie Liedtexte? Genius autorisieren in '
|
||||
google:
|
||||
title: Google
|
||||
icecast:
|
||||
title: Icecast
|
||||
lastfm:
|
||||
title: LastFM
|
||||
love: Liebe es
|
||||
@ -385,13 +383,6 @@ settings:
|
||||
locale:
|
||||
title: Sprache
|
||||
description: ISO 639 Sprachcode in Kleinbuchstaben und ein großgeschriebener ISO 3166-1 alpha-2 Ländercode, verbunden mit einem Unterstrich (z.B. 'de_DE')
|
||||
icecast:
|
||||
enable:
|
||||
label: Aktivieren
|
||||
description: Audio an diesen Browser streamen
|
||||
location:
|
||||
label: Standort
|
||||
description: Die vollständige URL zu Ihrem Stream-Endpunkt
|
||||
user:
|
||||
title: Benutzer
|
||||
title_window: '%{name} (Benutzer)'
|
||||
|
||||
@ -130,8 +130,6 @@ services:
|
||||
want_lyrics: 'Want track lyrics? Authorize Genius in '
|
||||
google:
|
||||
title: Google
|
||||
icecast:
|
||||
title: Icecast
|
||||
lastfm:
|
||||
title: LastFM
|
||||
love: Love
|
||||
@ -415,13 +413,6 @@ settings:
|
||||
locale:
|
||||
title: Locale
|
||||
description: Lowercase ISO 639 language code and an uppercase ISO 3166-1 alpha-2 country code, joined by an underscore (eg 'en_NZ')
|
||||
icecast:
|
||||
enable:
|
||||
label: Enable
|
||||
description: Stream audio to this browser
|
||||
location:
|
||||
label: Location
|
||||
description: The full URL to your stream endpoint
|
||||
user:
|
||||
title: User
|
||||
title_window: '%{name} (user)'
|
||||
|
||||
@ -129,8 +129,6 @@ services:
|
||||
want_lyrics: 'Vous voulez les paroles ? Autorisez Genius sous '
|
||||
google:
|
||||
title: Google
|
||||
icecast:
|
||||
title: Icecast
|
||||
lastfm:
|
||||
title: LastFM
|
||||
love: Aimer
|
||||
@ -408,13 +406,6 @@ settings:
|
||||
locale:
|
||||
title: Locale
|
||||
description: Code de langage ISO 639 en minuscules et code de pays ISO 3166-1 alpha-2 en majucule, séparés par un tiret bas (par exemple 'fr_FR')
|
||||
icecast:
|
||||
enable:
|
||||
label: Activer
|
||||
description: Envoyer le flux audio sur ce navigateur
|
||||
location:
|
||||
label: Adresse
|
||||
description: URL du flux audio
|
||||
user:
|
||||
title: Utilisateur
|
||||
title_window: '%{name} (utilisateur)'
|
||||
|
||||
@ -125,8 +125,6 @@ services:
|
||||
want_lyrics: 'Wil je songteksten? Geef Genius toestemming in '
|
||||
google:
|
||||
title: Google
|
||||
icecast:
|
||||
title: Icecast
|
||||
lastfm:
|
||||
title: LastFM
|
||||
love: Leuk
|
||||
@ -391,13 +389,6 @@ settings:
|
||||
locale:
|
||||
title: Taal
|
||||
description: Een ISO 639 taalcode in kleine letters en een ISO 3166-1 alpha-2 landcode in hoofdletters, verbonden met een liggend streepje (bv 'nl_NL' of 'nl_BE')
|
||||
icecast:
|
||||
enable:
|
||||
label: Actief
|
||||
description: Stream audio naar deze browser
|
||||
location:
|
||||
label: Locatie
|
||||
description: De volledige URL naar het stream-eindpunt
|
||||
user:
|
||||
title: Gebruiker
|
||||
title_window: '%{name} (gebruiker)'
|
||||
|
||||
9
src/js/locale/sv.yaml
Normal file → Executable file
9
src/js/locale/sv.yaml
Normal file → Executable file
@ -129,8 +129,6 @@ services:
|
||||
want_lyrics: 'Vill du ha låttext? auktorisera Genius i '
|
||||
google:
|
||||
title: Google
|
||||
icecast:
|
||||
title: Icecast
|
||||
lastfm:
|
||||
title: LastFM
|
||||
love: Love
|
||||
@ -410,13 +408,6 @@ settings:
|
||||
locale:
|
||||
title: Språk
|
||||
description: Gemen ISO 639 språkkod och en versal ISO 3166-1 alpha-2 landskod, tillsammans med ett understreck (ex 'sv_SE')
|
||||
icecast:
|
||||
enable:
|
||||
label: Aktivera
|
||||
description: Spela ljud till denna webbläsare
|
||||
location:
|
||||
label: Location
|
||||
description: The full URL to your stream endpoint
|
||||
user:
|
||||
title: Användare
|
||||
title_window: '%{name} (user)'
|
||||
|
||||
@ -151,14 +151,6 @@ export function calculateGroupVolume(id, clients) {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Record loaders
|
||||
*
|
||||
* We've got a loaded record, now we just need to plug it in to our state and stores.
|
||||
* */
|
||||
|
||||
export function serverLoaded(server) {
|
||||
return {
|
||||
type: 'SNAPCAST_SERVER_LOADED',
|
||||
|
||||
@ -96,8 +96,14 @@ const SnapcastMiddleware = (function () {
|
||||
store.dispatch(snapcastActions.groupLoaded(message.params));
|
||||
break;
|
||||
|
||||
case 'Group.OnNameChanged':
|
||||
store.dispatch(snapcastActions.groupLoaded(message.params));
|
||||
break;
|
||||
|
||||
case 'Server.OnUpdate':
|
||||
store.dispatch(snapcastActions.serverLoaded(message.param));
|
||||
store.dispatch(snapcastActions.serverLoaded(message.params.server.server));
|
||||
store.dispatch(snapcastActions.groupsLoaded(message.params.server.groups, true));
|
||||
store.dispatch(snapcastActions.streamsLoaded(message.params.server.streams, true));
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -330,24 +336,23 @@ const SnapcastMiddleware = (function () {
|
||||
next(action);
|
||||
break;
|
||||
|
||||
case 'SNAPCAST_CLIENTS_LOADED':
|
||||
var clients_index = { ...snapcast.clients };
|
||||
var clients_loaded = [];
|
||||
case 'SNAPCAST_CLIENTS_LOADED': {
|
||||
const nextClients = action.flush ? {} : { ...snapcast.clients };
|
||||
|
||||
for (const raw_client of action.clients) {
|
||||
var client = formatClient(raw_client);
|
||||
|
||||
if (clients_index[client.id]) {
|
||||
client = { ...clients_index[client.id], ...client };
|
||||
}
|
||||
|
||||
clients_loaded.push(client);
|
||||
const client = formatClient(raw_client);
|
||||
nextClients[client.id] = {
|
||||
...nextClients[client.id],
|
||||
...client,
|
||||
};
|
||||
}
|
||||
|
||||
action.clients = clients_loaded;
|
||||
|
||||
next(action);
|
||||
next({
|
||||
...action,
|
||||
clients: nextClients,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case 'SNAPCAST_SET_CLIENT_NAME':
|
||||
var client = snapcast.clients[action.id];
|
||||
|
||||
@ -17,16 +17,7 @@ export default function reducer(snapcast = {}, action) {
|
||||
return { ...snapcast, server: action.server };
|
||||
|
||||
case 'SNAPCAST_CLIENTS_LOADED':
|
||||
if (action.flush) {
|
||||
var clients = {};
|
||||
} else {
|
||||
var clients = { ...snapcast.clients };
|
||||
}
|
||||
|
||||
for (const client of action.clients) {
|
||||
clients[client.id] = client;
|
||||
}
|
||||
return { ...snapcast, clients };
|
||||
return { ...snapcast, clients: action.clients };
|
||||
|
||||
case 'SNAPCAST_GROUPS_LOADED':
|
||||
if (action.flush) {
|
||||
|
||||
@ -712,7 +712,7 @@ const formatClient = function (data) {
|
||||
if (data.volume.percent) {
|
||||
client.volume = data.volume.percent;
|
||||
}
|
||||
if (data.volume.muted) {
|
||||
if (data.volume.muted !== undefined) {
|
||||
client.mute = data.volume.muted;
|
||||
}
|
||||
}
|
||||
|
||||
@ -496,6 +496,10 @@ const iconFromKeyword = (name) => {
|
||||
icon: 'smartphone',
|
||||
words: ['phone', 'mobile']
|
||||
},
|
||||
{
|
||||
icon: 'web',
|
||||
words: ['web', 'browser', 'iris']
|
||||
},
|
||||
];
|
||||
for (let item of iconWords) {
|
||||
for (let word of item.words) {
|
||||
|
||||
Reference in New Issue
Block a user