@darco2903/altv-web-api 1.6.0 → 2.0.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.
@@ -0,0 +1,155 @@
1
+ import { initContract, ZodErrorSchema } from "@ts-rest/core";
2
+ import { z } from "zod";
3
+ import { apiError, apiSuccess, authResponses } from "../types.js";
4
+ import { roomNameSchema, roomStateSchema, roomSummarySchema, } from "../types/room.js";
5
+ import { sceneActionSchema } from "../types/scene.js";
6
+ const c = initContract();
7
+ const notFound = {
8
+ 404: apiError(z.literal("ROOM_NOT_FOUND"), z.literal("The requested room was not found")),
9
+ };
10
+ const internalError = {
11
+ 500: apiError(z.literal("INTERNAL_SERVER_ERROR"), z.string()),
12
+ };
13
+ const noTrack = apiError(z.literal("NO_TRACK"), z.literal("The room has no track"));
14
+ const invalidVideoId = apiError(z.literal("INVALID_VIDEO_ID"), z.literal("The provided video ID is invalid"));
15
+ export default c.router({
16
+ create: {
17
+ method: "POST",
18
+ path: "/room",
19
+ description: "Create a room owned by the logged in user",
20
+ body: z.object({
21
+ name: roomNameSchema.nullable(),
22
+ }),
23
+ responses: {
24
+ 200: apiSuccess(z.object({
25
+ roomId: z.string(),
26
+ })),
27
+ 400: ZodErrorSchema,
28
+ ...authResponses,
29
+ 409: apiError(z.literal("TOO_MANY_ROOMS"), z.literal("You can't own more rooms")),
30
+ ...internalError,
31
+ },
32
+ },
33
+ list: {
34
+ method: "GET",
35
+ path: "/room",
36
+ description: "Rooms owned by the logged in user",
37
+ responses: {
38
+ 200: apiSuccess(z.object({
39
+ rooms: z.array(roomSummarySchema),
40
+ })),
41
+ ...authResponses,
42
+ ...internalError,
43
+ },
44
+ },
45
+ get: {
46
+ method: "GET",
47
+ path: "/room/:roomId",
48
+ description: "Get the state of a room",
49
+ responses: {
50
+ 200: apiSuccess(roomStateSchema),
51
+ ...notFound,
52
+ ...internalError,
53
+ },
54
+ },
55
+ rename: {
56
+ method: "PATCH",
57
+ path: "/room/:roomId",
58
+ description: "Rename a room, owner only, listeners get the name through the roomName socket event",
59
+ body: z.object({
60
+ name: roomNameSchema.nullable(),
61
+ }),
62
+ responses: {
63
+ 200: c.noBody(),
64
+ 400: ZodErrorSchema,
65
+ ...authResponses,
66
+ ...notFound,
67
+ ...internalError,
68
+ },
69
+ },
70
+ delete: {
71
+ method: "DELETE",
72
+ path: "/room/:roomId",
73
+ description: "Delete a room, owner only",
74
+ body: z.undefined(),
75
+ responses: {
76
+ 200: c.noBody(),
77
+ ...authResponses,
78
+ ...notFound,
79
+ ...internalError,
80
+ },
81
+ },
82
+ track: {
83
+ method: "POST",
84
+ path: "/room/:roomId/track",
85
+ description: "Play a YouTube video in a room, listeners switch through the roomTrack socket event. Resolves once it plays, a cache miss downloads the whole video first",
86
+ body: z.object({
87
+ videoId: z.string(),
88
+ }),
89
+ responses: {
90
+ 200: c.noBody(),
91
+ 400: z.union([ZodErrorSchema, invalidVideoId]),
92
+ ...authResponses,
93
+ ...notFound,
94
+ ...internalError,
95
+ },
96
+ },
97
+ play: {
98
+ method: "POST",
99
+ path: "/room/:roomId/play",
100
+ description: "Play the track of a room, from its position. A room without a stream starts a new one",
101
+ body: z.undefined(),
102
+ responses: {
103
+ 200: c.noBody(),
104
+ 400: z.union([noTrack, invalidVideoId]),
105
+ ...authResponses,
106
+ ...notFound,
107
+ ...internalError,
108
+ },
109
+ },
110
+ pause: {
111
+ method: "POST",
112
+ path: "/room/:roomId/pause",
113
+ description: "Pause a room",
114
+ body: z.undefined(),
115
+ responses: {
116
+ 200: c.noBody(),
117
+ ...authResponses,
118
+ ...notFound,
119
+ ...internalError,
120
+ },
121
+ },
122
+ seek: {
123
+ method: "POST",
124
+ path: "/room/:roomId/seek",
125
+ description: "Seek the track of a room",
126
+ query: z.object({
127
+ time: z.coerce.number().min(0),
128
+ }),
129
+ body: z.undefined(),
130
+ responses: {
131
+ 200: c.noBody(),
132
+ 400: z.union([ZodErrorSchema, noTrack]),
133
+ ...authResponses,
134
+ ...notFound,
135
+ ...internalError,
136
+ },
137
+ },
138
+ scene: {
139
+ method: "POST",
140
+ path: "/room/:roomId/scene",
141
+ description: "Change the scene of a room, listeners get the new scene through the roomScene socket event",
142
+ body: sceneActionSchema,
143
+ responses: {
144
+ 200: c.noBody(),
145
+ 400: z.union([
146
+ ZodErrorSchema,
147
+ apiError(z.literal("SCENE_FULL"), z.literal("The scene can't have more of this object")),
148
+ apiError(z.literal("SCENE_OBJECT_NOT_FOUND"), z.literal("The scene object was not found")),
149
+ ]),
150
+ ...authResponses,
151
+ ...notFound,
152
+ ...internalError,
153
+ },
154
+ },
155
+ });
@@ -1,2 +1,2 @@
1
- export * from "./mediaStream.js";
1
+ export * from "./room.js";
2
2
  export * from "./types.js";
@@ -1,2 +1,2 @@
1
- export * from "./mediaStream.js";
1
+ export * from "./room.js";
2
2
  export * from "./types.js";
@@ -0,0 +1,28 @@
1
+ import type { RoomState } from "../types/room.js";
2
+ import type { MediaStreamScene } from "../types/scene.js";
3
+ export interface SocketClientToServerRoom {
4
+ /** Calls back with the state of the room, null if it doesn't exist */
5
+ roomJoin: (roomId: string, callback: (state: RoomState | null) => void) => void;
6
+ roomLeave: (roomId: string, callback: (success: boolean) => void) => void;
7
+ }
8
+ /**
9
+ * - `ended`: the track played to the end, clients still have the last seconds buffered
10
+ * - `idle`: nobody listened, or the room stayed paused, for a while
11
+ * - `error`: the media couldn't be read
12
+ * - `shutdown`: the server is stopping
13
+ */
14
+ export type RoomEndReason = "ended" | "idle" | "error" | "shutdown";
15
+ export interface SocketServerToClientRoom {
16
+ roomPlay: (roomId: string, startTime: number) => void;
17
+ roomPause: (roomId: string) => void;
18
+ roomSeek: (roomId: string, startTime: number) => void;
19
+ /** A new stream started, for a new track or to resume one: the audio url changed */
20
+ roomTrack: (roomId: string, state: RoomState) => void;
21
+ /** The stream is gone, its url now returns 404. The room stays, with this state. */
22
+ roomEnd: (roomId: string, reason: RoomEndReason, state: RoomState) => void;
23
+ /** The scene changed, the whole new scene */
24
+ roomScene: (roomId: string, scene: MediaStreamScene) => void;
25
+ roomName: (roomId: string, name: string | null) => void;
26
+ /** The room is gone, every request about it now returns 404 */
27
+ roomDeleted: (roomId: string) => void;
28
+ }
@@ -1,5 +1,5 @@
1
- import { SocketClientToServerMediaStream, SocketServerToClientMediaStream } from "./mediaStream.js";
2
- export interface ClientToServerEvents extends SocketClientToServerMediaStream {
1
+ import { SocketClientToServerRoom, SocketServerToClientRoom } from "./room.js";
2
+ export interface ClientToServerEvents extends SocketClientToServerRoom {
3
3
  }
4
- export interface ServerToClientEvents extends SocketServerToClientMediaStream {
4
+ export interface ServerToClientEvents extends SocketServerToClientRoom {
5
5
  }
@@ -1,2 +1,3 @@
1
1
  export * from "./youtube.js";
2
+ export * from "./room.js";
2
3
  export * from "./scene.js";
@@ -1,2 +1,3 @@
1
1
  export * from "./youtube.js";
2
+ export * from "./room.js";
2
3
  export * from "./scene.js";
@@ -0,0 +1,241 @@
1
+ import { z } from "zod";
2
+ export declare const ROOM_ID_PATTERN: RegExp;
3
+ export declare const ROOM_NAME_MAX_LENGTH = 64;
4
+ export declare const roomNameSchema: z.ZodString;
5
+ export declare const roomTrackSchema: z.ZodObject<{
6
+ videoId: z.ZodString;
7
+ title: z.ZodString;
8
+ /** seconds */
9
+ duration: z.ZodNumber;
10
+ }, "strip", z.ZodTypeAny, {
11
+ videoId: string;
12
+ title: string;
13
+ duration: number;
14
+ }, {
15
+ videoId: string;
16
+ title: string;
17
+ duration: number;
18
+ }>;
19
+ export declare const roomStateSchema: z.ZodObject<{
20
+ roomId: z.ZodString;
21
+ name: z.ZodNullable<z.ZodString>;
22
+ /** public_id of the owner */
23
+ ownerId: z.ZodString;
24
+ track: z.ZodNullable<z.ZodObject<{
25
+ videoId: z.ZodString;
26
+ title: z.ZodString;
27
+ /** seconds */
28
+ duration: z.ZodNumber;
29
+ }, "strip", z.ZodTypeAny, {
30
+ videoId: string;
31
+ title: string;
32
+ duration: number;
33
+ }, {
34
+ videoId: string;
35
+ title: string;
36
+ duration: number;
37
+ }>>;
38
+ /** Audio of the current track, null while the room has no stream: nothing is playing, or it was paused for long */
39
+ url: z.ZodNullable<z.ZodString>;
40
+ paused: z.ZodBoolean;
41
+ /** Server timestamp (ms) at which the media was at 0 */
42
+ startTime: z.ZodNumber;
43
+ /** seconds, media position when the state was sent */
44
+ currentTime: z.ZodNumber;
45
+ scene: z.ZodObject<{
46
+ speakers: z.ZodArray<z.ZodObject<{
47
+ id: z.ZodString;
48
+ x: z.ZodNumber;
49
+ y: z.ZodNumber;
50
+ z: z.ZodNumber;
51
+ yaw: z.ZodNumber;
52
+ pitch: z.ZodNumber;
53
+ directional: z.ZodBoolean;
54
+ }, "strip", z.ZodTypeAny, {
55
+ id: string;
56
+ x: number;
57
+ y: number;
58
+ z: number;
59
+ yaw: number;
60
+ pitch: number;
61
+ directional: boolean;
62
+ }, {
63
+ id: string;
64
+ x: number;
65
+ y: number;
66
+ z: number;
67
+ yaw: number;
68
+ pitch: number;
69
+ directional: boolean;
70
+ }>, "many">;
71
+ cars: z.ZodArray<z.ZodObject<{
72
+ id: z.ZodString;
73
+ x: z.ZodNumber;
74
+ z: z.ZodNumber;
75
+ yaw: z.ZodNumber;
76
+ doors: z.ZodArray<z.ZodBoolean, "many">;
77
+ windows: z.ZodArray<z.ZodBoolean, "many">;
78
+ radio: z.ZodBoolean;
79
+ }, "strip", z.ZodTypeAny, {
80
+ id: string;
81
+ x: number;
82
+ z: number;
83
+ yaw: number;
84
+ doors: boolean[];
85
+ windows: boolean[];
86
+ radio: boolean;
87
+ }, {
88
+ id: string;
89
+ x: number;
90
+ z: number;
91
+ yaw: number;
92
+ doors: boolean[];
93
+ windows: boolean[];
94
+ radio: boolean;
95
+ }>, "many">;
96
+ }, "strip", z.ZodTypeAny, {
97
+ speakers: {
98
+ id: string;
99
+ x: number;
100
+ y: number;
101
+ z: number;
102
+ yaw: number;
103
+ pitch: number;
104
+ directional: boolean;
105
+ }[];
106
+ cars: {
107
+ id: string;
108
+ x: number;
109
+ z: number;
110
+ yaw: number;
111
+ doors: boolean[];
112
+ windows: boolean[];
113
+ radio: boolean;
114
+ }[];
115
+ }, {
116
+ speakers: {
117
+ id: string;
118
+ x: number;
119
+ y: number;
120
+ z: number;
121
+ yaw: number;
122
+ pitch: number;
123
+ directional: boolean;
124
+ }[];
125
+ cars: {
126
+ id: string;
127
+ x: number;
128
+ z: number;
129
+ yaw: number;
130
+ doors: boolean[];
131
+ windows: boolean[];
132
+ radio: boolean;
133
+ }[];
134
+ }>;
135
+ }, "strip", z.ZodTypeAny, {
136
+ name: string | null;
137
+ roomId: string;
138
+ ownerId: string;
139
+ track: {
140
+ videoId: string;
141
+ title: string;
142
+ duration: number;
143
+ } | null;
144
+ url: string | null;
145
+ paused: boolean;
146
+ startTime: number;
147
+ currentTime: number;
148
+ scene: {
149
+ speakers: {
150
+ id: string;
151
+ x: number;
152
+ y: number;
153
+ z: number;
154
+ yaw: number;
155
+ pitch: number;
156
+ directional: boolean;
157
+ }[];
158
+ cars: {
159
+ id: string;
160
+ x: number;
161
+ z: number;
162
+ yaw: number;
163
+ doors: boolean[];
164
+ windows: boolean[];
165
+ radio: boolean;
166
+ }[];
167
+ };
168
+ }, {
169
+ name: string | null;
170
+ roomId: string;
171
+ ownerId: string;
172
+ track: {
173
+ videoId: string;
174
+ title: string;
175
+ duration: number;
176
+ } | null;
177
+ url: string | null;
178
+ paused: boolean;
179
+ startTime: number;
180
+ currentTime: number;
181
+ scene: {
182
+ speakers: {
183
+ id: string;
184
+ x: number;
185
+ y: number;
186
+ z: number;
187
+ yaw: number;
188
+ pitch: number;
189
+ directional: boolean;
190
+ }[];
191
+ cars: {
192
+ id: string;
193
+ x: number;
194
+ z: number;
195
+ yaw: number;
196
+ doors: boolean[];
197
+ windows: boolean[];
198
+ radio: boolean;
199
+ }[];
200
+ };
201
+ }>;
202
+ export declare const roomSummarySchema: z.ZodObject<{
203
+ roomId: z.ZodString;
204
+ name: z.ZodNullable<z.ZodString>;
205
+ track: z.ZodNullable<z.ZodObject<{
206
+ videoId: z.ZodString;
207
+ title: z.ZodString;
208
+ /** seconds */
209
+ duration: z.ZodNumber;
210
+ }, "strip", z.ZodTypeAny, {
211
+ videoId: string;
212
+ title: string;
213
+ duration: number;
214
+ }, {
215
+ videoId: string;
216
+ title: string;
217
+ duration: number;
218
+ }>>;
219
+ paused: z.ZodBoolean;
220
+ }, "strip", z.ZodTypeAny, {
221
+ name: string | null;
222
+ roomId: string;
223
+ track: {
224
+ videoId: string;
225
+ title: string;
226
+ duration: number;
227
+ } | null;
228
+ paused: boolean;
229
+ }, {
230
+ name: string | null;
231
+ roomId: string;
232
+ track: {
233
+ videoId: string;
234
+ title: string;
235
+ duration: number;
236
+ } | null;
237
+ paused: boolean;
238
+ }>;
239
+ export type RoomTrack = z.infer<typeof roomTrackSchema>;
240
+ export type RoomState = z.infer<typeof roomStateSchema>;
241
+ export type RoomSummary = z.infer<typeof roomSummarySchema>;
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ import { mediaStreamSceneSchema } from "./scene.js";
3
+ export const ROOM_ID_PATTERN = /^[a-zA-Z0-9]{8}$/;
4
+ export const ROOM_NAME_MAX_LENGTH = 64;
5
+ export const roomNameSchema = z
6
+ .string()
7
+ .trim()
8
+ .min(1)
9
+ .max(ROOM_NAME_MAX_LENGTH);
10
+ export const roomTrackSchema = z.object({
11
+ videoId: z.string(),
12
+ title: z.string(),
13
+ /** seconds */
14
+ duration: z.number(),
15
+ });
16
+ export const roomStateSchema = z.object({
17
+ roomId: z.string(),
18
+ name: z.string().nullable(),
19
+ /** public_id of the owner */
20
+ ownerId: z.string(),
21
+ track: roomTrackSchema.nullable(),
22
+ /** Audio of the current track, null while the room has no stream: nothing is playing, or it was paused for long */
23
+ url: z.string().nullable(),
24
+ paused: z.boolean(),
25
+ /** Server timestamp (ms) at which the media was at 0 */
26
+ startTime: z.number(),
27
+ /** seconds, media position when the state was sent */
28
+ currentTime: z.number(),
29
+ scene: mediaStreamSceneSchema,
30
+ });
31
+ export const roomSummarySchema = z.object({
32
+ roomId: z.string(),
33
+ name: z.string().nullable(),
34
+ track: roomTrackSchema.nullable(),
35
+ paused: z.boolean(),
36
+ });
@@ -43,7 +43,7 @@ export declare const sceneCarSchema: z.ZodObject<{
43
43
  doors: z.ZodArray<z.ZodBoolean, "many">;
44
44
  /** Open (lowered) windows, one per door */
45
45
  windows: z.ZodArray<z.ZodBoolean, "many">;
46
- /** The radio plays the stream */
46
+ /** The radio plays the room */
47
47
  radio: z.ZodBoolean;
48
48
  }, "strip", z.ZodTypeAny, {
49
49
  id: string;
@@ -62,7 +62,7 @@ export declare const sceneCarSchema: z.ZodObject<{
62
62
  windows: boolean[];
63
63
  radio: boolean;
64
64
  }>;
65
- /** Where the stream is heard, shared by every listener of the stream */
65
+ /** Where a room is heard, shared by every listener of the room */
66
66
  export declare const mediaStreamSceneSchema: z.ZodObject<{
67
67
  speakers: z.ZodArray<z.ZodObject<{
68
68
  id: z.ZodString;
@@ -102,7 +102,7 @@ export declare const mediaStreamSceneSchema: z.ZodObject<{
102
102
  doors: z.ZodArray<z.ZodBoolean, "many">;
103
103
  /** Open (lowered) windows, one per door */
104
104
  windows: z.ZodArray<z.ZodBoolean, "many">;
105
- /** The radio plays the stream */
105
+ /** The radio plays the room */
106
106
  radio: z.ZodBoolean;
107
107
  }, "strip", z.ZodTypeAny, {
108
108
  id: string;
@@ -220,7 +220,7 @@ export declare const sceneActionSchema: z.ZodDiscriminatedUnion<"type", [z.ZodOb
220
220
  doors: z.ZodArray<z.ZodBoolean, "many">;
221
221
  /** Open (lowered) windows, one per door */
222
222
  windows: z.ZodArray<z.ZodBoolean, "many">;
223
- /** The radio plays the stream */
223
+ /** The radio plays the room */
224
224
  radio: z.ZodBoolean;
225
225
  }, "x" | "z" | "yaw">, "strip", z.ZodTypeAny, {
226
226
  x: number;
@@ -30,10 +30,10 @@ export const sceneCarSchema = z.object({
30
30
  doors: z.array(z.boolean()).length(CAR_DOORS),
31
31
  /** Open (lowered) windows, one per door */
32
32
  windows: z.array(z.boolean()).length(CAR_DOORS),
33
- /** The radio plays the stream */
33
+ /** The radio plays the room */
34
34
  radio: z.boolean(),
35
35
  });
36
- /** Where the stream is heard, shared by every listener of the stream */
36
+ /** Where a room is heard, shared by every listener of the room */
37
37
  export const mediaStreamSceneSchema = z.object({
38
38
  speakers: z.array(sceneSpeakerSchema),
39
39
  cars: z.array(sceneCarSchema),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@darco2903/altv-web-api",
3
- "version": "1.6.0",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "keywords": [],
@@ -1,21 +0,0 @@
1
- import type { MediaStreamScene } from "../types/scene.js";
2
- export interface SocketClientToServerMediaStream {
3
- mediaStreamListen: (streamId: string, callback: (success: boolean) => void) => void;
4
- mediaStreamUnlisten: (streamId: string, callback: (success: boolean) => void) => void;
5
- }
6
- /**
7
- * - `ended`: the media played to the end, clients still have the last seconds buffered
8
- * - `idle`: nobody listened for a while
9
- * - `error`: the media couldn't be read
10
- * - `shutdown`: the server is stopping
11
- */
12
- export type MediaStreamEndReason = "ended" | "idle" | "error" | "shutdown";
13
- export interface SocketServerToClientMediaStream {
14
- mediaStreamPlay: (streamId: string, startTime: number) => void;
15
- mediaStreamPause: (streamId: string) => void;
16
- mediaStreamSeek: (streamId: string, startTime: number) => void;
17
- /** The stream is gone, every request about it now returns 404 */
18
- mediaStreamEnd: (streamId: string, reason: MediaStreamEndReason) => void;
19
- /** The scene changed, the whole new scene */
20
- mediaStreamScene: (streamId: string, scene: MediaStreamScene) => void;
21
- }
File without changes