mirror of
https://github.com/FoxxMD/multi-scrobbler.git
synced 2026-04-26 10:51:06 +00:00
Merge pull request #570 from FoxxMD/fixListenRange
Some checks failed
Publish Docker image to Dockerhub / test (push) Has been cancelled
Publish Docker image to Dockerhub / Build OCI Images (push) Has been cancelled
Publish Docker image to Dockerhub / Build OCI Images-1 (push) Has been cancelled
Publish Docker image to Dockerhub / Merge OCI Images and Push (push) Has been cancelled
Some checks failed
Publish Docker image to Dockerhub / test (push) Has been cancelled
Publish Docker image to Dockerhub / Build OCI Images (push) Has been cancelled
Publish Docker image to Dockerhub / Build OCI Images-1 (push) Has been cancelled
Publish Docker image to Dockerhub / Merge OCI Images and Push (push) Has been cancelled
Fix listen range
This commit is contained in:
commit
ec4a51074e
11 changed files with 69 additions and 18 deletions
11
package-lock.json
generated
11
package-lock.json
generated
|
|
@ -49,7 +49,7 @@
|
|||
"dotenv": "^10.0.0",
|
||||
"express": "^5.2.1",
|
||||
"express-session": "^1.19.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-equals": "^6.0.0",
|
||||
"fixed-size-list": "^0.3.0",
|
||||
"flat-cache": "^6.1.13",
|
||||
"formidable": "^3.5",
|
||||
|
|
@ -8253,6 +8253,15 @@
|
|||
"version": "3.1.3",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-equals": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-6.0.0.tgz",
|
||||
"integrity": "sha512-PFhhIGgdM79r5Uztdj9Zb6Tt1zKafqVfdMGwVca1z5z6fbX7DmsySSuJd8HiP6I1j505DCS83cLxo5rmSNeVEA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.2",
|
||||
"dev": true,
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@
|
|||
"dotenv": "^10.0.0",
|
||||
"express": "^5.2.1",
|
||||
"express-session": "^1.19.0",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-equals": "^6.0.0",
|
||||
"fixed-size-list": "^0.3.0",
|
||||
"flat-cache": "^6.1.13",
|
||||
"formidable": "^3.5",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { childLogger, Logger } from "@foxxmd/logging";
|
|||
import {
|
||||
cacheFunctions,
|
||||
} from "@foxxmd/regex-buddy-core";
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import { LifecycleStep, PlayData, PlayObject, TransformResult } from "../../core/Atomic.js";
|
||||
import { buildPlayHumanDiffable, buildTrackString } from "../../core/StringUtils.js";
|
||||
import { CommonClientConfig } from "./infrastructure/config/client/index.js";
|
||||
|
|
@ -27,6 +26,7 @@ import { MSCache } from "./Cache.js";
|
|||
import { diffObjects, diffObjectsConsoleOutput, patchObject } from "../../core/DataUtils.js";
|
||||
import clone from "clone";
|
||||
import { loggerNoop } from "./MaybeLogger.js";
|
||||
import { objectsEqual } from "../utils/DataUtils.js";
|
||||
|
||||
export type AbstractComponentConfig = (CommonClientConfig | CommonSourceConfig) & { transformManager?: TransformerManager };
|
||||
|
||||
|
|
@ -406,7 +406,7 @@ export default abstract class AbstractComponent extends AbstractInitializable {
|
|||
} else {
|
||||
step.flowResult = onSuccess;
|
||||
|
||||
if (!deepEqual(playTruth.data, newTransformedPlay.data)) {
|
||||
if (!objectsEqual(playTruth.data, newTransformedPlay.data)) {
|
||||
const o = JSON.parse(JSON.stringify(playTruth.data));
|
||||
const t = JSON.parse(JSON.stringify(newTransformedPlay.data));
|
||||
const patch = diffObjects(o, t); // jdiff.diff(o, t);
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { parseVersion } from "./version.js";
|
|||
import { initServer } from "./server/index.js";
|
||||
import { createHeartbeatClientsTask } from "./tasks/heartbeatClients.js";
|
||||
import { createHeartbeatSourcesTask } from "./tasks/heartbeatSources.js";
|
||||
import { isDebugMode, parseBool, retry, sleep } from "./utils.js";
|
||||
import { isDebugMode, parseBool, retry } from "./utils.js";
|
||||
import { readJson } from './utils/DataUtils.js';
|
||||
//import { createVegaGenerator } from './utils/SchemaUtils.js';
|
||||
import ScrobbleClients from './scrobblers/ScrobbleClients.js';
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ export abstract class AbstractPlayerState {
|
|||
...this.currentPlay.data,
|
||||
playDate: this.playFirstSeenAt,
|
||||
listenedFor: this.getListenDuration(),
|
||||
listenRanges: ranges,
|
||||
listenRanges: ranges.map(x => ({start: x.start, end: x.end})),
|
||||
playDateCompleted: completed ? dayjs() : undefined,
|
||||
repeat: this.isRepeatPlay
|
||||
},
|
||||
|
|
|
|||
2
src/backend/tests/cache/cache.test.ts
vendored
2
src/backend/tests/cache/cache.test.ts
vendored
|
|
@ -166,7 +166,7 @@ describe('#Caching', function () {
|
|||
|
||||
await using test = new TestScrobbler();
|
||||
await test.initialize();
|
||||
const plays = generatePlays(100);
|
||||
const plays = generatePlays(100, {}, {}, {listenRanges: true});
|
||||
await test.queueScrobble(plays, 'testSource');
|
||||
const queued = test.queuedScrobbles.map(x => x.play);
|
||||
await sleep(101);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export function staggerMapper<Element, NewElement>(options: StaggerOptions) {
|
|||
|
||||
return (mapper: Mapper<Element, NewElement>) => async (x: Element, index: number) => {
|
||||
if (index < concurrency) {
|
||||
sleep(initialStagger);
|
||||
await sleep(initialStagger);
|
||||
initialStagger += initialInterval;
|
||||
} else {
|
||||
const s = Math.min((Math.random() * 1000), maxRandomStagger)
|
||||
|
|
|
|||
|
|
@ -20,10 +20,9 @@ export const rehydratePlay = (obj: AmbPlayObject): PlayObject => {
|
|||
if(obj.data.playDateCompleted !== undefined) {
|
||||
obj.data.playDateCompleted = dayjs(obj.data.playDateCompleted);
|
||||
}
|
||||
|
||||
if(obj.data.listenRanges !== undefined) {
|
||||
obj.data.listenRanges = obj.data.listenRanges.map(rehydrateListenRangeData);
|
||||
}
|
||||
}
|
||||
if(obj.data.listenRanges !== undefined) {
|
||||
obj.data.listenRanges = obj.data.listenRanges.map(rehydrateListenRangeData);
|
||||
}
|
||||
return obj as PlayObject;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import JSON5 from "json5";
|
||||
import { constants, promises } from "fs";
|
||||
import { MaybeLogger } from '../common/MaybeLogger.js';
|
||||
import { deepEqual } from 'fast-equals';
|
||||
|
||||
export const asArray = <T>(data: T | T[]): T[] => {
|
||||
if (Array.isArray(data)) {
|
||||
|
|
@ -166,4 +167,12 @@ export const shuffleArray = (array: any[]): void => {
|
|||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[array[i], array[j]] = [array[j], array[i]];
|
||||
}
|
||||
}
|
||||
|
||||
export const objectsEqual = (a: object, b: object) => {
|
||||
try {
|
||||
return deepEqual(a, b);
|
||||
} catch (e) {
|
||||
throw new Error('Could not compare objects', {cause: e});
|
||||
}
|
||||
}
|
||||
|
|
@ -107,8 +107,8 @@ export interface ListenRangeDataAmb {
|
|||
}
|
||||
|
||||
export interface ListenRangeData extends ListenRangeDataAmb {
|
||||
start: ListenProgress
|
||||
end: ListenProgress
|
||||
start: PlayProgress
|
||||
end: PlayProgress
|
||||
}
|
||||
|
||||
/** https://musicbrainz.org/doc/MusicBrainz_Database/Schema#Overview */
|
||||
|
|
@ -554,6 +554,8 @@ export interface TransformResult {
|
|||
|
||||
export const KNOWN_MEDIA_PROVIDER_URLS = [
|
||||
'spotify.com',
|
||||
// spotify cdn
|
||||
'scdn.co',
|
||||
'bandcamp.com',
|
||||
'youtube.com',
|
||||
'deezer.com',
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { nanoid } from 'nanoid';
|
|||
import { LastFMTrackObject } from '../backend/common/vendor/LastfmApiClient.js';
|
||||
import { MarkOptional } from 'ts-essentials';
|
||||
import { defaultLifecycle } from '../backend/utils/PlayTransformUtils.js';
|
||||
import clone from 'clone';
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(isBetween);
|
||||
|
|
@ -161,11 +162,13 @@ export const generatePlayerStateData = (options: Omit<PlayerStateDataMaybePlay,
|
|||
}
|
||||
|
||||
export interface GeneratePlayOpts {
|
||||
playDateCompleted?: boolean
|
||||
playDateCompleted?: boolean,
|
||||
listenRanges?: boolean
|
||||
}
|
||||
export const generatePlay = (data: ObjectPlayData = {}, meta: MarkOptional<PlayMeta, 'lifecycle'> = {}, opts: GeneratePlayOpts = {}): PlayObject => {
|
||||
const {
|
||||
playDateCompleted = false
|
||||
playDateCompleted = false,
|
||||
listenRanges = false,
|
||||
} = opts;
|
||||
|
||||
const duration = faker.number.int({min: 30, max: 300});
|
||||
|
|
@ -201,6 +204,35 @@ export const generatePlay = (data: ObjectPlayData = {}, meta: MarkOptional<PlayM
|
|||
play.data.playDateCompleted = play.data.playDate.add(play.data.duration)
|
||||
}
|
||||
|
||||
if(listenRanges) {
|
||||
play.data.listenRanges = [];
|
||||
const lf = play.data.listenedFor;
|
||||
const sessions = faker.number.int({min: 1, max: 3});
|
||||
const sessionTime = lf / sessions;
|
||||
let nextTime = play.data.playDate;
|
||||
switch(faker.number.int({min: 1, max: 2})) {
|
||||
case 1:
|
||||
// timestamps only
|
||||
for(let i = 0; i < sessions; i++) {
|
||||
const newTime = nextTime.add(sessionTime, 's');
|
||||
play.data.listenRanges.push({start: {timestamp: clone(nextTime)}, end: {timestamp: newTime}});
|
||||
nextTime = newTime;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// timestamps + position
|
||||
let position = 0;
|
||||
for(let i = 0; i < sessions; i++) {
|
||||
const newTime = nextTime.add(sessionTime, 's');
|
||||
const nextPosition = position + sessionTime;
|
||||
play.data.listenRanges.push({start: {timestamp: clone(nextTime), position}, end: {timestamp: newTime, position: nextPosition}});
|
||||
nextTime = newTime;
|
||||
position = nextPosition;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return play;
|
||||
}
|
||||
|
||||
|
|
@ -262,8 +294,8 @@ export const generatePlayPlatformId = (deviceId?: string, userId?: string): Play
|
|||
return [did, uid];
|
||||
}
|
||||
|
||||
export const generatePlays = (numberOfPlays: number, data: ObjectPlayData = {}, meta: MarkOptional<PlayMeta, 'lifecycle'> = {}): PlayObject[] => {
|
||||
return Array.from(Array(numberOfPlays), () => generatePlay(data, meta));
|
||||
export const generatePlays = (numberOfPlays: number, data: ObjectPlayData = {}, meta: MarkOptional<PlayMeta, 'lifecycle'> = {}, opts: GeneratePlayOpts = {}): PlayObject[] => {
|
||||
return Array.from(Array(numberOfPlays), () => generatePlay(data, meta, opts));
|
||||
}
|
||||
|
||||
export const generateArtist = () => faker.music.artist;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue