Merge origin/master into PR 955

This commit is contained in:
4gray committed 2026-05-22 01:34:56 +03:00
commit 2a769ad44e
89 files changed
+6476 -465

No files matched your search

+1
View File
@@ -36,6 +36,7 @@ export * from './lib/xtream-item.interface';
export * from './lib/xtream-live-stream.interface';
export * from './lib/xtream-response.interface';
export * from './lib/xtream-restore-state.util';
export * from './lib/xtream-recently-added.utils';
export * from './lib/xtream-serie-details.interface';
export * from './lib/xtream-serie-item.interface';
export * from './lib/xtream-vod-details.interface';
@@ -0,0 +1,28 @@
import {
getXtreamRecentlyAddedMaxEpochSeconds,
toXtreamRecentlyAddedTimestamp,
} from './xtream-recently-added.utils';
describe('Xtream recently-added timestamp helpers', () => {
const now = Date.parse('2026-05-19T00:00:00.000Z');
it('treats far-future provider timestamps as invalid for recent ranking', () => {
expect(toXtreamRecentlyAddedTimestamp('1893456000', now)).toBe(0);
});
it('normalizes valid epoch seconds to milliseconds', () => {
expect(toXtreamRecentlyAddedTimestamp('1779062400', now)).toBe(
Date.parse('2026-05-18T00:00:00.000Z')
);
});
it('treats the epoch unit threshold itself as milliseconds', () => {
expect(toXtreamRecentlyAddedTimestamp('10000000000', now)).toBe(
10_000_000_000
);
});
it('builds the query upper-bound with the same future grace window', () => {
expect(getXtreamRecentlyAddedMaxEpochSeconds(now)).toBe('1779235200');
});
});
@@ -0,0 +1,60 @@
const EPOCH_MILLISECONDS_THRESHOLD = 10_000_000_000;
export const XTREAM_RECENTLY_ADDED_FUTURE_GRACE_MS = 24 * 60 * 60 * 1000;
function normalizeEpochNumber(value: number): number {
return value >= EPOCH_MILLISECONDS_THRESHOLD ? value : value * 1000;
}
function parseTimestampMs(value: unknown): number {
if (typeof value === 'number') {
return normalizeEpochNumber(value);
}
if (typeof value !== 'string') {
return 0;
}
const trimmed = value.trim();
if (!trimmed) {
return 0;
}
if (/^\d+$/.test(trimmed)) {
return normalizeEpochNumber(Number(trimmed));
}
return 0;
}
export function toXtreamRecentlyAddedTimestamp(
value: unknown,
nowMs = Date.now()
): number {
const timestampMs = parseTimestampMs(value);
if (!Number.isFinite(timestampMs) || timestampMs <= 0) {
return 0;
}
if (timestampMs > nowMs + XTREAM_RECENTLY_ADDED_FUTURE_GRACE_MS) {
return 0;
}
return timestampMs;
}
export function toXtreamRecentlyAddedEpochSeconds(
value: unknown,
nowMs = Date.now()
): string {
const timestampMs = toXtreamRecentlyAddedTimestamp(value, nowMs);
return timestampMs > 0 ? String(Math.floor(timestampMs / 1000)) : '';
}
export function getXtreamRecentlyAddedMaxEpochSeconds(
nowMs = Date.now()
): string {
return String(
Math.floor((nowMs + XTREAM_RECENTLY_ADDED_FUTURE_GRACE_MS) / 1000)
);
}