BLAM3.SDK
3.1.7.25
dotnet add package BLAM3.SDK --version 3.1.7.25
NuGet\Install-Package BLAM3.SDK -Version 3.1.7.25
<PackageReference Include="BLAM3.SDK" Version="3.1.7.25" />
paket add BLAM3.SDK --version 3.1.7.25
#r "nuget: BLAM3.SDK, 3.1.7.25"
// Install BLAM3.SDK as a Cake Addin #addin nuget:?package=BLAM3.SDK&version=3.1.7.25 // Install BLAM3.SDK as a Cake Tool #tool nuget:?package=BLAM3.SDK&version=3.1.7.25
Blue Lucy SDK
Blue Lucy is a media services integration platform that unifies production and supply chain operations for the media industry. The Blue Lucy .Net SDK provides the coding framework for developers to create their own BLidgets and Plugins for running within the Blue Lucy Workflow Runner. It can also be used to rapidly delevop bespoke integrations directly with the Blue Lucy API.
NuGet Packages
Package | Latest Version |
---|---|
BLAM3.SDK |
Documentation
This README provides a quick overview of how to make use of the Blue Lucy .Net SDK. You can check out some sample code on our public GitHub repository for more in depth examples.
Quick Start
[!IMPORTANT] To use the Blue Lucy .Net SDK, you will need to have an active application account on a Blue Lucy system. Please contact support, or your system adminstrator for assistance in setting up an account on your system.
To get started, first add the BLAM3.SDK package to your project by running the following command:
dotnet add package BLAM3.SDK
You can now create a BlamClient
using your application credentials and begin searching your media:
// Create a new BlamClient instance using application credentials
var blamClient = new BlamClient(new BlamClientConfig
{
BlamApiEndpoint = "https://<your>.bluelucy.com/api",
Username = "<ApplicationUsername>",
Password = "<ApplicationPassword>"
});
// Search for the first 100 video assets
var results = await blamClient.Search(new SearchQueryInputModel
{
Query = "type=video",
Limit = 100
});
// Select first asset from search results
var asset = results.Items.First();
Update asset metadata
// Create a new metadata snapshot updating just the specified values
var snapshot = await blamClient.CreateMetadataSnapshot(new AssetMetadataSnapshotInputModel
{
AssetId = asset.Id,
Values = new Dictionary<string, object>
{
{ "show_name", "Cats on Air" },
{ "vtr", 899223 },
{ "language", "Dutch" },
{ "tx_date", "2033-01-01T15:00:00+01:00" },
{ "for_broadcast", true }
},
Merge = true
});
Updating the metadata on an asset creates a new snapshot version. Creating a snapshot with Merge = true
will use the current metadata as the snapshot base and replace just the specified values.
Add an asset to a folder
// Search for an existing folder
var folders = await blamClient.Search(new FolderQueryInputModel
{
Query = "name=Cats on Air",
Limit = 1,
Depth = FolderStructureDepth.Contents
});
// Select first folder from search results
var folder = folders.Items.First();
// Attach the asset to the folder
await blamClient.AttachAssetsToFolder(new FolderAssetListInputModel
{
FolderId = folder.Id,
AssetIds = [asset.Id]
});
Assets can be added or removed in bulk by providing a collection of asset Id
s.
Creating a link to the proxy
// Create a temporary link to the proxy file
var link = await blamClient.CreateStreamToken(new StreamTokenRequestInputModel
{
Asset = new AssetSelectorInputModel { AssetId = asset.Id, Selector = "Current Version;Browse" },
MaxAllowedClaims = MaxAllowedClaims.Unlimited,
ShortUrl = true
});
// Use the url for streaming
var url = link.Url;
Links can be single or multi-use and a sharable with external users or processes (e.g. transcoding into different formats).
Triggering a workflow
// Fetch the list of available workflows
var blueprints = await blamClient.GetWorkflowBlueprints();
var blueprint = blueprints.First(b => b.Name == "Deliver Asset to S3");
// Trigger the selected workflow blueprint
var run = await blamClient.TriggerWorkflow(blueprint.Id, new WorkflowRunInputModel
{
Name = "Delivery for <Customer>",
Type = BlidgetDataType.Assets,
Data = new AssetsIO { AssetIds = [asset.Id] }
});
// Check the current state of the workflow run
var runState = await blamClient.GetWorkflowRun(run.Ids.First());
while (runState.Status != WorkflowRunStatus.Complete)
{
// ...
}
[!IMPORTANT] Workflows can be highly customised per platform. You should contact your workflow administrator to understand what workflows you have access to on your platform. Access to some workflows can be restricted based on your account group access permissions.
BLidgets
BLidgets are short lived micro-processes executed as steps within a Blue Lucy workflow. A BLidget represents a "unit of work" within a workflow and its scope limited to a single task during execution (e.g. Transcode video, Copy file, Import metadata, Run job etc.)
The Blue Lucy SDK simplifies BLidget creation by providing an inheritable base class which implements the BLidget control flow and handles the passing of messages between the BLidget process and the Workflow runner.
BLidget control flow is broken down into 3 phases; Init
, Run
and Complete
:
// ExampleBlidget implements base class `Blidget` with I/O of type `AssetsIO`
internal class ExampleBlidget : Blidget<AssetsIO, AssetsIO>
{
private BlamClient _client;
private ExampleState _runState;
// INIT PHASE - runs on every blidget run
protected override void Init(AssetsIO inputData)
{
_client = new BlamClient(); // Initialise BlamClient (inherits configuration from the Workflow runner)
_runState = GetRunState<ExampleState>(); // Initialise or retrieve the current saved run state
}
// INIT PAHSE - runs on every blidget run (asynchronous variant of `void Init(...)`)
// protected override Task InitAsync(AssetsIO inputData)
// {
// // Asynchronous init operations
// }
// RUN PHASE - runs on the first BLidget execution only, skipped after waking up from `Waiting` or `Idle`
protected override async Task<BlidgetOutput<AssetsIO>> RunAsync(AssetsIO inputData)
{
// Fetch BLidget configuration value
var nameTemplate = GetArgument<string>("name_template");
foreach (var assetId in inputData.AssetIds)
{
// Fetch input resources
var asset = await _client.GetAssetById(assetId);
// Use interpolator to resolve runtime values
var name = await Interpolate(nameTemplate, _client, new TemplateData { Asset = asset });
// Do work ...
// Add data to the run state
var job = new Job { Uuid = Guid.NewGuid() };
_runState.Jobs.Add(job);
// Log Debug message
LogDebug("Added job with ID: '{id}'", job.Uuid);
}
// End process and save current run state; wake again once time has elapsed or receiving a call back
return Waiting(_runState, pollingIntervalSeconds: PollingInterval.OneHour, message: "Waking again in one hour.");
// or return Idle(_runState, _runState.Jobs.Select(j => new Callback("jobId", $"{j.Uuid}")), message: "Waking on callback.");
// or return Complete(inputData);
}
// COMPLETE PHASE - called each time the BLidget runs after waking up from `Waiting` or `Idle`
protected override async Task<BlidgetOutput<AssetsIO>> CompleteAsync(AssetsIO inputData)
{
// Loop through jobs and update run state
foreach (var job in _runState.Jobs)
{
// Do work ...
// Update progress state (used to report progress)
UpdateProgress(50, "Processed 1 of 2 job(s).");
}
// Complete process and pass on output data
return Complete(inputData, message: $"Processed {_runState.Jobs.Count} job(s).");
// or return Waiting(_runState, pollingIntervalSeconds: PollingInterval.OneHour, message: "Waking again in one hour.");
// or return Idle(_runState, _runState.Jobs.Select(j => new Callback("jobId", $"{j.Uuid}")), message: "Waking on callback.");
}
}
internal class ExampleState
{
public List<Job> Jobs { get; set; } = [];
}
internal class Job
{
public Guid Uuid { get; set; }
}
More detailed examples can be found on the samples GitHub repository.
Plugins
Plugins are long running processes which are commonly used for watching for and reacting to internal or external events (e.g. files moving into a folder, a timer elapsing, receiving a message). They can also be used as extensions to the API by implementing additional end points or even act as web server for custom UIs.
Plugins implement the .Net IHostedService
and provide two methods for control flow; OnStart
and OnStop
. Additional services or middleware can also be added using .Net's Dependency Injection
allowing developers to create fully featured services within a single Plugin.
There are two possible builder patterns depending on whether a web server is required:
Service Pattern
// ExamplePlugin implements base class `PluginInitialiser`
internal class ExamplePlugin : PluginInitialiser
{
private async Task CreateHostedService()
{
var builder = new HostBuilder()
.ConfigureAppConfiguration((hostContext, configuration) =>
{
})
.ConfigureServices((hostContext, services) =>
{ // Configure serivces using dependency injection
services.AddSingleton<BlamClient>();
services.AddHostedService<ExamplePluginService>();
});
var host = builder
.Build();
// Runs hosted service(s)
await host.RunAsync();
}
public override async Task InitAsync()
{ // Plugin entry point - fetch configuration values here by calling `GetArgumnet`
await CreateHostedService();
}
}
// Inject `BlamClient` singleton using dependency injection
internal class ExamplePluginService(BlamClient client) : PluginService
{
private ExampleState _runState;
private WorkflowBlueprintViewModel _blueprint;
// ONSTART PHASE - called when the hosted service first starts on `host.RunAsync()`
protected override async Task OnStartAsync(CancellationToken cancellationToken)
{
_runState = GetRunState<ExampleState>(); // Initialise or retrieve the current saved run state
var blueprintId = GetArgument<int>("blueprint_id"); // Fetch BLidget configuration value
_blueprint = await client.GetWorkflowBlueprint(blueprintId); // Fetch argument resource
ProcessWork(cancellationToken); // Begin work
}
// ONSTOP PHASE - called when the host application is stopped e.g. SIGTERM received
protected override Task OnStopAsync(CancellationToken cancellationToken)
{
// Clean up steps ...
return Task.CompletedTask;
}
private void ProcessWork(CancellationToken cancellationToken) =>
Task.Run(async () => await ProcessWorkAsync(cancellationToken), cancellationToken);
private async Task ProcessWorkAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
// Do work ...
// Save run state during plugin execution
Save(_runState);
// Log Debug
LogDebug("Saved run state.");
}
}
}
internal class ExampleState
{
public List<TrackedItem> Items { get; set; }
}
internal class TrackedItem
{
public Guid Uuid { get; set; }
}
Web Server Pattern
// ExamplePlugin implements base class `PluginInitialiser`
internal class ExamplePlugin : PluginInitialiser
{
private async Task CreateWebApp(string baseRoute, int port)
{
baseRoute = string.IsNullOrWhiteSpace(baseRoute) ? null : $"/{baseRoute}";
var builder = WebApplication.CreateBuilder();
// Configure serivces using dependency injection
builder.Services.AddSingleton<ExamplePluginService>();
builder.Services.AddHostedService(p => p.GetService<ExamplePluginService>());
builder.Logging.ClearProviders();
var app = builder.Build();
// Define API endpoints using .Net minimal API
app.MapGet($"{baseRoute}/hello", () =>
{
return "hello, world";
});
// Start web server and listen on incoming port
await app.RunAsync($"http://*:{port}");
}
public override async Task InitAsync()
{ // Plugin entry point - fetch configuration values here by calling `GetArgumnet`
var baseRoute = GetArgument<string>("base_route");
var port = GetArgument<int>("port");
await CreateWebApp(baseRoute.Trim('/'), port);
}
}
Next steps
To learn more about Blue Lucy, visit the Blue Lucy website.
Samples
- Samples: Samples in this repository serve as a reference for developing BLigdets and Plugins
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- Microsoft.AspNetCore.SignalR.Client (>= 8.0.8)
- Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson (>= 8.0.8)
- Microsoft.Extensions.Diagnostics (>= 8.0.0)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Http (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.1)
- Microsoft.IdentityModel.JsonWebTokens (>= 8.0.2)
- Newtonsoft.Json (>= 13.0.3)
- OpenTelemetry.Exporter.OpenTelemetryProtocol (>= 1.9.0)
- OpenTelemetry.Extensions.Hosting (>= 1.9.0)
- OpenTelemetry.Instrumentation.AspNetCore (>= 1.9.0)
- OpenTelemetry.Instrumentation.Http (>= 1.9.0)
- Polly (>= 8.4.1)
- System.Reactive (>= 6.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
3.1.7.25 | 215 | 12/4/2024 |
3.1.5.12 | 2,087 | 5/16/2024 |
3.1.5.11 | 289 | 5/7/2024 |
3.1.5.8 | 470 | 4/3/2024 |
3.1.4.9 | 510 | 2/6/2024 |
3.1.4.8 | 126 | 2/5/2024 |
3.1.3.19 | 179 | 2/5/2024 |
3.1.3.16 | 1,744 | 10/4/2023 |
3.1.0.25 | 329 | 8/14/2023 |
3.1.0.24 | 332 | 7/25/2023 |
3.1.0.23 | 208 | 7/17/2023 |
3.1.0.19 | 431 | 6/20/2023 |
3.1.0.18 | 215 | 6/8/2023 |
3.0.8.16 | 337 | 3/15/2023 |
3.0.8.11 | 356 | 2/7/2023 |
3.0.8.10 | 362 | 2/1/2023 |
3.0.7.16 | 461 | 11/29/2022 |
3.0.7.13 | 1,073 | 11/9/2022 |
3.0.6.62 | 482 | 10/4/2022 |
3.0.6.60 | 790 | 7/25/2022 |
3.0.6.59 | 501 | 7/15/2022 |
3.0.6.57 | 580 | 7/7/2022 |
3.0.6.49 | 586 | 6/15/2022 |
3.0.6.44 | 1,981 | 5/18/2022 |
3.0.6.42 | 546 | 5/11/2022 |
3.0.6.34 | 741 | 4/19/2022 |
3.0.6.33 | 594 | 4/7/2022 |
3.0.6.23 | 573 | 3/15/2022 |
3.0.6.21 | 545 | 3/7/2022 |
3.0.6.20 | 532 | 2/28/2022 |
3.0.6.14 | 564 | 1/16/2022 |
3.0.6.10 | 1,820 | 12/23/2021 |
3.0.6.8 | 385 | 12/15/2021 |
3.0.6.6 | 386 | 12/10/2021 |
3.0.6.5 | 552 | 12/9/2021 |
3.0.6.4 | 384 | 12/2/2021 |
3.0.6.2 | 3,078 | 11/25/2021 |
3.0.6 | 421 | 11/17/2021 |
3.0.5.46 | 430 | 11/10/2021 |
3.0.5.45 | 417 | 11/8/2021 |
3.0.5.42 | 410 | 10/26/2021 |
3.0.5.41 | 448 | 10/19/2021 |
3.0.5.36 | 496 | 9/15/2021 |
3.0.5.35 | 482 | 9/9/2021 |
3.0.5.34 | 462 | 8/31/2021 |
3.0.5.33 | 449 | 8/26/2021 |
3.0.5.32 | 423 | 8/20/2021 |
3.0.5.31 | 427 | 8/20/2021 |
3.0.5.30 | 442 | 8/19/2021 |
3.0.5.29 | 437 | 8/18/2021 |
3.0.5.28 | 433 | 8/18/2021 |
3.0.5.27 | 425 | 8/18/2021 |
3.0.5.26 | 439 | 8/16/2021 |
3.0.5.25 | 425 | 8/13/2021 |
3.0.5.24 | 441 | 8/12/2021 |
3.0.5.23 | 577 | 7/23/2021 |
3.0.5.22 | 410 | 7/21/2021 |
3.0.5.21 | 474 | 7/16/2021 |
3.0.5.20 | 482 | 7/16/2021 |
3.0.5.19 | 473 | 7/6/2021 |
3.0.5.18 | 551 | 6/24/2021 |
3.0.5.17 | 554 | 6/12/2021 |
3.0.5.16 | 490 | 6/3/2021 |
3.0.5.15 | 484 | 6/1/2021 |
3.0.5.14 | 470 | 5/20/2021 |
3.0.5.13 | 462 | 5/13/2021 |
3.0.5.12 | 446 | 5/10/2021 |
3.0.5.10 | 484 | 4/27/2021 |
3.0.5.8 | 484 | 4/21/2021 |
3.0.5.7 | 460 | 4/20/2021 |
3.0.5.5 | 486 | 4/9/2021 |
3.0.5.4 | 473 | 4/8/2021 |
3.0.5.3 | 431 | 4/1/2021 |
3.0.5.2 | 444 | 3/31/2021 |
3.0.5.1 | 446 | 3/31/2021 |
3.0.5 | 481 | 3/26/2021 |
3.0.4.14 | 505 | 3/24/2021 |
3.0.4.13 | 478 | 3/10/2021 |
3.0.4.12 | 472 | 3/8/2021 |
3.0.4.11 | 467 | 3/5/2021 |
3.0.4.10 | 449 | 3/4/2021 |
3.0.4.9 | 465 | 3/3/2021 |
3.0.4.8 | 490 | 3/2/2021 |
3.0.4.7 | 528 | 2/26/2021 |
3.0.4.6 | 513 | 2/24/2021 |
3.0.4.5 | 513 | 2/17/2021 |
3.0.4.4 | 509 | 2/11/2021 |
3.0.4.3 | 503 | 2/11/2021 |
3.0.4.2 | 504 | 2/9/2021 |
3.0.4.1 | 483 | 2/3/2021 |
3.0.4 | 511 | 2/2/2021 |
3.0.3.9 | 556 | 1/26/2021 |
3.0.3.8 | 500 | 1/26/2021 |
3.0.3.7 | 510 | 1/20/2021 |
3.0.3.6 | 502 | 1/18/2021 |
3.0.3.5 | 558 | 1/14/2021 |
3.0.3.4 | 521 | 1/12/2021 |
3.0.3.3 | 497 | 1/11/2021 |
3.0.3.2 | 498 | 1/10/2021 |
3.0.3 | 525 | 1/5/2021 |
3.0.2.9 | 529 | 12/23/2020 |
3.0.2.8 | 532 | 12/22/2020 |
3.0.2.7 | 574 | 12/10/2020 |
3.0.2.6 | 559 | 12/9/2020 |
3.0.2.5 | 537 | 12/8/2020 |
3.0.2.4 | 595 | 11/20/2020 |
3.0.2.3 | 554 | 11/19/2020 |
3.0.2.2 | 566 | 11/19/2020 |
3.0.2.1 | 583 | 11/18/2020 |
3.0.2 | 577 | 11/17/2020 |
3.0.1.49 | 569 | 11/11/2020 |
3.0.1.48 | 552 | 11/9/2020 |
3.0.1.47 | 564 | 10/27/2020 |
3.0.1.46 | 725 | 10/13/2020 |
3.0.1.45 | 577 | 10/12/2020 |
3.0.1.44 | 549 | 10/12/2020 |
3.0.1.43 | 584 | 10/8/2020 |
3.0.1.42 | 543 | 10/6/2020 |
3.0.1.41 | 559 | 10/5/2020 |
3.0.1.40 | 555 | 10/2/2020 |
3.0.1.39 | 537 | 10/1/2020 |
3.0.1.38 | 574 | 9/30/2020 |
3.0.1.37 | 555 | 9/30/2020 |
3.0.1.35 | 615 | 9/28/2020 |
3.0.1.34 | 598 | 9/11/2020 |
3.0.1.33 | 623 | 9/3/2020 |
3.0.1.32 | 585 | 9/1/2020 |
3.0.1.31 | 577 | 8/26/2020 |
3.0.1.30 | 582 | 8/25/2020 |
3.0.1.29 | 586 | 8/21/2020 |
3.0.1.28 | 564 | 8/18/2020 |
3.0.1.27 | 576 | 8/18/2020 |
3.0.1.26 | 560 | 8/17/2020 |
3.0.1.25 | 543 | 8/14/2020 |
3.0.1.24 | 553 | 8/10/2020 |
3.0.1.23 | 624 | 8/6/2020 |
3.0.1.22 | 659 | 7/29/2020 |
3.0.1.21 | 630 | 7/29/2020 |
3.0.1.20 | 583 | 7/29/2020 |
3.0.1.19 | 672 | 7/16/2020 |
3.0.1.18 | 593 | 7/15/2020 |
3.0.1.17 | 601 | 7/13/2020 |
3.0.1.16 | 657 | 7/10/2020 |
3.0.1.15 | 560 | 7/10/2020 |
3.0.1.14 | 625 | 7/8/2020 |
3.0.1.13 | 564 | 7/7/2020 |
3.0.1.12 | 663 | 7/2/2020 |
3.0.1.11 | 565 | 7/2/2020 |
3.0.1.10 | 584 | 7/2/2020 |
3.0.1.9 | 544 | 7/1/2020 |
3.0.1.8 | 539 | 7/1/2020 |
3.0.1.7 | 593 | 6/30/2020 |
3.0.1.6 | 571 | 6/30/2020 |
3.0.1.5 | 576 | 6/23/2020 |
3.0.1.4 | 634 | 6/22/2020 |
3.0.1.3 | 644 | 6/15/2020 |
3.0.1.2 | 680 | 6/14/2020 |
3.0.0.1 | 657 | 6/12/2020 |
3.0.0 | 649 | 6/10/2020 |
0.4.34 | 583 | 6/5/2020 |
0.4.33 | 683 | 6/4/2020 |
0.4.32 | 631 | 6/3/2020 |
0.4.31 | 629 | 6/2/2020 |
0.4.30 | 689 | 5/29/2020 |
0.4.29 | 617 | 5/20/2020 |
0.4.28 | 607 | 5/19/2020 |
0.4.27 | 634 | 5/5/2020 |
0.4.26 | 694 | 5/3/2020 |
0.4.25 | 630 | 5/1/2020 |
0.4.24 | 621 | 4/27/2020 |
0.4.23 | 629 | 4/23/2020 |
0.4.22 | 583 | 4/20/2020 |
0.4.21 | 577 | 4/20/2020 |
0.4.20 | 628 | 4/13/2020 |
0.4.19 | 764 | 4/3/2020 |
0.4.18 | 634 | 3/30/2020 |
0.4.16 | 757 | 3/28/2020 |
0.4.15 | 636 | 3/26/2020 |
0.4.14 | 701 | 3/25/2020 |
0.4.13 | 615 | 3/20/2020 |
0.4.12 | 597 | 3/19/2020 |
0.4.11 | 631 | 3/17/2020 |
0.4.10 | 624 | 3/10/2020 |
0.4.8 | 602 | 3/6/2020 |
0.4.7 | 593 | 3/6/2020 |
0.4.6 | 596 | 3/6/2020 |
0.4.5 | 639 | 3/5/2020 |
0.4.4 | 649 | 3/4/2020 |
0.4.3 | 626 | 2/27/2020 |
0.4.2 | 576 | 2/26/2020 |
0.4.1 | 631 | 2/18/2020 |
0.4.0 | 746 | 2/17/2020 |
0.0.3.24 | 658 | 2/17/2020 |
0.0.3.23 | 597 | 2/13/2020 |
0.0.3.22 | 648 | 2/10/2020 |
0.0.3.21 | 732 | 2/8/2020 |
0.0.3.20 | 746 | 1/30/2020 |
0.0.3.18 | 647 | 1/27/2020 |
0.0.3.17 | 667 | 1/17/2020 |
0.0.3.16 | 617 | 1/17/2020 |
0.0.3.15 | 604 | 1/17/2020 |
0.0.3.14 | 756 | 1/9/2020 |
0.0.3.13 | 677 | 1/8/2020 |
0.0.3.12 | 673 | 1/7/2020 |
0.0.3.11 | 657 | 1/6/2020 |
0.0.3.10 | 741 | 1/3/2020 |
0.0.3.9 | 716 | 1/2/2020 |
0.0.3.8 | 730 | 12/31/2019 |
0.0.3.7 | 681 | 12/20/2019 |
0.0.3.6 | 870 | 12/18/2019 |
0.0.3.5 | 673 | 12/17/2019 |
0.0.3.4 | 711 | 12/16/2019 |
0.0.3.3 | 732 | 12/2/2019 |
0.0.3.2 | 644 | 11/20/2019 |
0.0.3.1 | 694 | 11/18/2019 |
0.0.3 | 663 | 11/7/2019 |
0.0.2.9 | 693 | 11/1/2019 |
0.0.2.8 | 674 | 10/30/2019 |
0.0.2.7 | 785 | 10/26/2019 |
0.0.2.6 | 792 | 10/24/2019 |
0.0.2.5 | 673 | 10/22/2019 |
0.0.2.4 | 1,440 | 10/11/2019 |
0.0.2.3 | 676 | 10/11/2019 |
0.0.2.2 | 699 | 10/1/2019 |
0.0.2.1 | 687 | 9/26/2019 |
0.0.2 | 712 | 9/26/2019 |