I'm working on project that uses the YouTube api v3 with the Google .net client libary.
It's done, however sometimes it takes long time to respond when getting a video-list from playlist id. When this happens, it gives error "bad request" after 1 minute and tells me request timed out.
Is there any way to increase the request timeout or another solution for this problem?
List<YTVideo> videos = new List<YTVideo>();
var searchRequest = youtubeService.PlaylistItems.List("snippet");
searchRequest.PlaylistId = playlistId;
searchRequest.MaxResults = 1;
var searchResponse = await searchRequest.ExecuteAsync();
var playlistItemsInfo = searchResponse.Items.FirstOrDefault();
// to get videos from playlistItemsInfo
foreach (var searchResult in searchResponse.Items)
{
var videoSearchRequest = youtubeService.Videos.List("snippet, statistics, contentDetails");
videoSearchRequest.Id = searchResult.Snippet.ResourceId.VideoId;
videoSearchRequest.MaxResults = 1;
var videoSearchResponse = videoSearchRequest.Execute();
var video = videoSearchResponse.Items.FirstOrDefault();
if (video != null)
{
YTVideo yTVideo = new YTVideo
{
Title = video.Snippet.Title,
VideoId = video.Id,
Image = video.Snippet.Thumbnails.Maxres != null ? video.Snippet.Thumbnails.Maxres.Url : video.Snippet.Thumbnails.High.Url,
IsSelected = true
};
videos.Add(yTVideo);
}
}
My problem happens in searchResponse when I execute search the request.
Related
Trying to make use of the AndroidPublisherService from Play Developer API Client.
I can list active tracks and the releases in those tracks, but when I try to upload a new build there seems to be no way of attaching the authentication already made previously to read data.
I've authenticated using var googleCredentials = GoogleCredential.FromStream(keyDataStream) .CreateWithUser(serviceUsername); where serviceUsername is the email for my service account.
private static void Execute(string packageName, string aabfile, string credfile, string serviceUsername)
{
var credentialsFilename = credfile;
if (string.IsNullOrWhiteSpace(credentialsFilename))
{
// Check env. var
credentialsFilename =
Environment.GetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS",
EnvironmentVariableTarget.Process);
}
Console.WriteLine($"Using credentials {credfile} with package {packageName} for aab file {aabfile}");
var keyDataStream = File.OpenRead(credentialsFilename);
var googleCredentials = GoogleCredential.FromStream(keyDataStream)
.CreateWithUser(serviceUsername);
var credentials = googleCredentials.UnderlyingCredential as ServiceAccountCredential;
var service = new AndroidPublisherService();
var edit = service.Edits.Insert(new AppEdit { ExpiryTimeSeconds = "3600" }, packageName);
edit.Credential = credentials;
var activeEditSession = edit.Execute();
Console.WriteLine($"Edits started with id {activeEditSession.Id}");
var tracksList = service.Edits.Tracks.List(packageName, activeEditSession.Id);
tracksList.Credential = credentials;
var tracksResponse = tracksList.Execute();
foreach (var track in tracksResponse.Tracks)
{
Console.WriteLine($"Track: {track.TrackValue}");
Console.WriteLine("Releases: ");
foreach (var rel in track.Releases)
Console.WriteLine($"{rel.Name} version: {rel.VersionCodes.FirstOrDefault()} - Status: {rel.Status}");
}
using var fileStream = File.OpenRead(aabfile);
var upload = service.Edits.Bundles.Upload(packageName, activeEditSession.Id, fileStream, "application/octet-stream");
var uploadProgress = upload.Upload();
if (uploadProgress == null || uploadProgress.Exception != null)
{
Console.WriteLine($"Failed to upload. Error: {uploadProgress?.Exception}");
return;
}
Console.WriteLine($"Upload {uploadProgress.Status}");
var tracksUpdate = service.Edits.Tracks.Update(new Track
{
Releases = new List<TrackRelease>(new[]
{
new TrackRelease
{
Name = "Roswell - Grenis Dev Test",
Status = "completed",
VersionCodes = new List<long?>(new[] {(long?) upload?.ResponseBody?.VersionCode})
}
})
}, packageName, activeEditSession.Id, "internal");
tracksUpdate.Credential = credentials;
var trackResult = tracksUpdate.Execute();
Console.WriteLine($"Track {trackResult?.TrackValue}");
var commitResult = service.Edits.Commit(packageName, activeEditSession.Id);
Console.WriteLine($"{commitResult.EditId} has been committed");
}
And as the code points out, all action objects such as tracksList.Credential = credentials; can be given the credentials generated from the service account.
BUT the actual upload action var upload = service.Edits.Bundles.Upload(packageName, activeEditSession.Id, fileStream, "application/octet-stream"); does not expose a .Credential object, and it always fails with:
The service androidpublisher has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project. [401]
Errors [
Message[Login Required.] Location[Authorization - header] Reason[required] Domain[global]
]
at Google.Apis.Upload.ResumableUpload`1.InitiateSessionAsync(CancellationToken cancellationToken)
at Google.Apis.Upload.ResumableUpload.UploadAsync(CancellationToken cancellationToken)
So, how would I go about providing the actual Upload action with the given credentials here?
Managed to figure this out during the day, I was missing one call to CreateScoped() when creating the GoogleCredential object as well as a call to InitiateSession() on the upload object.
var googleCredentials = GoogleCredential.FromStream(keyDataStream)
.CreateWithUser(serviceUsername)
.CreateScoped(AndroidPublisherService.Scope.Androidpublisher);
Once that was done I could then get a valid oauth token by calling
var googleCredentials = GoogleCredential.FromStream(keyDataStream)
.CreateWithUser(serviceUsername)
.CreateScoped(AndroidPublisherService.Scope.Androidpublisher);
var credentials = googleCredentials.UnderlyingCredential as ServiceAccountCredential;
var oauthToken = credentials?.GetAccessTokenForRequestAsync(AndroidPublisherService.Scope.Androidpublisher).Result;
And I can now use that oauth token in the upload request:
upload.OauthToken = oauthToken;
_ = await upload.InitiateSessionAsync();
var uploadProgress = await upload.UploadAsync();
if (uploadProgress == null || uploadProgress.Exception != null)
{
Console.WriteLine($"Failed to upload. Error: {uploadProgress?.Exception}");
return;
}
The full code example for successfully uploading a new aab file to google play store internal test track thus looks something like this:
private async Task UploadGooglePlayRelease(string fileToUpload, string changeLogFile, string serviceUsername, string packageName)
{
var serviceAccountFile = ResolveServiceAccountCertificateInfoFile();
if (!serviceAccountFile.Exists)
throw new ApplicationException($"Failed to find the service account certificate file. {serviceAccountFile.FullName}");
var keyDataStream = File.OpenRead(serviceAccountFile.FullName);
var googleCredentials = GoogleCredential.FromStream(keyDataStream)
.CreateWithUser(serviceUsername)
.CreateScoped(AndroidPublisherService.Scope.Androidpublisher);
var credentials = googleCredentials.UnderlyingCredential as ServiceAccountCredential;
var oauthToken = credentials?.GetAccessTokenForRequestAsync(AndroidPublisherService.Scope.Androidpublisher).Result;
var service = new AndroidPublisherService();
var edit = service.Edits.Insert(new AppEdit { ExpiryTimeSeconds = "3600" }, packageName);
edit.Credential = credentials;
var activeEditSession = await edit.ExecuteAsync();
_logger.LogInformation($"Edits started with id {activeEditSession.Id}");
var tracksList = service.Edits.Tracks.List(packageName, activeEditSession.Id);
tracksList.Credential = credentials;
var tracksResponse = await tracksList.ExecuteAsync();
foreach (var track in tracksResponse.Tracks)
{
_logger.LogInformation($"Track: {track.TrackValue}");
_logger.LogInformation("Releases: ");
foreach (var rel in track.Releases)
_logger.LogInformation($"{rel.Name} version: {rel.VersionCodes.FirstOrDefault()} - Status: {rel.Status}");
}
var fileStream = File.OpenRead(fileToUpload);
var upload = service.Edits.Bundles.Upload(packageName, activeEditSession.Id, fileStream, "application/octet-stream");
upload.OauthToken = oauthToken;
_ = await upload.InitiateSessionAsync();
var uploadProgress = await upload.UploadAsync();
if (uploadProgress == null || uploadProgress.Exception != null)
{
Console.WriteLine($"Failed to upload. Error: {uploadProgress?.Exception}");
return;
}
_logger.LogInformation($"Upload {uploadProgress.Status}");
var releaseNotes = await File.ReadAllTextAsync(changeLogFile);
var tracksUpdate = service.Edits.Tracks.Update(new Track
{
Releases = new List<TrackRelease>(new[]
{
new TrackRelease
{
Name = $"{upload?.ResponseBody?.VersionCode}",
Status = "completed",
InAppUpdatePriority = 5,
CountryTargeting = new CountryTargeting { IncludeRestOfWorld = true },
ReleaseNotes = new List<LocalizedText>(new []{ new LocalizedText { Language = "en-US", Text = releaseNotes } }),
VersionCodes = new List<long?>(new[] {(long?) upload?.ResponseBody?.VersionCode})
}
})
}, packageName, activeEditSession.Id, "internal");
tracksUpdate.Credential = credentials;
var trackResult = await tracksUpdate.ExecuteAsync();
_logger.LogInformation($"Track {trackResult?.TrackValue}");
var commitResult = service.Edits.Commit(packageName, activeEditSession.Id);
commitResult.Credential = credentials;
await commitResult.ExecuteAsync();
_logger.LogInformation($"{commitResult.EditId} has been committed");
}
I have the problem with counting the messages of mailbox.
I use c# and Microsoft.Graph 1.18.0
Here is my code
public async Task<long> GetItemsCountAsync(string userId)
{
var countOption = new QueryOption("$count", "true");
var request = ServiceClient.Value.Users[userId].Messages.Request();
request.QueryOptions.Add(countOption);
var resultMessages = new List<Message>();
var count = 0L;
do
{
var messagesResult = await request.GetAsync();
if (messagesResult.AdditionalData != null && messagesResult.AdditionalData.TryGetValue("#odata.count", out var messagesCount))
{
count = (long)messagesCount;
}
resultMessages.AddRange(messagesResult);
request = messagesResult.NextPageRequest;
}
while (request != null);
return count;
}
And I have at the end count = 1417 and resultMessages.Count = 760
Did I miss something?
Thank you for any help!
Everything is fine with the provided example. It appears $count for List messages endpoint could not be trusted here since API does not return accurate count for messages from a specified search folder (refer, for example, this answer for a more details).
To get messages count List mailFolders endpoint could be utilized instead:
GET /users/{id | userPrincipalName}/mailFolders?$select=totalItemCount
where totalItemCount represents the number of items in the mail folder.
C# example
var folders = await graphClient.Users[userId].MailFolders.Request().Select(f =>f.TotalItemCount).GetAsync();
var totalMessagesCount = folders.Sum(folder => folder.TotalItemCount);
I am writing some simple C# code to try automatically getting HAR file from Chrome browser. I am using browser-mob-proxy and there is a function: GetHar() which is supposed to return some different entries of URL, request and response time, etc. However, it always return me only 1 entry which is the original URL I am negativing to: www.google.com
I've tried to use dr.Navigate().Refresh() to make sure the page is reloaded so there are some activities on chrome DevTool Network section.
server.Start();
Thread.Sleep(1000);
Client client = server.CreateProxy();
client.NewHar("google");
var chromeOptions = new ChromeOptions();
var seleniumProxy = new Proxy { HttpProxy = client.SeleniumProxy };
chromeOptions.Proxy = seleniumProxy;
var dr = new ChromeDriver(chromeOptions);
dr.Navigate().GoToUrl("http://www.google.com");
dr.FindElementByClassName("gb_e").Click();
Thread.Sleep(3500);
dr.Navigate().Refresh();
// Get the performance stats
HarResult harData = client.GetHar();
Log log = harData.Log;
Entry[] entries = log.Entries;
foreach (var e in entries)
{
Request request = e.Request;
Response response = e.Response;
var url = request.Url;
var time = e.Time;
var status = response.Status;
var testStr = "Url: " + url + " - Time: " + time + " Response: " + status;
}
I expected GetHar() function will return more entries instead of only 1.
Not sure why, but the issue has been resolved by adding SSL proxy:
var seleniumProxy = new Proxy { HttpProxy = client.SeleniumProxy , SslProxy = client.SeleniumProxy };
I am trying to perform paginated search on Active Directory using System.DirectoryServices.Protocols.PageResultRequestControl.
I do get the search results in pages, however, the searchResponse that I get does NOT have the correct TotalCount for total number of pages.
Is it not supported? Or am I missing something here?
This is sample code that I have used in order to implement above. I am using System.DirectoryServices.Protocols to query Active Directory.
When PageResultRequestControl is added with page number, everything works perfectly except for totalSize.
For example, in this code
LdapConnection connection = new LdapConnection(ldapDirectoryIdentifier, credential);
SearchRequest sr = new SearchRequest("", "(displayName=*)", System.DirectoryServices.Protocols.SearchScope.Subtree, new[] { "displayName"});
PageResultRequestControl pr = new PageResultRequestControl(50);
SearchOptionsControl so = new SearchOptionsControl(SearchOption.DomainScope);
sr.Controls.Add(pr);
sr.Controls.Add(so);
SearchResponse searchResponse;
while (true)
{
searchResponse = (SearchResponse)connection.SendRequest(sr);
if (searchResponse.Controls.Length != 1 || !(searchResponse.Controls[0] is PageResultResponseControl))
{
totalPageCount = 0;
return null;
}
PageResultResponseControl pageResponse = (PageResultResponseControl)searchResponse.Controls[0];
totalPageCount = pageResponse.TotalCount;
if (pageResponse.Cookie.Length == 0)
{
break;
}
else
{
pageRequest.Cookie = pageResponse.Cookie;
}
}
As documentation says, the TotalCount property contains the estimated result set count (https://technet.microsoft.com/en-us/library/system.directoryservices.protocols.pageresultresponsecontrol.totalcount)
I'm using the v3 Google YouTubeAPI to get all videos from a channel, but of the 3 available, only one is returned. Any idea what is going wrong? Here is the code for the function:
public List<Video> GetVideos()
{
var vids = new List<Video>();
YouTubeService youtube = new YouTubeService(new BaseClientService.Initializer());
SearchResource.ListRequest listRequest = youtube.Search.List("id,snippet");
listRequest.Key = WebConfigurationManager.AppSettings["youTubeKey"];
listRequest.ChannelId = WebConfigurationManager.AppSettings["youTubeChannel"];
listRequest.MaxResults = 25;
listRequest.Type = "video";
SearchListResponse resp = listRequest.Execute();
foreach (SearchResult result in resp.Items)
{
vids.Add(new Video(result.Id.VideoId, result.Snippet));
}
return vids;
}
I did verify that all 3 videos are public, and do play on YouTube.