Skip to content

Commit

Permalink
Enabled tuneIn, PockeCast and Amazon music, improved album art loading
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalopitz committed Jun 3, 2020
1 parent 974cb60 commit c9264ba
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 59 deletions.
2 changes: 1 addition & 1 deletion app/package.json
@@ -1,7 +1,7 @@
{
"name": "sonos-controller-unofficial",
"description": "Unoffical sonos controller for linux.",
"version": "0.2.0-beta2.1",
"version": "0.2.0-beta3",
"author": "Pascal Opitz <contact@pascalopitz.com>",
"main": "main.js",
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "sonos-controller-unofficial",
"version": "0.2.0-beta2.1",
"version": "0.2.0-beta3",
"description": "Unoffical sonos controller for linux",
"main": "app/main.js",
"homepage": "http://pascalopitz.github.io/unoffical-sonos-controller-for-linux/",
Expand Down
67 changes: 63 additions & 4 deletions src/ui/components/AlbumArt.jsx
@@ -1,7 +1,10 @@
import _ from 'lodash';

import React, { Component } from 'react';
import shallowCompare from 'shallow-compare';

import SonosService from '../services/SonosService';
import { getByServiceId } from '../services/MusicServiceClient';

import {
getClosest,
Expand All @@ -28,12 +31,16 @@ export class AlbumArt extends Component {
}

async _loadImage() {
const { visible, failed, src } = this.state;
const { visible, failed, src, propsSrc } = this.state;
// here we make sure it's still visible, a URL and hasn't failed previously
if (!visible || failed) {
return;
}

if (this.loadPromise) {
this.loadPromise.cancel();
}

this.loadPromise = this.makeLoadPromise(src);
this.loadPromise.promise
.then(() => {
Expand All @@ -43,7 +50,7 @@ export class AlbumArt extends Component {
loaded: true,
});
})
.catch((err) => {
.catch(async (err) => {
if (err && err.isCancelled) {
return;
}
Expand All @@ -52,6 +59,53 @@ export class AlbumArt extends Component {
return;
}

try {
const urlToParse = propsSrc.match(/^\//)
? `http://localhost${propsSrc}`
: propsSrc;

const parsed = new URL(
new URL(urlToParse).searchParams.get('u')
);

const sid = parsed.searchParams.get('sid');

if (!sid) {
return;
}

const client = getByServiceId(sid);

if (!client) {
return null;
}

const response = await client.getExtendedMetadata(
decodeURIComponent(parsed.pathname).replace('.mp3', '')
);

const newSrc = _.get(
response,
'mediaMetadata.trackMetadata.albumArtURI'
);

if (newSrc) {
this.setState(
{
src: newSrc,
loading: false,
},
() => {
this._loadImage();
}
);

return;
}
} catch (e) {
// noop
}

this.setState({
failed: true,
loading: false,
Expand Down Expand Up @@ -129,9 +183,14 @@ export class AlbumArt extends Component {
if (visible && needsRecompute) {
const sonos = SonosService._currentDevice;

const url = serviceId ? getServiceLogoUrl(serviceId) : src;
const url =
src && typeof src === 'object' && src._
? src._
: serviceId
? getServiceLogoUrl(serviceId)
: src;

if (url) {
if (url && typeof url === 'string') {
const srcUrl =
url.indexOf('https://') === 0 ||
url.indexOf('http://') === 0 ||
Expand Down
3 changes: 2 additions & 1 deletion src/ui/components/BrowserListItem.jsx
Expand Up @@ -99,7 +99,7 @@ class InlineMenu extends PureComponent {
const isPlayNow =
item.class === 'object.item.audioItem' ||
(item.metadata &&
item.metadata.class === 'object.item.audioItem.audioBroadcast');
item.class === 'object.item.audioItem.audioBroadcast');

const isService = item.action === 'service';
const isSonosPlaylist = item._raw && item._raw.parentID === 'SQ:';
Expand Down Expand Up @@ -192,6 +192,7 @@ export class BrowserListItem extends Component {
if (
item.class === 'object.item.audioItem.musicTrack' ||
item.class === 'object.item.audioItem' ||
item.streamMetadata ||
item.trackMetadata
) {
this.props.playNow(item);
Expand Down
27 changes: 17 additions & 10 deletions src/ui/helpers/sonos.js
Expand Up @@ -2,6 +2,8 @@ import { DeviceDiscovery, Sonos, Helpers, Services } from 'sonos';
import request from 'axios';
import _ from 'lodash';

const TUNEIN_ID = 65031;

class ContentDirectoryEnhanced extends Services.ContentDirectory {
_enumItems(resultcontainer) {
if (resultcontainer === undefined) {
Expand Down Expand Up @@ -59,6 +61,8 @@ class SonosEnhanced extends Sonos {
data.AvailableServiceDescriptorList
);

console.log(servicesObj);

const serviceDescriptors = servicesObj.Services.Service.map((obj) => {
const stringsUri = _.get(obj, 'Presentation.Strings.Uri');
const mapUri = _.get(obj, 'Presentation.PresentationMap.Uri');
Expand All @@ -75,17 +79,20 @@ class SonosEnhanced extends Sonos {

const services = [];

data.AvailableServiceTypeList.split(',').forEach(async (t) => {
const serviceId = Math.floor(Math.abs((t - 7) / 256)) || Number(t);
const match = _.find(serviceDescriptors, {
Id: String(serviceId),
});

if (match) {
match.ServiceIDEncoded = Number(t);
services.push(match);
[TUNEIN_ID, ...data.AvailableServiceTypeList.split(',')].forEach(
async (t) => {
const serviceId =
Math.floor(Math.abs((t - 7) / 256)) || Number(t);
const match = _.find(serviceDescriptors, {
Id: String(serviceId),
});

if (match) {
match.ServiceIDEncoded = Number(t);
services.push(match);
}
}
});
);

return services;
}
Expand Down
44 changes: 27 additions & 17 deletions src/ui/reduxActions/BrowserListActions.js
@@ -1,4 +1,5 @@
import _ from 'lodash';

import { createAction } from 'redux-actions';
import Constants from '../constants';

Expand All @@ -16,6 +17,8 @@ import {

import { loadPlaylists, loadPlaylistItems } from './PlaylistActions';

const TUNEIN_ID = 65031;

async function _fetchLineIns() {
const { deviceSearches } = store.getState().sonosService;

Expand Down Expand Up @@ -81,16 +84,17 @@ export async function _getItem(item) {
const meta = client.encodeItemMetadata(uri, item, token);

return {
uri: _.escape(uri),
metadata: meta,
uri,
...meta,
};
}

const uri = await client.getMediaURI(item.id);
const meta = client.encodeItemMetadata(uri, item);

return {
uri: _.escape(uri),
metadata: client.encodeItemMetadata(uri, item),
uri,
...meta,
};
}

Expand Down Expand Up @@ -338,12 +342,10 @@ const selectBrowseServices = async (item) => {
};

const selectService = async (item) => {
const client = new MusicServiceClient(item.service.service);

if (client.auth !== 'Anonymous') {
client.setAuthToken(item.service.authToken.authToken);
client.setKey(item.service.authToken.privateKey);
}
const client = new MusicServiceClient(
item.service.service,
item.service.authToken || {}
);

const res = await client.getMetadata('root', 0, 100);
const searchTermMap = await client.getSearchTermMap();
Expand Down Expand Up @@ -451,7 +453,11 @@ export const select = createAction(
store.getState().browserList.history
);

if (item.serviceClient && item.itemType !== 'track') {
if (
item.serviceClient &&
item.itemType !== 'track' &&
item.itemType !== 'stream'
) {
return await selectServiceMediaCollectionItem(item);
}

Expand Down Expand Up @@ -493,14 +499,18 @@ export const playNow = createAction(
const item = await _getItem(eventTarget);

if (
_.get(
eventTarget,
'serviceClient._serviceDefinition.ServiceIDEncoded'
) === TUNEIN_ID
) {
await sonos.playTuneinRadio(eventTarget.id, eventTarget.title);
await sonos.play();
} else if (
item.metadata &&
item.metadataRaw &&
item.metadata.class === 'object.item.audioItem.audioBroadcast'
item.class === 'object.item.audioItem.audioBroadcast'
) {
await sonos.play({
uri: item.uri,
metadata: item.metadataRaw,
});
await sonos.setAVTransportURI(item);
} else if (item.class && item.class === 'object.item.audioItem') {
await sonos.play(item.uri);
} else {
Expand Down

0 comments on commit c9264ba

Please sign in to comment.