diff --git a/src/backend/common/vendor/ListenbrainzApiClient.ts b/src/backend/common/vendor/ListenbrainzApiClient.ts index c3720311..5866d359 100644 --- a/src/backend/common/vendor/ListenbrainzApiClient.ts +++ b/src/backend/common/vendor/ListenbrainzApiClient.ts @@ -4,7 +4,6 @@ import request, { Request, Response } from 'superagent'; import { BrainzMeta, PlayObject, PlayObjectLifecycleless, ScrobbleActionResult, UnixTimestamp, URLData } from "../../../core/Atomic.js"; import { combinePartsToString, slice } from "../../../core/StringUtils.js"; import { - findDelimiters, normalizeListenbrainzUrl, normalizeStr, parseArtistCredits, @@ -12,6 +11,7 @@ import { parseTrackCredits, uniqueNormalizedStrArr, } from "../../utils/StringUtils.js"; +import { findDelimiters } from "../../../core/StringUtils.js"; import { getScrobbleTsSOCDate } from "../../utils/TimeUtils.js"; import { UpstreamError } from "../errors/UpstreamError.js"; import { AbstractApiOptions, DEFAULT_RETRY_MULTIPLIER, DELIMITERS, FormatPlayObjectOptions, PagelessListensTimeRangeOptions, PagelessTimeRangeListens, PagelessTimeRangeListensResult } from "../infrastructure/Atomic.js"; diff --git a/src/backend/server/api.ts b/src/backend/server/api.ts index 6c9e7ac0..12701f3e 100644 --- a/src/backend/server/api.ts +++ b/src/backend/server/api.ts @@ -21,7 +21,8 @@ import { getRoot } from "../ioc.js"; import AbstractScrobbleClient from "../scrobblers/AbstractScrobbleClient.js"; import AbstractSource from "../sources/AbstractSource.js"; import MemorySource from "../sources/MemorySource.js"; -import { parseBool, sortByNewestPlayDate } from "../utils.js"; +import { parseBool } from "../utils.js"; +import { sortByNewestPlayDate } from '../../core/PlayUtils.js'; import { setupAuthRoutes } from "./auth.js"; import { setupDeezerRoutes } from "./deezerRoutes.js"; import {setupLZEndpointRoutes} from "./endpointListenbrainzRoutes.js"; diff --git a/src/backend/sources/AbstractSource.ts b/src/backend/sources/AbstractSource.ts index f0ef0a0a..15522711 100644 --- a/src/backend/sources/AbstractSource.ts +++ b/src/backend/sources/AbstractSource.ts @@ -29,9 +29,9 @@ import { playObjDataMatch, pollingBackoff, sleep, - sortByNewestPlayDate, sortByOldestPlayDate, } from "../utils.js"; +import { sortByNewestPlayDate } from '../../core/PlayUtils.js'; import { formatNumber } from '../../core/DataUtils.js'; import { timeToHumanTimestamp } from "../utils/TimeUtils.js"; import { todayAwareFormat } from "../../core/TimeUtils.js"; diff --git a/src/backend/sources/ChromecastSource.ts b/src/backend/sources/ChromecastSource.ts index 8722eac3..6486774a 100644 --- a/src/backend/sources/ChromecastSource.ts +++ b/src/backend/sources/ChromecastSource.ts @@ -30,7 +30,8 @@ import { PlatformApplicationWithContext, PlatformType } from "../common/vendor/chromecast/interfaces.js"; -import { difference, genGroupIdStr, isDebugMode, parseBool } from "../utils.js"; +import { difference, isDebugMode, parseBool } from "../utils.js"; +import { genGroupIdStr } from '../../core/PlayUtils.js'; import { findCauseByReference } from "../utils/ErrorUtils.js"; import { discoveryAvahi, discoveryNative } from "../utils/MDNSUtils.js"; import { RecentlyPlayedOptions } from "./AbstractSource.js"; diff --git a/src/backend/sources/JellyfinApiSource.ts b/src/backend/sources/JellyfinApiSource.ts index 9bb7540a..42820d6d 100644 --- a/src/backend/sources/JellyfinApiSource.ts +++ b/src/backend/sources/JellyfinApiSource.ts @@ -55,7 +55,8 @@ import { PlayPlatformId, REPORTED_PLAYER_STATUSES } from "../common/infrastructure/Atomic.js"; import { JellyApiSourceConfig } from "../common/infrastructure/config/source/jellyfin.js"; -import { genGroupIdStr, getPlatformIdFromData, isDebugMode, parseBool, } from "../utils.js"; +import { getPlatformIdFromData, isDebugMode, parseBool, } from "../utils.js"; +import { genGroupIdStr } from '../../core/PlayUtils.js'; import { joinedUrl } from "../utils/NetworkUtils.js"; import { hashObject, parseArrayFromMaybeString } from "../utils/StringUtils.js"; import { MemoryPositionalSource } from "./MemoryPositionalSource.js"; diff --git a/src/backend/sources/MemorySource.ts b/src/backend/sources/MemorySource.ts index 49deda64..ed6d5ad0 100644 --- a/src/backend/sources/MemorySource.ts +++ b/src/backend/sources/MemorySource.ts @@ -14,12 +14,12 @@ import { SourceType, SourceConfig } from '../common/infrastructure/config/source import { PollingOptions } from "../common/infrastructure/config/common.js"; import { genGroupId, - genGroupIdStr, getPlatformIdFromData, isDebugMode, playObjDataMatch, thresholdResultSummary, } from "../utils.js"; +import { genGroupIdStr } from '../../core/PlayUtils.js'; import { formatNumber } from '../../core/DataUtils.js'; import { timePassesScrobbleThreshold, timeToHumanTimestamp } from "../utils/TimeUtils.js"; import { PromisePool } from "@supercharge/promise-pool"; diff --git a/src/backend/sources/PlayerState/AbstractPlayerState.ts b/src/backend/sources/PlayerState/AbstractPlayerState.ts index eaaa424d..7ab4c7d2 100644 --- a/src/backend/sources/PlayerState/AbstractPlayerState.ts +++ b/src/backend/sources/PlayerState/AbstractPlayerState.ts @@ -13,7 +13,8 @@ import { ReportedPlayerStatus, } from "../../common/infrastructure/Atomic.js"; import { PollingOptions } from "../../common/infrastructure/config/common.js"; -import { genGroupIdStr, playObjDataMatch, progressBar } from "../../utils.js"; +import { playObjDataMatch, progressBar } from "../../utils.js"; +import { genGroupIdStr } from '../../../core/PlayUtils.js'; import { formatNumber } from '../../../core/DataUtils.js'; import { ListenProgress } from "./ListenProgress.js"; import { ListenRange, ListenRangePositional } from "./ListenRange.js"; diff --git a/src/backend/sources/PlexApiSource.ts b/src/backend/sources/PlexApiSource.ts index cf4ac8b9..26ca83fc 100644 --- a/src/backend/sources/PlexApiSource.ts +++ b/src/backend/sources/PlexApiSource.ts @@ -11,7 +11,8 @@ import { PlayerStateDataMaybePlay, PlayPlatformId, REPORTED_PLAYER_STATUSES } from "../common/infrastructure/Atomic.js"; -import { genGroupIdStr, getFirstNonEmptyString, getPlatformIdFromData, isDebugMode, parseBool, } from "../utils.js"; +import { getFirstNonEmptyString, getPlatformIdFromData, isDebugMode, parseBool, } from "../utils.js"; +import { genGroupIdStr } from '../../core/PlayUtils.js'; import { buildStatePlayerPlayIdententifyingInfo, hashObject, parseArrayFromMaybeString } from "../utils/StringUtils.js"; import { GetSessionsMetadata } from "@lukehagar/plexjs/sdk/models/operations/getsessions.js"; import { PlexAPI, HTTPClient, Fetcher } from "@lukehagar/plexjs"; diff --git a/src/backend/tests/cache/cache.test.ts b/src/backend/tests/cache/cache.test.ts index f694afbc..4b005251 100644 --- a/src/backend/tests/cache/cache.test.ts +++ b/src/backend/tests/cache/cache.test.ts @@ -6,7 +6,7 @@ import dayjs from "dayjs"; import withLocalTmpDir from 'with-local-tmp-dir'; import path from 'path'; import { initFileCache, initMemoryCache, initValkeyCache, MSCache } from "../../common/Cache.js"; -import { generatePlay, generatePlayerStateData, generatePlays, normalizePlays } from "../utils/PlayTestUtils.js"; +import { generatePlay, generatePlayerStateData, generatePlays, normalizePlays } from "../../../core/PlayTestUtils.js"; import { ListenProgressPositional, ListenProgressTS } from "../../sources/PlayerState/ListenProgress.js"; import { isPortReachableConnect } from "../../utils/NetworkUtils.js"; import { getRoot } from "../../ioc.js"; diff --git a/src/backend/tests/component/transformers.test.ts b/src/backend/tests/component/transformers.test.ts index 8dc037a5..05aa0810 100644 --- a/src/backend/tests/component/transformers.test.ts +++ b/src/backend/tests/component/transformers.test.ts @@ -7,7 +7,7 @@ import AbstractComponent, { AbstractComponentConfig } from "../../common/Abstrac import { ConditionalSearchAndReplaceRegExp, STAGE_TYPES, STAGE_TYPES_METADATA, STAGE_TYPES_USER, TRANSFORM_HOOK } from "../../common/infrastructure/Transform.js"; import { isConditionalSearchAndReplace } from "../../utils/PlayTransformUtils.js"; -import { asPlays, generateArtistsStr, generatePlay, normalizePlays } from "../utils/PlayTestUtils.js"; +import { asPlays, generateArtistsStr, generatePlay, normalizePlays } from "../../../core/PlayTestUtils.js"; import { WebhookPayload } from "../../common/infrastructure/config/health/webhooks.js"; import { findCauseByMessage } from "../../utils/ErrorUtils.js"; import NativeTransformer from "../../common/transforms/NativeTransformer.js"; diff --git a/src/backend/tests/jellyfin/jellyfin.test.ts b/src/backend/tests/jellyfin/jellyfin.test.ts index 229402f4..db6ed485 100644 --- a/src/backend/tests/jellyfin/jellyfin.test.ts +++ b/src/backend/tests/jellyfin/jellyfin.test.ts @@ -7,7 +7,7 @@ import { JsonPlayObject, PlayMeta, PlayObject } from "../../../core/Atomic.js"; import JellyfinApiSource from "../../sources/JellyfinApiSource.js"; import validSession from './validSession.json' with { type: "json" }; import { JellyApiData } from "../../common/infrastructure/config/source/jellyfin.js"; -import { generatePlay } from "../utils/PlayTestUtils.js"; +import { generatePlay } from "../../../core/PlayTestUtils.js"; import { fakerJA } from "@faker-js/faker"; import { // @ts-expect-error weird typings? diff --git a/src/backend/tests/lastfm/lastfm.test.ts b/src/backend/tests/lastfm/lastfm.test.ts index d1d2a9fc..1623f454 100644 --- a/src/backend/tests/lastfm/lastfm.test.ts +++ b/src/backend/tests/lastfm/lastfm.test.ts @@ -1,7 +1,7 @@ import chai, { expect } from 'chai'; import asPromised from 'chai-as-promised'; import { after, before, describe, it } from 'mocha'; -import { generateLastfmTrackObject, generateMbid, generatePlay } from "../utils/PlayTestUtils.js"; +import { generateLastfmTrackObject, generateMbid, generatePlay } from "../../../core/PlayTestUtils.js"; import LastfmApiClient, { playToClientPayload, formatPlayObj } from '../../common/vendor/LastfmApiClient.js'; import { MockNetworkError, withRequestInterception } from '../utils/networking.js'; diff --git a/src/backend/tests/listenbrainz/listenbrainz.test.ts b/src/backend/tests/listenbrainz/listenbrainz.test.ts index af610a6f..2d9a7f69 100644 --- a/src/backend/tests/listenbrainz/listenbrainz.test.ts +++ b/src/backend/tests/listenbrainz/listenbrainz.test.ts @@ -22,7 +22,7 @@ import slightlyDifferentNames from './correctlyMapped/trackNameSlightlyDifferent // incorrect mappings import incorrectMultiArtistsTrackName from './incorrectlyMapped/multiArtistsInTrackName.json' with { type: "json" }; import veryWrong from './incorrectlyMapped/veryWrong.json' with { type: "json" }; -import { generatePlay } from "../utils/PlayTestUtils.js"; +import { generatePlay } from "../../../core/PlayTestUtils.js"; import { defaultLifecycle } from "../../utils/PlayTransformUtils.js"; interface LZTestFixture { diff --git a/src/backend/tests/musicbrainz/musicbrainz.test.ts b/src/backend/tests/musicbrainz/musicbrainz.test.ts index a69f4145..fea951f3 100644 --- a/src/backend/tests/musicbrainz/musicbrainz.test.ts +++ b/src/backend/tests/musicbrainz/musicbrainz.test.ts @@ -12,7 +12,7 @@ import path from 'path'; import { MusicbrainzApiConfigData } from '../../common/infrastructure/Atomic.js'; import { MockNetworkError, withRequestInterception } from '../utils/networking.js'; import { http, HttpResponse, delay } from "msw"; -import { generatePlay, withBrainz } from '../utils/PlayTestUtils.js'; +import { generatePlay, withBrainz } from '../../../core/PlayTestUtils.js'; import { intersect, missingMbidTypes } from '../../utils.js'; import { defaultLifecycle } from '../../utils/PlayTransformUtils.js'; import { CoverArtApiClient, CoverArtApiConfig } from '../../common/vendor/musicbrainz/CoverArtApiClient.js'; diff --git a/src/backend/tests/player/player.test.ts b/src/backend/tests/player/player.test.ts index b0f9822d..dff27917 100644 --- a/src/backend/tests/player/player.test.ts +++ b/src/backend/tests/player/player.test.ts @@ -13,7 +13,7 @@ import { } from "../../common/infrastructure/Atomic.js"; import { GenericPlayerState } from "../../sources/PlayerState/GenericPlayerState.js"; import { playObjDataMatch } from "../../utils.js"; -import { generatePlay } from "../utils/PlayTestUtils.js"; +import { generatePlay } from "../../../core/PlayTestUtils.js"; import { PositionalPlayerState } from "../../sources/PlayerState/PositionalPlayerState.js"; import { ListenProgressPositional } from "../../sources/PlayerState/ListenProgress.js"; import { ListenRangePositional } from "../../sources/PlayerState/ListenRange.js"; diff --git a/src/backend/tests/plays/playParsing.test.ts b/src/backend/tests/plays/playParsing.test.ts index 201a9344..9b277408 100644 --- a/src/backend/tests/plays/playParsing.test.ts +++ b/src/backend/tests/plays/playParsing.test.ts @@ -3,7 +3,7 @@ import chai, { assert, expect } from 'chai'; import asPromised from 'chai-as-promised'; import { after, before, describe, it } from 'mocha'; -import { asPlays, generateArtistsStr, generatePlay, normalizePlays } from "../utils/PlayTestUtils.js"; +import { asPlays, generateArtistsStr, generatePlay, normalizePlays } from "../../../core/PlayTestUtils.js"; import { parseArtistCredits, parseContextAwareStringList, parseCredits, parseTrackCredits, uniqueNormalizedStrArr } from "../../utils/StringUtils.js"; import testData from '../utils/playTestData.json' with { type: "json" }; import { intersect } from "../../utils.js"; diff --git a/src/backend/tests/plex/plex.test.ts b/src/backend/tests/plex/plex.test.ts index 3cd26804..4b502d36 100644 --- a/src/backend/tests/plex/plex.test.ts +++ b/src/backend/tests/plex/plex.test.ts @@ -5,7 +5,7 @@ import { describe, it } from 'mocha'; import { JsonPlayObject, PlayMeta, PlayObject } from "../../../core/Atomic.js"; import validSessionResponse from './validSession.json' with { type: "json" }; -import { generatePlay } from "../utils/PlayTestUtils.js"; +import { generatePlay } from "../../../core/PlayTestUtils.js"; import { PlayerStateDataMaybePlay } from "../../common/infrastructure/Atomic.js"; import { PlexApiData } from "../../common/infrastructure/config/source/plex.js"; import PlexApiSource from "../../sources/PlexApiSource.js"; diff --git a/src/backend/tests/scrobbler/scrobblers.test.ts b/src/backend/tests/scrobbler/scrobblers.test.ts index 9ce34b91..f88fd7bf 100644 --- a/src/backend/tests/scrobbler/scrobblers.test.ts +++ b/src/backend/tests/scrobbler/scrobblers.test.ts @@ -7,11 +7,12 @@ import { after, before, describe, it } from 'mocha'; import { http, HttpResponse } from 'msw'; import pEvent from 'p-event'; import { PlayObject, SOURCE_SOT } from "../../../core/Atomic.js"; -import { genGroupIdStr, sleep, sortByOldestPlayDate } from "../../utils.js"; +import { sleep, sortByOldestPlayDate } from "../../utils.js"; +import { genGroupIdStr } from '../../../core/PlayUtils.js'; import mixedDuration from '../plays/mixedDuration.json' with { type: 'json' }; import withDuration from '../plays/withDuration.json' with { type: 'json' }; import { MockNetworkError, withRequestInterception } from "../utils/networking.js"; -import { asPlays, generatePlay, generatePlayPlatformId, generatePlays, generateSourcePlayerObj, normalizePlays } from "../utils/PlayTestUtils.js"; +import { asPlays, generatePlay, generatePlayPlatformId, generatePlays, generateSourcePlayerObj, normalizePlays } from "../../../core/PlayTestUtils.js"; import MockDate from 'mockdate'; import { NowPlayingScrobbler, TestAuthScrobbler, TestScrobbler } from "./TestScrobbler.js"; diff --git a/src/backend/tests/source/source.test.ts b/src/backend/tests/source/source.test.ts index 873ceaca..ac72d1a5 100644 --- a/src/backend/tests/source/source.test.ts +++ b/src/backend/tests/source/source.test.ts @@ -6,7 +6,7 @@ import { after, before, describe, it } from 'mocha'; import pEvent from "p-event"; import clone from 'clone'; import { PlayObject } from "../../../core/Atomic.js"; -import { generatePlay, generatePlayerStateData, generatePlays, normalizePlays } from "../utils/PlayTestUtils.js"; +import { generatePlay, generatePlayerStateData, generatePlays, normalizePlays } from "../../../core/PlayTestUtils.js"; import { TestMemoryPositionalSource, TestMemorySource, TestSource } from "./TestSource.js"; import spotifyPayload from '../plays/spotifyCurrentPlaybackState.json' with { type: "json" }; import SpotifySource from "../../sources/SpotifySource.js"; diff --git a/src/backend/tests/tealfm/tealfm.test.ts b/src/backend/tests/tealfm/tealfm.test.ts index 5aa59148..f7ba19d3 100644 --- a/src/backend/tests/tealfm/tealfm.test.ts +++ b/src/backend/tests/tealfm/tealfm.test.ts @@ -1,7 +1,7 @@ import chai, { expect } from 'chai'; import asPromised from 'chai-as-promised'; import { after, before, describe, it } from 'mocha'; -import { generateLastfmTrackObject, generateMbid, generatePlay, generateTealPlayRecord } from "../utils/PlayTestUtils.js"; +import { generateLastfmTrackObject, generateMbid, generatePlay, generateTealPlayRecord } from "../../../core/PlayTestUtils.js"; import { AbstractBlueSkyApiClient, listRecordToPlay } from '../../common/vendor/bluesky/AbstractBlueSkyApiClient.js'; import dayjs from 'dayjs'; diff --git a/src/backend/tests/utilitiesTests/time.test.ts b/src/backend/tests/utilitiesTests/time.test.ts index c4fa7831..052a128e 100644 --- a/src/backend/tests/utilitiesTests/time.test.ts +++ b/src/backend/tests/utilitiesTests/time.test.ts @@ -2,7 +2,7 @@ import { assert, expect } from 'chai'; import { describe, it } from 'mocha'; import { closeToPlayEnd, closeToPlayStart, repeatDurationPlayed, timePassesScrobbleThreshold } from '../../utils/TimeUtils.js'; import { DEFAULT_CLOSE_POSITION_ABSOLUTE, DEFAULT_CLOSE_POSITION_PERCENT, DEFAULT_DURATION_REPEAT_ABSOLUTE, DEFAULT_DURATION_REPEAT_PERCENT, DEFAULT_SCROBBLE_DURATION_THRESHOLD, DEFAULT_SCROBBLE_PERCENT_THRESHOLD } from '../../common/infrastructure/Atomic.js'; -import { generatePlay } from '../utils/PlayTestUtils.js'; +import { generatePlay } from '../../../core/PlayTestUtils.js'; describe('Play Position', function() { diff --git a/src/backend/tests/utils/playComparisons.test.ts b/src/backend/tests/utils/playComparisons.test.ts index deebe690..a23a8de0 100644 --- a/src/backend/tests/utils/playComparisons.test.ts +++ b/src/backend/tests/utils/playComparisons.test.ts @@ -3,7 +3,7 @@ import { assert, expect } from 'chai'; import clone from "clone"; import { describe, it } from 'mocha'; import { genericSourcePlayMatch, playsAreAddedOnly, playsAreBumpedOnly, playsAreSortConsistent } from "../../utils/PlayComparisonUtils.js"; -import { generatePlay, generatePlays } from "./PlayTestUtils.js"; +import { generatePlay, generatePlays } from "../../../core/PlayTestUtils.js"; import { PlayObject } from "../../../core/Atomic.js"; const logger = loggerTest; diff --git a/src/backend/tests/ytm/ytm.test.ts b/src/backend/tests/ytm/ytm.test.ts index f11ac815..2219e6c1 100644 --- a/src/backend/tests/ytm/ytm.test.ts +++ b/src/backend/tests/ytm/ytm.test.ts @@ -6,7 +6,7 @@ import clone from "clone"; import YTMusicSource, { ytiHistoryResponseFromShelfToPlays, ytiHistoryResponseToListItems } from "../../sources/YTMusicSource.js"; import ytHistoryRes from './ytres.json' with {type: 'json'}; import EventEmitter from "events"; -import { generatePlay, generatePlays, normalizePlays } from '../utils/PlayTestUtils.js'; +import { generatePlay, generatePlays, normalizePlays } from '../../../core/PlayTestUtils.js'; import { YTMusicSourceConfig } from '../../common/infrastructure/config/source/ytmusic.js'; import { sleep } from '../../utils.js'; import dayjs from 'dayjs'; diff --git a/src/backend/utils.ts b/src/backend/utils.ts index 46631bf3..e1085fed 100644 --- a/src/backend/utils.ts +++ b/src/backend/utils.ts @@ -18,6 +18,7 @@ import { RemoteIdentityParts, ScrobbleThresholdResult, } from "./common/infrastructure/Atomic.js"; +import { genGroupIdStr } from '../core/PlayUtils.js'; //const { default: Ajv } = AjvNS; dayjs.extend(utc); @@ -50,30 +51,6 @@ export const sortByOldestPlayDate = (a: PlayObject, b: PlayObject) => { return aPlayDate.isAfter(bPlayDate) ? 1 : -1 }; -/** sorts playObj formatted objects by playDate in descending (newest first) order */ -export const sortByNewestPlayDate = (a: PlayObject, b: PlayObject) => { - const { - data: { - playDate: aPlayDate - } = {} - } = a; - const { - data: { - playDate: bPlayDate - } = {} - } = b; - if(aPlayDate === undefined && bPlayDate === undefined) { - return 0; - } - if(aPlayDate === undefined) { - return 1; - } - if(bPlayDate === undefined) { - return -1; - } - return aPlayDate.isBefore(bPlayDate) ? 1 : -1 -}; - export const setIntersection = (setA: any, setB: any) => { const _intersection = new Set() for (const elem of setB) { @@ -332,9 +309,6 @@ export const genGroupIdStrFromPlay = (play: PlayObject) => { const groupId = genGroupId(play); return genGroupIdStr(groupId); }; -export const genGroupIdStr = (id: PlayPlatformId) => { - return `${id[0]}-${id[1]}`; -} export const genGroupId = (play: PlayObject): PlayPlatformId => [play.meta.deviceId ?? NO_DEVICE, play.meta.user ?? NO_USER]; export const getPlatformIdFromData = (data: PlayObject | PlayerStateDataMaybePlay) => { diff --git a/src/backend/utils/ListenFetchUtils.ts b/src/backend/utils/ListenFetchUtils.ts index 66d80005..844a6c2d 100644 --- a/src/backend/utils/ListenFetchUtils.ts +++ b/src/backend/utils/ListenFetchUtils.ts @@ -4,7 +4,8 @@ import { Duration } from "dayjs/plugin/duration.js"; import { PlayObject, UnixTimestamp } from "../../core/Atomic.js"; import { CursorType, hasPagelessTimeRangeListens, hasPaginatedTimeRangeListens, PagelessListensTimeRangeOptions, PagelessTimeRangeListens, PagelessTimeRangeListensResult, PaginatedListensTimeRangeOptions, PaginatedTimeRangeCommonOptions, PaginatedTimeRangeListens, PaginatedTimeRangeListensResult, PaginatedTimeRangeOptions, PaginatedTimeRangeSource, REFRESH_STALE_DEFAULT, TimeRangeListensFetcher } from "../common/infrastructure/Atomic.js"; import { loggerNoop, MaybeLogger } from "../common/logging.js"; -import { sortByNewestPlayDate, sortByOldestPlayDate } from "../utils.js"; +import { sortByOldestPlayDate } from "../utils.js"; +import { sortByNewestPlayDate } from '../../core/PlayUtils.js'; import { todayAwareFormat } from "../../core/TimeUtils.js"; import { playDateWithinDurationOfAny } from "./PlayComparisonUtils.js"; diff --git a/src/backend/utils/StringUtils.ts b/src/backend/utils/StringUtils.ts index 577ab881..4979d631 100644 --- a/src/backend/utils/StringUtils.ts +++ b/src/backend/utils/StringUtils.ts @@ -2,7 +2,8 @@ import { strategies, stringSameness, StringSamenessResult } from "@foxxmd/string import { hasher } from 'node-object-hash'; import { PlayObject } from "../../core/Atomic.js"; import { asPlayerStateData, DELIMITERS, DELIMITERS_NO_AMP, PlayerStateDataMaybePlay } from "../common/infrastructure/Atomic.js"; -import { genGroupIdStr, getPlatformIdFromData, intersect, parseBool, parseBoolStrict, parseRegexSingleOrFail } from "../utils.js"; +import { getPlatformIdFromData, intersect, parseBool, parseBoolStrict, parseRegexSingleOrFail } from "../utils.js"; +import { genGroupIdStr } from '../../core/PlayUtils.js'; import { buildTrackString } from "../../core/StringUtils.js"; const {levenStrategy, diceStrategy} = strategies; @@ -227,20 +228,6 @@ export const rejoinBypassed = (str: string): string => { } return bypassed; } -export const containsDelimiters = (str: string) => null !== str.match(/[,&/\\]+/i) -export const findDelimiters = (str: string, delimiters = DELIMITERS) => { - const found: string[] = []; - for (const d of delimiters) { - if (str.indexOf(d) !== -1) { - found.push(d); - } - } - if (found.length === 0) { - return undefined; - } - return found; -} - export interface TrackSamenessResults { naive: StringSamenessResult, cleaned: StringSamenessResult, diff --git a/src/backend/tests/utils/PlayTestUtils.ts b/src/core/PlayTestUtils.ts similarity index 96% rename from src/backend/tests/utils/PlayTestUtils.ts rename to src/core/PlayTestUtils.ts index d5927a00..ddf6c248 100644 --- a/src/backend/tests/utils/PlayTestUtils.ts +++ b/src/core/PlayTestUtils.ts @@ -5,16 +5,17 @@ import isBetween from "dayjs/plugin/isBetween.js"; import relativeTime from "dayjs/plugin/relativeTime.js"; import timezone from "dayjs/plugin/timezone.js"; import utc from "dayjs/plugin/utc.js"; -import { FEAT, JOINERS, JOINERS_FINAL, JsonPlayObject, MissingMbidType, ObjectPlayData, PlayMeta, PlayObject, SourcePlayerObj } from "../../../core/Atomic.js"; -import { genGroupIdStr, getPlatformIdFromData, sortByNewestPlayDate } from "../../utils.js"; -import { CALCULATED_PLAYER_STATUSES, NO_DEVICE, NO_USER, PlayerStateDataMaybePlay, PlayPlatformId, REPORTED_PLAYER_STATUSES, ReportedPlayerStatus, SINGLE_USER_PLATFORM_ID, SourceIdentifier } from '../../common/infrastructure/Atomic.js'; -import { arrayListAnd } from '../../../core/StringUtils.js'; -import { findDelimiters } from '../../utils/StringUtils.js'; -import { ListRecord, ScrobbleRecord } from '../../common/infrastructure/config/client/tealfm.js'; +import { FEAT, JOINERS, JOINERS_FINAL, JsonPlayObject, MissingMbidType, ObjectPlayData, PlayMeta, PlayObject, SourcePlayerObj } from "./Atomic.js"; +import { genGroupIdStr } from './PlayUtils.js'; +import { sortByNewestPlayDate } from './PlayUtils.js'; +import { CALCULATED_PLAYER_STATUSES, NO_DEVICE, NO_USER, PlayerStateDataMaybePlay, PlayPlatformId, REPORTED_PLAYER_STATUSES, SINGLE_USER_PLATFORM_ID } from '../backend/common/infrastructure/Atomic.js'; +import { arrayListAnd } from './StringUtils.js'; +import { findDelimiters } from "./StringUtils.js"; +import { ListRecord, ScrobbleRecord } from '../backend/common/infrastructure/config/client/tealfm.js'; import { nanoid } from 'nanoid'; -import { LastFMTrackObject } from '../../common/vendor/LastfmApiClient.js'; +import { LastFMTrackObject } from '../backend/common/vendor/LastfmApiClient.js'; import { MarkOptional } from 'ts-essentials'; -import { defaultLifecycle } from '../../utils/PlayTransformUtils.js'; +import { defaultLifecycle } from '../backend/utils/PlayTransformUtils.js'; dayjs.extend(utc) dayjs.extend(isBetween); diff --git a/src/core/PlayUtils.ts b/src/core/PlayUtils.ts new file mode 100644 index 00000000..ddcb611d --- /dev/null +++ b/src/core/PlayUtils.ts @@ -0,0 +1,30 @@ +import { PlayPlatformId } from "../backend/common/infrastructure/Atomic.js"; +import { PlayObject } from "./Atomic.js"; + + +/** sorts playObj formatted objects by playDate in descending (newest first) order */ +export const sortByNewestPlayDate = (a: PlayObject, b: PlayObject) => { + const { + data: { + playDate: aPlayDate + } = {} + } = a; + const { + data: { + playDate: bPlayDate + } = {} + } = b; + if (aPlayDate === undefined && bPlayDate === undefined) { + return 0; + } + if (aPlayDate === undefined) { + return 1; + } + if (bPlayDate === undefined) { + return -1; + } + return aPlayDate.isBefore(bPlayDate) ? 1 : -1; +};export const genGroupIdStr = (id: PlayPlatformId) => { + return `${id[0]}-${id[1]}`; +}; + diff --git a/src/core/StringUtils.ts b/src/core/StringUtils.ts index 0743e8aa..0e506e41 100644 --- a/src/core/StringUtils.ts +++ b/src/core/StringUtils.ts @@ -13,7 +13,7 @@ import { ScrobbleTsSOC, TrackStringOptions } from "./Atomic.js"; -import { DELIMETERS_REGEX } from "../backend/common/infrastructure/Atomic.js"; +import { DELIMETERS_REGEX, DELIMITERS } from "../backend/common/infrastructure/Atomic.js"; import { parseRegexSingle } from "@foxxmd/regex-buddy-core"; dayjs.extend(utc) @@ -289,4 +289,17 @@ export const arrayListAnd = (list: string[], joiner: string, finalJoiner: string return [start.join(joinerProper), end].join(finalProper); } -export const safeStringify = (json: unknown) => JSON.stringify(json, null, 2); \ No newline at end of file +export const safeStringify = (json: unknown) => JSON.stringify(json, null, 2); +export const findDelimiters = (str: string, delimiters = DELIMITERS) => { + const found: string[] = []; + for (const d of delimiters) { + if (str.indexOf(d) !== -1) { + found.push(d); + } + } + if (found.length === 0) { + return undefined; + } + return found; +};export const containsDelimiters = (str: string) => null !== str.match(/[,&/\\]+/i); + diff --git a/src/core/tests/utils/fixtures.ts b/src/core/tests/utils/fixtures.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/stories/ActivityTimeline.stories.tsx b/src/stories/ActivityTimeline.stories.tsx index b58468a6..3582843f 100644 --- a/src/stories/ActivityTimeline.stories.tsx +++ b/src/stories/ActivityTimeline.stories.tsx @@ -5,7 +5,7 @@ import { fn } from 'storybook/test'; import { Container } from '@chakra-ui/react'; import { ActivityTimeline } from "../client/components/ActivityTimeline"; import {Provider} from "../client/components/Provider"; -import { generateJsonPlays } from "../backend/tests/utils/PlayTestUtils.js"; +import { generateJsonPlays } from "../core/PlayTestUtils.js"; import { ErrorLike, JsonPlayObject, PlayLifecycle } from "../core/Atomic.js"; import { examplePlay, lastfmErrorExample } from "./storyUtils.js"; diff --git a/src/stories/List.stories.tsx b/src/stories/List.stories.tsx index f77bfcf7..15447798 100644 --- a/src/stories/List.stories.tsx +++ b/src/stories/List.stories.tsx @@ -5,7 +5,7 @@ import { fn } from 'storybook/test'; import { Container } from '@chakra-ui/react'; import { CList } from "../client/components/List"; import {Provider} from "../client/components/Provider"; -import { generateJsonPlays } from "../backend/tests/utils/PlayTestUtils.js"; +import { generateJsonPlays } from "../core/PlayTestUtils.js"; import { ErrorLike, JsonPlayObject } from "../core/Atomic.js"; import {examplePlay, lastfmErrorExample} from './storyUtils.js'; diff --git a/src/stories/PlayInfo.stories.tsx b/src/stories/PlayInfo.stories.tsx index 6ee1aed4..f6aaf9a5 100644 --- a/src/stories/PlayInfo.stories.tsx +++ b/src/stories/PlayInfo.stories.tsx @@ -4,7 +4,7 @@ import React from 'react'; import { fn } from 'storybook/test'; import { PlayData, PlayInfoContainer } from "../client/components/PlayData.js"; import {Provider} from "../client/components/Provider"; -import { generateArtists, generateJsonPlay, generatePlay } from "../backend/tests/utils/PlayTestUtils" +import { generateArtists, generateJsonPlay, generatePlay } from "../core/PlayTestUtils.js" import clone from "clone"; type PropsAndCustomArgs = React.ComponentProps & {