bonanza-ugc 0.1.0__tar.gz

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,73 @@
1
+ Metadata-Version: 2.4
2
+ Name: bonanza-ugc
3
+ Version: 0.1.0
4
+ Summary: AI-generated UGC videos via Higgsfield Marketing Studio
5
+ Author: Bonanza Labs
6
+ License-Expression: MIT
7
+ Keywords: ugc,ai,video,higgsfield,marketing
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Multimedia :: Video
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+
18
+ # Bonanza UGC
19
+
20
+ AI-generated UGC videos via Higgsfield Marketing Studio.
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ pip install bonanza-ugc
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ```python
31
+ from bonanza_ugc import UGCGenerator, UGCConfig
32
+
33
+ gen = UGCGenerator()
34
+ video = gen.generate(
35
+ prompt="A stylish young woman shows off her silver chain necklace...",
36
+ avatar="mei",
37
+ mode="ugc",
38
+ duration=15,
39
+ )
40
+ print(f"Video URL: {video.result_url}")
41
+ print(f"Virality: {video.virality_score}/100")
42
+ ```
43
+
44
+ ## CLI
45
+
46
+ ```bash
47
+ # Generate a video
48
+ ugc generate --prompt "..." --avatar mei --mode ugc
49
+
50
+ # Use a template
51
+ ugc generate --template silverjstore --product "Venetian Chain" --avatar mei
52
+
53
+ # Score virality
54
+ ugc score <video_path_or_url>
55
+
56
+ # List avatars
57
+ ugc avatars
58
+
59
+ # Batch generate
60
+ ugc batch --leads leads.json --mode ugc
61
+ ```
62
+
63
+ ## Features
64
+
65
+ - 🎬 Generate UGC videos with AI avatars
66
+ - 📊 Score video virality with Brain Activity analysis
67
+ - 🎯 Prompt templates for product categories
68
+ - 📦 Batch generation support
69
+ - 🔄 Product URL fetching and integration
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,56 @@
1
+ # Bonanza UGC
2
+
3
+ AI-generated UGC videos via Higgsfield Marketing Studio.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install bonanza-ugc
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from bonanza_ugc import UGCGenerator, UGCConfig
15
+
16
+ gen = UGCGenerator()
17
+ video = gen.generate(
18
+ prompt="A stylish young woman shows off her silver chain necklace...",
19
+ avatar="mei",
20
+ mode="ugc",
21
+ duration=15,
22
+ )
23
+ print(f"Video URL: {video.result_url}")
24
+ print(f"Virality: {video.virality_score}/100")
25
+ ```
26
+
27
+ ## CLI
28
+
29
+ ```bash
30
+ # Generate a video
31
+ ugc generate --prompt "..." --avatar mei --mode ugc
32
+
33
+ # Use a template
34
+ ugc generate --template silverjstore --product "Venetian Chain" --avatar mei
35
+
36
+ # Score virality
37
+ ugc score <video_path_or_url>
38
+
39
+ # List avatars
40
+ ugc avatars
41
+
42
+ # Batch generate
43
+ ugc batch --leads leads.json --mode ugc
44
+ ```
45
+
46
+ ## Features
47
+
48
+ - 🎬 Generate UGC videos with AI avatars
49
+ - 📊 Score video virality with Brain Activity analysis
50
+ - 🎯 Prompt templates for product categories
51
+ - 📦 Batch generation support
52
+ - 🔄 Product URL fetching and integration
53
+
54
+ ## License
55
+
56
+ MIT
@@ -0,0 +1,9 @@
1
+ """Bonanza UGC — AI-generated UGC videos via Higgsfield Marketing Studio."""
2
+
3
+ __version__ = "0.1.0"
4
+ __author__ = "Bonanza Labs"
5
+
6
+ from .generator import UGCGenerator
7
+ from .config import UGCConfig
8
+
9
+ __all__ = ["UGCGenerator", "UGCConfig"]
@@ -0,0 +1,227 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ from bonanza_ugc.generator import UGCGenerator, UGCVideo
9
+ from bonanza_ugc.config import UGCConfig
10
+ import logging
11
+
12
+
13
+ def cmd_generate(args):
14
+ """Generate a UGC video."""
15
+ gen = UGCGenerator(UGCConfig.from_env())
16
+
17
+ if args.template:
18
+ video = gen.generate_from_template(
19
+ template=args.template,
20
+ product_name=args.product or "silver jewelry",
21
+ avatar=args.avatar,
22
+ mode=args.mode,
23
+ gender=args.gender or "woman",
24
+ duration=args.duration,
25
+ resolution=args.resolution,
26
+ )
27
+ else:
28
+ if not args.prompt:
29
+ print("❌ --prompt is required when not using --template")
30
+ sys.exit(1)
31
+
32
+ video = gen.generate(
33
+ prompt=args.prompt,
34
+ avatar=args.avatar,
35
+ mode=args.mode,
36
+ duration=args.duration,
37
+ resolution=args.resolution,
38
+ aspect_ratio=args.aspect_ratio,
39
+ product_url=args.product_url or "",
40
+ wait=not args.no_wait,
41
+ )
42
+
43
+ print(f"\n🎬 UGC Video Generated!")
44
+ print(f" Job ID: {video.job_id}")
45
+ print(f" Status: {video.status}")
46
+ print(f" URL: {video.result_url}")
47
+ if video.local_path:
48
+ print(f" Local: {video.local_path}")
49
+ if video.virality_score:
50
+ print(f" Virality: {video.virality_score}/100")
51
+ print()
52
+
53
+ # Save result
54
+ result_file = Path(gen.output_dir) / f"result_{video.job_id[:8]}.json"
55
+ with open(result_file, "w") as f:
56
+ json.dump(video.to_dict(), f, indent=2)
57
+ print(f" Result saved: {result_file}")
58
+
59
+
60
+ def cmd_score(args):
61
+ """Score a video's virality."""
62
+ gen = UGCGenerator(UGCConfig.from_env())
63
+
64
+ video = args.video
65
+ # If it's a job ID, get the result URL first
66
+ if len(video) < 40 and not video.startswith("http") and not Path(video).exists():
67
+ # It's likely a local file in output dir
68
+ local = Path(gen.output_dir) / video
69
+ if local.exists():
70
+ video = str(local)
71
+
72
+ score = gen.score(video)
73
+
74
+ print(f"\n📊 Virality Score")
75
+ print(f" Overall: {score.overall_score}/100")
76
+ print(f" Hook: {score.hook_score}/100")
77
+ print(f" Viral: {score.viral_potential}/100")
78
+ print(f" Engagement: {score.brain_engagement}/100")
79
+ print(f" Sustain: {score.sustain_score}/100")
80
+ print(f" Peak at: {score.peak_second}s")
81
+ if score.recommendations:
82
+ print(f"\n 💡 Recommendations:")
83
+ for rec in score.recommendations:
84
+ print(f" • {rec}")
85
+ print()
86
+
87
+
88
+ def cmd_avatars(args):
89
+ """List available avatars."""
90
+ gen = UGCGenerator(UGCConfig.from_env())
91
+ avatars = gen.list_avatars()
92
+
93
+ if not avatars:
94
+ # Fallback to config
95
+ print("Available avatars (from config):")
96
+ for name, id in gen.config.avatars.items():
97
+ print(f" {name:12s} {id}")
98
+ else:
99
+ print("Available avatars:")
100
+ for av in avatars:
101
+ name = av.get("name", "?")
102
+ gender = av.get("gender", "?")
103
+ av_id = av.get("id", "?")
104
+ print(f" {name:12s} {gender:6s} {av_id}")
105
+
106
+
107
+ def cmd_products(args):
108
+ """List imported products."""
109
+ gen = UGCGenerator(UGCConfig.from_env())
110
+ products = gen.list_products()
111
+
112
+ if not products:
113
+ print("No products imported yet.")
114
+ else:
115
+ print("Imported products:")
116
+ for p in products:
117
+ title = p.get("title", "?")
118
+ status = p.get("status", "?")
119
+ p_id = p.get("id", "?")[:8]
120
+ print(f" {p_id} {status:12s} {title}")
121
+
122
+
123
+ def cmd_batch(args):
124
+ """Generate videos for multiple products/scenarios."""
125
+ gen = UGCGenerator(UGCConfig.from_env())
126
+
127
+ leads_path = Path(args.leads)
128
+ if not leads_path.exists():
129
+ print(f"❌ File not found: {args.leads}")
130
+ sys.exit(1)
131
+
132
+ with open(leads_path) as f:
133
+ items = json.load(f)
134
+
135
+ print(f"🎬 Batch generation: {len(items)} videos")
136
+ print()
137
+
138
+ results = []
139
+ for i, item in enumerate(items, 1):
140
+ prompt = item.get("prompt", "")
141
+ avatar = item.get("avatar", args.avatar)
142
+ mode = item.get("mode", args.mode)
143
+ product_url = item.get("product_url", "")
144
+
145
+ print(f" [{i}/{len(items)}] Generating: {prompt[:60]}...")
146
+
147
+ try:
148
+ video = gen.generate(
149
+ prompt=prompt,
150
+ avatar=avatar,
151
+ mode=mode,
152
+ duration=args.duration,
153
+ product_url=product_url,
154
+ wait=True,
155
+ )
156
+ results.append(video.to_dict())
157
+ print(f" ✅ Score: {video.virality_score}/100 | {video.result_url[:60]}...")
158
+ except Exception as e:
159
+ print(f" ❌ Failed: {e}")
160
+ results.append({"error": str(e), "prompt": prompt})
161
+
162
+ # Save batch results
163
+ batch_file = Path(gen.output_dir) / f"batch_{int(__import__('time').time())}.json"
164
+ with open(batch_file, "w") as f:
165
+ json.dump(results, f, indent=2)
166
+
167
+ print(f"\n✅ Batch complete! Results: {batch_file}")
168
+
169
+
170
+ def main():
171
+ parser = argparse.ArgumentParser(
172
+ description="Bonanza UGC — AI-generated UGC videos",
173
+ prog="ugc.py",
174
+ )
175
+ sub = parser.add_subparsers(dest="command", help="Commands")
176
+
177
+ # Generate
178
+ p_gen = sub.add_parser("generate", help="Generate a UGC video")
179
+ p_gen.add_argument("--prompt", help="Video prompt")
180
+ p_gen.add_argument("--avatar", default="mei", help="Avatar name or ID (default: mei)")
181
+ p_gen.add_argument("--mode", default="ugc", choices=UGCGenerator.MODES, help="Marketing Studio mode")
182
+ p_gen.add_argument("--duration", type=int, default=15, help="Duration in seconds (10, 15)")
183
+ p_gen.add_argument("--resolution", default="720p", help="Resolution (720p, 1080p)")
184
+ p_gen.add_argument("--aspect-ratio", default="9:16", help="Aspect ratio (9:16, 16:9)")
185
+ p_gen.add_argument("--product-url", help="Product URL to fetch")
186
+ p_gen.add_argument("--template", help="Prompt template (silverjstore)")
187
+ p_gen.add_argument("--product", help="Product name for template")
188
+ p_gen.add_argument("--gender", help="Gender for template")
189
+ p_gen.add_argument("--no-wait", action="store_true", help="Don't wait for completion")
190
+
191
+ # Score
192
+ p_score = sub.add_parser("score", help="Score video virality")
193
+ p_score.add_argument("video", help="Video file path, URL, or job ID")
194
+
195
+ # Avatars
196
+ sub.add_parser("avatars", help="List available avatars")
197
+
198
+ # Products
199
+ sub.add_parser("products", help="List imported products")
200
+
201
+ # Batch
202
+ p_batch = sub.add_parser("batch", help="Batch generate videos")
203
+ p_batch.add_argument("--leads", required=True, help="JSON file with prompts")
204
+ p_batch.add_argument("--avatar", default="mei", help="Default avatar")
205
+ p_batch.add_argument("--mode", default="ugc", help="Default mode")
206
+ p_batch.add_argument("--duration", type=int, default=15, help="Duration")
207
+
208
+ args = parser.parse_args()
209
+
210
+ commands = {
211
+ "generate": cmd_generate,
212
+ "score": cmd_score,
213
+ "avatars": cmd_avatars,
214
+ "products": cmd_products,
215
+ "batch": cmd_batch,
216
+ }
217
+
218
+ if args.command in commands:
219
+ commands[args.command](args)
220
+ else:
221
+ parser.print_help()
222
+
223
+
224
+ if __name__ == "__main__":
225
+ import logging
226
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
227
+ main()
@@ -0,0 +1,75 @@
1
+ """Configuration for Bonanza UGC."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+
9
+
10
+ @dataclass
11
+ class UGCConfig:
12
+ """UGC generation configuration."""
13
+
14
+ # Higgsfield defaults
15
+ default_avatar: str = "44ee57aa" # Mei (female, diverse)
16
+ default_mode: str = "ugc"
17
+ default_duration: int = 15
18
+ default_resolution: str = "720p"
19
+ default_aspect_ratio: str = "9:16"
20
+ default_generate_audio: bool = True
21
+
22
+ # Output
23
+ output_dir: str = str(Path.home() / ".openclaw" / "workspace" / "ugc-output")
24
+
25
+ # Virality scoring
26
+ score_virality: bool = True
27
+ min_virality_score: int = 40 # Warn if below this
28
+
29
+ # Product config (SilverJStore defaults)
30
+ product_base_url: str = "https://silverjstore.com"
31
+
32
+ # Avatar mapping (name → id)
33
+ avatars: dict = None
34
+
35
+ def __post_init__(self):
36
+ if self.avatars is None:
37
+ self.avatars = {
38
+ "mei": "44ee57aa-d1f4-4a0a-a55f-cdf9dacee265",
39
+ "yuna": "24d6d9cc-42df-43c7-bdfc-be06f317b924",
40
+ "adriana": "aa9260cc",
41
+ "clara": "daf4bf2e",
42
+ "sofia": "bba3087a",
43
+ "valentina": "cd6fb78c",
44
+ "jia": "ffc8862b",
45
+ "lily": "cec35719",
46
+ "maria": "bbf8e803",
47
+ "nia": "e6231767",
48
+ "hana": "ed87431a",
49
+ "jayden": "672be390",
50
+ "stefan": "35cd52c0",
51
+ "tae": "6c21ac3e",
52
+ "felix": "83711427",
53
+ "malik": "94950cff",
54
+ "liam": "734451fd",
55
+ "joon": "48b5553f",
56
+ "erik": "e572fd1d",
57
+ "ryu": "e1c17f9c",
58
+ }
59
+
60
+ @classmethod
61
+ def from_env(cls) -> "UGCConfig":
62
+ return cls(
63
+ output_dir=os.getenv("UGC_OUTPUT_DIR", str(Path.home() / ".openclaw" / "workspace" / "ugc-output")),
64
+ )
65
+
66
+ def get_avatar_id(self, name_or_id: str) -> str:
67
+ """Get full avatar ID from name or partial ID."""
68
+ # Already a full UUID
69
+ if len(name_or_id) == 36 and "-" in name_or_id:
70
+ return name_or_id
71
+ # Short ID (8 chars)
72
+ if len(name_or_id) == 8 and name_or_id.isalnum():
73
+ return name_or_id
74
+ # Name lookup
75
+ return self.avatars.get(name_or_id.lower(), name_or_id)
@@ -0,0 +1,387 @@
1
+ """UGC Generator — Generate AI UGC videos via Higgsfield Marketing Studio."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ import subprocess
8
+ import tempfile
9
+ import time
10
+ from dataclasses import dataclass, field
11
+ from pathlib import Path
12
+ from typing import Optional
13
+
14
+ from .config import UGCConfig
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ @dataclass
20
+ class UGCVideo:
21
+ """A generated UGC video."""
22
+ job_id: str = ""
23
+ status: str = "pending"
24
+ result_url: str = ""
25
+ local_path: str = ""
26
+ prompt: str = ""
27
+ mode: str = ""
28
+ avatar: str = ""
29
+ duration: int = 0
30
+ virality_score: int = 0
31
+ brain_activity: dict = field(default_factory=dict)
32
+ product_url: str = ""
33
+ product_id: str = ""
34
+
35
+ def to_dict(self) -> dict:
36
+ return {
37
+ "job_id": self.job_id,
38
+ "status": self.status,
39
+ "result_url": self.result_url,
40
+ "local_path": self.local_path,
41
+ "prompt": self.prompt,
42
+ "mode": self.mode,
43
+ "avatar": self.avatar,
44
+ "duration": self.duration,
45
+ "virality_score": self.virality_score,
46
+ "product_url": self.product_url,
47
+ "product_id": self.product_id,
48
+ }
49
+
50
+
51
+ @dataclass
52
+ class UGCScore:
53
+ """Virality score from Brain Activity."""
54
+ job_id: str = ""
55
+ overall_score: int = 0
56
+ hook_score: int = 0
57
+ viral_potential: int = 0
58
+ brain_engagement: int = 0
59
+ sustain_score: int = 0
60
+ peak_second: int = 0
61
+ regions: dict = field(default_factory=dict)
62
+ recommendations: list = field(default_factory=list)
63
+
64
+ def to_dict(self) -> dict:
65
+ return {
66
+ "overall_score": self.overall_score,
67
+ "hook_score": self.hook_score,
68
+ "viral_potential": self.viral_potential,
69
+ "brain_engagement": self.brain_engagement,
70
+ "sustain_score": self.sustain_score,
71
+ "peak_second": self.peak_second,
72
+ "regions": self.regions,
73
+ "recommendations": self.recommendations,
74
+ }
75
+
76
+
77
+ class UGCGenerator:
78
+ """Generate UGC videos using Higgsfield Marketing Studio.
79
+
80
+ Usage:
81
+ from bonanza_ugc import UGCGenerator, UGCConfig
82
+
83
+ gen = UGCGenerator()
84
+ video = gen.generate(
85
+ prompt="Dutch woman shows off her silver chain necklace...",
86
+ avatar="mei",
87
+ mode="ugc",
88
+ duration=15,
89
+ )
90
+ print(f"Video URL: {video.result_url}")
91
+ print(f"Virality: {video.virality_score}/100")
92
+
93
+ # Score a video
94
+ score = gen.score(video)
95
+ print(f"Hook: {score.hook_score}/100")
96
+ """
97
+
98
+ # Marketing Studio modes
99
+ MODES = [
100
+ "ugc", "ugc_how_to", "ugc_unboxing", "product_showcase",
101
+ "product_review", "tv_spot", "wild_card",
102
+ "ugc_virtual_try_on", "virtual_try_on",
103
+ ]
104
+
105
+ # Product-specific prompt templates
106
+ PROMPT_TEMPLATES = {
107
+ "silverjstore": {
108
+ "ugc": "A stylish young {gender} enthusiastically shows off their beautiful 925 sterling silver {product_name}. They hold it up to the light, talking about how it sparkles and makes them feel elegant and confident. They mention it's affordable luxury from SilverJStore with free shipping across the Netherlands. Natural lighting, warm tone, authentic UGC style.",
109
+ "ugc_unboxing": "Excited {gender} unboxes a package from SilverJStore, revealing a stunning 925 sterling silver {product_name}. They react to the luxury packaging, show the hallmark, and try it on. Authentic unboxing experience, warm lighting.",
110
+ "product_showcase": "Close-up product showcase of a 925 sterling silver {product_name} from SilverJStore. The piece catches the light beautifully, showing the craftsmanship and hallmark. Professional but approachable, 9:16 vertical format.",
111
+ "product_review": "A {gender} gives an honest review of their 925 sterling silver {product_name} from SilverJStore. They show the quality, the hallmark, mention the free shipping and 14-day return policy. Authentic, trustworthy review style.",
112
+ },
113
+ }
114
+
115
+ def __init__(self, config: Optional[UGCConfig] = None):
116
+ self.config = config or UGCConfig()
117
+ self.output_dir = Path(self.config.output_dir)
118
+ self.output_dir.mkdir(parents=True, exist_ok=True)
119
+
120
+ def _higgsfield(self, *args: str, json_output: bool = True) -> dict | list:
121
+ """Run a higgsfield CLI command and return parsed JSON output."""
122
+ cmd = ["higgsfield"] + list(args)
123
+ if json_output:
124
+ cmd.append("--json")
125
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
126
+ if result.returncode != 0:
127
+ logger.error(f"Higgsfield error: {result.stderr}")
128
+ raise RuntimeError(f"Higgsfield command failed: {result.stderr}")
129
+ if json_output and result.stdout.strip():
130
+ return json.loads(result.stdout)
131
+ return {"raw": result.stdout}
132
+
133
+ def _higgsfield_wait(self, job_id: str, timeout: int = 600, interval: int = 15) -> dict:
134
+ """Wait for a Higgsfield job to complete."""
135
+ start = time.time()
136
+ while time.time() - start < timeout:
137
+ result = self._higgsfield("generate", "get", job_id)
138
+ status = result.get("status", "unknown")
139
+ if status == "completed":
140
+ return result
141
+ elif status == "failed":
142
+ raise RuntimeError(f"Job {job_id} failed: {result.get('fail_reason', 'unknown')}")
143
+ logger.info(f"Job {job_id}: {status}...")
144
+ time.sleep(interval)
145
+ raise TimeoutError(f"Job {job_id} timed out after {timeout}s")
146
+
147
+ def generate(
148
+ self,
149
+ prompt: str,
150
+ avatar: str = "mei",
151
+ mode: str = "ugc",
152
+ duration: int = 15,
153
+ resolution: str = "720p",
154
+ aspect_ratio: str = "9:16",
155
+ generate_audio: bool = True,
156
+ product_url: str = "",
157
+ wait: bool = True,
158
+ timeout: int = 600,
159
+ ) -> UGCVideo:
160
+ """Generate a UGC video.
161
+
162
+ Args:
163
+ prompt: Video prompt describing the scene
164
+ avatar: Avatar name (e.g., "mei", "jayden") or ID
165
+ mode: Marketing Studio mode (ugc, ugc_how_to, etc.)
166
+ duration: Video duration in seconds (10, 15)
167
+ resolution: Video resolution (720p, 1080p)
168
+ aspect_ratio: Video aspect ratio (9:16, 16:9, 1:1)
169
+ generate_audio: Whether to generate speech audio
170
+ product_url: Product URL to fetch and include
171
+ wait: Whether to wait for completion
172
+ timeout: Max wait time in seconds
173
+
174
+ Returns:
175
+ UGCVideo with result_url if wait=True
176
+ """
177
+ avatar_id = self.config.get_avatar_id(avatar)
178
+
179
+ # Build command
180
+ cmd_args = [
181
+ "generate", "create", "marketing_studio_video",
182
+ "--prompt", prompt,
183
+ "--mode", mode,
184
+ "--duration", str(duration),
185
+ "--resolution", resolution,
186
+ "--aspect-ratio", aspect_ratio,
187
+ ]
188
+
189
+ if generate_audio:
190
+ cmd_args.extend(["--generate-audio", "true"])
191
+
192
+ # Avatar
193
+ avatar_data = [{"id": avatar_id, "type": "preset"}]
194
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
195
+ json.dump(avatar_data, f)
196
+ avatar_file = f.name
197
+ cmd_args.extend(["--avatars", f"@{avatar_file}"])
198
+
199
+ # Product
200
+ product_id = ""
201
+ if product_url:
202
+ try:
203
+ result = self._higgsfield(
204
+ "marketing-studio", "products", "fetch",
205
+ "--url", product_url, "--json"
206
+ )
207
+ product_id = result.get("id", "")
208
+ if product_id:
209
+ product_data = [product_id]
210
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
211
+ json.dump(product_data, f)
212
+ cmd_args.extend(["--product_ids", f"@{f.name}"])
213
+ except Exception as e:
214
+ logger.warning(f"Failed to fetch product: {e}")
215
+
216
+ # Create job
217
+ if not wait:
218
+ cmd_args.append("--json")
219
+ result = self._higgsfield(*cmd_args[2:]) # skip "higgsfield" prefix
220
+ job_ids = result if isinstance(result, list) else [result]
221
+ video = UGCVideo(
222
+ job_id=job_ids[0] if job_ids else "",
223
+ status="submitted",
224
+ prompt=prompt,
225
+ mode=mode,
226
+ avatar=avatar,
227
+ duration=duration,
228
+ product_url=product_url,
229
+ product_id=product_id,
230
+ )
231
+ return video
232
+
233
+ # Wait for completion
234
+ cmd_args.extend(["--wait", "--wait-timeout", f"{timeout}s"])
235
+ try:
236
+ result = self._higgsfield(*cmd_args[2:])
237
+ except Exception as e:
238
+ logger.error(f"Video generation failed: {e}")
239
+ # Try to get job ID from error
240
+ return UGCVideo(status="failed", prompt=prompt)
241
+
242
+ video = UGCVideo(
243
+ job_id=result.get("id", ""),
244
+ status=result.get("status", "unknown"),
245
+ result_url=result.get("result_url", ""),
246
+ prompt=prompt,
247
+ mode=mode,
248
+ avatar=avatar,
249
+ duration=duration,
250
+ product_url=product_url,
251
+ product_id=product_id,
252
+ )
253
+
254
+ # Download if we have a URL
255
+ if video.result_url:
256
+ local_path = self.download(video.result_url, f"{mode}_{avatar}_{int(time.time())}.mp4")
257
+ video.local_path = str(local_path)
258
+
259
+ # Auto-score virality
260
+ if self.config.score_virality and video.result_url:
261
+ try:
262
+ score = self.score(video)
263
+ video.virality_score = score.overall_score
264
+ video.brain_activity = score.to_dict()
265
+ except Exception as e:
266
+ logger.warning(f"Virality scoring failed: {e}")
267
+
268
+ return video
269
+
270
+ def score(self, video: UGCVideo | str, wait: bool = True) -> UGCScore:
271
+ """Score a video's virality using Brain Activity.
272
+
273
+ Args:
274
+ video: UGCVideo object, video URL, or local file path
275
+ wait: Whether to wait for completion
276
+
277
+ Returns:
278
+ UGCScore with virality metrics
279
+ """
280
+ video_path = ""
281
+ if isinstance(video, UGCVideo):
282
+ video_path = video.local_path or video.result_url
283
+ else:
284
+ video_path = str(video)
285
+
286
+ if not video_path:
287
+ raise ValueError("No video path available")
288
+
289
+ cmd_args = ["generate", "create", "brain_activity", "--video", video_path, "--json"]
290
+
291
+ if wait:
292
+ cmd_args.extend(["--wait", "--wait-timeout", "300s"])
293
+
294
+ result = self._higgsfield(*cmd_args[2:])
295
+
296
+ if wait:
297
+ analysis = result.get("params", {}).get("analysis", {})
298
+ scores = analysis.get("scores", {})
299
+ summary = analysis.get("summary", {})
300
+ regions = analysis.get("regions", [])
301
+
302
+ # Build recommendations
303
+ recommendations = []
304
+ hook = scores.get("hook_score", 0)
305
+ if hook < 50:
306
+ recommendations.append(f"Hook score ({hook}/100) is weak. Start with the product visible in the first second.")
307
+ overall = scores.get("overall_score", 0)
308
+ if overall < 40:
309
+ recommendations.append("Overall score below 40. Try different hooks, angles, or avatar expressions.")
310
+ sustain = scores.get("sustain", 0)
311
+ if sustain < 70:
312
+ recommendations.append("Sustain is low. Add variety — close-ups, different angles, text overlays.")
313
+
314
+ return UGCScore(
315
+ job_id=result.get("id", ""),
316
+ overall_score=scores.get("overall_score", 0),
317
+ hook_score=scores.get("hook_score", 0),
318
+ viral_potential=scores.get("viral_potential", 0),
319
+ brain_engagement=scores.get("brain_engagement", 0),
320
+ sustain_score=scores.get("sustain", 0),
321
+ peak_second=scores.get("peak_second", 0),
322
+ regions={r.get("id", ""): r for r in regions},
323
+ recommendations=recommendations,
324
+ )
325
+ else:
326
+ job_ids = result if isinstance(result, list) else [result]
327
+ return UGCScore(job_id=job_ids[0] if job_ids else "", status="submitted")
328
+
329
+ def download(self, url: str, filename: str = "") -> Path:
330
+ """Download a video from URL to local storage."""
331
+ if not filename:
332
+ filename = f"ugc_{int(time.time())}.mp4"
333
+ local_path = self.output_dir / filename
334
+
335
+ import urllib.request
336
+ urllib.request.urlretrieve(url, str(local_path))
337
+ logger.info(f"Downloaded: {local_path}")
338
+ return local_path
339
+
340
+ def list_avatars(self) -> list[dict]:
341
+ """List all available preset avatars."""
342
+ try:
343
+ return self._higgsfield("marketing-studio", "avatars", "list")
344
+ except Exception:
345
+ return []
346
+
347
+ def list_products(self) -> list[dict]:
348
+ """List all imported products."""
349
+ try:
350
+ return self._higgsfield("marketing-studio", "products", "list")
351
+ except Exception:
352
+ return []
353
+
354
+ def generate_from_template(
355
+ self,
356
+ template: str = "silverjstore",
357
+ product_name: str = "",
358
+ avatar: str = "mei",
359
+ mode: str = "ugc",
360
+ gender: str = "woman",
361
+ **kwargs,
362
+ ) -> UGCVideo:
363
+ """Generate a UGC video from a prompt template.
364
+
365
+ Args:
366
+ template: Template name (currently: "silverjstore")
367
+ product_name: Product name to include in prompt
368
+ avatar: Avatar name or ID
369
+ mode: Marketing Studio mode
370
+ gender: Gender for prompt template
371
+ **kwargs: Additional args passed to generate()
372
+
373
+ Returns:
374
+ UGCVideo
375
+ """
376
+ templates = self.PROMPT_TEMPLATES.get(template, {})
377
+ prompt_template = templates.get(mode, templates.get("ugc", ""))
378
+
379
+ if not prompt_template:
380
+ raise ValueError(f"No template found for '{template}' mode '{mode}'")
381
+
382
+ prompt = prompt_template.format(
383
+ product_name=product_name or "silver jewelry",
384
+ gender=gender,
385
+ )
386
+
387
+ return self.generate(prompt=prompt, avatar=avatar, mode=mode, **kwargs)
@@ -0,0 +1,73 @@
1
+ Metadata-Version: 2.4
2
+ Name: bonanza-ugc
3
+ Version: 0.1.0
4
+ Summary: AI-generated UGC videos via Higgsfield Marketing Studio
5
+ Author: Bonanza Labs
6
+ License-Expression: MIT
7
+ Keywords: ugc,ai,video,higgsfield,marketing
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Multimedia :: Video
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+
18
+ # Bonanza UGC
19
+
20
+ AI-generated UGC videos via Higgsfield Marketing Studio.
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ pip install bonanza-ugc
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ```python
31
+ from bonanza_ugc import UGCGenerator, UGCConfig
32
+
33
+ gen = UGCGenerator()
34
+ video = gen.generate(
35
+ prompt="A stylish young woman shows off her silver chain necklace...",
36
+ avatar="mei",
37
+ mode="ugc",
38
+ duration=15,
39
+ )
40
+ print(f"Video URL: {video.result_url}")
41
+ print(f"Virality: {video.virality_score}/100")
42
+ ```
43
+
44
+ ## CLI
45
+
46
+ ```bash
47
+ # Generate a video
48
+ ugc generate --prompt "..." --avatar mei --mode ugc
49
+
50
+ # Use a template
51
+ ugc generate --template silverjstore --product "Venetian Chain" --avatar mei
52
+
53
+ # Score virality
54
+ ugc score <video_path_or_url>
55
+
56
+ # List avatars
57
+ ugc avatars
58
+
59
+ # Batch generate
60
+ ugc batch --leads leads.json --mode ugc
61
+ ```
62
+
63
+ ## Features
64
+
65
+ - 🎬 Generate UGC videos with AI avatars
66
+ - 📊 Score video virality with Brain Activity analysis
67
+ - 🎯 Prompt templates for product categories
68
+ - 📦 Batch generation support
69
+ - 🔄 Product URL fetching and integration
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ bonanza_ugc/__init__.py
4
+ bonanza_ugc/cli.py
5
+ bonanza_ugc/config.py
6
+ bonanza_ugc/generator.py
7
+ bonanza_ugc.egg-info/PKG-INFO
8
+ bonanza_ugc.egg-info/SOURCES.txt
9
+ bonanza_ugc.egg-info/dependency_links.txt
10
+ bonanza_ugc.egg-info/entry_points.txt
11
+ bonanza_ugc.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ugc = bonanza_ugc.cli:main
@@ -0,0 +1 @@
1
+ bonanza_ugc
@@ -0,0 +1,31 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "bonanza-ugc"
7
+ version = "0.1.0"
8
+ description = "AI-generated UGC videos via Higgsfield Marketing Studio"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "Bonanza Labs" },
14
+ ]
15
+ keywords = ["ugc", "ai", "video", "higgsfield", "marketing"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Multimedia :: Video",
24
+ ]
25
+ dependencies = []
26
+
27
+ [project.scripts]
28
+ ugc = "bonanza_ugc.cli:main"
29
+
30
+ [tool.setuptools.packages.find]
31
+ include = ["bonanza_ugc*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+