public async Task SchedulePostWithVideoAsync() { var videoType = Path.GetExtension(TestsBase.TestVideoPath); var bytes = File.ReadAllBytes(TestsBase.TestVideoPath); var post = new VideoCreatingRequest(videoType) { Source = bytes, FileSize = bytes.Length, Description = $"Description_{TestsBase.Context.TestName}_{Guid.NewGuid().ToString("N")}", Name = $"Name_{TestsBase.Context.TestName}_{Guid.NewGuid().ToString("N").Remove(16)}", Title = $"Title_{TestsBase.Context.TestName}_{Guid.NewGuid().ToString("N").Remove(8)}", Published = false, UnpublishedContentType = UnpublishedContentType.Scheduled, ScheduledPublishTime = DateTimeUtil.CountTimeStamp(DateTime.Now.AddHours(1)) }; var response = await post.PostAsync(TestsBase.PageId, TestsBase.PageAccessToken); Assert.IsTrue(response.Code == ResponseCode.SUCCESS, response.ReasonPhrase ?? String.Empty); var jobj = JObject.Parse(response.Data); Assert.IsTrue(jobj["id"] != null && !String.IsNullOrEmpty(jobj["id"].ToString())); }
/// <summary> /// Simplify the target <see cref="VideoCreatingRequest"/> object. /// </summary> /// <param name="request"> /// A <see cref="VideoCreatingRequest"/> object. /// </param> private static void SimplifyVideoCreatingRequest(VideoCreatingRequest request) { request.EndOffset = null; request.FileUrl = null; request.Source = null; request.StartOffset = null; request.UploadPhase = null; request.UploadSessionId = null; request.VideoFileChunk = null; }
public void Initialize() { var videoType = Path.GetExtension(TestsBase.TestVideoPath); _request = new VideoCreatingRequest(videoType) { Description = $"Description_{TestsBase.Context.TestName}_{Guid.NewGuid().ToString("N")}", Name = $"Name_{TestsBase.Context.TestName}_{Guid.NewGuid().ToString("N").Remove(16)}", Title = $"Title_{TestsBase.Context.TestName}_{Guid.NewGuid().ToString("N").Remove(8)}" }; }
/// <summary> /// Uploads an ad video to facebook by non-resumable upload as an asynchronous operation. /// </summary> /// <param name="filename"> /// The full file path of the video. /// </param> /// <param name="adAccountId"> /// The ad account id of user. /// </param> /// <param name="accessToken"> /// Access token. /// </param> /// <param name="request"> /// A <see cref="VideoCreatingRequest"/> object, of which basic information will be in use. /// </param> /// <returns> /// The task object representing the asynchronous operation. /// </returns> /// <exception cref="FileNotFoundException"> /// Throw if the target file doesn't exists. /// </exception> /// <exception cref="ArgumentNullException"> /// Throw if the parameter targetId or accessToken is null or empty string, or request equals to null. /// </exception> public static async Task <ResponseMessage <string> > UploadAsAdVideoAsync(string filename, string adAccountId, string accessToken, VideoCreatingRequest request) { #region Data check & preprocess. if (!File.Exists(filename)) { throw new FileNotFoundException($"Cannot find file {filename}"); } if (String.IsNullOrEmpty(adAccountId) || String.IsNullOrEmpty(accessToken) || request == null) { throw new ArgumentNullException(); } SimplifyVideoCreatingRequest(request); #endregion request.Source = await File.ReadAllBytesAsync(filename); request.FileSize = request.Source.Length; return(await request.PostAsAdVideoAsync(adAccountId, accessToken)); }
/// <summary> /// Uploads a video to facebook by resumable upload as an asynchronous operation. /// </summary> /// <param name="filename"> /// The full file path of the video. /// </param> /// <param name="request"> /// A <see cref="VideoCreatingRequest"/> object, of which basic information will be in use. /// </param> /// <param name="retries"> /// Chances of retry. The retry operation will be triggered on failed to upload chunk file. /// </param> /// <param name="asyncRequestOperation"> /// A function to post <see cref="VideoCreatingRequest"/> asynchronously, a /// <see cref="ResponseMessage{TResult}"/> object should be returns. /// </param> /// <returns> /// The task object representing the asynchronous operation. /// </returns> private static async Task <ResponseMessage <string> > UploadInChunksAsync(string filename, VideoCreatingRequest request, int retries, Func <VideoCreatingRequest, Task <ResponseMessage <string> > > asyncRequestOperation) { var response = new ResponseMessage <string>(); var fileExtension = Path.GetExtension(filename); var fileInfo = new FileInfo(filename); var videoId = String.Empty; var uploadSessionId = String.Empty; var allUploaded = false; JObject jobj = null; Exception exception = null; // To init chunked upload. var initRequest = new VideoCreatingRequest(fileExtension) { UploadPhase = UploadPhase.Start, FileSize = fileInfo.Length }; response = await asyncRequestOperation(initRequest); #if DEBUG System.Diagnostics.Debug.WriteLine($"Initializes chunked upload returns {response.Data}"); #endif if (response.Code == ResponseCode.SUCCESS) { jobj = JObject.Parse(response.Data); if (jobj["video_id"] != null && jobj["upload_session_id"] != null && !String.IsNullOrEmpty(jobj["video_id"].ToString()) && !String.IsNullOrEmpty(jobj["upload_session_id"].ToString())) { videoId = jobj["video_id"].ToString(); uploadSessionId = jobj["upload_session_id"].ToString(); var transferRequest = new VideoCreatingRequest(fileExtension) { UploadSessionId = uploadSessionId, StartOffset = Int64.Parse(jobj["start_offset"].ToString()), EndOffset = Int64.Parse(jobj["end_offset"].ToString()), UploadPhase = UploadPhase.Transfer }; // To open file and transfer data. using (var fs = fileInfo.OpenRead()) { do { var bufferLength = (Int32)(transferRequest.EndOffset - transferRequest.StartOffset).Value; var buffer = new byte[bufferLength]; fs.Position = transferRequest.StartOffset.Value; fs.Read(buffer, 0, bufferLength); transferRequest.VideoFileChunk = buffer; var uploaded = false; // Indicates if current chunk file uploaded successfully. var uploadCounter = 0; // Indicates the uploading attempts of current chunk file. #region Transfers single chunk file. do { try { exception = null; // Reset exception. #if DEBUG var startTime = DateTime.Now; #endif response = await asyncRequestOperation(transferRequest); #if DEBUG var duration = DateTime.Now - startTime; System.Diagnostics.Debug.WriteLine($"Transfering {transferRequest.StartOffset}-{transferRequest.EndOffset} returns {response.Code}. Duration: {duration.TotalSeconds.ToString("N2")}s."); #endif if (response.Code == ResponseCode.SUCCESS) { jobj = JObject.Parse(response.Data); if (jobj["start_offset"] != null && jobj["end_offset"] != null && !String.IsNullOrEmpty(jobj["start_offset"].ToString()) && !String.IsNullOrEmpty(jobj["end_offset"].ToString())) { transferRequest.StartOffset = Int64.Parse(jobj["start_offset"].ToString()); transferRequest.EndOffset = Int64.Parse(jobj["end_offset"].ToString()); uploaded = true; } } } catch (Exception ex) { exception = ex; await Task.Delay(1000); } }while (!uploaded && uploadCounter++ < retries); #endregion if (!uploaded) { if (exception != null) { throw exception; // Throw exception if it occured during the last attempt. } else { break; // None successful attempt, break. } } else if (transferRequest.StartOffset >= transferRequest.EndOffset) { allUploaded = true; } }while (!allUploaded); } if (allUploaded) { // To complete chunked upload. SimplifyVideoCreatingRequest(request); request.UploadSessionId = uploadSessionId; request.UploadPhase = UploadPhase.Finish; response = await asyncRequestOperation(request); jobj = JObject.Parse(response.Data); jobj["video_id"] = videoId; response.Data = jobj.ToString(); } } } return(response); }
/// <summary> /// Uploads an ad video to facebook by resumable upload as an asynchronous operation. /// </summary> /// <param name="filename"> /// The full file path of the video. /// </param> /// <param name="adAccountId"> /// The ad account id of user. /// </param> /// <param name="accessToken"> /// Access token. /// </param> /// <param name="request"> /// A <see cref="VideoCreatingRequest"/> object, of which basic information will be in use. /// </param> /// <param name="retries"> /// Chances of retry. The retry operation will be triggered on failed to upload chunk file. /// </param> /// <returns> /// The task object representing the asynchronous operation. /// </returns> /// <exception cref="FileNotFoundException"> /// Throw if the target file doesn't exists. /// </exception> /// <exception cref="ArgumentNullException"> /// Throw if the parameter targetId or accessToken is null or empty string, or request equals to null. /// </exception> public static async Task <ResponseMessage <string> > UploadAsAdVideoInChunksAsync(string filename, string adAccountId, string accessToken, VideoCreatingRequest request, int retries = 2) { #region Data check. if (!File.Exists(filename)) { throw new FileNotFoundException($"Cannot find file {filename}"); } if (String.IsNullOrEmpty(adAccountId) || String.IsNullOrEmpty(accessToken) || request == null) { throw new ArgumentNullException(); } #endregion return(await UploadInChunksAsync(filename, request, retries, async (req) => { return await req.PostAsAdVideoAsync(adAccountId, accessToken); })); }
/// <summary> /// Publishes video ad to facebook with a specified cover as an asynchronous operation. /// </summary> /// <param name="video"> /// The filepath of the ad video. /// </param> /// <param name="cover"> /// The url or filepath of the video cover. Sets to null or an empty string to use the thumbnail that /// generated by facebook as the cover. /// </param> /// <param name="request"> /// An object of <see cref="AdCreativeCreatingRequest"/>, which contains most of publish infomation. /// </param> /// <param name="adAccountId"> /// The ad account id of user. /// </param> /// <param name="accessToken"> /// User access token. /// </param> /// <param name="pageAccessToken"> /// Page access token. /// </param> /// <param name="publish"> /// If true, the photo ad will be published to a facebook page immediately. /// </param> /// <param name="threshold"> /// If the filesize (in MByte) of video exceeded this value, the video will be uploaded in resumable upload; /// otherwise, non-resumable upload. The default threshold is set to 50MB. /// </param> /// <returns> /// The task object representing the asynchronous operation. /// </returns> /// <exception cref="FileNotFoundException"> /// Throw if the target video or cover file doesn't exist. /// </exception> public static async Task <ResponseMessage <string> > PublishVideoAdAsync(string video, string cover, AdCreativeCreatingRequest request, string adAccountId, string accessToken, string pageAccessToken, bool publish, int threshold = 50) { ResponseMessage <string> response = null; JObject jobj = null; var videoId = String.Empty; var coverUrl = String.Empty; var adCreative = new AdCreative(); // 1. Gets the video_id. #region Uploads video to facebook. if (!File.Exists(video)) { throw new FileNotFoundException($"Cannot find file {video}"); } var videoInfo = new FileInfo(video); var videoSize = videoInfo.Length; var enableChunkedUpload = videoSize > (50L * 1024 * 1024); var videoCreatingRequest = new VideoCreatingRequest(videoInfo.Extension) { Name = videoInfo.Name, Title = Path.GetFileNameWithoutExtension(video) }; response = enableChunkedUpload ? await VideoUploader.UploadAsAdVideoInChunksAsync(video, adAccountId, accessToken, videoCreatingRequest) : await VideoUploader.UploadAsAdVideoAsync(video, adAccountId, accessToken, videoCreatingRequest); if (response.Code == ResponseCode.SUCCESS) { jobj = JObject.Parse(response.Data); if (enableChunkedUpload) { // Parses resumable upload result. if (jobj["video_id"] != null && !String.IsNullOrEmpty(jobj["video_id"].ToString())) { videoId = jobj["video_id"].ToString(); } } else { // Parses non-resumable upload result. if (jobj["id"] != null && !String.IsNullOrEmpty(jobj["id"].ToString())) { videoId = jobj["id"].ToString(); } } if (String.IsNullOrEmpty(videoId)) { return(new ResponseMessage <string> { Code = ResponseCode.UNKNOWN_ERROR, ReasonPhrase = $"Failed to upload video {video} to facebook." }); } } else { return(response); } #endregion #if DEBUG System.Diagnostics.Debug.WriteLine($"Got video_id: {videoId}"); #endif // 2. Gets the url of cover. if (!String.IsNullOrEmpty(cover)) { // Customizes cover. #pragma warning disable SA1008 var(AdPhotoUrl, AdPhotoResponse) = await GetAdPhotoUrlAsync(cover, adAccountId, accessToken); #pragma warning restore SA1008 if (String.IsNullOrEmpty(AdPhotoUrl)) { return(AdPhotoResponse); } coverUrl = AdPhotoUrl; } /* The thumbnail won't be generated until the video has been decoded by facebook successfully, * just wait for a while. * */ await Task.Delay(10_000); #pragma warning disable SA1008 var(ThumbnailUrl, ThumbnailResponse) = await GetVideoThumbnailAsync(videoId, accessToken, 20); #pragma warning restore SA1008 if (String.IsNullOrEmpty(cover)) { if (String.IsNullOrEmpty(ThumbnailUrl)) { return(ThumbnailResponse); } coverUrl = ThumbnailUrl; } #if DEBUG System.Diagnostics.Debug.WriteLine($"Got cover_url: {coverUrl}"); #endif // 3. Posts ad creative creating request. request.ObjectStorySpec.VideoData.VideoId = videoId; request.ObjectStorySpec.VideoData.Cover = coverUrl; #pragma warning disable SA1008 var(AdCreativeId, AdCreativeResponse) = await PostAdCreativeAsync(request, adAccountId, accessToken); #pragma warning restore SA1008 if (String.IsNullOrEmpty(AdCreativeId)) { return(AdCreativeResponse); } adCreative.Id = AdCreativeId; #if DEBUG System.Diagnostics.Debug.WriteLine($"Got ad_creative_id: {AdCreativeId}"); #endif if (publish) { // 4. Publishes ad creative. response = await adCreative.PublishAsync(accessToken, pageAccessToken); } return(response); }