| |
| |
| param( |
| [Parameter(Mandatory=$true)] |
| [string]$StoryFile |
| ) |
|
|
| $storyPath = Join-Path ".\stories" $StoryFile |
| if (-not (Test-Path $storyPath)) { |
| Write-Host "Story not found: $storyPath" -ForegroundColor Red |
| return |
| } |
|
|
| $story = Get-Content $storyPath -Raw | ConvertFrom-Json |
| $storyStart = Get-Date |
|
|
| Write-Host "========================================" -ForegroundColor Cyan |
| Write-Host " STORY: $($story.story_title)" -ForegroundColor Cyan |
| Write-Host " Pages: $($story.pages.Count)" -ForegroundColor Cyan |
| Write-Host "========================================" -ForegroundColor Cyan |
|
|
| $storyOutput = ".\output\stories\$($story.story_id)" |
| New-Item -Path $storyOutput -ItemType Directory -Force | Out-Null |
|
|
| $pageNum = 0 |
| foreach ($page in $story.pages) { |
| $pageNum++ |
| Write-Host " |
| ========================================" -ForegroundColor Yellow |
| Write-Host " PAGE $pageNum/$($story.pages.Count): $($page.page_title)" -ForegroundColor Yellow |
| Write-Host " Template: $($page.page_template) | Panels: $($page.panels.Count)" -ForegroundColor Yellow |
| Write-Host "========================================" -ForegroundColor Yellow |
|
|
| |
| $pageScene = @{ |
| scene_id = "$($story.story_id)_p$pageNum" |
| page_template = $page.page_template |
| panels = @() |
| global_settings = $story.global_settings |
| } |
|
|
| foreach ($panel in $page.panels) { |
| $pageScene.panels += @{ |
| panel_id = $panel.panel_id |
| base_prompt = $story.base_prompt |
| seed = $story.base_seed |
| edit_prompt = $panel.edit_prompt |
| } |
| } |
|
|
| $tempFile = "_story_p$pageNum.json" |
| $pageScene | ConvertTo-Json -Depth 5 | Set-Content ".\scenes\$tempFile" |
|
|
| |
| .\run_comic_pipeline.ps1 -SceneFile $tempFile |
|
|
| |
| $pageFile = Get-ChildItem ".\output\stage3_pages" -Filter "page_$($story.story_id)_p$pageNum*" | |
| Sort-Object LastWriteTime -Descending | Select-Object -First 1 |
|
|
| if ($pageFile) { |
| $destName = "page_$($pageNum.ToString('D2'))_$($page.page_title -replace ' ','_').png" |
| Copy-Item $pageFile.FullName -Destination (Join-Path $storyOutput $destName) -Force |
| Write-Host " Saved: $destName" -ForegroundColor Green |
| } |
|
|
| [System.Console]::Beep(800, 200) |
| } |
|
|
| |
| Write-Host " |
| Adding titles..." -ForegroundColor Yellow |
| $pageFiles = Get-ChildItem $storyOutput -Filter "page_*.png" | Sort-Object Name |
|
|
| foreach ($pf in $pageFiles) { |
| $titleMatch = $pf.Name -match 'page_\d+_(.*?)\.png' |
| if ($titleMatch) { |
| $title = ($Matches[1] -replace '_',' ').ToUpper() |
| } else { |
| $title = "PAGE" |
| } |
|
|
| python -c " |
| from PIL import Image, ImageDraw, ImageFont |
| import math |
| img = Image.open(r'$($pf.FullName)') |
| pw, ph = img.size |
| bar_h = 140 |
| new = Image.new('RGB', (pw, ph+bar_h), (10,8,20)) |
| new.paste(img, (0, bar_h)) |
| draw = ImageDraw.Draw(new) |
| font = ImageFont.truetype('C:/Windows/Fonts/impact.ttf', 90) |
| title = '$title' |
| bbox = draw.textbbox((0,0), title, font=font) |
| tw, th = bbox[2]-bbox[0], bbox[3]-bbox[1] |
| cx, cy = pw//2, bar_h//2 |
| sweep, steps = 0.95, 100 |
| r_top, r_bot = 220, 120 |
| tp, bp = [], [] |
| for i in range(steps+1): |
| t=i/steps; a=math.pi+(1-sweep)/2*math.pi+t*sweep*math.pi |
| tp.append((cx+int(pw*0.55*math.cos(a)), cy-15+r_top*math.sin(a))) |
| bp.append((cx+int(pw*0.55*math.cos(a)), cy+15+r_bot*math.sin(a))) |
| bp.reverse() |
| draw.polygon(tp+bp, fill=(100,10,10), outline=(0,0,0)) |
| itp, ibp = [], [] |
| for i in range(steps+1): |
| t=i/steps; a=math.pi+(1-sweep)/2*math.pi+t*sweep*math.pi |
| itp.append((cx+int(pw*0.53*math.cos(a)), cy-15+(r_top-10)*math.sin(a))) |
| ibp.append((cx+int(pw*0.53*math.cos(a)), cy+15+(r_bot+10)*math.sin(a))) |
| ibp.reverse() |
| draw.polygon(itp+ibp, fill=(190,20,20)) |
| chars=list(title); cws=[] |
| for c in chars: b=draw.textbbox((0,0),c,font=font); cws.append(b[2]-b[0]) |
| sp=8; ts=sum(cws)+sp*(len(chars)-1); cur=cx-ts//2 |
| for i,c in enumerate(chars): |
| cw=cws[i]; ccx=cur+cw//2 |
| t2=(ccx-cx)/(ts/2+1); yo=int(25*t2*t2); ad=-t2*9 |
| tmp=Image.new('RGBA',(cw+30,th+30),(0,0,0,0)); td=ImageDraw.Draw(tmp) |
| for dx in [-4,-3,-2,0,2,3,4]: |
| for dy in [-4,-3,-2,0,2,3,4]: |
| if dx or dy: td.text((15+dx,15+dy),c,fill=(0,0,0,255),font=font) |
| td.text((15,15),c,fill=(255,215,0,255),font=font) |
| tmp=tmp.rotate(ad,expand=True,resample=Image.BICUBIC) |
| new.paste(tmp,(int(ccx-tmp.width//2),int(cy-th//2-10+yo)),tmp) |
| cur+=cw+sp |
| new.save(r'$($pf.FullName)') |
| print('Titled: $($pf.Name)') |
| " |
| } |
|
|
| |
| Write-Host " |
| Building PDF..." -ForegroundColor Yellow |
| $sortedPages = (Get-ChildItem $storyOutput -Filter "page_*.png" | Sort-Object Name | ForEach-Object { $_.FullName -replace '\\','/' }) |
| $pyList = ($sortedPages | ForEach-Object { "'$_'" }) -join "," |
|
|
| python -c " |
| from PIL import Image |
| pages = [$pyList] |
| imgs = [Image.open(p).convert('RGB') for p in pages] |
| out = '$($storyOutput -replace '\\','/')/$($story.story_id)_complete.pdf' |
| imgs[0].save(out, save_all=True, append_images=imgs[1:], resolution=150) |
| print('PDF: ' + out + ' (' + str(len(imgs)) + ' pages)') |
| " |
|
|
| |
| Remove-Item ".\scenes\_story_*.json" -Force -ErrorAction SilentlyContinue |
|
|
| $totalMin = [math]::Round(((Get-Date)-$storyStart).TotalMinutes, 1) |
| Write-Host " |
| ========================================" -ForegroundColor Green |
| Write-Host " STORY COMPLETE: $($story.story_title)" -ForegroundColor Green |
| Write-Host " Pages: $($story.pages.Count)" -ForegroundColor Green |
| Write-Host " Time: $totalMin min" -ForegroundColor Green |
| Write-Host " Output: $storyOutput" -ForegroundColor Green |
| Write-Host "========================================" -ForegroundColor Green |
|
|
| 1000, 1200, 1500, 2000 | ForEach-Object { [System.Console]::Beep($_, 250) }
|
|
|