Merge branch 'jobs/instagram-repost/ai-description'
Some checks failed
Push image to registry / build-image (push) Failing after 1m23s
Some checks failed
Push image to registry / build-image (push) Failing after 1m23s
This commit is contained in:
@ -33,7 +33,7 @@ abstract class BrowserJob implements ShouldQueue
|
||||
|
||||
public int $jobId;
|
||||
|
||||
public $timeout = 500;
|
||||
public $timeout = 300; // 5 minutes
|
||||
|
||||
public function __construct(int $jobId)
|
||||
{
|
||||
@ -53,6 +53,7 @@ abstract class BrowserJob implements ShouldQueue
|
||||
|
||||
$this->browse(function (Browser $browser) use ($callback, &$log) {
|
||||
try {
|
||||
$browser->driver->manage()->timeouts()->implicitlyWait(20);
|
||||
$log = $callback($browser);
|
||||
// } catch (Exception $e) {
|
||||
// $browser->screenshot("failure-{$this->jobId}");
|
||||
@ -160,7 +161,7 @@ abstract class BrowserJob implements ShouldQueue
|
||||
'--disable-setuid-sandbox',
|
||||
'--whitelisted-ips=""',
|
||||
'--disable-dev-shm-usage',
|
||||
'--user-data-dir=/home/seluser/profile/',
|
||||
'--user-data-dir=/home/seluser/profile/nigga/', // seems that selenium doesn't like docker having a volume on the exact same folder ("session not created: probably user data directory is already in use")
|
||||
])->all());
|
||||
|
||||
return RemoteWebDriver::create(
|
||||
@ -169,6 +170,13 @@ abstract class BrowserJob implements ShouldQueue
|
||||
ChromeOptions::CAPABILITY,
|
||||
$options
|
||||
)
|
||||
->setCapability('timeouts', [
|
||||
'implicit' => 20000, // 20 seconds
|
||||
'pageLoad' => 300000, // 5 minutes
|
||||
'script' => 30000, // 30 seconds
|
||||
]),
|
||||
4000,
|
||||
$this->timeout * 1000
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -17,11 +17,14 @@ use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Laravel\Dusk\Browser;
|
||||
use App\Services\AIPrompt\OpenAPIPrompt;
|
||||
|
||||
class InstagramRepostJob extends BrowserJob implements ShouldBeUniqueUntilProcessing
|
||||
{
|
||||
// === CONFIGURATION ===
|
||||
|
||||
public $timeout = 1800; // 30 minutes
|
||||
|
||||
private const APPROXIMATIVE_RUNNING_MINUTES = 2;
|
||||
|
||||
private Collection $jobInfos;
|
||||
@ -29,6 +32,10 @@ class InstagramRepostJob extends BrowserJob implements ShouldBeUniqueUntilProces
|
||||
|
||||
protected IInstagramVideoDownloader $videoDownloader;
|
||||
|
||||
protected ReelDescriptor $ReelDescriptor;
|
||||
|
||||
protected OpenAPIPrompt $openAPIPrompt;
|
||||
|
||||
protected string $downloadFolder = "app/Browser/downloads/InstagramRepost/";
|
||||
|
||||
/**
|
||||
@ -40,12 +47,14 @@ class InstagramRepostJob extends BrowserJob implements ShouldBeUniqueUntilProces
|
||||
*/
|
||||
protected InstagramDescriptionPipeline $descriptionPipeline;
|
||||
|
||||
public function __construct($jobId = 4)
|
||||
public function __construct($jobId = 4, ReelDescriptor $ReelDescriptor = null, OpenAPIPrompt $openAPIPrompt = null)
|
||||
{
|
||||
parent::__construct($jobId);
|
||||
|
||||
$this->downloadFolder = base_path($this->downloadFolder);
|
||||
$this->videoDownloader = new YTDLPDownloader();
|
||||
$this->ReelDescriptor = $ReelDescriptor ?? app(ReelDescriptor::class);
|
||||
$this->openAPIPrompt = $openAPIPrompt ?? app(OpenAPIPrompt::class);
|
||||
$this->descriptionPipeline = new InstagramDescriptionPipeline([
|
||||
// Add steps to the pipeline here
|
||||
new DescriptionPipeline\RemoveAccountsReferenceStep(),
|
||||
@ -152,13 +161,17 @@ class InstagramRepostJob extends BrowserJob implements ShouldBeUniqueUntilProces
|
||||
*/
|
||||
$downloadedReels = [];
|
||||
foreach ($toDownloadReels as $repost) {
|
||||
$downloadInfos = $this->downloadReel(
|
||||
$browser,
|
||||
$repost
|
||||
);
|
||||
|
||||
$downloadedReels[] = [
|
||||
$repost,
|
||||
$this->downloadReel(
|
||||
$browser,
|
||||
$repost
|
||||
)
|
||||
$downloadInfos
|
||||
];
|
||||
|
||||
$this->describeReel($repost, $downloadInfos);
|
||||
}
|
||||
|
||||
$this->jobRun->addArtifact(new JobArtifact([
|
||||
@ -282,6 +295,15 @@ class InstagramRepostJob extends BrowserJob implements ShouldBeUniqueUntilProces
|
||||
return $videoInfo;
|
||||
}
|
||||
|
||||
protected function describeReel(InstagramRepost $reel, IInstagramVideo $videoInfo): void
|
||||
{
|
||||
// Set the video description to the reel description
|
||||
$reel->video_description = $this->ReelDescriptor->getDescription($videoInfo->getFilename());
|
||||
$reel->save();
|
||||
|
||||
Log::info("Reel description set: {$reel->reel_id} - {$reel->video_description}");
|
||||
}
|
||||
|
||||
protected function repostReel(Browser $browser, InstagramRepost $reel, IInstagramVideo $videoInfo): bool
|
||||
{
|
||||
try {
|
||||
@ -321,16 +343,17 @@ class InstagramRepostJob extends BrowserJob implements ShouldBeUniqueUntilProces
|
||||
$this->clickNext($browser); // Skip cover photo and trim
|
||||
|
||||
// Add a caption
|
||||
$captionText = $this->descriptionPipeline->process($videoInfo->getDescription());
|
||||
$captionText = $this->descriptionPipeline->process($this->getReelCaption($reel, $videoInfo));
|
||||
$this->pasteText($browser, $captionText, 'div[contenteditable]');
|
||||
|
||||
sleep(2); // Wait for the caption to be added
|
||||
|
||||
if (config("app.environment") !== "local") { // Don't share the post in local environment
|
||||
if (config("app.env") !== "local") { // Don't share the post in local environment
|
||||
$this->clickNext($browser); // Share the post
|
||||
}
|
||||
|
||||
sleep(5); // Wait for the post to be completed
|
||||
sleep(7); // Wait for the post to be completed
|
||||
$this->removePopups($browser);
|
||||
|
||||
// Check if the post was successful
|
||||
try {
|
||||
@ -364,6 +387,56 @@ class InstagramRepostJob extends BrowserJob implements ShouldBeUniqueUntilProces
|
||||
}
|
||||
}
|
||||
|
||||
private function getReelCaption(InstagramRepost $reel, IInstagramVideo $videoInfo): string
|
||||
{
|
||||
if (isset($reel->instagram_caption)) {
|
||||
return $reel->instagram_caption;
|
||||
}
|
||||
|
||||
// Get the reel description from the database or the video info
|
||||
$reelDescription = $reel->video_description;
|
||||
$originalDescription = $videoInfo->getDescription();
|
||||
$llmAnswer = $this->openAPIPrompt->generate(
|
||||
config('llm.models.chat.name'),
|
||||
"Original Caption: {$originalDescription}
|
||||
Video Description/Directive: {$reelDescription}",
|
||||
[],
|
||||
outputFormat: '{"type": "object", "properties": {"answer": {"type": "string"}}, "required": ["answer"]}',
|
||||
systemMessage: "You are an AI assistant specialized in creating engaging and concise Instagram Reel captions. Your primary task is to transform the provided original caption (often from Twitter) and description/directions into a fresh, unique, but still relevant caption for Instagram Reels format.
|
||||
|
||||
Key instructions:
|
||||
1. **Analyze Input:** You will receive two things: an *original reel caption* (usually starting with \"credit:\" or mentioning a Twitter handle like `t/TwitterUser`), and either a *video description* or explicit directions about the joke/idea behind the video.
|
||||
2. **Transform, Don't Reproduce:** Your output must be significantly different from the original provided caption. It should capture the essence of the content described but phrase it anew – often with humor if appropriate.
|
||||
3. **Keep it Short & Punchy:** Instagram Reels thrive on quick engagement. Prioritize brevity (ideally under two lines, or three lines max) and impact. Make sure your caption is concise enough for fast-scroll viewing.
|
||||
4. **Maintain the Core Idea:** The new caption must directly relate to the video's content/direction/joke without simply restating it like a description would. Focus on what makes the reel *interesting* or *funny* in its own right.
|
||||
5. **Preserve Original Credit (Optional):** If an explicit \"credit\" line is provided, you may incorporate this into your new caption naturally, perhaps using `(via...)` or similar phrasing if it fits well and doesn't sound awkward. **Do not** include any original Instagram account mentions (@handles). They are often intended for promotion which isn't our goal.
|
||||
6. **Use Emoji Judiciously:** Incorporate relevant emojis to enhance the tone (funny, relatable, etc.) or add visual interest. Use them purposefully and in moderation – they should complement the caption, not overwhelm it.
|
||||
7. **Add Hashtags (Optional but Recommended):** Generate a few relevant Instagram hashtags automatically at the end of your output to increase visibility. Keep these organic to the content and avoid forcing irrelevant tags.
|
||||
|
||||
Your response structure is as follows:
|
||||
- The generated caption (your core answer).
|
||||
- Then, if you generate any hashtags, list them on the next line(s) prefixed with `#`.
|
||||
|
||||
Example Input Structure:
|
||||
Original Caption: credit: t/otherhandle This banana is looking fly today!
|
||||
Video Description/Directive: A man walks into a store holding a banana and wearing sunglasses. He looks around confidently before leaving.
|
||||
|
||||
Your answer should only contain the generated caption, and optionally hashtags if relevant.
|
||||
|
||||
Remember to be creative and ensure the generated caption feels like something you would see naturally on an Instagram Reel. Aim for personality and relevance.
|
||||
",
|
||||
keepAlive: true,
|
||||
shouldThink: config('llm.models.chat.shouldThink')
|
||||
);
|
||||
$llmAnswer = json_decode($llmAnswer, true)['answer'] ?? null;
|
||||
if ($llmAnswer !== null) {
|
||||
$reel->instagram_caption = $llmAnswer;
|
||||
$reel->save();
|
||||
Log::info("Reel caption generated: {$reel->reel_id} - {$llmAnswer}");
|
||||
}
|
||||
return $llmAnswer;
|
||||
}
|
||||
|
||||
private function clickNext(Browser $browser) {
|
||||
$nextButton = $browser->driver->findElement(WebDriverBy::xpath('//div[contains(text(), "Next") or contains(text(), "Share")]'));
|
||||
$nextButton->click();
|
||||
|
11
app/Browser/Jobs/InstagramRepost/ReelDescriptor.php
Normal file
11
app/Browser/Jobs/InstagramRepost/ReelDescriptor.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Browser\Jobs\InstagramRepost;
|
||||
|
||||
use App\Services\AIPrompt\OpenAPIPrompt;
|
||||
use App\Services\FileTools\OCR\IImageOCR;
|
||||
|
||||
class ReelDescriptor extends \App\Services\FileTools\VideoDescriptor\OCRLLMVideoDescriptor
|
||||
{
|
||||
public const DESCRIPTION_PROMPT = "Analyze this Instagram Reel sequence. You are given information for each individual screenshot/analysis from the video:";
|
||||
}
|
Reference in New Issue
Block a user