fix remaining erasure issues

This commit is contained in:
FoxxMD 2026-07-09 01:59:58 +00:00
parent 69b19da6aa
commit 77de2a7526
4 changed files with 35 additions and 36 deletions

View file

@ -1,13 +1,12 @@
import { findCauseByFunc } from "../../utils/ErrorUtils.js";
import { isArbitraryObject } from "../infrastructure/Atomic.js";
import ErrnoException = NodeJS.ErrnoException;
export type NodeNetworkErrorCode = 'ENOTFOUND' | 'ETIMEDOUT' | 'EAI_AGAIN' | 'ECONNRESET' | 'ECONNREFUSED' | 'ERRADDRINUSE' | 'EADDRNOTAVAIL' | 'ECONNABORTED' | 'EHOSTUNREACH';
export const NETWORK_ERROR_CODES: NodeNetworkErrorCode[] = ['ENOTFOUND', 'ETIMEDOUT', 'EAI_AGAIN', 'ECONNRESET', 'ECONNREFUSED', 'ERRADDRINUSE', 'EADDRNOTAVAIL', 'ECONNABORTED', 'EHOSTUNREACH'];
export const NETWORK_ERROR_FAILURE_CODES: NodeNetworkErrorCode[] = ['ENOTFOUND', 'EAI_AGAIN', 'ECONNRESET', 'ECONNREFUSED', 'ERRADDRINUSE', 'EADDRNOTAVAIL', 'ECONNABORTED', 'EHOSTUNREACH'];
export const isErrnoException = (error: unknown): error is ErrnoException => {
export const isErrnoException = (error: unknown): error is NodeJS.ErrnoException => {
return isArbitraryObject(error) &&
error instanceof Error &&
(typeof error.errno === "number" || typeof error.errno === "undefined") &&
@ -16,7 +15,7 @@ export const isErrnoException = (error: unknown): error is ErrnoException => {
(typeof error.syscall === "string" || typeof error.syscall === "undefined");
}
export interface NodeNetworkException extends ErrnoException {
export interface NodeNetworkException extends NodeJS.ErrnoException {
code: NodeNetworkErrorCode
}

View file

@ -1,14 +1,14 @@
import { type Logger } from '@foxxmd/logging';
import { Dayjs, type ManipulateType } from "dayjs";
import type { Logger } from '@foxxmd/logging';
import type { Dayjs, ManipulateType } from "dayjs";
import { type Request, type Response } from "express";
import { type NextFunction, type ParamsDictionary, type Query } from "express-serve-static-core";
import type { NextFunction, ParamsDictionary, Query } from "express-serve-static-core";
import { FixedSizeList } from 'fixed-size-list';
import { type ErrorLike, isPlayObject, type PlayMeta, type PlayObject, type PlayObjectMinimal, type UnixTimestamp } from "../../../core/Atomic.js";
import TupleMap from "../TupleMap.js";
import { MusicBrainzApi } from 'musicbrainz-api';
import { type SourceType } from './config/source/sources.js';
import type { SourceType } from './config/source/sources.js';
import { type ClientType, clientTypes } from './config/client/clients.js';
import assert, { AssertionError } from 'assert';
import assert from 'assert';
export const lowGranularitySources: SourceType[] = ['subsonic', 'ytmusic'];

View file

@ -31,12 +31,12 @@ import { readJson } from '../utils/DataUtils.js';
import { findCauseByFunc } from "../utils/ErrorUtils.js";
import { joinedUrl } from "../utils/NetworkUtils.js";
import { type RecentlyPlayedOptions } from "./AbstractSource.js";
import AlbumObjectSimplified = SpotifyApi.AlbumObjectSimplified;
import ArtistObjectSimplified = SpotifyApi.ArtistObjectSimplified;
import CurrentlyPlayingObject = SpotifyApi.CurrentlyPlayingObject;
import PlayHistoryObject = SpotifyApi.PlayHistoryObject;
import TrackObjectFull = SpotifyApi.TrackObjectFull;
import UserDevice = SpotifyApi.UserDevice;
// import SpotifyApi.AlbumObjectSimplified = SpotifyApi.SpotifyApi.AlbumObjectSimplified;
// import SpotifyApi.ArtistObjectSimplified = SpotifyApi.SpotifyApi.ArtistObjectSimplified;
// import SpotifyApi.CurrentlyPlayingObject = SpotifyApi.SpotifyApi.CurrentlyPlayingObject;
// import SpotifyApi.PlayHistoryObject = SpotifyApi.SpotifyApi.PlayHistoryObject;
// import SpotifyApi.TrackObjectFull = SpotifyApi.SpotifyApi.TrackObjectFull;
// import SpotifyApi.UserDevice = SpotifyApi.SpotifyApi.UserDevice;
import { MemoryPositionalSource } from "./MemoryPositionalSource.js";
import { baseFormatPlayObj } from "../utils/PlayTransformUtils.js";
import { createGetScrobblesForTimeRangeFunc } from "../utils/ListenFetchUtils.js";
@ -81,14 +81,14 @@ export default class SpotifySource extends MemoryPositionalSource implements Pag
this.getScrobblesForTimeRange = createGetScrobblesForTimeRangeFunc(this, this.logger);
}
static formatPlayObj(obj: PlayHistoryObject | CurrentlyPlayingObject, options: FormatPlayObjectOptions = {}): PlayObject {
static formatPlayObj(obj: SpotifyApi.PlayHistoryObject | SpotifyApi.CurrentlyPlayingObject, options: FormatPlayObjectOptions = {}): PlayObject {
const {
newFromSource = false
} = options;
let artists: ArtistObjectSimplified[];
let album: AlbumObjectSimplified;
let artists: SpotifyApi.ArtistObjectSimplified[];
let album: SpotifyApi.AlbumObjectSimplified;
let name: string;
let duration_ms: number;
let played_at: Dayjs;
@ -165,11 +165,11 @@ export default class SpotifySource extends MemoryPositionalSource implements Pag
isrc
} = {},
track_number
} = item as TrackObjectFull;
} = item as SpotifyApi.TrackObjectFull;
delete (obj.item as TrackObjectFull).available_markets;
if((obj.item as TrackObjectFull).album !== undefined) {
delete (obj.item as TrackObjectFull).album.available_markets;
delete (obj.item as SpotifyApi.TrackObjectFull).available_markets;
if((obj.item as SpotifyApi.TrackObjectFull).album !== undefined) {
delete (obj.item as SpotifyApi.TrackObjectFull).album.available_markets;
}
scrobbleTsSOC = SCROBBLE_TS_SOC_START;
@ -196,7 +196,7 @@ export default class SpotifySource extends MemoryPositionalSource implements Pag
images = []
} = album || {};
let actualAlbumArtists: ArtistObjectSimplified[] = [];
let actualAlbumArtists: SpotifyApi.ArtistObjectSimplified[] = [];
if ((artists.length !== albumArtists.length) || !artists.every(artist => albumArtists.some(albumArtist => artist.id === albumArtist.id))) {
// only include album artists if they are not the EXACT same as the track artists
// ...if they aren't the exact same then include all artists, even if they are duplicates of track artists
@ -438,7 +438,7 @@ export default class SpotifySource extends MemoryPositionalSource implements Pag
const result = await this.callApi<ReturnType<typeof this.spotifyApi.getMyRecentlyPlayedTracks>>((api: SpotifyWebApi) => api.getMyRecentlyPlayedTracks(options));
let more = true;
let plays = result.body.items.map((x: PlayHistoryObject) => SpotifySource.formatPlayObj(x)).sort(sortByOldestPlayDate);
let plays = result.body.items.map((x: SpotifyApi.PlayHistoryObject) => SpotifySource.formatPlayObj(x)).sort(sortByOldestPlayDate);
if(to !== undefined) {
const toDate = dayjs.unix(to);
@ -455,7 +455,7 @@ export default class SpotifySource extends MemoryPositionalSource implements Pag
}
return {
data: result.body.items.map((x: PlayHistoryObject) => SpotifySource.formatPlayObj(x)).sort(sortByOldestPlayDate),
data: result.body.items.map((x: SpotifyApi.PlayHistoryObject) => SpotifySource.formatPlayObj(x)).sort(sortByOldestPlayDate),
meta: {
...params,
total: result.body.total,
@ -493,7 +493,7 @@ export default class SpotifySource extends MemoryPositionalSource implements Pag
return undefined;
}
getCurrentPlaybackState = async (logError = true): Promise<{device?: UserDevice, playerState?: PlayerStateData}> => {
getCurrentPlaybackState = async (logError = true): Promise<{device?: SpotifyApi.UserDevice, playerState?: PlayerStateData}> => {
try {
const funcState = (api: SpotifyWebApi) => api.getMyCurrentPlaybackState();
const res = await this.callApi<ReturnType<typeof this.spotifyApi.getMyCurrentPlaybackState>>(funcState);
@ -634,9 +634,9 @@ export default class SpotifySource extends MemoryPositionalSource implements Pag
protected getBackloggedPlays = async (options: RecentlyPlayedOptions = {}) => await this.getPlayHistory({formatted: true, ...options})
}
const asPlayHistoryObject = (obj: object): obj is PlayHistoryObject => 'played_at' in obj
const asPlayHistoryObject = (obj: object): obj is SpotifyApi.PlayHistoryObject => 'played_at' in obj
const asCurrentlyPlayingObject = (obj: object): obj is CurrentlyPlayingObject => 'is_playing' in obj
const asCurrentlyPlayingObject = (obj: object): obj is SpotifyApi.CurrentlyPlayingObject => 'is_playing' in obj
const hasApiPermissionError = (e: Error): boolean => findCauseByFunc(e, (err) => err.message.includes('Permissions missing')) !== undefined

View file

@ -1,12 +1,12 @@
import { type LogDataPretty, type LogLevel } from "@foxxmd/logging";
import { Dayjs } from "dayjs";
import { type AdditionalTrackInfoResponse } from "../backend/common/vendor/listenbrainz/interfaces.js";
import type { DeepOmit, Merge, RequiredKeys, StrictOmit } from "ts-essentials";
import { type ErrorObject } from "serialize-error";
import { type PlayPlatformIdStr } from "../backend/common/infrastructure/Atomic.js";
import { type FlowControlTerm, type TransformHook } from "../backend/common/infrastructure/Transform.js";
import { type Changeset } from "json-diff-ts";
import {type IParseBaseOptions } from 'qs';
import type { LogDataPretty, LogLevel } from "@foxxmd/logging";
import type { Dayjs } from "dayjs";
import type { AdditionalTrackInfoResponse } from "../backend/common/vendor/listenbrainz/interfaces.js";
import type { Merge, RequiredKeys, StrictOmit } from "ts-essentials";
import type { ErrorObject } from "serialize-error";
import type { PlayPlatformIdStr } from "../backend/common/infrastructure/Atomic.js";
import type { FlowControlTerm, TransformHook } from "../backend/common/infrastructure/Transform.js";
import type { Changeset } from "json-diff-ts";
import type { IParseBaseOptions } from 'qs';
export type ComponentTypeClient = 'client';
export const COMPONENT_TYPE_CLIENT: ComponentTypeClient = 'client';