marqetive-lib 0.1.3__py3-none-any.whl → 0.1.4__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.
marqetive/__init__.py CHANGED
@@ -4,49 +4,54 @@ A comprehensive library providing utilities for working with web APIs,
4
4
  HTTP requests, data processing, and social media platform integrations.
5
5
 
6
6
  Usage:
7
- Basic API client:
8
- >>> from marqetive import APIClient
9
- >>> async with APIClient(base_url="https://api.example.com") as client:
10
- ... response = await client.get("/endpoint")
7
+ Simple usage with factory:
8
+ >>> from marqetive import get_client, AuthCredentials, PostCreateRequest
9
+ >>> credentials = AuthCredentials(
10
+ ... platform="twitter",
11
+ ... access_token="...",
12
+ ... refresh_token="..."
13
+ ... )
14
+ >>> client = await get_client(credentials)
15
+ >>> async with client:
16
+ ... post = await client.create_post(PostCreateRequest(content="Hello!"))
11
17
 
12
- Platform managers (high-level):
13
- >>> from marqetive import initialize_platform_registry, get_manager_for_platform
14
- >>> from marqetive.platforms.models import AuthCredentials, PostCreateRequest
15
- >>>
16
- >>> initialize_platform_registry()
17
- >>> manager = get_manager_for_platform("twitter")
18
- >>> credentials = AuthCredentials(platform="twitter", access_token="...")
19
- >>> request = PostCreateRequest(content="Hello!")
20
- >>> post = await manager.execute_post(credentials, request)
18
+ Factory with custom OAuth credentials:
19
+ >>> from marqetive import PlatformFactory, AuthCredentials
20
+ >>> factory = PlatformFactory(
21
+ ... twitter_client_id="your_client_id",
22
+ ... twitter_client_secret="your_client_secret"
23
+ ... )
24
+ >>> client = await factory.get_client(credentials)
21
25
 
22
- Platform clients (low-level):
26
+ Direct client usage:
23
27
  >>> from marqetive.platforms.twitter import TwitterClient
24
- >>> credentials = AuthCredentials(platform="twitter", access_token="...")
25
28
  >>> async with TwitterClient(credentials) as client:
26
29
  ... post = await client.create_post(request)
30
+
31
+ Basic API client:
32
+ >>> from marqetive import APIClient
33
+ >>> async with APIClient(base_url="https://api.example.com") as client:
34
+ ... response = await client.get("/endpoint")
27
35
  """
28
36
 
29
37
  # Core API client
30
- # Account management
31
- from marqetive.core.account_factory import BaseAccountFactory
32
-
33
- # Registry and managers
34
- from marqetive.core.base_manager import BasePostManager
35
38
  from marqetive.core.client import APIClient
36
39
 
37
- # Progress tracking
38
- from marqetive.core.progress import (
39
- ProgressCallback,
40
- ProgressEvent,
41
- ProgressStatus,
42
- ProgressTracker,
43
- )
44
- from marqetive.core.registry import (
45
- PlatformRegistry,
46
- get_available_platforms,
47
- get_manager_for_platform,
48
- get_registry,
49
- register_platform,
40
+ # Factory
41
+ from marqetive.factory import PlatformFactory, get_client
42
+
43
+ # Progress callback type
44
+ from marqetive.platforms.base import ProgressCallback
45
+
46
+ # Exceptions
47
+ from marqetive.platforms.exceptions import (
48
+ InvalidFileTypeError,
49
+ MediaUploadError,
50
+ PlatformAuthError,
51
+ PlatformError,
52
+ PostNotFoundError,
53
+ RateLimitError,
54
+ ValidationError,
50
55
  )
51
56
 
52
57
  # Models
@@ -62,10 +67,6 @@ from marqetive.platforms.models import (
62
67
  PostStatus,
63
68
  PostUpdateRequest,
64
69
  )
65
- from marqetive.registry_init import (
66
- initialize_platform_registry,
67
- is_registry_initialized,
68
- )
69
70
 
70
71
  # Utilities
71
72
  from marqetive.utils.helpers import format_response
@@ -73,28 +74,14 @@ from marqetive.utils.helpers import format_response
73
74
  # Retry utilities
74
75
  from marqetive.utils.retry import STANDARD_BACKOFF, BackoffConfig, retry_async
75
76
 
76
- __version__ = "0.1.0"
77
+ __version__ = "0.2.0"
77
78
 
78
79
  __all__ = [
79
80
  # Core
80
81
  "APIClient",
81
- "format_response",
82
- # Registry
83
- "PlatformRegistry",
84
- "initialize_platform_registry",
85
- "is_registry_initialized",
86
- "get_registry",
87
- "register_platform",
88
- "get_manager_for_platform",
89
- "get_available_platforms",
90
- # Managers and factories
91
- "BasePostManager",
92
- "BaseAccountFactory",
93
- # Progress tracking
94
- "ProgressEvent",
95
- "ProgressStatus",
96
- "ProgressTracker",
97
- "ProgressCallback",
82
+ # Factory
83
+ "PlatformFactory",
84
+ "get_client",
98
85
  # Retry
99
86
  "BackoffConfig",
100
87
  "STANDARD_BACKOFF",
@@ -110,4 +97,16 @@ __all__ = [
110
97
  "CommentStatus",
111
98
  "MediaAttachment",
112
99
  "MediaType",
100
+ # Exceptions
101
+ "PlatformError",
102
+ "PlatformAuthError",
103
+ "RateLimitError",
104
+ "PostNotFoundError",
105
+ "MediaUploadError",
106
+ "ValidationError",
107
+ "InvalidFileTypeError",
108
+ # Types
109
+ "ProgressCallback",
110
+ # Utilities
111
+ "format_response",
113
112
  ]
marqetive/factory.py ADDED
@@ -0,0 +1,380 @@
1
+ """Platform factory for creating social media clients.
2
+
3
+ This module provides a simple factory for creating authenticated platform clients
4
+ with automatic token refresh and validation.
5
+ """
6
+
7
+ import logging
8
+ import os
9
+ from typing import TYPE_CHECKING
10
+
11
+ from marqetive.platforms.exceptions import PlatformAuthError
12
+ from marqetive.platforms.models import AuthCredentials
13
+ from marqetive.utils.oauth import (
14
+ refresh_instagram_token,
15
+ refresh_linkedin_token,
16
+ refresh_tiktok_token,
17
+ refresh_twitter_token,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ from marqetive.platforms.base import SocialMediaPlatform
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+ # Supported platforms
26
+ SUPPORTED_PLATFORMS = frozenset({"twitter", "linkedin", "instagram", "tiktok"})
27
+
28
+
29
+ def _create_client(
30
+ platform: str, credentials: "AuthCredentials"
31
+ ) -> "SocialMediaPlatform":
32
+ """Create a client for a platform (with lazy imports).
33
+
34
+ Args:
35
+ platform: Platform name (twitter, linkedin, instagram, tiktok).
36
+ credentials: Authentication credentials for the platform.
37
+
38
+ Returns:
39
+ The platform client instance.
40
+
41
+ Raises:
42
+ ValueError: If platform is unknown.
43
+ """
44
+ if platform == "twitter":
45
+ from marqetive.platforms.twitter.client import TwitterClient
46
+
47
+ return TwitterClient(credentials=credentials)
48
+ elif platform == "linkedin":
49
+ from marqetive.platforms.linkedin.client import LinkedInClient
50
+
51
+ return LinkedInClient(credentials=credentials)
52
+ elif platform == "instagram":
53
+ from marqetive.platforms.instagram.client import InstagramClient
54
+
55
+ return InstagramClient(credentials=credentials)
56
+ elif platform == "tiktok":
57
+ from marqetive.platforms.tiktok.client import TikTokClient
58
+
59
+ return TikTokClient(credentials=credentials)
60
+ else:
61
+ raise ValueError(
62
+ f"Unknown platform: {platform}. "
63
+ f"Supported platforms: {', '.join(sorted(SUPPORTED_PLATFORMS))}"
64
+ )
65
+
66
+
67
+ class PlatformFactory:
68
+ """Factory for creating authenticated social media platform clients.
69
+
70
+ This factory handles:
71
+ - Token refresh before client creation (if expired)
72
+ - Platform-specific credential validation
73
+ - OAuth client credentials from environment variables
74
+
75
+ Example:
76
+ >>> factory = PlatformFactory()
77
+ >>> credentials = AuthCredentials(
78
+ ... platform="twitter",
79
+ ... access_token="token",
80
+ ... refresh_token="refresh"
81
+ ... )
82
+ >>> client = await factory.get_client(credentials)
83
+ >>> async with client:
84
+ ... post = await client.create_post(request)
85
+
86
+ Example with custom OAuth credentials:
87
+ >>> factory = PlatformFactory(
88
+ ... twitter_client_id="your_client_id",
89
+ ... twitter_client_secret="your_client_secret"
90
+ ... )
91
+ >>> client = await factory.get_client(credentials)
92
+ """
93
+
94
+ def __init__(
95
+ self,
96
+ *,
97
+ twitter_client_id: str | None = None,
98
+ twitter_client_secret: str | None = None,
99
+ twitter_api_key: str | None = None,
100
+ twitter_api_secret: str | None = None,
101
+ linkedin_client_id: str | None = None,
102
+ linkedin_client_secret: str | None = None,
103
+ tiktok_client_id: str | None = None,
104
+ tiktok_client_secret: str | None = None,
105
+ ) -> None:
106
+ """Initialize platform factory with OAuth credentials.
107
+
108
+ OAuth credentials can be provided directly or via environment variables:
109
+ - TWITTER_CLIENT_ID, TWITTER_CLIENT_SECRET, TWITTER_API_KEY, TWITTER_API_SECRET
110
+ - LINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET
111
+ - TIKTOK_CLIENT_ID, TIKTOK_CLIENT_SECRET
112
+
113
+ Note: Instagram uses long-lived tokens that don't require client credentials
114
+ for refresh (only the current access token is needed).
115
+
116
+ Args:
117
+ twitter_client_id: Twitter OAuth client ID.
118
+ twitter_client_secret: Twitter OAuth client secret.
119
+ twitter_api_key: Twitter API key (for media operations).
120
+ twitter_api_secret: Twitter API secret (for media operations).
121
+ linkedin_client_id: LinkedIn OAuth client ID.
122
+ linkedin_client_secret: LinkedIn OAuth client secret.
123
+ tiktok_client_id: TikTok OAuth client ID.
124
+ tiktok_client_secret: TikTok OAuth client secret.
125
+ """
126
+ self._oauth_credentials = {
127
+ "twitter": {
128
+ "client_id": twitter_client_id or os.getenv("TWITTER_CLIENT_ID"),
129
+ "client_secret": twitter_client_secret
130
+ or os.getenv("TWITTER_CLIENT_SECRET"),
131
+ "api_key": twitter_api_key or os.getenv("TWITTER_API_KEY"),
132
+ "api_secret": twitter_api_secret or os.getenv("TWITTER_API_SECRET"),
133
+ },
134
+ "linkedin": {
135
+ "client_id": linkedin_client_id or os.getenv("LINKEDIN_CLIENT_ID"),
136
+ "client_secret": linkedin_client_secret
137
+ or os.getenv("LINKEDIN_CLIENT_SECRET"),
138
+ },
139
+ "tiktok": {
140
+ "client_id": tiktok_client_id or os.getenv("TIKTOK_CLIENT_ID"),
141
+ "client_secret": tiktok_client_secret
142
+ or os.getenv("TIKTOK_CLIENT_SECRET"),
143
+ },
144
+ }
145
+
146
+ async def get_client(
147
+ self,
148
+ credentials: AuthCredentials,
149
+ *,
150
+ auto_refresh: bool = True,
151
+ ) -> "SocialMediaPlatform":
152
+ """Create a platform client with the given credentials.
153
+
154
+ If credentials are expired and auto_refresh is True, attempts to
155
+ refresh the token before creating the client.
156
+
157
+ Args:
158
+ credentials: Authentication credentials for the platform.
159
+ auto_refresh: Whether to automatically refresh expired tokens.
160
+
161
+ Returns:
162
+ Platform client (TwitterClient, LinkedInClient, etc.).
163
+ The client must be used as an async context manager.
164
+
165
+ Raises:
166
+ PlatformAuthError: If credentials are invalid or refresh fails.
167
+ ValueError: If platform is unknown.
168
+
169
+ Example:
170
+ >>> factory = PlatformFactory()
171
+ >>> client = await factory.get_client(credentials)
172
+ >>> async with client:
173
+ ... post = await client.create_post(request)
174
+ """
175
+ platform = credentials.platform.lower()
176
+
177
+ # Validate platform-specific requirements
178
+ self._validate_credentials(credentials)
179
+
180
+ # Refresh token if needed
181
+ if auto_refresh and credentials.needs_refresh():
182
+ logger.info(f"Refreshing expired token for {platform}")
183
+ credentials = await self._refresh_token(credentials)
184
+ credentials.mark_valid()
185
+
186
+ # Enrich credentials with API keys for Twitter (needed for media operations)
187
+ if platform == "twitter":
188
+ credentials = self._enrich_twitter_credentials(credentials)
189
+
190
+ # Create and return client
191
+ return _create_client(platform, credentials)
192
+
193
+ def _validate_credentials(self, credentials: AuthCredentials) -> None:
194
+ """Validate platform-specific credential requirements.
195
+
196
+ Args:
197
+ credentials: The credentials to validate.
198
+
199
+ Raises:
200
+ PlatformAuthError: If credentials are missing required fields.
201
+ """
202
+ platform = credentials.platform.lower()
203
+
204
+ if platform not in SUPPORTED_PLATFORMS:
205
+ raise ValueError(
206
+ f"Unknown platform: {platform}. "
207
+ f"Supported platforms: {', '.join(sorted(SUPPORTED_PLATFORMS))}"
208
+ )
209
+
210
+ if not credentials.access_token:
211
+ raise PlatformAuthError(
212
+ "Access token is required",
213
+ platform=platform,
214
+ )
215
+
216
+ if platform == "instagram":
217
+ # Instagram requires user_id or business_account_id
218
+ business_account_id = credentials.additional_data.get(
219
+ "instagram_business_account_id"
220
+ )
221
+ if not credentials.user_id and not business_account_id:
222
+ raise PlatformAuthError(
223
+ "Instagram requires user_id or 'instagram_business_account_id' "
224
+ "in additional_data",
225
+ platform=platform,
226
+ )
227
+
228
+ elif platform == "tiktok":
229
+ # TikTok requires open_id
230
+ if not credentials.additional_data.get("open_id"):
231
+ raise PlatformAuthError(
232
+ "TikTok requires 'open_id' in additional_data",
233
+ platform=platform,
234
+ )
235
+
236
+ elif platform == "linkedin":
237
+ # LinkedIn requires user_id (person URN)
238
+ if not credentials.user_id:
239
+ raise PlatformAuthError(
240
+ "LinkedIn requires user_id (person URN, e.g., 'urn:li:person:xxx')",
241
+ platform=platform,
242
+ )
243
+
244
+ async def _refresh_token(self, credentials: AuthCredentials) -> AuthCredentials:
245
+ """Refresh the access token for the given credentials.
246
+
247
+ Args:
248
+ credentials: The credentials with expired token.
249
+
250
+ Returns:
251
+ Updated credentials with new access token.
252
+
253
+ Raises:
254
+ PlatformAuthError: If refresh fails or required OAuth credentials are missing.
255
+ """
256
+ platform = credentials.platform.lower()
257
+
258
+ if platform == "twitter":
259
+ oauth_creds = self._oauth_credentials.get("twitter", {})
260
+ client_id = oauth_creds.get("client_id")
261
+ client_secret = oauth_creds.get("client_secret")
262
+
263
+ if not client_id or not client_secret:
264
+ raise PlatformAuthError(
265
+ "Twitter client_id and client_secret required for token refresh. "
266
+ "Provide them via PlatformFactory constructor or environment variables "
267
+ "(TWITTER_CLIENT_ID, TWITTER_CLIENT_SECRET).",
268
+ platform=platform,
269
+ )
270
+
271
+ return await refresh_twitter_token(credentials, client_id, client_secret)
272
+
273
+ elif platform == "linkedin":
274
+ oauth_creds = self._oauth_credentials.get("linkedin", {})
275
+ client_id = oauth_creds.get("client_id")
276
+ client_secret = oauth_creds.get("client_secret")
277
+
278
+ if not client_id or not client_secret:
279
+ raise PlatformAuthError(
280
+ "LinkedIn client_id and client_secret required for token refresh. "
281
+ "Provide them via PlatformFactory constructor or environment variables "
282
+ "(LINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET).",
283
+ platform=platform,
284
+ )
285
+
286
+ return await refresh_linkedin_token(credentials, client_id, client_secret)
287
+
288
+ elif platform == "instagram":
289
+ # Instagram doesn't need OAuth credentials for refresh
290
+ return await refresh_instagram_token(credentials)
291
+
292
+ elif platform == "tiktok":
293
+ oauth_creds = self._oauth_credentials.get("tiktok", {})
294
+ client_id = oauth_creds.get("client_id")
295
+ client_secret = oauth_creds.get("client_secret")
296
+
297
+ if not client_id or not client_secret:
298
+ raise PlatformAuthError(
299
+ "TikTok client_id and client_secret required for token refresh. "
300
+ "Provide them via PlatformFactory constructor or environment variables "
301
+ "(TIKTOK_CLIENT_ID, TIKTOK_CLIENT_SECRET).",
302
+ platform=platform,
303
+ )
304
+
305
+ return await refresh_tiktok_token(credentials, client_id, client_secret)
306
+
307
+ else:
308
+ raise ValueError(f"Unknown platform: {platform}")
309
+
310
+ def _enrich_twitter_credentials(
311
+ self, credentials: AuthCredentials
312
+ ) -> AuthCredentials:
313
+ """Enrich Twitter credentials with API keys for media operations.
314
+
315
+ Args:
316
+ credentials: The Twitter credentials.
317
+
318
+ Returns:
319
+ Credentials with api_key and api_secret in additional_data.
320
+ """
321
+ oauth_creds = self._oauth_credentials.get("twitter", {})
322
+
323
+ # Only add if not already present
324
+ if "api_key" not in credentials.additional_data:
325
+ api_key = oauth_creds.get("api_key")
326
+ if api_key:
327
+ credentials.additional_data["api_key"] = api_key
328
+
329
+ if "api_secret" not in credentials.additional_data:
330
+ api_secret = oauth_creds.get("api_secret")
331
+ if api_secret:
332
+ credentials.additional_data["api_secret"] = api_secret
333
+
334
+ return credentials
335
+
336
+ def get_supported_platforms(self) -> frozenset[str]:
337
+ """Get the set of supported platform names.
338
+
339
+ Returns:
340
+ Frozenset of supported platform names.
341
+ """
342
+ return SUPPORTED_PLATFORMS
343
+
344
+
345
+ async def get_client(
346
+ credentials: AuthCredentials,
347
+ *,
348
+ auto_refresh: bool = True,
349
+ ) -> "SocialMediaPlatform":
350
+ """Create a platform client with the given credentials.
351
+
352
+ This is a convenience function that creates a PlatformFactory with
353
+ environment-based OAuth credentials and returns a client.
354
+
355
+ For more control over OAuth credentials, create a PlatformFactory directly.
356
+
357
+ Args:
358
+ credentials: Authentication credentials for the platform.
359
+ auto_refresh: Whether to automatically refresh expired tokens.
360
+
361
+ Returns:
362
+ Platform client ready to be used as async context manager.
363
+
364
+ Raises:
365
+ PlatformAuthError: If credentials are invalid or refresh fails.
366
+ ValueError: If platform is unknown.
367
+
368
+ Example:
369
+ >>> from marqetive import get_client, AuthCredentials, PostCreateRequest
370
+ >>> credentials = AuthCredentials(
371
+ ... platform="twitter",
372
+ ... access_token="...",
373
+ ... refresh_token="..."
374
+ ... )
375
+ >>> client = await get_client(credentials)
376
+ >>> async with client:
377
+ ... post = await client.create_post(PostCreateRequest(content="Hello!"))
378
+ """
379
+ factory = PlatformFactory()
380
+ return await factory.get_client(credentials, auto_refresh=auto_refresh)
@@ -6,6 +6,7 @@ All concrete implementations must implement the abstract methods defined here.
6
6
  """
7
7
 
8
8
  from abc import ABC, abstractmethod
9
+ from collections.abc import Callable
9
10
  from datetime import datetime
10
11
  from traceback import TracebackException
11
12
  from typing import Any
@@ -25,6 +26,10 @@ from marqetive.platforms.models import (
25
26
  PostUpdateRequest,
26
27
  )
27
28
 
29
+ # Type alias for progress callback
30
+ # (operation: str, progress: int, total: int, message: str | None) -> None
31
+ type ProgressCallback = Callable[[str, int, int, str | None], None]
32
+
28
33
 
29
34
  class SocialMediaPlatform(ABC):
30
35
  """Abstract base class for social media platform integrations.
@@ -57,6 +62,7 @@ class SocialMediaPlatform(ABC):
57
62
  credentials: AuthCredentials,
58
63
  base_url: str,
59
64
  timeout: float = 30.0,
65
+ progress_callback: ProgressCallback | None = None,
60
66
  ) -> None:
61
67
  """Initialize the platform client.
62
68
 
@@ -65,6 +71,9 @@ class SocialMediaPlatform(ABC):
65
71
  credentials: Authentication credentials
66
72
  base_url: Base URL for the platform API
67
73
  timeout: Request timeout in seconds
74
+ progress_callback: Optional callback for progress updates during
75
+ long-running operations (e.g., media uploads). The callback
76
+ receives (operation, progress, total, message).
68
77
 
69
78
  Raises:
70
79
  PlatformAuthError: If credentials are invalid or expired
@@ -73,6 +82,7 @@ class SocialMediaPlatform(ABC):
73
82
  self.credentials = credentials
74
83
  self.base_url = base_url
75
84
  self.timeout = timeout
85
+ self._progress_callback = progress_callback
76
86
  self.api_client: APIClient | None = None
77
87
  self._rate_limit_remaining: int | None = None
78
88
  self._rate_limit_reset: datetime | None = None
@@ -152,6 +162,29 @@ class SocialMediaPlatform(ABC):
152
162
  self._rate_limit_remaining = remaining
153
163
  self._rate_limit_reset = reset_time
154
164
 
165
+ def _emit_progress(
166
+ self,
167
+ operation: str,
168
+ progress: int,
169
+ total: int,
170
+ message: str | None = None,
171
+ ) -> None:
172
+ """Emit a progress update if a callback is registered.
173
+
174
+ This method is safe to call even if no callback is registered.
175
+
176
+ Args:
177
+ operation: Name of the operation (e.g., "upload_media", "create_post")
178
+ progress: Current progress value
179
+ total: Total value for completion
180
+ message: Optional human-readable message
181
+
182
+ Example:
183
+ >>> self._emit_progress("upload_media", 1, 3, "Uploading image 1 of 3")
184
+ """
185
+ if self._progress_callback is not None:
186
+ self._progress_callback(operation, progress, total, message)
187
+
155
188
  # ==================== Abstract Authentication Methods ====================
156
189
 
157
190
  @abstractmethod
@@ -1,7 +1,5 @@
1
1
  """Instagram platform integration."""
2
2
 
3
3
  from marqetive.platforms.instagram.client import InstagramClient
4
- from marqetive.platforms.instagram.factory import InstagramAccountFactory
5
- from marqetive.platforms.instagram.manager import InstagramPostManager
6
4
 
7
- __all__ = ["InstagramClient", "InstagramAccountFactory", "InstagramPostManager"]
5
+ __all__ = ["InstagramClient"]
@@ -12,7 +12,7 @@ from typing import Any, Literal, cast
12
12
  import httpx
13
13
  from pydantic import HttpUrl
14
14
 
15
- from marqetive.platforms.base import SocialMediaPlatform
15
+ from marqetive.platforms.base import ProgressCallback, SocialMediaPlatform
16
16
  from marqetive.platforms.exceptions import (
17
17
  MediaUploadError,
18
18
  PlatformAuthError,
@@ -69,6 +69,7 @@ class InstagramClient(SocialMediaPlatform):
69
69
  credentials: AuthCredentials,
70
70
  timeout: float = 30.0,
71
71
  api_version: str = "v21.0",
72
+ progress_callback: ProgressCallback | None = None,
72
73
  ) -> None:
73
74
  """Initialize Instagram client.
74
75
 
@@ -76,6 +77,8 @@ class InstagramClient(SocialMediaPlatform):
76
77
  credentials: Instagram authentication credentials
77
78
  timeout: Request timeout in seconds
78
79
  api_version: Instagram Graph API version
80
+ progress_callback: Optional callback for progress updates during
81
+ long-running operations like media uploads.
79
82
 
80
83
  Raises:
81
84
  PlatformAuthError: If credentials are invalid
@@ -86,6 +89,7 @@ class InstagramClient(SocialMediaPlatform):
86
89
  credentials=credentials,
87
90
  base_url=base_url,
88
91
  timeout=timeout,
92
+ progress_callback=progress_callback,
89
93
  )
90
94
  self.instagram_account_id = credentials.user_id
91
95
  self.api_version = api_version
@@ -1,7 +1,5 @@
1
1
  """LinkedIn platform integration."""
2
2
 
3
3
  from marqetive.platforms.linkedin.client import LinkedInClient
4
- from marqetive.platforms.linkedin.factory import LinkedInAccountFactory
5
- from marqetive.platforms.linkedin.manager import LinkedInPostManager
6
4
 
7
- __all__ = ["LinkedInClient", "LinkedInAccountFactory", "LinkedInPostManager"]
5
+ __all__ = ["LinkedInClient"]
@@ -12,7 +12,7 @@ from typing import Any, cast
12
12
  import httpx
13
13
  from pydantic import HttpUrl
14
14
 
15
- from marqetive.platforms.base import SocialMediaPlatform
15
+ from marqetive.platforms.base import ProgressCallback, SocialMediaPlatform
16
16
  from marqetive.platforms.exceptions import (
17
17
  MediaUploadError,
18
18
  PlatformAuthError,
@@ -66,6 +66,7 @@ class LinkedInClient(SocialMediaPlatform):
66
66
  credentials: AuthCredentials,
67
67
  timeout: float = 30.0,
68
68
  api_version: str = "v2",
69
+ progress_callback: ProgressCallback | None = None,
69
70
  ) -> None:
70
71
  """Initialize LinkedIn client.
71
72
 
@@ -73,6 +74,8 @@ class LinkedInClient(SocialMediaPlatform):
73
74
  credentials: LinkedIn authentication credentials
74
75
  timeout: Request timeout in seconds
75
76
  api_version: LinkedIn API version
77
+ progress_callback: Optional callback for progress updates during
78
+ long-running operations like media uploads.
76
79
 
77
80
  Raises:
78
81
  PlatformAuthError: If credentials are invalid
@@ -83,6 +86,7 @@ class LinkedInClient(SocialMediaPlatform):
83
86
  credentials=credentials,
84
87
  base_url=base_url,
85
88
  timeout=timeout,
89
+ progress_callback=progress_callback,
86
90
  )
87
91
  self.author_urn = (
88
92
  credentials.user_id