Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d87f646
Print the reused bootstrap Python path as UTF-8
KBRASK Oct 13, 2026
8818f5c
Whole-video progress keeps its stage order and is never taken back
KBRASK Oct 13, 2026
fd6ce45
Autopilot conversation: card progress through a cold run, and the nar…
KBRASK Oct 13, 2026
96a135f
Agent conversation: an empty wait says in words to keep waiting
KBRASK Oct 13, 2026
75d58b9
Use device limits for Agent videos under 30 seconds
KBRASK Oct 13, 2026
b2d1d3c
Agent guide: a rough camera draft as an optional way to get a camera …
KBRASK Oct 13, 2026
c3b443a
Agent activity: remove the always-open list nothing shows any more
KBRASK Oct 13, 2026
3dcec1d
Agent: a name is enough to start a new timeline
KBRASK Oct 13, 2026
9fd875b
Editing view: a block can carry a short note, shown on the strip unde…
KBRASK Oct 13, 2026
68e2a4a
Editing view: a saved video put on the tracks brings the description …
KBRASK Oct 13, 2026
8357b7d
Install the patched Diffusers as a regular copy where the Windows cod…
KBRASK Oct 13, 2026
2fbd579
Windows int8: count resident blocks from the measured small-canvas wo…
KBRASK Oct 13, 2026
c892614
Background clean-up check finds the engine on installs where it start…
KBRASK Oct 13, 2026
f87f764
First-run guide: keep the first step's Chinese line together and wait…
KBRASK Oct 13, 2026
9787912
Whole-video progress and the time left are estimated from this comput…
KBRASK Oct 13, 2026
280bc75
Autopilot conversation: a job card's level follows the engine's numbe…
KBRASK Oct 13, 2026
30bc619
A slow download says on the main card that the network is slow and it…
KBRASK Oct 13, 2026
930ccc9
The long waits while an install runs are split into visible steps wit…
KBRASK Oct 13, 2026
3e7bdc4
Page check: wait out a slow startup before reporting that FreeVideo w…
KBRASK Oct 13, 2026
17f12c2
Third-party downloads list only the files this computer needs
KBRASK Oct 13, 2026
c577392
Editing view: a part that continues a finished video is generated at …
KBRASK Oct 13, 2026
b7f3713
A short note under the result says when other programs used a large p…
KBRASK Oct 13, 2026
189c7d8
Extend: image-to-video, first/last-frame and reference videos keep th…
KBRASK Oct 13, 2026
9ee81f4
Extend: a video without stored continuation data is continued from it…
KBRASK Oct 13, 2026
9cdcf43
Extend on a result or a saved video opens the editing view with a dra…
KBRASK Oct 13, 2026
48ea842
AI Autopilot: start a new conversation and return to earlier ones; th…
KBRASK Oct 13, 2026
7fc6887
Release 0.4.1
KBRASK Oct 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions freevideo.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ try {
Write-Host 'Preparing isolated Python for setup. Model/environment installation requires the setup confirmation.'
Write-Host "Bootstrap cache and log: $bootstrapRoot"
$bootstrapLock = [IO.File]::Open((Join-Path $bootstrapRoot 'bootstrap.lock'), 'OpenOrCreate', 'ReadWrite', 'None')
$patientDownloadNotice = $false
try {
$versions = Get-Content -LiteralPath (Join-Path $PSScriptRoot 'freevideo_engine\bootstrap_versions.json') -Raw -Encoding UTF8 | ConvertFrom-Json
$uvSpec = $versions.windows.uv
Expand Down Expand Up @@ -110,6 +111,10 @@ try {
if ($profile.Mode -eq 'patient') {
if (-not $allNetworkFailures) { break }
Write-Host 'The network is slow. Downloading again; this may take a while.'
$patientDownloadNotice = $true
if ($env:FREEVIDEO_UI_EVENTS -eq '1') {
Write-Host '{"event":"freevideo_ui","kind":"progress","key":"download-patient-python","label":"Download","detail":"The network is slow. Downloading again; this may take a while."}'
}
}
foreach ($name in @('UV_HTTP_TIMEOUT', 'UV_HTTP_RETRIES')) {
$value = $freevideoSavedEnvironment[$name]
Expand Down Expand Up @@ -139,6 +144,9 @@ try {
$freevideoPython = (& $uv python find --managed-python $versions.python | Select-Object -Last 1).Trim()
if ($LASTEXITCODE -ne 0 -or -not (Test-Path -LiteralPath $freevideoPython)) { throw 'Managed Python could not be located.' }
} finally {
if ($patientDownloadNotice -and $env:FREEVIDEO_UI_EVENTS -eq '1') {
Write-Host '{"event":"freevideo_ui","kind":"progress","key":"download-patient-python","label":"Download","detail":"","state":"complete"}'
}
$bootstrapLock.Dispose()
Write-Progress -Activity 'FreeVideo bootstrap' -Completed
}
Expand Down
31 changes: 29 additions & 2 deletions freevideo_engine/adaln_reclaim.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

from . import adaln_assets as assets
from .long_paths import io_path
from .variant_cleanup import inside, redirected
from .variant_cleanup import SIDECAR, SIDECAR_LIMIT, inside, redirected

RECEIPT = 'adaln-reclaim.json'
OFFER = 'adaln-reclaim-offer.json'
Expand Down Expand Up @@ -286,6 +286,18 @@ def verify_sources(cache, manifest, should_stop=lambda: False):
return None


def _remove_sidecars(cache, listed):
"""The small records an earlier download left beside a source (<file>.partial.source.json), so the folder can go."""
for _, path in listed:
sidecar = path.with_name(path.name + SIDECAR)
info = _regular(sidecar)
if info is not None and info.st_size <= SIDECAR_LIMIT and _contained(cache, sidecar):
try:
io_path(sidecar).unlink()
except OSError:
pass


def _remove_tables(cache, folders):
removed = 0
for folder, _ in folders:
Expand Down Expand Up @@ -373,6 +385,7 @@ def reclaim(cache, *, should_stop=lambda: False, schedules=None, lock=None, ask=
return kept('redirected')
freed += info.st_size
io_path(path).unlink()
_remove_sidecars(cache, listed)
try:
(io_path(cache) / SOURCES).rmdir()
except OSError:
Expand Down Expand Up @@ -423,11 +436,25 @@ def apply(machine, *, should_stop=lambda: False, schedules=None):
return result


def _installed_engine_schedules(machine):
"""engine_schedules for a process that has not put the engine's dependencies on its path (the background check)."""
ready = []
def answer(steps):
if not ready:
from . import paths
vdn = Path(machine['root']) / 'vendor' / 'vdn' if machine.get('root') else None
paths.add_vdn(vdn if vdn is not None and (vdn / 'src/models/hybrid_attention.py').is_file() else None)
ready.append(True)
return engine_schedules(steps)
return answer


def run(machine, *, should_stop=lambda: False):
"""Entry point of the background precompile: check and leave the offer, report one event, never raise."""
started = time.time()
try:
result = reclaim(machine['cache'], should_stop=should_stop, ask=True, release=installed_release())
result = reclaim(machine['cache'], should_stop=should_stop, ask=True, release=installed_release(),
schedules=_installed_engine_schedules(machine))
except Exception as error: # Best effort: the installation works either way.
result = dict(action='kept', reason='error:' + type(error).__name__, bytes=0, tables_bytes=0)
if result['action'] == 'kept' and result['reason'] not in ('declined', 'stopped', 'busy') \
Expand Down
1 change: 1 addition & 0 deletions freevideo_engine/agent/editor_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
case 'move': record = actions.moveParts(record, op.clip_ids, {frames: op.frames, track: null}); break;
case 'describe': record = model.describe(record, actions.linkedIds(record, op.clip_ids), op.text, by); break;
case 'set_ref': record = model.setRef(record, actions.linkedIds(record, op.clip_ids), op.ref); break;
case 'note': record = model.setNote(record, actions.linkedIds(record, op.clip_ids), op.text); break;
case 'use_version': record = model.restoreVersion(record, op.clip_id, op.index, by); break;
// One undo on the page; rejects as subtitles / range / text / locked / overlap.
case 'subtitles': record = texts.addSubtitles(record, op.cues, {replace: op.replace === true}); break;
Expand Down
2 changes: 1 addition & 1 deletion freevideo_engine/agent/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
'internal_error': (500, True, 'An internal error prevented this operation from completing.', 'Read the job list and report request_id if the error persists.'),
'local_only': (403, False, 'This endpoint does not allow requests from this peer address.', 'Run the client on the FreeVideo computer and connect using its loopback address.'),
'origin_forbidden': (403, False, 'Origin must match this FreeVideo instance; a different origin was received.', 'Open the page served by this FreeVideo instance, or use a local command-line client without Origin.'),
'continuation_unavailable': (409, False, 'This backend cannot continue video blocks.', 'Use an independent shot with seconds<=15, or enable a backend with continuation inputs and resume.'),
'continuation_unavailable': (409, False, 'This backend cannot continue video blocks.', 'Use POST /plan for a supported single block (unknown limit: seconds<=15), or enable continuation and resume.'),
'not_resumable': (409, False, 'Only failed, interrupted or cancelled generation jobs can resume.', 'Read the job; wait for active jobs or submit a new job for a different request.'),
'idempotency_conflict': (409, False, 'This idempotency key was already used for different request content.', 'Resend the original content with this key, or use a new ref for the changed request.'),
'source_missing': (409, False, 'A completed source block is no longer available.', 'Restore the completed block before resuming, or submit a new job.'),
Expand Down
7 changes: 4 additions & 3 deletions freevideo_engine/agent/forecast.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Machine-local timing forecasts shared by planning and durable films."""
import math
from .length import capability_table, single_frame_limit


def generation_estimate(port, spec, chain=None):
Expand All @@ -9,9 +10,9 @@ def generation_estimate(port, spec, chain=None):
reasons here so a film never invents a time for unsupported conditioning.
"""
from ..chain_plan import plan as chain_plan
chain = chain if chain is not None else chain_plan(spec['seconds'], continued=bool(spec.get('continue_from')))
table = port.capability_snapshot(dict(width=str(spec['width']), height=str(spec['height']),
task=spec['task'], adapters='1' if spec['loras'] else '0'))
table = capability_table(port, spec)
chain = chain if chain is not None else chain_plan(spec['seconds'], max_frames=single_frame_limit(spec, table=table),
continued=bool(spec.get('continue_from')))
seconds = None
reason = table['status'] if table['status'] in ('computing', 'unavailable') else 'no_matching_estimate'
if table['status'] == 'ready' and not spec['preview'] and not spec['media']:
Expand Down
Loading