Scrobble on completion of song if song was listened to

This commit is contained in:
simojenki
2021-07-07 17:28:26 +10:00
parent eec3313587
commit f7a1b3f52c
7 changed files with 378 additions and 108 deletions

View File

@@ -164,7 +164,7 @@ function server(
${SONOS_RECOMMENDED_IMAGE_SIZES.map(
(size) =>
`<sizeEntry size="${size}" substitution="/art/size/${size}"/>`
)}
).join("")}
</imageSizeMap>
</Match>
</PresentationMap>

View File

@@ -242,6 +242,10 @@ export const album = (
title: album.name,
albumArtURI: defaultAlbumArtURI(webAddress, accessToken, album),
canPlay: true,
// defaults
// canScroll: false,
// canEnumerate: true,
// canAddToFavorites: true
});
export const track = (
@@ -487,6 +491,26 @@ function bindSmapiSoapServiceToExpress(
},
},
}));
case "album":
return musicLibrary.album(typeId).then((it) => ({
getExtendedMetadataResult: {
mediaCollection: {
attributes: {
readOnly: true,
userContent: false,
renameable: false,
},
...album(webAddress, accessToken, it),
},
// <mediaCollection readonly="true">
// </mediaCollection>
// <relatedText>
// <id>AL:123456</id>
// <type>ALBUM_NOTES</type>
// </relatedText>
// </getExtendedMetadataResult>
},
}));
default:
throw `Unsupported getExtendedMetadata id=${id}`;
}
@@ -784,10 +808,42 @@ function bindSmapiSoapServiceToExpress(
}
})
.then((_) => ({ removeFromContainerResult: { updateId: "" } })),
setPlayedSeconds: async (
{ id, seconds }: { id: string; seconds: string },
_,
headers?: SoapyHeaders
) =>
auth(musicService, accessTokens, headers)
.then(splitId(id))
.then(({ musicLibrary, type, typeId }) => {
switch (type) {
case "track":
musicLibrary.track(typeId).then(({ duration }) => {
if (
(duration < 30 && +seconds >= 10) ||
(duration >= 30 && +seconds >= 30)
) {
musicLibrary.scrobble(typeId);
}
});
break;
default:
logger.info("Unsupported scrobble", { id, seconds });
break;
}
})
.then((_) => ({
setPlayedSecondsResult: {},
})),
},
},
},
readFileSync(WSDL_FILE, "utf8")
readFileSync(WSDL_FILE, "utf8"),
(err: any, res: any) => {
if (err) {
logger.error("BOOOOM", { err, res });
}
}
);
soapyService.log = (type, data) => {

View File

@@ -5,10 +5,12 @@ import { MusicService } from "@svrooij/sonos/lib/services";
import { head } from "underscore";
import logger from "./logger";
import { SOAP_PATH, STRINGS_ROUTE, PRESENTATION_MAP_ROUTE } from "./smapi";
import qs from "querystring"
import qs from "querystring";
export const PRESENTATION_AND_STRINGS_VERSION = "18";
// NOTE: manifest requires https for the URL,
// otherwise you will get an error trying to register
export type Capability =
| "search"
| "trFavorites"
@@ -16,7 +18,9 @@ export type Capability =
| "ucPlaylists"
| "extendedMD"
| "contextHeaders"
| "authorizationHeader";
| "authorizationHeader"
| "logging"
| "manifest";
export const BONOB_CAPABILITIES: Capability[] = [
"search",
@@ -24,6 +28,7 @@ export const BONOB_CAPABILITIES: Capability[] = [
// "alFavorites",
"ucPlaylists",
"extendedMD",
"logging",
];
export type Device = {
@@ -38,13 +43,13 @@ export type Service = {
sid: number;
uri: string;
secureUri: string;
strings: { uri?: string; version?: string };
presentation: { uri?: string; version?: string };
strings?: { uri?: string; version?: string };
presentation?: { uri?: string; version?: string };
pollInterval?: number;
authType: "Anonymous" | "AppLink" | "DeviceLink" | "UserId";
};
const stripTailingSlash = (url: string) =>
export const stripTailingSlash = (url: string) =>
url.endsWith("/") ? url.substring(0, url.length - 1) : url;
export const bonobService = (
@@ -113,12 +118,10 @@ export const asCustomdForm = (csrfToken: string, service: Service) => ({
secureUri: service.secureUri,
pollInterval: `${service.pollInterval || 1200}`,
authType: service.authType,
stringsVersion: service.strings.version || "",
stringsUri: service.strings.uri || "",
presentationMapVersion: service.presentation.version || "",
presentationMapUri: service.presentation.uri || "",
manifestVersion: "0",
manifestUri: "",
stringsVersion: service.strings?.version || "0",
stringsUri: service.strings?.uri || "",
presentationMapVersion: service.presentation?.version || "0",
presentationMapUri: service.presentation?.uri || "",
containerType: "MService",
caps: BONOB_CAPABILITIES,
});
@@ -193,9 +196,10 @@ export function autoDiscoverySonos(sonosSeedHost?: string): Sonos {
);
return false;
}
const customdForm = asCustomdForm(csrfToken, service);
logger.info(`Registering with sonos @ ${customd}`, { customdForm });
return axios
.post(customd, new URLSearchParams(qs.stringify(asCustomdForm(csrfToken, service))), {
.post(customd, new URLSearchParams(qs.stringify(customdForm)), {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},