pocketcasts-api 0.1.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/LICENSE +21 -0
- package/README.md +158 -0
- package/dist/client.d.ts +52 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +151 -0
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +210 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +145 -0
- package/dist/types.js.map +1 -0
- package/package.json +59 -0
- package/src/client.ts +231 -0
- package/src/errors.ts +23 -0
- package/src/index.ts +14 -0
- package/src/types.ts +171 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { PocketCasts, type PocketCastsOptions } from './client.js';
|
|
2
|
+
export {
|
|
3
|
+
PocketCastsError,
|
|
4
|
+
PocketCastsAuthError,
|
|
5
|
+
PocketCastsAPIError,
|
|
6
|
+
} from './errors.js';
|
|
7
|
+
export type {
|
|
8
|
+
Podcast,
|
|
9
|
+
Episode,
|
|
10
|
+
SearchResult,
|
|
11
|
+
ListeningStats,
|
|
12
|
+
PlayingStatus,
|
|
13
|
+
} from './types.js';
|
|
14
|
+
export { PLAYING_STATUS_TO_API } from './types.js';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// --- Playing Status ---
|
|
4
|
+
// The API returns playing status as either:
|
|
5
|
+
// - a number: 0=unplayed, 1=not played (same as unplayed), 2=playing, 3=played
|
|
6
|
+
// - a string: "unplayed", "playing", "played"
|
|
7
|
+
|
|
8
|
+
const PLAYING_STATUS_NUM_MAP: Record<number, PlayingStatus> = {
|
|
9
|
+
0: 'unplayed',
|
|
10
|
+
1: 'unplayed', // value 1 appears in the wild, treat as unplayed
|
|
11
|
+
2: 'playing',
|
|
12
|
+
3: 'played',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const PLAYING_STATUS_STRINGS = ['unplayed', 'playing', 'played'] as const;
|
|
16
|
+
|
|
17
|
+
export const PlayingStatusSchema = z.union([
|
|
18
|
+
z.number().transform((n) => PLAYING_STATUS_NUM_MAP[n] ?? 'unplayed'),
|
|
19
|
+
z.enum(PLAYING_STATUS_STRINGS),
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
export type PlayingStatus = 'unplayed' | 'playing' | 'played';
|
|
23
|
+
|
|
24
|
+
export const PLAYING_STATUS_TO_API: Record<PlayingStatus, number> = {
|
|
25
|
+
unplayed: 0,
|
|
26
|
+
playing: 2,
|
|
27
|
+
played: 3,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// --- Podcast ---
|
|
31
|
+
|
|
32
|
+
export const PodcastSchema = z
|
|
33
|
+
.looseObject({
|
|
34
|
+
uuid: z.string(),
|
|
35
|
+
title: z.string(),
|
|
36
|
+
author: z.string(),
|
|
37
|
+
description: z.string().default(''),
|
|
38
|
+
url: z.string(),
|
|
39
|
+
thumbnail_url: z.string().optional(),
|
|
40
|
+
language: z.string().optional(),
|
|
41
|
+
categories: z.array(z.string()).optional(),
|
|
42
|
+
media_type: z.string().optional(),
|
|
43
|
+
sort_order: z.number().optional(),
|
|
44
|
+
})
|
|
45
|
+
.transform(({ thumbnail_url, media_type, sort_order, ...rest }) => ({
|
|
46
|
+
...rest,
|
|
47
|
+
thumbnailUrl: thumbnail_url,
|
|
48
|
+
mediaType: media_type,
|
|
49
|
+
sortOrder: sort_order,
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
export type Podcast = z.output<typeof PodcastSchema>;
|
|
53
|
+
|
|
54
|
+
// --- Episode ---
|
|
55
|
+
// The real API returns camelCase fields (podcastUuid, playingStatus, etc.)
|
|
56
|
+
// We accept both camelCase and snake_case for flexibility.
|
|
57
|
+
|
|
58
|
+
const boolOrNum = z
|
|
59
|
+
.union([z.boolean(), z.number().transform((n) => n === 1)])
|
|
60
|
+
.default(false);
|
|
61
|
+
const stringOrNum = z
|
|
62
|
+
.union([z.number(), z.string().transform((s) => parseInt(s, 10) || 0)])
|
|
63
|
+
.optional();
|
|
64
|
+
|
|
65
|
+
export const EpisodeSchema = z
|
|
66
|
+
.looseObject({
|
|
67
|
+
uuid: z.string(),
|
|
68
|
+
title: z.string(),
|
|
69
|
+
url: z.string().default(''),
|
|
70
|
+
// Accept both camelCase (real API) and snake_case
|
|
71
|
+
podcastUuid: z.string().optional(),
|
|
72
|
+
podcast_uuid: z.string().optional(),
|
|
73
|
+
podcastTitle: z.string().optional(),
|
|
74
|
+
podcast_title: z.string().optional(),
|
|
75
|
+
duration: z.number().default(0),
|
|
76
|
+
published: z.string().optional(),
|
|
77
|
+
publishedAt: z.string().optional(),
|
|
78
|
+
published_at: z.string().optional(),
|
|
79
|
+
fileType: z.string().optional(),
|
|
80
|
+
file_type: z.string().optional(),
|
|
81
|
+
size: stringOrNum,
|
|
82
|
+
playingStatus: PlayingStatusSchema.optional(),
|
|
83
|
+
playing_status: PlayingStatusSchema.optional(),
|
|
84
|
+
playedUpTo: z.number().optional(),
|
|
85
|
+
played_up_to: z.number().optional(),
|
|
86
|
+
starred: boolOrNum,
|
|
87
|
+
isDeleted: boolOrNum,
|
|
88
|
+
is_deleted: boolOrNum,
|
|
89
|
+
isVideo: boolOrNum,
|
|
90
|
+
is_video: boolOrNum,
|
|
91
|
+
})
|
|
92
|
+
.transform((e) => ({
|
|
93
|
+
...e,
|
|
94
|
+
podcastUuid: e.podcastUuid ?? e.podcast_uuid ?? '',
|
|
95
|
+
podcastTitle: e.podcastTitle ?? e.podcast_title,
|
|
96
|
+
publishedAt: e.publishedAt || e.published_at || e.published || '',
|
|
97
|
+
fileType: e.fileType ?? e.file_type,
|
|
98
|
+
playingStatus: e.playingStatus ?? e.playing_status ?? ('unplayed' as const),
|
|
99
|
+
playedUpTo: e.playedUpTo ?? e.played_up_to ?? 0,
|
|
100
|
+
isDeleted: e.isDeleted || e.is_deleted || false,
|
|
101
|
+
isVideo: e.isVideo || e.is_video || false,
|
|
102
|
+
}));
|
|
103
|
+
|
|
104
|
+
export type Episode = z.output<typeof EpisodeSchema>;
|
|
105
|
+
|
|
106
|
+
// --- Search Result ---
|
|
107
|
+
|
|
108
|
+
export const SearchResultSchema = z
|
|
109
|
+
.looseObject({
|
|
110
|
+
uuid: z.string(),
|
|
111
|
+
title: z.string(),
|
|
112
|
+
author: z.string().default(''),
|
|
113
|
+
description: z.string().optional(),
|
|
114
|
+
thumbnail_url: z.string().optional(),
|
|
115
|
+
})
|
|
116
|
+
.transform(({ thumbnail_url, ...rest }) => ({
|
|
117
|
+
...rest,
|
|
118
|
+
thumbnailUrl: thumbnail_url,
|
|
119
|
+
}));
|
|
120
|
+
|
|
121
|
+
export type SearchResult = z.output<typeof SearchResultSchema>;
|
|
122
|
+
|
|
123
|
+
// --- Listening Stats ---
|
|
124
|
+
// Real API returns camelCase with some values as strings (not numbers).
|
|
125
|
+
|
|
126
|
+
const numOrStr = z
|
|
127
|
+
.union([z.number(), z.string().transform((s) => parseFloat(s) || 0)])
|
|
128
|
+
.default(0);
|
|
129
|
+
|
|
130
|
+
export const ListeningStatsSchema = z
|
|
131
|
+
.looseObject({
|
|
132
|
+
// camelCase (real API)
|
|
133
|
+
timeSilenceRemoval: numOrStr,
|
|
134
|
+
timeListened: numOrStr,
|
|
135
|
+
timeSkipping: numOrStr,
|
|
136
|
+
timeIntroSkipping: numOrStr,
|
|
137
|
+
timeVariableSpeed: numOrStr,
|
|
138
|
+
totalPodcasts: numOrStr,
|
|
139
|
+
totalEpisodes: numOrStr,
|
|
140
|
+
timesStartedAt: z.string().optional(),
|
|
141
|
+
startedAt: z.string().optional(),
|
|
142
|
+
// snake_case fallback
|
|
143
|
+
time_silence_removal: numOrStr,
|
|
144
|
+
time_listened: numOrStr,
|
|
145
|
+
time_skipping: numOrStr,
|
|
146
|
+
time_intro_skipping: numOrStr,
|
|
147
|
+
time_variable_speed: numOrStr,
|
|
148
|
+
total_podcasts: numOrStr,
|
|
149
|
+
total_episodes: numOrStr,
|
|
150
|
+
started_at: z.string().optional(),
|
|
151
|
+
})
|
|
152
|
+
.transform((s) => ({
|
|
153
|
+
...s,
|
|
154
|
+
timeSilenceRemoval: s.timeSilenceRemoval || s.time_silence_removal || 0,
|
|
155
|
+
timeListened: s.timeListened || s.time_listened || 0,
|
|
156
|
+
timeSkipping: s.timeSkipping || s.time_skipping || 0,
|
|
157
|
+
timeIntroSkipping: s.timeIntroSkipping || s.time_intro_skipping || 0,
|
|
158
|
+
timeVariableSpeed: s.timeVariableSpeed || s.time_variable_speed || 0,
|
|
159
|
+
totalPodcasts: s.totalPodcasts || s.total_podcasts || 0,
|
|
160
|
+
totalEpisodes: s.totalEpisodes || s.total_episodes || 0,
|
|
161
|
+
startedAt: s.timesStartedAt ?? s.startedAt ?? s.started_at ?? '',
|
|
162
|
+
}));
|
|
163
|
+
|
|
164
|
+
export type ListeningStats = z.output<typeof ListeningStatsSchema>;
|
|
165
|
+
|
|
166
|
+
// --- Login Response ---
|
|
167
|
+
|
|
168
|
+
export const LoginResponseSchema = z.object({
|
|
169
|
+
token: z.string(),
|
|
170
|
+
uuid: z.string(),
|
|
171
|
+
});
|