···11import 'dotenv/config';
22-import {LogDataPretty, Logger} from "@foxxmd/logging";
22+import { childLogger, LogDataPretty, Logger as FoxLogger } from "@foxxmd/logging";
33import dayjs from 'dayjs';
44-import utc from 'dayjs/plugin/utc.js';
44+import duration from 'dayjs/plugin/duration.js';
55import isBetween from 'dayjs/plugin/isBetween.js';
66import relativeTime from 'dayjs/plugin/relativeTime.js';
77-import duration from 'dayjs/plugin/duration.js';
87import timezone from 'dayjs/plugin/timezone.js';
99-import { parseBool, readJson, sleep } from "./utils.js";
88+import utc from 'dayjs/plugin/utc.js';
109import * as path from "path";
1010+import { SimpleIntervalJob, ToadScheduler } from "toad-scheduler";
1111import { projectDir } from "./common/index.js";
1212-import SpotifySource from "./sources/SpotifySource.js";
1312import { AIOConfig } from "./common/infrastructure/config/aioConfig.js";
1313+import { appLogger, initLogger as getInitLogger } from "./common/logging.js";
1414import { getRoot } from "./ioc.js";
1515-import {appLogger, initLogger as getInitLogger} from "./common/logging.js";
1615import { initServer } from "./server/index.js";
1717-import {SimpleIntervalJob, ToadScheduler} from "toad-scheduler";
1818-import { createHeartbeatSourcesTask } from "./tasks/heartbeatSources.js";
1916import { createHeartbeatClientsTask } from "./tasks/heartbeatClients.js";
2020-import {loggerDebug, childLogger, LogData, Logger as FoxLogger} from '@foxxmd/logging';
1717+import { createHeartbeatSourcesTask } from "./tasks/heartbeatSources.js";
1818+import { parseBool, readJson, sleep } from "./utils.js";
21192220dayjs.extend(utc)
2321dayjs.extend(isBetween);
+9-9
src/backend/ioc.ts
···11-import {createContainer} from "iti";
11+import { Logger } from "@foxxmd/logging";
22+import { EventEmitter } from "events";
33+import fs from 'fs';
44+import { createContainer } from "iti";
55+import normalizeUrl from 'normalize-url';
26import path from "path";
33-import { configDir, projectDir } from "./common/index.js";
44-import ScrobbleClients from "./scrobblers/ScrobbleClients.js";
55-import ScrobbleSources from "./sources/ScrobbleSources.js";
66-import { Notifiers } from "./notifier/Notifiers.js";
77-import {EventEmitter} from "events";
77+import { projectDir } from "./common/index.js";
88import { logPath } from "./common/logging.js";
99import { WildcardEmitter } from "./common/WildcardEmitter.js";
1010-import normalizeUrl from 'normalize-url';
1111-import fs from 'fs';
1212-import {Logger} from "@foxxmd/logging";
1010+import { Notifiers } from "./notifier/Notifiers.js";
1111+import ScrobbleClients from "./scrobblers/ScrobbleClients.js";
1212+import ScrobbleSources from "./sources/ScrobbleSources.js";
13131414let version = 'Unknown';
1515
+15-16
src/backend/utils.ts
···11-import {accessSync, constants, promises} from "fs";
22-import dayjs, {Dayjs} from 'dayjs';
33-import utc from 'dayjs/plugin/utc.js';
44-import {Logger} from '@foxxmd/logging';
55-import JSON5 from 'json5';
66-import {TimeoutError, WebapiError} from "spotify-web-api-node/src/response-error.js";
77-import {Schema} from 'ajv';
11+import { Logger } from '@foxxmd/logging';
22+import backoffStrategies from '@kenyip/backoff-strategies';
33+import address from "address";
84import * as AjvNS from 'ajv';
99-import Ajv from 'ajv';
55+import Ajv, { Schema } from 'ajv';
66+import { replaceResultTransformer, stripIndentTransformer, TemplateTag, trimResultTransformer } from 'common-tags';
77+import dayjs, { Dayjs } from 'dayjs';
88+import { Duration } from "dayjs/plugin/duration.js";
99+import utc from 'dayjs/plugin/utc.js';
1010+import { Request } from "express";
1111+import { accessSync, constants, promises } from "fs";
1212+import JSON5 from 'json5';
1313+import pathUtil from "path";
1414+import { getErrorCause } from "pony-cause";
1515+import { TimeoutError, WebapiError } from "spotify-web-api-node/src/response-error.js";
1616+import { PlayObject } from "../core/Atomic.js";
1017import {
1118 asPlayerStateData,
1219 NO_DEVICE,
···1926 RemoteIdentityParts,
2027 ScrobbleThresholdResult,
2128} from "./common/infrastructure/Atomic.js";
2222-import {Request} from "express";
2323-import pathUtil from "path";
2424-import {getErrorCause} from "pony-cause";
2525-import backoffStrategies from '@kenyip/backoff-strategies';
2626-import {replaceResultTransformer, stripIndentTransformer, TemplateTag, trimResultTransformer} from 'common-tags';
2727-import {Duration} from "dayjs/plugin/duration.js";
2828-import { PlayObject } from "../core/Atomic.js";
2929-import address from "address";
30293130//const { default: Ajv } = AjvNS;
3231dayjs.extend(utc);
+3-3
src/core/Atomic.ts
···11-import {Dayjs} from "dayjs";
22-import {ListenProgress} from "../backend/sources/PlayerState/ListenProgress.js";
33-import {LogDataPretty, LogLevel} from "@foxxmd/logging";
11+import { LogDataPretty, LogLevel } from "@foxxmd/logging";
22+import { Dayjs } from "dayjs";
33+import { ListenProgress } from "../backend/sources/PlayerState/ListenProgress.js";
4455export interface SourceStatusData {
66 status: string;
+10-10
src/backend/common/logging.ts
···11-import path from "path";
22-import { projectDir } from "./index.js";
33-import process from "process";
41import {
55- parseLogOptions,
66- loggerAppRolling,
77- LogOptions as FoxLogOptions,
22+ childLogger,
83 Logger as FoxLogger,
99- childLogger, LogLevel, PrettyOptionsExtra,
44+ loggerAppRolling,
55+ LogLevel,
66+ LogOptions as FoxLogOptions,
77+ parseLogOptions,
108} from '@foxxmd/logging';
1111-import {PassThrough, Transform} from "node:stream";
1212-import {buildLogger, buildDestinationStdout, buildDestinationJsonPrettyStream} from "@foxxmd/logging/factory";
1313-import {parseBool} from "../utils.js";
99+import { buildDestinationJsonPrettyStream, buildDestinationStdout, buildLogger } from "@foxxmd/logging/factory";
1010+import { PassThrough, Transform } from "node:stream";
1111+import path from "path";
1212+import process from "process";
1313+import { projectDir } from "./index.js";
14141515export let logPath = path.resolve(projectDir, `./logs`);
1616if (typeof process.env.CONFIG_DIR === 'string') {
+1-2
src/backend/notifier/AbstractWebhookNotifier.ts
···11+import { childLogger, Logger } from "@foxxmd/logging";
12import { GotifyConfig, NtfyConfig, WebhookPayload } from "../common/infrastructure/config/health/webhooks.js";
22-import {childLogger, Logger} from "@foxxmd/logging";
33-import { mergeArr } from "../utils.js";
4354export abstract class AbstractWebhookNotifier {
65
+5-5
src/backend/notifier/GotifyWebhookNotifier.ts
···11-import { AbstractWebhookNotifier } from "./AbstractWebhookNotifier.js";
22-import { GotifyConfig, PrioritiesConfig, WebhookPayload } from "../common/infrastructure/config/health/webhooks.js";
33-import {gotify} from 'gotify';
11+import { Logger } from "@foxxmd/logging";
22+import { HTTPError } from "got";
33+import { gotify } from 'gotify';
44import request from 'superagent';
55-import {HTTPError} from "got";
66-import {Logger} from "@foxxmd/logging";
55+import { GotifyConfig, PrioritiesConfig, WebhookPayload } from "../common/infrastructure/config/health/webhooks.js";
66+import { AbstractWebhookNotifier } from "./AbstractWebhookNotifier.js";
7788export class GotifyWebhookNotifier extends AbstractWebhookNotifier {
99
+8-3
src/backend/notifier/Notifiers.ts
···11-import {childLogger, Logger} from '@foxxmd/logging';
22-import { GotifyConfig, NtfyConfig, WebhookConfig, WebhookPayload } from "../common/infrastructure/config/health/webhooks.js";
11+import { childLogger, Logger } from '@foxxmd/logging';
22+import { EventEmitter } from "events";
33+import {
44+ GotifyConfig,
55+ NtfyConfig,
66+ WebhookConfig,
77+ WebhookPayload
88+} from "../common/infrastructure/config/health/webhooks.js";
39import { AbstractWebhookNotifier } from "./AbstractWebhookNotifier.js";
410import { GotifyWebhookNotifier } from "./GotifyWebhookNotifier.js";
511import { NtfyWebhookNotifier } from "./NtfyWebhookNotifier.js";
66-import {EventEmitter} from "events";
712813export class Notifiers {
914
+5-5
src/backend/notifier/NtfyWebhookNotifier.ts
···11-import { AbstractWebhookNotifier } from "./AbstractWebhookNotifier.js";
22-import { NtfyConfig, PrioritiesConfig, WebhookPayload } from "../common/infrastructure/config/health/webhooks.js";
33-import {publish} from 'ntfy';
11+import { Logger } from "@foxxmd/logging";
22+import { publish } from 'ntfy';
33+import { Config } from "ntfy/interfaces.js";
44import request from "superagent";
55-import {Logger} from "@foxxmd/logging";
66-import {Config} from "ntfy/interfaces.js";
55+import { NtfyConfig, PrioritiesConfig, WebhookPayload } from "../common/infrastructure/config/health/webhooks.js";
66+import { AbstractWebhookNotifier } from "./AbstractWebhookNotifier.js";
7788export class NtfyWebhookNotifier extends AbstractWebhookNotifier {
99
+23-25
src/backend/scrobblers/AbstractScrobbleClient.ts
···11-import dayjs, {Dayjs} from "dayjs";
11+import { childLogger, Logger } from "@foxxmd/logging";
22+import dayjs, { Dayjs } from "dayjs";
33+import EventEmitter from "events";
44+import { FixedSizeList } from 'fixed-size-list';
55+import { nanoid } from "nanoid";
66+import { messageWithCauses } from "pony-cause";
27import {
33- comparingMultipleArtists,
44- mergeArr,
55- playObjDataMatch,
66- pollingBackoff,
77- setIntersection,
88- sleep,
99- sortByOldestPlayDate,
1010-} from "../utils.js";
88+ DeadLetterScrobble,
99+ PlayObject,
1010+ QueuedScrobble,
1111+ TA_CLOSE,
1212+ TA_FUZZY,
1313+ TrackStringOptions,
1414+} from "../../core/Atomic.js";
1515+import { buildTrackString, capitalize, truncateStringToLength } from "../../core/StringUtils.js";
1616+import { hasNodeNetworkException } from "../common/errors/NodeErrors.js";
1717+import { hasUpstreamError, UpstreamError } from "../common/errors/UpstreamError.js";
1118import {
1219 ARTIST_WEIGHT,
1320 Authenticatable,
···1926 INITIALIZING,
2027 InitState,
2128 NOT_INITIALIZED,
2222- REFERENCE_WEIGHT,
2329 ScrobbledPlayObject,
2430 TIME_WEIGHT,
2531 TITLE_WEIGHT,
2632} from "../common/infrastructure/Atomic.js";
2727-import {childLogger, Logger} from "@foxxmd/logging";
2833import { CommonClientConfig } from "../common/infrastructure/config/client/index.js";
2934import { Notifiers } from "../notifier/Notifiers.js";
3030-import {FixedSizeList} from 'fixed-size-list';
3135import {
3232- DeadLetterScrobble,
3333- PlayObject,
3434- QueuedScrobble,
3535- TA_CLOSE,
3636- TA_FUZZY,
3737- TrackStringOptions,
3838-} from "../../core/Atomic.js";
3939-import { buildTrackString, capitalize, truncateStringToLength } from "../../core/StringUtils.js";
4040-import EventEmitter from "events";
3636+ comparingMultipleArtists,
3737+ playObjDataMatch,
3838+ pollingBackoff,
3939+ setIntersection,
4040+ sleep,
4141+ sortByOldestPlayDate,
4242+} from "../utils.js";
4143import { compareScrobbleArtists, compareScrobbleTracks, normalizeStr } from "../utils/StringUtils.js";
4242-import { hasUpstreamError, UpstreamError } from "../common/errors/UpstreamError.js";
4343-import {nanoid} from "nanoid";
4444-import {messageWithCauses} from "pony-cause";
4545-import { hasNodeNetworkException } from "../common/errors/NodeErrors.js";
4644import {
4745 comparePlayTemporally,
4846 temporalAccuracyIsAtLeast,
+10-19
src/backend/scrobblers/LastfmScrobbler.ts
···11-import AbstractScrobbleClient from "./AbstractScrobbleClient.js";
11+import { Logger } from "@foxxmd/logging";
22import dayjs from 'dayjs';
33-44-import {
55- playObjDataMatch,
66- removeUndefinedKeys,
77- setIntersection,
88- sleep,
99- sortByOldestPlayDate,
1010-} from "../utils.js";
1111-import LastfmApiClient from "../common/vendor/LastfmApiClient.js";
1212-import { FormatPlayObjectOptions, INITIALIZING, ScrobbledPlayObject } from "../common/infrastructure/Atomic.js";
1313-import { LastfmClientConfig } from "../common/infrastructure/config/client/lastfm.js";
1414-import {TrackScrobblePayload, TrackScrobbleResponse, UserGetRecentTracksResponse} from "lastfm-node-client";
1515-import { Notifiers } from "../notifier/Notifiers.js";
1616-import {Logger} from "@foxxmd/logging";
1717-import { PlayObject, TrackStringOptions } from "../../core/Atomic.js";
1818-import { buildTrackString, capitalize } from "../../core/StringUtils.js";
193import EventEmitter from "events";
2020-import { UpstreamError } from "../common/errors/UpstreamError.js";
44+import { TrackScrobbleResponse, UserGetRecentTracksResponse } from "lastfm-node-client";
55+import { PlayObject } from "../../core/Atomic.js";
66+import { buildTrackString, capitalize } from "../../core/StringUtils.js";
217import { isNodeNetworkException } from "../common/errors/NodeErrors.js";
2222-import { getScrobbleTsSOCDate } from "../utils/TimeUtils.js";
88+import { UpstreamError } from "../common/errors/UpstreamError.js";
99+import { FormatPlayObjectOptions, INITIALIZING } from "../common/infrastructure/Atomic.js";
1010+import { LastfmClientConfig } from "../common/infrastructure/config/client/lastfm.js";
1111+import LastfmApiClient from "../common/vendor/LastfmApiClient.js";
1212+import { Notifiers } from "../notifier/Notifiers.js";
1313+import AbstractScrobbleClient from "./AbstractScrobbleClient.js";
23142415export default class LastfmScrobbler extends AbstractScrobbleClient {
2516
+9-9
src/backend/scrobblers/ListenbrainzScrobbler.ts
···11+import { Logger } from "@foxxmd/logging";
12import dayjs from 'dayjs';
22-33-import AbstractScrobbleClient from "./AbstractScrobbleClient.js";
33+import EventEmitter from "events";
44+import { PlayObject } from "../../core/Atomic.js";
55+import { buildTrackString, capitalize } from "../../core/StringUtils.js";
66+import { isNodeNetworkException } from "../common/errors/NodeErrors.js";
77+import { UpstreamError } from "../common/errors/UpstreamError.js";
48import { FormatPlayObjectOptions, INITIALIZING } from "../common/infrastructure/Atomic.js";
55-import { Notifiers } from "../notifier/Notifiers.js";
66-import {Logger} from "@foxxmd/logging";
79import { ListenBrainzClientConfig } from "../common/infrastructure/config/client/listenbrainz.js";
810import { ListenbrainzApiClient, ListenPayload } from "../common/vendor/ListenbrainzApiClient.js";
99-import { PlayObject, TrackStringOptions } from "../../core/Atomic.js";
1010-import { buildTrackString, capitalize } from "../../core/StringUtils.js";
1111-import EventEmitter from "events";
1212-import { UpstreamError } from "../common/errors/UpstreamError.js";
1313-import { isNodeNetworkException } from "../common/errors/NodeErrors.js";
1111+import { Notifiers } from "../notifier/Notifiers.js";
1212+1313+import AbstractScrobbleClient from "./AbstractScrobbleClient.js";
14141515export default class ListenbrainzScrobbler extends AbstractScrobbleClient {
1616
+13-13
src/backend/scrobblers/MalojaScrobbler.ts
···11-import AbstractScrobbleClient from "./AbstractScrobbleClient.js";
22-import request, {ResponseError, SuperAgentRequest} from 'superagent';
33-import dayjs from 'dayjs';
11+import { Logger } from "@foxxmd/logging";
42import compareVersions from 'compare-versions';
55-import { sleep, parseRetryAfterSecsFromObj } from "../utils.js";
33+import dayjs from 'dayjs';
44+import EventEmitter from "events";
55+import normalizeUrl from "normalize-url";
66+import request, { SuperAgentRequest } from 'superagent';
77+import { PlayObject } from "../../core/Atomic.js";
88+import { buildTrackString, capitalize } from "../../core/StringUtils.js";
99+import { isSuperAgentResponseError } from "../common/errors/ErrorUtils.js";
1010+import { isNodeNetworkException } from "../common/errors/NodeErrors.js";
1111+import { UpstreamError } from "../common/errors/UpstreamError.js";
612import { DEFAULT_RETRY_MULTIPLIER, FormatPlayObjectOptions, INITIALIZING } from "../common/infrastructure/Atomic.js";
713import { MalojaClientConfig } from "../common/infrastructure/config/client/maloja.js";
88-import { Notifiers } from "../notifier/Notifiers.js";
99-import {Logger} from "@foxxmd/logging";
1014import {
1115 getMalojaResponseError,
1216 isMalojaAPIErrorBody,
···1923 MalojaV2ScrobbleData,
2024 MalojaV3ScrobbleData,
2125} from "../common/vendor/maloja/interfaces.js";
2222-import { PlayObject, TrackStringOptions } from "../../core/Atomic.js";
2323-import { buildTrackString, capitalize } from "../../core/StringUtils.js";
2424-import EventEmitter from "events";
2525-import normalizeUrl from "normalize-url";
2626-import { UpstreamError } from "../common/errors/UpstreamError.js";
2626+import { Notifiers } from "../notifier/Notifiers.js";
2727+import { parseRetryAfterSecsFromObj, sleep } from "../utils.js";
2728import { getScrobbleTsSOCDate, getScrobbleTsSOCDateWithContext } from "../utils/TimeUtils.js";
2828-import { isNodeNetworkException } from "../common/errors/NodeErrors.js";
2929-import { isSuperAgentResponseError } from "../common/errors/ErrorUtils.js";
2929+import AbstractScrobbleClient from "./AbstractScrobbleClient.js";
30303131const feat = ["ft.", "ft", "feat.", "feat", "featuring", "Ft.", "Ft", "Feat.", "Feat", "Featuring"];
3232
+13-16
src/backend/scrobblers/ScrobbleClients.ts
···11/* eslint-disable no-case-declarations */
22-import dayjs, {Dayjs} from "dayjs";
33-import {
44- readJson,
55- validateJson,
66-} from "../utils.js";
77-import MalojaScrobbler from "./MalojaScrobbler.js";
88-import LastfmScrobbler from "./LastfmScrobbler.js";
22+import { childLogger, Logger } from '@foxxmd/logging';
33+import dayjs, { Dayjs } from "dayjs";
44+import { PlayObject } from "../../core/Atomic.js";
95import { clientTypes, ConfigMeta } from "../common/infrastructure/Atomic.js";
106import { AIOConfig } from "../common/infrastructure/config/aioConfig.js";
77+import { ClientAIOConfig, ClientConfig } from "../common/infrastructure/config/client/clients.js";
88+import { LastfmClientConfig } from "../common/infrastructure/config/client/lastfm.js";
99+import { ListenBrainzClientConfig } from "../common/infrastructure/config/client/listenbrainz.js";
1010+import { MalojaClientConfig } from "../common/infrastructure/config/client/maloja.js";
1111import * as aioSchema from '../common/schema/aio-client.json';
1212import * as clientSchema from '../common/schema/client.json';
1313-import { ClientAIOConfig, ClientConfig } from "../common/infrastructure/config/client/clients.js";
1414-import { MalojaClientConfig } from "../common/infrastructure/config/client/maloja.js";
1515-import { LastfmClientConfig } from "../common/infrastructure/config/client/lastfm.js";
1616-import { Notifiers } from "../notifier/Notifiers.js";
1717-import AbstractScrobbleClient from "./AbstractScrobbleClient.js";
1818-import {childLogger, Logger} from '@foxxmd/logging';
1919-import ListenbrainzScrobbler from "./ListenbrainzScrobbler.js";
2020-import { ListenBrainzClientConfig } from "../common/infrastructure/config/client/listenbrainz.js";
2121-import { PlayObject } from "../../core/Atomic.js";
2213import { WildcardEmitter } from "../common/WildcardEmitter.js";
1414+import { Notifiers } from "../notifier/Notifiers.js";
1515+import { readJson, validateJson, } from "../utils.js";
1616+import AbstractScrobbleClient from "./AbstractScrobbleClient.js";
1717+import LastfmScrobbler from "./LastfmScrobbler.js";
1818+import ListenbrainzScrobbler from "./ListenbrainzScrobbler.js";
1919+import MalojaScrobbler from "./MalojaScrobbler.js";
23202421type groupedNamedConfigs = {[key: string]: ParsedConfig[]};
2522
+21-20
src/backend/server/api.ts
···11-import {ExpressWithAsync} from "@awaitjs/express";
22-import { getRoot } from "../ioc.js";
33-import { makeClientCheckMiddle, makeSourceCheckMiddle } from "./middleware.js";
44-import AbstractSource from "../sources/AbstractSource.js";
11+import { ExpressWithAsync } from "@awaitjs/express";
22+import { LogDataPretty, Logger, LogLevel } from "@foxxmd/logging";
33+import bsseDef from 'better-sse';
44+import bodyParser from "body-parser";
55+import { FixedSizeList } from 'fixed-size-list';
66+import { PassThrough } from "node:stream";
77+import { Transform } from "stream";
58import {
69 ClientStatusData,
77- DeadLetterScrobble, LeveledLogData,
1010+ DeadLetterScrobble,
1111+ LeveledLogData,
812 LogOutputConfig,
99- PlayObject, SOURCE_SOT,
1313+ PlayObject,
1414+ SOURCE_SOT,
1015 SourceStatusData,
1116} from "../../core/Atomic.js";
1212-import {LogDataPretty, Logger, LogLevel} from "@foxxmd/logging";
1313-import {Transform} from "stream";
1414-import * as bsse from 'better-sse';
1515-import bsseDef from 'better-sse';
1616-import { setupTautulliRoutes } from "./tautulliRoutes.js";
1717-import { setupPlexRoutes } from "./plexRoutes.js";
1818-import { setupJellyfinRoutes } from "./jellyfinRoutes.js";
1919-import { setupDeezerRoutes } from "./deezerRoutes.js";
2020-import { setupAuthRoutes } from "./auth.js";
2121-import {ExpressHandler} from "../common/infrastructure/Atomic.js";
2222-import MemorySource from "../sources/MemorySource.js";
2317import { capitalize } from "../../core/StringUtils.js";
1818+import { ExpressHandler } from "../common/infrastructure/Atomic.js";
1919+import { getRoot } from "../ioc.js";
2420import AbstractScrobbleClient from "../scrobblers/AbstractScrobbleClient.js";
2121+import AbstractSource from "../sources/AbstractSource.js";
2222+import MemorySource from "../sources/MemorySource.js";
2523import { sortByNewestPlayDate } from "../utils.js";
2626-import bodyParser from "body-parser";
2424+import { setupAuthRoutes } from "./auth.js";
2525+import { setupDeezerRoutes } from "./deezerRoutes.js";
2626+import { setupJellyfinRoutes } from "./jellyfinRoutes.js";
2727+import { makeClientCheckMiddle, makeSourceCheckMiddle } from "./middleware.js";
2828+import { setupPlexRoutes } from "./plexRoutes.js";
2929+import { setupTautulliRoutes } from "./tautulliRoutes.js";
2730import { setupWebscrobblerRoutes } from "./webscrobblerRoutes.js";
2828-import {FixedSizeList} from 'fixed-size-list';
2929-import {PassThrough} from "node:stream";
30313132const maxBufferSize = 300;
3233const output: Record<number, FixedSizeList<LogDataPretty>> = {};
+4-4
src/backend/server/auth.ts
···11-import {ExpressWithAsync} from "@awaitjs/express";
22-import {Logger} from "@foxxmd/logging";
33-import ScrobbleSources from "../sources/ScrobbleSources.js";
11+import { ExpressWithAsync } from "@awaitjs/express";
22+import { Logger } from "@foxxmd/logging";
43import passport from "passport";
54import { ExpressHandler } from "../common/infrastructure/Atomic.js";
66-import ScrobbleClients from "../scrobblers/ScrobbleClients.js";
75import LastfmScrobbler from "../scrobblers/LastfmScrobbler.js";
66+import ScrobbleClients from "../scrobblers/ScrobbleClients.js";
87import LastfmSource from "../sources/LastfmSource.js";
88+import ScrobbleSources from "../sources/ScrobbleSources.js";
99import SpotifySource from "../sources/SpotifySource.js";
10101111export const setupAuthRoutes = (app: ExpressWithAsync, logger: Logger, sourceMiddle: ExpressHandler, clientMiddle: ExpressHandler, scrobbleSources: ScrobbleSources, scrobbleClients: ScrobbleClients) => {
+5-8
src/backend/server/deezerRoutes.ts
···11-import { ExpressHandler } from "../common/infrastructure/Atomic.js";
22-import { mergeArr, parseBool, sleep } from "../utils.js";
33-import {ExpressWithAsync} from "@awaitjs/express";
44-import {Logger} from "@foxxmd/logging";
55-import ScrobbleSources from "../sources/ScrobbleSources.js";
66-import PlexSource, { plexRequestMiddle } from "../sources/PlexSource.js";
77-import { PlexNotifier } from "../sources/ingressNotifiers/PlexNotifier.js";
88-import DeezerSource from "../sources/DeezerSource.js";
11+import { ExpressWithAsync } from "@awaitjs/express";
22+import { Logger } from "@foxxmd/logging";
93import passport from "passport";
44+import DeezerSource from "../sources/DeezerSource.js";
55+import ScrobbleSources from "../sources/ScrobbleSources.js";
66+import { sleep } from "../utils.js";
107118export const setupDeezerRoutes = (app: ExpressWithAsync, logger: Logger, scrobbleSources: ScrobbleSources) => {
129
+8-9
src/backend/server/index.ts
···11-import {addAsync, Router} from '@awaitjs/express';
22-import express from 'express';
33-import ViteExpress from "vite-express";
11+import { addAsync, Router } from '@awaitjs/express';
22+import { childLogger, LogDataPretty, Logger } from "@foxxmd/logging";
43import bodyParser from 'body-parser';
55-import passport from 'passport';
44+import { stripIndents } from "common-tags";
55+import express from 'express';
66import session from 'express-session';
77+import { PassThrough } from "node:stream";
88+import passport from 'passport';
99+import ViteExpress from "vite-express";
710import { getRoot } from "../ioc.js";
1111+import { getAddress, parseBool } from "../utils.js";
812import { setupApi } from "./api.js";
99-import { getAddress, mergeArr, parseBool } from "../utils.js";
1010-import {stripIndents} from "common-tags";
1111-import {childLogger, LogData, LogDataPretty} from "@foxxmd/logging";
1212-import {PassThrough} from "node:stream";
1313-import {Logger} from '@foxxmd/logging';
14131514const app = addAsync(express());
1615const router = Router();
+4-4
src/backend/server/jellyfinRoutes.ts
···11-import { parseBool, remoteHostIdentifiers } from "../utils.js";
22-import {ExpressWithAsync} from "@awaitjs/express";
33-import {Logger} from "@foxxmd/logging";
44-import ScrobbleSources from "../sources/ScrobbleSources.js";
11+import { ExpressWithAsync } from "@awaitjs/express";
22+import { Logger } from "@foxxmd/logging";
53import bodyParser from "body-parser";
64import { JellyfinNotifier } from "../sources/ingressNotifiers/JellyfinNotifier.js";
75import JellyfinSource from "../sources/JellyfinSource.js";
66+import ScrobbleSources from "../sources/ScrobbleSources.js";
77+import { parseBool, remoteHostIdentifiers } from "../utils.js";
8899export const setupJellyfinRoutes = (app: ExpressWithAsync, logger: Logger, scrobbleSources: ScrobbleSources) => {
1010
···11-import { GenericPlayerState } from "./GenericPlayerState.js";
22-import {Logger} from "@foxxmd/logging";
11+import { Logger } from "@foxxmd/logging";
22+import { PlayObject } from "../../../core/Atomic.js";
33import { PlayPlatformId, ReportedPlayerStatus } from "../../common/infrastructure/Atomic.js";
44import { PlayerStateOptions } from "./AbstractPlayerState.js";
55-import { PlayObject } from "../../../core/Atomic.js";
55+import { GenericPlayerState } from "./GenericPlayerState.js";
6677export class JellyfinPlayerState extends GenericPlayerState {
88 constructor(logger: Logger, platformId: PlayPlatformId, opts?: PlayerStateOptions) {
+1-1
src/backend/sources/PlayerState/ListenProgress.ts
···11-import dayjs, {Dayjs} from "dayjs";
11+import dayjs, { Dayjs } from "dayjs";
2233import { PlayProgress, Second } from "../../../core/Atomic.js";
44
+2-2
src/backend/sources/PlayerState/ListenRange.ts
···11-import { ListenProgress } from "./ListenProgress.js";
22-import dayjs, {Dayjs} from "dayjs";
11+import dayjs, { Dayjs } from "dayjs";
32import { ListenRangeData, Second } from "../../../core/Atomic.js";
33+import { ListenProgress } from "./ListenProgress.js";
4455export class ListenRange implements ListenRangeData {
66
···11-import { IngressNotifier } from "./IngressNotifier.js";
22-import {Request} from "express";
11+import { Logger } from "@foxxmd/logging";
22+import { Request } from "express";
33import path from "path";
44-import {Logger} from "@foxxmd/logging";
44+import { IngressNotifier } from "./IngressNotifier.js";
5566export class WebhookNotifier extends IngressNotifier {
77
+6-6
src/backend/tests/jellyfin/jellyfin.test.ts
···11-import {describe, it} from 'mocha';
22-import {assert} from 'chai';
33-import samplePayload from './playbackProgressSample.json';
11+import { loggerTest } from "@foxxmd/logging";
22+import { assert } from 'chai';
33+import EventEmitter from "events";
44+import { describe, it } from 'mocha';
55+import { JsonPlayObject } from "../../../core/Atomic.js";
4657import JellyfinSource from "../../sources/JellyfinSource.js";
66-import EventEmitter from "events";
77-import { JsonPlayObject, PlayObject } from "../../../core/Atomic.js";
88-import {loggerTest} from "@foxxmd/logging";
88+import samplePayload from './playbackProgressSample.json';
991010const dataAsFixture = (data: any): TestFixture => {
1111 return data as TestFixture;
···11-import {describe, it} from 'mocha';
22-import {assert} from 'chai';
11+import { loggerTest } from "@foxxmd/logging";
22+import { assert } from 'chai';
33+import dayjs from "dayjs";
44+import { describe, it } from 'mocha';
55+import { http, HttpResponse } from "msw";
66+import { PlayObject } from "../../../core/Atomic.js";
77+import { UpstreamError } from "../../common/errors/UpstreamError.js";
88+99+import { ListenbrainzApiClient, ListenResponse } from "../../common/vendor/ListenbrainzApiClient.js";
1010+import { ExpectedResults } from "../utils/interfaces.js";
1111+import { withRequestInterception } from "../utils/networking.js";
1212+import artistWithProperJoiner from './correctlyMapped/artistProperHasJoinerInName.json';
313// correct mappings
414import multiArtistInArtistName from './correctlyMapped/multiArtistInArtistName.json';
515import multiArtistsInTrackName from './correctlyMapped/multiArtistInTrackName.json';
66-import noArtistMapping from './correctlyMapped/noArtistMapping.json';
716import multiMappedArtistsWithSingleUserArtist from './correctlyMapped/multiArtistMappingWithSingleRecordedArtist.json';
88-import artistWithProperJoiner from './correctlyMapped/artistProperHasJoinerInName.json';
1717+import noArtistMapping from './correctlyMapped/noArtistMapping.json';
918import normalizedValues from './correctlyMapped/normalizedName.json';
1019import slightlyDifferentNames from './correctlyMapped/trackNameSlightlyDifferent.json';
11201221// incorrect mappings
1322import incorrectMultiArtistsTrackName from './incorrectlyMapped/multiArtistsInTrackName.json';
1423import veryWrong from './incorrectlyMapped/veryWrong.json';
1515-1616-import { ListenbrainzApiClient, ListenResponse } from "../../common/vendor/ListenbrainzApiClient.js";
1717-import { PlayObject } from "../../../core/Atomic.js";
1818-import dayjs from "dayjs";
1919-import { withRequestInterception } from "../utils/networking.js";
2020-import {http, HttpResponse} from "msw";
2121-import { UpstreamError } from "../../common/errors/UpstreamError.js";
2222-import { ExpectedResults } from "../utils/interfaces.js";
2323-import {loggerTest} from "@foxxmd/logging";
24242525interface LZTestFixture {
2626 data: ListenResponse
+13-8
src/backend/tests/player/player.test.ts
···11-import {describe, it} from 'mocha';
22-import {assert} from 'chai';
33-import { generatePlay } from "../utils/PlayTestUtils.js";
44-import { GenericPlayerState } from "../../sources/PlayerState/GenericPlayerState.js";
55-import { CALCULATED_PLAYER_STATUSES, NO_DEVICE, NO_USER, REPORTED_PLAYER_STATUSES } from "../../common/infrastructure/Atomic.js";
66-import { playObjDataMatch } from "../../utils.js";
77-import dayjs from "dayjs";
11+import { loggerTest } from "@foxxmd/logging";
22+import { assert } from 'chai';
83import clone from "clone";
99-import {loggerTest} from "@foxxmd/logging";
44+import dayjs from "dayjs";
55+import { describe, it } from 'mocha';
66+import {
77+ CALCULATED_PLAYER_STATUSES,
88+ NO_DEVICE,
99+ NO_USER,
1010+ REPORTED_PLAYER_STATUSES
1111+} from "../../common/infrastructure/Atomic.js";
1212+import { GenericPlayerState } from "../../sources/PlayerState/GenericPlayerState.js";
1313+import { playObjDataMatch } from "../../utils.js";
1414+import { generatePlay } from "../utils/PlayTestUtils.js";
10151116const logger = loggerTest;
1217
+4-4
src/backend/tests/scrobbler/TestScrobbler.ts
···11-import AbstractScrobbleClient from "../../scrobblers/AbstractScrobbleClient.js";
22-import { PlayObject } from "../../../core/Atomic.js";
33-import { Notifiers } from "../../notifier/Notifiers.js";
11+import { loggerTest } from "@foxxmd/logging";
42import EventEmitter from "events";
53import request from "superagent";
66-import {loggerTest} from "@foxxmd/logging";
44+import { PlayObject } from "../../../core/Atomic.js";
55+import { Notifiers } from "../../notifier/Notifiers.js";
66+import AbstractScrobbleClient from "../../scrobblers/AbstractScrobbleClient.js";
7788export class TestScrobbler extends AbstractScrobbleClient {
99
+11-12
src/backend/tests/scrobbler/scrobblers.test.ts
···11-import {describe, it, after, before} from 'mocha';
22-import chai, {assert} from 'chai';
11+import chai, { assert } from 'chai';
32import asPromised from 'chai-as-promised';
43import clone from 'clone';
55-import pEvent from 'p-event';
44+import dayjs from "dayjs";
55+import { after, before, describe, it } from 'mocha';
66import { http, HttpResponse } from 'msw';
77-88-chai.use(asPromised);
99-1010-import withDuration from '../plays/withDuration.json';
77+import pEvent from 'p-event';
88+import { PlayObject } from "../../../core/Atomic.js";
99+import { sleep } from "../../utils.js";
1110import mixedDuration from '../plays/mixedDuration.json';
1111+import withDuration from '../plays/withDuration.json';
1212+import { MockNetworkError, withRequestInterception } from "../utils/networking.js";
1313+import { asPlays, generatePlay, normalizePlays } from "../utils/PlayTestUtils.js";
12141315import { TestScrobbler } from "./TestScrobbler.js";
1414-import { asPlays, generatePlay, normalizePlays } from "../utils/PlayTestUtils.js";
1515-import dayjs from "dayjs";
1616-import { sleep } from "../../utils.js";
1717-import { MockNetworkError, withRequestInterception } from "../utils/networking.js";
1818-import { PlayObject } from "../../../core/Atomic.js";
1616+1717+chai.use(asPromised);
19182019const firstPlayDate = dayjs().subtract(1, 'hour');
2120const olderFirstPlayDate = dayjs().subtract(4, 'hour');
+5-5
src/backend/tests/utils/PlayTestUtils.ts
···11-import { JsonPlayObject, ObjectPlayData, PlayData, PlayMeta, PlayObject } from "../../../core/Atomic.js";
22-import dayjs, {Dayjs} from "dayjs";
33-import utc from "dayjs/plugin/utc.js";
11+import { faker } from '@faker-js/faker';
22+import dayjs, { Dayjs } from "dayjs";
33+import duration from "dayjs/plugin/duration.js";
44import isBetween from "dayjs/plugin/isBetween.js";
55import relativeTime from "dayjs/plugin/relativeTime.js";
66-import duration from "dayjs/plugin/duration.js";
76import timezone from "dayjs/plugin/timezone.js";
88-import {faker} from '@faker-js/faker';
77+import utc from "dayjs/plugin/utc.js";
88+import { JsonPlayObject, ObjectPlayData, PlayMeta, PlayObject } from "../../../core/Atomic.js";
991010dayjs.extend(utc)
1111dayjs.extend(isBetween);
+1-1
src/backend/tests/utils/networking.ts
···11+import { setupServer, SetupServer } from 'msw/node';
12import { NodeNetworkErrorCode, NodeNetworkException } from "../../common/errors/NodeErrors.js";
22-import { setupServer, SetupServer } from 'msw/node';
3344export class MockNetworkError extends Error implements NodeNetworkException {
55
+4-4
src/backend/tests/utils/strings.test.ts
···11-import {describe, it} from 'mocha';
22-import {assert} from 'chai';
11+import { assert } from 'chai';
22+import { describe, it } from 'mocha';
33+import { intersect } from "../../utils.js";
34import {
45 compareNormalizedStrings,
56 normalizeStr,
67 parseTrackCredits,
78 uniqueNormalizedStrArr
89} from "../../utils/StringUtils.js";
99-import testData from './playTestData.json';
1010import { ExpectedResults } from "./interfaces.js";
1111-import { intersect } from "../../utils.js";
1111+import testData from './playTestData.json';
12121313interface PlayTestFixture {
1414 caseHints: string[]