UPDATE: I originally posted this blog in October 2012. At that time the REST API service version was 1.0. A few days back, Media Services team released a new version of REST API (version 2.0). There are some changes in version 2.0 over version 1.0 so I decided to update this blog post.
In the last post about Windows Azure Media Service (Media Service), I talked about setup, a little bit of API and how to get access token from Windows Azure Access Control Service (ACS). You can read that post here: https://gauravmantri.com/2012/10/08/windows-azure-media-service-part-ii-setup-api-and-access-token/.
In this post, I’ll talk about managing assets from REST API point of view. However before we do that, first we need to know how to connect to media service. So let’s start!
Connecting to the Media Service
Default URI for connecting to media service is https://media.windows.net/. When implementing REST wrapper, we would need to first connect to this URI to find out if Media Service is redirecting us to a new URI or not. This can be identified by checking the response status. If we get a response status code of 301 (Moved Permanently), we should fetch the new URI from the “Location” response header and use that for subsequent requests.
Based on this, let’s look at the modified code for MediaServiceContext class. I’m including only the new changes I made for this purpose.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | private string _wamsEndpoint = "https://media.windows.net/"; /// <summary> /// Gets the endpoint for making REST API calls. /// </summary> public string WamsEndpoint { get { return _wamsEndpoint; } } /// <summary> /// This function checks if we need to redirect all Media Service requests. /// </summary> public void CheckForRedirection() { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(WamsEndpoint); request.AllowAutoRedirect = false; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, AccessToken)); request.Method = HttpVerbs.Get; using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.MovedPermanently) { string newLocation = response.Headers["Location"]; if (!newLocation.Equals(_wamsEndpoint)) { _wamsEndpoint = newLocation; _accessToken = string.Empty;//So that we can force to get a new access token. _accessTokenExpiry = DateTime.MinValue; } } } } |
If you notice, if we find the requests are redirected to another URI, we’re resetting the “_accessToken” variable. This is done so that we will re-fetch the access token now that we’re using a new URI. Another thing you will notice in the code above is the we’re forcing the request to not auto-redirect to the new URI. This is important because based on the documentation, HTTP verbs and request bodies will not be forwarded to the new URI.
So the process would be:
- Create an instance of MediaServiceContext class.
- Fetch access token.
- Check if we need to use a new URI instead of default URI.
To read more about this, please visit MSDN documentation here: http://msdn.microsoft.com/en-us/library/windowsazure/jj129576.aspx.
JSON or XML?
How about both? Bless the Media Service team for this . Here’s an interesting thing I found. Unlike other Windows Azure Services REST API (like Storage and Service Management) where XML is the only way to send and receive the management data, Media Service REST API actually supports both JSON and XML. I knew about JSON because REST API documentation talks about that. However I accidently found out that it supports XML as well (AtomPUB format). In fact the SDK communicates with the REST API in XML data format.
You need to tell REST API about the format in which you’re sending the data. You can do so by specifying the "content-type" of the request. For example:
1 2 3 4 5 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri); //Sending the data in JSON format. request.ContentType = "application/json;odata=verbose"; //Sending the data in XML format. request.ContentType = "application/atom+xml"; |
Similarly, you need to tell REST API about the format in which you wish to receive the data. You can do so by specifying the "Accept" property of the request. For example:
1 2 3 4 5 | HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUri); //Sending the data in JSON format. request.Accept = "application/json;odata=verbose"; //Sending the data in XML format. request.Accept = "application/atom+xml,application/xml"; |
Since I want to learn JSON, we’re going to focus on that in this series.
Managing Assets
Now let’s get back to topic at hand i.e. managing assets .
Definition
An asset can be defined as a logical entity which contains information about media. It may contain one more files (audio, video etc.) which needs to be processed.
Asset Entity
An asset entity defines the abstract concept of an asset in Media Service. You can read about the attributes of an asset entity here on MSDN: http://msdn.microsoft.com/en-us/library/windowsazure/hh974277.aspx#asset_entity_properties.
Based on this, we’ll create our own class. Let’s call it Asset. Here’s the code for that:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WAMSRestWrapper { /// <summary> /// Class representing an asset in a media service /// </summary> public class Asset { private readonly MediaServiceContext _context; /// <summary> /// Creates an instance of <see cref="Asset"/> /// </summary> /// <param name="context"> /// <see cref="MediaServiceContext"/> /// </param> internal Asset(MediaServiceContext context) { _context = context; } /// <summary> /// Creates an instance of <see cref="Asset"/> /// </summary> /// <param name="context"> /// <see cref="MediaServiceContext"/> /// </param> /// <param name="id"> /// Asset id /// </param> internal Asset(MediaServiceContext context, string id) : this(context) { Id = id; } /// <summary> /// Asset id. /// </summary> public string Id { get; internal set; } /// <summary> /// Asset state - Initialized (0), Deleted (1) [In Version 2.0, "Published" state is deprecated] /// </summary> public AssetState State { get; set; } /// <summary> /// Date/time asset is created. /// </summary> public DateTime Created { get; internal set; } /// <summary> /// Date/time asset was last modified. /// </summary> public DateTime LastModified { get; internal set; } /// <summary> /// Alternate id for asset. /// </summary> public string AlternateId { get; set; } /// <summary> /// Friendly name for asset. /// </summary> public string Name { get; set; } /// <summary> /// Encryption options for asset - None (0), StorageEncrypted (1), CommonEncryptionProtected (2) /// </summary> public AssetEncryptionOption Options { get; set; } } /// <summary> /// Enumeration indicating asset state. /// Please note that version 2.0, Publish Asset action is deprecated. Hence it is removed from here. /// </summary> public enum AssetState { Initialized = 0, //Published = 1, Deleted = 1, } /// <summary> /// Enumeration for asset encryption options. /// </summary> public enum AssetEncryptionOption { None = 0, StorageEncrypted = 1, CommonEncryptionProtected = 2, } } |
Operations
REST API allows you to perform following operations on an asset:
- Create
- Delete
- Update
- Publish (As of version 2.0, this operation is now deprecated)
- List
- Rest of the blog post will be focused on these operations and we’ll write some code to implement these operations.
Create
As the name suggests, this operation creates a new asset in Media Service for a given media service. A few things to consider here:
Endpoint | https://media.windows.net/Assets OR the URI received when we checked for redirection (see "Connecting to the Media Service" section above) and append “Assets” to that. |
HTTP Method | POST |
Request Headers | DataServiceVersion MaxDataServiceVersion x-ms-version Authorization |
Request Content Type | application/json;odata=verbose |
Accept | application/json;odata=verbose |
Request Body Format | { "Name": "<Asset Name>", "State": "0", "Options":"<Asset encryption option (as Integer)", "Created":"<Asset creation date in yyyy-MM-ddT00:00:00 format>", "LastModified":"<Asset last modified date in yyyy-MM-ddT00:00:00 format>" } e.g. { "Name": "Test Asset", "State": "0", "Options":"0", "Created":"0001-01-01T000000", "LastModified":"0001-01-01T000000" } |
Here’s the code for creating an asset:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | /// <summary> /// Creates an asset /// </summary> public void Create() { if (string.IsNullOrWhiteSpace(Name)) { Name = Guid.NewGuid().ToString(); } string assetCreateRequestPayloadFormat = @"{0} ""Name"": ""{1}"", ""State"": ""{2}"", ""Options"":""{3}"", ""Created"":""{4}"", ""LastModified"":""{5}"" {6}"; string requestBody = string.Format(CultureInfo.InvariantCulture, assetCreateRequestPayloadFormat, "{", Name, (int)State, (int)Options, Created.ToString("yyyy-MM-ddTHH:mm:ss"), LastModified.ToString("yyyy-MM-ddTHH:mm:ss"), "}"); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets/", _context.WamsEndpoint)); request.Method = HttpVerbs.Post; request.ContentType = RequestContentType.Json; request.Accept = RequestContentType.Json; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, _context.AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); request.ContentLength = Encoding.UTF8.GetByteCount(requestBody); using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream())) { streamWriter.Write(requestBody); } using (var response = (HttpWebResponse)request.GetResponse()) { using (StreamReader streamReader = new StreamReader(response.GetResponseStream(), true)) { var returnBody = streamReader.ReadToEnd(); JObject responseJsonObject = JObject.Parse(returnBody); var d = responseJsonObject["d"]; Id = d.Value<string>("Id"); State = (AssetState)d.Value<int>("State"); Options = (AssetEncryptionOption)d.Value<int>("Options"); AlternateId = d.Value<string>("AlternateId"); Created = d.Value<DateTime>("Created"); LastModified = d.Value<DateTime>("LastModified"); } } } |
and the helper code for calling this function:
1 2 3 | var asset = context.GetAssetReference(); asset.Name = "My 1st Media Service Asset"; asset.Create(); |
Delete
As the name suggests, this operation deletes an existing asset in Media Service for a given media service. A few things to consider here:
Endpoint | https://media.windows.net/Assets(‘<assetid>’) OR the URI received when we checked for redirection (see "Connecting to the Media Service" section above) and append “Assets(‘<assetid>’)” to that [Don’t forget to replace <assetid> with the actual asset id value ![]() |
HTTP Method | DELETE |
Request Headers | DataServiceVersion MaxDataServiceVersion x-ms-version Authorization |
Here’s the code for deleting an asset:
01 02 03 04 05 06 07 08 09 10 11 12 13 | /// <summary> /// Deletes the asset. /// </summary> public void Delete() { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets('{1}')", _context.WamsEndpoint, Id)); request.Method = HttpVerbs.Delete; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, _context.AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); var response = (HttpWebResponse)request.GetResponse(); } |
and the helper code for calling this function:
1 2 3 | var assetId = "<asset id e.g. nb:cid:uuid:63b0ddd2-5784-4bc7-b9df-85785eaf57a1>"; var asset = context.GetAssetReference(assetId); asset.Delete(); |
Please note that when an asset is deleted, all the files associated with that particular asset. If I understand the documentation correctly, one needs to delete locators and content keys associated with an asset before deleting it.
Update
This operation allows you to update some of the properties of an asset in Media Service for a given media service. Please note that not all properties of an asset can be updated. For example, none of the system defined properties like Created or LastModified etc. can be updated. Also Options properties can’t be updated as well.
A few things to consider here:
Endpoint | https://media.windows.net/Assets(‘<assetid>’) OR the URI received when we checked for redirection (see "Connecting to the Media Service" section above) and append “Assets(‘<assetid>’)” to that [Don’t forget to replace <assetid> with the actual asset id value ![]() |
HTTP Method | MERGE |
Request Headers | DataServiceVersion MaxDataServiceVersion x-ms-version Authorization |
Request Content Type | application/json;odata=verbose |
Request Body Format | { "Name": "<New Asset Name>", "AlternateId":"<Asset Alternate Id>" } e.g. { "Name": "New Test Asset", "AlternateId":"An alternate id" } |
Here’s the code for updating an asset:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | /// <summary> /// Updates an asset /// </summary> public void Update() { string assetCreateRequestPayloadFormat = @"{0} ""Name"": ""{1}"", ""AlternateId"": ""{2}"" {3}"; string requestBody = string.Format(CultureInfo.InvariantCulture, assetCreateRequestPayloadFormat, "{", Name, AlternateId, "}"); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets('{1}')", _context.WamsEndpoint, Id)); request.Method = HttpVerbs.Merge; request.ContentType = RequestContentType.Json; request.Accept = RequestContentType.Json; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, _context.AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); request.ContentLength = Encoding.UTF8.GetByteCount(requestBody); using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream())) { streamWriter.Write(requestBody); } var response = (HttpWebResponse)request.GetResponse(); } |
and the helper code for calling this function:
1 2 3 4 5 | var assetId = "<asset e.g. nb:cid:uuid:63b0ddd2-5784-4bc7-b9df-85785eaf57a1>"; var asset = context.GetAssetReference(assetId); asset.Name = "new asset name"; asset.AlternateId = Guid.NewGuid().ToString(); asset.Update(); |
Publish
PLEASE NOTE: As of version 2.0, this operation is now deprecated.
This operation allows you to publish an asset. Publishing an asset means telling Media Service that this asset is ready to be processed. This should be the last step of “Ingestion” process after you have uploaded all the files associated with an asset and done all other things which are required. Once an asset is published, you can’t make any changes to that asset.
A few things to consider here:
Endpoint | https://media.windows.net/Assets(‘<assetid>’)/Publish OR the URI received when we checked for redirection (see "Connecting to the Media Service" section above) and append “Assets(‘<assetid>’)/Publish” to that [Don’t forget to replace <assetid> with the actual asset id value ![]() |
HTTP Method | POST |
Request Headers | DataServiceVersion MaxDataServiceVersion x-ms-version Authorization |
Here’s the code for updating an asset:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | /// <summary> /// Publishes an asset /// </summary> public void Publish() { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets('{1}')/Publish", _context.WamsEndpoint, Id)); request.Method = HttpVerbs.Post; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, _context.AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); request.ContentLength = 0; var response = (HttpWebResponse)request.GetResponse(); } } |
and the helper code for calling this function:
1 2 3 | var assetId = "<asset id e.g. nb:cid:uuid:63b0ddd2-5784-4bc7-b9df-85785eaf57a1>"; var asset = context.GetAssetReference(assetId); asset.Publish(); |
Please ensure that the asset has at least one file uploaded otherwise the publish operation will fail.
List
As the name suggests, this operation fetches a list of assets associated with a media service. This function can also be used to fetch a single asset.
Listing All Assets:
Endpoint | https://media.windows.net/Assets OR the URI received when we checked for redirection (see "Connecting to the Media Service" section above) and append “Assets”. |
HTTP Method | GET |
Request Headers | DataServiceVersion MaxDataServiceVersion x-ms-version Authorization |
Here’s the code for fetching the list of all assets.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | /// <summary> /// List all assets in a media service. /// </summary> /// <returns> /// </returns> public IEnumerable<Asset> ListAssets() { List<Asset> assets = new List<Asset>(); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets", WamsEndpoint)); request.Accept = RequestContentType.Json; request.Method = HttpVerbs.Get; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); using (var response = (HttpWebResponse)request.GetResponse()) { using (StreamReader streamReader = new StreamReader(response.GetResponseStream(), true)) { var returnBody = streamReader.ReadToEnd(); JObject responseJsonObject = JObject.Parse(returnBody); var items = responseJsonObject["d"]["results"]; foreach (var d in items) { var Id = d.Value<string>("Id"); var State = (AssetState)d.Value<int>("State"); var Options = (AssetEncryptionOption)d.Value<int>("Options"); var AlternateId = d.Value<string>("AlternateId"); var Created = d.Value<DateTime>("Created"); var LastModified = d.Value<DateTime>("LastModified"); var Name = d.Value<string>("Name"); assets.Add(new Asset(this, Id) { Name = Name, State = State, Options = Options, AlternateId = AlternateId, Created = Created, LastModified = LastModified, }); } } } return assets; } |
and the helper code for calling this function:
1 | var assets = context.ListAssets(); |
Listing Single Asset:
Endpoint | https://media.windows.net/Assets(‘<assetid>’) OR the URI received when we checked for redirection (see "Connecting to the Media Service" section above) and append “Assets(‘<assetid>’)” to that [Don’t forget to replace <assetid> with the actual asset id value ![]() |
HTTP Method | GET |
Request Headers | DataServiceVersion MaxDataServiceVersion x-ms-version Authorization |
Here’s the code for fetching information about a single asset.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /// <summary> /// Gets information about an asset. /// </summary> public void Get() { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets('{1}')", _context.WamsEndpoint, Id)); request.Method = HttpVerbs.Get; request.Accept = RequestContentType.Json; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, _context.AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); using (var response = (HttpWebResponse)request.GetResponse()) { using (StreamReader streamReader = new StreamReader(response.GetResponseStream(), true)) { var returnBody = streamReader.ReadToEnd(); JObject responseJsonObject = JObject.Parse(returnBody); var d = responseJsonObject["d"]; Id = d.Value<string>("Id"); State = (AssetState)d.Value<int>("State"); Options = (AssetEncryptionOption)d.Value<int>("Options"); AlternateId = d.Value<string>("AlternateId"); Created = d.Value<DateTime>("Created"); LastModified = d.Value<DateTime>("LastModified"); } } } } |
and the helper code for calling this function:
1 2 3 | var assetId = "<asset e.g. nb:cid:uuid:63b0ddd2-5784-4bc7-b9df-85785eaf57a1>"; var asset = context.GetAssetReference(assetId); asset.Get(); |
Wrapping Up
In this blog post, we learnt about the REST API for managing assets in Media Service. Here’s the complete source code for what we have done so far. As I said in my previous post, this is definitely not the best code and desires a lot of optimization / refactoring. Please feel free to modify the code to suit your need.
AcsToken.cs
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WAMSRestWrapper { public class AcsToken { public string token_type { get; set; } public string access_token { get; set; } public int expires_in { get; set; } public string scope { get; set; } } } |
Constants.cs
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WAMSRestWrapper { /// <summary> /// Values for various request headers /// </summary> internal static class RequestHeaderValues { /// <summary> /// DataServiceVersion request header (3.0) /// </summary> internal const string DataServiceVersion = "3.0"; /// <summary> /// MaxDataServiceVersion request header (3.0) /// </summary> internal const string MaxDataServiceVersion = "3.0"; /// <summary> /// x-ms-version request header (2.0) /// </summary> internal const string XMsVersion = "2.0"; /// <summary> /// Authorization request header format /// </summary> internal const string Authorization = "Bearer {0}"; } /// <summary> /// Request header names. /// </summary> internal static class RequestHeaders { /// <summary> /// DataServiceVersion request header /// </summary> internal const string DataServiceVersion = "DataServiceVersion"; /// <summary> /// MaxDataServiceVersion request header /// </summary> internal const string MaxDataServiceVersion = "MaxDataServiceVersion"; /// <summary> /// x-ms-version request header /// </summary> internal const string XMsVersion = "x-ms-version"; /// <summary> /// Authorization request header /// </summary> internal const string Authorization = "Authorization"; } /// <summary> /// HTTP Verbs /// </summary> internal static class HttpVerbs { /// <summary> /// POST HTTP verb /// </summary> internal const string Post = "POST"; /// <summary> /// GET HTTP verb /// </summary> internal const string Get = "GET"; /// <summary> /// MERGE HTTP verb /// </summary> internal const string Merge = "MERGE"; /// <summary> /// DELETE HTTP verb /// </summary> internal const string Delete = "DELETE"; } internal static class RequestContentType { internal const string Json = "application/json;odata=verbose"; internal const string Atom = "application/atom+xml"; } } |
MediaServiceContext.cs
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.Web; using Newtonsoft.Json; using System.Globalization; using System.IO; using Newtonsoft.Json.Linq; namespace WAMSRestWrapper { public class MediaServiceContext { private const string acsEndpoint = "https://wamsprodglobal001acs.accesscontrol.windows.net/v2/OAuth2-13"; private const string acsRequestBodyFormat = "grant_type=client_credentials&client_id={0}&client_secret={1}&scope=urn%3aWindowsAzureMediaServices"; private string _accountName; private string _accountKey; private string _accessToken; private DateTime _accessTokenExpiry; private string _wamsEndpoint = "https://media.windows.net/"; /// <summary> /// Creates a new instance of <see cref="MediaServiceContext"/> /// </summary> /// <param name="accountName"> /// Media service account name. /// </param> /// <param name="accountKey"> /// Media service account key. /// </param> public MediaServiceContext(string accountName, string accountKey) { this._accountName = accountName; this._accountKey = accountKey; } /// <summary> /// Gets the access token. If access token is not yet fetched or the access token has expired, /// it gets a new access token. /// </summary> public string AccessToken { get { if (string.IsNullOrWhiteSpace(_accessToken) || _accessTokenExpiry < DateTime.UtcNow) { var tuple = FetchAccessToken(); _accessToken = tuple.Item1; _accessTokenExpiry = tuple.Item2; } return _accessToken; } } /// <summary> /// Gets the endpoint for making REST API calls. /// </summary> public string WamsEndpoint { get { return _wamsEndpoint; } } /// <summary> /// This function makes the web request and gets the access token. /// </summary> /// <returns> /// <see cref="System.Tuple"/> containing 2 items - /// 1. The access token. /// 2. Token expiry date/time. /// </returns> private Tuple<string, DateTime> FetchAccessToken() { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(acsEndpoint); request.Method = HttpVerbs.Post; string requestBody = string.Format(CultureInfo.InvariantCulture, acsRequestBodyFormat, _accountName, HttpUtility.UrlEncode(_accountKey)); request.ContentLength = Encoding.UTF8.GetByteCount(requestBody); request.ContentType = "application/x-www-form-urlencoded"; using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream())) { streamWriter.Write(requestBody); } using (var response = (HttpWebResponse)request.GetResponse()) { using (StreamReader streamReader = new StreamReader(response.GetResponseStream(), true)) { var returnBody = streamReader.ReadToEnd(); var acsToken = JsonConvert.DeserializeObject<AcsToken>(returnBody); return new Tuple<string, DateTime>(acsToken.access_token, DateTime.UtcNow.AddSeconds(acsToken.expires_in)); } } } /// <summary> /// This function checks if we need to redirect all WAMS requests. /// </summary> public void CheckForRedirection() { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(WamsEndpoint); request.AllowAutoRedirect = false; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, AccessToken)); request.Method = HttpVerbs.Get; using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.Moved || response.StatusCode == HttpStatusCode.MovedPermanently) { string newLocation = response.Headers["Location"]; if (!newLocation.Equals(_wamsEndpoint)) { _wamsEndpoint = newLocation; _accessToken = string.Empty;//So that we can force to get a new access token. _accessTokenExpiry = DateTime.MinValue; } } } } public Asset GetAssetReference(string assetId) { return new Asset(this, assetId); } public Asset GetAssetReference() { return new Asset(this); } /// <summary> /// List all assets in a media service. /// </summary> /// <returns> /// </returns> public IEnumerable<Asset> ListAssets() { List<Asset> assets = new List<Asset>(); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets", WamsEndpoint)); request.Accept = RequestContentType.Json; request.Method = HttpVerbs.Get; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); using (var response = (HttpWebResponse)request.GetResponse()) { using (StreamReader streamReader = new StreamReader(response.GetResponseStream(), true)) { var returnBody = streamReader.ReadToEnd(); JObject responseJsonObject = JObject.Parse(returnBody); var items = responseJsonObject["d"]["results"]; foreach (var d in items) { var Id = d.Value<string>("Id"); var State = (AssetState)d.Value<int>("State"); var Options = (AssetEncryptionOption)d.Value<int>("Options"); var AlternateId = d.Value<string>("AlternateId"); var Created = d.Value<DateTime>("Created"); var LastModified = d.Value<DateTime>("LastModified"); var Name = d.Value<string>("Name"); assets.Add(new Asset(this, Id) { Name = Name, State = State, Options = Options, AlternateId = AlternateId, Created = Created, LastModified = LastModified, }); } } } return assets; } } } |
Asset.cs
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Globalization; using System.Net; using System.IO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace WAMSRestWrapper { /// <summary> /// Class representing an asset in a media service /// </summary> public class Asset { private readonly MediaServiceContext _context; /// <summary> /// Creates an instance of <see cref="Asset"/> /// </summary> /// <param name="context"> /// <see cref="MediaServiceContext"/> /// </param> internal Asset(MediaServiceContext context) { _context = context; } /// <summary> /// Creates an instance of <see cref="Asset"/> /// </summary> /// <param name="context"> /// <see cref="MediaServiceContext"/> /// </param> /// <param name="id"> /// Asset id /// </param> internal Asset(MediaServiceContext context, string id) : this(context) { Id = id; } /// <summary> /// Asset id. /// </summary> public string Id { get; internal set; } /// <summary> /// Asset state - Initialized (0), Deleted (1) [In Version 2.0, "Published" state is deprecated] /// </summary> public AssetState State { get; set; } /// <summary> /// Date/time asset is created. /// </summary> public DateTime Created { get; internal set; } /// <summary> /// Date/time asset was last modified. /// </summary> public DateTime LastModified { get; internal set; } /// <summary> /// Alternate id for asset. /// </summary> public string AlternateId { get; set; } /// <summary> /// Friendly name for asset. /// </summary> public string Name { get; set; } /// <summary> /// Encryption options for asset - None (0), StorageEncrypted (1), CommonEncryptionProtected (2) /// </summary> public AssetEncryptionOption Options { get; set; } /// <summary> /// Creates an asset /// </summary> public void Create() { if (string.IsNullOrWhiteSpace(Name)) { Name = Guid.NewGuid().ToString(); } string assetCreateRequestPayloadFormat = @"{0} ""Name"": ""{1}"", ""State"": ""{2}"", ""Options"":""{3}"", ""Created"":""{4}"", ""LastModified"":""{5}"" {6}"; string requestBody = string.Format(CultureInfo.InvariantCulture, assetCreateRequestPayloadFormat, "{", Name, (int)State, (int)Options, Created.ToString("yyyy-MM-ddTHH:mm:ss"), LastModified.ToString("yyyy-MM-ddTHH:mm:ss"), "}"); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets/", _context.WamsEndpoint)); request.Method = HttpVerbs.Post; request.ContentType = RequestContentType.Json; request.Accept = RequestContentType.Json; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, _context.AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); request.ContentLength = Encoding.UTF8.GetByteCount(requestBody); using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream())) { streamWriter.Write(requestBody); } using (var response = (HttpWebResponse)request.GetResponse()) { using (StreamReader streamReader = new StreamReader(response.GetResponseStream(), true)) { var returnBody = streamReader.ReadToEnd(); JObject responseJsonObject = JObject.Parse(returnBody); var d = responseJsonObject["d"]; Id = d.Value<string>("Id"); State = (AssetState)d.Value<int>("State"); Options = (AssetEncryptionOption)d.Value<int>("Options"); AlternateId = d.Value<string>("AlternateId"); Created = d.Value<DateTime>("Created"); LastModified = d.Value<DateTime>("LastModified"); } } } /// <summary> /// Deletes the asset. /// </summary> public void Delete() { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets('{1}')", _context.WamsEndpoint, Id)); request.Method = HttpVerbs.Delete; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, _context.AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); var response = (HttpWebResponse)request.GetResponse(); } /// <summary> /// Updates an asset /// </summary> public void Update() { string assetCreateRequestPayloadFormat = @"{0} ""Name"": ""{1}"", ""AlternateId"": ""{2}"" {3}"; string requestBody = string.Format(CultureInfo.InvariantCulture, assetCreateRequestPayloadFormat, "{", Name, AlternateId, "}"); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets('{1}')", _context.WamsEndpoint, Id)); request.Method = HttpVerbs.Merge; request.ContentType = RequestContentType.Json; request.Accept = RequestContentType.Json; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, _context.AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); request.ContentLength = Encoding.UTF8.GetByteCount(requestBody); using (StreamWriter streamWriter = new StreamWriter(request.GetRequestStream())) { streamWriter.Write(requestBody); } var response = (HttpWebResponse)request.GetResponse(); } /// <summary> /// Publishes an asset. This function is deprecated in version 2.0. /// </summary> public void Publish() { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets('{1}')/Publish", _context.WamsEndpoint, Id)); request.Method = HttpVerbs.Post; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, _context.AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); request.ContentLength = 0; var response = (HttpWebResponse)request.GetResponse(); } /// <summary> /// Gets information about an asset. /// </summary> public void Get() { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(CultureInfo.InvariantCulture, "{0}Assets('{1}')", _context.WamsEndpoint, Id)); request.Method = HttpVerbs.Get; request.Accept = RequestContentType.Json; request.Headers.Add(RequestHeaders.XMsVersion, RequestHeaderValues.XMsVersion); request.Headers.Add(RequestHeaders.Authorization, string.Format(CultureInfo.InvariantCulture, RequestHeaderValues.Authorization, _context.AccessToken)); request.Headers.Add(RequestHeaders.DataServiceVersion, RequestHeaderValues.DataServiceVersion); request.Headers.Add(RequestHeaders.MaxDataServiceVersion, RequestHeaderValues.MaxDataServiceVersion); using (var response = (HttpWebResponse)request.GetResponse()) { using (StreamReader streamReader = new StreamReader(response.GetResponseStream(), true)) { var returnBody = streamReader.ReadToEnd(); JObject responseJsonObject = JObject.Parse(returnBody); var d = responseJsonObject["d"]; Id = d.Value<string>("Id"); State = (AssetState)d.Value<int>("State"); Options = (AssetEncryptionOption)d.Value<int>("Options"); AlternateId = d.Value<string>("AlternateId"); Created = d.Value<DateTime>("Created"); LastModified = d.Value<DateTime>("LastModified"); } } } } /// <summary> /// Enumeration indicating asset state. /// Please note that version 2.0, Publish Asset action is deprecated. Hence it is removed from here. /// </summary> public enum AssetState { Initialized = 0, //Published = 1, Deleted = 1, } /// <summary> /// Enumeration for asset encryption options. /// </summary> public enum AssetEncryptionOption { None = 0, StorageEncrypted = 1, CommonEncryptionProtected = 2, } } |
Summary
That’s it for this post. In the next post, we will deal with more REST API functionality and expand this library. I hope you have found this information useful. As always, if you find some issues with this blog post please let me know immediately and I will fix them ASAP.
So long and stay tuned!