YouTube API V3 Upload video throws exception - youtube-api

I am working with Web application with C# where user will browse a video file and web application will upload to YouTube via YouTube API V3. I am getting the error. Please advice where i am doing wrong?
Error: System.ArgumentNullException: Value cannot be null. Parameter name: baseUri at Microsoft.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at Microsoft.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccess(Task task) at Microsoft.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at Microsoft.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at Google.Apis.Upload.ResumableUpload1.d__e.MoveNext() in c:\code\google.com\google-api-dotnet-client\default\Tools\Google.Apis.Release\bin\Debug\output\default\Src\GoogleApis\Apis[Media]\Upload\ResumableUpload.cs:line 459
I followed the below points.
Created ClientID for Webapplication and downloaded client_secrets.json file from API Access page.
Used the .Net sample code provided in https://developers.google.com/youtube/v3/docs/videos/insert#examples also referred same code from https://developers.google.com/youtube/v3/code_samples/dotnet
My application got authorized to YouTube API.
While uploading the video file, i am getting below error.
I am pasting below my code for your reference.
/* TestFileUploader.aspx */
<%# Page Language="C#" AutoEventWireup="true" CodeFile="TestFileUploader.aspx.cs" Inherits="TestFileUploader" Async="true" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<form id="qaform" runat="server">
<div>
<p>
<asp:FileUpload runat="server" ID="FileUpload1" onchange="AddEventChoosePicture(this,'FileUpload1')"
Style="width: 100%;" />
</p>
<p>
<asp:Button ID="submit" runat="server" OnClick="submit_Click" Text="Submit" />
</p>
</div>
</form>
</body>
</html>
/* TestFileUploader.aspx.cs */
using System;
using System.Web;
using System.IO;
using QA.credential;
using Google.Apis.Auth.OAuth2;
using Google.Apis.YouTube.v3;
using Google.Apis.Services;
using Google.Apis.YouTube.v3.Data;
using Google.Apis.Upload;
public partial class TestFileUploader : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void submit_Click(object sender, EventArgs e)
{
try
{
if (FileUpload1.HasFile)
{
String FileUpload1_Ext = System.IO.Path.GetExtension(this.FileUpload1.PostedFile.FileName);
UploadVideos(FileUpload1.FileContent, FileUpload1.PostedFile.ContentType);
}
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
}
private async void UploadVideos(Stream uploadedStream, String contenttype)
{
try
{
UserCredential credential;
String clientsecretkeypath = HttpContext.Current.Server.MapPath("~/client_secrets.json");
using (var stream = new FileStream(clientsecretkeypath, FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { YouTubeService.Scope.YoutubeUpload },
"user", System.Threading.CancellationToken.None);
}
// Create the service.
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
//ApiKey = "AIzaSyAFxuAA4r4pf6VX75zEwCrIh5z4QkzOZ6M",
HttpClientInitializer = credential,
ApplicationName = PhotoandVideoupload.applicationname
});
var video = new Video();
video.Snippet = new VideoSnippet();
video.Snippet.Title = "My Test Movie";
video.Snippet.Description = "My description";
video.Snippet.Tags = new string[] { "Autos" };
video.Snippet.CategoryId = "2";
video.Status = new VideoStatus();
video.Status.PrivacyStatus = "unlisted";
// Using snippet,status below throws 401(UnAuthorized issue).
// Using snippet alone throws 404(Bad Request).
//In both case, Null Exception throws for parameter baseURI.
var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet", uploadedStream, contenttype);
videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
Google.Apis.Upload.IUploadProgress progress = await videosInsertRequest.UploadAsync();
switch (progress.Status)
{
case UploadStatus.Uploading:
Response.Write(String.Format("{0} bytes sent.", progress.BytesSent));
break;
case UploadStatus.Failed:
Response.Write(String.Format("{0}<br/>", progress.Exception.Message));
Response.Write(String.Format("{0}<br/>", progress.Exception.StackTrace));
break;
}
// Also tried to read file from server path instead of uploading via fileupload control.
/*
using (var fileStream = new FileStream(HttpContext.Current.Server.MapPath("~/1.mp4"), FileMode.Open))
{
var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, contenttype);
videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
await videosInsertRequest.UploadAsync();
}
*/
}
catch (Exception ex)
{
Response.Write(ex.Message + " " + ex.StackTrace);
}
finally
{
}
}
void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
switch (progress.Status)
{
case UploadStatus.Uploading:
Response.Write(String.Format("{0} bytes sent.", progress.BytesSent));
break;
case UploadStatus.Failed:
Response.Write(String.Format("{0}<br/>", progress.Exception.Message));
Response.Write(String.Format("{0}<br/>", progress.Exception.StackTrace));
break;
}
}
void videosInsertRequest_ResponseReceived(Video video)
{
Response.Write(String.Format("Video id '{0}' was successfully uploaded.", video.Id));
}
}
Please advice where i am doing wrong?
Thanks,

You might try the following change:
From:
var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet", uploadedStream, contenttype);
To:
var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", uploadedStream, contenttype);
Also, what is in contenttype? I use video/* as the contenttype for video uploads.
The status code 401 error may be because the YouTube API has not been authorized. Check that you have authorized the YouTube API for your credentials. Go to Google Developers Console and click on your project name. Then click on "APIs" in the left navigation menu. Check to see that YouTube Data API v3 is On.

Related

How to get messages from Azure Service Bus (Queue) in View.cshtml in ASP.NET MVC web app

I have a console application with which I can get messages from Azure Service Bus (Queue).
using System;
using System.Text;
using System.Text.Json;
using Microsoft.Azure.ServiceBus;
using SampleShared.Models;
namespace SampleAppReceiver
{
class Program
{
const string connString = "<my_connection_string>";
static IQueueClient qClient;
static async Task Main(string[] args)
{
qClient = new QueueClient(connString, "<my_queue_name>");
var msgOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
// How many messages we can process at time
MaxConcurrentCalls = 1,
// need to wait until a message is fully processed
AutoComplete = false,
};
qClient.RegisterMessageHandler(ProcessMessageAsync, msgOptions);
Console.ReadLine();
await qClient.CloseAsync();
}
private static async Task ProcessMessageAsync(Message msg, CancellationToken token)
{
// Deserialise the msg body
var jsonBody = Encoding.UTF8.GetString(msg.Body);
var personObj = JsonSerializer.Deserialize<Person>(jsonBody);
Console.WriteLine($"Login: {personObj.Login}");
Console.WriteLine($"Message: {personObj.Message}");
// Updating the queue that the message has been processed sucsessfully
await qClient.CompleteAsync(msg.SystemProperties.LockToken);
}
private static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs args)
{
Console.WriteLine($"Something went wrong, {args.Exception}");
return Task.CompletedTask;
}
}
}
How can I correctly add all the received messages to View.cshtml from controller?
Now I have a service (C# interface) with which I can send messages from View.cshtml to Azure Service Bus (queue):
// ...
public interface IAzureBusService
{
Task SendMessageAsync(Person personMessage, string queueName);
}
// ...
Controller method:
[HttpPost]
public async Task<IActionResult> Index(Person person)
{
await _busService.SendMessageAsync(person, "personqueue");
return RedirectToAction("Index");
}
Create a service Bus in Azure portal.
Create a Queue as per the below screenshot.
I followed the below steps in displaying the queue messages in a view.
You can use the console application reference in your MVC project to display queue messages in a View by calling the method of fetching the messages from queue.
You need to use the below code in the controller class.
public ActionResult Index()
{
List<QueueMsgs> queMsglist = new List<QueueMsgs>();
QueueMsgs msgs = new QueueMsgs();
queMsglist = GetMessagesFromQueue();
return View(queMsglist);
}
public void GetMessagesFromQueue()
{
ServiceBusReceiver receiver = new ServiceBusReceiver();
receiver.Listener();
}
public void Listener()
{
ServiceBusConnectionStringBuilder conStr;
QueueClient client;
try
{
conStr = new ServiceBusConnectionStringBuilder(QueueAccessKey);
client = new QueueClient(conStr, ReceiveMode.ReceiveAndDelete, RetryPolicy.Default);
var messageHandler = new MessageHandlerOptions(ListenerExceptionHandler)
{
MaxConcurrentCalls = 1,
AutoComplete = false
};
client.RegisterMessageHandler(ReceiveMessageFromQ, messageHandler);
}
catch (Exception exe)
{
Console.WriteLine("{0}", exe.Message);
Console.WriteLine("Please restart application ");
}
public async Task ReceiveMessageFromQ(Message message, CancellationToken token)
{
string result = Encoding.UTF8.GetString(message.Body);
Console.WriteLine("Message received from Queue = {0}", result);
await Task.CompletedTask;
}
public Task ListenerExceptionHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
Console.WriteLine("{0}", exceptionReceivedEventArgs.Exception);
return Task.CompletedTask;
}
#model IEnumerable<MVC.Models.QueueMsgs>
#{
ViewBag.Title = "Queue Messages";
}
#foreach (var item in Model)
{
<div>
#item.message
<hr />
</div>
}
Displaying Queue Messages in View.

Blazor WebAssembly Using stream in pdf.js

How can I use a base64 stream in pdf.js inside a Blazor app?
It's easier to use a local path (src="path?file=filePath"), but not good documented how to handle a pdf stream.
Download, unpack and implement pdf.js in your blazor app in wwwroot/lib.
Add at index.html
<script type="text/javascript" src="lib/pdfjs/build/pdf.js"></script>
<script type="text/javascript">
function loadPdf(base64Data) {
try {
var pdfjsframe = document.getElementById('pdfViewer');
if (!base64Data == "") {
pdfjsframe.contentWindow.PDFViewerApplication.open(base64Data);
}
} catch (error) { console.error("Error at pdfjsframe.contentWindow.PDFViewerApplication.open(base64Data)"); }
}
</script>
Add at your page or component.razor:
<iframe id="pdfViewer" src="/lib/pdfjs/web/viewer.html"></iframe>
and in the cs:
public partial class PdfViewerComponent
{
[Parameter]
public int DocumentNumber { get; set; }
private string _stream = "";
protected override async Task OnParametersSetAsync()
{
_stream = await HttpClientService.GetDocumentStreamById(DocumentNumber);
if (!string.IsNullOrEmpty(_stream))
await OpenDocument(_stream);
_stream = ""; // that will ensure that your loading the right pdf at the right time
}
private async Task OpenDocument(string stream)
{
await JSRuntime.InvokeVoidAsync("loadPdf", stream);
}
}
In this example the _stream comes from a API. Put in the property _stream your stream string wherever you will get it from.

Ajax call refreshes the client page

I'm using html2canvas to take screenshot of a div and then upload it to the server. After the image is saved on the server, the page on client refreshes. And that is something I don't want. This is the code, that saves the picture:
public void SaveImage(string imageString, int id)
{
string s = imageString.Replace("data:image/png;base64,", "");
byte[] imageB = Convert.FromBase64String(s);
var filePath = Path.Combine(Server.MapPath("~/screens/" + id));
Directory.CreateDirectory(filePath);
using (var ms = new MemoryStream(imageB))
{
using (var image = Image.FromStream(ms))
{
image.Save(filePath + "/screen.png", ImageFormat.Png);
}
}
}
This is the Javascript code
html2canvas(document.querySelector("#screen")).then(function (canvas) {
var data = canvas.toDataURL("image/png");
$.ajax({
url: '#Url.Action("SaveImage")',
type: 'POST',
data: { imageString: data, id: #ViewBag.id },
dataType: 'json'
});
});
I have tried some advices I found online like preventDefault() in jQuery. But that's about it. I feel like all the other issues and solutions does not affect me.
EDIT: Simple HTML that refreshes the page too
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Test</title>
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/html2canvas.js"></script>
<script>
$(document).ready(function () {
html2canvas(document.querySelector("#screen")).then(function (canvas) {
var data = canvas.toDataURL("image/png");
$.ajax({
url: '#Url.Action("SaveImage")',
type: 'POST',
data: { imageString: data, id: 2 },
dataType: 'json',
});
});
});
</script>
</head>
<body>
<div id="screen">Some text</div>
</body>
</html>
EDIT 1: The whole controller with SaveImage method
using System;
using System.Linq;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using BcVnc.Models;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
namespace BcVnc.Controllers
{
[Authorize(Roles = "Admin, User")]
public class ConnectionController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: Connection
public ActionResult Index(int id, bool viewOnly)
{
int access = checkUserAccess(id, viewOnly);
if(access != 0)
{
ViewBag.id = id;
if (access == 1 & viewOnly == true)
ViewBag.viewOnly = true.ToString().ToLower();
else if(access == 1 && viewOnly == false)
ViewBag.viewOnly = false.ToString().ToLower();
else
ViewBag.viewOnly = true.ToString().ToLower();
return View();
}
else
{
return View("Error");
}
}
private int checkUserAccess(int id, bool viewOnly)
{
var userId = User.Identity.GetUserId();
var userDevice = db.UserDevices.Where(ud => ud.UserId == userId).FirstOrDefault(ud => ud.DeviceId == id);
var device = db.Devices.FirstOrDefault(d => d.Id == id);
ViewBag.name = device.Name;
if (userDevice == null)
return 0;
else
{
if (userDevice.ViewOnly == false)
return 1;
else
return -1;
}
}
public void SaveImage(string imageString, int id)
{
string s = imageString.Replace("data:image/png;base64,", "");
byte[] imageB = Convert.FromBase64String(s);
var filePath = Path.Combine(Server.MapPath("~/screens/" + id));
Directory.CreateDirectory(filePath);
using (var ms = new MemoryStream(imageB))
{
using (var image = Image.FromStream(ms))
{
image.Save(filePath + "/screen.png", ImageFormat.Png);
}
}
}
}
}
Not sure how come I was not able to find this before, but the whole problem was in Visual Studio settings. The refresh probably would not happen outside the localhost: Refresh in browser when uploading mvc file c #
After changing that in settings, no refresh anymore.

Upload video on YouTube channel without end users login prompt using Google.Apis.YouTube.v3

I want use Google.Apis.YouTube.v3 for video upload on YouTube.
My requirement is Upload video on YouTube channel without end users login prompt.
So please let me know where there is some method where i can validate the user at server side and upload video directly to my channel in YouTube.
I want to implement in MVC C# application
[HttpPost]
public async Task<ActionResult> UploadVideo(HttpPostedFileBase video, UploadVideoInfo info)
{
try
{
var httpPostedFile = Request.Files[0];
if (httpPostedFile != null)
{
UserCredential credential;
using (var stream = new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
//This OAuth 2.0 access scope allows an application to upload files to the
//authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
});
var videos = new Video();
videos.Snippet = new VideoSnippet();
videos.Snippet.Title = info.VideoTitle;
videos.Snippet.Description = info.VideoDescription;
videos.Snippet.Tags = new string[] { "tag1", "tag2" };
videos.Snippet.CategoryId = "22";
videos.Status = new VideoStatus();
videos.Status.PrivacyStatus = "public"; // or "public" or "unlisted"
var videosInsertRequest = youtubeService.Videos.Insert(videos, "snippet,status", video.InputStream, "video/*");
videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
await videosInsertRequest.UploadAsync();
}
}
catch (Exception ex)
{
}
return View();
}
void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
switch (progress.Status)
{
case UploadStatus.Uploading:
Console.WriteLine("{0} bytes sent.", progress.BytesSent);
break;
case UploadStatus.Failed:
Console.WriteLine("An error prevented the upload from completing.\n{0}", progress.Exception);
break;
}
}
void videosInsertRequest_ResponseReceived(Video video)
{
Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id);
}

Using AJAX with ASP.NET MVC 1.0 on IIS 6

I am running into a problem trying to use AJAX and jQuery with ASP.NET MVC on IIS 6.0. I receive a 403.1 error when I attempt to invoke an action via jQuery. Is there anything I must add to the web.config in order to support this?
Client Code
<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>
<script src="../../Scripts/jquery-1.3.2.js" type="text/javascript"></script>
<script type="text/javascript">
function deleteRecord(recordId) {
// Perform delete
$.ajax(
{
type: "DELETE",
url: "/Financial.mvc/DeleteSibling/" + recordId,
data: "{}",
success: function(result) {
window.location.reload();
},
error: function(req, status, error) {
alert("Unable to delete record.");
}
});
}
</script>
<a onclick="deleteRecord(<%= sibling.Id %>)" href="JavaScript:void(0)">Delete</a>
Server Code
[AcceptVerbs(HttpVerbs.Delete)]
public virtual ActionResult DeleteSibling(int id)
{
var sibling = this.siblingRepository.Retrieve(id);
if (sibling != null)
{
this.siblingRepository.Delete(sibling);
this.siblingRepository.SubmitChanges();
}
return RedirectToAction(this.Actions.Siblings);
}
Error
You have attempted to execute a CGI, ISAPI, or other executable program from a directory that does not allow programs to be executed.
HTTP Error 403.1 - Forbidden: Execute access is denied.
Internet Information Services (IIS)
Update
Darin correctly pointend out that it helps if you add the DELETE verb to .mvc extension, however I an now running into the following issue:
[HttpException (0x80004005): Path 'DELETE' is forbidden.] System.Web.HttpMethodNotAllowedHandler.ProcessRequest(HttpContext context) +80 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication+IExecutionStep.Execute() +179 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Status: 405 - Method not allowed
When you register the .mvc extension with aspnet_isapi.dll in IIS you need to enable the DELETE verb:
alt text http://support.citrix.com/article/html/images/CTX104183-1.gif
This is how to change this in code:
class IISDirEntry
{
public void SetProperty(string metabasePath, string propertyName, string newValue)
{
// metabasePath is of the form "IIS://servername/path"
try
{
DirectoryEntry path = new DirectoryEntry(metabasePath);
PropertyValueCollection propValues = path.Properties[propertyName];
object[] propv = ((object[])propValues.Value);
int searchIndex = newValue.IndexOf(',');
int index = -1;
for (int i = 0; i < propv.Length; i++)
{
if (propv[i].ToString().ToLower().StartsWith(newValue.ToLower().Substring(0, searchIndex + 1)))
{
index = i;
break;
}
}
if (index != -1)
{
propv[index] = newValue;
}
else
{
List<object> proplist = new List<object>(propv);
proplist.Add(newValue);
propv = proplist.ToArray();
}
path.Properties[propertyName].Value = propv;
path.CommitChanges();
Console.WriteLine("IIS6 Verbs fixed.");
}
catch (Exception ex)
{
if ("HRESULT 0x80005006" == ex.Message)
Console.WriteLine(" Property {0} does not exist at {1}", propertyName, metabasePath);
else
Console.WriteLine("Failed in SetProperty with the following exception: \n{0}", ex.Message);
}
}
}
public void ChangeIIS6Verbs()
{
if (IISVersion < 7.0)
{
IISDirEntry iisDirEntry = new IISDirEntry();
string windir = Environment.GetEnvironmentVariable("windir");
iisDirEntry.SetProperty("IIS://localhost/W3SVC/" + SiteIndex + "/ROOT", "ScriptMaps",
#".aspx," + Path.Combine(windir, #"\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll") + ",1,GET,HEAD,POST,DEBUG,DELETE");
}
}
Useful if need to configure on install

Resources