I'm trying to download a caption track using YouTube API v3 (https://developers.google.com/youtube/v3/docs/captions/download) and official .NET SDK nuget package (https://www.nuget.org/packages/Google.Apis.YouTube.v3/, version 1.9.0.1360).
Returned stream contains the following text:
"The OAuth token was received in the query string, which this API forbids for response formats other than JSON or XML. If possible, try sending the OAuth token in the Authorization header instead."
instead of the SRT plain text content which I just uploaded and verified manually through YouTube.com UI.
I found the type of error: lockedDomainCreationFailure
My code:
...
_service = new YTApi.YouTubeService(new BaseClientService.Initializer {
ApplicationName = config.AppName,
ApiKey = config.DeveloperKey
});
...
public Stream CaptionsDownload(
string accessToken,
string trackId
)
{
var request = _service.Captions.Download(trackId);
request.OauthToken = accessToken;
request.Tfmt = YTApi.CaptionsResource.DownloadRequest.TfmtEnum.Srt;
var trackStream = new MemoryStream();
request.Download(trackStream);
trackStream.Position = 0;
return trackStream;
}
I cannot seem to find the way to set any headers on _service.HttpClient, and I guess I shouldn't do it manually. I expect that DownloadRequest (or YouTubeBaseServiceRequest) will put
/// <summary>
/// OAuth 2.0 token for the current user.
/// </summary>
[RequestParameter("oauth_token", RequestParameterType.Query)]
public virtual string OauthToken { get; set; }
into a correct authorization header. I don't see this implemented in the version 1.9.0.1360.
Maybe I'm overlooking something? Any help is greatly appreciated.
Note: I use other caption-related methods with this SDK, and 'download' is the only one I'm having a trouble with.
You initialed the service WITHOUT the user credential (you only used the API key). Take a look in one of the samples in our developers guide, (and pick the right flow... are you using installed application, windows phone, etc.?)
You will have to change the way you create your service to do something like the following:
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { YoutubeService.Scope.<THE_RIGHT_SCOPE_HERE> },
"user", CancellationToken.None);
}
// Create the service.
_service = new YouTubeService(new BaseClientService.Initializer {
ApplicationName = config.AppName,
HttpClientInitializer = credential,
ApplicationName = "Books API Sample",
});
Then, for each request to the youtube service, your OAuth access token will be included as an additional header on the HTTP request itself.
Related
I'm trying to upload files using the Google Drive API and am getting a URI mismatch error from Google when clicking the upload button on my page. The URI that Google shows isn't even a part of the website, nor is a URI that I supplied to Google, so I have no idea where it's coming from.
Here is the APIHelper class I created based off of this tutorial (which shows that the code should work on a website)
public class GoogleDriveAPIHelper
{
//add scope
public static string[] Scopes = { DriveService.Scope.Drive };
//create Drive API service.
public static DriveService GetService()
{
//get Credentials from client_secret.json file
UserCredential credential;
//Root Folder of project
var CSPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
using (var stream = new FileStream(Path.Combine(CSPath, "client_secret.json"), FileMode.Open, FileAccess.Read))
{
string FolderPath = System.Web.Hosting.HostingEnvironment.MapPath("~/");
string FilePath = Path.Combine(FolderPath, "DriveServiceCredentials.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(FilePath, true)).Result;
}
//create Drive API service.
DriveService service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Documents Uploader",
});
return service;
}
//file Upload to the Google Drive.
public static void UploadFile(string folderID, HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
//create service
DriveService service = GetService();
string path = Path.Combine(HttpContext.Current.Server.MapPath("~/GoogleDriveFiles"),
Path.GetFileName(file.FileName));
file.SaveAs(path);
var FileMetaData = new Google.Apis.Drive.v3.Data.File
{
Name = Path.GetFileName(file.FileName),
MimeType = MimeMapping.GetMimeMapping(path),
//id of parent folder
Parents = new List<string>
{
folderID
}
};
FilesResource.CreateMediaUpload request;
using (var stream = new FileStream(path, FileMode.Open))
{
request = service.Files.Create(FileMetaData, stream, FileMetaData.MimeType);
request.Fields = "id";
request.Upload();
}
}
}
}
And the post
[HttpPost]
public ActionResult Index(HttpPostedFileBase file)
{
string folderID = "1L9QUUgmtg8KUdNvutQ1yncIwN_uLz4xs";
if (TempData["Success"] == null)
{
// show all fields
ViewBag.ShowForm = true;
ViewBag.ShowButtons = false;
}
else
{
// hide all elements on the page for success message
ViewBag.ShowForm = false;
ViewBag.ShowButtons = true;
}
GoogleDriveAPIHelper.UploadFile(folderID, file);
TempData["Success"] = "File successfully uploaded";
return View();
}
I have heard that the tutorial is referencing code that only works for standalone apps and not web apps, so it's odd that the screenshots in the tutorial are from a website. shrug I'll keep looking for tips and tricks, but in the meantime, I'm posting this to see if anyone else has written a site to upload through the Google drive to a specific folder, not the root. TIA!
Edit: Here are screenshots of the redirect URI I set up in the Google Cloud Console. Prod & localhost
Edit: Startup.Auth.cs - this is used for pass through ADFS authentication and has nothing to do with the Google Drive API
private void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(
new CookieAuthenticationOptions
{
// TempData and Owin don't get along, use this workaround to force a custom cookie manager
// https://stackoverflow.com/questions/28559237/intermittent-redirection-loops-during-adfs-authentication
CookieManager = new SystemWebCookieManager()
});
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = ConfigurationManager.AppSettings["ida:Wtrealm"],
MetadataAddress = ConfigurationManager.AppSettings["ida:ADFSMetadata"]
});
}
The realm matches the URI in the Google console and the metadata is the same xml link I use in all my web apps that use ADFS pass through auth, which has worked flawlessly. Nothing in my web.config file mention the IP address that Google says is my redirect URI either.
The URI that Google shows isn't even a part of the website, nor is a URI that I supplied to Google, so I have no idea where it's coming from.
The redirect uri is built buy the client library you are using. Your app is set to run http not https its running localhost and not hosted so its 127.0.0.1 the port is also either being randomly generated by your app or something that you have set up statically. the /authorize is attached again by the client library.
The redirect uri is the location your code is prepared to accept the response from the authorization server. This URI needs to be configured in Google cloud console. The easiest solution is to copy it exactly and add it as a redirect uri in Google cloud console. Just make sure that your app is set to use a static port if the port changes its not going to work.
This video will show you how to add it. Google OAuth2: How the fix redirect_uri_mismatch error. Part 2 server sided web applications.
Web applications
public void ConfigureServices(IServiceCollection services)
{
...
// This configures Google.Apis.Auth.AspNetCore3 for use in this app.
services
.AddAuthentication(o =>
{
// This forces challenge results to be handled by Google OpenID Handler, so there's no
// need to add an AccountController that emits challenges for Login.
o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// This forces forbid results to be handled by Google OpenID Handler, which checks if
// extra scopes are required and does automatic incremental auth.
o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
// Default scheme that will handle everything else.
// Once a user is authenticated, the OAuth2 token info is stored in cookies.
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddGoogleOpenIdConnect(options =>
{
options.ClientId = {YOUR_CLIENT_ID};
options.ClientSecret = {YOUR_CLIENT_SECRET};
});
}
Using .net, I'm trying to make a request to the Google ScriptService, however I keep getting this error "Request is missing required authentication credential", although I am including the credential. In fact I use the same credential not too long before to successfully make a request to the YoutubeService.
Below is my code, it actually used to work, so I'm not sure what has changed:
Scopes = new string[] { ScriptService.Scope.Forms, ScriptService.Scope.Spreadsheets,
ScriptService.Scope.Drive, YouTubeService.Scope.YoutubeUpload, YouTubeService.Scope.Youtube };
UserCredential credential;
using (var stream = new FileStream(#"Resources\client_secret.json", FileMode.Open, FileAccess.Read))
{
var credPath = Path.Combine(parentDir, ".credentials/" + folderName);
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
}
// Create Google Apps Script Execution API service.
var service = new ScriptService(new BaseClientService.Initializer()
{
HttpClientInitializer = this.credential,
ApplicationName = Properties.Resources.ApplicationName,
});
// Create an execution request object.
ExecutionRequest request = new ExecutionRequest();
request.Function = "createForm";
request.Parameters = new List<object>();
request.Parameters.Add(this.id);
request.Parameters.Add(name);
request.Parameters.Add(email);
request.Parameters.Add(this.link);
ScriptsResource.RunRequest runReq = service.Scripts.Run(request, Globals.Script_ID);
try
{
// Make the API request.
Operation op = runReq.Execute();
catch (Google.GoogleApiException e)
{
Debug.WriteLine("Error calling API:\n{0}", e.ToString());
}
I have enabled the API and generated OAuth 2.0 credentials for my platform in the developer console. The client_secret.json is the OAuth 2.0 credential that I downloaded from my console.
Any thoughts on what could be going wrong? I recall having a similar issue after updating my google packages, however in this instance I did not do so. I also tried updating the packages and still got the same issue.
The issue was that I did not have the correct authorization scopes. Originally I had:
Scopes = new string[] { ScriptService.Scope.Forms, ScriptService.Scope.Spreadsheets,
ScriptService.Scope.Drive, YouTubeService.Scope.YoutubeUpload, YouTubeService.Scope.Youtube };
However, upon checking the "Scopes" tab in my apps script Project Properties, I was missing the "userinfo.email" scope. And so, I updated my code in the following way:
Scopes = new string[] { ScriptService.Scope.Forms, ScriptService.Scope.Spreadsheets,
ScriptService.Scope.Drive, ScriptService.Scope.UserinfoEmail, YouTubeService.Scope.YoutubeUpload, YouTubeService.Scope.Youtube };
My guess is that the API had been updated since I originally wrote my script some months ago, as I was able to successfully run it at that time with the three original authorizations that I had.
I implemented login process successfully in my Xamarin forms app using Xamarin.auth. now I want to connect to Google APIs and upload AppData. here is the Code I tried,
I tread to fetch the GoogleCredential using token and providing this Credential to Google API but it failed.
var store = AccountStore.Create();
var SavedAccount = store.FindAccountsForService(GoogleDriveBackup.Auth.Constants.AppName).FirstOrDefault();
GoogleCredential credential2 = GoogleCredential.FromAccessToken(SavedAccount.Properties["access_token"]);
var driveService = new Google.Apis.Drive.v3.DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential2,
ApplicationName = "myApp",
});
FilesResource.CreateMediaUpload request;
var filePath = Path.Combine(path, filename);
using (var stream = new System.IO.FileStream(filePath,
System.IO.FileMode.Open))
{
request = driveService.Files.Create(
fileMetadata, stream, "application/json");
request.Fields = "id";
request.Upload();
}
var file = request.ResponseBody;
request.ResponseBody is always null. I thought that it has something to do with credentials.
I tried using
var store = AccountStore.Create();
var SavedAccount = store.FindAccountsForService(GoogleDriveBackup.Auth.Constants.AppName).FirstOrDefault();
var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "xxxx-xxx.apps.googleusercontent.com",
ClientSecret = "xxxxxxxx"
}
});
Google.Apis.Auth.OAuth2.Responses.TokenResponse responseToken = new Google.Apis.Auth.OAuth2.Responses.TokenResponse()
{
AccessToken = SavedAccount.Properties["access_token"],
ExpiresInSeconds = Convert.ToInt64(SavedAccount.Properties["expires_in"]),
RefreshToken = SavedAccount.Properties["refresh_token"],
Scope = DriveService.Scope.DriveAppdata,
TokenType = SavedAccount.Properties["token_type"],
};
var token= SavedAccount.Properties["access_token"];
var credential = new UserCredential(flow, "", responseToken);
But above case requires Client Secret which I don't have as I created "Android App" in the google console and signed in using on ClientId. So I read somewhere that I should create "Others" in the google console and use ClientId and Client Secret from there which makes not much sense to me because I am logged in with different client id's. Anyway, I tried that also but the response was null.
So what is the deal here? How can achieve my goal?
The google .net client library doesn't support xamarin. I am actually surprised you got it working this far. The main issue you are going to have is the authentication as you have already noticed the credential type for the .net client library is going to be either browser, native or api key. The mobile (Android Ios) clients arnt going to work as you dont have a secret the method of authentication is different and the client library doesn't have ability to do this.
The only suggestion i would have would be to work out Oauth2 authentication with xamarin on your own and then build the TokenResponse as you are doing now. You may then be able to feed that token to the Google .net client library if you can get the dlls into your project.
To my knowlage we have no plans to support xamarinwith the Google .net clinet library in the near future please see 984 840 1167
I've programmed to my API has a service account in my application, it works fine, all connection, upload, download and delete stuff, but when I used to use User Service, all files goes to my personal drive, now it goes somewhere, I think it goes to Google Cloud Platform...
The question is, I don't have any account over there, because you need to pay to use that, so, does anyone knows where all these files goes?
Here the code I'm using to make a connection call
public static DriveService Connection(string path, string username, string p12Path)
{
var certificate = new X509Certificate2(p12Path, "XXXXXXXX", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("g service account")
{
Scopes = Scopes
}.FromCertificate(certificate));
DriveService service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName
});
return service;
}
And here the method I'm using to upload a file.
[Authorize]
public static Google.Apis.Drive.v3.Data.File Upload(DriveService service, string uploadFile, string name)
{
var body = new Google.Apis.Drive.v3.Data.File();
body.Name = name;
body.MimeType = GetMimeType(uploadFile);
byte[] byteArray = System.IO.File.ReadAllBytes(uploadFile);
System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);
FilesResource.CreateMediaUpload request = service.Files.Create(body, stream, GetMimeType(uploadFile));
request.Upload();
return request.ResponseBody;
}
So, can anyone help me?
As stated in this thread, the code is the same and there is no difference if you are using Oauth2 or a service account. You may check with this tutorial. Also based from this related post, if you want uploaded files to be in your own Drive contents, then you need to use your own account credentials to the Drive SDK. This does not need to involve user interaction. You simply need to acquire a refresh token one time, then use that subsequently to generate the access token for Drive. Hope this helps!
I want to tried to get youtube live broadcast id. But I can't get this.
Here is my code:
UserCredential credential;
Response.Write("AAA");
var stream2 = new FileStream("c:/users/gislap/documents/visual studio 2012/Projects/youtube/secrect.json", FileMode.Open, FileAccess.Read);
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream2).Secrets,
new[] { YouTubeService.Scope.Youtube },
"user",
CancellationToken.None,
new FileDataStore(this.GetType().ToString())
);
Response.Write("DDD");
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = this.GetType().ToString()
});
var my_video_request = youtubeService.LiveBroadcasts.ToString();
Label1.Text = my_video_request.ToString();
Or any way to get all videos list?
You may refer on this thread. If you want to retrieve information on another channel's current live broadcasts, you have to use the standard Search/list endpoint:
part -> snippet
channelId -> [channelId of the channel/user with the live event]
eventType -> live
type -> video (required when setting eventType to live)
HTTP GET https://www.googleapis.com/youtube/v3/search?part=snippet&channelId={channelId}&eventType=live&type=video&key={YOUR_API_KEY}
Also, based from this documentation, try to use this HTTP request to return a list of YouTube broadcasts that match the API request parameters.
GET https://www.googleapis.com/youtube/v3/liveBroadcasts
Here are examples which might help:
https://developers.google.com/youtube/v3/live/code_samples/
https://github.com/search?l=C%23&q=LiveBroadcasts&type=Code&utf8=%E2%9C%93