marqetive-lib 0.1.6__py3-none-any.whl → 0.1.7__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
@@ -62,6 +62,8 @@ from marqetive.core.models import (
62
62
  PostCreateRequest,
63
63
  PostStatus,
64
64
  PostUpdateRequest,
65
+ ProgressEvent,
66
+ ProgressStatus,
65
67
  )
66
68
 
67
69
  # Factory
@@ -85,17 +87,21 @@ __all__ = [
85
87
  "BackoffConfig",
86
88
  "STANDARD_BACKOFF",
87
89
  "retry_async",
88
- # Models
89
- "AuthCredentials",
90
+ # Enums
90
91
  "AccountStatus",
91
- "Post",
92
+ "CommentStatus",
93
+ "MediaType",
92
94
  "PostStatus",
93
- "PostCreateRequest",
94
- "PostUpdateRequest",
95
+ "ProgressStatus",
96
+ # Core Models
97
+ "AuthCredentials",
95
98
  "Comment",
96
- "CommentStatus",
97
99
  "MediaAttachment",
98
- "MediaType",
100
+ "Post",
101
+ "ProgressEvent",
102
+ # Base Request Models
103
+ "PostCreateRequest",
104
+ "PostUpdateRequest",
99
105
  # Exceptions
100
106
  "PlatformError",
101
107
  "PlatformAuthError",
@@ -31,17 +31,19 @@ __all__ = [
31
31
  # Base class
32
32
  "SocialMediaPlatform",
33
33
  "ProgressCallback",
34
- # Models
34
+ # Enums
35
35
  "AccountStatus",
36
+ "CommentStatus",
37
+ "MediaType",
38
+ "PostStatus",
39
+ # Core Models
36
40
  "AuthCredentials",
37
41
  "Comment",
38
- "CommentStatus",
39
42
  "MediaAttachment",
40
- "MediaType",
41
43
  "PlatformResponse",
42
44
  "Post",
45
+ # Base Request Models
43
46
  "PostCreateRequest",
44
- "PostStatus",
45
47
  "PostUpdateRequest",
46
48
  # Exceptions
47
49
  "InvalidFileTypeError",
marqetive/core/base.py CHANGED
@@ -5,8 +5,9 @@ for implementing platform-specific clients (Instagram, Twitter, LinkedIn, etc.).
5
5
  All concrete implementations must implement the abstract methods defined here.
6
6
  """
7
7
 
8
+ import inspect
8
9
  from abc import ABC, abstractmethod
9
- from collections.abc import Callable
10
+ from collections.abc import Awaitable, Callable
10
11
  from datetime import datetime
11
12
  from traceback import TracebackException
12
13
  from typing import Any
@@ -24,11 +25,15 @@ from marqetive.core.models import (
24
25
  Post,
25
26
  PostCreateRequest,
26
27
  PostUpdateRequest,
28
+ ProgressEvent,
29
+ ProgressStatus,
27
30
  )
28
31
 
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
+ # Type aliases for progress callbacks
33
+ # Supports both sync and async callbacks using ProgressEvent
34
+ type SyncProgressCallback = Callable[[ProgressEvent], None]
35
+ type AsyncProgressCallback = Callable[[ProgressEvent], Awaitable[None]]
36
+ type ProgressCallback = SyncProgressCallback | AsyncProgressCallback
32
37
 
33
38
 
34
39
  class SocialMediaPlatform(ABC):
@@ -162,28 +167,66 @@ class SocialMediaPlatform(ABC):
162
167
  self._rate_limit_remaining = remaining
163
168
  self._rate_limit_reset = reset_time
164
169
 
165
- def _emit_progress(
170
+ async def _emit_progress(
166
171
  self,
167
172
  operation: str,
173
+ status: ProgressStatus,
168
174
  progress: int,
169
175
  total: int,
170
176
  message: str | None = None,
177
+ *,
178
+ entity_id: str | None = None,
179
+ file_path: str | None = None,
180
+ bytes_uploaded: int | None = None,
181
+ total_bytes: int | None = None,
171
182
  ) -> None:
172
183
  """Emit a progress update if a callback is registered.
173
184
 
174
- This method is safe to call even if no callback is registered.
185
+ This method supports both synchronous and asynchronous callbacks.
186
+ It is safe to call even if no callback is registered.
175
187
 
176
188
  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
189
+ operation: Name of the operation (e.g., "upload_media", "create_post", "create_thread")
190
+ status: Current status of the operation
191
+ progress: Current progress value (0-100 for percentage, or bytes)
192
+ total: Total value for completion (100 for percentage, or total bytes)
193
+ message: Optional human-readable status message
194
+ entity_id: Optional platform-specific ID (media_id, post_id, etc.)
195
+ file_path: Optional file path for upload operations
196
+ bytes_uploaded: Optional bytes uploaded so far
197
+ total_bytes: Optional total bytes to upload
181
198
 
182
199
  Example:
183
- >>> self._emit_progress("upload_media", 1, 3, "Uploading image 1 of 3")
200
+ >>> await self._emit_progress(
201
+ ... operation="upload_media",
202
+ ... status=ProgressStatus.UPLOADING,
203
+ ... progress=50,
204
+ ... total=100,
205
+ ... message="Uploading image 1 of 3",
206
+ ... entity_id="media_123",
207
+ ... )
184
208
  """
185
- if self._progress_callback is not None:
186
- self._progress_callback(operation, progress, total, message)
209
+ if self._progress_callback is None:
210
+ return
211
+
212
+ event = ProgressEvent(
213
+ operation=operation,
214
+ platform=self.platform_name,
215
+ status=status,
216
+ progress=progress,
217
+ total=total,
218
+ message=message,
219
+ entity_id=entity_id,
220
+ file_path=file_path,
221
+ bytes_uploaded=bytes_uploaded,
222
+ total_bytes=total_bytes,
223
+ )
224
+
225
+ result = self._progress_callback(event)
226
+
227
+ # If callback returned a coroutine, await it
228
+ if inspect.iscoroutine(result):
229
+ await result
187
230
 
188
231
  # ==================== Abstract Authentication Methods ====================
189
232
 
@@ -307,6 +350,42 @@ class SocialMediaPlatform(ABC):
307
350
  """
308
351
  pass
309
352
 
353
+ async def create_thread(
354
+ self,
355
+ posts: list[PostCreateRequest],
356
+ ) -> list[Post]:
357
+ """Create a thread of connected posts.
358
+
359
+ Not all platforms support threads. The default implementation raises
360
+ NotImplementedError. Platforms that support threads (like Twitter)
361
+ should override this method.
362
+
363
+ Args:
364
+ posts: List of post requests to create as a thread.
365
+ Each post can have its own content, media, etc.
366
+ First post is the head of the thread.
367
+
368
+ Returns:
369
+ List of Post objects for each post in the thread.
370
+
371
+ Raises:
372
+ NotImplementedError: If platform doesn't support threads.
373
+ ValidationError: If posts list is empty.
374
+ PlatformAuthError: If not authenticated.
375
+
376
+ Example:
377
+ >>> # Twitter thread example
378
+ >>> posts = [
379
+ ... TwitterPostRequest(content="Thread start! 1/3"),
380
+ ... TwitterPostRequest(content="Middle 2/3", media_urls=[...]),
381
+ ... TwitterPostRequest(content="End 3/3"),
382
+ ... ]
383
+ >>> thread = await client.create_thread(posts)
384
+ """
385
+ raise NotImplementedError(
386
+ f"{self.platform_name} does not support thread creation"
387
+ )
388
+
310
389
  # ==================== Abstract Comment Methods ====================
311
390
 
312
391
  @abstractmethod
marqetive/core/models.py CHANGED
@@ -6,10 +6,10 @@ and type safety.
6
6
  """
7
7
 
8
8
  from datetime import UTC, datetime, timedelta
9
- from enum import Enum
9
+ from enum import Enum, StrEnum
10
10
  from typing import Any
11
11
 
12
- from pydantic import BaseModel, Field, HttpUrl
12
+ from pydantic import BaseModel, ConfigDict, Field, HttpUrl
13
13
 
14
14
 
15
15
  class MediaType(str, Enum):
@@ -19,6 +19,8 @@ class MediaType(str, Enum):
19
19
  VIDEO = "video"
20
20
  CAROUSEL = "carousel"
21
21
  DOCUMENT = "document"
22
+ REEL = "reel"
23
+ STORY = "story"
22
24
 
23
25
 
24
26
  class PostStatus(str, Enum):
@@ -55,6 +57,29 @@ class AccountStatus(str, Enum):
55
57
  ERROR = "error"
56
58
 
57
59
 
60
+ class ProgressStatus(StrEnum):
61
+ """Standard progress status for operations across all platforms.
62
+
63
+ Used with ProgressEvent to provide consistent progress tracking
64
+ for long-running operations like media uploads and post creation.
65
+
66
+ Attributes:
67
+ INITIALIZING: Operation is starting, preparing resources.
68
+ UPLOADING: Actively transferring data (e.g., uploading media).
69
+ PROCESSING: Server-side processing (e.g., video transcoding).
70
+ FINALIZING: Completing the operation (e.g., publishing post).
71
+ COMPLETED: Operation finished successfully.
72
+ FAILED: Operation failed with an error.
73
+ """
74
+
75
+ INITIALIZING = "initializing"
76
+ UPLOADING = "uploading"
77
+ PROCESSING = "processing"
78
+ FINALIZING = "finalizing"
79
+ COMPLETED = "completed"
80
+ FAILED = "failed"
81
+
82
+
58
83
  class MediaAttachment(BaseModel):
59
84
  """Represents a media attachment (image, video, etc.).
60
85
 
@@ -294,8 +319,82 @@ class PlatformResponse(BaseModel):
294
319
  rate_limit_reset: datetime | None = None
295
320
 
296
321
 
322
+ class ProgressEvent(BaseModel):
323
+ """Unified progress event for tracking long-running operations.
324
+
325
+ Provides a consistent interface for progress callbacks across all platforms.
326
+ Supports both synchronous and asynchronous callbacks.
327
+
328
+ Attributes:
329
+ operation: Name of the operation (e.g., "upload_media", "create_post", "create_thread").
330
+ platform: Platform name (e.g., "twitter", "linkedin", "instagram", "tiktok").
331
+ status: Current status of the operation.
332
+ progress: Current progress value (0-100 for percentage, or bytes uploaded).
333
+ total: Total value for completion (100 for percentage, or total bytes).
334
+ message: Optional human-readable status message.
335
+ entity_id: Optional platform-specific ID (media_id, post_id, container_id).
336
+ file_path: Optional file path for upload operations.
337
+ bytes_uploaded: Optional bytes uploaded so far.
338
+ total_bytes: Optional total bytes to upload.
339
+
340
+ Example:
341
+ >>> # Progress callback for media upload
342
+ >>> def on_progress(event: ProgressEvent) -> None:
343
+ ... print(f"{event.operation}: {event.percentage:.1f}% - {event.message}")
344
+ ...
345
+ >>> # Async progress callback
346
+ >>> async def on_progress_async(event: ProgressEvent) -> None:
347
+ ... await log_to_database(event)
348
+
349
+ >>> event = ProgressEvent(
350
+ ... operation="upload_media",
351
+ ... platform="twitter",
352
+ ... status=ProgressStatus.UPLOADING,
353
+ ... progress=50,
354
+ ... total=100,
355
+ ... message="Uploading image 1 of 2",
356
+ ... bytes_uploaded=524288,
357
+ ... total_bytes=1048576,
358
+ ... )
359
+ >>> print(event.percentage) # 50.0
360
+ """
361
+
362
+ operation: str
363
+ platform: str
364
+ status: ProgressStatus
365
+ progress: int
366
+ total: int
367
+ message: str | None = None
368
+
369
+ # Optional detailed info
370
+ entity_id: str | None = None
371
+ file_path: str | None = None
372
+ bytes_uploaded: int | None = None
373
+ total_bytes: int | None = None
374
+
375
+ @property
376
+ def percentage(self) -> float:
377
+ """Calculate progress as a percentage.
378
+
379
+ Returns:
380
+ Progress percentage (0.0 to 100.0).
381
+ """
382
+ if self.total == 0:
383
+ return 0.0
384
+ return (self.progress / self.total) * 100
385
+
386
+ model_config = ConfigDict(frozen=True)
387
+
388
+
297
389
  class PostCreateRequest(BaseModel):
298
- """Request model for creating a new post.
390
+ """Base request model for creating a new post.
391
+
392
+ This is a minimal base model with universal fields that work across all platforms.
393
+ For platform-specific features, use the dedicated request models:
394
+ - TwitterPostRequest
395
+ - LinkedInPostRequest
396
+ - InstagramPostRequest
397
+ - TikTokPostRequest
299
398
 
300
399
  Attributes:
301
400
  content: Text content of the post
@@ -305,7 +404,7 @@ class PostCreateRequest(BaseModel):
305
404
  link: URL to include in the post
306
405
  tags: List of hashtags or user tags
307
406
  location: Location/place tag for the post
308
- additional_data: Platform-specific data
407
+ additional_data: Platform-specific data for backward compatibility
309
408
 
310
409
  Example:
311
410
  >>> request = PostCreateRequest(
@@ -326,10 +425,15 @@ class PostCreateRequest(BaseModel):
326
425
 
327
426
 
328
427
  class PostUpdateRequest(BaseModel):
329
- """Request model for updating an existing post.
428
+ """Base request model for updating an existing post.
429
+
430
+ Note: Not all platforms support editing posts:
431
+ - Twitter: No editing support (for most users)
432
+ - Instagram: No editing support
433
+ - TikTok: No editing support
434
+ - LinkedIn: Supports updating content, CTA, and landing page
330
435
 
331
- Note: Not all platforms support editing posts. Fields that can be
332
- updated vary by platform.
436
+ For LinkedIn-specific update features, use LinkedInPostUpdateRequest.
333
437
 
334
438
  Attributes:
335
439
  content: Updated text content
@@ -1,5 +1,6 @@
1
1
  """Instagram platform integration."""
2
2
 
3
3
  from marqetive.platforms.instagram.client import InstagramClient
4
+ from marqetive.platforms.instagram.models import InstagramPostRequest
4
5
 
5
- __all__ = ["InstagramClient"]
6
+ __all__ = ["InstagramClient", "InstagramPostRequest"]
@@ -11,8 +11,9 @@ This module provides comprehensive media management for:
11
11
  """
12
12
 
13
13
  import asyncio
14
+ import inspect
14
15
  import logging
15
- from collections.abc import Callable
16
+ from collections.abc import Awaitable, Callable
16
17
  from dataclasses import dataclass
17
18
  from enum import Enum
18
19
  from typing import Any, Literal
@@ -23,8 +24,14 @@ from marqetive.core.exceptions import (
23
24
  MediaUploadError,
24
25
  ValidationError,
25
26
  )
27
+ from marqetive.core.models import ProgressEvent, ProgressStatus
26
28
  from marqetive.utils.retry import STANDARD_BACKOFF, retry_async
27
29
 
30
+ # Type aliases for progress callbacks
31
+ type SyncProgressCallback = Callable[[ProgressEvent], None]
32
+ type AsyncProgressCallback = Callable[[ProgressEvent], Awaitable[None]]
33
+ type ProgressCallback = SyncProgressCallback | AsyncProgressCallback
34
+
28
35
  logger = logging.getLogger(__name__)
29
36
 
30
37
  # Instagram API limits
@@ -136,7 +143,7 @@ class InstagramMediaManager:
136
143
  *,
137
144
  api_version: str = "v21.0",
138
145
  timeout: float = 30.0,
139
- progress_callback: Callable[[str, str, int], None] | None = None,
146
+ progress_callback: ProgressCallback | None = None,
140
147
  ) -> None:
141
148
  """Initialize Instagram media manager.
142
149
 
@@ -145,7 +152,8 @@ class InstagramMediaManager:
145
152
  access_token: Instagram/Facebook access token.
146
153
  api_version: Instagram Graph API version.
147
154
  timeout: Request timeout in seconds.
148
- progress_callback: Optional callback(container_id, status, progress_pct).
155
+ progress_callback: Optional callback for progress updates.
156
+ Receives ProgressEvent objects with upload status and metrics.
149
157
  """
150
158
  self.ig_user_id = ig_user_id
151
159
  self.access_token = access_token
@@ -167,6 +175,44 @@ class InstagramMediaManager:
167
175
  """Exit async context and cleanup."""
168
176
  await self.client.aclose()
169
177
 
178
+ async def _emit_progress(
179
+ self,
180
+ status: ProgressStatus,
181
+ progress: int,
182
+ total: int,
183
+ message: str | None = None,
184
+ *,
185
+ entity_id: str | None = None,
186
+ file_path: str | None = None,
187
+ bytes_uploaded: int | None = None,
188
+ total_bytes: int | None = None,
189
+ ) -> None:
190
+ """Emit a progress update if a callback is registered.
191
+
192
+ Supports both sync and async callbacks.
193
+ """
194
+ if self.progress_callback is None:
195
+ return
196
+
197
+ event = ProgressEvent(
198
+ operation="upload_media",
199
+ platform="instagram",
200
+ status=status,
201
+ progress=progress,
202
+ total=total,
203
+ message=message,
204
+ entity_id=entity_id,
205
+ file_path=file_path,
206
+ bytes_uploaded=bytes_uploaded,
207
+ total_bytes=total_bytes,
208
+ )
209
+
210
+ result = self.progress_callback(event)
211
+
212
+ # If callback returned a coroutine, await it
213
+ if inspect.iscoroutine(result):
214
+ await result
215
+
170
216
  async def create_feed_containers(
171
217
  self,
172
218
  media_items: list[MediaItem],
@@ -260,9 +306,14 @@ class InstagramMediaManager:
260
306
  container_ids.append(container_id)
261
307
 
262
308
  # Notify progress
263
- if self.progress_callback:
264
- progress = int(((idx + 1) / len(media_items)) * 100)
265
- self.progress_callback(container_id, "created", progress)
309
+ progress = int(((idx + 1) / len(media_items)) * 100)
310
+ await self._emit_progress(
311
+ status=ProgressStatus.COMPLETED,
312
+ progress=progress,
313
+ total=100,
314
+ message=f"Created container {idx + 1}/{len(media_items)}",
315
+ entity_id=container_id,
316
+ )
266
317
 
267
318
  # If carousel, create parent container
268
319
  if is_carousel:
@@ -355,8 +406,13 @@ class InstagramMediaManager:
355
406
  media_type="reel",
356
407
  )
357
408
 
358
- if self.progress_callback:
359
- self.progress_callback(container_id, "ready", 100)
409
+ await self._emit_progress(
410
+ status=ProgressStatus.COMPLETED,
411
+ progress=100,
412
+ total=100,
413
+ message="Reel container ready for publishing",
414
+ entity_id=container_id,
415
+ )
360
416
 
361
417
  return container_id
362
418
 
@@ -426,8 +482,13 @@ class InstagramMediaManager:
426
482
  media_type="story",
427
483
  )
428
484
 
429
- if self.progress_callback:
430
- self.progress_callback(container_id, "ready", 100)
485
+ await self._emit_progress(
486
+ status=ProgressStatus.COMPLETED,
487
+ progress=100,
488
+ total=100,
489
+ message="Story container ready for publishing",
490
+ entity_id=container_id,
491
+ )
431
492
 
432
493
  return container_id
433
494
 
@@ -549,9 +610,14 @@ class InstagramMediaManager:
549
610
  )
550
611
 
551
612
  # Notify progress
552
- if self.progress_callback:
553
- progress = min(int((elapsed / timeout) * 90), 90) # Cap at 90%
554
- self.progress_callback(container_id, "processing", progress)
613
+ progress = min(int((elapsed / timeout) * 90), 90) # Cap at 90%
614
+ await self._emit_progress(
615
+ status=ProgressStatus.PROCESSING,
616
+ progress=progress,
617
+ total=100,
618
+ message=f"Processing {media_type} container",
619
+ entity_id=container_id,
620
+ )
555
621
 
556
622
  await asyncio.sleep(check_interval)
557
623
  elapsed += check_interval
@@ -0,0 +1,74 @@
1
+ """Instagram-specific models for post creation.
2
+
3
+ This module defines Instagram-specific data models for creating feed posts,
4
+ carousels, reels, and stories.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+ from marqetive.core.models import MediaType
12
+
13
+
14
+ class InstagramPostRequest(BaseModel):
15
+ """Instagram-specific post creation request.
16
+
17
+ Supports feed posts, carousels, reels, and stories.
18
+ Instagram requires media for all posts (no text-only posts).
19
+
20
+ Attributes:
21
+ caption: Post caption/text
22
+ media_urls: List of media URLs (required, 1 for single, 2-10 for carousel)
23
+ media_ids: List of pre-uploaded media container IDs
24
+ media_type: Type of post (IMAGE, VIDEO, CAROUSEL, REEL, STORY)
25
+ alt_texts: Alt text for each media item (accessibility)
26
+ location_id: Facebook Place ID for location tag
27
+ cover_url: Cover/thumbnail image URL (for Reels)
28
+ share_to_feed: Share Reel/Story to main feed
29
+ collaborators: List of collaborator Instagram user IDs
30
+ product_tags: Product tags for shopping posts
31
+ audio_name: Audio track name (for Reels)
32
+
33
+ Example:
34
+ >>> # Single image post
35
+ >>> request = InstagramPostRequest(
36
+ ... caption="Beautiful sunset!",
37
+ ... media_urls=["https://example.com/sunset.jpg"],
38
+ ... media_type=MediaType.IMAGE,
39
+ ... alt_texts=["A colorful sunset over the ocean"]
40
+ ... )
41
+
42
+ >>> # Carousel post
43
+ >>> request = InstagramPostRequest(
44
+ ... caption="Our product lineup",
45
+ ... media_urls=[
46
+ ... "https://example.com/product1.jpg",
47
+ ... "https://example.com/product2.jpg",
48
+ ... "https://example.com/product3.jpg"
49
+ ... ],
50
+ ... media_type=MediaType.CAROUSEL,
51
+ ... alt_texts=["Product 1", "Product 2", "Product 3"]
52
+ ... )
53
+
54
+ >>> # Reel
55
+ >>> request = InstagramPostRequest(
56
+ ... caption="Check out this tutorial!",
57
+ ... media_urls=["https://example.com/tutorial.mp4"],
58
+ ... media_type=MediaType.REEL,
59
+ ... cover_url="https://example.com/thumbnail.jpg",
60
+ ... share_to_feed=True
61
+ ... )
62
+ """
63
+
64
+ caption: str | None = None
65
+ media_urls: list[str] = Field(default_factory=list)
66
+ media_ids: list[str] = Field(default_factory=list)
67
+ media_type: MediaType = MediaType.IMAGE
68
+ alt_texts: list[str] = Field(default_factory=list)
69
+ location_id: str | None = None
70
+ cover_url: str | None = None
71
+ share_to_feed: bool = True
72
+ collaborators: list[str] = Field(default_factory=list)
73
+ product_tags: list[dict[str, Any]] = Field(default_factory=list)
74
+ audio_name: str | None = None
@@ -1,5 +1,54 @@
1
- """LinkedIn platform integration."""
1
+ """LinkedIn platform integration using the Community Management API.
2
+
3
+ This module provides the LinkedIn client and related models for interacting
4
+ with LinkedIn's Community Management API, supporting:
5
+ - Posts (create, read, update, delete, list)
6
+ - Comments (with nested replies)
7
+ - Reactions (Like, Celebrate, Love, Insightful, Support, Funny)
8
+ - Social metadata and engagement metrics
9
+ - Organization (Company Page) management
10
+ - Media uploads (images, videos, documents)
11
+ """
2
12
 
3
13
  from marqetive.platforms.linkedin.client import LinkedInClient
14
+ from marqetive.platforms.linkedin.media import (
15
+ LinkedInMediaManager,
16
+ MediaAsset,
17
+ UploadProgress,
18
+ VideoProcessingState,
19
+ )
20
+ from marqetive.platforms.linkedin.models import (
21
+ CallToActionLabel,
22
+ CommentsState,
23
+ FeedDistribution,
24
+ LinkedInPostRequest,
25
+ LinkedInPostUpdateRequest,
26
+ Organization,
27
+ OrganizationType,
28
+ PostVisibility,
29
+ Reaction,
30
+ ReactionType,
31
+ SocialMetadata,
32
+ )
4
33
 
5
- __all__ = ["LinkedInClient"]
34
+ __all__ = [
35
+ # Client
36
+ "LinkedInClient",
37
+ # Media
38
+ "LinkedInMediaManager",
39
+ "MediaAsset",
40
+ "UploadProgress",
41
+ "VideoProcessingState",
42
+ # Models
43
+ "CallToActionLabel",
44
+ "CommentsState",
45
+ "FeedDistribution",
46
+ "LinkedInPostRequest",
47
+ "LinkedInPostUpdateRequest",
48
+ "Organization",
49
+ "OrganizationType",
50
+ "PostVisibility",
51
+ "Reaction",
52
+ "ReactionType",
53
+ "SocialMetadata",
54
+ ]