@ansango/lastfm-api 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +62 -3
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -1
- package/dist/client.js.map +1 -1
- package/dist/entrypoints/insights.d.ts +4 -0
- package/dist/entrypoints/insights.d.ts.map +1 -0
- package/dist/entrypoints/insights.js +3 -0
- package/dist/entrypoints/insights.js.map +1 -0
- package/dist/entrypoints/insights.schemas.d.ts +2 -0
- package/dist/entrypoints/insights.schemas.d.ts.map +1 -0
- package/dist/entrypoints/insights.schemas.js +3 -0
- package/dist/entrypoints/insights.schemas.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +2 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/insights/binges.d.ts +8 -0
- package/dist/services/insights/binges.d.ts.map +1 -0
- package/dist/services/insights/binges.js +53 -0
- package/dist/services/insights/binges.js.map +1 -0
- package/dist/services/insights/compare.d.ts +8 -0
- package/dist/services/insights/compare.d.ts.map +1 -0
- package/dist/services/insights/compare.js +39 -0
- package/dist/services/insights/compare.js.map +1 -0
- package/dist/services/insights/discoveries.d.ts +8 -0
- package/dist/services/insights/discoveries.d.ts.map +1 -0
- package/dist/services/insights/discoveries.js +53 -0
- package/dist/services/insights/discoveries.js.map +1 -0
- package/dist/services/insights/hours.d.ts +8 -0
- package/dist/services/insights/hours.d.ts.map +1 -0
- package/dist/services/insights/hours.js +43 -0
- package/dist/services/insights/hours.js.map +1 -0
- package/dist/services/insights/lib/binges.d.ts +31 -0
- package/dist/services/insights/lib/binges.d.ts.map +1 -0
- package/dist/services/insights/lib/binges.js +61 -0
- package/dist/services/insights/lib/binges.js.map +1 -0
- package/dist/services/insights/lib/compare.d.ts +31 -0
- package/dist/services/insights/lib/compare.d.ts.map +1 -0
- package/dist/services/insights/lib/compare.js +68 -0
- package/dist/services/insights/lib/compare.js.map +1 -0
- package/dist/services/insights/lib/discoveries.d.ts +17 -0
- package/dist/services/insights/lib/discoveries.d.ts.map +1 -0
- package/dist/services/insights/lib/discoveries.js +24 -0
- package/dist/services/insights/lib/discoveries.js.map +1 -0
- package/dist/services/insights/lib/diversity.d.ts +28 -0
- package/dist/services/insights/lib/diversity.d.ts.map +1 -0
- package/dist/services/insights/lib/diversity.js +39 -0
- package/dist/services/insights/lib/diversity.js.map +1 -0
- package/dist/services/insights/lib/hours.d.ts +39 -0
- package/dist/services/insights/lib/hours.d.ts.map +1 -0
- package/dist/services/insights/lib/hours.js +66 -0
- package/dist/services/insights/lib/hours.js.map +1 -0
- package/dist/services/insights/lib/mood.d.ts +22 -0
- package/dist/services/insights/lib/mood.d.ts.map +1 -0
- package/dist/services/insights/lib/mood.js +187 -0
- package/dist/services/insights/lib/mood.js.map +1 -0
- package/dist/services/insights/lib/periods.d.ts +17 -0
- package/dist/services/insights/lib/periods.d.ts.map +1 -0
- package/dist/services/insights/lib/periods.js +66 -0
- package/dist/services/insights/lib/periods.js.map +1 -0
- package/dist/services/insights/lib/personality.d.ts +30 -0
- package/dist/services/insights/lib/personality.d.ts.map +1 -0
- package/dist/services/insights/lib/personality.js +131 -0
- package/dist/services/insights/lib/personality.js.map +1 -0
- package/dist/services/insights/lib/trends.d.ts +30 -0
- package/dist/services/insights/lib/trends.d.ts.map +1 -0
- package/dist/services/insights/lib/trends.js +67 -0
- package/dist/services/insights/lib/trends.js.map +1 -0
- package/dist/services/insights/mood.d.ts +8 -0
- package/dist/services/insights/mood.d.ts.map +1 -0
- package/dist/services/insights/mood.js +53 -0
- package/dist/services/insights/mood.js.map +1 -0
- package/dist/services/insights/now-playing.d.ts +20 -0
- package/dist/services/insights/now-playing.d.ts.map +1 -0
- package/dist/services/insights/now-playing.js +128 -0
- package/dist/services/insights/now-playing.js.map +1 -0
- package/dist/services/insights/personality.d.ts +8 -0
- package/dist/services/insights/personality.d.ts.map +1 -0
- package/dist/services/insights/personality.js +42 -0
- package/dist/services/insights/personality.js.map +1 -0
- package/dist/services/insights/summary.d.ts +8 -0
- package/dist/services/insights/summary.d.ts.map +1 -0
- package/dist/services/insights/summary.js +117 -0
- package/dist/services/insights/summary.js.map +1 -0
- package/dist/services/insights/trends.d.ts +8 -0
- package/dist/services/insights/trends.d.ts.map +1 -0
- package/dist/services/insights/trends.js +96 -0
- package/dist/services/insights/trends.js.map +1 -0
- package/dist/services/insights.d.ts +96 -0
- package/dist/services/insights.d.ts.map +1 -0
- package/dist/services/insights.js +26 -0
- package/dist/services/insights.js.map +1 -0
- package/dist/services/insights.schemas.d.ts +710 -0
- package/dist/services/insights.schemas.d.ts.map +1 -0
- package/dist/services/insights.schemas.js +307 -0
- package/dist/services/insights.schemas.js.map +1 -0
- package/package.json +12 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mood.js","sourceRoot":"","sources":["../../../../src/services/insights/lib/mood.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Period as CanonicalPeriod } from '../../schemas/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Supported period inputs for insights (canonical tokens + human aliases)
|
|
4
|
+
*/
|
|
5
|
+
export type InsightsPeriodInput = CanonicalPeriod | 'daily' | 'weekly' | 'monthly' | 'yearly';
|
|
6
|
+
export interface ResolvedPeriod {
|
|
7
|
+
readonly lastfm: CanonicalPeriod;
|
|
8
|
+
readonly from?: number;
|
|
9
|
+
readonly to: number;
|
|
10
|
+
readonly label: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function periodToLastfm(p: string): CanonicalPeriod;
|
|
13
|
+
/**
|
|
14
|
+
* Resolves an insight period into a canonical Last.fm period token and time range
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolvePeriod(p?: string, clock?: () => number): ResolvedPeriod;
|
|
17
|
+
//# sourceMappingURL=periods.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"periods.d.ts","sourceRoot":"","sources":["../../../../src/services/insights/lib/periods.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAEvE;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,eAAe,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE7F,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAA;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACtB;AA6CD,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,eAAe,CAEzD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAE,MAAe,EAAE,KAAK,GAAE,MAAM,MAAiB,GAAG,cAAc,CAqBhG"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const SECOND = 1;
|
|
2
|
+
const MINUTE = 60 * SECOND;
|
|
3
|
+
const HOUR = 60 * MINUTE;
|
|
4
|
+
const DAY = 24 * HOUR;
|
|
5
|
+
const LASTFM_BY_PERIOD = {
|
|
6
|
+
daily: '7day',
|
|
7
|
+
weekly: '7day',
|
|
8
|
+
monthly: '1month',
|
|
9
|
+
yearly: '12month',
|
|
10
|
+
'7day': '7day',
|
|
11
|
+
'1month': '1month',
|
|
12
|
+
'3month': '3month',
|
|
13
|
+
'6month': '6month',
|
|
14
|
+
'12month': '12month',
|
|
15
|
+
overall: 'overall',
|
|
16
|
+
};
|
|
17
|
+
const LABEL_BY_PERIOD = {
|
|
18
|
+
daily: 'last 24 hours',
|
|
19
|
+
weekly: 'this week',
|
|
20
|
+
monthly: 'this month',
|
|
21
|
+
yearly: 'this year',
|
|
22
|
+
'7day': 'last 7 days',
|
|
23
|
+
'1month': 'last 30 days',
|
|
24
|
+
'3month': 'last 90 days',
|
|
25
|
+
'6month': 'last 180 days',
|
|
26
|
+
'12month': 'last 365 days',
|
|
27
|
+
overall: 'all time',
|
|
28
|
+
};
|
|
29
|
+
const WINDOW_SECONDS = {
|
|
30
|
+
daily: 1 * DAY,
|
|
31
|
+
weekly: 7 * DAY,
|
|
32
|
+
monthly: 30 * DAY,
|
|
33
|
+
yearly: 365 * DAY,
|
|
34
|
+
'7day': 7 * DAY,
|
|
35
|
+
'1month': 30 * DAY,
|
|
36
|
+
'3month': 90 * DAY,
|
|
37
|
+
'6month': 180 * DAY,
|
|
38
|
+
'12month': 365 * DAY,
|
|
39
|
+
};
|
|
40
|
+
export function periodToLastfm(p) {
|
|
41
|
+
return LASTFM_BY_PERIOD[p] ?? '7day';
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolves an insight period into a canonical Last.fm period token and time range
|
|
45
|
+
*/
|
|
46
|
+
export function resolvePeriod(p = '7day', clock = Date.now) {
|
|
47
|
+
const lastfm = periodToLastfm(p);
|
|
48
|
+
const label = LABEL_BY_PERIOD[p] ?? LABEL_BY_PERIOD[lastfm] ?? p;
|
|
49
|
+
const to = Math.floor(clock() / 1000);
|
|
50
|
+
if (p === 'overall' || lastfm === 'overall') {
|
|
51
|
+
return {
|
|
52
|
+
lastfm: 'overall',
|
|
53
|
+
to,
|
|
54
|
+
label: 'all time',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const span = WINDOW_SECONDS[p] ?? WINDOW_SECONDS[lastfm] ?? 7 * DAY;
|
|
58
|
+
const from = to - span;
|
|
59
|
+
return {
|
|
60
|
+
lastfm,
|
|
61
|
+
from,
|
|
62
|
+
to,
|
|
63
|
+
label,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=periods.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"periods.js","sourceRoot":"","sources":["../../../../src/services/insights/lib/periods.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,GAAG,CAAC,CAAA;AAChB,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,CAAA;AAC1B,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,CAAA;AACxB,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAA;AAErB,MAAM,gBAAgB,GAAoC;IACzD,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,QAAQ;IAClB,SAAS,EAAE,SAAS;IACpB,OAAO,EAAE,SAAS;CAClB,CAAA;AAED,MAAM,eAAe,GAA2B;IAC/C,KAAK,EAAE,eAAe;IACtB,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,aAAa;IACrB,QAAQ,EAAE,cAAc;IACxB,QAAQ,EAAE,cAAc;IACxB,QAAQ,EAAE,eAAe;IACzB,SAAS,EAAE,eAAe;IAC1B,OAAO,EAAE,UAAU;CACnB,CAAA;AAED,MAAM,cAAc,GAA2B;IAC9C,KAAK,EAAE,CAAC,GAAG,GAAG;IACd,MAAM,EAAE,CAAC,GAAG,GAAG;IACf,OAAO,EAAE,EAAE,GAAG,GAAG;IACjB,MAAM,EAAE,GAAG,GAAG,GAAG;IACjB,MAAM,EAAE,CAAC,GAAG,GAAG;IACf,QAAQ,EAAE,EAAE,GAAG,GAAG;IAClB,QAAQ,EAAE,EAAE,GAAG,GAAG;IAClB,QAAQ,EAAE,GAAG,GAAG,GAAG;IACnB,SAAS,EAAE,GAAG,GAAG,GAAG;CACpB,CAAA;AAED,MAAM,UAAU,cAAc,CAAC,CAAS;IACvC,OAAO,gBAAgB,CAAC,CAAC,CAAC,IAAI,MAAM,CAAA;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,MAAM,EAAE,QAAsB,IAAI,CAAC,GAAG;IAC/E,MAAM,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAAA;IAChC,MAAM,KAAK,GAAG,eAAe,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAChE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAA;IAErC,IAAI,CAAC,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC7C,OAAO;YACN,MAAM,EAAE,SAAS;YACjB,EAAE;YACF,KAAK,EAAE,UAAU;SACjB,CAAA;IACF,CAAC;IAED,MAAM,IAAI,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAA;IACnE,MAAM,IAAI,GAAG,EAAE,GAAG,IAAI,CAAA;IACtB,OAAO;QACN,MAAM;QACN,IAAI;QACJ,EAAE;QACF,KAAK;KACL,CAAA;AACF,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* personality.ts — classify the user into a listening personality archetype.
|
|
3
|
+
*/
|
|
4
|
+
export type ArchetypeId = 'Devotee' | 'Explorer' | 'Drifter' | 'DJ' | 'Nocturnal' | 'Archivist';
|
|
5
|
+
export declare const ARCHETYPE_ORDER: readonly ArchetypeId[];
|
|
6
|
+
export interface PersonalityFeatures {
|
|
7
|
+
readonly totalScrobbles: number;
|
|
8
|
+
readonly uniqueArtists: number;
|
|
9
|
+
readonly top1Share: number;
|
|
10
|
+
readonly top3Share: number;
|
|
11
|
+
readonly top5Share: number;
|
|
12
|
+
readonly normalizedDiversity: number;
|
|
13
|
+
readonly newArtistsLast30d: number;
|
|
14
|
+
readonly totalArtistsLast30d: number;
|
|
15
|
+
readonly nightHourShare: number;
|
|
16
|
+
readonly morningHourShare: number;
|
|
17
|
+
readonly weekdayShare: number;
|
|
18
|
+
}
|
|
19
|
+
export interface PersonalityResult {
|
|
20
|
+
readonly scores: Record<ArchetypeId, number>;
|
|
21
|
+
readonly winner: ArchetypeId;
|
|
22
|
+
readonly reasons: readonly string[];
|
|
23
|
+
}
|
|
24
|
+
export declare const ARCHETYPE_INFO: Record<ArchetypeId, {
|
|
25
|
+
name: string;
|
|
26
|
+
emoji: string;
|
|
27
|
+
blurb: string;
|
|
28
|
+
}>;
|
|
29
|
+
export declare function scoreArchetypes(features: PersonalityFeatures): PersonalityResult;
|
|
30
|
+
//# sourceMappingURL=personality.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"personality.d.ts","sourceRoot":"","sources":["../../../../src/services/insights/lib/personality.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,IAAI,GAAG,WAAW,GAAG,WAAW,CAAA;AAE/F,eAAO,MAAM,eAAe,EAAE,SAAS,WAAW,EAOjD,CAAA;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;IACpC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAClC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;IAC5C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CACnC;AAMD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,WAAW,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA+B9F,CAAA;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,mBAAmB,GAAG,iBAAiB,CA2FhF"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* personality.ts — classify the user into a listening personality archetype.
|
|
3
|
+
*/
|
|
4
|
+
export const ARCHETYPE_ORDER = [
|
|
5
|
+
'Devotee',
|
|
6
|
+
'Explorer',
|
|
7
|
+
'Drifter',
|
|
8
|
+
'DJ',
|
|
9
|
+
'Nocturnal',
|
|
10
|
+
'Archivist',
|
|
11
|
+
];
|
|
12
|
+
function clamp01(n) {
|
|
13
|
+
return Math.max(0, Math.min(1, n));
|
|
14
|
+
}
|
|
15
|
+
export const ARCHETYPE_INFO = {
|
|
16
|
+
Devotee: {
|
|
17
|
+
name: 'The Devotee',
|
|
18
|
+
emoji: '🛐',
|
|
19
|
+
blurb: 'Extreme loyalty to your favorite artists. High concentration, low dispersion.',
|
|
20
|
+
},
|
|
21
|
+
Explorer: {
|
|
22
|
+
name: 'The Explorer',
|
|
23
|
+
emoji: '🧭',
|
|
24
|
+
blurb: 'Always discovering new music. Your musical radar is constantly searching.',
|
|
25
|
+
},
|
|
26
|
+
Drifter: {
|
|
27
|
+
name: 'The Drifter',
|
|
28
|
+
emoji: '🍂',
|
|
29
|
+
blurb: 'Balanced listening across varied genres without strong fixations.',
|
|
30
|
+
},
|
|
31
|
+
DJ: {
|
|
32
|
+
name: 'The DJ',
|
|
33
|
+
emoji: '🎧',
|
|
34
|
+
blurb: 'High listening velocity across a broad and rotating selection of artists.',
|
|
35
|
+
},
|
|
36
|
+
Nocturnal: {
|
|
37
|
+
name: 'The Nocturnal',
|
|
38
|
+
emoji: '🌙',
|
|
39
|
+
blurb: 'Your music comes alive at night. The late hours are your peak listening zone.',
|
|
40
|
+
},
|
|
41
|
+
Archivist: {
|
|
42
|
+
name: 'The Archivist',
|
|
43
|
+
emoji: '📚',
|
|
44
|
+
blurb: 'A massive, eclectic, and deep personal music library with balanced play counts.',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
export function scoreArchetypes(features) {
|
|
48
|
+
const scores = {
|
|
49
|
+
Devotee: 0,
|
|
50
|
+
Explorer: 0,
|
|
51
|
+
Drifter: 0,
|
|
52
|
+
DJ: 0,
|
|
53
|
+
Nocturnal: 0,
|
|
54
|
+
Archivist: 0,
|
|
55
|
+
};
|
|
56
|
+
const reasons = {
|
|
57
|
+
Devotee: [],
|
|
58
|
+
Explorer: [],
|
|
59
|
+
Drifter: [],
|
|
60
|
+
DJ: [],
|
|
61
|
+
Nocturnal: [],
|
|
62
|
+
Archivist: [],
|
|
63
|
+
};
|
|
64
|
+
// Devotee: high top1Share + low diversity + small unique roster
|
|
65
|
+
{
|
|
66
|
+
const focus = clamp01((features.top1Share - 0.15) / 0.5);
|
|
67
|
+
const monotony = clamp01((0.6 - features.normalizedDiversity) / 0.6);
|
|
68
|
+
const smallness = clamp01((20 - features.uniqueArtists) / 20);
|
|
69
|
+
scores.Devotee = Math.round((0.5 * focus + 0.3 * monotony + 0.2 * smallness) * 100) / 100;
|
|
70
|
+
if (focus > 0.5)
|
|
71
|
+
reasons.Devotee.push(`Top artist accounts for ${(features.top1Share * 100).toFixed(0)}% of scrobbles.`);
|
|
72
|
+
if (monotony > 0.5)
|
|
73
|
+
reasons.Devotee.push(`Low diversity score (${features.normalizedDiversity.toFixed(2)}).`);
|
|
74
|
+
}
|
|
75
|
+
// Explorer: high diversity + many new artists relative to total roster
|
|
76
|
+
{
|
|
77
|
+
const diversity = features.normalizedDiversity;
|
|
78
|
+
const newRatio = features.totalArtistsLast30d > 0 ? clamp01(features.newArtistsLast30d / features.totalArtistsLast30d) : 0;
|
|
79
|
+
scores.Explorer = Math.round((0.5 * diversity + 0.5 * newRatio) * 100) / 100;
|
|
80
|
+
if (diversity > 0.7)
|
|
81
|
+
reasons.Explorer.push(`High diversity score (${diversity.toFixed(2)}).`);
|
|
82
|
+
if (newRatio > 0.2)
|
|
83
|
+
reasons.Explorer.push(`${features.newArtistsLast30d} new artists discovered this month out of ${features.totalArtistsLast30d} played.`);
|
|
84
|
+
}
|
|
85
|
+
// Drifter: middle of the road on diversity and low novelty
|
|
86
|
+
{
|
|
87
|
+
const midDiversity = 1 - Math.abs(features.normalizedDiversity - 0.65) / 0.35;
|
|
88
|
+
const lowNovelty = clamp01((0.15 - (features.totalArtistsLast30d > 0 ? features.newArtistsLast30d / features.totalArtistsLast30d : 0)) /
|
|
89
|
+
0.15);
|
|
90
|
+
const lowFocus = clamp01((0.4 - features.top3Share) / 0.4);
|
|
91
|
+
scores.Drifter = Math.round((0.4 * midDiversity + 0.3 * lowNovelty + 0.3 * lowFocus) * 100) / 100;
|
|
92
|
+
if (midDiversity > 0.7)
|
|
93
|
+
reasons.Drifter.push(`Moderate diversity (${features.normalizedDiversity.toFixed(2)}).`);
|
|
94
|
+
}
|
|
95
|
+
// DJ: high velocity + many artists
|
|
96
|
+
{
|
|
97
|
+
const velocity = clamp01((features.totalScrobbles - 200) / 1800);
|
|
98
|
+
const breadth = clamp01((features.uniqueArtists - 20) / 80);
|
|
99
|
+
scores.DJ = Math.round((0.5 * velocity + 0.3 * breadth + 0.2 * features.normalizedDiversity) * 100) / 100;
|
|
100
|
+
if (velocity > 0.5)
|
|
101
|
+
reasons.DJ.push(`${features.totalScrobbles} total scrobbles in the window.`);
|
|
102
|
+
}
|
|
103
|
+
// Nocturnal: nightHourShare dominant
|
|
104
|
+
{
|
|
105
|
+
const nightness = clamp01((features.nightHourShare - 0.4) / 0.2);
|
|
106
|
+
const antiDaytime = clamp01((0.3 - features.morningHourShare) / 0.3);
|
|
107
|
+
scores.Nocturnal = Math.round((0.7 * nightness + 0.3 * antiDaytime) * 100) / 100;
|
|
108
|
+
if (nightness > 0.5)
|
|
109
|
+
reasons.Nocturnal.push(`${(features.nightHourShare * 100).toFixed(0)}% of scrobbles occurred during night hours.`);
|
|
110
|
+
}
|
|
111
|
+
// Archivist: massive library, spread across many artists
|
|
112
|
+
{
|
|
113
|
+
const massive = clamp01((features.totalScrobbles - 1500) / 3500);
|
|
114
|
+
const hugeRoster = clamp01((features.uniqueArtists - 100) / 200);
|
|
115
|
+
const spread = clamp01((1 - features.top5Share) / 0.5);
|
|
116
|
+
scores.Archivist = Math.round((0.4 * massive + 0.4 * hugeRoster + 0.2 * spread) * 100) / 100;
|
|
117
|
+
if (hugeRoster > 0.5)
|
|
118
|
+
reasons.Archivist.push(`${features.uniqueArtists} unique artists in window.`);
|
|
119
|
+
}
|
|
120
|
+
let winner = 'Devotee';
|
|
121
|
+
let best = -Number.POSITIVE_INFINITY;
|
|
122
|
+
for (const id of ARCHETYPE_ORDER) {
|
|
123
|
+
const s = scores[id];
|
|
124
|
+
if (s > best) {
|
|
125
|
+
best = s;
|
|
126
|
+
winner = id;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return { scores, winner, reasons: reasons[winner] ?? [] };
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=personality.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"personality.js","sourceRoot":"","sources":["../../../../src/services/insights/lib/personality.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* trends.ts — diff two ranked lists to calculate rank & count movements.
|
|
3
|
+
*
|
|
4
|
+
* Partitions items into 4 buckets:
|
|
5
|
+
* - risers: climbed in rank (positive deltaRank) or increased plays
|
|
6
|
+
* - fallers: dropped in rank (negative deltaRank) or decreased plays
|
|
7
|
+
* - newcomers: present in current period but absent in previous
|
|
8
|
+
* - departures: present in previous period but absent in current
|
|
9
|
+
*/
|
|
10
|
+
export interface RankedItem {
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly playcount: number;
|
|
13
|
+
}
|
|
14
|
+
export interface RankedItemWithDelta extends RankedItem {
|
|
15
|
+
readonly currentRank: number;
|
|
16
|
+
readonly previousRank?: number;
|
|
17
|
+
readonly deltaRank: number;
|
|
18
|
+
readonly deltaCount: number;
|
|
19
|
+
}
|
|
20
|
+
export interface DiffRankingsOptions {
|
|
21
|
+
readonly maxResults?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface RankingDiffResult {
|
|
24
|
+
readonly risers: RankedItemWithDelta[];
|
|
25
|
+
readonly fallers: RankedItemWithDelta[];
|
|
26
|
+
readonly newcomers: RankedItemWithDelta[];
|
|
27
|
+
readonly departures: RankedItem[];
|
|
28
|
+
}
|
|
29
|
+
export declare function diffRankings(current: readonly RankedItem[], previous: readonly RankedItem[], options?: DiffRankingsOptions): RankingDiffResult;
|
|
30
|
+
//# sourceMappingURL=trends.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trends.d.ts","sourceRoot":"","sources":["../../../../src/services/insights/lib/trends.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,mBAAoB,SAAQ,UAAU;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC3B;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,MAAM,EAAE,mBAAmB,EAAE,CAAA;IACtC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,EAAE,CAAA;IACvC,QAAQ,CAAC,SAAS,EAAE,mBAAmB,EAAE,CAAA;IACzC,QAAQ,CAAC,UAAU,EAAE,UAAU,EAAE,CAAA;CACjC;AAUD,wBAAgB,YAAY,CAC3B,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,QAAQ,EAAE,SAAS,UAAU,EAAE,EAC/B,OAAO,GAAE,mBAAwB,GAC/B,iBAAiB,CA8DnB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
function indexByName(list) {
|
|
2
|
+
const m = new Map();
|
|
3
|
+
list.forEach((item, idx) => {
|
|
4
|
+
m.set(item.name, { idx, item });
|
|
5
|
+
});
|
|
6
|
+
return m;
|
|
7
|
+
}
|
|
8
|
+
export function diffRankings(current, previous, options = {}) {
|
|
9
|
+
const curIdx = indexByName(current);
|
|
10
|
+
const prevIdx = indexByName(previous);
|
|
11
|
+
const risers = [];
|
|
12
|
+
const fallers = [];
|
|
13
|
+
const newcomers = [];
|
|
14
|
+
current.forEach((cur, i) => {
|
|
15
|
+
const prev = prevIdx.get(cur.name);
|
|
16
|
+
if (!prev) {
|
|
17
|
+
newcomers.push({
|
|
18
|
+
name: cur.name,
|
|
19
|
+
playcount: cur.playcount,
|
|
20
|
+
currentRank: i + 1,
|
|
21
|
+
deltaRank: 0,
|
|
22
|
+
deltaCount: cur.playcount,
|
|
23
|
+
});
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const deltaRank = prev.idx - i; // positive = climbed in rank
|
|
27
|
+
const deltaCount = cur.playcount - prev.item.playcount;
|
|
28
|
+
if (deltaRank > 0 || deltaCount > 0) {
|
|
29
|
+
risers.push({
|
|
30
|
+
name: cur.name,
|
|
31
|
+
playcount: cur.playcount,
|
|
32
|
+
currentRank: i + 1,
|
|
33
|
+
previousRank: prev.idx + 1,
|
|
34
|
+
deltaRank,
|
|
35
|
+
deltaCount,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else if (deltaRank < 0 || deltaCount < 0) {
|
|
39
|
+
fallers.push({
|
|
40
|
+
name: cur.name,
|
|
41
|
+
playcount: cur.playcount,
|
|
42
|
+
currentRank: i + 1,
|
|
43
|
+
previousRank: prev.idx + 1,
|
|
44
|
+
deltaRank,
|
|
45
|
+
deltaCount,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
const departures = [];
|
|
50
|
+
previous.forEach((prev) => {
|
|
51
|
+
if (!curIdx.has(prev.name)) {
|
|
52
|
+
departures.push(prev);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
// Sort buckets by current rank ascending
|
|
56
|
+
risers.sort((a, b) => a.currentRank - b.currentRank);
|
|
57
|
+
fallers.sort((a, b) => a.currentRank - b.currentRank);
|
|
58
|
+
newcomers.sort((a, b) => a.currentRank - b.currentRank);
|
|
59
|
+
const max = options.maxResults ?? Number.POSITIVE_INFINITY;
|
|
60
|
+
return {
|
|
61
|
+
risers: risers.slice(0, max),
|
|
62
|
+
fallers: fallers.slice(0, max),
|
|
63
|
+
newcomers: newcomers.slice(0, max),
|
|
64
|
+
departures: departures.slice(0, max),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=trends.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trends.js","sourceRoot":"","sources":["../../../../src/services/insights/lib/trends.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { LastFmConfig } from '../../config.js';
|
|
2
|
+
import type { InsightsMoodRequest, InsightsMoodResponse } from '../insights.schemas.js';
|
|
3
|
+
/**
|
|
4
|
+
* Classifies a user's emotional mood profile (energy vs. valence coordinates,
|
|
5
|
+
* mood quadrant, and top genre categories) based on user and artist tags.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getMood(config: LastFmConfig, params: InsightsMoodRequest, init?: RequestInit): Promise<InsightsMoodResponse>;
|
|
8
|
+
//# sourceMappingURL=mood.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mood.d.ts","sourceRoot":"","sources":["../../../src/services/insights/mood.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAKvF;;;GAGG;AACH,wBAAsB,OAAO,CAC5B,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,mBAAmB,EAC3B,IAAI,CAAC,EAAE,WAAW,GAChB,OAAO,CAAC,oBAAoB,CAAC,CA2D/B"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createArtistService } from '../artist.js';
|
|
2
|
+
import { createUserService } from '../user.js';
|
|
3
|
+
import { classifyMood } from './lib/mood.js';
|
|
4
|
+
import { resolvePeriod } from './lib/periods.js';
|
|
5
|
+
/**
|
|
6
|
+
* Classifies a user's emotional mood profile (energy vs. valence coordinates,
|
|
7
|
+
* mood quadrant, and top genre categories) based on user and artist tags.
|
|
8
|
+
*/
|
|
9
|
+
export async function getMood(config, params, init) {
|
|
10
|
+
const userService = createUserService(config);
|
|
11
|
+
const artistService = createArtistService(config);
|
|
12
|
+
const resolved = resolvePeriod(params.period ?? '7day');
|
|
13
|
+
const topArtistsLimit = typeof params.topArtistsLimit === 'string'
|
|
14
|
+
? Number.parseInt(params.topArtistsLimit, 10)
|
|
15
|
+
: (params.topArtistsLimit ?? 10);
|
|
16
|
+
const [topArtistsRes, userTagsRes] = await Promise.all([
|
|
17
|
+
userService.getTopArtists({ user: params.user, period: resolved.lastfm, limit: topArtistsLimit }, init),
|
|
18
|
+
userService.getTopTags({ user: params.user, limit: 50 }, init),
|
|
19
|
+
]);
|
|
20
|
+
const artistNames = (topArtistsRes.topartists?.artist ?? [])
|
|
21
|
+
.map((a) => a.name)
|
|
22
|
+
.filter((name) => typeof name === 'string' && name.length > 0)
|
|
23
|
+
.slice(0, topArtistsLimit);
|
|
24
|
+
const userTags = (userTagsRes.toptags?.tag ?? [])
|
|
25
|
+
.map((t) => t.name)
|
|
26
|
+
.filter((name) => typeof name === 'string' && name.length > 0);
|
|
27
|
+
const perArtist = await Promise.allSettled(artistNames.map((name) => artistService
|
|
28
|
+
.getTopTags({ artist: name }, init)
|
|
29
|
+
.then((res) => (res.toptags?.tag ?? []).map((t) => t.name).filter((n) => typeof n === 'string' && n.length > 0))));
|
|
30
|
+
const artistTags = [];
|
|
31
|
+
let artistHits = 0;
|
|
32
|
+
for (const r of perArtist) {
|
|
33
|
+
if (r.status === 'fulfilled') {
|
|
34
|
+
artistTags.push(...r.value);
|
|
35
|
+
artistHits++;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const allTags = [...userTags, ...artistTags];
|
|
39
|
+
const profile = classifyMood(allTags);
|
|
40
|
+
const primarySource = userTags.length === 0 ? 'artist-tags' : artistTags.length === 0 ? 'user-tags' : 'mixed';
|
|
41
|
+
return {
|
|
42
|
+
user: params.user,
|
|
43
|
+
period: resolved.lastfm,
|
|
44
|
+
axes: profile.axes,
|
|
45
|
+
label: profile.label,
|
|
46
|
+
categories: [...profile.categories],
|
|
47
|
+
confidence: profile.confidence,
|
|
48
|
+
tagSourceCount: allTags.length,
|
|
49
|
+
artistCount: artistHits,
|
|
50
|
+
primarySource,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=mood.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mood.js","sourceRoot":"","sources":["../../../src/services/insights/mood.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LastFmConfig } from '../../config.js';
|
|
2
|
+
import type { InsightsNowPlayingRequest, InsightsNowPlayingResponse } from '../insights.schemas.js';
|
|
3
|
+
/**
|
|
4
|
+
* Strips Last.fm wiki markup and HTML tags from biography text.
|
|
5
|
+
*/
|
|
6
|
+
export declare function stripWiki(s: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Truncates biography text cleanly on a word boundary.
|
|
9
|
+
*/
|
|
10
|
+
export declare function summarizeBio(bio: string | undefined, maxChars: number): string;
|
|
11
|
+
/**
|
|
12
|
+
* Finds the highest quality image available from a Last.fm image array.
|
|
13
|
+
*/
|
|
14
|
+
export declare function findBestImage(images: unknown): string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Fetches the user's current (or most recent) track and enriches it with
|
|
17
|
+
* artist biography and similar artists.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getNowPlaying(config: LastFmConfig, params: InsightsNowPlayingRequest, init?: RequestInit): Promise<InsightsNowPlayingResponse>;
|
|
20
|
+
//# sourceMappingURL=now-playing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"now-playing.d.ts","sourceRoot":"","sources":["../../../src/services/insights/now-playing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEnD,OAAO,KAAK,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAA;AAGnG;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAO3C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO9E;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAQjE;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAClC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,yBAAyB,EACjC,IAAI,CAAC,EAAE,WAAW,GAChB,OAAO,CAAC,0BAA0B,CAAC,CAmGrC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { createArtistService } from '../artist.js';
|
|
2
|
+
import { createUserService } from '../user.js';
|
|
3
|
+
/**
|
|
4
|
+
* Strips Last.fm wiki markup and HTML tags from biography text.
|
|
5
|
+
*/
|
|
6
|
+
export function stripWiki(s) {
|
|
7
|
+
return s
|
|
8
|
+
.replace(/\[\[([^|\]]+)\|([^\]]+)\]\]/g, '$2')
|
|
9
|
+
.replace(/\[\[([^\]]+)\]\]/g, '$1')
|
|
10
|
+
.replace(/<[^>]+>/g, '')
|
|
11
|
+
.replace(/\s+/g, ' ')
|
|
12
|
+
.trim();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Truncates biography text cleanly on a word boundary.
|
|
16
|
+
*/
|
|
17
|
+
export function summarizeBio(bio, maxChars) {
|
|
18
|
+
if (!bio)
|
|
19
|
+
return '';
|
|
20
|
+
const clean = stripWiki(bio);
|
|
21
|
+
if (clean.length <= maxChars)
|
|
22
|
+
return clean;
|
|
23
|
+
const cut = clean.slice(0, maxChars);
|
|
24
|
+
const lastSpace = cut.lastIndexOf(' ');
|
|
25
|
+
return `${lastSpace > 0 ? cut.slice(0, lastSpace) : cut}…`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Finds the highest quality image available from a Last.fm image array.
|
|
29
|
+
*/
|
|
30
|
+
export function findBestImage(images) {
|
|
31
|
+
if (!Array.isArray(images))
|
|
32
|
+
return undefined;
|
|
33
|
+
const order = ['mega', 'extralarge', 'large', 'medium', 'small'];
|
|
34
|
+
for (const size of order) {
|
|
35
|
+
const hit = images.find((i) => i?.size === size && typeof i['#text'] === 'string' && i['#text'].length > 0);
|
|
36
|
+
if (hit)
|
|
37
|
+
return hit['#text'];
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Fetches the user's current (or most recent) track and enriches it with
|
|
43
|
+
* artist biography and similar artists.
|
|
44
|
+
*/
|
|
45
|
+
export async function getNowPlaying(config, params, init) {
|
|
46
|
+
const similarLimit = typeof params.similarLimit === 'string' ? Number.parseInt(params.similarLimit, 10) : (params.similarLimit ?? 3);
|
|
47
|
+
const bioMaxChars = params.bioMaxChars ?? 320;
|
|
48
|
+
const userService = createUserService(config);
|
|
49
|
+
const artistService = createArtistService(config);
|
|
50
|
+
const recentRes = await userService.getRecentTracks({ user: params.user, limit: 1 }, init);
|
|
51
|
+
const tracks = recentRes.recenttracks?.track ?? [];
|
|
52
|
+
if (tracks.length === 0) {
|
|
53
|
+
return {
|
|
54
|
+
user: params.user,
|
|
55
|
+
nowPlaying: false,
|
|
56
|
+
track: { name: '' },
|
|
57
|
+
artist: { name: '' },
|
|
58
|
+
bio: '',
|
|
59
|
+
similar: [],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const last = tracks[0];
|
|
63
|
+
const attr = (last['@attr'] ?? recentRes.recenttracks?.['@attr']);
|
|
64
|
+
const nowPlaying = attr?.nowplaying === 'true' || attr?.nowplaying === true;
|
|
65
|
+
const trackName = String(last.name ?? '');
|
|
66
|
+
const rawArtist = last.artist;
|
|
67
|
+
const artistName = typeof rawArtist === 'object' && rawArtist !== null
|
|
68
|
+
? String(rawArtist.name ?? rawArtist['#text'] ?? '')
|
|
69
|
+
: String(rawArtist ?? '');
|
|
70
|
+
const rawAlbum = last.album;
|
|
71
|
+
const album = typeof rawAlbum === 'object' && rawAlbum !== null
|
|
72
|
+
? String(rawAlbum['#text'] ?? rawAlbum.title ?? '')
|
|
73
|
+
: typeof rawAlbum === 'string'
|
|
74
|
+
? rawAlbum
|
|
75
|
+
: undefined;
|
|
76
|
+
const image = findBestImage(last.image);
|
|
77
|
+
let bio = '';
|
|
78
|
+
let artistMbid = typeof rawArtist === 'object' && rawArtist !== null ? rawArtist.mbid : undefined;
|
|
79
|
+
let artistUrl = typeof rawArtist === 'object' && rawArtist !== null ? rawArtist.url : undefined;
|
|
80
|
+
const similar = [];
|
|
81
|
+
if (artistName.length > 0) {
|
|
82
|
+
try {
|
|
83
|
+
const [infoRes, similarRes] = await Promise.all([
|
|
84
|
+
artistService.getInfo({ artist: artistName }, init),
|
|
85
|
+
artistService.getSimilar({ artist: artistName, limit: similarLimit }, init),
|
|
86
|
+
]);
|
|
87
|
+
const bioSummary = infoRes.artist?.bio?.summary ?? infoRes.artist?.bio?.content ?? '';
|
|
88
|
+
bio = summarizeBio(bioSummary, bioMaxChars);
|
|
89
|
+
if (!artistMbid && infoRes.artist?.mbid) {
|
|
90
|
+
artistMbid = infoRes.artist.mbid;
|
|
91
|
+
}
|
|
92
|
+
if (!artistUrl && infoRes.artist?.url) {
|
|
93
|
+
artistUrl = infoRes.artist.url;
|
|
94
|
+
}
|
|
95
|
+
const rawSimilar = similarRes.similarartists?.artist ?? [];
|
|
96
|
+
for (const s of rawSimilar.slice(0, similarLimit)) {
|
|
97
|
+
const matchNum = Number(s.match);
|
|
98
|
+
similar.push({
|
|
99
|
+
name: s.name,
|
|
100
|
+
url: s.url || undefined,
|
|
101
|
+
match: Number.isNaN(matchNum) ? undefined : matchNum,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Best-effort enrichment: if artist lookup fails, still return track & artist data
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
user: params.user,
|
|
111
|
+
nowPlaying,
|
|
112
|
+
track: {
|
|
113
|
+
name: trackName,
|
|
114
|
+
mbid: typeof last.mbid === 'string' && last.mbid.length > 0 ? last.mbid : undefined,
|
|
115
|
+
url: typeof last.url === 'string' && last.url.length > 0 ? last.url : undefined,
|
|
116
|
+
},
|
|
117
|
+
artist: {
|
|
118
|
+
name: artistName,
|
|
119
|
+
mbid: artistMbid || undefined,
|
|
120
|
+
url: artistUrl || undefined,
|
|
121
|
+
},
|
|
122
|
+
album: album || undefined,
|
|
123
|
+
image,
|
|
124
|
+
bio,
|
|
125
|
+
similar,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=now-playing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"now-playing.js","sourceRoot":"","sources":["../../../src/services/insights/now-playing.ts"],"names":[],"mappings":"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"}
|