asset computeワーカーの開発

asset computeワーカーは、Asset computeプロジェクトの中心で、新しいレンディションを作成するためにアセットで実行される作業を実行または編成するカスタム機能を提供します。

asset computeプロジェクトは、アセットの元のバイナリを名前付きのレンディションに変換せずにコピーする、単純なワーカーを自動生成します。 このチュートリアルでは、このワーカーを変更して、より興味深いレンディションを作成し、Asset computeワーカーの力を説明します。

asset computeのぼやけたバージョンのアセットで、アセットレンディションの左右の空き領域をカバーする新しい水平画像レンディションを生成するアセットワーカーを作成します。 最終的なレンディションの幅、高さ、ぼかしがパラメーター化されます。

asset computeワーカー呼び出しの論理フロー

asset computeワーカーは、Asset computeSDKワーカーAPI契約を、概念上次のようなrenditionCallback(...)関数に実装します。

  • 入力: AEMアセットの元のバイナリパラメーターと処理プロファイルパラメーター
  • 出力: AEMアセットに追加する1つ以上のレンディション

asset computeワーカーの論理フロー

  1. AEMオーサーサービスは、Asset computeの​(1a)​元のバイナリ(sourceパラメーター)と、処理プロファイル(rendition.instructionsパラメーター)で定義された​(1b)​パラメーターを提供して、アセットワーカーを呼び出します。

  2. asset computeSDKは、カスタムAsset computeメタデータワーカーのrenditionCallback(...)関数の実行を編成し、アセットの元のバイナリ​(1a)​とパラメーター​(1b)​に基づいて新しいバイナリレンディションを生成します。

    • このチュートリアルでは、レンディションは「処理中」に作成されます。つまり、ワーカーがレンディションを構成しますが、レンディション生成用に他のWebサービスAPIにソースバイナリを送信することもできます。
  3. asset computeワーカーは、新しいレンディションのバイナリデータをrendition.pathに保存します。

  4. rendition.pathに書き込まれたバイナリデータは、Asset computeSDKを介してAEMオーサーサービスに転送され、(4a)​テキストレンディションおよび​(4b)​としてアセットのメタデータノードに公開されます。

上の図は、開発者が直面するAsset computeの問題と、Asset computeワーカーの呼び出しへの論理的な流れを示しています。 興味深いことに、Asset computeの実行の内部詳細は利用可能ですが、パブリックAsset computeSDK APIの契約のみに依存できます。

作業者の分析

すべてのAsset computeワーカーは、同じ基本構造と入出力契約に従います。

'use strict';

// Any npm module imports used by the worker
const { worker, SourceCorruptError } = require('@adobe/asset-compute-sdk');
const fs = require('fs').promises;

/**
Exports the worker implemented by a custom rendition callback function, which parametrizes the input/output contract for the worker.
 + `source` represents the asset's original binary used as the input for the worker.
 + `rendition` represents the worker's output, which is the creation of a new asset rendition.
 + `params` are optional parameters, which map to additional key/value pairs, including a sub `auth` object that contains Adobe I/O access credentials.
**/
exports.main = worker(async (source, rendition, params) => {
    // Perform any necessary source (input) checks
    const stats = await fs.stat(source.path);
    if (stats.size === 0) {
        // Throw appropriate errors whenever an erring condition is met
        throw new SourceCorruptError('source file is empty');
    }

    // Access any custom parameters provided via the Processing Profile configuration
    let param1 = rendition.instructions.exampleParam;

    /** 
    Perform all work needed to transform the source into the rendition.
    
    The source data can be accessed:
        + In the worker via a file available at `source.path`
        + Or via a presigned GET URL at `source.url`
    **/
    if (success) {
        // A successful worker must write some data back to `renditions.path`. 
        // This example performs a trivial 1:1 copy of the source binary to the rendition
        await fs.copyFile(source.path, rendition.path);
    } else {
        // Upon failure an Asset Compute Error (exported by @adobe/asset-compute-commons) should be thrown.
        throw new GenericError("An error occurred!", "example-worker");
    }
});

/**
Optionally create helper classes or functions the worker's rendition callback function invokes to help organize code.

Code shared across workers, or to complex to be managed in a single file, can be broken out across supporting JavaScript files in the project and imported normally into the worker. 
**/
function customHelperFunctions() { ... }

ワーカーindex.jsを開く

自動生成されたindex.js

  1. VS CodeでAsset computeプロジェクトが開いていることを確認します。
  2. /actions/workerフォルダーに移動します。
  3. index.jsファイルを開きます。

これは、このチュートリアルで変更するワーカーJavaScriptファイルです。

サポートされているnpmモジュールのインストールとインポート

Node.jsベースのAsset computeプロジェクトは、堅牢なnpmモジュールエコシステムのメリットを享受できます。 npmモジュールを活用するには、まずnpmモジュールをAsset computeプロジェクトにインストールする必要があります。

このワーカーでは、jimpを利用して、Node.jsコードで直接レンディション画像を作成および操作します。

警告

アセット操作用のnpmモジュールの一部が、Asset computeでサポートされているわけではありません。 ImageMagickなどのアプリケーションの存在に依存するnpmモジュールや、その他のOS依存ライブラリはサポートされていません。 JavaScriptのみのnpmモジュールの使用を制限することをお勧めします。

  1. asset computeプロジェクトのルートにあるコマンドラインを開き(ターミナル/新しいターミナル​を使用してVSコードで実行できます)、次のコマンドを実行します。

    $ npm install jimp
    
  2. jimpモジュールをワーカーコードにインポートし、Jimp JavaScriptオブジェクト経由で使用できるようにします。
    ワーカーのindex.jsの先頭にあるrequireディレクティブを更新し、jimpモジュールからJimpオブジェクトをインポートします。

    'use strict';
    
    const Jimp = require('jimp');
    const { worker, SourceCorruptError } = require('@adobe/asset-compute-sdk');
    const fs = require('fs').promises;
    
    exports.main = worker(async (source, rendition, params) => {
        // Check handle a corrupt input source
        const stats = await fs.stat(source.path);
        if (stats.size === 0) {
            throw new SourceCorruptError('source file is empty');
        }
    
        // Do work here
    });
    

パラメーターの読み取り

asset computeワーカーは、AEM as a Processing Authorサービスで定義された処理プロファイルを介して渡すことができるCloud Serviceーを読み取ることができます。 パラメーターは、rendition.instructionsオブジェクトを介してワーカーに渡されます。

これらは、ワーカーコードのrendition.instructions.<parameterName>にアクセスすることで読み取ることができます。

ここでは、設定可能なレンディションのSIZEBRIGHTNESSおよびCONTRASTを読み取り、処理プロファイル経由でデフォルト値が提供されていない場合のデフォルト値を提供します。 AEMからCloud Service処理プロファイルとして呼び出されると、renditions.instructionsは文字列として渡されるので、ワーカーコード内の正しいデータ型に変換されていることを確認してください。

'use strict';

const Jimp = require('jimp');
const { worker, SourceCorruptError } = require('@adobe/asset-compute-sdk');
const fs = require('fs').promises;

exports.main = worker(async (source, rendition, params) => {
    const stats = await fs.stat(source.path);
    if (stats.size === 0) {
        throw new SourceCorruptError('source file is empty');
    }

    // Read in parameters and set defaults if parameters are provided
    // Processing Profiles pass in instructions as Strings, so make sure to parse to correct data types
    const SIZE = parseInt(rendition.instructions.size) || 800; 
    const CONTRAST = parseFloat(rendition.instructions.contrast) || 0;
    const BRIGHTNESS = parseFloat(rendition.instructions.brightness) || 0;

    // Do work here
}

エラーのスロー

asset computeワーカーは、エラーを引き起こす状況が発生する場合があります。 AdobeAsset computeSDKには、事前に定義されたエラーのスイートが用意されています。このエラーは、このような状況が発生した場合にスローされます。 特定のエラータイプが適用されない場合は、GenericErrorを使用するか、特定のカスタムClientErrorsを定義できます。

レンディションの処理を開始する前に、このワーカーのコンテキストですべてのパラメーターが有効でサポートされていることを確認します。

  • SIZECONTRASTおよびBRIGHTNESSのレンディション指示パラメーターが有効であることを確認します。 そうでない場合は、カスタムエラーRenditionInstructionsErrorをスローします。
    • ClientErrorを拡張するカスタムのRenditionInstructionsErrorクラスが、このファイルの最下部で定義されます。 特定のカスタムエラーを使用すると、ワーカーのテスト🔗を書き込む場合に役立ちます。
'use strict';

const Jimp = require('jimp');
// Import the Asset Compute SDK provided `ClientError` 
const { worker, SourceCorruptError, ClientError } = require('@adobe/asset-compute-sdk');
const fs = require('fs').promises;

exports.main = worker(async (source, rendition, params) => {
    const stats = await fs.stat(source.path);
    if (stats.size === 0) {
        throw new SourceCorruptError('source file is empty');
    }

    // Read in parameters and set defaults if parameters are provided
    const SIZE = parseInt(rendition.instructions.size) || 800; 
    const CONTRAST = parseFloat(rendition.instructions.contrast) || 0;
    const BRIGHTNESS = parseFloat(rendition.instructions.brightness) || 0;

    if (SIZE <= 10 || SIZE >= 10000) {
        // Ensure size is within allowable bounds
        throw new RenditionInstructionsError("'size' must be between 10 and 1,0000");
    } else if (CONTRAST <= -1 || CONTRAST >= 1) {
        // Ensure contrast is valid value
        throw new RenditionInstructionsError("'contrast' must between -1 and 1");
    } else if (BRIGHTNESS <= -1 || BRIGHTNESS >= 1) {
        // Ensure contrast is valid value
        throw new RenditionInstructionsError("'brightness' must between -1 and 1");
    }

    // Do work here
}

// Create a new ClientError to handle invalid rendition.instructions values
class RenditionInstructionsError extends ClientError {
    constructor(message) {
        // Provide a:
        // + message: describing the nature of this erring condition
        // + name: the name of the error; usually same as class name
        // + reason: a short, searchable, unique error token that identifies this error
        super(message, "RenditionInstructionsError", "rendition_instructions_error");

        // Capture the strack trace
        Error.captureStackTrace(this, RenditionInstructionsError);
    }
}

レンディションの作成

読み取られ、不要部分が削除され、検証されたパラメーターを使用して、コードが書き込まれ、レンディションが生成されます。 レンディション生成の擬似コードを次に示します。

  1. sizeパラメーターで指定された正方形の寸法で新しいrenditionImageキャンバスを作成します。
  2. ソースアセットのバイナリからimageオブジェクトを作成する
  3. Jimp​ライブラリを使用して画像を変換します。
    • 元の画像を中央の四角形に切り抜く
    • 「2乗」画像の中心から円を切り取る
    • SIZEパラメータ値で定義された寸法内に収まるように拡大/縮小
    • CONTRASTパラメーター値に基づいてコントラストを調整する
    • BRIGHTNESSパラメータ値に基づいて明るさを調整する
  4. 変換後のimageを、背景が透明なrenditionImageの中心に配置します。
  5. 構成済みのrenditionImagerendition.pathに書き込み、アセットレンディションとしてAEMに保存し直すことができます。

このコードでは、Jimp APIを使用して、これらの画像変換を実行します。

asset computeワーカーは同期的に作業を完了する必要があり、rendition.pathはワーカーのrenditionCallbackが完了する前に完全にに書き戻す必要があります。 そのためには、await演算子を使用して非同期関数呼び出しが同期的におこなわれる必要があります。 JavaScriptの非同期関数と、それらを同期的に実行する方法について詳しくない場合は、JavaScriptのawait演算子について理解してください。

完成したワーカーindex.jsは次のようになります。

'use strict';

const Jimp = require('jimp');
const { worker, SourceCorruptError, ClientError } = require('@adobe/asset-compute-sdk');
const fs = require('fs').promises;

exports.main = worker(async (source, rendition, params) => {
    const stats = await fs.stat(source.path);
    if (stats.size === 0) {
        throw new SourceCorruptError('source file is empty');
    }

    // Read/parse and validate parameters
    const SIZE = parseInt(rendition.instructions.size) || 800; 
    const CONTRAST = parseFloat(rendition.instructions.contrast) || 0;
    const BRIGHTNESS = parseFloat(rendition.instructions.brightness) || 0;

    if (SIZE <= 10 || SIZE >= 10000) {
        throw new RenditionInstructionsError("'size' must be between 10 and 1,0000");
    } else if (CONTRAST <= -1 || CONTRAST >= 1) {
        throw new RenditionInstructionsError("'contrast' must between -1 and 1");
    } else if (BRIGHTNESS <= -1 || BRIGHTNESS >= 1) {
        throw new RenditionInstructionsError("'brightness' must between -1 and 1");
    }

    // Create target rendition image 
    let renditionImage =  new Jimp(SIZE, SIZE, 0x0);

    // Read and perform transformations on the source binary image
    let image = await Jimp.read(source.path);

    // Crop a circle from the source asset, and then apply contrast and brightness
    image.crop(
            image.bitmap.width < image.bitmap.height ? 0 : (image.bitmap.width - image.bitmap.height) / 2,
            image.bitmap.width < image.bitmap.height ? (image.bitmap.height - image.bitmap.width) / 2 : 0,
            image.bitmap.width < image.bitmap.height ? image.bitmap.width : image.bitmap.height,
            image.bitmap.width < image.bitmap.height ? image.bitmap.width : image.bitmap.height
        )   
        .circle()
        .scaleToFit(SIZE, SIZE)
        .contrast(CONTRAST)
        .brightness(BRIGHTNESS);

    // Place the transformed image onto the transparent renditionImage to save as PNG
    renditionImage.composite(image, 0, 0)

    // Write the final transformed image to the asset's rendition
    await renditionImage.writeAsync(rendition.path);
});

// Custom error used for renditions.instructions parameter checking
class RenditionInstructionsError extends ClientError {
    constructor(message) {
        super(message, "RenditionInstructionsError", "rendition_instructions_error");
        Error.captureStackTrace(this, RenditionInstructionsError);
    }
}

ワーカーの実行

これで、ワーカーコードが完了し、以前にmanifest.ymlに登録および設定されたので、ローカルAsset compute開発ツールを使用して実行し、結果を確認できます。

  1. asset computeプロジェクトのルート

  2. 実行 aio app run

  3. asset compute開発ツールが新しいウィンドウで開くのを待つ

  4. ファイルを選択…​ドロップダウンで、処理するサンプル画像を選択します。

    • ソースアセットバイナリとして使用するサンプル画像ファイルを選択します
    • まだ存在しない場合は、左側の​(+)​をタップし、サンプル画像ファイルをアップロードして、開発ツールブラウザーウィンドウを更新します
  5. "name": "rendition.png"をこのワーカーとして更新し、透明なPNGを生成します。

    • この「name」パラメーターは開発ツールにのみ使用され、依存しないでください。
    {
        "renditions": [
            {
                "worker": "...",
                "name": "rendition.png"
            }
        ]
    }
    
  6. ​を実行」をタップし、レンディションが生成されるのを待ちます。

  7. レンディション​セクションで、生成されたレンディションのプレビューを表示します。 レンディションのプレビューをタップして、レンディション全体をダウンロードします。

    デフォルトのPNGレンディション

パラメータを指定して作業者を実行

処理プロファイル設定を介して渡されるパラメーターは、レンディションパラメーターJSONにキーと値のペアとして提供することで、Asset compute開発ツールでシミュレートできます。

警告

ローカル開発時に、様々なデータ型を使用して値を渡すことができます。AEMからCloud Service処理プロファイルに文字列として渡す場合は、必要に応じて正しいデータ型が解析されるようにします。
例えば、Jimpのcrop(width, height)関数のパラメーターはintである必要があります。parseInt(rendition.instructions.size)がintに解析されない場合、jimp.crop(SIZE, SIZE)への呼び出しは失敗します。これは、パラメーターが「String」型と互換性がないためです。

アドビのコードは次のパラメーターを受け取ります。

  • size レンディションのサイズを定義します(高さと幅を整数で指定)。
  • contrast コントラスト調整を定義します。-1 ~ 1の範囲を浮動小数点として指定する必要があります。
  • brightness 明るい調整を定義します。-1から1の間である必要があります。

これらは、次を介してワーカーindex.jsに読み込まれます。

  • const SIZE = parseInt(rendition.instructions.size) || 800
  • const CONTRAST = parseFloat(rendition.instructions.contrast) || 0
  • const BRIGHTNESS = parseFloat(rendition.instructions.brightness) || 0
  1. レンディションパラメーターを更新して、サイズ、コントラスト、明るさをカスタマイズします。

    {
        "renditions": [
            {
                "worker": "...",
                "name": "rendition.png",
                "size": "450",
                "contrast": "0.30",
                "brightness": "0.15"
            }
        ]
    }
    
  2. もう一度​​を実行」をタップします

  3. レンディションのプレビューをタップして、生成されたレンディションをダウンロードし、確認します。 デフォルトのレンディションと比較して、サイズとコントラストと明るさがどのように変更されたかを確認します。

    パラメーター化PNGレンディション

  4. ソースファイル​ドロップダウンに他の画像をアップロードし、異なるパラメーターでワーカーを実行してみてください。

GithubのWorker index.js

最終的なindex.jsは、GitHubで次の場所から入手できます。

トラブルシューティング

このページ