fastpix-python 0.1.0__py3-none-any.whl

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.
fastpix/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ from fastpix.client.client import Client
2
+ from fastpix.async_client.client import Client as AsyncClient
3
+
4
+ __all__ = ['Client', 'AsyncClient']
@@ -0,0 +1,3 @@
1
+ from fastpix.async_client.client import Client as AsyncClient
2
+
3
+ __all__ = ['AsyncClient']
@@ -0,0 +1,33 @@
1
+ import aiohttp
2
+ from fastpix.utilis.exceptions import APIError
3
+ from fastpix.utilis.constants import BASE_URL
4
+
5
+
6
+ async def make_request(method, endpoint, headers=None, data=None, params=None):
7
+ url = f"{BASE_URL}{endpoint}"
8
+ headers = headers or {}
9
+
10
+ try:
11
+ async with aiohttp.ClientSession() as session:
12
+ async with session.request(
13
+ method=method,
14
+ url=url,
15
+ headers=headers,
16
+ json=data,
17
+ params=params,
18
+ ) as response:
19
+ try:
20
+ response_data = await response.json()
21
+ except aiohttp.ContentTypeError:
22
+ response_data = await response.text()
23
+
24
+ if response.ok:
25
+ return response_data
26
+ else:
27
+ raise APIError(
28
+ message=f"Request failed with status {response.status}",
29
+ status_code=response.status,
30
+ details=response_data
31
+ )
32
+ except aiohttp.ClientError as e:
33
+ raise APIError(f"Request failed: {str(e)}")
@@ -0,0 +1,52 @@
1
+ import base64
2
+ from fastpix.utilis.exceptions import APIError
3
+ from fastpix.async_client.media import MediaResource
4
+ from fastpix.async_client.playback_ids import PlaybackIDs
5
+ from fastpix.async_client.live_streams import Livestream
6
+
7
+
8
+ class Client:
9
+ def __init__(self, username=None, password=None, api_key=None):
10
+ """
11
+ Initialize the client with flexible authentication.
12
+
13
+ Args:
14
+ username (str, optional): Username for authentication
15
+ password (str, optional): Password for authentication
16
+ api_key (str, optional): Pre-generated base64 encoded API key
17
+ """
18
+
19
+ # Validate and prepare API key
20
+ if api_key:
21
+ self.api_key = api_key
22
+ elif username and password:
23
+ # Encode credentials to base64
24
+ credentials = f"{username}:{password}"
25
+ self.api_key = base64.b64encode(credentials.encode('utf-8')).decode('utf-8')
26
+ else:
27
+ raise ValueError("Must provide either username and password or a pre-generated API key")
28
+
29
+ # Prepare headers
30
+ self.headers = {
31
+ "accept": "application/json",
32
+ "content-type": "application/json",
33
+ "Authorization": f"Basic {self.api_key}",
34
+ }
35
+
36
+ # Initialize resources
37
+ self.media = MediaResource(self)
38
+ self.playback_ids = PlaybackIDs(self)
39
+ self.livestreams = Livestream(self)
40
+
41
+ async def _validate_credentials(self):
42
+ """
43
+ Validate credentials by attempting to fetch media.
44
+ Raises an APIError if authentication fails.
45
+ """
46
+ try:
47
+ # Attempt to fetch media to verify credentials (async)
48
+ response = await self.media.get_all()
49
+ return response
50
+
51
+ except APIError as e:
52
+ raise APIError(f"Authentication failed: {str(e)}") from e
@@ -0,0 +1,147 @@
1
+ from fastpix.utilis.exceptions import APIError
2
+ from fastpix.async_client.api import make_request
3
+ from fastpix.utilis.utilis import validate_uuid, validate_request_body
4
+
5
+
6
+ class Livestream:
7
+ def __init__(self, client):
8
+ self.client = client
9
+
10
+ async def create(self, data):
11
+ """Create a live stream."""
12
+
13
+ validate_request_body(data) # validating request body is valid json or not
14
+
15
+ try:
16
+ return await make_request(
17
+ method="POST",
18
+ endpoint="/live/streams",
19
+ headers=self.client.headers,
20
+ data=data
21
+ )
22
+ except APIError as e:
23
+ return e.to_dict()
24
+
25
+ async def list(self, params):
26
+ """Retrieve all live streams."""
27
+ try:
28
+ return await make_request(
29
+ method="GET",
30
+ endpoint="/live/streams",
31
+ headers=self.client.headers,
32
+ params=params
33
+ )
34
+ except APIError as e:
35
+ return e.to_dict()
36
+
37
+ async def get(self, stream_id):
38
+ """Retrieve a specific live stream by ID."""
39
+
40
+ validate_uuid(stream_id) # validating stream_id
41
+
42
+ try:
43
+ return await make_request(
44
+ method="GET",
45
+ endpoint=f"/live/streams/{stream_id}",
46
+ headers=self.client.headers
47
+ )
48
+ except APIError as e:
49
+ return e.to_dict()
50
+
51
+ async def update(self, stream_id, data):
52
+ """Update a specific live stream."""
53
+ if not stream_id:
54
+ raise ValueError("Stream ID must be provided.")
55
+
56
+ try:
57
+ return await make_request(
58
+ method="PATCH",
59
+ endpoint=f"/live/streams/{stream_id}",
60
+ headers=self.client.headers,
61
+ data=data
62
+ )
63
+ except APIError as e:
64
+ return e.to_dict()
65
+
66
+ async def delete(self, stream_id):
67
+ """Delete a specific live stream."""
68
+
69
+ validate_uuid(stream_id) # validating stream_id
70
+
71
+ try:
72
+ return await make_request(
73
+ method="DELETE",
74
+ endpoint=f"/live/streams/{stream_id}",
75
+ headers=self.client.headers
76
+ )
77
+ except APIError as e:
78
+ return e.to_dict()
79
+
80
+ async def create_simulcast(self, stream_id, data):
81
+ """Create a simulcast for a live stream."""
82
+
83
+ validate_uuid(stream_id) # validating stream_id
84
+ validate_request_body(data) # validating request body as valid json body or not
85
+
86
+ try:
87
+ return await make_request(
88
+ method="POST",
89
+ endpoint=f"/live/streams/{stream_id}/simulcast",
90
+ headers=self.client.headers,
91
+ data=data
92
+ )
93
+ except APIError as e:
94
+ return e.to_dict()
95
+
96
+ async def get_simulcast(self, stream_id, simulcast_id):
97
+ """Retrieve a specific simulcast."""
98
+
99
+ if simulcast_id is None:
100
+ raise ValueError("Simulcast ID must be provided.")
101
+
102
+ validate_uuid(stream_id) # validating stream_id
103
+
104
+ try:
105
+ return await make_request(
106
+ method="GET",
107
+ endpoint=f"/live/streams/{stream_id}/simulcast/{simulcast_id}",
108
+ headers=self.client.headers
109
+ )
110
+ except APIError as e:
111
+ return e.to_dict()
112
+
113
+ async def update_simulcast(self, stream_id, simulcast_id, data):
114
+ """Update a specific simulcast."""
115
+
116
+ if simulcast_id is None:
117
+ raise ValueError("Simulcast ID must be provided.")
118
+
119
+ validate_uuid(stream_id) # validating stream_id
120
+ validate_request_body(data) # validating request body as valid json body or not
121
+
122
+ try:
123
+ return await make_request(
124
+ method="PUT",
125
+ endpoint=f"/live/streams/{stream_id}/simulcast/{simulcast_id}",
126
+ headers=self.client.headers,
127
+ data=data
128
+ )
129
+ except APIError as e:
130
+ return e.to_dict()
131
+
132
+ async def delete_simulcast(self, stream_id, simulcast_id):
133
+ """Delete a specific simulcast."""
134
+
135
+ if simulcast_id is None:
136
+ raise ValueError("Simulcast ID must be provided.")
137
+
138
+ validate_uuid(stream_id) # validating stream_id
139
+
140
+ try:
141
+ return await make_request(
142
+ method="DELETE",
143
+ endpoint=f"/live/streams/{stream_id}/simulcast/{simulcast_id}",
144
+ headers=self.client.headers
145
+ )
146
+ except APIError as e:
147
+ return e.to_dict()
@@ -0,0 +1,121 @@
1
+ from fastpix.utilis.exceptions import APIError
2
+ from fastpix.async_client.api import make_request
3
+ from fastpix.utilis.utilis import validate_uuid, validate_request_body
4
+
5
+
6
+ class MediaResource:
7
+ def __init__(self, client):
8
+ self.client = client
9
+
10
+ async def get_all_media(self, params):
11
+ """Fetch all medias."""
12
+ try:
13
+ return await make_request(
14
+ method="GET",
15
+ endpoint="/on-demand",
16
+ headers=self.client.headers,
17
+ params=params
18
+ )
19
+ except APIError as e:
20
+ return e.to_dict()
21
+
22
+ async def get_by_mediaId(self, media_id):
23
+ """Fetch media by its ID."""
24
+
25
+ validate_uuid(media_id) # validating media id
26
+
27
+ try:
28
+ return await make_request(
29
+ method="GET",
30
+ endpoint=f"/on-demand/{media_id}",
31
+ headers=self.client.headers
32
+ )
33
+ except APIError as e:
34
+ return e.to_dict()
35
+
36
+ async def update(self, media_id, data):
37
+ """Update media by its ID."""
38
+
39
+ validate_uuid(media_id) # validating media id
40
+ validate_request_body(data) # validating request body is valid json or not
41
+
42
+ try:
43
+ return await make_request(
44
+ method="PATCH",
45
+ endpoint=f"/on-demand/{media_id}",
46
+ headers=self.client.headers,
47
+ data=data
48
+ )
49
+ except APIError as e:
50
+ return e.to_dict()
51
+
52
+ async def delete(self, media_id):
53
+ """Delete media by its ID."""
54
+
55
+ validate_uuid(media_id) # validating media id
56
+
57
+ try:
58
+ return await make_request(
59
+ method="DELETE",
60
+ endpoint=f"/on-demand/{media_id}",
61
+ headers=self.client.headers
62
+ )
63
+ except APIError as e:
64
+ return e.to_dict()
65
+
66
+ async def create_pull_video(self, data):
67
+ """Create an on-demand request by calling the /on-demand endpoint."""
68
+ endpoint = "/on-demand"
69
+
70
+ validate_request_body(data) # validating body
71
+
72
+ if "accessPolicy" not in data:
73
+ data["accessPolicy"] = "public"
74
+
75
+ try:
76
+ response = await make_request(
77
+ method="POST",
78
+ endpoint=endpoint,
79
+ headers=self.client.headers,
80
+ data=data
81
+ )
82
+ return response
83
+ except APIError as e:
84
+ return e.to_dict()
85
+
86
+ async def get_presigned_url(self, data):
87
+ """Create an on-demand presigned URL request by calling the /on-demand/uploads endpoint."""
88
+ endpoint = "/on-demand/uploads"
89
+
90
+ validate_request_body(data) # validating body
91
+
92
+ if "corsOrigin" not in data:
93
+ data["corsOrigin"] = "*"
94
+
95
+ try:
96
+ response = await make_request(
97
+ method="POST",
98
+ endpoint=endpoint,
99
+ headers=self.client.headers,
100
+ data=data
101
+ )
102
+ return response
103
+ except APIError as e:
104
+ return e.to_dict()
105
+
106
+ async def get_media_info(self, media_id):
107
+ """Retrieve media input info by media ID."""
108
+ endpoint = f"/on-demand/{media_id}/input-info"
109
+
110
+ validate_uuid(media_id) # validating media id
111
+
112
+ try:
113
+ response = await make_request(
114
+ method="GET",
115
+ endpoint=endpoint,
116
+ headers=self.client.headers
117
+ )
118
+ return response
119
+ except APIError as e:
120
+ return e.to_dict()
121
+
@@ -0,0 +1,79 @@
1
+ from fastpix.utilis.exceptions import APIError
2
+ from fastpix.async_client.api import make_request
3
+ from fastpix.utilis.utilis import validate_uuid, validate_request_body
4
+
5
+
6
+ class PlaybackIDs:
7
+ def __init__(self, client):
8
+ self.client = client
9
+
10
+ def _get_endpoint(self, media_type, media_id):
11
+ """Helper method to get the correct endpoint based on media type."""
12
+ if media_type == "livestream":
13
+ return f"/live/streams/{media_id}/playback-ids"
14
+ elif media_type == "video_on_demand":
15
+ return f"/on-demand/{media_id}/playback-ids"
16
+ else:
17
+ raise ValueError("Invalid media type. Must be 'livestream' or 'video_on_demand'.")
18
+
19
+ async def create(self, media_type, media_id, data):
20
+ """Create a playback ID for a media resource (livestream or video on demand)."""
21
+
22
+ # Validate media_id
23
+ validate_uuid(media_id) # Ensure valid UUID format
24
+
25
+ # Validate request body (data)
26
+ validate_request_body(data) # Ensure the request body is valid JSON
27
+
28
+ try:
29
+ # Get the correct endpoint based on media type
30
+ endpoint = self._get_endpoint(media_type, media_id)
31
+
32
+ return await make_request(
33
+ method="POST",
34
+ endpoint=endpoint,
35
+ headers=self.client.headers,
36
+ data=data
37
+ )
38
+ except APIError as e:
39
+ return e.to_dict()
40
+
41
+ async def delete(self, media_type, media_id, playback_ids):
42
+ """Delete a specific playback ID for a media resource (livestream or video on demand)."""
43
+
44
+ # Validate media_id
45
+ validate_uuid(media_id) # Ensure valid UUID format
46
+
47
+ try:
48
+ # Get the correct endpoint based on media type
49
+ endpoint = self._get_endpoint(media_type, media_id)
50
+
51
+ return await make_request(
52
+ method="DELETE",
53
+ endpoint=endpoint,
54
+ headers=self.client.headers,
55
+ params={'playbackId': playback_ids}
56
+ )
57
+ except APIError as e:
58
+ return e.to_dict()
59
+
60
+ async def get(self, media_type, media_id, playback_id):
61
+ """Retrieve a specific playback ID for a media resource (livestream or video on demand)."""
62
+
63
+ # Validate media_id
64
+ validate_uuid(media_id) # Ensure valid UUID format
65
+
66
+ if media_type == "video_on_demand":
67
+ return {"error": "The 'get' method is not available for video_on_demand."}
68
+
69
+ try:
70
+ # Get the correct endpoint based on media type
71
+ endpoint = self._get_endpoint(media_type, media_id) + f"/{playback_id}"
72
+
73
+ return await make_request(
74
+ method="GET",
75
+ endpoint=endpoint,
76
+ headers=self.client.headers
77
+ )
78
+ except APIError as e:
79
+ return e.to_dict()
@@ -0,0 +1,3 @@
1
+ from fastpix.client.client import Client
2
+
3
+ __all__ = ['Client']
fastpix/client/api.py ADDED
@@ -0,0 +1,28 @@
1
+ import requests
2
+ from fastpix.utilis.exceptions import APIError
3
+ from fastpix.utilis.constants import BASE_URL
4
+
5
+
6
+ def make_request(method, endpoint, headers=None, data=None, params=None):
7
+ url = f"{BASE_URL}{endpoint}"
8
+ headers = headers or {}
9
+
10
+ try:
11
+ response = requests.request(
12
+ method=method,
13
+ url=url,
14
+ headers=headers,
15
+ json=data,
16
+ params=params
17
+ )
18
+
19
+ if response.ok:
20
+ return response.json()
21
+ else:
22
+ raise APIError(
23
+ message=f"Request failed with status {response.status_code}",
24
+ status_code=response.status_code,
25
+ details=response.json() if response.headers.get("content-type") == "application/json" else response.text
26
+ )
27
+ except requests.RequestException as e:
28
+ raise APIError(f"Request failed: {str(e)}")
@@ -0,0 +1,57 @@
1
+ import base64
2
+ from fastpix.utilis.exceptions import APIError
3
+ from fastpix.client.media import MediaResource
4
+ from fastpix.client.playback_ids import PlaybackIDs
5
+ from fastpix.client.live_streams import Livestream
6
+
7
+
8
+ class Client:
9
+ def __init__(self, username=None, password=None, api_key=None):
10
+ """
11
+ Initialize the client with flexible authentication.
12
+
13
+ Args:
14
+ username (str, optional): Username for authentication
15
+ password (str, optional): Password for authentication
16
+ api_key (str, optional): Pre-generated base64 encoded API key
17
+ """
18
+
19
+ # Validate and prepare API key
20
+ if api_key:
21
+ self.api_key = api_key
22
+ elif username and password:
23
+
24
+ # Encode credentials to base64
25
+ credentials = f"{username}:{password}"
26
+ self.api_key = base64.b64encode(credentials.encode('utf-8')).decode('utf-8')
27
+ else:
28
+ raise ValueError("Must provide either username and password or a pre-generated API key")
29
+
30
+ # Prepare headers
31
+ self.headers = {
32
+ "accept": "application/json",
33
+ "content-type": "application/json",
34
+ "Authorization": f"Basic {self.api_key}",
35
+ }
36
+
37
+ # Initialize resources
38
+ self.media = MediaResource(self)
39
+ self.playback_ids = PlaybackIDs(self)
40
+ self.livestreams = Livestream(self)
41
+
42
+ # Validate credentials
43
+ self._validate_credentials()
44
+
45
+ def _validate_credentials(self):
46
+ """
47
+ Validate credentials by attempting to fetch media.
48
+ Raises an APIError if authentication fails.
49
+ """
50
+ try:
51
+ # Attempt to fetch media to verify credentials
52
+ response = self.media.get_all_media()
53
+ return response
54
+
55
+ except APIError as e:
56
+ raise APIError(f"Authentication failed: {str(e)}") from e
57
+
@@ -0,0 +1,150 @@
1
+ from fastpix.utilis.exceptions import APIError
2
+ from fastpix.client.api import make_request
3
+ from fastpix.utilis.utilis import validate_uuid, validate_request_body
4
+
5
+
6
+ class Livestream:
7
+ def __init__(self, client):
8
+ self.client = client
9
+
10
+ def create(self, data):
11
+ """Create a live stream."""
12
+
13
+ validate_request_body(data) # validating request body is valid json or not
14
+
15
+ try:
16
+ return make_request(
17
+ method="POST",
18
+ endpoint="/live/streams",
19
+ headers=self.client.headers,
20
+ data=data
21
+ )
22
+ except APIError as e:
23
+ return e.to_dict()
24
+
25
+ def list(self, params=None):
26
+ """Retrieve all live streams."""
27
+
28
+ try:
29
+ return make_request(
30
+ method="GET",
31
+ endpoint="/live/streams",
32
+ headers=self.client.headers,
33
+ params=params
34
+ )
35
+ except APIError as e:
36
+ return e.to_dict()
37
+
38
+ def get(self, stream_id):
39
+ """Retrieve a specific live stream by ID."""
40
+
41
+ validate_uuid(stream_id) # validating stream_id
42
+
43
+ try:
44
+ return make_request(
45
+ method="GET",
46
+ endpoint=f"/live/streams/{stream_id}",
47
+ headers=self.client.headers
48
+ )
49
+ except APIError as e:
50
+ return e.to_dict()
51
+
52
+ def update(self, stream_id, data):
53
+ """Update a specific live stream."""
54
+
55
+ validate_uuid(stream_id) # validating stream_id
56
+ validate_request_body(data) # validating request body is valid json or not
57
+
58
+ try:
59
+ return make_request(
60
+ method="PATCH",
61
+ endpoint=f"/live/streams/{stream_id}",
62
+ headers=self.client.headers,
63
+ data=data
64
+ )
65
+ except APIError as e:
66
+ return e.to_dict()
67
+
68
+ def delete(self, stream_id):
69
+ """Delete a specific live stream."""
70
+
71
+ validate_uuid(stream_id) # validating stream_id
72
+
73
+ try:
74
+ return make_request(
75
+ method="DELETE",
76
+ endpoint=f"/live/streams/{stream_id}",
77
+ headers=self.client.headers
78
+ )
79
+ except APIError as e:
80
+ return e.to_dict()
81
+
82
+ def create_simulcast(self, stream_id, data):
83
+ """Create a simulcast for a live stream."""
84
+
85
+ validate_uuid(stream_id) # validating stream_id
86
+ validate_request_body(data) # validating request body as valid json body or not
87
+
88
+ try:
89
+ return make_request(
90
+ method="POST",
91
+ endpoint=f"/live/streams/{stream_id}/simulcast",
92
+ headers=self.client.headers,
93
+ data=data
94
+ )
95
+ except APIError as e:
96
+ return e.to_dict()
97
+
98
+ def get_simulcast(self, stream_id, simulcast_id):
99
+ """Retrieve a specific simulcast."""
100
+
101
+ if simulcast_id is None:
102
+ raise ValueError("Simulcast ID must be provided.")
103
+
104
+ validate_uuid(stream_id) # validating stream_id
105
+
106
+ try:
107
+ return make_request(
108
+ method="GET",
109
+ endpoint=f"/live/streams/{stream_id}/simulcast/{simulcast_id}",
110
+ headers=self.client.headers
111
+ )
112
+ except APIError as e:
113
+ return e.to_dict()
114
+
115
+ def update_simulcast(self, stream_id, simulcast_id, data):
116
+ """Update a specific simulcast."""
117
+
118
+ if simulcast_id is None:
119
+ raise ValueError("Simulcast ID must be provided.")
120
+
121
+ validate_uuid(stream_id) # validating stream_id
122
+ validate_request_body(data) # validating request body as valid json body or not
123
+
124
+ try:
125
+ return make_request(
126
+ method="PUT",
127
+ endpoint=f"/live/streams/{stream_id}/simulcast/{simulcast_id}",
128
+ headers=self.client.headers,
129
+ data=data
130
+ )
131
+ except APIError as e:
132
+ return e.to_dict()
133
+
134
+ def delete_simulcast(self, stream_id, simulcast_id):
135
+ """Delete a specific simulcast."""
136
+
137
+ if simulcast_id is None:
138
+ raise ValueError("Simulcast ID must be provided.")
139
+
140
+ validate_uuid(stream_id) # validating stream_id
141
+
142
+ try:
143
+ return make_request(
144
+ method="DELETE",
145
+ endpoint=f"/live/streams/{stream_id}/simulcast/{simulcast_id}",
146
+ headers=self.client.headers
147
+ )
148
+ except APIError as e:
149
+ return e.to_dict()
150
+
@@ -0,0 +1,110 @@
1
+ from fastpix.utilis.exceptions import APIError
2
+ from fastpix.client.api import make_request
3
+ from fastpix.utilis.utilis import validate_uuid, validate_request_body
4
+
5
+
6
+ class MediaResource:
7
+ def __init__(self, client):
8
+ self.client = client
9
+
10
+ def get_all_media(self, params):
11
+ """Fetch all medias."""
12
+ try:
13
+ return make_request(
14
+ method="GET",
15
+ endpoint="/on-demand",
16
+ headers=self.client.headers,
17
+ params=params
18
+ )
19
+ except APIError as e:
20
+ return e.to_dict()
21
+
22
+ def get_by_mediaId(self, media_id):
23
+ """Fetch media by its ID."""
24
+ validate_uuid(media_id) # Validate media ID
25
+ try:
26
+ return make_request(
27
+ method="GET",
28
+ endpoint=f"/on-demand/{media_id}",
29
+ headers=self.client.headers
30
+ )
31
+ except APIError as e:
32
+ return e.to_dict()
33
+
34
+ def update(self, media_id, data):
35
+ """Update media by its ID."""
36
+ validate_uuid(media_id) # Validate media ID
37
+ validate_request_body(data) # Validate request body
38
+
39
+ try:
40
+ return make_request(
41
+ method="PATCH",
42
+ endpoint=f"/on-demand/{media_id}",
43
+ headers=self.client.headers,
44
+ data=data
45
+ )
46
+ except APIError as e:
47
+ return e.to_dict()
48
+
49
+ def delete(self, media_id):
50
+ """Delete media by its ID."""
51
+ validate_uuid(media_id) # Validate media ID
52
+
53
+ try:
54
+ return make_request(
55
+ method="DELETE",
56
+ endpoint=f"/on-demand/{media_id}",
57
+ headers=self.client.headers
58
+ )
59
+ except APIError as e:
60
+ return e.to_dict()
61
+
62
+ def create_pull_video(self, data):
63
+ """Create an on-demand request by calling the /on-demand endpoint."""
64
+ endpoint = "/on-demand"
65
+ validate_request_body(data) # Validate body
66
+
67
+ if "accessPolicy" not in data:
68
+ data["accessPolicy"] = "public"
69
+
70
+ try:
71
+ return make_request(
72
+ method="POST",
73
+ endpoint=endpoint,
74
+ headers=self.client.headers,
75
+ data=data
76
+ )
77
+ except APIError as e:
78
+ return e.to_dict()
79
+
80
+ def get_presigned_url(self, data):
81
+ """Create an on-demand presigned URL request by calling the /on-demand/uploads endpoint."""
82
+ endpoint = "/on-demand/uploads"
83
+ validate_request_body(data) # Validate body
84
+
85
+ if "corsOrigin" not in data:
86
+ data["corsOrigin"] = "*"
87
+
88
+ try:
89
+ return make_request(
90
+ method="POST",
91
+ endpoint=endpoint,
92
+ headers=self.client.headers,
93
+ data=data
94
+ )
95
+ except APIError as e:
96
+ return e.to_dict()
97
+
98
+ def get_media_info(self, media_id):
99
+ """Retrieve media input info by media ID."""
100
+ validate_uuid(media_id) # Validate media ID
101
+
102
+ try:
103
+ endpoint = f"/on-demand/{media_id}/input-info"
104
+ return make_request(
105
+ method="GET",
106
+ endpoint=endpoint,
107
+ headers=self.client.headers
108
+ )
109
+ except APIError as e:
110
+ return e.to_dict()
@@ -0,0 +1,80 @@
1
+ from fastpix.utilis.exceptions import APIError
2
+ from fastpix.client.api import make_request
3
+ from fastpix.utilis.utilis import validate_uuid, validate_request_body
4
+
5
+
6
+ class PlaybackIDs:
7
+ def __init__(self, client):
8
+ self.client = client
9
+
10
+ def _get_endpoint(self, media_type, media_id):
11
+ """Helper method to get the correct endpoint based on media type."""
12
+ if media_type == "livestream":
13
+ return f"/live/streams/{media_id}/playback-ids"
14
+ elif media_type == "video_on_demand":
15
+ return f"/on-demand/{media_id}/playback-ids"
16
+ else:
17
+ raise ValueError("Invalid media type. Must be 'livestream' or 'video_on_demand'.")
18
+
19
+ def create(self, media_type, media_id, data):
20
+ """Create a playback ID for a media resource (livestream or video on demand)."""
21
+
22
+ # Validate media_id
23
+ validate_uuid(media_id) # Ensure valid UUID format
24
+
25
+ # Validate request body (data)
26
+ validate_request_body(data) # Ensure the request body is valid JSON
27
+
28
+ try:
29
+ # Get the correct endpoint based on media type
30
+ endpoint = self._get_endpoint(media_type, media_id)
31
+
32
+ return make_request(
33
+ method="POST",
34
+ endpoint=endpoint,
35
+ headers=self.client.headers,
36
+ data=data
37
+ )
38
+ except APIError as e:
39
+ return e.to_dict()
40
+
41
+ def delete(self, media_type, media_id, playback_ids):
42
+ """Delete a specific playback ID for a media resource (livestream or video on demand)."""
43
+
44
+ # Validate media_id
45
+ validate_uuid(media_id) # Ensure valid UUID format
46
+
47
+ try:
48
+ # Get the correct endpoint based on media type
49
+ endpoint = self._get_endpoint(media_type, media_id)
50
+
51
+ return make_request(
52
+ method="DELETE",
53
+ endpoint=endpoint,
54
+ headers=self.client.headers,
55
+ params={'playbackId': playback_ids}
56
+ )
57
+ except APIError as e:
58
+ return e.to_dict()
59
+
60
+ def get(self, media_type, media_id, playback_id):
61
+ """Retrieve a specific playback ID for a media resource (livestream or video on demand)."""
62
+
63
+ # Validate media_id
64
+ validate_uuid(media_id) # Ensure valid UUID format
65
+
66
+ if media_type == "video_on_demand":
67
+ return {"error": "The 'get' method is not available for video_on_demand."}
68
+
69
+ try:
70
+ # Get the correct endpoint based on media type
71
+ endpoint = self._get_endpoint(media_type, media_id) + f"/{playback_id}"
72
+
73
+ return make_request(
74
+ method="GET",
75
+ endpoint=endpoint,
76
+ headers=self.client.headers
77
+ )
78
+ except APIError as e:
79
+ return e.to_dict()
80
+
@@ -0,0 +1,4 @@
1
+ from .constants import BASE_URL, validate_uuid, validate_request_body
2
+ from .exceptions import APIError
3
+
4
+ __all__ = ['constants', 'exceptions']
@@ -0,0 +1 @@
1
+ BASE_URL = "https://v1.fastpix.io"
@@ -0,0 +1,14 @@
1
+ class APIError(Exception):
2
+ """Custom exception for API-related errors."""
3
+ def __init__(self, message, status_code=None, details=None):
4
+ super().__init__(message)
5
+ self.message = message
6
+ self.status_code = status_code
7
+ self.details = details
8
+
9
+ def to_dict(self):
10
+ return {
11
+ "error": self.message,
12
+ "status_code": self.status_code,
13
+ "details": self.details,
14
+ }
@@ -0,0 +1,17 @@
1
+ import uuid
2
+ import json
3
+
4
+
5
+ def validate_uuid(id_str):
6
+ try:
7
+ uuid.UUID(str(id_str))
8
+ except ValueError:
9
+ raise ValueError(f"Invalid UUID format: {id_str}")
10
+
11
+
12
+ def validate_request_body(data):
13
+ try:
14
+ json.dumps(data)
15
+ except (TypeError, ValueError):
16
+ raise ValueError("Invalid JSON data in request body")
17
+
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastpix-python
3
+ Version: 0.1.0
4
+ Summary: FastPix SDK with both sync and async support
5
+ Home-page: https://github.com/fastpix-io/fastpix-python-server-sdk
6
+ Author: FastPix
7
+ Author-email: dev@fastpix.io
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.7
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Requires-Python: >=3.7
16
+ License-File: LICENSE
17
+ Requires-Dist: requests>=2.25.0
18
+ Provides-Extra: async
19
+ Requires-Dist: aiohttp>=3.8.0; extra == "async"
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: home-page
24
+ Dynamic: license-file
25
+ Dynamic: provides-extra
26
+ Dynamic: requires-dist
27
+ Dynamic: requires-python
28
+ Dynamic: summary
@@ -0,0 +1,22 @@
1
+ fastpix/__init__.py,sha256=loB8C7K059okqu9xAO32Jr9_wBF8kzZXIVjWWp_v9x8,140
2
+ fastpix/async_client/__init__.py,sha256=0brJ1RCLOd7cNl6cjtr96AqftUd66Owp2W1fkfo4Rbo,89
3
+ fastpix/async_client/api.py,sha256=jmALp5zny5FaqUm7RDmVXF9T0iFnNyCKtJpEQ0cxtiA,1144
4
+ fastpix/async_client/client.py,sha256=UggrQiehFSNBvF3dH0evq0DacVsvEooxRaAx-Wpr88g,1878
5
+ fastpix/async_client/live_streams.py,sha256=sx9_xtiR1FD6MGg_F3aq8NwjXHOwuVD1eBvp4NjGBvk,4765
6
+ fastpix/async_client/media.py,sha256=l4CMp61eEvX0v67jrcurZ2CAysvJQZbr4VRQDNOowtc,3704
7
+ fastpix/async_client/playback_ids.py,sha256=1pKQ9VNuOUzADZQiAlJPftRkdB3gS97srwz7QUB4WJY,2924
8
+ fastpix/client/__init__.py,sha256=3C-Vhg1RULZzdjI0kCnfNj_LzwOHSHO-di--gSA-gTQ,63
9
+ fastpix/client/api.py,sha256=WUN0Z7crvw7V3E5_0XzrTXfmLb4mFoDogj1ADoru4sk,890
10
+ fastpix/client/client.py,sha256=GiFPu3jq0cXrUw1iqmwvJRIXaZKC_HtgMlklsYXNUyY,1928
11
+ fastpix/client/live_streams.py,sha256=p_3r4PzipnTEYmd6KlZUi4kHSwiOkOvvJxT2QAVEav4,4706
12
+ fastpix/client/media.py,sha256=-VdOb5fm18G-f11DGnZD7dTjaBY9BeI2c1jebrUh8tE,3369
13
+ fastpix/client/playback_ids.py,sha256=2duK4WAjOIhL-nOBKHt1PpFbPSclUsMh5Upta1lWIcQ,2890
14
+ fastpix/utilis/__init__.py,sha256=-9bDUz5FeRXp-ZyeMJEap0q5EXvhIr-ljadB24_5yUo,142
15
+ fastpix/utilis/constants.py,sha256=s4jcq1qk7N5-t4wcZE4aIutzsIoHx8pattbg4NBIAnw,35
16
+ fastpix/utilis/exceptions.py,sha256=fctNkUrSg2VkukwVdTGa1j8v2VMcUM4rPna76VHIe3o,454
17
+ fastpix/utilis/utilis.py,sha256=3JIwmN-fuDc8JQ1jpzPLom-vQI3aXEuch0V9LactpxU,343
18
+ fastpix_python-0.1.0.dist-info/licenses/LICENSE,sha256=F9rynAolz0RQr0fiAQO0TSptdJq_Q-KPx0sguGiLdAI,1063
19
+ fastpix_python-0.1.0.dist-info/METADATA,sha256=_2990kEoq7jVBiEkt2fp0QqmP3SiqxA-XWMi8k9_dQY,896
20
+ fastpix_python-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
+ fastpix_python-0.1.0.dist-info/top_level.txt,sha256=SADFSkxaYaU-AHZY56jucIDG-iqlTMU3y3EL2FksnDM,8
22
+ fastpix_python-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 FastPix
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ fastpix