diff --git a/.nuget/windowsazure.mediaservices.nuspec b/.nuget/windowsazure.mediaservices.nuspec index 6fde89a6..4f0a8d7e 100644 --- a/.nuget/windowsazure.mediaservices.nuspec +++ b/.nuget/windowsazure.mediaservices.nuspec @@ -2,7 +2,7 @@ windowsazure.mediaservices - 3.3.0.0 + 3.4.0.0 Windows Azure Media Services .NET SDK Microsoft Corporation Microsoft Corporation @@ -10,7 +10,7 @@ http://aka.ms/wamsmsdn http://nimbuspmteam.blob.core.windows.net/nuget/Media Services 150.png true - This package contains Windows Azure Media Service library 3.3.0.0 for .NET. + This package contains Windows Azure Media Service library 3.4.0.0 for .NET. For more information, please check our forum: http://social.msdn.microsoft.com/Forums/en-US/MediaServices/threads For MSDN documentation please visit http://aka.ms/wamsmsdn diff --git a/Readme.md b/Readme.md index 943810dd..ccbe60c7 100644 --- a/Readme.md +++ b/Readme.md @@ -3,6 +3,7 @@ Windows Azure Media Services allows you to build a media distribution solution that can stream audio and video to Windows, iOS, Android, and other devices and platforms.To learn more, visit our [Developer Center](http://www.windowsazure.com/en-us/develop/media-services/). + ## Release Notes Please read the latest note here: https://github.com/Azure/azure-sdk-for-media-services/releases. diff --git a/build.proj b/build.proj index 70d4ad80..4cb4bf09 100644 --- a/build.proj +++ b/build.proj @@ -99,7 +99,7 @@ - + @@ -149,7 +149,7 @@ ContinueOnError="false" /> - + diff --git a/ci/SignMedia.bat b/ci/SignMedia.bat index 379fd169..adc40770 100644 --- a/ci/SignMedia.bat +++ b/ci/SignMedia.bat @@ -1,36 +1,17 @@ -@echo off - -echo Cleaning signed and packages directories -del /q c:\signing\signed\*.* -del /q c:\packages\*.* - -echo Copying managed desktop library DLLs to signing source directory -copy /y .\Publish\Build\Release\Microsoft.WindowsAzure.MediaServices.Client.dll c:\signing\tosign\ -copy /y .\Publish\Build\Release\Microsoft.WindowsAzure.MediaServices.Client.Common.FileEncryption.dll c:\signing\tosign\ -copy /y .\Publish\Build\Release\Microsoft.WindowsAzure.MediaServices.Client.Common.BlobTransfer.dll c:\signing\tosign\ -if %ERRORLEVEL% neq 0 goto copyfailed -echo OK - -echo Signing managed desktop library DLLs... -%CI_SIGNING%\CodeSignUtility\csu.exe /c1=72 /c2=10006 "/d=.NET SDK" "/kw=MediaServices" -if %ERRORLEVEL% neq 0 goto signfailed -echo OK - -echo Removing all unsigned files from \\adksdksign\unsigned... -del /q c:\signing\tosign\*.* -echo OK echo Copying Media SDK signed managed DLLs and the pdbs to the final drop location... echo Creating \drop\WAMSSDK\lib\net45 md .\drop\WAMSSDK\lib\net45 + echo Copy MediaServices.Client.dll -copy /y c:\signing\signed\Microsoft.WindowsAzure.MediaServices.Client.dll .\drop\WAMSSDK\lib\net45\ +copy /y \\adxsdkbuilder\Signed\Microsoft.WindowsAzure.MediaServices.Client.dll .\drop\WAMSSDK\lib\net45\ echo Copy Microsoft.WindowsAzure.MediaServices.Client.Common.FileEncryption.dll -copy /y c:\signing\signed\Microsoft.WindowsAzure.MediaServices.Client.Common.FileEncryption.dll .\drop\WAMSSDK\lib\net45\ +copy /y \\adxsdkbuilder\Signed\Microsoft.WindowsAzure.MediaServices.Client.Common.FileEncryption.dll .\drop\WAMSSDK\lib\net45\ echo Copy Microsoft.WindowsAzure.MediaServices.Client.Common.BlobTransfer.dll -copy /y c:\signing\signed\Microsoft.WindowsAzure.MediaServices.Client.Common.BlobTransfer.dll .\drop\WAMSSDK\lib\net45\ +copy /y \\adxsdkbuilder\Signed\Microsoft.WindowsAzure.MediaServices.Client.Common.BlobTransfer.dll .\drop\WAMSSDK\lib\net45\ echo Copy MediaServices.Client.pdb copy /y .\Publish\Build\Release\Microsoft.WindowsAzure.MediaServices.Client.pdb .\drop\WAMSSDK\lib\net45\ + echo Copy Nuget spec copy /y .\.nuget\windowsazure.mediaservices.nuspec .\drop\WAMSSDK\ if %ERRORLEVEL% neq 0 goto copyfailed @@ -46,13 +27,7 @@ copy .\drop\*.* c:\packages if %ERRORLEVEL% neq 0 goto copyfailed echo OK -echo Removing all signed files from \\adksdksign\unsigned... -del /q c:\signing\signed\*.* -echo OK -echo Removing all unsigned files from \\adksdksign\unsigned... -del /q c:\signing\tosign\*.* -echo OK echo SUCCESS. Signed files are available at \\adxsdksign\packages diff --git a/src/net/Client/AzureMediaServicesClassFactory.cs b/src/net/Client/AzureMediaServicesClassFactory.cs index 8e907303..732326f9 100644 --- a/src/net/Client/AzureMediaServicesClassFactory.cs +++ b/src/net/Client/AzureMediaServicesClassFactory.cs @@ -14,13 +14,16 @@ // limitations under the License. // +using Microsoft.WindowsAzure.MediaServices.Client.OAuth; +using Microsoft.WindowsAzure.MediaServices.Client.RequestAdapters; +using Microsoft.WindowsAzure.MediaServices.Client.TransientFaultHandling; using System; +using System.Collections.Generic; using System.Data.Services.Client; using System.Data.Services.Common; +using System.Linq; using System.Net; -using Microsoft.WindowsAzure.MediaServices.Client.OAuth; -using Microsoft.WindowsAzure.MediaServices.Client.RequestAdapters; -using Microsoft.WindowsAzure.MediaServices.Client.TransientFaultHandling; + namespace Microsoft.WindowsAzure.MediaServices.Client { @@ -49,7 +52,7 @@ public class AzureMediaServicesClassFactory : MediaServicesClassFactory private const int ConnectionRetrySleepQuantum = 100; private static Cache _endpointCache = new Cache(); - private IWebRequestAdapter _clientRequestIdAdapter ; + private IWebRequestAdapter _clientRequestIdAdapter; public AzureMediaServicesClassFactory() { @@ -61,7 +64,7 @@ public AzureMediaServicesClassFactory() /// The instance. public AzureMediaServicesClassFactory(Uri azureMediaServicesEndpoint, CloudMediaContext mediaContext) { - _dataServiceAdapter = new OAuthDataServiceAdapter(mediaContext.Credentials, NimbusRestApiCertificateThumbprint,NimbusRestApiCertificateSubject); + _dataServiceAdapter = new OAuthDataServiceAdapter(mediaContext.Credentials, NimbusRestApiCertificateThumbprint, NimbusRestApiCertificateSubject); _serviceVersionAdapter = new ServiceVersionAdapter(KnownApiVersions.Current); _userAgentAdapter = new UserAgentAdapter(KnownClientVersions.Current); _mediaContext = mediaContext; @@ -78,18 +81,30 @@ public AzureMediaServicesClassFactory(Uri azureMediaServicesEndpoint, CloudMedia /// The user agent request adapter public AzureMediaServicesClassFactory(Uri azureMediaServicesEndpoint, OAuthDataServiceAdapter dataServiceAdapter, ServiceVersionAdapter serviceVersionAdapter, MediaContextBase mediaContext, UserAgentAdapter userAgentAdapter) { - this._dataServiceAdapter = dataServiceAdapter; - this._serviceVersionAdapter = serviceVersionAdapter; - this._mediaContext = mediaContext; + _dataServiceAdapter = dataServiceAdapter; + _serviceVersionAdapter = serviceVersionAdapter; + _mediaContext = mediaContext; _userAgentAdapter = userAgentAdapter; _azureMediaServicesEndpoint = CreateAzureMediaServicesEndPoint(azureMediaServicesEndpoint, mediaContext); } /// - /// Creates a data service context. + /// Creates instance of .Deafault list of applied . /// - /// The new DataServiceContext instance. + /// The new instance. public override IMediaDataServiceContext CreateDataServiceContext() + { + + return CreateDataServiceContext(new List()); + + } + + /// + /// Creates instance of with contains additional applyed adapters + /// + /// + /// + public override IMediaDataServiceContext CreateDataServiceContext(IEnumerable adapters) { DataServiceContext dataContext = new DataServiceContext(_azureMediaServicesEndpoint, DataServiceProtocolVersion.V3) { @@ -98,19 +113,16 @@ public override IMediaDataServiceContext CreateDataServiceContext() MergeOption = MergeOption.PreserveChanges, }; - var clientRequestIdAdapter = new ClientRequestIdAdapter(); + List dataServiceContextAdapters = GetDefaultDataContextAdapters().ToList(); + dataServiceContextAdapters.AddRange(adapters.ToList()); + dataServiceContextAdapters.ForEach(c => c.Adapt(dataContext)); - this._dataServiceAdapter.Adapt(dataContext); - this._serviceVersionAdapter.Adapt(dataContext); - this._userAgentAdapter.Adapt(dataContext); - clientRequestIdAdapter.Adapt(dataContext); - - dataContext.ReadingEntity += this.OnReadingEntity; + ClientRequestIdAdapter clientRequestIdAdapter = dataServiceContextAdapters.FirstOrDefault(c => c is ClientRequestIdAdapter) as ClientRequestIdAdapter; + dataContext.ReadingEntity += OnReadingEntity; var queryRetryPolicy = GetQueryRetryPolicy(null); var context = new MediaDataServiceContext(dataContext, queryRetryPolicy, clientRequestIdAdapter); queryRetryPolicy.RetryPolicyAdapter = context; return context; - } /// @@ -125,6 +137,17 @@ public override IWebRequestAdapter CreateClientRequestIdAdapter() } return _clientRequestIdAdapter; } + + /// + /// Returns IEnumerable of type which applied by default for each request + /// + /// + public override IEnumerable GetDefaultDataContextAdapters() + { + var clientRequestIdAdapter = new ClientRequestIdAdapter(); + return new IDataServiceContextAdapter[]{ _dataServiceAdapter, _serviceVersionAdapter, _userAgentAdapter, clientRequestIdAdapter }; + } + /// /// Creates retry policy for working with Azure blob storage. /// @@ -239,7 +262,7 @@ private void OnReadingEntity(object sender, ReadingWritingEntityEventArgs args) IMediaContextContainer mediaContextContainer = args.Entity as IMediaContextContainer; if (mediaContextContainer != null) { - mediaContextContainer.SetMediaContext(this._mediaContext); + mediaContextContainer.SetMediaContext(_mediaContext); } } @@ -252,7 +275,7 @@ private Uri CreateAzureMediaServicesEndPoint(Uri azureMediaServicesEndpoint, Med return (_endpointCache.GetOrAdd( cacheKey, - () => GetAccountApiEndpoint(_dataServiceAdapter,_serviceVersionAdapter, azureMediaServicesEndpoint, _userAgentAdapter,CreateClientRequestIdAdapter()), + () => GetAccountApiEndpoint(_dataServiceAdapter, _serviceVersionAdapter, azureMediaServicesEndpoint, _userAgentAdapter, CreateClientRequestIdAdapter()), () => mediaContext.Credentials.TokenExpiration)); } } diff --git a/src/net/Client/CloudMediaContext.cs b/src/net/Client/CloudMediaContext.cs index f51b8b51..bec3d56b 100644 --- a/src/net/Client/CloudMediaContext.cs +++ b/src/net/Client/CloudMediaContext.cs @@ -25,16 +25,6 @@ namespace Microsoft.WindowsAzure.MediaServices.Client /// public partial class CloudMediaContext : MediaContextBase { - /// - /// The certificate thumbprint for Nimbus services. - /// - internal const string NimbusRestApiCertificateThumbprint = "AC24B49ADEF9D6AA17195E041D3F8D07C88EC145"; - - /// - /// The certificate subject for Nimbus services. - /// - internal const string NimbusRestApiCertificateSubject = "CN=NimbusRestApi"; - private static readonly Uri _mediaServicesUri = new Uri("https://media.windows.net/"); private AssetCollection _assets; @@ -53,7 +43,7 @@ public partial class CloudMediaContext : MediaContextBase private EncodingReservedUnitCollection _encodingReservedUnits; private MediaServicesClassFactory _classFactory; private Uri apiServer; - + private StreamingFilterBaseCollection _streamingFilters; /// /// Initializes a new instance of the class. @@ -360,5 +350,21 @@ public override EncodingReservedUnitCollection EncodingReservedUnits return this._encodingReservedUnits; } } + + /// + /// Gets the collection of Filters (account level Filter) + /// + public override StreamingFilterBaseCollection Filters + { + get + { + if (_streamingFilters == null) + { + Interlocked.CompareExchange(ref _streamingFilters, new StreamingFilterBaseCollection(this), null); + } + return this._streamingFilters; + + } + } } } \ No newline at end of file diff --git a/src/net/Client/Collections/AssetFilterBaseCollection.cs b/src/net/Client/Collections/AssetFilterBaseCollection.cs new file mode 100644 index 00000000..41bf8dc8 --- /dev/null +++ b/src/net/Client/Collections/AssetFilterBaseCollection.cs @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.WindowsAzure.MediaServices.Client.TransientFaultHandling; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + public class AssetFilterBaseCollection : BaseCollection + { + public static readonly string AssetFilterSet = "AssetFilters"; + private IAsset _parentAsset; + private List _filterData; + + internal AssetFilterBaseCollection(MediaContextBase cloudMediaContext, IAsset parentAsset, List filterDatas) + : base(cloudMediaContext) + { + _parentAsset = parentAsset; + _filterData = filterDatas.Select(af => af as IStreamingAssetFilter).ToList(); + } + + /// + /// Gets the queryable collection of programs. + /// + protected override IQueryable Queryable + { + get { return _filterData.AsQueryable(); } + set { throw new NotSupportedException(); } + } + + /// + /// Creates new Filter + /// + /// filter name + /// streaming time range + /// filter track conditions + /// The created filter. + public IStreamingAssetFilter Create(string name, PresentationTimeRange timeRange, IList trackConditions) + { + return AsyncHelper.Wait(CreateAsync(name, timeRange, trackConditions)); + } + + /// + /// Asynchronously creates new StreamingFilter. + /// + /// filter name + /// filter boundaries + /// filter track conditions + /// The task to create the filter. + public Task CreateAsync(string name, PresentationTimeRange timeRange, IList trackConditions) + { + if (String.IsNullOrEmpty(name)) + { + throw new ArgumentNullException("name"); + } + + AssetFilterData filter = new AssetFilterData(_parentAsset.Id, name, timeRange, trackConditions); + + filter.SetMediaContext(MediaContext); + + IMediaDataServiceContext dataContext = MediaContext.MediaServicesClassFactory.CreateDataServiceContext(); + dataContext.AddObject(AssetFilterSet, filter); + + MediaRetryPolicy retryPolicy = MediaContext.MediaServicesClassFactory.GetSaveChangesRetryPolicy(dataContext as IRetryPolicyAdapter); + + return retryPolicy.ExecuteAsync(() => dataContext.SaveChangesAsync(filter)) + .ContinueWith( + t => + { + t.ThrowIfFaulted(); + return (AssetFilterData)t.Result.AsyncState; + }, + TaskContinuationOptions.ExecuteSynchronously); + } + } +} diff --git a/src/net/Client/Collections/StreamingFilterBaseCollection.cs b/src/net/Client/Collections/StreamingFilterBaseCollection.cs new file mode 100644 index 00000000..8083f887 --- /dev/null +++ b/src/net/Client/Collections/StreamingFilterBaseCollection.cs @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.WindowsAzure.MediaServices.Client.TransientFaultHandling; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + public class StreamingFilterBaseCollection : BaseCollection + { + public static readonly string FilterSet = "Filters"; + private readonly Lazy> _filterQuery; + + internal StreamingFilterBaseCollection(MediaContextBase cloudMediaContext) + : base(cloudMediaContext) + { + var dataContext = cloudMediaContext.MediaServicesClassFactory.CreateDataServiceContext(); + _filterQuery = new Lazy>(() => dataContext.CreateQuery(FilterSet)); + } + + /// + /// Gets the queryable collection of programs. + /// + protected override IQueryable Queryable + { + get { return _filterQuery.Value; } + set { throw new NotSupportedException(); } + } + + /// + /// Creates new Filter + /// + /// filter name + /// streaming time range + /// filter track conditions + /// The created filter. + public IStreamingFilter Create(string name, PresentationTimeRange timeRange, IList trackConditions) + { + return AsyncHelper.Wait(CreateAsync(name, timeRange, trackConditions)); + } + + /// + /// Asynchronously creates new StreamingFilter. + /// + /// filter name + /// streaming time range + /// filter track conditions + /// The task to create the filter. + public Task CreateAsync(string name, PresentationTimeRange timeRange, IList trackConditions) + { + if (String.IsNullOrEmpty(name)) + { + throw new ArgumentNullException("name"); + } + + StreamingFilterData filter = new StreamingFilterData(name, timeRange, trackConditions); + + filter.SetMediaContext(MediaContext); + + IMediaDataServiceContext dataContext = MediaContext.MediaServicesClassFactory.CreateDataServiceContext(); + dataContext.AddObject(FilterSet, filter); + + MediaRetryPolicy retryPolicy = MediaContext.MediaServicesClassFactory.GetSaveChangesRetryPolicy(dataContext as IRetryPolicyAdapter); + + return retryPolicy.ExecuteAsync(() => dataContext.SaveChangesAsync(filter)) + .ContinueWith( + t => + { + t.ThrowIfFaulted(); + return (StreamingFilterData)t.Result.AsyncState; + }, + TaskContinuationOptions.ExecuteSynchronously); + } + } +} diff --git a/src/net/Client/Common/Common.BlobTransfer/BlobDownloader.cs b/src/net/Client/Common/Common.BlobTransfer/BlobDownloader.cs index af18c376..31b306f3 100644 --- a/src/net/Client/Common/Common.BlobTransfer/BlobDownloader.cs +++ b/src/net/Client/Common/Common.BlobTransfer/BlobDownloader.cs @@ -23,6 +23,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using System.Security.Cryptography; using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Auth; using Microsoft.WindowsAzure.Storage.Blob; @@ -49,14 +50,14 @@ public Task DownloadBlob( long start = 0, long length = -1, int parallelTransferThreadCount = 10, - int numberOfConcurrentTransfers = 2) + int numberOfConcurrentTransfers = default(int)) { if (client != null && getSharedAccessSignature != null) { throw new InvalidOperationException("The arguments client and getSharedAccessSignature cannot both be non-null"); } - SetConnectionLimits(uri, Environment.ProcessorCount * numberOfConcurrentTransfers * parallelTransferThreadCount); + SetConnectionLimits(uri, numberOfConcurrentTransfers); Task task = Task.Factory.StartNew( @@ -81,12 +82,12 @@ private void DownloadFileFromBlob( long length = -1, int parallelTransferThreadCount = 10) { - int numThreads = Environment.ProcessorCount * parallelTransferThreadCount; ManualResetEvent downloadCompletedSignal = new ManualResetEvent(false); BlobRequestOptions blobRequestOptions = new BlobRequestOptions { RetryPolicy = retryPolicy }; CloudBlockBlob blob = null; BlobTransferContext transferContext = new BlobTransferContext(); transferContext.Exceptions = new ConcurrentBag(); + try { blob = GetCloudBlockBlob(uri, client, retryPolicy, getSharedAccessSignature); @@ -111,6 +112,14 @@ private void DownloadFileFromBlob( sizeToDownload = length; } + transferContext.Length = sizeToDownload; + transferContext.LocalFilePath = localFile; + transferContext.OnComplete = () => downloadCompletedSignal.Set(); + transferContext.Blob = blob; + transferContext.FileEncryption = fileEncryption; + transferContext.InitializationVector = initializationVector; + transferContext.InitialOffset = start; + if (sizeToDownload == 0) { using (FileStream stream = @@ -124,8 +133,45 @@ private void DownloadFileFromBlob( } } + else if (sizeToDownload < cloudBlockBlobUploadDownloadSizeLimit) + { + AccessCondition accessCondition = AccessCondition.GenerateEmptyCondition(); + OperationContext operationContext = new OperationContext(); + operationContext.ClientRequestID = DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture); + using (FileStream fileStream = new FileStream( + transferContext.LocalFilePath, + FileMode.OpenOrCreate, + FileAccess.ReadWrite, + FileShare.Read + )) + { + blob.DownloadToStream(fileStream, accessCondition: accessCondition, options: blobRequestOptions, operationContext: operationContext); + if (fileEncryption != null) + { + using (MemoryStream msDecrypt = new MemoryStream()) + { + //Using CryptoTransform APIs per Quintin's suggestion. + using (FileEncryptionTransform fileEncryptionTransform = fileEncryption.GetTransform(initializationVector, 0)) + { + fileStream.Position = 0; + fileStream.CopyTo(msDecrypt); + msDecrypt.Position = 0; + fileStream.Position = 0; + using (CryptoStream csEncrypt = new CryptoStream(msDecrypt, fileEncryptionTransform, CryptoStreamMode.Read)) + { + csEncrypt.CopyTo(fileStream); + } + } + } + + } + } + InvokeProgressCallback(transferContext, sizeToDownload, sizeToDownload); + transferContext.OnComplete(); + } else { + int numThreads = parallelTransferThreadCount; int blockSize = GetBlockSize(blob.Properties.Length); transferContext.BlocksToTransfer = PrepareUploadDownloadQueue(sizeToDownload, blockSize, @@ -138,12 +184,7 @@ private void DownloadFileFromBlob( } transferContext.BlockSize = blockSize; transferContext.CancellationToken = cancellationToken; - transferContext.Blob = blob; transferContext.BlobRequestOptions = blobRequestOptions; - transferContext.Length = sizeToDownload; - - transferContext.LocalFilePath = localFile; - transferContext.OnComplete = () => downloadCompletedSignal.Set(); transferContext.MemoryManager = MemoryManagerFactory.GetMemoryManager(blockSize); transferContext.Client = client; transferContext.RetryPolicy = retryPolicy; @@ -151,9 +192,6 @@ private void DownloadFileFromBlob( transferContext.ShouldDoFileIO = shouldDoFileIO; transferContext.BufferStreams = new ConcurrentDictionary(); transferContext.ClientRequestId = DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture); - transferContext.FileEncryption = fileEncryption; - transferContext.InitializationVector = initializationVector; - transferContext.InitialOffset = start; using (FileStream stream = new FileStream( transferContext.LocalFilePath, diff --git a/src/net/Client/Common/Common.BlobTransfer/BlobTransferBase.cs b/src/net/Client/Common/Common.BlobTransfer/BlobTransferBase.cs index 5e4d3dfe..26d30bae 100644 --- a/src/net/Client/Common/Common.BlobTransfer/BlobTransferBase.cs +++ b/src/net/Client/Common/Common.BlobTransfer/BlobTransferBase.cs @@ -34,7 +34,7 @@ internal abstract class BlobTransferBase private readonly TimeSpan SasPolicyActivationMaxTimeThreshold = TimeSpan.FromSeconds(5); private readonly BlobTransferSpeedCalculator _uploadDownloadSpeedCalculator = new BlobTransferSpeedCalculator(SpeedCalculatorCapacity); - + protected readonly long cloudBlockBlobUploadDownloadSizeLimit = 32 * 1024 * 1024; public event EventHandler TransferCompleted; public event EventHandler TransferProgressChanged; diff --git a/src/net/Client/Common/Common.BlobTransfer/BlobTransferClient.cs b/src/net/Client/Common/Common.BlobTransfer/BlobTransferClient.cs index 8ecefe43..96a02ce2 100644 --- a/src/net/Client/Common/Common.BlobTransfer/BlobTransferClient.cs +++ b/src/net/Client/Common/Common.BlobTransfer/BlobTransferClient.cs @@ -65,8 +65,8 @@ public class BlobTransferClient public BlobTransferClient(TimeSpan forceSharedAccessSignatureRetry = default(TimeSpan)) { _forceSharedAccessSignatureRetry = forceSharedAccessSignatureRetry; - ParallelTransferThreadCount = 10; - NumberOfConcurrentTransfers = 2; + ParallelTransferThreadCount = ServicePointModifier.DefaultConnectionLimit(); + NumberOfConcurrentTransfers = ServicePointModifier.DefaultConnectionLimit(); } /// diff --git a/src/net/Client/Common/Common.BlobTransfer/BlobUploader.cs b/src/net/Client/Common/Common.BlobTransfer/BlobUploader.cs index e915ebbd..789167c8 100644 --- a/src/net/Client/Common/Common.BlobTransfer/BlobUploader.cs +++ b/src/net/Client/Common/Common.BlobTransfer/BlobUploader.cs @@ -21,6 +21,7 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -49,9 +50,9 @@ public Task UploadBlob( string subDirectory = "", Func getSharedAccessSignature = null, int parallelTransferThreadCount = 10, - int numberOfConcurrentTransfers = 2) + int numberOfConcurrentTransfers = default(int)) { - SetConnectionLimits(url, Environment.ProcessorCount * parallelTransferThreadCount * numberOfConcurrentTransfers); + SetConnectionLimits(url, numberOfConcurrentTransfers); return Task.Factory.StartNew( () => UploadFileToBlob( cancellationToken, @@ -84,67 +85,90 @@ private void UploadFileToBlob( transferContext.Exceptions = new ConcurrentBag(); try { - //attempt to open the file first so that we throw an exception before getting into the async work - using (new FileStream(localFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - } - - ManualResetEvent uploadCompletedSignal = new ManualResetEvent(false); - BlobRequestOptions blobRequestOptions = new BlobRequestOptions - { - RetryPolicy = retryPolicy, - ServerTimeout = TimeSpan.FromSeconds(90) - }; - - CloudBlockBlob blob = GetCloudBlockBlob(uri, client, subDirectory, localFile, contentType, - getSharedAccessSignature); - BlobPolicyActivationWait(() => blob.DeleteIfExists(options: blobRequestOptions)); - - FileInfo file = new FileInfo(localFile); - long fileSize = file.Length; - - if (fileSize == 0) - { - blob.UploadFromByteArray(new byte[1], 0, 0, options: blobRequestOptions); - } - else - { - int numThreads = Environment.ProcessorCount * parallelTransferThreadCount; - int blockSize = GetBlockSize(fileSize); - - transferContext.BlocksToTransfer = PrepareUploadDownloadQueue(fileSize, blockSize, ref numThreads); - - transferContext.BlocksForFileIO = new ConcurrentDictionary(); - for (int i = 0; i < transferContext.BlocksToTransfer.Count(); i++) + ManualResetEvent uploadCompletedSignal = new ManualResetEvent(false); + BlobRequestOptions blobRequestOptions = new BlobRequestOptions + { + RetryPolicy = retryPolicy, + ServerTimeout = TimeSpan.FromSeconds(90) + }; + //attempt to open the file first so that we throw an exception before getting into the async work + using (FileStream fileStream = new FileStream(localFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { - transferContext.BlocksForFileIO[i] = null; } - transferContext.BlockSize = blockSize; - transferContext.CancellationToken = cancellationToken; - transferContext.Blob = blob; - transferContext.BlobRequestOptions = blobRequestOptions; + CloudBlockBlob blob = GetCloudBlockBlob(uri, client, subDirectory, localFile, contentType, + getSharedAccessSignature); + BlobPolicyActivationWait(() => blob.DeleteIfExists(options: blobRequestOptions)); + + FileInfo file = new FileInfo(localFile); + long fileSize = file.Length; transferContext.Length = fileSize; transferContext.LocalFilePath = localFile; transferContext.OnComplete = () => uploadCompletedSignal.Set(); - transferContext.MemoryManager = MemoryManagerFactory.GetMemoryManager(blockSize); - transferContext.Client = client; - transferContext.RetryPolicy = retryPolicy; - transferContext.GetSharedAccessSignature = getSharedAccessSignature; - transferContext.ShouldDoFileIO = shouldDoFileIO; - transferContext.BufferStreams = new ConcurrentDictionary(); - transferContext.ClientRequestId = DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture); + transferContext.Blob = blob; transferContext.FileEncryption = fileEncryption; - transferContext.ContentType = contentType; - transferContext.BlobSubFolder = subDirectory; - transferContext.NextFileIOBlock = 0; - transferContext.PartialFileIOState = new ConcurrentDictionary(); - - using ( - FileStream stream = new FileStream(localFile, FileMode.Open, FileAccess.Read, - FileShare.ReadWrite)) + if (fileSize == 0) { - RunUploadLoop(transferContext, stream, numThreads); + blob.UploadFromByteArray(new byte[1], 0, 0, options: blobRequestOptions); + } + else if (fileSize < cloudBlockBlobUploadDownloadSizeLimit) + { + AccessCondition accessCondition = AccessCondition.GenerateEmptyCondition(); + OperationContext operationContext = new OperationContext(); + operationContext.ClientRequestID = DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture); + using (FileStream fileStream = new FileStream(localFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + using (var memoryStream = new MemoryStream()) + { + fileStream.CopyTo(memoryStream); + byte[] fileContent = memoryStream.ToArray(); + ApplyEncryptionTransform( + transferContext.FileEncryption, + Path.GetFileName(transferContext.LocalFilePath), + 0, + fileContent, + Convert.ToInt32(fileStream.Length)); + using (var uploadMemoryStream = new MemoryStream(fileContent)) + { + blob.UploadFromStream(uploadMemoryStream, accessCondition: accessCondition, options: blobRequestOptions, operationContext: operationContext); + } + } + } + InvokeProgressCallback(transferContext, fileSize, fileSize); + transferContext.OnComplete(); } + else + { + int numThreads = parallelTransferThreadCount; + int blockSize = GetBlockSize(fileSize); + + transferContext.BlocksToTransfer = PrepareUploadDownloadQueue(fileSize, blockSize, ref numThreads); + + transferContext.BlocksForFileIO = new ConcurrentDictionary(); + for (int i = 0; i < transferContext.BlocksToTransfer.Count(); i++) + { + transferContext.BlocksForFileIO[i] = null; + } + transferContext.BlockSize = blockSize; + transferContext.CancellationToken = cancellationToken; + transferContext.BlobRequestOptions = blobRequestOptions; + transferContext.MemoryManager = MemoryManagerFactory.GetMemoryManager(blockSize); + transferContext.Client = client; + transferContext.RetryPolicy = retryPolicy; + transferContext.GetSharedAccessSignature = getSharedAccessSignature; + transferContext.ShouldDoFileIO = shouldDoFileIO; + transferContext.BufferStreams = new ConcurrentDictionary(); + transferContext.ClientRequestId = DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture); + transferContext.ContentType = contentType; + transferContext.BlobSubFolder = subDirectory; + transferContext.NextFileIOBlock = 0; + transferContext.PartialFileIOState = new ConcurrentDictionary(); + + using ( + FileStream stream = new FileStream(localFile, FileMode.Open, FileAccess.Read, + FileShare.ReadWrite)) + { + RunUploadLoop(transferContext, stream, numThreads); + } } } catch (Exception e) diff --git a/src/net/Client/Common/Common.BlobTransfer/MemoryManager.cs b/src/net/Client/Common/Common.BlobTransfer/MemoryManager.cs index 5870f1b0..1a7ee2c4 100644 --- a/src/net/Client/Common/Common.BlobTransfer/MemoryManager.cs +++ b/src/net/Client/Common/Common.BlobTransfer/MemoryManager.cs @@ -26,7 +26,12 @@ internal class MemoryManager private int _buffersInUse; - public MemoryManager(int bufferSize, long capacity = 0x4000000L) + public MemoryManager(int bufferSize) + : this(bufferSize, Environment.Is64BitProcess ? 0x40000000L : 0x4000000L) + { + } + + public MemoryManager(int bufferSize, long capacity) { long num = capacity / ((long)bufferSize); int cellsCount = (int)Math.Min(0x2000L, num); diff --git a/src/net/Client/Common/Common.BlobTransfer/Properties/AssemblyInfo.cs b/src/net/Client/Common/Common.BlobTransfer/Properties/AssemblyInfo.cs index b0a5ce3a..0578b67d 100644 --- a/src/net/Client/Common/Common.BlobTransfer/Properties/AssemblyInfo.cs +++ b/src/net/Client/Common/Common.BlobTransfer/Properties/AssemblyInfo.cs @@ -49,8 +49,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.3.0.0")] -[assembly: AssemblyFileVersion("3.3.0.0")] +[assembly: AssemblyVersion("3.4.0.0")] +[assembly: AssemblyFileVersion("3.4.0.0")] [assembly: InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Scenario")] [assembly: InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Unit")] [assembly: InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Common")] \ No newline at end of file diff --git a/src/net/Client/Common/Common.BlobTransfer/Properties/DelaySign_AssemblyInfo.cs b/src/net/Client/Common/Common.BlobTransfer/Properties/DelaySign_AssemblyInfo.cs index 2e47ccd8..2b9ad1b5 100644 --- a/src/net/Client/Common/Common.BlobTransfer/Properties/DelaySign_AssemblyInfo.cs +++ b/src/net/Client/Common/Common.BlobTransfer/Properties/DelaySign_AssemblyInfo.cs @@ -49,8 +49,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.3.0.0")] -[assembly: AssemblyFileVersion("3.3.0.0")] +[assembly: AssemblyVersion("3.4.0.0")] +[assembly: AssemblyFileVersion("3.4.0.0")] //For delay signing specify PublicKey for each friendly assembly [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Scenario, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Unit, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/net/Client/Common/Common.BlobTransfer/ServicePointModifier.cs b/src/net/Client/Common/Common.BlobTransfer/ServicePointModifier.cs index c48cf5e3..0b0e5305 100644 --- a/src/net/Client/Common/Common.BlobTransfer/ServicePointModifier.cs +++ b/src/net/Client/Common/Common.BlobTransfer/ServicePointModifier.cs @@ -22,8 +22,14 @@ namespace Microsoft.WindowsAzure.MediaServices.Client internal static class ServicePointModifier { private const int DefaultConnectionLimitMultiplier = 8; + private const int MaxConnectionLimit = 30; private static readonly TimeSpan DefaultConnectionLeaseTimeout = TimeSpan.FromMinutes(5); + public static int DefaultConnectionLimit() + { + return Math.Min(MaxConnectionLimit, Environment.ProcessorCount * DefaultConnectionLimitMultiplier); + } + public static void SetConnectionPropertiesForSmallPayloads( Uri uri, int connectionLimit = default(int), diff --git a/src/net/Client/Common/Common.FileEncryption/Properties/AssemblyInfo.cs b/src/net/Client/Common/Common.FileEncryption/Properties/AssemblyInfo.cs index d4f8f0a0..3798644c 100644 --- a/src/net/Client/Common/Common.FileEncryption/Properties/AssemblyInfo.cs +++ b/src/net/Client/Common/Common.FileEncryption/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.3.0.0")] -[assembly: AssemblyFileVersion("3.3.0.0")] +[assembly: AssemblyVersion("3.4.0.0")] +[assembly: AssemblyFileVersion("3.4.0.0")] diff --git a/src/net/Client/Common/Common.FileEncryption/Properties/DelaySign_AssemblyInfo.cs b/src/net/Client/Common/Common.FileEncryption/Properties/DelaySign_AssemblyInfo.cs index 1ffc2946..fa9da7b5 100644 --- a/src/net/Client/Common/Common.FileEncryption/Properties/DelaySign_AssemblyInfo.cs +++ b/src/net/Client/Common/Common.FileEncryption/Properties/DelaySign_AssemblyInfo.cs @@ -32,8 +32,8 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.3.0.0")] -[assembly: AssemblyFileVersion("3.3.0.0")] +[assembly: AssemblyVersion("3.4.0.0")] +[assembly: AssemblyFileVersion("3.4.0.0")] //For delay signing specify PublicKey for each friendly assembly [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Scenario, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Unit, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/net/Client/ContentKeyAuthorization/Templates/EncodeUtilities.cs b/src/net/Client/ContentKeyAuthorization/Templates/EncodeUtilities.cs index 904e3029..4aad6a48 100644 --- a/src/net/Client/ContentKeyAuthorization/Templates/EncodeUtilities.cs +++ b/src/net/Client/ContentKeyAuthorization/Templates/EncodeUtilities.cs @@ -15,8 +15,6 @@ // using System; -using System.Text; -using System.Web; namespace Microsoft.WindowsAzure.MediaServices.Client.ContentKeyAuthorization { @@ -62,7 +60,7 @@ private static string UrlDecode(string arg) s += "="; break; // One pad char default: - throw new System.Exception( + throw new Exception( "Illegal base64url string!"); } return s; diff --git a/src/net/Client/ContentKeyAuthorization/Templates/JsonWebKeyExtension.cs b/src/net/Client/ContentKeyAuthorization/Templates/JsonWebKeyExtension.cs index d3a11471..9b2fdab1 100644 --- a/src/net/Client/ContentKeyAuthorization/Templates/JsonWebKeyExtension.cs +++ b/src/net/Client/ContentKeyAuthorization/Templates/JsonWebKeyExtension.cs @@ -14,18 +14,18 @@ // limitations under the License. // -using Microsoft.WindowsAzure.MediaServices.Client; -using Microsoft.WindowsAzure.MediaServices.Client.ContentKeyAuthorization; using System; using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using Microsoft.WindowsAzure.MediaServices.Client; +using Microsoft.WindowsAzure.MediaServices.Client.ContentKeyAuthorization; namespace Microsoft.IdentityModel.Protocols { public static class JsonWebKeyExtension { - public static TokenVerificationKey AsTokenVerificationKey(this Microsoft.IdentityModel.Protocols.JsonWebKey jwk) + public static TokenVerificationKey AsTokenVerificationKey(this JsonWebKey jwk) { X509Certificate2 cert = null; X509CertTokenVerificationKey key = null; diff --git a/src/net/Client/ContentKeyAuthorization/Templates/OpenIdConnectDiscoveryDocument.cs b/src/net/Client/ContentKeyAuthorization/Templates/OpenIdConnectDiscoveryDocument.cs index a915f671..9a6e371b 100644 --- a/src/net/Client/ContentKeyAuthorization/Templates/OpenIdConnectDiscoveryDocument.cs +++ b/src/net/Client/ContentKeyAuthorization/Templates/OpenIdConnectDiscoveryDocument.cs @@ -14,16 +14,7 @@ // limitations under the License. // -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; -using System.Xml; -using Newtonsoft.Json.Linq; namespace Microsoft.WindowsAzure.MediaServices.Client.ContentKeyAuthorization { diff --git a/src/net/Client/DynamicEncryption/OutputAsset.cs b/src/net/Client/DynamicEncryption/OutputAsset.cs index 3219b7cf..28bc86fb 100644 --- a/src/net/Client/DynamicEncryption/OutputAsset.cs +++ b/src/net/Client/DynamicEncryption/OutputAsset.cs @@ -16,6 +16,7 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Microsoft.WindowsAzure.MediaServices.Client.DynamicEncryption; namespace Microsoft.WindowsAzure.MediaServices.Client @@ -26,6 +27,8 @@ namespace Microsoft.WindowsAzure.MediaServices.Client /// This is used when creating task to specify properties for a Task's output. internal partial class OutputAsset : BaseEntity, IAsset { + + /// /// Gets the delivery policies associated with the asset. /// @@ -54,5 +57,6 @@ AssetEncryptionState IAsset.GetEncryptionState(AssetDeliveryProtocol protocol) { throw new NotSupportedException(); } + } } diff --git a/src/net/Client/Entities/ApiClasses.cs b/src/net/Client/Entities/ApiClasses.cs index 825ce707..15d2805e 100644 --- a/src/net/Client/Entities/ApiClasses.cs +++ b/src/net/Client/Entities/ApiClasses.cs @@ -99,13 +99,11 @@ bool IsPrimary get; set; } - /// long ContentFileSize { get; set; } - /// [Obsolete] string ContentChecksum @@ -147,6 +145,11 @@ string InitializationVector { get; } + /// + AssetFileOptions AssetFileOptions + { + get; + } } public partial interface IContentKey { diff --git a/src/net/Client/Entities/AssetData.cs b/src/net/Client/Entities/AssetData.cs index 3213fc60..b087a471 100644 --- a/src/net/Client/Entities/AssetData.cs +++ b/src/net/Client/Entities/AssetData.cs @@ -12,17 +12,18 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Data.Services.Common; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.WindowsAzure.MediaServices.Client.DynamicEncryption; -using Microsoft.WindowsAzure.MediaServices.Client.TransientFaultHandling; - +// + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Data.Services.Common; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.WindowsAzure.MediaServices.Client.DynamicEncryption; +using Microsoft.WindowsAzure.MediaServices.Client.RequestAdapters; +using Microsoft.WindowsAzure.MediaServices.Client.TransientFaultHandling; + namespace Microsoft.WindowsAzure.MediaServices.Client { /// @@ -34,9 +35,11 @@ internal partial class AssetData : BaseEntity,IAsset private const string ContentKeysPropertyName = "ContentKeys"; private const string DeliveryPoliciesPropertyName = "DeliveryPolicies"; private const string LocatorsPropertyName = "Locators"; - private const string ParentAssetsPropertyName = "ParentAssets"; + private const string ParentAssetsPropertyName = "ParentAssets"; + private const string FilterPropertyName = "AssetFilters"; - private AssetFileCollection _fileCollection; + private AssetFileCollection _fileCollection; + private AssetFilterBaseCollection _filterCollection; private ReadOnlyCollection _locatorCollection; private IList _contentKeyCollection; private ReadOnlyCollection _parentAssetCollection; @@ -53,8 +56,8 @@ public AssetData() this.Locators = new List(); this.ContentKeys = new List(); this.DeliveryPolicies = new List(); - this.Files = new List(); - + this.Files = new List(); + this.AssetFilters = new List(); } /// @@ -90,9 +93,29 @@ AssetFileBaseCollection IAsset.AssetFiles } } - public List Files { get; set; } + /// + /// Get a collection of filters for this asset + /// + AssetFilterBaseCollection IAsset.AssetFilters + { + get + { + if (((this._filterCollection == null) || (this.AssetFilters == null)) && !string.IsNullOrWhiteSpace(this.Id)) + { + IMediaDataServiceContext dataContext = this._mediaContextBase.MediaServicesClassFactory.CreateDataServiceContext(); + dataContext.AttachTo(AssetCollection.AssetSet, this); + LoadProperty(dataContext, FilterPropertyName); + + this._filterCollection = new AssetFilterBaseCollection(_mediaContextBase, this, this.AssetFilters ?? new List()); + } + + return this._filterCollection; + } + } + + public List AssetFilters { get; set; } /// /// Gets or sets the locators. @@ -196,6 +219,7 @@ private void InitCloudMediaContext(MediaContextBase context) InvalidateContentKeysCollection(); InvalidateDeliveryPoliciesCollection(); InvalidateFilesCollection(); + InvalidateFilterCollection(); if (context != null) { this._fileCollection = new AssetFileCollection(context, this); @@ -241,7 +265,7 @@ Uri IAsset.Uri /// /// A function delegate that returns the future result to be available through the Task. public Task UpdateAsync() - { + { AssetCollection.VerifyAsset(this); IMediaDataServiceContext dataContext = this._mediaContextBase.MediaServicesClassFactory.CreateDataServiceContext(); @@ -273,26 +297,34 @@ public void Update() { throw exception.InnerException; } - } - - /// - /// Asynchronously deletes this asset instance. + } + /// + /// Deletes this asset instance including underlying azure storage container /// - /// A function delegate that returns the future result to be available through the Task. public Task DeleteAsync() { - AssetCollection.VerifyAsset(this); - - - IMediaDataServiceContext dataContext = this._mediaContextBase.MediaServicesClassFactory.CreateDataServiceContext(); - dataContext.AttachTo(AssetCollection.AssetSet, this); - this.InvalidateContentKeysCollection(); - this.InvalidateDeliveryPoliciesCollection(); + return DeleteAsync(false); + } + + + /// + /// Asynchronously deletes this asset instance. + /// + /// if set to true underlying storage asset container is preserved during the delete operation. + /// Task of type + public Task DeleteAsync(bool keepAzureStorageContainer) + { + AssetCollection.VerifyAsset(this); + + AssetDeleteOptionsRequestAdapter deleteRequestAdapter = new AssetDeleteOptionsRequestAdapter(keepAzureStorageContainer); + IMediaDataServiceContext dataContext = this._mediaContextBase.MediaServicesClassFactory.CreateDataServiceContext(new[] { deleteRequestAdapter }); + dataContext.AttachTo(AssetCollection.AssetSet, this); + this.InvalidateContentKeysCollection(); + this.InvalidateDeliveryPoliciesCollection(); dataContext.DeleteObject(this); - MediaRetryPolicy retryPolicy = this._mediaContextBase.MediaServicesClassFactory.GetSaveChangesRetryPolicy(dataContext as IRetryPolicyAdapter); - - return retryPolicy.ExecuteAsync(() => dataContext.SaveChangesAsync(this)); + MediaRetryPolicy retryPolicy = this._mediaContextBase.MediaServicesClassFactory.GetSaveChangesRetryPolicy(dataContext as IRetryPolicyAdapter); + return retryPolicy.ExecuteAsync(() => dataContext.SaveChangesAsync(this)); } /// @@ -302,21 +334,38 @@ internal void InvalidateContentKeysCollection() { this.ContentKeys.Clear(); this._contentKeyCollection = null; - } - - /// - /// Deletes this asset instance. - /// - public void Delete() + } + + /// + /// Deletes this asset instance + /// + /// if set to true underlying storage asset container is preserved during the delete operation. + /// IMediaDataServiceResponse. + public IMediaDataServiceResponse Delete(bool keepAzureStorageContainer) { try - { - this.DeleteAsync().Wait(); + { + return DeleteAsync(keepAzureStorageContainer).Result; } catch (AggregateException exception) { - throw exception.InnerException; + throw exception.Flatten().InnerException; } + } + + /// + /// Deletes this asset instance + /// + public void Delete() + { + try + { + var result = this.DeleteAsync(false).Result; + } + catch (AggregateException exception) + { + throw exception.Flatten().InnerException; + } } /// @@ -342,6 +391,13 @@ private void InvalidateFilesCollection() { this.Files.Clear(); this._fileCollection = null; + } + + + private void InvalidateFilterCollection() + { + this.AssetFilters.Clear(); + this._filterCollection = null; } public override void SetMediaContext(MediaContextBase value) diff --git a/src/net/Client/Entities/AssetFileData.cs b/src/net/Client/Entities/AssetFileData.cs index 5c9d3bb2..0300337b 100644 --- a/src/net/Client/Entities/AssetFileData.cs +++ b/src/net/Client/Entities/AssetFileData.cs @@ -1,17 +1,17 @@ -//----------------------------------------------------------------------- -// Copyright 2012 Microsoft Corporation -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this path except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +//----------------------------------------------------------------------- +// Copyright 2012 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this path except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // using System; @@ -26,213 +26,235 @@ using Microsoft.WindowsAzure.MediaServices.Client.TransientFaultHandling; using Microsoft.WindowsAzure.Storage.RetryPolicies; -namespace Microsoft.WindowsAzure.MediaServices.Client -{ - /// - /// Represents an asset path. - /// - [DataServiceKey("Id")] - internal partial class AssetFileData : BaseEntity,IAssetFile - { - /// - /// The name of the files set. - /// - internal const string FileSet = "Files"; - - private readonly object _lock = new object(); - - private IAsset _asset; - - #region IAssetFile Members - - /// - /// Occurs when the download progress is updated. - /// - public event EventHandler DownloadProgressChanged; - - /// - /// Occurs when the upload progress is updated. - /// - public event EventHandler UploadProgressChanged; - - /// - /// Gets the asset. - /// - IAsset IAssetFile.Asset - { - get { return this.Asset; } - } - - /// - /// Gets the asset. - /// - private IAsset Asset - { - get - { - if ((this._asset == null) && !String.IsNullOrWhiteSpace(this.ParentAssetId)) - { - this._asset = this.GetMediaContext().Assets.Where(c => c.Id == this.ParentAssetId).Single(); - } - - return this._asset; - } - } - - - /// - /// Asynchronously downloads the represented file to the specified destination path. - /// - /// The path to download the file to. - /// The which is used to download files. - /// An asset which defines permissions associated with the Asset. - /// The cancellation token. - /// - /// A function delegate that returns the future result to be available through the Task. - /// - public Task DownloadAsync(string destinationPath, BlobTransferClient blobTransferClient, ILocator locator, CancellationToken cancellationToken) - { - MediaRetryPolicy retryPolicy = this.GetMediaContext().MediaServicesClassFactory.GetBlobStorageClientRetryPolicy(); - return this.DownloadToFileAsync(destinationPath, blobTransferClient, locator, retryPolicy.AsAzureStorageClientRetryPolicy(), cancellationToken); - } - - - /// - /// Downloads the represented file to the specified destination path. - /// - /// The path to download the file to. - public void Download(string destinationPath) - { - IAccessPolicy accessPolicy = null; - ILocator locator = null; - try - { - accessPolicy = this.GetMediaContext().AccessPolicies.Create("SdkDownload", TimeSpan.FromHours(12), AccessPermissions.Read); - locator = this.GetMediaContext().Locators.CreateSasLocator(this.Asset, accessPolicy); - - BlobTransferClient blobTransfer = this.GetMediaContext().MediaServicesClassFactory.GetBlobTransferClient(); - blobTransfer.NumberOfConcurrentTransfers = this.GetMediaContext().NumberOfConcurrentTransfers; - blobTransfer.ParallelTransferThreadCount = this.GetMediaContext().ParallelTransferThreadCount; - - this.DownloadAsync(destinationPath, blobTransfer, locator, CancellationToken.None).Wait(); - } - catch (AggregateException exception) - { - throw exception.Flatten(); - } - finally +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Represents an asset path. + /// + [DataServiceKey("Id")] + internal partial class AssetFileData : BaseEntity,IAssetFile + { + /// + /// The name of the files set. + /// + internal const string FileSet = "Files"; + + private readonly object _lock = new object(); + + private IAsset _asset; + + #region IAssetFile Members + + /// + /// Occurs when the download progress is updated. + /// + public event EventHandler DownloadProgressChanged; + + /// + /// Occurs when the upload progress is updated. + /// + public event EventHandler UploadProgressChanged; + + /// + /// Gets the asset. + /// + IAsset IAssetFile.Asset + { + get { return this.Asset; } + } + + /// + /// Gets the asset. + /// + private IAsset Asset + { + get { - Cleanup(null,null,locator,accessPolicy); - } - } - - /// - /// Asynchronously saves this instance. - /// - /// A function delegate that returns the future result to be available through the Task. - public Task UpdateAsync() - { - if (this.Asset.State != AssetState.Initialized) - { - throw new NotSupportedException(StringTable.NotSupportedFileInfoSave); - } - - IMediaDataServiceContext dataContext = this.GetMediaContext().MediaServicesClassFactory.CreateDataServiceContext(); - dataContext.AttachTo(FileSet, this); + if ((this._asset == null) && !String.IsNullOrWhiteSpace(this.ParentAssetId)) + { + this._asset = this.GetMediaContext().Assets.Where(c => c.Id == this.ParentAssetId).Single(); + } + + return this._asset; + } + } + + /// + /// Gets the for this AssetFile + /// + private static AssetFileOptions GetExposedOptions(int options) + { + return (AssetFileOptions) options; + } + + /// + /// Asynchronously downloads the represented file to the specified destination path. + /// + /// The path to download the file to. + /// The which is used to download files. + /// An asset which defines permissions associated with the Asset. + /// The cancellation token. + /// + /// A function delegate that returns the future result to be available through the Task. + /// + public Task DownloadAsync(string destinationPath, BlobTransferClient blobTransferClient, ILocator locator, CancellationToken cancellationToken) + { + if (this.IsFragmented()) + { + throw new NotSupportedException(StringTable.NotSupportedFragblobDownload); + } + + MediaRetryPolicy retryPolicy = this.GetMediaContext().MediaServicesClassFactory.GetBlobStorageClientRetryPolicy(); + return this.DownloadToFileAsync(destinationPath, blobTransferClient, locator, retryPolicy.AsAzureStorageClientRetryPolicy(), cancellationToken); + } + + + /// + /// Downloads the represented file to the specified destination path. + /// + /// The path to download the file to. + public void Download(string destinationPath) + { + if (this.IsFragmented()) + { + throw new NotSupportedException(StringTable.NotSupportedFragblobDownload); + } + + IAccessPolicy accessPolicy = null; + ILocator locator = null; + try + { + accessPolicy = this.GetMediaContext().AccessPolicies.Create("SdkDownload", TimeSpan.FromHours(12), AccessPermissions.Read); + locator = this.GetMediaContext().Locators.CreateSasLocator(this.Asset, accessPolicy); + + BlobTransferClient blobTransfer = this.GetMediaContext().MediaServicesClassFactory.GetBlobTransferClient(); + blobTransfer.NumberOfConcurrentTransfers = this.GetMediaContext().NumberOfConcurrentTransfers; + blobTransfer.ParallelTransferThreadCount = this.GetMediaContext().ParallelTransferThreadCount; + + this.DownloadAsync(destinationPath, blobTransfer, locator, CancellationToken.None).Wait(); + } + catch (AggregateException exception) + { + throw exception.Flatten(); + } + finally + { + Cleanup(null,null,locator,accessPolicy); + } + } + + /// + /// Asynchronously saves this instance. + /// + /// A function delegate that returns the future result to be available through the Task. + public Task UpdateAsync() + { + if (this.Asset.State != AssetState.Initialized) + { + throw new NotSupportedException(StringTable.NotSupportedFileInfoSave); + } + + IMediaDataServiceContext dataContext = this.GetMediaContext().MediaServicesClassFactory.CreateDataServiceContext(); + dataContext.AttachTo(FileSet, this); dataContext.UpdateObject(this); - MediaRetryPolicy retryPolicy = this.GetMediaContext().MediaServicesClassFactory.GetSaveChangesRetryPolicy(dataContext as IRetryPolicyAdapter); - - return retryPolicy.ExecuteAsync(() => dataContext.SaveChangesAsync(this)) - .ContinueWith( - t => - { - t.ThrowIfFaulted(); - AssetFileData data = (AssetFileData)t.Result.AsyncState; - return data; - }); - } - - /// - /// Saves this instance. - /// - public void Update() - { - try - { - this.UpdateAsync().Wait(); - } - catch (AggregateException exception) - { - throw exception.InnerException; - } - } - - /// - /// Deletes this instance. - /// - public void Delete() - { - try - { - this.DeleteAsync().Wait(); - } - catch (AggregateException exception) - { - throw exception.InnerException; - } - } - - /// - /// Asynchronously deletes this instance. - /// - /// A function delegate that returns the future result to be available through the Task. - public Task DeleteAsync() - { - IMediaDataServiceContext dataContext = this.GetMediaContext().MediaServicesClassFactory.CreateDataServiceContext(); - dataContext.AttachTo(FileSet, this); + MediaRetryPolicy retryPolicy = this.GetMediaContext().MediaServicesClassFactory.GetSaveChangesRetryPolicy(dataContext as IRetryPolicyAdapter); + + return retryPolicy.ExecuteAsync(() => dataContext.SaveChangesAsync(this)) + .ContinueWith( + t => + { + t.ThrowIfFaulted(); + AssetFileData data = (AssetFileData)t.Result.AsyncState; + return data; + }); + } + + /// + /// Saves this instance. + /// + public void Update() + { + try + { + this.UpdateAsync().Wait(); + } + catch (AggregateException exception) + { + throw exception.InnerException; + } + } + + /// + /// Deletes this instance. + /// + public void Delete() + { + try + { + this.DeleteAsync().Wait(); + } + catch (AggregateException exception) + { + throw exception.InnerException; + } + } + + /// + /// Asynchronously deletes this instance. + /// + /// A function delegate that returns the future result to be available through the Task. + public Task DeleteAsync() + { + IMediaDataServiceContext dataContext = this.GetMediaContext().MediaServicesClassFactory.CreateDataServiceContext(); + dataContext.AttachTo(FileSet, this); dataContext.DeleteObject(this); - MediaRetryPolicy retryPolicy = this.GetMediaContext().MediaServicesClassFactory.GetSaveChangesRetryPolicy(dataContext as IRetryPolicyAdapter); - - return retryPolicy.ExecuteAsync(() => dataContext.SaveChangesAsync(this)); - } - - - /// - /// Uploads the destinationPath with given path asynchronously - /// - /// The path of a destinationPath to upload - /// The which is used to upload files. - /// An asset which defines permissions associated with the Asset. - /// A to use for canceling upload operation. - /// A function delegate that returns the future result to be available through the Task. - public Task UploadAsync(string path, BlobTransferClient blobTransferClient, ILocator locator, CancellationToken token) - { - if (blobTransferClient == null) - { - throw new ArgumentNullException("blobTransferClient"); - } - - if (locator == null) - { - throw new ArgumentNullException("locator"); - } - - if (path == null) - { - throw new ArgumentNullException("path"); - } - - if (!File.Exists(path)) - { - throw new FileNotFoundException(path); - } - - ValidateFileName(path); - - IContentKey contentKeyData = null; - FileEncryption fileEncryption = null; + MediaRetryPolicy retryPolicy = this.GetMediaContext().MediaServicesClassFactory.GetSaveChangesRetryPolicy(dataContext as IRetryPolicyAdapter); + + return retryPolicy.ExecuteAsync(() => dataContext.SaveChangesAsync(this)); + } + + + /// + /// Uploads the destinationPath with given path asynchronously + /// + /// The path of a destinationPath to upload + /// The which is used to upload files. + /// An asset which defines permissions associated with the Asset. + /// A to use for canceling upload operation. + /// A function delegate that returns the future result to be available through the Task. + public Task UploadAsync(string path, BlobTransferClient blobTransferClient, ILocator locator, CancellationToken token) + { + if (this.IsFragmented()) + { + throw new NotSupportedException(StringTable.NotSupportedFragblobUpload); + } + + if (blobTransferClient == null) + { + throw new ArgumentNullException("blobTransferClient"); + } + + if (locator == null) + { + throw new ArgumentNullException("locator"); + } + + if (path == null) + { + throw new ArgumentNullException("path"); + } + + if (!File.Exists(path)) + { + throw new FileNotFoundException(path); + } + + ValidateFileName(path); + + IContentKey contentKeyData = null; + FileEncryption fileEncryption = null; AssetCreationOptions assetCreationOptions = this.Asset.Options; if (assetCreationOptions.HasFlag(AssetCreationOptions.StorageEncrypted)) { @@ -245,305 +267,315 @@ public Task UploadAsync(string path, BlobTransferClient blobTransferClient, ILoc fileEncryption = new FileEncryption(contentKeyData.GetClearKeyValue(), EncryptionUtils.GetKeyIdAsGuid(contentKeyData.Id)); ulong iv = Convert.ToUInt64(this.InitializationVector, CultureInfo.InvariantCulture); fileEncryption.SetInitializationVectorForFile(this.Name,iv); - } - - EventHandler handler = (s, e) => OnUploadProgressChanged(path, e); - - blobTransferClient.TransferProgressChanged += handler; - - MediaRetryPolicy retryPolicy = this.GetMediaContext().MediaServicesClassFactory.GetBlobStorageClientRetryPolicy(); - - return blobTransferClient.UploadBlob( - new Uri(locator.BaseUri), - path, - null, - fileEncryption, - token, - retryPolicy.AsAzureStorageClientRetryPolicy(), - () => locator.ContentAccessComponent) - .ContinueWith( - ts=> - { - blobTransferClient.TransferProgressChanged -= handler; - this.PostUploadAction(ts, path, token); - }); - } - - private void OnUploadProgressChanged(string file, BlobTransferProgressChangedEventArgs blobTransferProgressChangedEventArgs) - { - if (blobTransferProgressChangedEventArgs.LocalFile == file) - { - var uploadChangeHandler = UploadProgressChanged; - if (uploadChangeHandler != null) - { - uploadChangeHandler( - sender: this, - e: new UploadProgressChangedEventArgs( - blobTransferProgressChangedEventArgs.BytesTransferred, - blobTransferProgressChangedEventArgs.TotalBytesToTransfer)); - } - } - } - - private void ValidateFileName(string path) - { - string filename = Path.GetFileName(path).ToUpperInvariant(); - if (filename != this.Name.ToUpperInvariant()) - { - throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, StringTable.FileNameMismatch, this.Name, filename)); - } - } - - - private void PostUploadAction(Task task,string path, CancellationToken token) - { - task.ThrowIfFaulted(); - token.ThrowIfCancellationRequested(); - - FileInfo fileInfo = new FileInfo(path); - - //Updating Name based on file name to avoid exceptions trying to download file.Mapping to storage account is through file name - this.Name = fileInfo.Name; - - // Set the ContentFileSize base on the local file size - this.ContentFileSize = fileInfo.Length; - - this.Update(); - } - #endregion - - #region IMediaContextContainer Members - - - - #endregion - - /// - /// Downloads the file asynchronously . - /// - /// The path to download the file to. - /// The which is used to download files. - /// An asset which defines permissions associated with the Asset. - /// The retry policy. - /// The cancellation token. - /// A function delegate that returns the future result to be available through the Task. - internal Task DownloadToFileAsync(string destinationPath,BlobTransferClient blobTransferClient, ILocator locator, IRetryPolicy retryPolicy, CancellationToken cancellationToken) - { - FileEncryption fileEncryption = this.GetFileEncryption(); - cancellationToken.Register(() => this.Cleanup(null, fileEncryption, null, null)); - return Task.Factory.StartNew(() => - { - cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, fileEncryption, null, null)); - ulong iv = Convert.ToUInt64(this.InitializationVector, CultureInfo.InvariantCulture); - UriBuilder uriBuilder = new UriBuilder(locator.BaseUri); - uriBuilder.Path += String.Concat("/", Name); - blobTransferClient.TransferProgressChanged += this.OnDownloadBlobTransferProgressChanged; - - blobTransferClient.DownloadBlob( - uriBuilder.Uri, - destinationPath, - fileEncryption, - iv, - cancellationToken, - retryPolicy, - () => locator.ContentAccessComponent) - .Wait(cancellationToken); - - cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, fileEncryption, null, null)); - }, - cancellationToken) - .ContinueWith( - t => - { - t.ThrowIfFaulted(() => this.Cleanup(null, fileEncryption, null, null)); - cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, fileEncryption, null, null)); - this.Cleanup(null, fileEncryption, null, null); - }, - cancellationToken); - } - - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "All exception needs to be caught while cleaning up.")] - private void Cleanup(BlobTransferClient blobTransfer, FileEncryption fileEncryption, ILocator locator, IAccessPolicy accessPolicy) - { - lock (this._lock) - { - if (blobTransfer != null) - { - try - { - blobTransfer.TransferProgressChanged -= this.OnDownloadBlobTransferProgressChanged; - } - catch - { - } - finally - { - blobTransfer = null; - } - } - - if (fileEncryption != null) - { - try - { - fileEncryption.Dispose(); - } - catch - { - } - finally - { - fileEncryption = null; - } - } - - if (locator != null) - { - try - { - locator.Delete(); - } - catch - { - } - finally - { - locator = null; - } - } - - if (accessPolicy != null) - { - try - { - accessPolicy.Delete(); - } - catch - { - } - finally - { - accessPolicy = null; - } - } - } - } - - private FileEncryption GetFileEncryption() - { - if (!this.IsEncrypted) - { - return null; - } - - // We want to support downloading PlayReady encrypted content too. - if (this.EncryptionScheme != FileEncryption.SchemeName) - { - return null; - } - - IContentKey key = this.Asset.ContentKeys.Where(c => c.ContentKeyType == ContentKeyType.StorageEncryption).FirstOrDefault(); - Guid keyId = EncryptionUtils.GetKeyIdAsGuid(key.Id); - - return new FileEncryption(key.GetClearKeyValue(), keyId); - } - - private void OnDownloadBlobTransferProgressChanged(object sender, BlobTransferProgressChangedEventArgs e) - { - EventHandler downloadProgressEvent = this.DownloadProgressChanged; - if (downloadProgressEvent != null) - { - downloadProgressEvent(this, new DownloadProgressChangedEventArgs(e.BytesTransferred, e.TotalBytesToTransfer)); - } - } - - /// - /// Gets the MIME type of the specified destinationPath. - /// - /// Name of the destinationPath. - /// The MIME type. - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "All exception needs to be caught and return null in case of error.")] - internal static string GetMimeType(string fileName) - { - string mimeType = null; - - try - { - RegistryKey registryKey = Registry.ClassesRoot.OpenSubKey(Path.GetExtension(fileName)); - - if (registryKey != null) - { - object registryValue = registryKey.GetValue("Content Type"); - mimeType = registryValue != null ? registryValue.ToString() : null; - } - } - catch - { - // Catch all exceptions. - // If this operation fails, the default MIME type will not be set. - } - - return mimeType; - } - - - /// - /// Uploads the file with given path - /// - /// The path of a file to upload - public void Upload(string path) - { - UploadAsync(path, CancellationToken.None).Wait(); - } - - /// - /// Uploads the file with given path asynchronously - /// - /// The path of a file to upload - /// A to use for canceling upload operation. - /// A function delegate that returns the future result to be available through the Task. - internal Task UploadAsync(string path, CancellationToken cancellationToken) - { - ValidateFileName(path); - - IAccessPolicy accessPolicy = null; - ILocator locator = null; - - var policyName = "SdkUpload" + Guid.NewGuid().ToString(); - return GetMediaContext().AccessPolicies - .CreateAsync(policyName, TimeSpan.FromHours(12), AccessPermissions.Write) - .ContinueWith( - t => - { - accessPolicy = t.Result; - - t.ThrowIfFaulted(() => this.Cleanup(null, null, locator, accessPolicy)); - cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, null, locator, accessPolicy)); - - locator = this.GetMediaContext().Locators.CreateSasLocator(this.Asset, accessPolicy); - cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, null, locator, accessPolicy)); - - return locator; - }, - cancellationToken). - ContinueWith( - t => - { - locator = t.Result; - t.ThrowIfFaulted(() => this.Cleanup(null, null, locator, accessPolicy)); - cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, null, locator, accessPolicy)); - - var blobTransfer = GetMediaContext().MediaServicesClassFactory.GetBlobTransferClient(); - - blobTransfer.NumberOfConcurrentTransfers = this.GetMediaContext().NumberOfConcurrentTransfers; - blobTransfer.ParallelTransferThreadCount = this.GetMediaContext().ParallelTransferThreadCount; - - UploadAsync(path, blobTransfer, locator, cancellationToken).Wait(); - locator.Delete(); - cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, null, null, accessPolicy)); - accessPolicy.Delete(); - }, - cancellationToken); - } - } -} + } + + EventHandler handler = (s, e) => OnUploadProgressChanged(path, e); + + blobTransferClient.TransferProgressChanged += handler; + + MediaRetryPolicy retryPolicy = this.GetMediaContext().MediaServicesClassFactory.GetBlobStorageClientRetryPolicy(); + + return blobTransferClient.UploadBlob( + new Uri(locator.BaseUri), + path, + null, + fileEncryption, + token, + retryPolicy.AsAzureStorageClientRetryPolicy(), + () => locator.ContentAccessComponent) + .ContinueWith( + ts=> + { + blobTransferClient.TransferProgressChanged -= handler; + this.PostUploadAction(ts, path, token); + }); + } + + private void OnUploadProgressChanged(string file, BlobTransferProgressChangedEventArgs blobTransferProgressChangedEventArgs) + { + if (blobTransferProgressChangedEventArgs.LocalFile == file) + { + var uploadChangeHandler = UploadProgressChanged; + if (uploadChangeHandler != null) + { + uploadChangeHandler( + sender: this, + e: new UploadProgressChangedEventArgs( + blobTransferProgressChangedEventArgs.BytesTransferred, + blobTransferProgressChangedEventArgs.TotalBytesToTransfer)); + } + } + } + + private void ValidateFileName(string path) + { + string filename = Path.GetFileName(path).ToUpperInvariant(); + if (filename != this.Name.ToUpperInvariant()) + { + throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, StringTable.FileNameMismatch, this.Name, filename)); + } + } + + + private void PostUploadAction(Task task,string path, CancellationToken token) + { + task.ThrowIfFaulted(); + token.ThrowIfCancellationRequested(); + + FileInfo fileInfo = new FileInfo(path); + + //Updating Name based on file name to avoid exceptions trying to download file.Mapping to storage account is through file name + this.Name = fileInfo.Name; + + // Set the ContentFileSize base on the local file size + this.ContentFileSize = fileInfo.Length; + + this.Update(); + } + #endregion + + #region IMediaContextContainer Members + + + + #endregion + + /// + /// Downloads the file asynchronously . + /// + /// The path to download the file to. + /// The which is used to download files. + /// An asset which defines permissions associated with the Asset. + /// The retry policy. + /// The cancellation token. + /// A function delegate that returns the future result to be available through the Task. + internal Task DownloadToFileAsync(string destinationPath,BlobTransferClient blobTransferClient, ILocator locator, IRetryPolicy retryPolicy, CancellationToken cancellationToken) + { + FileEncryption fileEncryption = this.GetFileEncryption(); + cancellationToken.Register(() => this.Cleanup(null, fileEncryption, null, null)); + return Task.Factory.StartNew(() => + { + cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, fileEncryption, null, null)); + ulong iv = Convert.ToUInt64(this.InitializationVector, CultureInfo.InvariantCulture); + UriBuilder uriBuilder = new UriBuilder(locator.BaseUri); + uriBuilder.Path += String.Concat("/", Name); + blobTransferClient.TransferProgressChanged += this.OnDownloadBlobTransferProgressChanged; + + blobTransferClient.DownloadBlob( + uriBuilder.Uri, + destinationPath, + fileEncryption, + iv, + cancellationToken, + retryPolicy, + () => locator.ContentAccessComponent) + .Wait(cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, fileEncryption, null, null)); + }, + cancellationToken) + .ContinueWith( + t => + { + t.ThrowIfFaulted(() => this.Cleanup(null, fileEncryption, null, null)); + cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, fileEncryption, null, null)); + this.Cleanup(null, fileEncryption, null, null); + }, + cancellationToken); + } + + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "All exception needs to be caught while cleaning up.")] + private void Cleanup(BlobTransferClient blobTransfer, FileEncryption fileEncryption, ILocator locator, IAccessPolicy accessPolicy) + { + lock (this._lock) + { + if (blobTransfer != null) + { + try + { + blobTransfer.TransferProgressChanged -= this.OnDownloadBlobTransferProgressChanged; + } + catch + { + } + finally + { + blobTransfer = null; + } + } + + if (fileEncryption != null) + { + try + { + fileEncryption.Dispose(); + } + catch + { + } + finally + { + fileEncryption = null; + } + } + + if (locator != null) + { + try + { + locator.Delete(); + } + catch + { + } + finally + { + locator = null; + } + } + + if (accessPolicy != null) + { + try + { + accessPolicy.Delete(); + } + catch + { + } + finally + { + accessPolicy = null; + } + } + } + } + + private FileEncryption GetFileEncryption() + { + if (!this.IsEncrypted) + { + return null; + } + + // We want to support downloading PlayReady encrypted content too. + if (this.EncryptionScheme != FileEncryption.SchemeName) + { + return null; + } + + IContentKey key = this.Asset.ContentKeys.Where(c => c.ContentKeyType == ContentKeyType.StorageEncryption).FirstOrDefault(); + Guid keyId = EncryptionUtils.GetKeyIdAsGuid(key.Id); + + return new FileEncryption(key.GetClearKeyValue(), keyId); + } + + private void OnDownloadBlobTransferProgressChanged(object sender, BlobTransferProgressChangedEventArgs e) + { + EventHandler downloadProgressEvent = this.DownloadProgressChanged; + if (downloadProgressEvent != null) + { + downloadProgressEvent(this, new DownloadProgressChangedEventArgs(e.BytesTransferred, e.TotalBytesToTransfer)); + } + } + + /// + /// Gets the MIME type of the specified destinationPath. + /// + /// Name of the destinationPath. + /// The MIME type. + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "All exception needs to be caught and return null in case of error.")] + internal static string GetMimeType(string fileName) + { + string mimeType = null; + + try + { + RegistryKey registryKey = Registry.ClassesRoot.OpenSubKey(Path.GetExtension(fileName)); + + if (registryKey != null) + { + object registryValue = registryKey.GetValue("Content Type"); + mimeType = registryValue != null ? registryValue.ToString() : null; + } + } + catch + { + // Catch all exceptions. + // If this operation fails, the default MIME type will not be set. + } + + return mimeType; + } + + + /// + /// Uploads the file with given path + /// + /// The path of a file to upload + public void Upload(string path) + { + if (this.IsFragmented()) + { + throw new NotSupportedException(StringTable.NotSupportedFragblobUpload); + } + + UploadAsync(path, CancellationToken.None).Wait(); + } + + /// + /// Uploads the file with given path asynchronously + /// + /// The path of a file to upload + /// A to use for canceling upload operation. + /// A function delegate that returns the future result to be available through the Task. + internal Task UploadAsync(string path, CancellationToken cancellationToken) + { + ValidateFileName(path); + + IAccessPolicy accessPolicy = null; + ILocator locator = null; + + var policyName = "SdkUpload" + Guid.NewGuid().ToString(); + return GetMediaContext().AccessPolicies + .CreateAsync(policyName, TimeSpan.FromHours(12), AccessPermissions.Write) + .ContinueWith( + t => + { + accessPolicy = t.Result; + + t.ThrowIfFaulted(() => this.Cleanup(null, null, locator, accessPolicy)); + cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, null, locator, accessPolicy)); + + locator = this.GetMediaContext().Locators.CreateSasLocator(this.Asset, accessPolicy); + cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, null, locator, accessPolicy)); + + return locator; + }, + cancellationToken). + ContinueWith( + t => + { + locator = t.Result; + t.ThrowIfFaulted(() => this.Cleanup(null, null, locator, accessPolicy)); + cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, null, locator, accessPolicy)); + + var blobTransfer = GetMediaContext().MediaServicesClassFactory.GetBlobTransferClient(); + + blobTransfer.NumberOfConcurrentTransfers = this.GetMediaContext().NumberOfConcurrentTransfers; + blobTransfer.ParallelTransferThreadCount = this.GetMediaContext().ParallelTransferThreadCount; + + UploadAsync(path, blobTransfer, locator, cancellationToken).Wait(); + locator.Delete(); + cancellationToken.ThrowIfCancellationRequested(() => this.Cleanup(null, null, null, accessPolicy)); + accessPolicy.Delete(); + }, + cancellationToken); + } + + private bool IsFragmented() + { + return GetExposedOptions(Options).HasFlag(Client.AssetFileOptions.Fragmented); + } + } +} diff --git a/src/net/Client/Entities/AssetFileOptions.cs b/src/net/Client/Entities/AssetFileOptions.cs new file mode 100644 index 00000000..7e1c27eb --- /dev/null +++ b/src/net/Client/Entities/AssetFileOptions.cs @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------- +// Copyright 2012 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Specifies the the different options that an can have. + /// + /// This is a flags enum. + [Flags] + public enum AssetFileOptions + { + /// + /// Specifies no asset file options. + /// + /// This is the default value. + None = 0x0, + + /// + /// Specifies that the AssetFile is Fragmented. + /// + Fragmented = 0x1 + } +} diff --git a/src/net/Client/Entities/AssetFilterData.cs b/src/net/Client/Entities/AssetFilterData.cs new file mode 100644 index 00000000..59946f8f --- /dev/null +++ b/src/net/Client/Entities/AssetFilterData.cs @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Data.Services.Common; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// AssetFilter data class holds AssetFilter entity from REST + /// + [DataServiceKey("Id")] + internal class AssetFilterData : StreamingFilterData, IStreamingAssetFilter + { + + public AssetFilterData() + { + ResourceSetName = AssetFilterBaseCollection.AssetFilterSet; + Id = String.Empty; + } + + public AssetFilterData( + string parentAssetId, + string name, + PresentationTimeRange timeRange, + IList trackConditions) + : base(name, timeRange, trackConditions) + { + ParentAssetId = parentAssetId; + Id = String.Empty; + ResourceSetName = AssetFilterBaseCollection.AssetFilterSet; + } + + public string Id { get; set; } + + public string ParentAssetId { get; set; } + + } +} diff --git a/src/net/Client/Entities/FilterTrackBitrateRange.cs b/src/net/Client/Entities/FilterTrackBitrateRange.cs new file mode 100644 index 00000000..2a11eb94 --- /dev/null +++ b/src/net/Client/Entities/FilterTrackBitrateRange.cs @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Globalization; +using System.Linq; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Define a bitrate range for track selection condition + /// + public class FilterTrackBitrateRange + { + public FilterTrackBitrateRange() + { + } + + public FilterTrackBitrateRange(Int32? lowBound, Int32? highBound) + { + LowBound = lowBound; + HighBound = highBound; + } + + internal FilterTrackBitrateRange(FilterTrackBitrateRange other) + { + LowBound = other.LowBound; + HighBound = other.HighBound; + } + + internal FilterTrackBitrateRange(string strValue) + { + Parse(strValue); + } + + /// + /// Define low bound of bitrate + /// + public Int32? LowBound { get; set; } + + /// + /// Define high bound of bitrate + /// + public Int32? HighBound { get; set; } + + private void Parse(string strBitrate) + { + String[] sepStrings = strBitrate.Split(new char[] { FilterTrackBitrateRangeData.cRangeSign }); + + if (sepStrings.Count() > 1) + { + if (!String.IsNullOrEmpty(sepStrings[0])) + { + LowBound = Int32.Parse(sepStrings[0], CultureInfo.InvariantCulture); + } + else + { + LowBound = null; + } + + if (!String.IsNullOrEmpty(sepStrings[1])) + { + HighBound = Int32.Parse(sepStrings[1], CultureInfo.InvariantCulture); + } + else + { + HighBound = null; + } + } + else + { + LowBound = HighBound = Int32.Parse(strBitrate, CultureInfo.InvariantCulture); + } + } + } +} diff --git a/src/net/Client/Entities/FilterTrackBitrateRangeCondition.cs b/src/net/Client/Entities/FilterTrackBitrateRangeCondition.cs new file mode 100644 index 00000000..6004a81a --- /dev/null +++ b/src/net/Client/Entities/FilterTrackBitrateRangeCondition.cs @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Bitrate range condition + /// + public class FilterTrackBitrateRangeCondition : FilterTrackPropertyBaseCondition + { + public FilterTrackBitrateRangeCondition() + { + } + + /// + /// + /// + /// A range of bitrates or a specific bitrate. For example, 0-2427000. + /// + public FilterTrackBitrateRangeCondition(FilterTrackBitrateRange filterTrackBitrateRange, FilterTrackCompareOperator filterTrackCompareOperator = FilterTrackCompareOperator.Equal) + : base(filterTrackCompareOperator) + { + Value = filterTrackBitrateRange; + } + /// + /// + /// + /// string representation of + /// string representation of + internal FilterTrackBitrateRangeCondition(string filterTrackBitrateRange, string filterTrackCompareOperator) + : base(filterTrackCompareOperator) + { + Value = new FilterTrackBitrateRange(filterTrackBitrateRange); + } + /// + /// A range of bitrates or a specific bitrate. For example, 0-2427000. + /// + public FilterTrackBitrateRange Value { get; private set; } + } +} diff --git a/src/net/Client/Entities/FilterTrackBitrateRangeData.cs b/src/net/Client/Entities/FilterTrackBitrateRangeData.cs new file mode 100644 index 00000000..f2042b1c --- /dev/null +++ b/src/net/Client/Entities/FilterTrackBitrateRangeData.cs @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Globalization; +using System.Text; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Bitrate range condition data + /// + internal class FilterTrackBitrateRangeData + { + internal const char cRangeSign = '-'; + + public FilterTrackBitrateRangeData() + { + } + + public FilterTrackBitrateRangeData(FilterTrackBitrateRange range) + { + Range = Serialize(range); + } + + public string Range { get; set; } + + /// + /// Serialize bitrate to a string presentation like 128000-56000 + /// + private static string Serialize(FilterTrackBitrateRange range) + { + if (!range.LowBound.HasValue && !range.HighBound.HasValue) + { + return string.Empty; + } + + if (range.LowBound.HasValue && range.HighBound.HasValue && range.LowBound == range.HighBound) + { + return range.LowBound.Value.ToString(CultureInfo.InvariantCulture); + } + + StringBuilder builder = new StringBuilder(); + + if (range.LowBound.HasValue) + { + builder.Append(range.LowBound.Value.ToString(CultureInfo.InvariantCulture)); + } + + builder.Append(cRangeSign); + + if (range.HighBound.HasValue) + { + builder.Append(range.HighBound.Value.ToString(CultureInfo.InvariantCulture)); + } + + return builder.ToString(); + } + + } +} diff --git a/src/net/Client/Entities/FilterTrackCompareOperator.cs b/src/net/Client/Entities/FilterTrackCompareOperator.cs new file mode 100644 index 00000000..4d2352a8 --- /dev/null +++ b/src/net/Client/Entities/FilterTrackCompareOperator.cs @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Compare operator + /// + public enum FilterTrackCompareOperator + { + Equal = 0, + NotEqual + } +} diff --git a/src/net/Client/Entities/FilterTrackFourCCCondition.cs b/src/net/Client/Entities/FilterTrackFourCCCondition.cs new file mode 100644 index 00000000..52e89347 --- /dev/null +++ b/src/net/Client/Entities/FilterTrackFourCCCondition.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// FourCC condition + /// + public class FilterTrackFourCCCondition : FilterTrackPropertyBaseCondition + { + public FilterTrackFourCCCondition() + { + } + + /// + /// Initilizing FilterTrackFourCCCondition + /// + /// The first element of codecs format, as specified in RFC 6381. + /// FilterTrackCompareOperator + public FilterTrackFourCCCondition(string codecFormat, FilterTrackCompareOperator filterTrackCompareOperator = FilterTrackCompareOperator.Equal) + : base(filterTrackCompareOperator) + { + Value = codecFormat; + } + + /// + /// Initilizing FilterTrackFourCCCondition + /// + /// The first element of codecs format, as specified in RFC 6381. + /// String representation of FilterTrackCompareOperator + internal FilterTrackFourCCCondition(string codecFormat, string filterTrackCompareOperator) + : base(filterTrackCompareOperator) + { + Value = codecFormat; + } + + /// + /// The first element of codecs format, as specified in RFC 6381. + /// + public string Value { get; private set; } + } +} diff --git a/src/net/Client/Entities/FilterTrackLanguageCondition.cs b/src/net/Client/Entities/FilterTrackLanguageCondition.cs new file mode 100644 index 00000000..3dbf89df --- /dev/null +++ b/src/net/Client/Entities/FilterTrackLanguageCondition.cs @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Language condition + /// + public class FilterTrackLanguageCondition : FilterTrackPropertyBaseCondition + { + public FilterTrackLanguageCondition() + { + } + /// + /// Initilizes FilterTrackLanguageCondition + /// + /// + /// + public FilterTrackLanguageCondition(string languageValue, FilterTrackCompareOperator filterTrackCompareOperator = FilterTrackCompareOperator.Equal) + : base(filterTrackCompareOperator) + { + Value = languageValue; + } + /// + /// Initilizes FilterTrackLanguageCondition + /// + /// Tag of a language you want to include, as specified in RFC 5646. For example, en. + /// + /// string representation of + internal FilterTrackLanguageCondition(string languageValue, string filterTrackCompareOperator) + : base(filterTrackCompareOperator) + { + Value = languageValue; + } + /// + /// Tag of a language you want to include, as specified in RFC 5646. For example, en. + /// + public string Value { get; private set; } + } +} diff --git a/src/net/Client/Entities/FilterTrackNameCondition.cs b/src/net/Client/Entities/FilterTrackNameCondition.cs new file mode 100644 index 00000000..8b011f0f --- /dev/null +++ b/src/net/Client/Entities/FilterTrackNameCondition.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Track Name condition + /// + public class FilterTrackNameCondition : FilterTrackPropertyBaseCondition + { + public FilterTrackNameCondition() + { + } + + /// + /// Initilize FilterTrackNameCondition + /// + /// Name of a track + /// + public FilterTrackNameCondition(string trackName, FilterTrackCompareOperator filterTrackCompareOperator = FilterTrackCompareOperator.Equal) + : base(filterTrackCompareOperator) + { + Value = trackName; + } + + /// + /// + /// + /// Name of a track + /// string representation of + internal FilterTrackNameCondition(string trackName, string filterTrackCompareOperator) + : base(filterTrackCompareOperator) + { + Value = trackName; + } + + /// + /// Contains name of a track used for filtering + /// + public string Value { get; private set; } + } +} diff --git a/src/net/Client/Entities/FilterTrackPropertyBaseCondition.cs b/src/net/Client/Entities/FilterTrackPropertyBaseCondition.cs new file mode 100644 index 00000000..330597dc --- /dev/null +++ b/src/net/Client/Entities/FilterTrackPropertyBaseCondition.cs @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.IO; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Base track property condition + /// + public abstract class FilterTrackPropertyBaseCondition : IFilterTrackPropertyCondition + { + protected FilterTrackPropertyBaseCondition() + { + Operator = FilterTrackCompareOperator.Equal; + } + + protected FilterTrackPropertyBaseCondition(FilterTrackCompareOperator op) + { + Operator = op; + } + + internal FilterTrackPropertyBaseCondition(string strOperator) + { + FilterTrackCompareOperator op; + if (!Enum.TryParse(strOperator, true, out op)) + { + throw new InvalidDataException("Filter track compare operation is invalid"); + } + + Operator = op; + } + + /// + /// Compare operator + /// + public FilterTrackCompareOperator Operator { get; private set; } + } +} diff --git a/src/net/Client/Entities/FilterTrackPropertyConditionData.cs b/src/net/Client/Entities/FilterTrackPropertyConditionData.cs new file mode 100644 index 00000000..8b291bb4 --- /dev/null +++ b/src/net/Client/Entities/FilterTrackPropertyConditionData.cs @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Track property condition data + /// + internal class FilterTrackPropertyConditionData + { + internal static readonly string TypeProperty = "Type"; + internal static readonly string NameProperty = "Name"; + internal static readonly string LanguageProperty = "Language"; + internal static readonly string FourCCProperty = "FourCC"; + internal static readonly string BitrateProperty = "Bitrate"; + + public FilterTrackPropertyConditionData() + { + } + + public FilterTrackPropertyConditionData(IFilterTrackPropertyCondition baseCondition) + { + Set(baseCondition as FilterTrackTypeCondition); + Set(baseCondition as FilterTrackNameCondition); + Set(baseCondition as FilterTrackLanguageCondition); + Set(baseCondition as FilterTrackFourCCCondition); + Set(baseCondition as FilterTrackBitrateRangeCondition); + } + + [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] + private void Set(FilterTrackTypeCondition condition) + { + if (condition != null) + { + Property = TypeProperty; + Value = condition.Value.ToString().ToLower(CultureInfo.InvariantCulture); + Operator = condition.Operator.ToString(); + } + } + + private void Set(FilterTrackNameCondition condition) + { + if (condition != null) + { + Property = NameProperty; + Value = condition.Value; + Operator = condition.Operator.ToString(); + } + } + + private void Set(FilterTrackLanguageCondition condition) + { + if (condition != null) + { + Property = LanguageProperty; + Value = condition.Value; + Operator = condition.Operator.ToString(); + } + } + + private void Set(FilterTrackFourCCCondition condition) + { + if (condition != null) + { + Property = FourCCProperty; + Value = condition.Value; + Operator = condition.Operator.ToString(); + } + } + + private void Set(FilterTrackBitrateRangeCondition condition) + { + if (condition != null) + { + Property = BitrateProperty; + FilterTrackBitrateRangeData rangeData = + new FilterTrackBitrateRangeData(condition.Value); + Value = rangeData.Range; + Operator = condition.Operator.ToString(); + } + } + + /// + /// FilterTrackProperty + /// + public String Property { get; set; } + + /// + /// Value of property, depend on type + /// + public String Value { get; set; } + + /// + /// FilterTrackCompareOperator + /// + public String Operator { get; set; } + + } +} diff --git a/src/net/Client/Entities/FilterTrackSelectStatement.cs b/src/net/Client/Entities/FilterTrackSelectStatement.cs new file mode 100644 index 00000000..1d031660 --- /dev/null +++ b/src/net/Client/Entities/FilterTrackSelectStatement.cs @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Track select statement + /// + public class FilterTrackSelectStatement + { + public FilterTrackSelectStatement() + { + PropertyConditions = new List(); + } + + internal FilterTrackSelectStatement(FilterTrackSelectStatementData data) + { + PropertyConditions = data.PropertyConditions.Select(pc => FromData(pc)).ToList().AsReadOnly(); + } + + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public IList PropertyConditions { get; set; } + + private static IFilterTrackPropertyCondition FromData(FilterTrackPropertyConditionData data) + { + if (data.Property == FilterTrackPropertyConditionData.TypeProperty) + { + return new FilterTrackTypeCondition(data.Value, data.Operator); + } + else if (data.Property == FilterTrackPropertyConditionData.NameProperty) + { + return new FilterTrackNameCondition(data.Value, data.Operator); + } + else if (data.Property == FilterTrackPropertyConditionData.LanguageProperty) + { + return new FilterTrackLanguageCondition(data.Value, data.Operator); + } + else if (data.Property == FilterTrackPropertyConditionData.FourCCProperty) + { + return new FilterTrackFourCCCondition(data.Value, data.Operator); + } + else if (data.Property == FilterTrackPropertyConditionData.BitrateProperty) + { + return new FilterTrackBitrateRangeCondition(data.Value, data.Operator); + } + + return null; + } + } +} diff --git a/src/net/Client/Entities/FilterTrackSelectStatementData.cs b/src/net/Client/Entities/FilterTrackSelectStatementData.cs new file mode 100644 index 00000000..93b013e3 --- /dev/null +++ b/src/net/Client/Entities/FilterTrackSelectStatementData.cs @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Track select statement data + /// + internal class FilterTrackSelectStatementData + { + public FilterTrackSelectStatementData() + { + PropertyConditions = new List(); + } + + public FilterTrackSelectStatementData(FilterTrackSelectStatement track) + { + if (track != null) + { + PropertyConditions = + track.PropertyConditions.Select(c => new FilterTrackPropertyConditionData(c)).ToList(); + } + } + + public List PropertyConditions { get; set; } + } +} diff --git a/src/net/Client/Entities/FilterTrackType.cs b/src/net/Client/Entities/FilterTrackType.cs new file mode 100644 index 00000000..2dbbe37f --- /dev/null +++ b/src/net/Client/Entities/FilterTrackType.cs @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Type of Track + /// + public enum FilterTrackType + { + /// + /// Audio + /// + Audio = 0, + + /// + /// Video + /// + Video = 1, + + /// + /// Text + /// + Text = 2, + } +} diff --git a/src/net/Client/Entities/FilterTrackTypeCondition.cs b/src/net/Client/Entities/FilterTrackTypeCondition.cs new file mode 100644 index 00000000..debb6fdf --- /dev/null +++ b/src/net/Client/Entities/FilterTrackTypeCondition.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.IO; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Type condition for track + /// + public class FilterTrackTypeCondition : FilterTrackPropertyBaseCondition + { + public FilterTrackTypeCondition() + { + } + + public FilterTrackTypeCondition(FilterTrackType type, FilterTrackCompareOperator op = FilterTrackCompareOperator.Equal) + : base(op) + { + Value = type; + } + + internal FilterTrackTypeCondition(string strTypeValue, string strOperator) + : base(strOperator) + { + Parse(strTypeValue); + } + + public FilterTrackType Value { get; set; } + + private void Parse(string strTypeValue) + { + FilterTrackType trackType; + if (!FilterTrackType.TryParse(strTypeValue, true, out trackType)) + { + throw new InvalidDataException("Filter track type is invalid"); + } + + Value = trackType; + } + } +} diff --git a/src/net/Client/Entities/IAsset.cs b/src/net/Client/Entities/IAsset.cs index b68ee4a1..9737f2af 100644 --- a/src/net/Client/Entities/IAsset.cs +++ b/src/net/Client/Entities/IAsset.cs @@ -32,6 +32,11 @@ public partial interface IAsset /// A collection of files contained by the Asset. AssetFileBaseCollection AssetFiles { get; } + /// + /// Get a collection of filters for this asset + /// + AssetFilterBaseCollection AssetFilters { get; } + /// /// Gets the Locators associated with this asset. /// @@ -73,15 +78,29 @@ public partial interface IAsset void Update(); /// - /// Asynchronously deletes this asset instance. + /// Asynchronously deletes this asset instance including underlying azure storage container /// /// A function delegate that returns the future result to be available through the Task. Task DeleteAsync(); /// - /// Deletes this asset instance. + /// Asynchronously deletes this asset instance. + /// + /// if set to true underlying storage asset container is preserved during the delete operation. + /// Task of type + Task DeleteAsync(bool keepAzureStorageContainer); + + /// + /// Deletes this asset instance including underlying azure storage container /// void Delete(); + + /// + /// Deletes this asset instance + /// + /// if set to true underlying storage asset container is preserved during the delete operation. + /// IMediaDataServiceResponse. + IMediaDataServiceResponse Delete(bool keepAzureStorageContainer); } } diff --git a/src/net/Client/Entities/IAssetFile.cs b/src/net/Client/Entities/IAssetFile.cs index 991be66c..ac8348c9 100644 --- a/src/net/Client/Entities/IAssetFile.cs +++ b/src/net/Client/Entities/IAssetFile.cs @@ -21,7 +21,7 @@ namespace Microsoft.WindowsAzure.MediaServices.Client { /// - /// Represents an file belonging to an Asset. + /// Represents a file belonging to an Asset. /// /// public partial interface IAssetFile diff --git a/src/net/Client/Entities/IFilterTrackPropertyCondition.cs b/src/net/Client/Entities/IFilterTrackPropertyCondition.cs new file mode 100644 index 00000000..6644ae99 --- /dev/null +++ b/src/net/Client/Entities/IFilterTrackPropertyCondition.cs @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Interface for track property condition + /// + public interface IFilterTrackPropertyCondition + { + FilterTrackCompareOperator Operator { get; } + } +} diff --git a/src/net/Client/Entities/IStreamingAssetFilter.cs b/src/net/Client/Entities/IStreamingAssetFilter.cs new file mode 100644 index 00000000..e9d74c6d --- /dev/null +++ b/src/net/Client/Entities/IStreamingAssetFilter.cs @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Interface for asset level filter + /// + public interface IStreamingAssetFilter : IStreamingFilter + { + /// + /// Id + /// + string Id { get; } + + /// + /// Parent asset id + /// + string ParentAssetId { get; } + } +} diff --git a/src/net/Client/Entities/IStreamingFilter.cs b/src/net/Client/Entities/IStreamingFilter.cs new file mode 100644 index 00000000..4064aea0 --- /dev/null +++ b/src/net/Client/Entities/IStreamingFilter.cs @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Interface for account level filter + /// + public interface IStreamingFilter + { + /// + /// Name of filter + /// + string Name { get; } + + /// + /// Presentation time range + /// + PresentationTimeRange PresentationTimeRange { get; set; } + + /// + /// Track selection conditions + /// + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + IList Tracks { get; set; } + + /// + /// Updates this filter instance asynchronously. + /// + /// Task to wait on for operation completion. + Task UpdateAsync(); + + /// + /// Updates this filter instance. + /// + void Update(); + + /// + /// Asynchronously revokes the specified filter + /// + /// A function delegate that returns the future result to be available through the Task<IStreamingFilter>. + Task DeleteAsync(); + + /// + /// Deletes the specified filter + /// + void Delete(); + } +} diff --git a/src/net/Client/Entities/ODataClasses.cs b/src/net/Client/Entities/ODataClasses.cs index 90b7ffd3..f2384288 100644 --- a/src/net/Client/Entities/ODataClasses.cs +++ b/src/net/Client/Entities/ODataClasses.cs @@ -94,6 +94,16 @@ partial class AssetFileData : IAssetFile public string EncryptionKeyId {get;set;} [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public string InitializationVector {get;set;} + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public int Options {get;set;} + + AssetFileOptions IAssetFile.AssetFileOptions + { + get + { + return GetExposedOptions(Options); + } + } } diff --git a/src/net/Client/Entities/OutputAsset.cs b/src/net/Client/Entities/OutputAsset.cs index e94bc9bf..ff430109 100644 --- a/src/net/Client/Entities/OutputAsset.cs +++ b/src/net/Client/Entities/OutputAsset.cs @@ -145,6 +145,11 @@ IStorageAccount IAsset.StorageAccount } } + public AssetFilterBaseCollection AssetFilters + { + get { throw new NotSupportedException(); } + } + /// /// Updates this instance. @@ -172,6 +177,18 @@ public Task DeleteAsync() throw new NotImplementedException(); } + + /// + /// Deletes this asset instance + /// Determines whether or not the underlying storage asset container is preseved during the delete operation + /// + /// if set to true underlying storage asset container is preserved during the delete operation. + /// IMediaDataServiceResponse. + public Task DeleteAsync(bool keepAzureStorageContainer) + { + throw new NotImplementedException(); + } + /// /// Deletes this instance. /// @@ -180,5 +197,14 @@ public void Delete() throw new NotImplementedException(); } + /// + /// Deletes this asset instance + /// + /// if set to true underlying storage asset container is preserved during the delete operation. + /// IMediaDataServiceResponse. + public IMediaDataServiceResponse Delete(bool keepAzureStorageContainer) + { + throw new NotSupportedException(); + } } } diff --git a/src/net/Client/Entities/PresentationTimeRange.cs b/src/net/Client/Entities/PresentationTimeRange.cs new file mode 100644 index 00000000..e9648d1d --- /dev/null +++ b/src/net/Client/Entities/PresentationTimeRange.cs @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Globalization; +using System.IO; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// Define a presentation time range + /// + public class PresentationTimeRange + { + public const UInt64 TimescaleHns = 10000000; + + + public PresentationTimeRange( + UInt64? timescale = TimescaleHns, + UInt64? start = null, + UInt64? end = null, + TimeSpan? pwDuration = null, + TimeSpan? backoff = null) + { + Timescale = timescale; + StartTimestamp = start; + EndTimestamp = end; + PresentationWindowDuration = pwDuration; + LiveBackoffDuration = backoff; + + Validate(); + } + + internal PresentationTimeRange(PresentationTimeRangeData data) + { + if (data.Timescale != (Int64) TimescaleHns) + { + Timescale = (UInt64)data.Timescale; + } + + if (data.StartTimestamp > 0) + { + StartTimestamp = (UInt64) data.StartTimestamp; + } + + if (data.EndTimestamp != Int64.MaxValue) + { + EndTimestamp = (UInt64)data.EndTimestamp; + } + + if (data.PresentationWindowDuration != Int64.MaxValue) + { + PresentationWindowDuration = + TimeSpan.FromMilliseconds((data.PresentationWindowDuration/data.Timescale)*1000); + } + + if (data.LiveBackoffDuration != 0) + { + LiveBackoffDuration = + TimeSpan.FromMilliseconds((data.LiveBackoffDuration / data.Timescale) * 1000); + } + + Validate(); + } + + /// + /// Timescale + /// + public UInt64? Timescale { get; private set; } + + /// + /// Define an absolute start point + /// + public UInt64? StartTimestamp { get; private set; } + + /// + /// Define an absolute end point + /// + public UInt64? EndTimestamp { get; private set; } + + /// + /// Define a sliding window, left edge is relative to the end + /// + public TimeSpan? PresentationWindowDuration { get; private set; } + + /// + /// Define a live back off, presentation window right edge is relative to the end + /// + public TimeSpan? LiveBackoffDuration { get; private set; } + + private void Validate() + { + if (StartTimestamp > EndTimestamp) + { + throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, "StartTimestamp is larger than EndTimestamp")); + } + } + } +} diff --git a/src/net/Client/Entities/PresentationTimeRangeData.cs b/src/net/Client/Entities/PresentationTimeRangeData.cs new file mode 100644 index 00000000..a1921be0 --- /dev/null +++ b/src/net/Client/Entities/PresentationTimeRangeData.cs @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + /// + /// PresentationTimeRangeData holds PresentationTimeRange property from REST + /// + public class PresentationTimeRangeData + { + public PresentationTimeRangeData() + { + Timescale = (Int64)PresentationTimeRange.TimescaleHns; + StartTimestamp = 0; + EndTimestamp = Int64.MaxValue; + PresentationWindowDuration = Int64.MaxValue; + LiveBackoffDuration = 0; + } + + public PresentationTimeRangeData(PresentationTimeRange range) + { + if (range == null) + { + throw new ArgumentNullException("range"); + } + + Timescale = (Int64)(range.Timescale ?? PresentationTimeRange.TimescaleHns); + StartTimestamp = (Int64) (range.StartTimestamp ?? 0); + EndTimestamp = (Int64) (range.EndTimestamp ?? Int64.MaxValue); + + PresentationWindowDuration = range.PresentationWindowDuration.HasValue && range.PresentationWindowDuration.Value != TimeSpan.MaxValue ? + (Int64)range.PresentationWindowDuration.Value.TotalMilliseconds * (Timescale / 1000) : + Int64.MaxValue; + LiveBackoffDuration = range.LiveBackoffDuration.HasValue && range.LiveBackoffDuration.Value != TimeSpan.MaxValue ? + (Int64)range.LiveBackoffDuration.Value.TotalMilliseconds * (Timescale / 1000) : + 0; + } + + /// + /// Timescale + /// + public Int64 Timescale { get; set; } + + /// + /// Define an absolute start point + /// + public Int64 StartTimestamp { get; set; } + + /// + /// Define an absolute end point + /// + public Int64 EndTimestamp { get; set; } + + /// + /// Define a sliding window, left edge is relative to the end + /// + public Int64 PresentationWindowDuration { get; set; } + + /// + /// Define a live back off, presentation window right edge is relative to the end + /// + public Int64 LiveBackoffDuration { get; set; } + + } +} diff --git a/src/net/Client/Entities/StreamingFilterData.cs b/src/net/Client/Entities/StreamingFilterData.cs new file mode 100644 index 00000000..d2198f37 --- /dev/null +++ b/src/net/Client/Entities/StreamingFilterData.cs @@ -0,0 +1,156 @@ +//----------------------------------------------------------------------- +// Copyright 2015 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Data.Services.Common; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.WindowsAzure.MediaServices.Client.TransientFaultHandling; + +namespace Microsoft.WindowsAzure.MediaServices.Client +{ + [DataServiceKey("Name")] + internal class StreamingFilterData : BaseEntity, IStreamingFilter + { + public StreamingFilterData() + { + PresentationTimeRange = new PresentationTimeRangeData(); + Tracks = new List(); + ResourceSetName = StreamingFilterBaseCollection.FilterSet; + } + + public StreamingFilterData(string name, PresentationTimeRange timeRange, + IList trackConditions) + { + Name = name; + PresentationTimeRange = timeRange != null + ? new PresentationTimeRangeData(timeRange) + : new PresentationTimeRangeData(); + Tracks = trackConditions != null + ? trackConditions.Select(track => new FilterTrackSelectStatementData(track)).ToList() + : new List(); + ResourceSetName = StreamingFilterBaseCollection.FilterSet; + } + + /// + /// Name of filter + /// + public string Name { get; set; } + + /// + /// Presentation time range + /// + public PresentationTimeRangeData PresentationTimeRange { get; set; } + + PresentationTimeRange IStreamingFilter.PresentationTimeRange + { + get + { + return new PresentationTimeRange(PresentationTimeRange); + } + set + { + if (value != null) + { + PresentationTimeRange = new PresentationTimeRangeData(value); + } + } + } + + /// + /// Track selection conditions + /// + public List Tracks { get; set; } + + protected string ResourceSetName { get; set; } + + IList IStreamingFilter.Tracks + { + get { return Tracks.Select(track => new FilterTrackSelectStatement(track)).ToList().AsReadOnly(); } + set + { + if (value != null) + { + Tracks = value.Select(sel => new FilterTrackSelectStatementData(sel)).ToList(); + } + } + } + + /// + /// Updates Filter asynchronouslly + /// + /// + public virtual Task UpdateAsync() + { + Validate(); + + IMediaDataServiceContext dataContext = this.GetMediaContext().MediaServicesClassFactory.CreateDataServiceContext(); + dataContext.AttachTo(ResourceSetName, this); + dataContext.UpdateObject(this); + + MediaRetryPolicy retryPolicy = this.GetMediaContext().MediaServicesClassFactory.GetSaveChangesRetryPolicy(dataContext as IRetryPolicyAdapter); + + return retryPolicy.ExecuteAsync(() => dataContext.SaveChangesAsync(this)); + } + + /// + /// Updates this instance + /// + public void Update() + { + AsyncHelper.Wait(UpdateAsync()); + } + + /// + /// Deletes this instance. + /// + public virtual void Delete() + { + AsyncHelper.Wait(DeleteAsync()); + } + + /// + /// Deletes this instance asynchronously. + /// + public virtual Task DeleteAsync() + { + Validate(); + + IMediaDataServiceContext dataContext = GetMediaContext().MediaServicesClassFactory.CreateDataServiceContext(); + dataContext.AttachTo(ResourceSetName, this); + dataContext.DeleteObject(this); + + MediaRetryPolicy retryPolicy = GetMediaContext().MediaServicesClassFactory.GetSaveChangesRetryPolicy(dataContext as IRetryPolicyAdapter); + + return retryPolicy.ExecuteAsync(() => dataContext.SaveChangesAsync(this)); + } + + private void Validate() + { + if (String.IsNullOrEmpty(Name)) + { + throw new InvalidDataException("Filter name is empty"); + } + + if (Tracks == null) + { + Tracks = new List(); + } + } + } +} diff --git a/src/net/Client/IMediaDataServiceContext.cs b/src/net/Client/IMediaDataServiceContext.cs index c3315ff4..b0741d13 100644 --- a/src/net/Client/IMediaDataServiceContext.cs +++ b/src/net/Client/IMediaDataServiceContext.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Data.Services.Client; using System.Linq; +using System.Threading; using System.Threading.Tasks; namespace Microsoft.WindowsAzure.MediaServices.Client @@ -295,6 +296,15 @@ public interface IMediaDataServiceContext /// The options. /// The state. /// A function delegate that returns the future result to be available through the Task. - Task SaveChangesAsync(SaveChangesOptions options, object state); + Task SaveChangesAsync(SaveChangesOptions options, object state); + + /// + /// Saves the changes asynchronously. + /// + /// The options. + /// The state. + /// + /// A function delegate that returns the future result to be available through the Task. + Task SaveChangesAsync(SaveChangesOptions options, object state, CancellationToken token); } } diff --git a/src/net/Client/Live/ChannelBaseCollection.cs b/src/net/Client/Live/ChannelBaseCollection.cs index dd1a55bc..0ef0c73e 100644 --- a/src/net/Client/Live/ChannelBaseCollection.cs +++ b/src/net/Client/Live/ChannelBaseCollection.cs @@ -36,7 +36,7 @@ public sealed class ChannelBaseCollection : CloudBaseCollection internal ChannelBaseCollection(MediaContextBase cloudMediaContext) : base(cloudMediaContext) { - Queryable = MediaContext.MediaServicesClassFactory.CreateDataServiceContext().CreateQuery(ChannelSet); + Queryable = MediaContext.MediaServicesClassFactory.CreateDataServiceContext().CreateQuery(ChannelSet); } /// diff --git a/src/net/Client/Live/ChannelEncodingType.cs b/src/net/Client/Live/ChannelEncodingType.cs index f0f5ef9b..1fb5493e 100644 --- a/src/net/Client/Live/ChannelEncodingType.cs +++ b/src/net/Client/Live/ChannelEncodingType.cs @@ -27,6 +27,11 @@ public enum ChannelEncodingType /// /// Channel has standard encoding capability. /// - Standard = 1 + Standard = 1, + + /// + /// Channel has premium encoding capability. + /// + Premium = 2, } } diff --git a/src/net/Client/Live/ProgramBaseCollection.cs b/src/net/Client/Live/ProgramBaseCollection.cs index 43e46c5f..db87bf7d 100644 --- a/src/net/Client/Live/ProgramBaseCollection.cs +++ b/src/net/Client/Live/ProgramBaseCollection.cs @@ -37,8 +37,8 @@ public class ProgramBaseCollection : CloudBaseCollection internal ProgramBaseCollection(MediaContextBase cloudMediaContext) : base(cloudMediaContext) { - var dataContext = cloudMediaContext.MediaServicesClassFactory.CreateDataServiceContext(); - _programQuery = new Lazy>(() => dataContext.CreateQuery(ProgramSet)); + var dataContext = cloudMediaContext.MediaServicesClassFactory.CreateDataServiceContext(); + _programQuery = new Lazy>(() => MediaContext.MediaServicesClassFactory.CreateDataServiceContext().CreateQuery(ProgramSet)); } /// diff --git a/src/net/Client/MediaContextBase.cs b/src/net/Client/MediaContextBase.cs index a00bf1a6..0d135b8c 100644 --- a/src/net/Client/MediaContextBase.cs +++ b/src/net/Client/MediaContextBase.cs @@ -109,6 +109,7 @@ public abstract partial class MediaContextBase public abstract IngestManifestCollection IngestManifests { get; } public abstract IngestManifestAssetCollection IngestManifestAssets { get; } public abstract LocatorBaseCollection Locators { get; } + public abstract StreamingFilterBaseCollection Filters { get; } /// /// Gets or sets the number of threads to use to for each blob transfer. diff --git a/src/net/Client/MediaDataServiceContext.cs b/src/net/Client/MediaDataServiceContext.cs index 14bae242..44a9f43e 100644 --- a/src/net/Client/MediaDataServiceContext.cs +++ b/src/net/Client/MediaDataServiceContext.cs @@ -17,7 +17,9 @@ using System; using System.Collections.Generic; using System.Data.Services.Client; +using System.Diagnostics; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.WindowsAzure.MediaServices.Client.RequestAdapters; using Microsoft.WindowsAzure.MediaServices.Client.TransientFaultHandling; @@ -414,12 +416,28 @@ public Task SaveChangesAsync(object state) /// The options. /// The state. /// A function delegate that returns the future result to be available through the Task. - public Task SaveChangesAsync(SaveChangesOptions options, object state) - { - return Task.Factory.FromAsync(_dataContext.BeginSaveChanges, _dataContext.EndSaveChanges, options, state) - .ContinueWith(t => WrapTask(t)); - } - + public Task SaveChangesAsync(SaveChangesOptions options, object state) + { + Task task = Task.Factory.FromAsync( + _dataContext.BeginSaveChanges, + _dataContext.EndSaveChanges, + options, + state); + return task.ContinueWith(t => WrapTask(t),TaskContinuationOptions.AttachedToParent); + } + + public Task SaveChangesAsync(SaveChangesOptions options, object state, CancellationToken token) + { + + Task task = Task.Factory.FromAsync( + _dataContext.BeginSaveChanges, + _dataContext.EndSaveChanges, + options, + state).HandleCancellation(token); + + return task.ContinueWith(t => WrapTask(t)); + } + private IMediaDataServiceResponse WrapTask(Task task) { task.ThrowIfFaulted(); diff --git a/src/net/Client/MediaQueryProvider.cs b/src/net/Client/MediaQueryProvider.cs index 3273d962..2cc5ecf7 100644 --- a/src/net/Client/MediaQueryProvider.cs +++ b/src/net/Client/MediaQueryProvider.cs @@ -40,7 +40,7 @@ public IQueryable CreateQuery(Expression expression) } var result = (IQueryable)_inner.CreateQuery(expression).Cast(); - return (IQueryable)new MediaQueryable(result); + return (IQueryable)new MediaQueryable(result, _queryRetryPolicy); } public IQueryable CreateQuery(Expression expression) diff --git a/src/net/Client/MediaServicesClassFactory.cs b/src/net/Client/MediaServicesClassFactory.cs index 17e6e247..f2949888 100644 --- a/src/net/Client/MediaServicesClassFactory.cs +++ b/src/net/Client/MediaServicesClassFactory.cs @@ -17,16 +17,31 @@ using System; using Microsoft.WindowsAzure.MediaServices.Client.RequestAdapters; using Microsoft.WindowsAzure.MediaServices.Client.TransientFaultHandling; +using System.Collections.Generic; namespace Microsoft.WindowsAzure.MediaServices.Client { public abstract class MediaServicesClassFactory { - /// - /// Creates a data service context. - /// - /// The new DataServiceContext instance. + /// + /// Creates instance of .Deafault list of applied . + /// + /// The new instance. public abstract IMediaDataServiceContext CreateDataServiceContext(); + + /// + /// Creates instance of with contains additional applyed adapters + /// + /// list of which will be applied additionally on top of default adapters + /// + public abstract IMediaDataServiceContext CreateDataServiceContext(IEnumerable adapters); + + /// + /// Returns list of which applied by default for each request + /// + /// + public abstract IEnumerable GetDefaultDataContextAdapters(); + /// /// Creates a ClientRequestIdAdapter /// diff --git a/src/net/Client/OAuth/OAuthDataServiceAdapter.cs b/src/net/Client/OAuth/OAuthDataServiceAdapter.cs index 2780b790..be94e3eb 100644 --- a/src/net/Client/OAuth/OAuthDataServiceAdapter.cs +++ b/src/net/Client/OAuth/OAuthDataServiceAdapter.cs @@ -20,15 +20,16 @@ using System.Globalization; using System.Net; using System.Net.Security; -using System.Security.Cryptography.X509Certificates; - +using System.Security.Cryptography.X509Certificates; +using Microsoft.WindowsAzure.MediaServices.Client.RequestAdapters; + namespace Microsoft.WindowsAzure.MediaServices.Client.OAuth { /// /// An OAuth adapter for a data service. - /// - public class OAuthDataServiceAdapter + /// + public class OAuthDataServiceAdapter : IDataServiceContextAdapter { private const string AuthorizationHeader = "Authorization"; private const string BearerTokenFormat = "Bearer {0}"; @@ -52,10 +53,6 @@ public OAuthDataServiceAdapter(MediaServicesCredentials credentials, string trus this._credentials = credentials; this._trustedRestCertificateHash = trustedRestCertificateHash; this._trustedRestSubject = trustedRestSubject; - - #if DEBUG - ServicePointManager.ServerCertificateValidationCallback = this.ValidateCertificate; - #endif } /// @@ -95,27 +92,6 @@ private void RefreshToken() this._credentials.RefreshToken(); } } - } - - private bool ValidateCertificate(object s, X509Certificate cert, X509Chain chain, SslPolicyErrors error) - { - if (error.HasFlag(SslPolicyErrors.RemoteCertificateNameMismatch) || error.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors)) - { - - // This is for local deployments. DevFabric generates its own certificate for load-balancing / port forwarding. - const string AzureDevFabricCertificateSubject = "CN=127.0.0.1, O=TESTING ONLY, OU=Windows Azure DevFabric"; - if (cert.Subject == AzureDevFabricCertificateSubject) - { - return true; - } - var cert2 = new X509Certificate2(cert); - if (this._trustedRestSubject == cert2.Subject && cert2.Thumbprint == this._trustedRestCertificateHash) - { - return true; - } - } - - return error == SslPolicyErrors.None; } /// diff --git a/src/net/Client/Properties/AssemblyInfo.cs b/src/net/Client/Properties/AssemblyInfo.cs index 66054ab7..070b8773 100644 --- a/src/net/Client/Properties/AssemblyInfo.cs +++ b/src/net/Client/Properties/AssemblyInfo.cs @@ -48,14 +48,14 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("3.3.0.0")] -[assembly: AssemblyFileVersion("3.3.0.0")] +[assembly: AssemblyVersion("3.4.0.0")] +[assembly: AssemblyFileVersion("3.4.0.0")] [assembly: NeutralResourcesLanguage("en-US")] //For delay signing specify PublicKey for each friendly assembly -//[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Scenario, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -//[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Unit, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -//[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Common, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] + [assembly: InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Scenario")] [assembly: InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Unit")] [assembly: InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Common")] +//Expose internals to test dll +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] diff --git a/src/net/Client/Properties/DelaySign_AssemblyInfo.cs b/src/net/Client/Properties/DelaySign_AssemblyInfo.cs index be17e8d6..2a13b6d2 100644 --- a/src/net/Client/Properties/DelaySign_AssemblyInfo.cs +++ b/src/net/Client/Properties/DelaySign_AssemblyInfo.cs @@ -16,6 +16,7 @@ using System.Reflection; using System.Resources; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -47,11 +48,13 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("3.3.0.0")] -[assembly: AssemblyFileVersion("3.3.0.0")] +[assembly: AssemblyVersion("3.4.0.0")] +[assembly: AssemblyFileVersion("3.4.0.0")] [assembly: NeutralResourcesLanguage("en-US")] //For delay signing specify PublicKey for each friendly assembly -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Scenario, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Unit, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Common, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Scenario, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Unit, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("Microsoft.WindowsAzure.MediaServices.Client.Tests.Common, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +//Expose internals to test dll +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] \ No newline at end of file diff --git a/src/net/Client/RequestAdapters/AssetDeleteOptionsRequestAdapter.cs b/src/net/Client/RequestAdapters/AssetDeleteOptionsRequestAdapter.cs new file mode 100644 index 00000000..63dc05a2 --- /dev/null +++ b/src/net/Client/RequestAdapters/AssetDeleteOptionsRequestAdapter.cs @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------- +// Copyright 2012 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + + + +using System; +using System.Data.Services.Client; +using Microsoft.WindowsAzure.Storage.Blob.Protocol; + +namespace Microsoft.WindowsAzure.MediaServices.Client.RequestAdapters +{ + /// + /// Modifies request to add Asset delete options parameters + /// + public class AssetDeleteOptionsRequestAdapter: IDataServiceContextAdapter + { + private readonly bool _keepAzureStorageContainer; + + /// + /// Initilizes a new instance of + /// + /// Determines whether or not the underlying storage asset container is preseved during the delete operation + public AssetDeleteOptionsRequestAdapter(bool keepAzureStorageContainer) + { + _keepAzureStorageContainer = keepAzureStorageContainer; + } + + /// + /// Adapting context to include additional url parameters ot http headers + /// + /// + public void Adapt(DataServiceContext context) + { + if (context == null) { throw new ArgumentNullException("context"); } + context.BuildingRequest += this.AddAssetDeleteUriParameter; + } + + private void AddAssetDeleteUriParameter(object sender, BuildingRequestEventArgs e) + { + UriBuilder builder = new UriBuilder(e.RequestUri); + var namevalue = "keepcontainer=" + _keepAzureStorageContainer.ToString().ToLower(); + builder.Query = String.IsNullOrEmpty(e.RequestUri.Query) ? namevalue :"&" + namevalue; + e.RequestUri = builder.Uri; + } + } +} \ No newline at end of file diff --git a/src/net/Client/RequestAdapters/IDataServiceContextAdapter.cs b/src/net/Client/RequestAdapters/IDataServiceContextAdapter.cs index f9e40585..0efe0aeb 100644 --- a/src/net/Client/RequestAdapters/IDataServiceContextAdapter.cs +++ b/src/net/Client/RequestAdapters/IDataServiceContextAdapter.cs @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + using System.Data.Services.Client; namespace Microsoft.WindowsAzure.MediaServices.Client.RequestAdapters diff --git a/src/net/Client/RequestAdapters/IWebRequestAdapter.cs b/src/net/Client/RequestAdapters/IWebRequestAdapter.cs index 9c42281b..bb91de21 100644 --- a/src/net/Client/RequestAdapters/IWebRequestAdapter.cs +++ b/src/net/Client/RequestAdapters/IWebRequestAdapter.cs @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + using System.Net; namespace Microsoft.WindowsAzure.MediaServices.Client.RequestAdapters diff --git a/src/net/Client/SDK.Client.csproj b/src/net/Client/SDK.Client.csproj index 0b7a3422..414eeb7c 100644 --- a/src/net/Client/SDK.Client.csproj +++ b/src/net/Client/SDK.Client.csproj @@ -172,7 +172,9 @@ + + @@ -223,6 +225,8 @@ + + @@ -276,8 +280,27 @@ Code + + + + + + + + + + + + + + + + + + + @@ -335,6 +358,7 @@ + diff --git a/src/net/Client/StringTable.Designer.cs b/src/net/Client/StringTable.Designer.cs index 1c50a6a6..86a7773a 100644 --- a/src/net/Client/StringTable.Designer.cs +++ b/src/net/Client/StringTable.Designer.cs @@ -1,17 +1,22 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.34209 +// Runtime Version:4.0.30319.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ +using System.CodeDom.Compiler; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Resources; +using System.Runtime.CompilerServices; + namespace Microsoft.WindowsAzure.MediaServices.Client { - using System; - - /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,27 +24,27 @@ namespace Microsoft.WindowsAzure.MediaServices.Client { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [DebuggerNonUserCode()] + [CompilerGenerated()] internal class StringTable { - private static global::System.Resources.ResourceManager resourceMan; + private static ResourceManager resourceMan; - private static global::System.Globalization.CultureInfo resourceCulture; + private static CultureInfo resourceCulture; - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal StringTable() { } /// /// Returns the cached ResourceManager instance used by this class. /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { + [EditorBrowsable(EditorBrowsableState.Advanced)] + internal static ResourceManager ResourceManager { get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.WindowsAzure.MediaServices.Client.StringTable", typeof(StringTable).Assembly); + if (ReferenceEquals(resourceMan, null)) { + ResourceManager temp = new ResourceManager("Microsoft.WindowsAzure.MediaServices.Client.StringTable", typeof(StringTable).Assembly); resourceMan = temp; } return resourceMan; @@ -50,8 +55,8 @@ internal StringTable() { /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { + [EditorBrowsable(EditorBrowsableState.Advanced)] + internal static CultureInfo Culture { get { return resourceCulture; } @@ -654,6 +659,24 @@ internal static string NotSupportedFiles { } } + /// + /// Looks up a localized string similar to Data for Fragmented AssetFiles cannot be downloaded.. + /// + internal static string NotSupportedFragblobDownload { + get { + return ResourceManager.GetString("NotSupportedFragblobDownload", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Data for Fragmented AssetFiles cannot be uploaded.. + /// + internal static string NotSupportedFragblobUpload { + get { + return ResourceManager.GetString("NotSupportedFragblobUpload", resourceCulture); + } + } + /// /// Looks up a localized string similar to Can't convert JsonWebKey to TokenVerificationKey. /// diff --git a/src/net/Client/StringTable.resx b/src/net/Client/StringTable.resx index 9023620a..dc91f0ce 100644 --- a/src/net/Client/StringTable.resx +++ b/src/net/Client/StringTable.resx @@ -343,5 +343,10 @@ String representation of {0} is not valid absolute Uri. - + + Data for Fragmented AssetFiles cannot be downloaded. + + + Data for Fragmented AssetFiles cannot be uploaded. + \ No newline at end of file diff --git a/src/net/Client/TaskExtensions.cs b/src/net/Client/TaskExtensions.cs index b11e45fe..f30cb694 100644 --- a/src/net/Client/TaskExtensions.cs +++ b/src/net/Client/TaskExtensions.cs @@ -76,5 +76,34 @@ public static void ThrowIfCancellationRequested(this CancellationToken cancellat cancellationToken.ThrowIfCancellationRequested(); } } + + public async static Task HandleCancellation( + this Task asyncTask, + CancellationToken cancellationToken) + { + + var tcs = new TaskCompletionSource(); + cancellationToken.Register(() => + tcs.TrySetCanceled(), useSynchronizationContext: false); + var cancellationTask = tcs.Task; + + // Create a task that completes when either the async operation completes, + // or cancellation is requested. + var readyTask = await Task.WhenAny(asyncTask, cancellationTask); + + // In case of cancellation, register a continuation to observe any unhandled + // exceptions from the asynchronous operation (once it completes). + // In .NET 4.0, unobserved task exceptions would terminate the process. + if (readyTask == cancellationTask) + #pragma warning disable 4014 + asyncTask.ContinueWith(_ => asyncTask.Exception, + #pragma warning restore 4014 + TaskContinuationOptions.OnlyOnFaulted | + TaskContinuationOptions.ExecuteSynchronously); + + return await readyTask; + } + + } } diff --git a/test/net/Scenario/AssetFilesTests.cs b/test/net/Scenario/AssetFilesTests.cs index b5d3886b..93fc8c5f 100644 --- a/test/net/Scenario/AssetFilesTests.cs +++ b/test/net/Scenario/AssetFilesTests.cs @@ -245,7 +245,7 @@ public void ShouldDownloadToFileFromStorageEncryptedAsset() } [TestMethod] - [Timeout(60000)] + [Timeout(600000)] [DeploymentItem(@"Media\SmallWmv.wmv", "Media")] public void ShouldDownloadManyConcurrentSmallFiles() { @@ -256,8 +256,7 @@ public void ShouldDownloadManyConcurrentSmallFiles() Assert.AreEqual(assetFile.Asset.Id, asset.Id); Assert.AreEqual(1, asset.Locators.Count); - VerifyAndDownloadAssetFileNTimes(assetFile, asset,10,0,true); - + VerifyAndDownloadAssetFileNTimes(assetFile, asset,100,0,true); } [TestMethod] [Timeout(60000)] diff --git a/test/net/Scenario/AssetFilterTests.cs b/test/net/Scenario/AssetFilterTests.cs new file mode 100644 index 00000000..5e685152 --- /dev/null +++ b/test/net/Scenario/AssetFilterTests.cs @@ -0,0 +1,247 @@ +//----------------------------------------------------------------------- +// Copyright 2012 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Data.Services.Client; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.WindowsAzure.MediaServices.Client.Tests.Common; +using Microsoft.WindowsAzure.Storage; + +namespace Microsoft.WindowsAzure.MediaServices.Client.Tests +{ + [TestClass] + public class AssetFilterTests + { + private CloudMediaContext _mediaContext; + private string _smallWmv; + + public TestContext TestContext { get; set; } + + [TestInitialize] + public void SetupTest() + { + _mediaContext = WindowsAzureMediaServicesTestConfiguration.CreateCloudMediaContext(); + _smallWmv = WindowsAzureMediaServicesTestConfiguration.GetVideoSampleFilePath(TestContext, WindowsAzureMediaServicesTestConfiguration.SmallWmv); + + } + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [TestCategory("Bvt")] + public void CreateUpdateDeleteFilterWithDefaultPresentationTimeRangeAndEmptyFilterTrackSelectStatement() + { + string filterName = "CreateUpdateDeleteFilter_" + Guid.NewGuid().ToString(); + IStreamingFilter filter = _mediaContext.Filters.Create(filterName, new PresentationTimeRange(), new List()); + Assert.IsNotNull(filter); + Assert.AreEqual(1, _mediaContext.Filters.Where(c => c.Name == filter.Name).Count()); + Assert.AreNotEqual(0, _mediaContext.Filters.Count()); + filter.PresentationTimeRange = new PresentationTimeRange(timescale:500); + filter.Update(); + Assert.IsNotNull(_mediaContext.Filters.Where(c => c.Name == filter.Name).FirstOrDefault()); + filter.Delete(); + Assert.IsNull(_mediaContext.Filters.Where(c=>c.Name == filter.Name).FirstOrDefault()); + + + } + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [TestCategory("Bvt")] + public void CreateUpdateDeleteFilterWithAllSelectStatements() + { + string filterName = "CreateUpdateDeleteFilter_" + Guid.NewGuid().ToString(); + List filterTrackSelectStatements = new List(); + FilterTrackSelectStatement filterTrackSelectStatement = new FilterTrackSelectStatement(); + filterTrackSelectStatement.PropertyConditions = new List(); + filterTrackSelectStatement.PropertyConditions.Add(new FilterTrackNameCondition("Track Name",FilterTrackCompareOperator.NotEqual)); + filterTrackSelectStatement.PropertyConditions.Add(new FilterTrackFourCCCondition("AACL", FilterTrackCompareOperator.NotEqual)); + filterTrackSelectStatement.PropertyConditions.Add(new FilterTrackBitrateRangeCondition(new FilterTrackBitrateRange(0,1), FilterTrackCompareOperator.NotEqual)); + filterTrackSelectStatement.PropertyConditions.Add(new FilterTrackLanguageCondition("ru", FilterTrackCompareOperator.NotEqual)); + filterTrackSelectStatement.PropertyConditions.Add(new FilterTrackTypeCondition(FilterTrackType.Text, FilterTrackCompareOperator.NotEqual)); + filterTrackSelectStatements.Add(filterTrackSelectStatement); + IStreamingFilter filter = _mediaContext.Filters.Create(filterName, new PresentationTimeRange(), filterTrackSelectStatements); + Assert.IsNotNull(filter); + Assert.AreEqual(1, _mediaContext.Filters.Where(c => c.Name == filter.Name).Count()); + Assert.AreNotEqual(0, _mediaContext.Filters.Count()); + Assert.AreEqual(5, _mediaContext.Filters.Where(c => c.Name == filter.Name).First().Tracks.First().PropertyConditions.Count); + filter.PresentationTimeRange = new PresentationTimeRange(timescale: 500); + filter.Update(); + Assert.AreEqual(5, _mediaContext.Filters.Where(c => c.Name == filter.Name).First().Tracks.First().PropertyConditions.Count); + Assert.IsNotNull(_mediaContext.Filters.Where(c => c.Name == filter.Name).FirstOrDefault()); + filter.Delete(); + Assert.IsNull(_mediaContext.Filters.Where(c => c.Name == filter.Name).FirstOrDefault()); + + + } + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [ExpectedException(typeof(DataServiceRequestException))] + public void InvalidFilterTrackLanguageConditionShouldThrow() + { + string filterName = "InvalidFilterTrackLanguageConditionShouldThrow_" + Guid.NewGuid().ToString(); + List filterTrackSelectStatements = new List(); + FilterTrackSelectStatement filterTrackSelectStatement = new FilterTrackSelectStatement(); + filterTrackSelectStatement.PropertyConditions = new List(); + filterTrackSelectStatement.PropertyConditions.Add(new FilterTrackLanguageCondition("expecting language validation here", FilterTrackCompareOperator.NotEqual)); + filterTrackSelectStatements.Add(filterTrackSelectStatement); + IStreamingFilter filter = _mediaContext.Filters.Create(filterName, new PresentationTimeRange(), filterTrackSelectStatements); + + } + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [ExpectedException(typeof(DataServiceRequestException))] + public void InvalidFourCCConditionShouldThrow() + { + string filterName = "InvalidFourCCConditionShouldThrow_" + Guid.NewGuid().ToString(); + List filterTrackSelectStatements = new List(); + FilterTrackSelectStatement filterTrackSelectStatement = new FilterTrackSelectStatement(); + filterTrackSelectStatement.PropertyConditions = new List(); + filterTrackSelectStatement.PropertyConditions.Add(new FilterTrackFourCCCondition("FourCCCondition validation here", FilterTrackCompareOperator.NotEqual)); + filterTrackSelectStatements.Add(filterTrackSelectStatement); + IStreamingFilter filter = _mediaContext.Filters.Create(filterName, new PresentationTimeRange(), filterTrackSelectStatements); + + } + + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [TestCategory("Bvt")] + public void CRUDAssetFilter() + { + string assetName = "CRUDAssetFilter_" + Guid.NewGuid().ToString(); + var asset = _mediaContext.Assets.Create(assetName,AssetCreationOptions.None); + Assert.IsNotNull(asset); + + string filterName = "CRUDAssetFilter_" + Guid.NewGuid().ToString(); + IStreamingAssetFilter filter = asset.AssetFilters.Create(filterName, new PresentationTimeRange(), new List()); + Assert.IsNotNull(filter); + Assert.IsNotNull(filter.ParentAssetId); + Assert.AreEqual(asset.Id,filter.ParentAssetId); + + asset = _mediaContext.Assets.Where(c => c.Id == asset.Id).FirstOrDefault(); + Assert.IsNotNull(asset); + Assert.AreEqual(1, asset.AssetFilters.Count()); + + //Why we are througing internal server exception here + filter.PresentationTimeRange = new PresentationTimeRange(timescale: 500); + filter.Update(); + + asset = _mediaContext.Assets.Where(c => c.Id == asset.Id).FirstOrDefault(); + Assert.IsNotNull(asset); + var filterUpdated = asset.AssetFilters.FirstOrDefault(); + Assert.IsNotNull(filterUpdated); + + Assert.AreEqual(filter.PresentationTimeRange.Timescale, filterUpdated.PresentationTimeRange.Timescale); + Assert.AreEqual((ulong)500, filterUpdated.PresentationTimeRange.Timescale); + + + //We don't have acess to asset filters here + var globalFilter = _mediaContext.Filters.Where(c => c.Name == filterName).FirstOrDefault(); + Assert.IsNull(globalFilter); + + //Why we are failing here + filter.Delete(); + + asset = _mediaContext.Assets.Where(c => c.Id == asset.Id).FirstOrDefault(); + Assert.IsNotNull(asset); + Assert.AreEqual(0,asset.AssetFilters.Count()); + + globalFilter = _mediaContext.Filters.Where(c => c.Name == filterName).FirstOrDefault(); + Assert.IsNull(globalFilter); + + } + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [DeploymentItem(@"Configuration\MP4 to Smooth Streams.xml", "Configuration")] + [DeploymentItem(@"Media\SmallMP41.mp4", "Media")] + public void ApplyDynamicManifestFilter() + { + const string typeAudio = "Type=\"audio\""; + const string typeVideo = "Type=\"video\""; + + string configuration = File.ReadAllText(WindowsAzureMediaServicesTestConfiguration.DefaultMp4ToSmoothConfig); + IAsset inputAsset = AssetTests.CreateAsset(_mediaContext, WindowsAzureMediaServicesTestConfiguration.SmallMp41, AssetCreationOptions.None); + IMediaProcessor mediaProcessor = JobTests.GetMediaProcessor(_mediaContext, WindowsAzureMediaServicesTestConfiguration.MpPackagerName); + IJob job = JobTests.CreateAndSubmitOneTaskJob(_mediaContext, "ApplyDynamicManifestFilter" + Guid.NewGuid().ToString().Substring(0, 5), mediaProcessor, configuration, inputAsset, TaskOptions.None); + JobTests.WaitForJob(job.Id, JobState.Finished, JobTests.VerifyAllTasksFinished); + + + var outputAsset = job.OutputMediaAssets.FirstOrDefault(); + outputAsset = _mediaContext.Assets.Where(c => c.Id == outputAsset.Id).FirstOrDefault(); + var assetFile = outputAsset.AssetFiles.Where(c => c.Name.EndsWith(".ism")).First(); + + IAccessPolicy policy = _mediaContext.AccessPolicies.Create("ApplyDynamicManifestFilter" + Guid.NewGuid().ToString().Substring(0, 5), TimeSpan.FromDays(30), AccessPermissions.Read); + ILocator originLocator = _mediaContext.Locators.CreateLocator(LocatorType.OnDemandOrigin, outputAsset, policy, DateTime.UtcNow.AddMinutes(-5)); + + string urlForClientStreaming = originLocator.Path + assetFile.Name + "/manifest"; + HttpClient client = new HttpClient(); + var message = client.GetAsync(urlForClientStreaming).Result; + var content = message.Content; + var result = content.ReadAsStringAsync().Result; + Assert.AreEqual(message.StatusCode,HttpStatusCode.OK); + Assert.IsTrue(result.Length >0); + + Assert.IsTrue(result.Contains(typeAudio)); + + Assert.IsTrue(result.Contains(typeVideo)); + + var manifestLength = result.Length; + + // string filterName = "ApplyDynamicManifestFilter_" + DateTime.Now; + string filterName = "ApplyDynamicManifestFilter_" + Guid.NewGuid().ToString().Substring(0,5); + List filterTrackSelectStatements = new List(); + FilterTrackSelectStatement filterTrackSelectStatement = new FilterTrackSelectStatement(); + filterTrackSelectStatement.PropertyConditions = new List(); + filterTrackSelectStatement.PropertyConditions.Add(new FilterTrackTypeCondition(FilterTrackType.Video, FilterTrackCompareOperator.NotEqual)); + filterTrackSelectStatements.Add(filterTrackSelectStatement); + IStreamingFilter filter = _mediaContext.Filters.Create(filterName, new PresentationTimeRange(), filterTrackSelectStatements); + Assert.IsNotNull(filter); + + + var filterUrlForClientStreaming = originLocator.Path + assetFile.Name + String.Format("/manifest(filter={0})",filterName); + HttpClient filterclient = new HttpClient(); + var filtermessage = filterclient.GetAsync(filterUrlForClientStreaming).Result; + Assert.AreEqual(filtermessage.StatusCode, HttpStatusCode.OK); + var filtercontent = filtermessage.Content; + var filterresult = filtercontent.ReadAsStringAsync().Result; + Assert.IsTrue(filterresult.Length > 0); + Assert.AreNotEqual(manifestLength, filterresult); + Assert.IsTrue(filterresult.Contains(typeAudio)); + Assert.IsFalse(filterresult.Contains(typeVideo)); + + outputAsset.DeleteAsync(); + inputAsset.DeleteAsync(); + job.DeleteAsync(); + filter.DeleteAsync(); + } + + } +} \ No newline at end of file diff --git a/test/net/Scenario/AssetTests.cs b/test/net/Scenario/AssetTests.cs index c496b103..5ded35f3 100644 --- a/test/net/Scenario/AssetTests.cs +++ b/test/net/Scenario/AssetTests.cs @@ -751,6 +751,36 @@ public void ShouldDeleteAsset() Assert.IsNull(newContext.Assets.Where(a => a.Id == asset.Id).SingleOrDefault()); } + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [DeploymentItem(@"Media\SmallWmv.wmv", "Media")] + public void ShouldDeleteAssetAndKeepAzureContainer() + { + IAsset asset = CreateAsset(_mediaContext, _smallWmv, AssetCreationOptions.None); + Assert.AreEqual(AssetState.Initialized, asset.State); + foreach (ILocator locator in asset.Locators) + { + locator.Delete(); + } + + var result = asset.DeleteAsync(true).Result; + + Assert.IsNull(_mediaContext.Assets.Where(a => a.Id == asset.Id).SingleOrDefault()); + + CloudMediaContext newContext = WindowsAzureMediaServicesTestConfiguration.CreateCloudMediaContext(); + + Assert.IsNull(newContext.Assets.Where(a => a.Id == asset.Id).SingleOrDefault()); + + CloudStorageAccount storageAccount = CloudStorageAccount.Parse(WindowsAzureMediaServicesTestConfiguration.ClientStorageConnectionString); + string containername = asset.Id.Replace("nb:cid:UUID:", "asset-"); + var client = storageAccount.CreateCloudBlobClient(); + var container = client.GetContainerReference(containername); + Assert.IsTrue(container.Exists(), "Asset container {0} can't be found", container); + + + } + [TestMethod] [TestCategory("ClientSDK")] [Owner("ClientSDK")] diff --git a/test/net/Scenario/JobTests.cs b/test/net/Scenario/JobTests.cs index 13a8b716..f0e42117 100644 --- a/test/net/Scenario/JobTests.cs +++ b/test/net/Scenario/JobTests.cs @@ -106,6 +106,39 @@ public void ShouldCreateJobPreset() Task task = job.GetExecutionProgressTask(CancellationToken.None); task.Wait(); Assert.AreEqual(JobState.Finished, job.State); + } + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [DeploymentItem(@"Media\SmallWmv.wmv", "Media")] + [TestCategory("Bvt")] + public void CreateJobFromTemplate() + { + IAsset asset = AssetTests.CreateAsset(_mediaContext, _smallWmv, AssetCreationOptions.StorageEncrypted); + IMediaProcessor mediaProcessor = GetMediaProcessor(_mediaContext, WindowsAzureMediaServicesTestConfiguration.MpEncoderName); + string name = GenerateName("Job For Template"); + + IJob job = _mediaContext.Jobs.Create(name); + job.Priority = InitialJobPriority; + ITask task = job.Tasks.AddNew("Task1", mediaProcessor, GetWamePreset(mediaProcessor), TaskOptions.None); + task.InputAssets.Add(asset); + task.OutputAssets.AddNew("JobTemplateOutPutAsset", AssetCreationOptions.None); + + DateTime timebeforeSubmit = DateTime.UtcNow; + job.Submit(); + Task jobRunningTask = job.GetExecutionProgressTask(CancellationToken.None); + jobRunningTask.Wait(); + IJobTemplate template = job.SaveAsTemplate("JobTemplate" + Guid.NewGuid().ToString().Substring(0, 10)); + var jobfromTemplate = _mediaContext.Jobs.Create("JobFromTemplate" + Guid.NewGuid().ToString().Substring(0, 10), template, new[]{asset}); + jobfromTemplate.Submit(); + jobRunningTask = jobfromTemplate.GetExecutionProgressTask(CancellationToken.None); + jobRunningTask.Wait(); + + var refreshed = _mediaContext.Jobs.Where(c => c.Id == jobfromTemplate.Id).FirstOrDefault(); + Assert.IsNull(refreshed); + + } [TestMethod] @@ -199,6 +232,11 @@ public void ShouldFinishJobWithSuccessWhenPresetISUTF8() string name = GenerateName("ShouldFinishJobWithSuccessWhenPresetISUTF8"); IJob job = CreateAndSubmitOneTaskJob(_mediaContext, name, mediaProcessor, presetXml, asset, TaskOptions.None); WaitForJob(job.Id, JobState.Finished, VerifyAllTasksFinished); + var task = job.Tasks.First(); + var assets = task.OutputAssets.ToList(); + var firstasset = assets.First(); + var files = firstasset.AssetFiles.ToList(); + Assert.IsNull(files); } [TestMethod] @@ -473,7 +511,8 @@ public void ShouldSubmitAndFihishMp4ToSmoothJob() IAsset asset = AssetTests.CreateAsset(_mediaContext, WindowsAzureMediaServicesTestConfiguration.SmallMp41, AssetCreationOptions.StorageEncrypted); IMediaProcessor mediaProcessor = GetMediaProcessor(_mediaContext, WindowsAzureMediaServicesTestConfiguration.MpPackagerName); IJob job = CreateAndSubmitOneTaskJob(_mediaContext, GenerateName("ShouldSubmitAndFihishMp4ToSmoothJob"), mediaProcessor, configuration, asset, TaskOptions.None); - WaitForJob(job.Id, JobState.Finished, VerifyAllTasksFinished); + WaitForJob(job.Id, JobState.Finished, VerifyAllTasksFinished); + } [TestMethod] diff --git a/test/net/Scenario/Live/ChannelTests.cs b/test/net/Scenario/Live/ChannelTests.cs index a1eee35f..cab11fdc 100644 --- a/test/net/Scenario/Live/ChannelTests.cs +++ b/test/net/Scenario/Live/ChannelTests.cs @@ -33,6 +33,16 @@ public void SetupTest() _mediaContext = WindowsAzureMediaServicesTestConfiguration.CreateCloudMediaContext(); } + + [TestMethod] + public void SimpleChannelQueries() + { + var i = _mediaContext.Assets.Count(); + var channel = _mediaContext.Channels.Where(c => c.Name == Guid.NewGuid().ToString()).FirstOrDefault(); + var programs = _mediaContext.Programs.Where(c => c.Name == Guid.NewGuid().ToString()).FirstOrDefault(); + + } + [TestMethod] [TestCategory("ClientSDK")] [Owner("ClientSDK")] diff --git a/test/net/Scenario/Live/LiveTranscodingTest.cs b/test/net/Scenario/Live/LiveTranscodingTest.cs index d54789d5..30931c9c 100644 --- a/test/net/Scenario/Live/LiveTranscodingTest.cs +++ b/test/net/Scenario/Live/LiveTranscodingTest.cs @@ -17,6 +17,7 @@ using System; using System.Collections.Generic; using System.Net; +using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.WindowsAzure.MediaServices.Client.Tests.Common; @@ -46,7 +47,7 @@ public void LiveEncodingChannelShowSlateTest() Preview = MakeChannelPreview(), Output = MakeChannelOutput(), EncodingType = ChannelEncodingType.Standard, - Encoding = MakeChannelEncoding() + Encoding = MakeChannelEncoding(ChannelEncodingType.Standard) }); try @@ -120,6 +121,38 @@ public void TestStreamSelectionPersistence() channel.Delete(); } + [TestMethod] + [Priority(1)] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + public void LivePremiumEncodingChannelTest() + { + IChannel channel = _mediaContext.Channels.Create( + new ChannelCreationOptions + { + Name = Guid.NewGuid().ToString().Substring(0, 30), + Input = MakeChannelInput(), + Preview = MakeChannelPreview(), + Output = MakeChannelOutput(), + EncodingType = ChannelEncodingType.Premium, + Encoding = MakeChannelEncoding(ChannelEncodingType.Premium) + }); + + try + { + channel.Start(); + + Thread.Sleep(1000 * 10); + + channel.Stop(); + } + finally + { + channel.Delete(); + } + } + + static ChannelInput MakeChannelInput() { return new ChannelInput @@ -168,11 +201,11 @@ static ChannelOutput MakeChannelOutput() }; } - static ChannelEncoding MakeChannelEncoding() + static ChannelEncoding MakeChannelEncoding(ChannelEncodingType encodingType) { return new ChannelEncoding { - SystemPreset = "Default720p", + SystemPreset = encodingType == ChannelEncodingType.Standard ? "Default720p" : "Default1080p", IgnoreCea708ClosedCaptions = false, AdMarkerSource = AdMarkerSource.Api, AudioStreams = new List {new AudioStream {Index = 103, Language = "eng"}}.AsReadOnly() diff --git a/test/net/Scenario/Properties/AssemblyInfo.cs b/test/net/Scenario/Properties/AssemblyInfo.cs index 133e1135..2743e093 100644 --- a/test/net/Scenario/Properties/AssemblyInfo.cs +++ b/test/net/Scenario/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("3.3.0.0")] -[assembly: AssemblyFileVersion("3.3.0.0")] +[assembly: AssemblyVersion("3.4.0.0")] +[assembly: AssemblyFileVersion("3.4.0.0")] diff --git a/test/net/Scenario/SDK.Client.Tests.Scenario.csproj b/test/net/Scenario/SDK.Client.Tests.Scenario.csproj index 87bf35d9..8a788ea4 100644 --- a/test/net/Scenario/SDK.Client.Tests.Scenario.csproj +++ b/test/net/Scenario/SDK.Client.Tests.Scenario.csproj @@ -147,6 +147,7 @@ + diff --git a/test/net/common/TestMediaServicesClassFactory.cs b/test/net/common/TestMediaServicesClassFactory.cs index 745121f6..898280b8 100644 --- a/test/net/common/TestMediaServicesClassFactory.cs +++ b/test/net/common/TestMediaServicesClassFactory.cs @@ -23,6 +23,8 @@ using Moq; using System.Net; using Microsoft.WindowsAzure.Storage.RetryPolicies; +using System.Collections.Generic; +using Microsoft.WindowsAzure.MediaServices.Client.RequestAdapters; namespace Microsoft.WindowsAzure.MediaServices.Client.Tests.Common { @@ -38,6 +40,11 @@ public override IMediaDataServiceContext CreateDataServiceContext() return _dataContext; } + public override IMediaDataServiceContext CreateDataServiceContext(IEnumerable adapters) + { + return _dataContext; + } + public override MediaRetryPolicy GetQueryRetryPolicy(IRetryPolicyAdapter adapter) { return base.GetQueryRetryPolicy(adapter); diff --git a/test/net/unit/AssetDeleteOptionsRequestAdapterTests.cs b/test/net/unit/AssetDeleteOptionsRequestAdapterTests.cs new file mode 100644 index 00000000..fff628da --- /dev/null +++ b/test/net/unit/AssetDeleteOptionsRequestAdapterTests.cs @@ -0,0 +1,58 @@ +using System; +using System.Data.Services.Client; +using System.Diagnostics; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.WindowsAzure.MediaServices.Client.RequestAdapters; + +namespace Microsoft.WindowsAzure.MediaServices.Client.Tests.Unit +{ + [TestClass] + public class AssetDeleteOptionsRequestAdapterTests + { + [TestMethod] + public void CheckParameterSetToTrue() + { + AssetDeleteOptionsRequestAdapter adapter = new AssetDeleteOptionsRequestAdapter(true); + Uri uri = ExecuteAssetDeleteRequest(adapter); + Assert.IsNotNull(uri); + Assert.IsTrue(uri.Query.Contains("keepcontainer=true")); + + } + + [TestMethod] + public void CheckParameterSetToFalse() + { + AssetDeleteOptionsRequestAdapter adapter = new AssetDeleteOptionsRequestAdapter(false); + Uri uri = ExecuteAssetDeleteRequest(adapter); + Assert.IsNotNull(uri); + Assert.IsTrue(uri.Query.Contains("keepcontainer=false")); + + } + + private static Uri ExecuteAssetDeleteRequest( AssetDeleteOptionsRequestAdapter adapter) + { + Uri uri = null; + var context = new DataServiceContext(new Uri("http://127.0.0.1/" + Guid.NewGuid().ToString())); + bool sendingRequestCalled = false; + context.SendingRequest2 += delegate(object o, SendingRequest2EventArgs args) + { + sendingRequestCalled = true; + uri = args.RequestMessage.Url; + }; + try + { + AssetData asset = new AssetData() {Id = Guid.NewGuid().ToString()}; + context.AttachTo("Assets", asset); + context.DeleteObject(asset); + adapter.Adapt(context); + context.SaveChanges(); + } + catch (DataServiceRequestException ex) + { + Debug.WriteLine(ex.Message); + } + Assert.IsTrue(sendingRequestCalled); + return uri; + } + } +} \ No newline at end of file diff --git a/test/net/unit/AssetDeliveryPolicyTests.cs b/test/net/unit/AssetDeliveryPolicyTests.cs new file mode 100644 index 00000000..d88f3060 --- /dev/null +++ b/test/net/unit/AssetDeliveryPolicyTests.cs @@ -0,0 +1,172 @@ +//----------------------------------------------------------------------- +// Copyright 2014 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + + +using System; +using System.Net; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.WindowsAzure.MediaServices.Client.DynamicEncryption; +using Microsoft.WindowsAzure.MediaServices.Client.Tests.Common; +using Moq; + +namespace Microsoft.WindowsAzure.MediaServices.Client.Tests.Unit +{ + + [TestClass] + public class AssetDeliveryPolicyTests + { + private CloudMediaContext _mediaContext; + + [TestInitialize] + public void SetupTest() + { + _mediaContext = Helper.GetMediaDataServiceContextForUnitTests(); + } + #region Retry Logic tests + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [TestCategory("Bvt")] + [Priority(0)] + public void TestAssetDeliveryPolicyCreateRetry() + { + var expected = new AssetDeliveryPolicyData { Name = "testData" }; + var fakeException = new WebException("test", WebExceptionStatus.ConnectionClosed); + var dataContextMock = TestMediaServicesClassFactory.CreateSaveChangesMock(fakeException, 2, expected); + + dataContextMock.Setup((ctxt) => ctxt.AddObject("AssetDeliveryPolicies", It.IsAny())); + + _mediaContext.MediaServicesClassFactory = new TestMediaServicesClassFactory(dataContextMock.Object); + + var task = _mediaContext.AssetDeliveryPolicies.CreateAsync(expected.Name, AssetDeliveryPolicyType.None, AssetDeliveryProtocol.None, null); + task.Wait(); + IAssetDeliveryPolicy actual = task.Result; + + Assert.AreEqual(expected.Name, actual.Name); + dataContextMock.Verify((ctxt) => ctxt.SaveChangesAsync(It.IsAny()), Times.Exactly(2)); + } + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [Priority(0)] + [ExpectedException(typeof(WebException))] + public void TestAssetDeliveryPolicyCreateFailedRetry() + { + var expected = new AssetDeliveryPolicyData { Name = "testData" }; + var fakeException = new WebException("test", WebExceptionStatus.ConnectionClosed); + var dataContextMock = TestMediaServicesClassFactory.CreateSaveChangesMock(fakeException, 10, expected); + + dataContextMock.Setup((ctxt) => ctxt.AddObject("AssetDeliveryPolicies", It.IsAny())); + + _mediaContext.MediaServicesClassFactory = new TestMediaServicesClassFactory(dataContextMock.Object); + + try + { + _mediaContext.AssetDeliveryPolicies.CreateAsync(expected.Name, AssetDeliveryPolicyType.None, AssetDeliveryProtocol.None, null).Wait(); + } + catch (AggregateException ax) + { + dataContextMock.Verify((ctxt) => ctxt.SaveChangesAsync(It.IsAny()), Times.AtLeast(3)); + WebException x = (WebException)ax.GetBaseException(); + Assert.AreEqual(fakeException, x); + throw x; + } + + Assert.Fail("Expected exception"); + } + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [Priority(0)] + [ExpectedException(typeof(WebException))] + public void TestAssetDeliveryPolicyCreateFailedRetryMessageLengthLimitExceeded() + { + var expected = new AssetDeliveryPolicyData { Name = "testData" }; + + var fakeException = new WebException("test", WebExceptionStatus.MessageLengthLimitExceeded); + + var dataContextMock = TestMediaServicesClassFactory.CreateSaveChangesMock(fakeException, 10, expected); + + dataContextMock.Setup((ctxt) => ctxt.AddObject("AssetDeliveryPolicies", It.IsAny())); + + _mediaContext.MediaServicesClassFactory = new TestMediaServicesClassFactory(dataContextMock.Object); + + try + { + _mediaContext.AssetDeliveryPolicies.CreateAsync(expected.Name, AssetDeliveryPolicyType.None, AssetDeliveryProtocol.None, null).Wait(); + } + catch (AggregateException ax) + { + dataContextMock.Verify((ctxt) => ctxt.SaveChangesAsync(It.IsAny()), Times.Exactly(1)); + WebException x = (WebException)ax.GetBaseException(); + Assert.AreEqual(fakeException, x); + throw x; + } + + Assert.Fail("Expected exception"); + } + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [Priority(0)] + public void TestAssetDeliveryPolicyUpdateRetry() + { + var data = new AssetDeliveryPolicyData { Name = "testData" }; + var fakeException = new WebException("test", WebExceptionStatus.ConnectionClosed); + var dataContextMock = TestMediaServicesClassFactory.CreateSaveChangesMock(fakeException, 2, data); + + dataContextMock.Setup((ctxt) => ctxt.AttachTo("AssetDeliveryPolicies", data)); + dataContextMock.Setup((ctxt) => ctxt.UpdateObject(data)); + + _mediaContext.MediaServicesClassFactory = new TestMediaServicesClassFactory(dataContextMock.Object); + + data.SetMediaContext(_mediaContext); + + data.Update(); + + dataContextMock.Verify((ctxt) => ctxt.SaveChangesAsync(data), Times.Exactly(2)); + } + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [Priority(0)] + public void TestAssetDeliveryPolicyDeleteRetry() + { + var data = new AssetDeliveryPolicyData { Name = "testData" }; + + var fakeException = new WebException("test", WebExceptionStatus.ConnectionClosed); + + var dataContextMock = TestMediaServicesClassFactory.CreateSaveChangesMock(fakeException, 2, data); + + dataContextMock.Setup((ctxt) => ctxt.AttachTo("AssetDeliveryPolicies", data)); + dataContextMock.Setup((ctxt) => ctxt.DeleteObject(data)); + + _mediaContext.MediaServicesClassFactory = new TestMediaServicesClassFactory(dataContextMock.Object); + + data.SetMediaContext(_mediaContext); + + data.Delete(); + + dataContextMock.Verify((ctxt) => ctxt.SaveChangesAsync(data), Times.Exactly(2)); + } + #endregion Retry Logic tests + } +} \ No newline at end of file diff --git a/test/net/unit/AssetFilterTests.cs b/test/net/unit/AssetFilterTests.cs new file mode 100644 index 00000000..37c7476f --- /dev/null +++ b/test/net/unit/AssetFilterTests.cs @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------- +// Copyright 2014 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.WindowsAzure.MediaServices.Client.Tests.Unit +{ + [TestClass] + public class AssetFilterTests + { + private CloudMediaContext _mediaContext; + public TestContext TestContext { get; set; } + + [TestInitialize] + public void SetupTest() + { + _mediaContext = Helper.GetMediaDataServiceContextForUnitTests(); + } + + [TestMethod] + public void AssetFilterCRUD() + { + IStreamingFilter filter = _mediaContext.Filters.Create("UniTest", new PresentationTimeRange(), new List()); + Assert.IsNotNull(filter); + Assert.IsNotNull(filter.Tracks); + filter.Delete(); + Assert.IsNull(_mediaContext.Assets.Where(c => c.Name == filter.Name).FirstOrDefault()); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void ShouldFailForEmptyName() + { + IStreamingFilter filter = _mediaContext.Filters.Create(String.Empty, new PresentationTimeRange(), new List()); + } + + [TestMethod] + public void ShouldNotValidateNonEmptyName() + { + StringBuilder bld = new StringBuilder(); + + //Generating long string + for (int i = 0; i < 10; i++) + { + bld.Append(Guid.NewGuid()); + } + IStreamingFilter filter = _mediaContext.Filters.Create(bld.ToString(), new PresentationTimeRange(), new List()); + + bld.Clear(); + bld.Append("treasure island"); + bld.Append("остров сокровищ"); + bld.Append("金银岛"); + bld.Append("Schatzinsel"); + bld.Append("कोष द्विप"); + bld.Append("جزيرة الكنز"); + bld.Append("!@#$%^&&**()_+?>"); + filter = _mediaContext.Filters.Create(bld.ToString(), new PresentationTimeRange(), new List()); + } + + [TestMethod] + public void ShouldNotValidateNameand4CTracksOnClientSide() + { + StringBuilder bld = new StringBuilder(); + bld.Append("treasure island"); + bld.Append("остров сокровищ"); + bld.Append("金银岛"); + bld.Append("Schatzinsel"); + bld.Append("कोष द्विप"); + bld.Append("جزيرة الكنز"); + bld.Append("!@#$%^&&**()_+?>"); + + //Generating long string + for (int i = 0; i < 10; i++) + { + bld.Append(Guid.NewGuid()); + } + List filterTrackSelectStatements = new List(); + filterTrackSelectStatements.Add(new FilterTrackSelectStatement() + { + PropertyConditions = new List() + { + new FilterTrackNameCondition(trackName:bld.ToString()) + } + }); + filterTrackSelectStatements.Add(new FilterTrackSelectStatement() + { + PropertyConditions = new List() + { + new FilterTrackFourCCCondition(Guid.NewGuid().ToString()) + } + }); + filterTrackSelectStatements.Add(new FilterTrackSelectStatement() + { + PropertyConditions = new List() + { + new FilterTrackLanguageCondition(Guid.NewGuid().ToString()) + } + }); + filterTrackSelectStatements.Add(new FilterTrackSelectStatement() + { + PropertyConditions = new List() + { + new FilterTrackBitrateRangeCondition(new FilterTrackBitrateRange()) + } + }); + + IStreamingFilter filter = _mediaContext.Filters.Create(bld.ToString(), new PresentationTimeRange(), filterTrackSelectStatements); + + + } + + } +} \ No newline at end of file diff --git a/test/net/unit/AssetUnitTest.cs b/test/net/unit/AssetUnitTest.cs index 8d127371..e2c34405 100644 --- a/test/net/unit/AssetUnitTest.cs +++ b/test/net/unit/AssetUnitTest.cs @@ -136,7 +136,26 @@ public void AssetFileCreateEnvelopeEncryptedFile() CallUpdateUploadDownloadAndDelete(file, "AssetFileCreateEnvelopeEncryptedFile"); } + [TestMethod] + public void AssetFileDownloadUploadThrowsExceptionForFragblob() + { + var fragblob= new Mock().Object; + fragblob.Options = 1; + try + { + fragblob.Upload("/foo/bar"); + Assert.Fail(); + } + catch (NotSupportedException) { } + + try + { + fragblob.Download("foo.bar"); + Assert.Fail(); + } + catch (NotSupportedException) { } + } [TestMethod] public void AssetCreateAsync() @@ -496,6 +515,27 @@ public void TestAssetDeleteRetry() dataContextMock.Verify((ctxt) => ctxt.SaveChangesAsync(data), Times.Exactly(2)); } + [TestMethod] + public void TestAssetDeleteRetryWithKeepAzureContainerOption() + { + var data = new AssetData { Name = "testData" }; + + var fakeException = new WebException("test", WebExceptionStatus.ConnectionClosed); + + var dataContextMock = TestMediaServicesClassFactory.CreateSaveChangesMock(fakeException, 2, data); + + dataContextMock.Setup((ctxt) => ctxt.AttachTo("Assets", data)); + dataContextMock.Setup((ctxt) => ctxt.DeleteObject(data)); + + _mediaContext.MediaServicesClassFactory = new TestMediaServicesClassFactory(dataContextMock.Object); + + data.SetMediaContext(_mediaContext); + + var result = data.DeleteAsync(true).Result; + + dataContextMock.Verify((ctxt) => ctxt.SaveChangesAsync(data), Times.Exactly(2)); + } + [TestMethod] public void TestAssetGetContentKeysRetry() { diff --git a/test/net/unit/Live/ChannelTest.cs b/test/net/unit/Live/ChannelTest.cs index 8e08195c..40b700bb 100644 --- a/test/net/unit/Live/ChannelTest.cs +++ b/test/net/unit/Live/ChannelTest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.WindowsAzure.MediaServices.Client.Tests.Common; @@ -51,6 +52,17 @@ public void TestChannelCreateRetry() dataContextMock.Verify(ctxt => ctxt.SaveChangesAsync(It.IsAny()), Times.Exactly(2)); } + + [TestMethod] + [TestCategory("ClientSDK")] + [Owner("ClientSDK")] + [Priority(0)] + public void TestChannelQueryRetry() + { + CloudMediaContext mediaContext = Helper.GetMediaDataServiceContextForUnitTests(0); + var result = mediaContext.Channels.FirstOrDefault(); + } + [TestMethod] [TestCategory("ClientSDK")] [Owner("ClientSDK")] diff --git a/test/net/unit/Properties/AssemblyInfo.cs b/test/net/unit/Properties/AssemblyInfo.cs index a7bb88a8..4ee06dfa 100644 --- a/test/net/unit/Properties/AssemblyInfo.cs +++ b/test/net/unit/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.3.0.0")] -[assembly: AssemblyFileVersion("3.3.0.0")] +[assembly: AssemblyVersion("3.4.0.0")] +[assembly: AssemblyFileVersion("3.4.0.0")] diff --git a/test/net/unit/SDK.Client.Tests.Unit.csproj b/test/net/unit/SDK.Client.Tests.Unit.csproj index d5f8af29..6c081bca 100644 --- a/test/net/unit/SDK.Client.Tests.Unit.csproj +++ b/test/net/unit/SDK.Client.Tests.Unit.csproj @@ -110,7 +110,9 @@ + + diff --git a/test/net/unit/TestCloudMediaDataContext.cs b/test/net/unit/TestCloudMediaDataContext.cs index 182174eb..2f43a122 100644 --- a/test/net/unit/TestCloudMediaDataContext.cs +++ b/test/net/unit/TestCloudMediaDataContext.cs @@ -24,7 +24,8 @@ using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using System.Threading; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Microsoft.Practices.TransientFaultHandling; using Microsoft.WindowsAzure.MediaServices.Client.TransientFaultHandling; namespace Microsoft.WindowsAzure.MediaServices.Client.Tests.Common @@ -141,16 +142,26 @@ public void InitilizeStubData() } }); - _persistedChanges.Add(JobBaseCollection.JobSet, - new List + _persistedChanges.Add(JobBaseCollection.JobSet, + new List { new JobData() { Id= Guid.NewGuid().ToString(), Name = "Mock Job", } + }); + + _persistedChanges.Add(AssetFilterBaseCollection.AssetFilterSet, + new List + { + new AssetFilterData() + { + Id= Guid.NewGuid().ToString(), + Name = "Mock Asset Filter", + } }); - } + } public bool IgnoreResourceNotFoundException { get; set; } @@ -174,8 +185,8 @@ private IQueryable CreateQuery(string entitySetName) public IQueryable CreateQuery(string entitySetName) { - IQueryable inner = (IQueryable)this.CreateQuery(entitySetName); - var result = new MediaQueryable(inner); + IQueryable inner = (IQueryable)this.CreateQuery(entitySetName); + var result = new MediaQueryable(inner, new MediaRetryPolicy(new QueryErrorDetectionStrategy(), new ExponentialBackoff())); return result; } @@ -265,16 +276,16 @@ public QueryOperationResponse LoadProperty(object entity, string propertyName) } if (entity is JobTemplateData) { - JobTemplateData data = (JobTemplateData)(entity); + JobTemplateData data = (JobTemplateData)(entity); switch (propertyName) { - case "TaskTemplates": - data.TaskTemplates = CreateQuery("TaskTemplates").ToList(); + case "TaskTemplates": + data.TaskTemplates = CreateQuery("TaskTemplates").ToList(); break; default: break; } } - return null; + return null; } public void UpdateObject(object entity) @@ -316,9 +327,9 @@ public void DeleteLink(object source, string sourceProperty, object target) public void AddRelatedObject(object source, string sourceProperty, object target) { - MethodInfo methodInfo = this.GetType().GetMethods().Where(c => c.Name == "AddObject" && c.IsGenericMethod).First(); - methodInfo = methodInfo.MakeGenericMethod(new[] { target.GetType() }); - methodInfo.Invoke(this, new[] { sourceProperty, target }); + MethodInfo methodInfo = this.GetType().GetMethods().Where(c => c.Name == "AddObject" && c.IsGenericMethod).First(); + methodInfo = methodInfo.MakeGenericMethod(new[] { target.GetType() }); + methodInfo.Invoke(this, new[] { sourceProperty, target }); } public Task> ExecuteAsync(DataServiceQueryContinuation continuation, object state) @@ -366,9 +377,9 @@ public Task LoadPropertyAsync(object entity, string prop throw new NotImplementedException(); } - public Task SaveChangesAsync(object state) + private Func SaveChangesFunc(object state) { - return Task.Factory.StartNew((object c) => + return (object c) => { if (_delaymilliseconds > 0) { @@ -384,7 +395,7 @@ public Task SaveChangesAsync(object state) var addRamgeMethodInfo = _persistedChanges[pendingChange.Key].GetType().GetMethods().Where(m => m.Name == "AddRange").FirstOrDefault(); if (addRamgeMethodInfo != null) { - addRamgeMethodInfo.Invoke(_persistedChanges[pendingChange.Key], new[] { pendingChange.Value }); + addRamgeMethodInfo.Invoke(_persistedChanges[pendingChange.Key], new[] {pendingChange.Value}); } } @@ -399,25 +410,32 @@ public Task SaveChangesAsync(object state) if (state != null) { response.AsyncState = state; - state.GetType().InvokeMember("Id", BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty, Type.DefaultBinder, state, new[] { "nb:kid:UUID:" + Guid.NewGuid() }); + if (state.GetType().GetProperty("Id") != null) + { + state.GetType().InvokeMember("Id", BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty, Type.DefaultBinder, state, new[] {"nb:kid:UUID:" + Guid.NewGuid()}); + } if (state is IMediaContextContainer) { - ((IMediaContextContainer)state).SetMediaContext(_mediaContextBase); + ((IMediaContextContainer) state).SetMediaContext(_mediaContextBase); } if (state is LocatorData) { - ((LocatorData)state).BaseUri = "http://contoso.com/" + Guid.NewGuid().ToString(); - ((LocatorData)state).Path = "http://contoso.com/" + Guid.NewGuid().ToString(); - ((LocatorData)state).ContentAccessComponent = Guid.NewGuid().ToString(); + ((LocatorData) state).BaseUri = "http://contoso.com/" + Guid.NewGuid().ToString(); + ((LocatorData) state).Path = "http://contoso.com/" + Guid.NewGuid().ToString(); + ((LocatorData) state).ContentAccessComponent = Guid.NewGuid().ToString(); } if (state is AssetData) { - ((AssetData)state).Uri = "http://contoso.com/" + Guid.NewGuid().ToString(); + ((AssetData) state).Uri = "http://contoso.com/" + Guid.NewGuid().ToString(); } } return response; - }, + }; + } + public Task SaveChangesAsync(object state) + { + return Task.Factory.StartNew( SaveChangesFunc(state), state, CancellationToken.None); } @@ -427,6 +445,13 @@ public Task SaveChangesAsync(SaveChangesOptions optio return SaveChangesAsync(state); } + public Task SaveChangesAsync(SaveChangesOptions options, object state, CancellationToken token) + { + return Task.Factory.StartNew(SaveChangesFunc(state), + state, + token); + } + private MethodInfo MakeMethodGeneric(object entity, string methodName) { MethodInfo methodInfo = this.GetType().GetMethods().Where(c => c.Name == methodName && c.IsGenericMethod).First();