"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderCommand = exports.RENDER_COMMAND = void 0;
const path_1 = __importDefault(require("path"));
const cli_1 = require("@remotion/cli");
const config_1 = require("@remotion/cli/config");
const lambda_client_1 = require("@remotion/lambda-client");
const constants_1 = require("@remotion/lambda-client/constants");
const renderer_1 = require("@remotion/renderer");
const client_1 = require("@remotion/renderer/client");
const serverless_1 = require("@remotion/serverless");
const no_react_1 = require("remotion/no-react");
const download_media_1 = require("../../../api/download-media");
const sleep_1 = require("../../../shared/sleep");
const validate_retries_1 = require("../../../shared/validate-retries");
const args_1 = require("../../args");
const get_aws_region_1 = require("../../get-aws-region");
const find_function_name_1 = require("../../helpers/find-function-name");
const get_webhook_custom_data_1 = require("../../helpers/get-webhook-custom-data");
const quit_1 = require("../../helpers/quit");
const log_1 = require("../../log");
const progress_1 = require("./progress");
exports.RENDER_COMMAND = 'render';
const { x264Option, audioBitrateOption, offthreadVideoCacheSizeInBytesOption, offthreadVideoThreadsOption, scaleOption, crfOption, gopSizeOption, jpegQualityOption, videoBitrateOption, mutedOption, colorSpaceOption, deleteAfterOption, enableMultiprocessOnLinuxOption, glOption, headlessOption, numberOfGifLoopsOption, encodingMaxRateOption, encodingBufferSizeOption, delayRenderTimeoutInMillisecondsOption, overwriteOption, binariesDirectoryOption, preferLosslessOption, metadataOption, mediaCacheSizeInBytesOption, darkModeOption, pixelFormatOption, browserExecutableOption, everyNthFrameOption, proResProfileOption, userAgentOption, disableWebSecurityOption, ignoreCertificateErrorsOption, audioCodecOption, videoCodecOption, overrideHeightOption, overrideWidthOption, overrideFpsOption, overrideDurationOption, sampleRateOption, } = client_1.BrowserSafeApis.options;
const renderCommand = async ({ args, remotionRoot, logLevel, providerSpecifics, }) => {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
    const serveUrl = args[0];
    if (!serveUrl) {
        log_1.Log.error({ indent: false, logLevel }, 'No serve URL passed.');
        log_1.Log.info({ indent: false, logLevel }, 'Pass an additional argument specifying a URL where your Remotion project is hosted.');
        log_1.Log.info({ indent: false, logLevel });
        log_1.Log.info({ indent: false, logLevel }, `${constants_1.BINARY_NAME} ${exports.RENDER_COMMAND} <serve-url> <composition-id> [output-location]`);
        (0, quit_1.quit)(1);
    }
    const region = (0, get_aws_region_1.getAwsRegion)();
    const { envVariables, frameRange, inputProps } = cli_1.CliInternals.getCliOptions({
        isStill: false,
        logLevel,
        indent: false,
    });
    const height = overrideHeightOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const width = overrideWidthOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const fps = overrideFpsOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const durationInFrames = overrideDurationOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const pixelFormat = pixelFormatOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const browserExecutable = browserExecutableOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const everyNthFrame = everyNthFrameOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const proResProfile = proResProfileOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const userAgent = userAgentOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const disableWebSecurity = disableWebSecurityOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const ignoreCertificateErrors = ignoreCertificateErrorsOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const x264Preset = x264Option.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const audioBitrate = audioBitrateOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const offthreadVideoCacheSizeInBytes = offthreadVideoCacheSizeInBytesOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const mediaCacheSizeInBytes = mediaCacheSizeInBytesOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const offthreadVideoThreads = offthreadVideoThreadsOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const scale = scaleOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const crf = crfOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const gopSize = gopSizeOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const jpegQuality = jpegQualityOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const videoBitrate = videoBitrateOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const muted = mutedOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const colorSpace = colorSpaceOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const deleteAfter = deleteAfterOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const enableMultiProcessOnLinux = enableMultiprocessOnLinuxOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const gl = glOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const headless = headlessOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const numberOfGifLoops = numberOfGifLoopsOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const encodingMaxRate = encodingMaxRateOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const encodingBufferSize = encodingBufferSizeOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const timeoutInMilliseconds = delayRenderTimeoutInMillisecondsOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const overwrite = overwriteOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }, false).value;
    const binariesDirectory = binariesDirectoryOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const preferLossless = preferLosslessOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const metadata = metadataOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const sampleRate = sampleRateOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const darkMode = darkModeOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const audioCodec = audioCodecOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }).value;
    const chromiumOptions = {
        disableWebSecurity,
        enableMultiProcessOnLinux,
        gl,
        headless,
        ignoreCertificateErrors,
        userAgent,
        darkMode,
    };
    let composition = args[1];
    if (!composition) {
        log_1.Log.info({ indent: false, logLevel }, 'No compositions passed. Fetching compositions...');
        lambda_client_1.LambdaClientInternals.validateServeUrl(serveUrl);
        if (!serveUrl.startsWith('https://') && !serveUrl.startsWith('http://')) {
            throw Error('Passing the shorthand serve URL without composition name is currently not supported.\n Make sure to pass a composition name after the shorthand serve URL or pass the complete serveURL without composition name to get to choose between all compositions.');
        }
        const server = await renderer_1.RenderInternals.prepareServer({
            offthreadVideoThreads: 1,
            indent: false,
            port: config_1.ConfigInternals.getRendererPortFromConfigFileAndCliFlag(),
            remotionRoot,
            logLevel,
            webpackConfigOrServeUrl: serveUrl,
            offthreadVideoCacheSizeInBytes,
            binariesDirectory,
            forceIPv4: false,
            sampleRate: 48000,
        });
        const indent = false;
        const { compositionId } = await cli_1.CliInternals.getCompositionWithDimensionOverride({
            args: args.slice(1),
            compositionIdFromUi: null,
            browserExecutable,
            chromiumOptions,
            envVariables,
            height,
            width,
            fps,
            durationInFrames,
            indent,
            serializedInputPropsWithCustomSchema: no_react_1.NoReactInternals.serializeJSONWithSpecialTypes({
                indent: undefined,
                staticBase: null,
                data: inputProps,
            }).serializedString,
            port: config_1.ConfigInternals.getRendererPortFromConfigFileAndCliFlag(),
            puppeteerInstance: undefined,
            serveUrlOrWebpackUrl: serveUrl,
            timeoutInMilliseconds,
            logLevel,
            server,
            offthreadVideoCacheSizeInBytes,
            offthreadVideoThreads,
            binariesDirectory,
            onBrowserDownload: cli_1.CliInternals.defaultBrowserDownloadProgress({
                indent,
                logLevel,
                quiet: cli_1.CliInternals.quietFlagProvided(),
                onProgress: () => undefined,
            }),
            chromeMode: 'headless-shell',
            mediaCacheSizeInBytes: mediaCacheSizeInBytes,
        });
        composition = compositionId;
    }
    const outName = args_1.parsedLambdaCli['out-name'];
    const downloadName = (_a = args[2]) !== null && _a !== void 0 ? _a : null;
    const { value: codec, source: reason } = videoCodecOption.getValue({
        commandLine: cli_1.CliInternals.parsedCli,
    }, {
        downloadName,
        outName: outName !== null && outName !== void 0 ? outName : null,
        configFile: (_b = config_1.ConfigInternals.getOutputCodecOrUndefined()) !== null && _b !== void 0 ? _b : null,
        uiCodec: null,
        compositionCodec: null,
    });
    const imageFormat = cli_1.CliInternals.getVideoImageFormat({
        codec,
        uiImageFormat: null,
    });
    const functionName = await (0, find_function_name_1.findFunctionName)({ logLevel, providerSpecifics });
    const maxRetries = (_c = args_1.parsedLambdaCli['max-retries']) !== null && _c !== void 0 ? _c : constants_1.DEFAULT_MAX_RETRIES;
    (0, validate_retries_1.validateMaxRetries)(maxRetries);
    const privacy = (_d = args_1.parsedLambdaCli.privacy) !== null && _d !== void 0 ? _d : constants_1.DEFAULT_OUTPUT_PRIVACY;
    (0, serverless_1.validatePrivacy)(privacy, true);
    const framesPerLambda = (_e = args_1.parsedLambdaCli['frames-per-lambda']) !== null && _e !== void 0 ? _e : undefined;
    const concurrency = (_f = args_1.parsedLambdaCli['concurrency']) !== null && _f !== void 0 ? _f : undefined;
    const concurrencyPerLambda = (_g = args_1.parsedLambdaCli['concurrency-per-lambda']) !== null && _g !== void 0 ? _g : 1;
    const webhookCustomData = (0, get_webhook_custom_data_1.getWebhookCustomData)(logLevel);
    const res = await lambda_client_1.LambdaClientInternals.internalRenderMediaOnLambdaRaw({
        functionName,
        serveUrl,
        inputProps,
        codec: codec,
        imageFormat,
        crf: crf !== null && crf !== void 0 ? crf : undefined,
        gopSize,
        envVariables,
        pixelFormat,
        proResProfile,
        jpegQuality,
        region,
        maxRetries,
        composition,
        framesPerLambda: framesPerLambda !== null && framesPerLambda !== void 0 ? framesPerLambda : null,
        concurrency: concurrency !== null && concurrency !== void 0 ? concurrency : null,
        privacy,
        logLevel,
        frameRange: frameRange !== null && frameRange !== void 0 ? frameRange : null,
        outName: (_h = args_1.parsedLambdaCli['out-name']) !== null && _h !== void 0 ? _h : null,
        timeoutInMilliseconds,
        chromiumOptions,
        scale,
        numberOfGifLoops,
        everyNthFrame,
        concurrencyPerLambda,
        muted,
        overwrite,
        audioBitrate,
        videoBitrate,
        encodingBufferSize,
        encodingMaxRate,
        forceHeight: height,
        forceWidth: width,
        forceFps: fps,
        forceDurationInFrames: durationInFrames,
        webhook: args_1.parsedLambdaCli.webhook
            ? {
                url: args_1.parsedLambdaCli.webhook,
                secret: (_j = args_1.parsedLambdaCli['webhook-secret']) !== null && _j !== void 0 ? _j : null,
                customData: webhookCustomData,
            }
            : null,
        rendererFunctionName: (_k = args_1.parsedLambdaCli['renderer-function-name']) !== null && _k !== void 0 ? _k : null,
        forceBucketName: (_l = args_1.parsedLambdaCli['force-bucket-name']) !== null && _l !== void 0 ? _l : null,
        audioCodec,
        deleteAfter: deleteAfter !== null && deleteAfter !== void 0 ? deleteAfter : null,
        colorSpace,
        downloadBehavior: { type: 'play-in-browser' },
        offthreadVideoCacheSizeInBytes: offthreadVideoCacheSizeInBytes !== null && offthreadVideoCacheSizeInBytes !== void 0 ? offthreadVideoCacheSizeInBytes : null,
        mediaCacheSizeInBytes: mediaCacheSizeInBytes !== null && mediaCacheSizeInBytes !== void 0 ? mediaCacheSizeInBytes : null,
        offthreadVideoThreads: offthreadVideoThreads !== null && offthreadVideoThreads !== void 0 ? offthreadVideoThreads : null,
        x264Preset: x264Preset !== null && x264Preset !== void 0 ? x264Preset : null,
        preferLossless,
        indent: false,
        forcePathStyle: (_m = args_1.parsedLambdaCli['force-path-style']) !== null && _m !== void 0 ? _m : false,
        metadata: metadata !== null && metadata !== void 0 ? metadata : null,
        licenseKey: (_o = args_1.parsedLambdaCli[client_1.BrowserSafeApis.options.licenseKeyOption.cliFlag]) !== null && _o !== void 0 ? _o : null,
        storageClass: (_p = args_1.parsedLambdaCli['storage-class']) !== null && _p !== void 0 ? _p : null,
        requestHandler: null,
        isProduction: (_q = args_1.parsedLambdaCli[client_1.BrowserSafeApis.options.isProductionOption.cliFlag]) !== null && _q !== void 0 ? _q : true,
        sampleRate,
    });
    const progressBar = cli_1.CliInternals.createOverwriteableCliOutput({
        quiet: cli_1.CliInternals.quietFlagProvided(),
        cancelSignal: null,
        // No browser logs in Lambda
        updatesDontOverwrite: false,
        indent: false,
        logLevel,
    });
    log_1.Log.info({ indent: false, logLevel }, cli_1.CliInternals.chalk.gray(`Bucket: ${cli_1.CliInternals.makeHyperlink({ text: res.bucketName, fallback: res.bucketName, url: `https://${(0, get_aws_region_1.getAwsRegion)()}.console.aws.amazon.com/s3/buckets/${res.bucketName}/?region=${(0, get_aws_region_1.getAwsRegion)()}` })}`));
    log_1.Log.info({ indent: false, logLevel }, cli_1.CliInternals.chalk.gray(`Function: ${cli_1.CliInternals.makeHyperlink({ text: functionName, fallback: functionName, url: `https://${(0, get_aws_region_1.getAwsRegion)()}.console.aws.amazon.com/lambda/home#/functions/${functionName}?tab=code` })}`));
    log_1.Log.info({ indent: false, logLevel }, cli_1.CliInternals.chalk.gray(`Render ID: ${cli_1.CliInternals.makeHyperlink({ text: res.renderId, fallback: res.renderId, url: res.folderInS3Console })}`));
    log_1.Log.info({ indent: false, logLevel }, cli_1.CliInternals.chalk.gray(`progress.json: ${cli_1.CliInternals.makeHyperlink({
        text: (clickInstruction) => {
            return `${clickInstruction} to view`;
        },
        fallback: res.progressJsonInConsole,
        url: res.progressJsonInConsole,
    })}`));
    log_1.Log.info({
        indent: false,
        logLevel,
    }, cli_1.CliInternals.chalk.gray(`${cli_1.CliInternals.makeHyperlink({
        text: 'Codec',
        fallback: 'Codec',
        url: 'https://remotion.dev/docs/encoding',
    })}: ${codec} (${reason})`));
    log_1.Log.verbose({ indent: false, logLevel }, 'CloudWatch logs (if enabled):', cli_1.CliInternals.makeHyperlink({
        text: `Main function`,
        url: res.cloudWatchMainLogs,
        fallback: res.cloudWatchMainLogs,
    }), cli_1.CliInternals.makeHyperlink({
        text: `Renderer functions`,
        url: res.cloudWatchLogs,
        fallback: res.cloudWatchLogs,
    }));
    log_1.Log.verbose({ indent: false, logLevel }, 'Lambda insights: (if enabled):', cli_1.CliInternals.makeHyperlink({
        text: (instruction) => `${instruction} to view`,
        url: res.lambdaInsightsLogs,
        fallback: res.lambdaInsightsLogs,
    }));
    if (!cli_1.CliInternals.supportsHyperlink()) {
        log_1.Log.verbose({ indent: false, logLevel }, cli_1.CliInternals.makeHyperlink({
            text: (instruction) => `${instruction} for Render folder`,
            url: res.folderInS3Console,
            fallback: `Render folder: ${res.folderInS3Console}`,
        }));
    }
    const adheresToFunctionNameConvention = lambda_client_1.LambdaClientInternals.parseFunctionName(functionName);
    const status = await (0, lambda_client_1.getRenderProgress)({
        functionName,
        bucketName: res.bucketName,
        renderId: res.renderId,
        region: (0, get_aws_region_1.getAwsRegion)(),
        logLevel,
        skipLambdaInvocation: Boolean(adheresToFunctionNameConvention),
    });
    progressBar.update((0, progress_1.makeProgressString)({
        downloadInfo: null,
        overall: status,
    }), false);
    // eslint-disable-next-line no-constant-condition
    while (true) {
        await (0, sleep_1.sleep)(500);
        const newStatus = await (0, lambda_client_1.getRenderProgress)({
            functionName,
            bucketName: res.bucketName,
            renderId: res.renderId,
            region: (0, get_aws_region_1.getAwsRegion)(),
            logLevel,
        });
        progressBar.update((0, progress_1.makeProgressString)({
            downloadInfo: null,
            overall: newStatus,
        }), false);
        if (newStatus.done) {
            let downloadOrNothing;
            if (downloadName) {
                const downloadStart = Date.now();
                const download = await (0, download_media_1.internalDownloadMedia)({
                    bucketName: res.bucketName,
                    outPath: downloadName,
                    region: (0, get_aws_region_1.getAwsRegion)(),
                    renderId: res.renderId,
                    logLevel,
                    onProgress: ({ downloaded, totalSize }) => {
                        progressBar.update((0, progress_1.makeProgressString)({
                            downloadInfo: {
                                doneIn: null,
                                downloaded,
                                totalSize,
                            },
                            overall: newStatus,
                        }), false);
                    },
                    providerSpecifics: providerSpecifics,
                    forcePathStyle: args_1.parsedLambdaCli['force-path-style'],
                    signal: new AbortController().signal,
                    customCredentials: null,
                    requestHandler: null,
                });
                downloadOrNothing = download;
                progressBar.update((0, progress_1.makeProgressString)({
                    downloadInfo: {
                        doneIn: Date.now() - downloadStart,
                        downloaded: download.sizeInBytes,
                        totalSize: download.sizeInBytes,
                    },
                    overall: newStatus,
                }), false);
            }
            log_1.Log.info({ indent: false, logLevel });
            log_1.Log.info({ indent: false, logLevel }, cli_1.CliInternals.chalk.blue('+ S3 '.padEnd(cli_1.CliInternals.LABEL_WIDTH)), cli_1.CliInternals.chalk.blue(cli_1.CliInternals.makeHyperlink({
                fallback: newStatus.outputFile,
                text: newStatus.outKey,
                url: newStatus.outputFile,
            })), cli_1.CliInternals.chalk.gray(cli_1.CliInternals.formatBytes(newStatus.outputSizeInBytes)));
            if (downloadOrNothing) {
                const relativeOutputPath = path_1.default.relative(process.cwd(), downloadOrNothing.outputPath);
                log_1.Log.info({ indent: false, logLevel }, cli_1.CliInternals.chalk.blue('↓'.padEnd(cli_1.CliInternals.LABEL_WIDTH)), cli_1.CliInternals.chalk.blue(cli_1.CliInternals.makeHyperlink({
                    url: `file://${downloadOrNothing.outputPath}`,
                    text: relativeOutputPath,
                    fallback: downloadOrNothing.outputPath,
                })), cli_1.CliInternals.chalk.gray(cli_1.CliInternals.formatBytes(downloadOrNothing.sizeInBytes)));
            }
            log_1.Log.info({ indent: false, logLevel });
            log_1.Log.info({ indent: false, logLevel }, [
                newStatus.renderMetadata
                    ? `${newStatus.renderMetadata.estimatedTotalLambdaInvokations} λ's used`
                    : null,
                newStatus.timeToFinish
                    ? `${(newStatus.timeToFinish / 1000).toFixed(2)}sec`
                    : null,
                `Estimated cost ${newStatus.costs.displayCost}`,
            ]
                .filter(Boolean)
                .join(', '));
            if (newStatus.mostExpensiveFrameRanges) {
                log_1.Log.verbose({ indent: false, logLevel }, 'Most expensive frame ranges:');
                log_1.Log.verbose({ indent: false, logLevel }, newStatus.mostExpensiveFrameRanges
                    .map((f) => {
                    return `${f.frameRange[0]}-${f.frameRange[1]} (Chunk ${f.chunk}, ${f.timeInMilliseconds}ms)`;
                })
                    .join(', '));
            }
            (0, quit_1.quit)(0);
        }
        if (newStatus.fatalErrorEncountered) {
            log_1.Log.error({ indent: false, logLevel }, '\n');
            const uniqueErrors = [];
            for (const err of newStatus.errors) {
                if (uniqueErrors.find((e) => e.stack === err.stack)) {
                    continue;
                }
                uniqueErrors.push(err);
                if (err.explanation) {
                    log_1.Log.error({ indent: false, logLevel }, err.explanation);
                }
                const frames = renderer_1.RenderInternals.parseStack(err.stack.split('\n'));
                const errorWithStackFrame = new renderer_1.RenderInternals.SymbolicateableError({
                    message: err.message,
                    frame: err.frame,
                    name: err.name,
                    stack: err.stack,
                    stackFrame: frames,
                    chunk: err.chunk,
                });
                await cli_1.CliInternals.printError(errorWithStackFrame, logLevel);
            }
            log_1.Log.info({ indent: false, logLevel });
            log_1.Log.info({ indent: false, logLevel }, `Accrued costs until error was thrown: ${newStatus.costs.displayCost}.`);
            log_1.Log.info({ indent: false, logLevel }, 'This is an estimate and continuing Lambda functions may incur additional costs.');
            (0, quit_1.quit)(1);
        }
    }
};
exports.renderCommand = renderCommand;
