Media in the mesh

Hello again, last time I covered how to create and view Data Entries within DataFeeds and also said about looking into MediaResouces.

Over the past week I have been creating an extension for the console application and allowing the browsing of everything in the Mesh.  Also the local client for the mesh.

It is possible to run both the Mesh Beta (from www.mesh.com) and the developer client (from developer.mesh-ctp.com) at the same time by firstly installing the Mesh Beta, adding the registry key and value

HKCU\Software\Microsoft\Live Framework Client\AllowSxS = 1 (DWORD)

then installing the developer client… and it works like a charm :)

https://blogs.technet.com/james/archive/2009/01/30/running-mesh-beta-client-at-the-same-time-as-live-framework-ctp-client.aspx

This allows the user to be logged in automatically without having to give their credentials.

So a slight aside from the walk-through, how to stream media files…

You can tell where a media resource is via the data entry’s Resource.EditMediaLink property.

therefore given a DataFeed (currentDataFeed) you can list them by:

 foreach (DataEntry de in currentDataFeed.DataEntries.Entries)
{
   if (de.Resource.MediaResource != null)
   {
      Console.WriteLine("{0}\t{1}\t{2}", de.Resource.MediaResource.Name, de.Resource.MediaResource.MediaType, de.Resource.MediaResource.LocalPath);
      Console.WriteLine("Edit link is:\r\n{0}", de.Resource.EditMediaLink);
   }
   else
   {
      Console.WriteLine("{0} does not have a media resource associated", de.Resource.Title);
   }
}

While this is all very useful, we don’t really want the user going to a url for a binary feed for their data, so how can we stream it?

the DataEntry has a nice method called ReadMediaResource which returns a Stream object (which under the covers is a Microsoft.Web.Clients.HttpClient.ResponseStreamWrapper) and so we can keep using ReadByte to read it until ReadByte returns –1 [EOF].

 Stream reader = null;
foreach (DataEntry de in currentDataFeed.DataEntries.Entries)
{
   Console.WriteLine("\r\n\t{0}",de.Resource.Title);
   try
   {
      using (reader = de.ReadMediaResource())
      {
         int byt = reader.ReadByte();
         while (byt != -1)
         {
            if (args.Contains("t"))
            {
               Console.Write((char)byt);
            }
            else
            {
               Console.Write(byt + ",");
            }
            byt = reader.ReadByte();
         }
         Console.WriteLine();
      }
   }
   catch (InvalidOperationException ex)
   {
      Console.WriteLine("Can not open the Media Resource for this Data Entry \r\n{0}", ex.Message);
   }
}

This method takes a string array called args, and is checking if a “t” flag is given to actually write the bytes out as chars (not recommended but you can do for text files).

Now as there is only one data feed… how will this work for folders? Surely you would need files under folders?

In both a good, and bad way, the files hierarchy in the datafeed is flat. Good as we don’t have to use recursion to go down folder structures, but bad as if you want to download the files, you have to ensure that the folders are there before the files, as you could get a file listing before the folder it is contained in.  Fortunately each item has a unique identifier and the collection is LINQ queriable which makes this a whole lot simpler than it would be.

 private static void DownloadMedia(string folder)
{
   if (folder == null || folder.Equals(string.Empty))
   {
      Console.WriteLine("Folder must be specified");
      return;
   }
   Hashtable foldersGenerated = new Hashtable();
   var foldersToGenerate = (from i in currentDataFeed.DataEntries.Entries
                      where i.Resource.Type.Equals("Folder")
                      select i);
   var files = (from i in currentDataFeed.DataEntries.Entries
             where !i.Resource.Type.Equals("Folder")
             select i);
   // create folders first
   foreach (DataEntry de in foldersToGenerate)
   {
      Console.WriteLine("Creating folder {0} in {1}", de.Resource.Title, folder + "\\" + foldersGenerated[de.Resource.ParentId]);
      Directory.CreateDirectory(folder + "\\" + foldersGenerated[de.Resource.ParentId] + de.Resource.Title);
      Console.WriteLine("Folder created");
      Console.WriteLine();
      if (foldersGenerated.ContainsKey(de.Resource.ParentId))
      {
         foldersGenerated.Add(de.Resource.Id, foldersGenerated[de.Resource.ParentId].ToString() + de.Resource.Title + "\\");
      }
      else
      {
         foldersGenerated.Add(de.Resource.Id, de.Resource.Title + "\\");
      }
   }
   // now add items into folders.
   foreach (DataEntry de in files)
   {
      Console.WriteLine("Downloading {0} to {1}", de.Resource.Title, folder + "\\" + foldersGenerated[de.Resource.ParentId]);
      try
      {
         if (!de.Resource.Type.Equals("Folder"))
         {
            using (FileStream fileWriter = new FileStream(folder + "\\" + foldersGenerated[de.Resource.ParentId] + de.Resource.Title, FileMode.OpenOrCreate, FileAccess.Write))
            {
               using (Stream reader = de.ReadMediaResource())
               {
                  int byt = reader.ReadByte();
                  while (byt != -1)
                  {
                     fileWriter.WriteByte((byte)byt);
                     byt = reader.ReadByte();
                  }
                  fileWriter.Flush();
                  fileWriter.Close();
                  reader.Close();
                  Console.WriteLine();
               }
            }
         }
      }
      catch (InvalidOperationException ex)
      {
         Console.WriteLine("Can not open the Media Resource for this Data Entry \r\n{0}", ex.Message);
      }
   }
}

This method uses a Hashtable to keep track of the folder hierarchy with a key/value pairing… the key being the Folder Id and the value being the relative location of this file.  The path for each item is checked with foldersGenerated[de.Resource.ParentId] which will output nothing (null) if the parent key is not found, otherwise the relative path if the key is found.

I have also been looking at Mike Taulty’s Photo Application for the writing of the streams and it seems that it is a similar process…

https://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2009/01/29/live-framework-sdk-revised-wpf-photo-sharing-application.aspx

 static void CreateNewDataEntriesFromPhotographs(DataFeed df, string[] photographFiles)
{
  foreach (string fileName in photographFiles)
  {
   try
   {
     using (FileStream fs = File.OpenRead(fileName))
     {
      df.DataEntries.Add(fs, Path.GetFileNameWithoutExtension(fileName));
      fs.Close();
     }
   }
   catch
   {
   }
  }
}

This takes the datafeed (as we did above) and a file stream (to read from, not write to this time), and then adds it.  As this overload of Add takes a Stream, in theory you could point it to a file, internet stream or wherever and add it to your mesh.

Next blog post I will get back onto the walkthrough and do some stuff with inviting people to view your data.