Video web search in Fast video cataloger

I will show you how to access the web browser in Fast video cataloger to easily integrate a web search with your video catalog. In Fast video cataloger 6.31 we opened up the integrated web browser in Fast video cataloger for scripting. If you are on an older version please update to the latest version from our download page or click the upgrade button in the program.

Web browser window

The web browser is what you see when you click on the view button and Help.

Click the help button to open the web browser.

Here is how it looks with just the script console and the web browser open in Fast video cataloger.

Script console and web browser open.

Scripting the browser

IScripting has a new member function GetBrowser(); that will get you the top-level interface to the integrated chromium web browser. The complete interface is documented on the cef GitHub site. To load a web page to the browser window you simply need to call Load() and give your URL as argument.

To get the underlying browser object and access the dom model you can call GetBrowser() to get and IBrowser object. The IBrowser interface has a MainFrame property of type IFrame and from here you can get access to execute javascript. That is outside the scope of this text, let me know if you are interested in more example on how to use that functionality.

Google search

You can do google searches by giving the right arguments to the google URL. http://www.google.com/search is the root URL. Search arguments are passed as q=[search terms]. Search terms need to be separated by a + sign. There are plenty of extra arguments to customize your search, for example, as_filetype=[file extension] to search for a specific filetype or as_sitesearch=[site URL] to search only at a specific site. In this example, I will only use the basic search query but you can easily expand on it to fit your needs. Remember to use & between arguments if you want to send more than the q argument y.

The search sample

We start by getting your current video selection. Then we fetch the VideoFileEntry for that selection using the VideoCatalogService interface. This is very similar to most other samples. Then we do a bit of string manipulation. Spaces in the Google query should be separated by + so we replace all spaces and also all _ as they are probably meant as spaces in the title. Last we check if the title has a “.” in there somewhere and get rid of everything after the . as that is probably a file extension. Now we have the string we want to search for. We need to pass this to google as q= so we put that in the query string.
Next, we get the browser with GetBrowser() and then call Load() with the Google base URL and the search query.

Script Code

Copy-paste the following into the script console to use the selected video title as a search term on google. Click Run to execute the script.

using System.Runtime;
using System.Collections.Generic;
using VideoCataloger;
using VideoCataloger.RemoteCatalogService;
class Script
{
  static public async System.Threading.Tasks.Task Run ( IScripting scripting, string arguments ) { 
  var selection = scripting.GetSelection();
  List selected_videos = selection.GetSelectedVideos();
  if (selected_videos.Count>0) {
    var cataloger = scripting.GetVideoCatalogService();
    var video_entry = cataloger.GetVideoFileEntry( selected_videos[0] );
    string title = video_entry.Title;
    title = title.Replace(' ', '+');
    title = title.Replace('_', '+');
    int extension = title.LastIndexOf(".");
    if (extension!=-1)
      title = title.Substring(0,extension);

    string query = "q=" + title;
    var browser = scripting.GetBrowser();
    browser.Load("http://www.google.com/search?" + query);
    }
  }
}

Using TMDB Web API ( The Movie DataBase )

TMDB (The Movie DataBase ) is a large open internet database you can use to search for movies, tv shows, and people in movies. It also has a web API.

Fast video cataloger is a software to organize video clips so you can quickly search, browse, and find what you are looking for.

This example will show how to use the scripting support in Fast video cataloger to integrate with the web API in TMDB. My goal is to show you have to integrate a search for actors using the TMDB web API and then add the metadata to Fast video cataloger. With this start, I hope you can expend it to what you need and perhaps use other functions in the TMDB API.

Script demo

Getting started

If you do not already have Fast video cataloger installed you can download a free trial from here

Before we can start you also need to create a free account at TMDB that you can get from here, When you have your account to go your profile, click settings and find the API tab and request a developer API key.

The script we show here can be downloaded from our sample git hub repository

Place the downloaded scripts get_actor.xaml and get_actor.xaml.cs in the script folder of your fast video cataloger installation.

Open the get_actor.xaml.cs and fine the line near the top that reads

static string api_key = "";

and replace the empty api_key string with your TMDB API key.

Running the script

To run the script in Fast video cataloger open the Script console window. In the window click on the Load button and load the get_actor.xaml.cs script. Click run and you should see a window like this:

tmdb actor search

Integrating with tmdb people search

Search for an actor and you should see something like this

tmdb search results

results of a search in the movie database

You can then click “Add to catalog” to add the actor to your Fast video cataloger database

Script overview

The C# script that is running uses WPF for the user interface. The user interface is defined in the XAML file. We use the rest API for TMDB. You can find full documentation for it here

Note that this sample just uses a minimal subset of the TMDB API. But, the principles are the same for the whole API, and it should be pretty easy to extend this with more functionality. For example, getting poster images or searching for movies.

Another difference to most other Fast video cataloger samples is that this one uses async programming which is needed for the web APIs.

Script walkthrough

You can find the full xaml file here and the full source file here

References

At the very top of the script we need to add our external references. This is what normally would go into “references” in a visual studio c# project. In a Fast video cataloger script you use a comment in the format of :”//css_ref [dependency]”.

//css_ref WindowsBase;
//css_ref PresentationCore;
//css_ref PresentationFramework;
//css_ref System.Runtime;
//css_ref System.ObjectModel;

WindowsBase, PresentationCore, and PresentationFramework are needed for the WPF user interface. System.Runtime and System.ObjectModel has the c# code we need to access and parse the web API.

Static variables

At the top of the script, we have a few static variables. The important one here is again the api_key variable, here you need to fill in your key as mentioned earlier.

Entry point

Run is the entry point of all scripts running from Fast Video cataloger:

static public async Task Run(IScripting scripting, string argument)

The only thing we do in the entry function is to save the passed in scripting interface and then create the GetActors WPF window and show it. We intentionally call Show() and not ShowDialog() since we want to be able to bring up the actor window in Fast video cataloger when we have this window open.

Constructor

In this example, we have put all code in the HelloWindow class. This has the same structure as the basic HelloWpf.cs sample.
The constructor loads the XAML interface definition and sets up the window. It also establishes the connection between the buttons and their click functions (unfortunately we do not support binding directly in the XAML as the XAML file is loaded through the script engine ).

Searching

When the search button is clicked we will get a callback to Search_Click(…). This is the main function that performs the search. We first fetch the text the user entered in the search box.

Then we call SearchActors asynchronously and expect to get a CPersonSearchResult back, more on that later.

private async Task SearchActors( string query )
{
  string encoded_query = System.Net.WebUtility.UrlEncode( query );
  string search_line = api_base_url + "search/person?api_key=" + api_key + /
         "&language=en-US&query=" + encoded_query + "&include_adult=true&page=1";
  CPersonSearchResult header_result = await GetFromJSON(search_line);
  return header_result;
}

After that, we have to check if we got a search hit. In this example, we only care about the first result but you can of course list all the results and let the user pick one of them.

We have our first actor and pick the name. We then call MakeProfileImageUrl(…) to generate a URL for the profile image. We call GetImageData(…) to get the image from that URL.

At this point, we have most of the data we need.

We create a new Actor class in the Fast video cataloger format and store that in m_CurrentActor. We fill in the data members with data from the search and the downloaded portrait data.

Next, we call GetPerson() with the TMDB id for the first actor. Once we have done that we have all the data we need from TMDB.
We create a WPF image from the downloaded image data and call SetActorToUI() to update the user interface.

SetActorToUI()

This function simply updates the user interface from the variable we have designed. The portrait is set to the image, the name and the description is set. The XAML file defines the whole layout of the user interface including size and positions of elements and fonts.

Adding to Fast Video cataloger catalog

If you click add to catalog after you have searched and found an actor you will get a callback to AddToCatalog_Click(…). Here we simply get the interface to the video catalog and call AddActorToDB() with the m_CurrentActor we filled in earlier when we got the search result.

GetConfiguration()

GetConfiguration() is a wrapper to load the TMDB configuration. You only need to call this once in your application and it is needed to among other things figure out the path to any image resource. The configuration is stored globally in m_Configuration. This is a very short function and it is a good example of how to use the TMDB rest API.

First, we create the URL that we will use to access TMDB. It always starts with the base URL and then the API we want to use and some arguments. Among the arguments, you always pass in your API key. We then use this URL and call one a utility function, GetFromJSON(), we have in the sample code. This function takes a class argument, which is of type of the JSON file we want to get from the URL. And, it returns an object of that class or null if it fails.
Let us take a closer look at that utility function.

GetFromJSON()

GetFromJSON() is an asynchronous function that you pass the class of what you want to read from the query string. There is some initial setup and then we call GetAsync( query ) that will asynchronously call the URL and get the result. Once we have the result we read the data from the response message as a string. We then create a memory stream to the string data and create a DataContractJsonSerializer object to parse our data from the JSON data and create the C# object.

public async Task GetFromJSON(string query)
{
  T parsed_stuct = default(T);
  var handler = new HttpClientHandler();
  handler.AllowAutoRedirect = false;
  var client = new HttpClient(handler);
  try
  {
    var videoGetIndexRequestResult = await client.GetAsync(query);
    var result = videoGetIndexRequestResult.Content.ReadAsStringAsync().Result;
    var ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(result));
    var serializer = new DataContractJsonSerializer(typeof(T));
    parsed_stuct = (T)serializer.ReadObject(ms);
    ms.Close();
  }
  catch (Exception ex)
  {
    Console.WriteLine(ex.Message);
  }
  return parsed_stuct;
}

If you want to use other parts of the TMDB API you need to create classes for the data that are returned by the functions.

SearchActors( string query )

The SearchActors() function is very similar to the GetConfiguration() function but it uses the search/person API. We pass the api_key but also some extra arguments.

One of the arguments is a query string, this needs to be URL encoded to handle special characters like spaces.

Then again, once we have our URL, we simply use the GetFromJSON() utility to read the result into the CPersonSearchResult class. This class includes info about how many results you have from your query and some basic information about each result returned in an array of class CActorResult. Here you have the name of the actor, an id that we can use in other API calls and a profile_path. Profile_path is needed when you want to create an URL to fetch the profile picture.

MakeProfileImageUrl()

MakeProfileImageUrl() is a utility function that is used to generate an URL to a profile image. For this, we need the configuration data where you have the base_url for all profile images. We need to decide on the size of the image, the different types of sizes are also listed in the profile image, and then finally we need the part of the URL that is unique for every actor. Once we have this URL we can just get the data.

GetImageData()

GetImageData() takes the URL from MakeProfileImageUrl() or really any image URL in the TMDB image URL format. We again use the WebClient API and call DownloadDataTaskAsync(). This function will asynchronously download the image data to an array. This is the right format we need to pass to Fast video cataloger. But we need one more step to display it in the WPF user interface.

private async Task GetImageData(string url)
{
  byte[] image_data = null;
  try
  {
    Uri image_url = new Uri(url);
    var webClient = new System.Net.WebClient();
    image_data = await webClient.DownloadDataTaskAsync(image_url);
  }
  catch (Exception ex)
  {
    Console.WriteLine(ex.Message);
  }
  return image_data;
}

LoadImageFromStream()

LoadImageFromStream() takes a memory stream to the bytes we loaded with GetImageData() and create a BitmapImage object from it. This object can then later be assigned as a source to any Image element we have in our XAML user interface.

Conclusion

I have shown how you can expand on the functionality of Fast video cataloger with web API. In this example we used TMDB but there are tons of other web APIs that might be useful and my hope is that this sample will make it easier for you to get started.

Setting tags to video files

Video tags and keywords

I will show you how to write tags to video files so they can be searched from Windows file explorer.

In Fast video cataloger, you can assign tags (keywords) to videos as well as to specific points in time inside of the video files. You can quickly search across all your videos even if the video files are archived away and not accessible. Keywords in Fast video cataloger are stored in the catalog and not in the video files. If you want to be able to use Windows search and have the same tags as in Fast video cataloger you need to transfer the tags to the actual video files. Fortunately, this can be easily be done with the integrated script support in Fast video cataloger.

Setting tags to video files

If you don’t care about the details simply copy the script at the bottom of this text into the script window in Fast video cataloger. Select the videos you want to transfer tags to and run the script.

Tags on files in Windows

When you do a search in Windows you search on metadata stored inside files. Windows can build and maintain an index of all this metadata to make searching a bit quicker so that it does not have to access all files every time you search. What metadata you can add to a file depends on the file format. It is not the same for all files and for some file formats it is actually quite limited. For example, not all video file formats support tags. To see what tags are set to a video file, click properties in Explorer and go to the Details tab.

Tags in video file details

Video detail property page

You can also get to the properties from inside Fast video cataloger, just right-click the video in the catalog list and select File/Properties.

If you see a Tags line in the property sheet then the format support Tags. You will also find a rating, title, and a comments field. If the tags field is not there for the file format, that format simply does not support tags. If the format does not support tags then this script will fail when trying to assign tags to it. This is also one of the reasons why Fast video cataloger does not store tags inside of the video files by default.

C# code walkthrough

WindowsAPICodePack

We will use the "Microsoft.WindowsAPICodePack.Shell" package to write the tags to files. To be able to use that package we need to add a reference to it from our project. If you are copying this code for a normal C# project, i.e. not a Fast video cataloger script you can add the reference in the Visual studio solution (look in references). For a script in Fast video cataloger we don’t have a solution, the way we need to do this is with a special comment. The first line in the script adds that reference: //css_ref Microsoft.WindowsAPICodePack;. Then we also need to add the using lines to bring in the library to the file: using Microsoft.WindowsAPICodePack.Shell;. Now that we have solved the extra dependencies we can use this library to write our tags to the videos.

The code

The beginning of this script is very similar to most other Fast video cataloger sample scripts. We get the id of the selected video(s) and then use that id to the path to the file.

The ConvertToLocalPath(...) is needed in case your catalog has used special folders and will convert these special folders into an absolute path. If the path is already absolute this will do nothing.

Then we need to get a shell reference to the file with ShellFile.FromFilePath. In the Windows shell not all entries are files. The shell API references entries in the shell using pidl and not normal file paths. This API call simply gets the path in the shell to the file.

The next step is to get the function to write to the property page of the shell object. file.Properties.GetPropertyWriter(); give us the interface we need and then we only need to call WriteProperty(..) to write property values of the file. For the calls to succeed the property needs to be supported by the file format as mentioned before and the file needs to be writable.

External references

As all other samples this sample is available on the Fast video cataloger github repository.

Documentation on properties for Windows files can be found here

C# code – Tag to video file


//css_ref Microsoft.WindowsAPICodePack;
using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
using System.Collections.Generic;
using System.Runtime;
using System;
using VideoCataloger;

namespace VideoCataloger
{
  public class WriteTags
  {
    static public void Run(IScripting scripting, string argument)
    {
        scripting.GetConsole().Clear();
        var catalog = scripting.GetVideoCatalogService();
        ISelection selection = scripting.GetSelection();
        List selected = selection.GetSelectedVideos();
        foreach (long video in selected)
        {
          try
          {
            var entry = catalog.GetVideoFileEntry(video);

            IUtilities utilities = scripting.GetUtilities();
            var selected_path = utilities.ConvertToLocalPath(entry.FilePath);

            var file = ShellFile.FromFilePath(selected_path );

            var selected_videos = new long[1];
            selected_videos[0] = video;
            var TagInstances = catalog.GetTagsForVideos(selected_videos);
            List tag_list = new List();
            foreach (var tag in TagInstances)
            {
              tag_list.Add( tag.Name );
            }

            scripting.GetConsole().Write( "Tagging : " + selected_path + " ..." );
            ShellPropertyWriter propertyWriter =  file.Properties.GetPropertyWriter();
            propertyWriter.WriteProperty(SystemProperties.System.Keywords, tag_list.ToArray() );
            int Rating = 0;
            if (entry.Rating==1)
              Rating = 1;
            if (entry.Rating==2)
              Rating = 25;
            if (entry.Rating==3)
              Rating = 50;
            if (entry.Rating==4)
              Rating = 75;
            if (entry.Rating==5)
              Rating = 99;
            propertyWriter.WriteProperty(SystemProperties.System.Rating, Rating );
            propertyWriter.WriteProperty(SystemProperties.System.Comment, entry.Description );
            propertyWriter.Close();

            scripting.GetConsole().WriteLine( "Done " );
          }
          catch (Exception ex)          
          {
            scripting.GetConsole().WriteLine( ex.Message );
          }
        }       
    }
  }
}

Build your custom video solution with a WPF user interface

Custom video solution

If you want to develop a custom video solution in Fast video cataloger you will need a user interface. This article will show you how to build a WPF user interface in a Fast video cataloger script.

What is WPF

WPF stands for Windows Presentation Foundation and is a Windows technology to create user interfaces. In WPF you defined your user interface in an XAML file. The whole user interface in Fast video cataloger is built with WPF.

XAML to define the user interface

In WPF you specify the layout of the user interface in an XML file. This file defines what standard controls you want on your page and how the layout is going to be arranged. Separating the user interface from the code like this makes it possible to hand over the design and layout of the user interface to an artist and let the programmer focus on the actual code of the application. Well at least in theory. Even if you are doing both the code and the user interface it is a nice separation to have your layout defined in a separate file. You can also use programs like Blend or Visual studio to design your user interface without touching the code of your application.

Problems with WPF in scripts

If you want to use WPF to build a user interface for a script in Fast video cataloger there are a few problems we need to solve. First, when compiling a WPF applicaton there are actually different compilers compiling the XAML code that defines the user interface and the C# code that is the program. The scripting in Fast video cataloger has no XAML compiler so that’s one problem. Another problem is that you do not specify a list of files to load, you only load your main C# script file. So to be able to define a user interface in XAML we need to load the file dynamically from our C# script. Luckily there is a function in .net to do just that System.Windows.Markup.XamlReader.Load(...).

Referencing external scripts

To be able to use WPF we need to bring in a number of external references. In a Visual studio solution you would add these these as references to the soluton. Again, in Fast video cataloger we load one script file. To solve this Fast video cataloger does special parsing of comments to let you use comments to bring in external references. css_ref is the syntax to bring in an external reference. To bring in everything you need for WPF in your script just add these lines to the top of your script:

//css_ref WindowsBase;
//css_ref PresentationCore;
//css_ref PresentationFramework;
//css_ref System.Runtime
//css_ref System.ObjectModel

Once we have the references we also need to add the “using” clause as usual in “.net”. Adding the following at the top of your script should bring in all we need for WPF.

using System;
using System.Xaml;
using System.Windows;
using System.IO;
using System.Windows.Controls;

Visual studio solution

The sample solution provided with the Fast video cataloger has all the references set up so you can add your XAML files and C# files there and use the UI editor to design your user interface. You are even able to compile the user interface even though the compilation in the solution works in a completely different way compared to the script. Even so, this is a good way to quickly catch errors. I highly recommend you use the provided sample solution and continue to build on the provided WPF sample.

Step by step through the script

This sample starts with a class that inherits from the Window class. This is the main window for the user interface. In the constructor we can set the size and title of the window.

Width = 320;
Height = 200;
Title = "Dynamic WPF window";

The next step is to load the actual xaml file that defines our user interface. Here is the code to do this:

string current_folder = Directory.GetCurrentDirectory();
string path = current_folder + "\\scripts\\samples\\hello_wpf.xaml";
using (FileStream fs = new FileStream( path, FileMode.Open))
{
m_RootElement = (DependencyObject) System.Windows.Markup.XamlReader.Load(fs);
}

XAML and the user interface definition

and if we look at the XAML file that defines the user interface it looks like this:


<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Foreground="Black"
Background="White"
UseLayoutRounding="True">
<StackPanel>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Label Foreground="Black">Enter message:</Label>
<TextBox Width="100" Name="input1" Margin="10"/>
</StackPanel>
<Button Name="button1"
Margin="10"
Width="96"
BorderThickness="2"
Content="Click Me" />
</StackPanel>
</Page>

The first lines are standard XAML for creating a page for a Window.

Next, a <StackPanel> controls the layout saying that everything below will be stacked vertically. Next, we have another <StackPanel> that says anything in it will be stacked horizontally. In there we add a <Label>, i..e. a text line and then a <TextBox> that accepts input from the user. Below the <StackPanel> we have a <Button>.

Connecting the button

We have now seen the definition of the user interface. Loading a XAML file dynamically like we do unfortunately mean that bindings does not work. Because of this we need to connect the button from the C# side and we do that like this:


Button btn = (Button)LogicalTreeHelper.FindLogicalNode(m_RootElement, "button1");
btn.Click += button1_Click;

The LogicalTreeHelper.FindLogicalNode function helps us find a UI element with the Name property set as “button1”, and we give the root of the loaded XAML file to search from. The function will return our Button and then we only need to hook up the Click event to the function we want to be called when the user clicks the button i.e. button1_Click

The last thing we do in the main window constructor is to set the content of the window to the loaded root element.
this.Content = m_RootElement;
This is what actually sets the loaded user interface to be displayed in the Window.

The script

The script starts as always at the static Run function. Here we create an object of our class and just call ShowDialog to show the window. When you run the script you will see the user interface. If you click the button we take what text has been input in the text box and shows that in a popup window.

The last thing to explain is the Main function. It is only needed because WPF expects the Main function when you compile in Visual Studio, it is never called or needed when running the C# file as a script in Fast Video cataloger.

WPF is a great option for building user interface and is highly recommended when you build a custom video solution on top of Fast video cataloger.

Complete XAML file


<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Foreground="Black"
Background="White"
UseLayoutRounding="True">
<StackPanel>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<Label Foreground="Black">Enter message:</Label>
<TextBox Width="100" Name="input1" Margin="10"/>
</StackPanel>
<Button Name="button1"
Margin="10"
Width="96"
BorderThickness="2"
Content="Click Me" />
</StackPanel>
</Page>

Complete C# file


//css_ref WindowsBase;
//css_ref PresentationCore;
//css_ref PresentationFramework;
//css_ref System.Runtime
//css_ref System.ObjectModel

using System;
using System.Xaml;
using System.Windows;
using System.IO;
using System.Windows.Controls;

namespace VideoCataloger
{

public partial class HelloWindow : Window
{
DependencyObject m_RootElement;

public HelloWindow()
{
Width = 320;
Height = 200;
Title = "Dynamic WPF window";

string current_folder = Directory.GetCurrentDirectory();
string path = current_folder + "\\scripts\\samples\\hello_wpf.xaml";
using (FileStream fs = new FileStream( path, FileMode.Open))
{
m_RootElement = (DependencyObject) System.Windows.Markup.XamlReader.Load(fs);
}

Button btn = (Button)LogicalTreeHelper.FindLogicalNode(m_RootElement, "button1");
btn.Click += button1_Click;

this.Content = m_RootElement;
}

private void button1_Click(object sender, RoutedEventArgs e)
{
TextBox textbox = (TextBox) LogicalTreeHelper.FindLogicalNode(m_RootElement, "input1");
MessageBox.Show("Message is: " + textbox.Text);
}

[STAThread]
public static void Main()
{
}

static public void Run(IScripting scripting, string argument)
{
HelloWindow wnd = new HelloWindow();
wnd.ShowDialog();
}

}
}

How to combine video clips using ffmpeg and scripting

Combining video clips

Merging lots of short video clips into one longer video file can be a great idea. But if you’ve tried to manually merge videos in the past, you may know how difficult this can be. Not any longer!

Concatenating video clips with FFmpeg can be done loss-less, and it is super easy thanks to Fast video cataloger and some scripting.

This tutorial will show you have to write a script to, for example ,combine a list of video clips from your phone into a longer clip, or perhaps combining short clips you have downloaded from the internet.

Fast video cataloger gives you a good overview of all your videos regardless of how long they are.

Why not just use a video editor?

You normally edit videos using a video editing software, like the free Blender.

Tip: There is a little known video editor that comes with Windows 10. To launch it simply right-click a video and select open with “photo”. This editor allows lots of simple video editing operations like trimming a video or adding text.

But if your plan is to combine lots of small clips into a larger one, this is far more complicated than it needs to be. Worse, if you use normal video editing software, it is most likely going to recompress the video once you have finished editing. This not only takes time, but it will also reduce the quality of the video.

If the videos are the exact same dimension and have the exact same compression, you can probably concatenate them without recompression and quality loss.

And it’s not as complicated as it may sound.

Using FFMpeg

FFmpeg has this function build-in with its concat command, expplained in detail here.

With c# scripting in Fast video cataloger, we can create a simple script that takes your selected videos and concatenates them. If the selected files are similar we generate the needed FFmpeg command line and temporary files and launches the program. They are are not the same format we give an error message.

Extended video properties

This sample is hopefully doing something useful. But, it also shows how you can get and uses extended properties from videos in Fast video cataloger.

For this example to work we need the extended properties for video format, this is added by default in newer versions of Fast video cataloger if you have the right checkbox set in preferences (the default is on).

Extracting meta data from video

Setting for extracting meta data during the video indexing process.

Extended video properties

Extended video properties are displayed in the video properties window

We write the script so that if the properties are missing, we will just accept the video and let FFmpeg present the error message instead when there are issues.

The script

I have organized the whole script as a class so you can continue to build on it or bring it into other scripts easy. However, everything is really done in the Concat() member function to make it easy to follow.

Entry function

As always, all scripts start in the run function

static public void Run(IScripting scripting, string argument)

Here we just do a simple check that more than one video is selected. Then create the Concat class and call the Concat function on the newly created Merger object.

The concat class constructor simply takes the root script interface and saves it as a member.

The Concat() member function, as said before, is where we do everything.

FFmpeg

The first thing we do is getting the path to the FFmpeg command-line tool. If you have not already downloaded it you can do that from here: https://ffmpeg.org/ and make sure it is installed in c:\\ffmpeg\\bin\\ffmpeg.exe (or update the script to the path where it is installed).

Temporary file for listing the video files

Next, we need to create a temporary file. The temporary file will be the list of videos to concatenate that will be passed to FFmpeg.

string tmp_file_path = System.IO.Path.GetTempFileName();

Will generate a unique temporary filename in your “tmp” folder. (NOTE that if you have too many files in your tmp folder creating files there will fail. This is actually a pretty common cause for random Windows problems. I.e we need to make sure we delete the file when we are done)

var stream_writer = System.IO.File.CreateText(tmp_file_path);

Will create a text with the temp file name in your temp folder.

Checking if videos are compatible

Next, we will check if the format is similar for all videos selected. This is done by iterating over all videos and getting all extended properties for that video


var extended = catalog.GetVideoFileExtendedProperty((int)video_id);
foreach (var prop in extended )
{
// here is where we do the checking
}

and the check for video format looks like this. (In a real-world solution you would write a shared check function taking arguments on what to check but for this short example I think it would be harder to follow, so copy-paste it is )

if (prop.Property == "video_Format")
{
  if (video_format == null)
  {
    video_format = prop.Value;
    m_scripting.GetConsole().WriteLine( video_path + " - Video format is "  + video_format );
  }
  else if (video_format != prop.Value)
  {
    string msg = "Aborting. All videos must be in the same video format.\n'";
    msg += video_path + "' is in " + prop.Value + "\n";
    msg += " previous was in " + video_format + "\n";
    m_scripting.GetConsole().WriteLine( msg);
    can_do_pure_concat = false;
  }
}

 

The time we see the property we check we store it, the next time we compare it to what was stored. If they are not the same we print an error message and flag the that the list of videos is incompatible can_do_pure_concat = false. If you want to extend on this script you could use this flag and generate a different command line for re-encoding the videos.

Picking output video path

If we know the videos are compatible it is time to present a save dialog.


Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.FileName = "concat" + extension;
dlg.DefaultExt = extension;
dlg.Filter = "All files|*.*";
Nullable result = dlg.ShowDialog();

Since we know the files are of the same format we suggest the file extension to be the same as the first video in the list.

Running FFMpeg

Now we have the list of source files, we know they are compatible and we know where we want the output file. It is time to call Fmpeg and merge the video.


System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = "cmd.exe";
string cmd_line = " -f concat -safe 0 -i " + tmp_file_path + conversion_parameters + " -c copy \"" + out_file + "\"";
startInfo.Arguments = "/C " + tool_path + " " + cmd_line;
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();

The /C switch to the windows command-line will close the window once the script has run to its end. If you instead use /K the window will remain on screen. That might be a good idea if you want to read error messages from FFMpeg.

Cleanup and playing the new video

Afte the command has run we should have a video. We need to clear up the temp file

File.Delete(tmp_file_path);

and finally, we use the windows shell to show the new video outside of Fast video cataloger.

System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo()
{
FileName = out_file,
UseShellExecute = true,
Verb = "open"
});

Running the script

To run the script load it into the script window in Fast video cataloger. Select the videos you want to concatenate and then click run. The program will check the file and present you with a save dialog to save the merged video and then play it with the default video player.

Here is the full script:

using System.Collections.Generic;
using VideoCataloger;
using System.IO;
using System;

public class ConcatVideos
{
static public void Run(IScripting scripting, string argument)
{
scripting.GetConsole().Clear();
ISelection selection = scripting.GetSelection();
List selected = selection.GetSelectedVideos();
if (selected.Count == 1)
{
scripting.GetConsole().WriteLine(“Select more than one video”);
return;
}

ConcatVideos merger = new ConcatVideos(scripting);
merger.Concat(null,null);
}

IScripting m_scripting = null;

ConcatVideos(IScripting scripting)
{
m_scripting = scripting;
}

public string GetFFMPEGPath()
{
string tool_path = “c:\\ffmpeg\\bin\\ffmpeg.exe”;
if (!File.Exists(tool_path))
{
System.Windows.MessageBox.Show(tool_path, “ffmpeg missing”);
}
return tool_path;
}

private void Concat( string out_file, string conversion_parameters )
{
string tool_path = GetFFMPEGPath();
string tmp_file_path = System.IO.Path.GetTempFileName();

var stream_writer = System.IO.File.CreateText(tmp_file_path);
if (stream_writer == null)
{
System.Windows.MessageBox.Show(tool_path, “Failed to write temporary text file”);
return;
}

ISelection selection = m_scripting.GetSelection();
IUtilities utilities = m_scripting.GetUtilities();
var catalog = m_scripting.GetVideoCatalogService();
string extension = null;
string video_format = null;
string video_width = null;
string video_height = null;
string audio_format = null;
List selected = selection.GetSelectedVideos();
bool can_do_pure_concat = true;
foreach (long video_id in selected)
{
var entry = catalog.GetVideoFileEntry(video_id);
string video_path = utilities.ConvertToLocalPath(entry.FilePath);

var extended = catalog.GetVideoFileExtendedProperty((int)video_id);
foreach (var prop in extended )
{
if (prop.Property == “video_Format”)
{
if (video_format == null)
{
video_format = prop.Value;
m_scripting.GetConsole().WriteLine( video_path + ” – format ” + video_format );
}
else if (video_format != prop.Value)
{
string msg = “Aborting. All videos must be in the same video format.\n'”;
msg += video_path + “‘ is in ” + prop.Value + “\n”;
msg += ” previous was in ” + video_format + “\n”;
m_scripting.GetConsole().WriteLine( msg);
can_do_pure_concat = false;
}
}
else if (prop.Property == “video_Width”)
{
if (video_width == null)
{
video_width = prop.Value;
m_scripting.GetConsole().WriteLine(video_path + ” – width ” + video_width);
}
else if (video_width != prop.Value)
{
string msg = “Aborting. All videos must be in the same dimension.\n'”;
msg += video_path + “‘ width is ” + prop.Value + “\n”;
msg += ” previous was in ” + video_width + “\n”;
m_scripting.GetConsole().WriteLine(msg);
can_do_pure_concat = false;
}
}
if (prop.Property == “video_Height”)
{
if (video_height == null)
{
video_height = prop.Value;
m_scripting.GetConsole().WriteLine(video_path + ” – height ” + video_height);
}
else if (video_height != prop.Value)
{
string msg = “Aborting. All videos must be in the same dimension.\n'”;
msg += video_path + “‘ height is ” + prop.Value + “\n”;
msg += ” previous was in ” + video_height + “\n”;
m_scripting.GetConsole().WriteLine(msg);
can_do_pure_concat = false;
}
}
if (prop.Property == “audio_Format”)
{
if (audio_format == null)
{
audio_format = prop.Value;
m_scripting.GetConsole().WriteLine(video_path + ” – Audio ” + audio_format);
}
else if (audio_format != prop.Value)
{
string msg = “Aborting. All videos must be in the same audio format.\n”;
msg += video_path + “is in ” + prop.Value + “\n”;
msg += ” previous was in ” + audio_format + “\n”;
m_scripting.GetConsole().WriteLine(msg);
can_do_pure_concat = false;
}
}
}
stream_writer.Write(“file ‘” + video_path + “‘” );
stream_writer.WriteLine();

if (!can_do_pure_concat)
return;
if (extension==null)
{
int extension_start = video_path.LastIndexOf(“.”);
extension = video_path.Substring(extension_start);
}
}
stream_writer.Close();

if (out_file == null)
{
Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.FileName = “concat” + extension;
dlg.DefaultExt = extension;
dlg.Filter = “All files|*.*”;
Nullable result = dlg.ShowDialog();
if (result == false)
{
return;
}
out_file = dlg.FileName;
}

System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = “cmd.exe”;
string cmd_line = ” -f concat -safe 0 -i ” + tmp_file_path + conversion_parameters + ” -c copy \”” + out_file + “\””;
startInfo.Arguments = “/C ” + tool_path + ” ” + cmd_line; // use /K instead of /C to keep the cmd window up
process.StartInfo = startInfo;

m_scripting.GetConsole().WriteLine(“Running ” + startInfo.Arguments);

process.Start();
process.WaitForExit();

File.Delete(tmp_file_path);

System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo()
{
FileName = out_file,
UseShellExecute = true,
Verb = “open”
});

}
}

Writing scripts for Fast video cataloger

Writing scripts for Fast video cataloger

Fast video cataloger supports scripting in C#. You write script straight into he scripts window and run them by clicking run. If you want to be efficient it pays to spend a few minutes and setup a proper development environment.

Environment

Fast video cataloger has a text editor but it is really not great for writing code. It has no syntax highlighting and there is no help with the APIs. I highly recommend that you download visual studio from Microsoft, and download the real visual studio not the code version.

Downlloado visual studio

With Visual Studio, you will get a proper editor that knows C# and will provide syntax coloring. We have also created a Visual Studio project with all the samples. This solution let you compile all the samples to catch simple syntax errors right in the environment. The solution produces an output file but that one is of no practical use so ignore that artifact. The only use of the visual studio solution is as a help for writing scripts.

Another benefit of using the provided solution is that you will get full IntelliSense to help with the scripting API as well as with any other .net library you might want to use. Add your own script to the solution and develop them there.

The documentation

When you install Fast video cataloger there are two documentations installed. The normal program documentation and scripting documentation in the form of a “scripting.chm” in the install folder. The scripting documentation details the whole scripting API and what you can do. Scripting in Fast video cataloger is focused on the actual catalog and not the user interface. You can make a lot of changes directly to the catalog but for the user interface to reflect the changes you need to explicitly call the Refresh() function from the script to force the user interface to reload from the state of the catalog.

There are also higher-level user interface functions in the program that are not exposed to scripting. For example, the repair functions can not be scripted. It is possible to write your own repair function using the API but we have not exposed the high-level user interface. Scripting by clicking menus and buttons in the interface should be possible using ui automation tools but that is the type of native scripting we talk about here.

The samples

When you install fast video cataloger a number of sample scripts are also installed in the Scripts/Samples folders. They are documented in the scripting documentation and the visual studio solution loads them all so it is easy to look at the code.

Writing your own scripts

When starting writing your own scripts I highly recommend starting with one of the supplied scripts. Find the script that is most close to what you want to do. Make a copy of that script. Add it to the sample solution and change the class name to something suitable. Build the solution to ensure it compiles and continue from there.

How to run scripts

Scripts are run from the script window as mentioned before. When you run a script you can also provide a string as an argument. Use this as a way to customize your script without having to edit the actual script. You load a script by clicking the “Load” button. If you are working with the script in visual studio you can reload the same script from the same file by holding Shift when clicking the Load button. That way you don’t need to pick the same file every time you need to reload the script as you are developing it in visual studio. (Selecting run in Visual studio will not work as we are not compiling a program and there is no way for Fast video cataloger to know you have clicked F5 and that one of your scripts is loaded in the program)

Setting up an action button for a script

Once you have a script you are happy about and expect to use often as part of your workflow it is time to bind an action to it. Actions are created from the preference dialog.

Action to run a script.

An Action will create a button in the Action window, in the “Custom Action” area and you can run your script just by clicking that button.

If clicking the button does not work there is probably something wrong with your script. Remember that the script is run and error messages are printed in the console window.

Setting a shortcut to a script

If you have a script you really use a lot it is possible to first create an Action and then bind a shortcut to that Action from the Shortcut window.

References

Most of the samples provided use the same resources as the standard .net library, these references are included by default and you only need to add a “using” line at the top of your script. If you want to use references that are not available by default you will need to add a comment starting with “//css_ref” followed by the reference you want. So for example //css_ref PresentationCore; references the Presentation core.



//css_ref WindowsBase;
//css_ref PresentationCore;
//css_ref PresentationFramework;
//css_ref System.Runtime
//css_ref System.ObjectModel

This is similar to a C# application where you would add a reference in the visual studio solution. To work with the sample solution you would need to include the reference in the solution to get it to compile and the //cs_ref line to get it running inside Fast video cataloger.

WPF and XAML resources

Since Fast video cataloger is running scripts and compiling the c# file when the script is loaded it won’t work with compiled XAML files. It is still possible to use WPF but you will need to load the XAML file dynamically.

If you have not done so already, download Fast video cataloger and try to write some scripts.