---
title: Ejemplo de grupo de recursos de BrightSign
slug: develop/es/ejemplo-de-grupo-de-recursos-de-brightsign
docTags: 
createdAt: 2025-03-11T07:07:19.526Z
---

Este script muestra cómo se puede usar BrightSign Asset Pool desde JavaScript. Funciona tanto desde NodeJS como desde Chromium. No está pensado como una aplicación completa y no hace nada con los assets que se descargan.

```javascript
// MIT License
//
// Copyright (c) 2020 BrightSign
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

// Customise these settings for your environment
const storagePath = "/storage/sd/";
const poolPath = storagePath + "examplePool";
const serverPrefix = "https://github.com/brightsign/javascript-assetpoolfetcher/blob/video-training/";

const fs = require('fs');
const AssetPool = require("@brightsign/assetpool");
const AssetPoolFiles = require("@brightsign/assetpoolfiles");
const AssetFetcher = require("@brightsign/assetfetcher");
const AssetRealizer = require("@brightsign/assetrealizer");

// Create the asset collection array to be used by the rest of the
// program. The asset collection will usually come either directly or
// indirectly from a server somewhere rather than being generated in
// code like this.
function makeAssetCollection()
{
    // Assets must have a name and a link. Everything else is
    // optional. You can add your own properties if required. For
    // example, the "osUpdate" property here is only used by this
    // script.
    let videoAsset1 = {
	name: "brightlink.mp4",
	hash: { method: "SHA256",
		hex: "82e5e800ff4b2608ae996b6c9fce78c0d84b82cba0da270251846324f48fb076" },
	link: serverPrefix + "brightlink%201min.mp4?raw=true",
	size: 17219989,
    };

    let videoAsset2 = {
	name: "CMScontrol.mp4",
	hash: { method: "SHA512",
		hex: "d476296b114fda612917b1568a9f7ffe6f54f3f927b2d1ecc932816e912619eeef980e1adc16d8b1d7914d3f172cd6a2ef17172ea3251ddcdbb3d151faa6818f" },
	link: serverPrefix + "CMS%2BControl%201%20min.mp4?raw=true",
	size: 5633877,
    };

    // We don't want to risk actually changing the OS version on the
    // BrightSign running this example cript, so this is not a real
    // BrightSignOS file. It will be realized into the root of the
    // storage device, but the OS will fail to find the expected
    // header and rename it to placeholder.bsfw_invalid.
    let osUpdateAsset = {
	name: "placeholder.bsfw",
	hash: { method: "SHA1", hex: "20e89c9ba0491590e3c34bc704171c0c02e643c3" },
	link: serverPrefix + "placeholder.bsfw?raw=true",
	size: 43,
	osUpdate: true,
    };

    let assetCollection = [
	videoAsset1,
	videoAsset2,
	osUpdateAsset,
    ];

    return assetCollection;
}

// Convert a progress event to a useful string for reporting
function progressString(event)
{
    if (event.currentFileTotal === undefined) {
	// If the size of the asset was not specified in the asset collection, then the total size may not be reported
	// during the fetch.
	return event.currentFileTransferred.toString() + " of unknown";
    } else {
	return event.currentFileTransferred.toString() + " of " + event.currentFileTotal.toString() + " "
	    + (100*event.currentFileTransferred / event.currentFileTotal).toFixed(0) + "%";
    }
}

// Download any assets that aren't already in the pool into the pool
// whilst reporting progress.
async function fetchAssets(assetPool, assetCollection)
{
    console.log("Fetch: " + JSON.stringify(assetCollection.map(asset => asset.name)));

    let assetFetcher = new AssetFetcher(assetPool);

    assetFetcher.addEventListener("fileevent", (event) => {
	// This is called each time the fetcher has finished trying to
	// download an asset, whether successful or not. It is not
	// called for any assets that are already in the pool.
	console.log("ASSET [" + (event.index + 1).toString() + "] "
		    + event.fileName + " complete: " + event.responseCode.toString() + " " + event.error);
    });

    assetFetcher.addEventListener("progressevent", (event) => {
	// This is called at approximately the progress interval
	// specified in the options to indicate how far through the
	// download
	console.log("ASSET [" + (event.index + 1).toString() + "/" + event.total.toString() + "] " + event.fileName
		    + " progress: " + progressString(event));
    });

    const fetchOptions = {
	// receive asset progress events about every five seconds.
	progressInterval: 5,
	// try to download each asset three times before giving up.
	fileRetryCount: 3,
	// Give up if we fail to download at least 1024 bytes in each
	// ten second period.
	minimumTransferRate: { bytesPerSecond: 1024, periodInSeconds: 10 },
    };

    try {
	await assetFetcher.start(assetCollection, fetchOptions);
    }
    catch (err) {
	console.log("FETCH FAILED: " + err.message);
	throw(err);
    }
}

// In order to make use of an asset from the pool you need to look up
// its pool filename so you can refer to it there.
async function useAssets(assetPool, assetCollection)
{
    let files = new AssetPoolFiles(assetPool, assetCollection);

    for (const fileName of [ 'brightlink.mp4', 'CMScontrol.mp4' ]) {
	const path = await files.getPath(fileName);
	console.log("Asset " + fileName + " is at " + path);
    }
}

// Some files need to appear in the filesystem outside the pool. For
// example, BrightSignOS update files must be written to the root of
// a storage device for them to be found. Realizing will copy files,
// so can be slow on large files.
async function realizeAssets(assetPool, assetCollection)
{
    let realizer = new AssetRealizer(assetPool, storagePath);

    // We only want to realize the files that we have to
    const assetsToRealize = assetCollection.filter(asset => asset.osUpdate);
    console.log("Realize: " + JSON.stringify(assetsToRealize.map(asset => asset.name)));
    await realizer.realize(assetsToRealize);
}

function ensureDirectoryExists(path) {
    try {
	fs.mkdirSync(path);
    } catch (err)
    {
	if (err.code != 'EEXIST')
	    throw(err);
    }
}

function exceptionToString(err)
{
    if (err instanceof Error)
        return err.name + ":" + err.message;
    else if (typeof(err) === "string")
        return err;
    else
        return JSON.stringify(err);
}

async function runExample()
{
    console.log("Start");
    ensureDirectoryExists(poolPath);

    // Only one AssetPool instance should be created for a given pool
    // path. Having multiple instances risks them disagreeing over
    // which assets are protected during pruning.
    let assetPool = new AssetPool(poolPath);

    // Don't let the pool grow any larger than 500MiB
    await assetPool.setMaximumPoolSize(500 * 1024 * 1024);

    // Don't let free space on the storage device fall below 100MiB
    await assetPool.reserveStorage(100 * 1024 * 1024);

    const assetCollection = makeAssetCollection();

    // We need to stop the fetcher from pruning any of the assets we
    // currently care about in order to make space for fetching new
    // assets or realizing existing ones. Assets are protected until
    // the AssetPool instance is destroyed or unprotectAssets is
    // called for the same name.
    await assetPool.protectAssets("collection1", assetCollection);

    await fetchAssets(assetPool, assetCollection);

    // In this case any failure to fetch the assets will cause
    // fetchAssets to have thrown an exception, so we won't get this
    // far. However, in a larger script it may be more convenient to
    // call areAssetsReady to determine whether the asset collection
    // is ready for use.
    if (await assetPool.areAssetsReady(assetCollection)) {
	await useAssets(assetPool, assetCollection);
	await realizeAssets(assetPool, assetCollection);
    } else {
	console.log("Assets were not downloaded successfully");
    }
}

runExample()
    .then(() => {
	console.log("Complete");
	process.exit(0);
    })
    .catch((err) => {
	console.log("Failed " + exceptionToString(err));
	process.exit(1);
    });
```

