Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/FoxxMD/multi-scrobbler. Scrobble plays from multiple sources to multiple clients docs.multi-scrobbler.app
deezer docker jellyfin koito lastfm listenbrainz maloja mopidy mpris music music-assistant plex scrobble self-hosted spotify subsonic tautulli youtube-music
Something went wrong. Try again.
4.3 kB · 105 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106import type {Logger} from "@foxxmd/logging";import type EventEmitter from "events";import {COMPONENT_AUTH_TYPE, type ComponentAuthType, type PlayObject, type SourcePlayerObj} from "../../core/Atomic.ts";import { capitalize } from "../../core/StringUtils.ts";import { buildTrackString } from "../../core/MusicMetadata.ts";import { isNodeNetworkException } from "../common/errors/NodeErrors.ts";import type {FormatPlayObjectOptions, TimeRangeListensFetcher} from "../common/infrastructure/Atomic.ts";import { playToListenPayload } from '../common/vendor/listenbrainz/lzUtils.ts';
import AbstractScrobbleClient, { nowPlayingUpdateByPlayDuration } from "./AbstractScrobbleClient.ts";import { isDebugMode } from "../utils.ts";import type {KoitoClientConfig} from "../common/infrastructure/config/client/koito.ts";import { KoitoApiClient, listenObjectResponseToPlay } from "../common/vendor/koito/KoitoApiClient.ts";import { createGetScrobblesForTimeRangeFunc } from "../utils/ListenFetchUtils.ts";
export default class KoitoScrobbler extends AbstractScrobbleClient {
api: KoitoApiClient; override authType: ComponentAuthType = COMPONENT_AUTH_TYPE.unattended; requiresAuth = true; requiresAuthInteraction = false; getScrobblesForTimeRange: TimeRangeListensFetcher declare config: KoitoClientConfig;
constructor(name: any, config: KoitoClientConfig, options = {}, emitter: EventEmitter, logger: Logger) { super('koito', name, config, emitter, logger); this.api = new KoitoApiClient(name, {...config.data, allowDeviceList: config.options?.allowDeviceList, includePlayPosition: config.options?.includePlayPosition}, {logger: this.logger}); // https://listenbrainz.readthedocs.io/en/latest/users/api/core.html#get--1-user-(user_name)-listens // 1000 is way too high. maxing at 100 this.MAX_INITIAL_SCROBBLES_FETCH = 100; this.supportsNowPlaying = true; this.nowPlayingMaxThreshold = nowPlayingUpdateByPlayDuration; this.getScrobblesForTimeRange = createGetScrobblesForTimeRangeFunc(this.api, this.api.logger); }
formatPlayObj = (obj: any, options: FormatPlayObjectOptions = {}) => listenObjectResponseToPlay(obj, options);
public playToClientPayload(playObject: PlayObject): object { return playToListenPayload(playObject, {allowDeviceList: this.config.options?.allowDeviceList}); }
protected async doBuildInitData(): Promise<true | string | undefined> { const { data: { token, } = {} } = this.config; if (token === undefined) { throw new Error('Must provide a User Token'); } return true; }
protected async doCheckConnection(): Promise<true | string | undefined> { await this.api.testConnection(); return true; }
doAuthentication = async () => {
try { return await this.api.testAuth(); } catch (e) { if(isNodeNetworkException(e)) { this.logger.error('Could not communicate with Koito API'); } throw e; } }
doScrobble = async (playObj: PlayObject) => { const { meta: { source, newFromSource = false, } = {} } = playObj;
try { const result = await this.api.submitListen(playObj, { log: isDebugMode()});
if (newFromSource) { this.logger.info(`Scrobbled (New) => (${source}) ${buildTrackString(playObj)}`); } else { this.logger.info(`Scrobbled (Backlog) => (${source}) ${buildTrackString(playObj)}`); } return result; } catch (e: any) { await this.notify({title: `Client - ${capitalize(this.type)} - ${this.name} - Scrobble Error`, message: `Failed to scrobble => ${buildTrackString(playObj)} | Error: ${e.message}`, priority: 'error'}); throw e; } }
doPlayingNow = async (data: SourcePlayerObj) => { try { if(data.play === undefined) { return; } await this.api.submitListen(data.play, { listenType: 'playing_now'}); } catch (e) { throw e; } }}