Ingesta masiva de archivos en Azure Media Services

Hace tiempo que vengo hablando de Azure Media Services, intentando mostrar paso por paso cómo iniciarse en el mundo media a través de la solución de Microsoft. Es cierto que en muchas ocasiones estamos tratando nuevos proyectos ¿pero y si ya tengo mi contenido media y necesito añadirlo a mi cuenta de Azure? Realizar este proceso a través del propio SDK de Azure Media Services puede generar cuellos de botella, ya que debería ir creando asset por asset y, a su vez, añadir y subir cada uno de los archivos (algunos de ellos puede que de gran tamaño). Para este escenario, existe un proceso llamado Ingest Assets in Bulk del cual me gustaría hablaros hoy, mientras que disfruto un poco de mis vacaciones.

Cuando queremos realizar una ingesta de asset de manera masiva lo que debemos hacer es desacoplar la creación de los assets de la subida de los archivos. A través del SDK vamos a preparar una estructura donde creamos los assets y definimos los archivos que contendrá cada uno antes de realizar la subida. Tendremos además un contenedor asociado a este proceso que será examinado por Azure Media Services, con el fin de esperar la subida de todos los archivos mencionados en la estructura y ubicarlos en el asset correspondiente.

using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.WindowsAzure.MediaServices.Client;

namespace IngestingAssetsBulk
{
    class Program
    {
        static void Main()
        {
            //0. Install-Package windowsazure.mediaservices

            //1. Variables
            var AccountName = ConfigurationManager.AppSettings["AccountName"];
            var AccountKey = ConfigurationManager.AppSettings["AccountKey"];
            var storageKey = ConfigurationManager.AppSettings["StorageKey"];
            var folder = ConfigurationManager.AppSettings["Folder"];
            const string ingestManifestName = "MyIngest";
            var tempFolder = string.Format(@"{0}temp", folder);

            //2. Get AMS context
            var context = new CloudMediaContext(AccountName, AccountKey);

            //3. Create the Ingest Manifest: Represents a set of assets to be created through bulk ingesting along their associated asset files.
            var manifest = context.IngestManifests.Create(ingestManifestName);

            //4. Create temporary folder
            Directory.CreateDirectory(tempFolder);

            //5. Loop directories and create the assets
            var directories = Directory.GetDirectories(folder);

            foreach (var directory in directories)
            {
                var files = Directory.GetFiles(directory);

                if (!files.Any()) continue;

                var directoryName = Path.GetFileName(directory);

                if (directoryName == "temp") continue;

                Console.WriteLine("Create asset from {0} directory", directoryName);

                manifest.IngestManifestAssets.Create(context.Assets.Create(directoryName, AssetCreationOptions.None), files);

                foreach (var file in files)
                {
                    File.Copy(file, String.Format(@"{0}{1}", tempFolder, Path.GetFileName(file)));
                }

            }

            //6. AzCopy task

            var process = new Process
            {
                StartInfo =
                {
                    FileName = @"C:Program Files (x86)Microsoft SDKsAzureAzCopyAzCopy.exe",
                    Arguments = string.Format(@"/Source:{0} /Dest:{1} /DestKey:{2} /Y /V /NC:5", tempFolder, manifest.BlobStorageUriForUpload, storageKey)
                }
            };

            process.Start();

            //7. Monitor Bulk Manifest
            var bContinue = true;
            var consoleWriteLock = new Object();

            while (bContinue)
            {
                //=== We need a new context here because IIngestManifestStatistics is considered an expensive ===//
                //=== property and not updated realtime for a context.                                        ===//
                context = new CloudMediaContext(AccountName, AccountKey);

                var myManifest = context.IngestManifests.Where(m => m.Id == manifest.Id).FirstOrDefault();

                if (myManifest != null)
                {
                    lock (consoleWriteLock)
                    {
                        Console.WriteLine("nWaiting on all file uploads.");
                        Console.WriteLine("PendingFilesCount  : {0}", myManifest.Statistics.PendingFilesCount);
                        Console.WriteLine("FinishedFilesCount : {0}", myManifest.Statistics.FinishedFilesCount);
                        Console.WriteLine("{0}% complete.n", myManifest.Statistics.FinishedFilesCount / (float)(myManifest.Statistics.FinishedFilesCount + myManifest.Statistics.PendingFilesCount) * 100);

                        if (myManifest.Statistics.PendingFilesCount == 0)
                        {
                            Console.WriteLine("Completedn");
                            bContinue = false;
                        }
                    }

                    if (myManifest.Statistics.PendingFilesCount > 0)
                    {
                        Thread.Sleep(60000);
                    }
                }
            }

            Console.WriteLine("Ingest bulk completed!");

            //8. Delete temporary folder
            Directory.Delete(tempFolder, true);

            //9. Delete manifest
            manifest.Delete();

            Console.ReadLine();
        }
    }
}
  1. Instalar el paquete windowsazure.mediaservices desde Nuget
  2. Necesitaremos los datos de la cuenta de Azure Media Services, la clave de la cuenta de Azure Storage, el nombre de la carpeta donde está el contenido, un nombre para el manifiesto y un nombre para la carpeta temporal.
  3. Recuperamos el contexto de la cuenta de Azure Media Services.
  4. Se crea un manifiesto de ingesta a través de context.IngestManifests.Create. Esta acción será la encargada de crear un contenedor donde deberemos subir todos los archivos correspondientes a los assets que definiremos después.
  5. En este ejemplo necesitamos crear un directorio temporal donde haremos una copia de los archivos a subir. El motivo es que AzCopy copia el contenido de la carpeta incluyendo los directorios que hay en ella, pero no permite aplanar la copia a una sola carpeta, sino que replica la estructura en el container. El manifiesto necesita que todos los archivos estén en la raíz.
  6. Utilizando la ruta de la carpeta asociada a la variable Folder, recuperamos todos los directorios contenidos en ella y a través del foreach creamos los assets utilizando el nombre de los directorios y pasamos un array con el nombre de los archivos que formarán parte de los mismos. Por otro lado, copiamos cada uno de los archivos en la carpeta temporal creada en el paso anterior.
  7. Ya tenemos establecida la estructura de assets y archivos que vamos a subir. En este ejemplo, realizo la llamada a la herramienta AzCopy para realizar la subida. Podría ser una operación totalmente independiente, pero he querido mostrarlo así para tener todo el proceso completo, a modo de demostración. Además de AzCopy, existen otros clientes de subida masiva como por ejemplo Aspera, el cual está disponible a través del marketplace de Azure.
  8. Ahora básicamente lo que hacemos es esperar… Gracias a este fragmento de código podremos ver el estado de la subida: cuántos elementos quedan pendientes de subir, cuántos se han procesado y además una llamada a Thread.Sleep de un minuto entre comprobaciones para dar tiempo a AzCopy a realizar las subidas pendientes.
  9. Una vez finaliza el proceso, se elimina la carpeta temporal con la copia de los archivos.
  10. Por último, se elimina además el manifiesto de ingesta, dejando únicamente los assets ya procesados en la cuenta de Azure Media Services.

Espero que sea de utilidad.

¡Saludos!