I am using the Google Speech API to transcribe streaming audio. I would like to receive the result transcript. I set MaxAlternatives = 0 so I won't receive interim results, only the final transcription result. I also set SingleUtterance = true. My understanding is that I will get the first response as SpeechEventType = EndOfSingleUtterance and then the second and final response will contain the results, where I can access the transcription. Below is the code I have tested (slightly adapted from the docs: https://cloud.google.com/speech-to-text/docs/streaming-recognize). However, the two foreach loops are not very elegant and I imagine would only be necessary if InterimResults = true. So how can I receive the single transcription result from a streaming request as described above? I notice that there is about three seconds between when I finish talking, and when the transcript is received so I think that there is something wrong with how I am reading the results.
while (await streamingCall.ResponseStream.MoveNext(default(CancellationToken)))
{
foreach (var result in streamingCall.ResponseStream.Current.Results)
{
foreach (var alternative in result.Alternatives)
{
//Do stuff with alternatvie.Transcript
}
}
}
Related
Using express and Node.js, I'm using the twitter streaming API and the needle npm package for accessing APIs to pull tweets related to keywords. The streaming is functional and I am successfully pulling tweets using the following (simplified) code:
const needle = require('needle');
const TOKEN = // My Token
const streamURL = 'https://api.twitter.com/2/tweets/search/stream';
function streamTweets() {
const stream = needle.get(streamURL, {
headers: {
Authorization: `Bearer ${TOKEN}`
}
});
stream.on('data', (data) => {
try {
const json = JSON.parse(data); // This line appears to be causing my error
const text = json.data.text;
} catch (error) {
console.log("error");
}
});
}
However, no matter which search term I use (and the subsequent large or small volume of tweets coming through), the catch block will consistently log 1-3 errors per minute, which look like this:
SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at PassThrough.<anonymous> (C:\Users\danie\OneDrive\Documents\Personal-Projects\twitter-program\server.js:56:31)
at PassThrough.emit (events.js:315:20)
at addChunk (internal/streams/readable.js:309:12)
at readableAddChunk (internal/streams/readable.js:284:9)
at PassThrough.Readable.push (internal/streams/readable.js:223:10)
at PassThrough.Transform.push (internal/streams/transform.js:166:32)
at PassThrough.afterTransform (internal/streams/transform.js:101:10)
at PassThrough._transform (internal/streams/passthrough.js:46:3)
at PassThrough.Transform._read (internal/streams/transform.js:205:10).
I've seen previous advice which says that data can be fired in multiple chunks, and to push the chunks to an array i.e. something like the following:
let chunks = [];
stream.on('data', (dataChunk) => {
chunks.push(dataChunk);
}).on('end',() => {
// combine chunks to create JSON object
})
But this didn't work either (may have been my implementation but I don't think so) and now I'm wondering if it's perhaps an error with the twitter API, because most of the tweet objects do come through correctly. I should note that the streamTweets() function above is called from an async function, and I am also wondering if that is having something to do with it.
Has anyone else encountered this error? Or does anyone have any idea how I might be fix it? Ideally i'd like 100% of the tweets to stream correctly.
Thanks in advance!
For future readers, this error is triggered by Twitter's heartbeat message that is sent every 20 seconds. Per the documentation:
The endpoint provides a 20-second keep alive heartbeat (it will look like a new line character).
Adding a guard against parsing the empty string will prevent the JSON parsing error.
if (data === "")
return
An empty string is invalid JSON, hence the emitted error.
Now, acknowledging that the heartbeat exists, it may be beneficial to add read_timeout = 20 * 1000 in the needle request to avoiding a stalled program with no data, be that due to a local network outage or DNS miss, etc.
I can see how to trigger the calculation of a spreadsheet using Microsoft Graph API here...
https://learn.microsoft.com/en-us/graph/api/workbookapplication-calculate?view=graph-rest-1.0&tabs=http
But, when I pull the results from the calculations they don't seem to be updated. However, I pull a second or third time, it is usually updated.
I assume this means that calculation hasn't finished b/c of the size of the file or complexity of the calcs.
However, being asynchronous, I'm not finding any way to check to see when the calculations have finished.
Any idea how to do this?
UPDATE 1:
This is the code I'm (now) using to create the session (per #UJJAVAL123-MSFT)
var persistChanges = false;
var wrkbk = await graphClient.Me.Drive.Items[strItemId].Workbook
.CreateSession(persistChanges)
.Request()
.PostAsync();
This will give me a value for 'id' like this...
cluster=US5&session=15.SN3PEPF000074ED1.A82.1.V24.7737Nwad4oPuVafaO%2fpAkiay14.5.en-US5.en-US26.10037ffe965d2cf2-Unlimited1.A1.N16.16.0.12904.3505114.5.en-US5.en-US1.V1.N0.1.A&usid=1b230e8f-3bd0-cdaa-2da6-47db74154075
I'm not exactly sure how/where to use this, or whether I use the entire thing (it looks like a query string) or if I'm supposed to parse and pull out one of the values...
cluster=US5
session=15.SN3PEPF000074ED1.A82.1.V24.xxxxxxxxxxxxxxxxx%2fpAkiay14.5.en-US5.en-US26.10037ffe965d2cf2-Unlimited1.A1.N16.16.0.12904.3505114.5.en-US5.en-US1.V1.N0.1.A
usid=1b230e8f-3bd0-cdaa-2da6-xxxxxxxxxxxx
and this is the code i'm using to trigger the calculation, but not sure how to connect the two...
var calculationType = "FullRebuild";
await graphClient.Me.Drive.Items[strItemId].Workbook.Application
.Calculate(calculationType)
.Request()
.PostAsync();
Also, I'm seeing that it is possible to create, refresh and close a session, but not entirely sure how to check on a specific async process inside that session.
Here is the code I'm using to check a specific range for a value, not sure where we pass the session-id here either...
var result = await graphClient.Me.Drive.Items[strItemId].Workbook.Worksheets[strSheetName]
.Range(strRangeName)
.Request()
.GetAsync();
UPDATE 2:
I can run an API call (presumably) in the same session successfully by passing the workbook-session-id (which is the ENTIRE string shown above) and I get the expected 204 No Content response. However, it is not clear from the c# Code Snippet in the Microsoft Graph Explorer how to pass the workbook-session-id in the request.
Here is the code it provides...
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
await graphClient.Me.Drive.Items["{item-id}"].Workbook.Application
.Calculate(null)
.Request()
.PostAsync();
So the question remains, how can I do a PostAsync or GetAsync and reference the workbook-session-id?
This code does NOT give me an error...
await graphClient.Me.Drive.Items[strItemId].Workbook.Application
.Calculate(calculationType)
.Request()
.Header("workbook-session-id",wrkbk.Id)
.PostAsync();
So now, the question is WHEN do I get the workbook-session-id? Do I get it when I initially open the workbook and then pass it to every call?
You should create a session and pass the session Id with each request. The presence of a
session Id in the requests ensures that you are using the Excel API in the most efficient
way possible.
Check here for API call to get a session
So after a decent amount of testing, i figured it out.
The answer is that you use the CreateSession method (https://learn.microsoft.com/en-us/graph/api/workbook-createsession?view=graph-rest-1.0&tabs=http) to get the workbook info, and you set the persistChanges setting, then you get back info about the workbook session.
Like this...
using Microsoft.Graph;
// strItemId = the id from the microsoft graph api of the item
// strUserId = the id of the user from the microsoft graph api (note: must have permissions set correctly)
public static async Task<WorkbookSessionInfo> GetWorkbookSessionId(string strItemId, string strUserId)
{
// true = you can see changes in the workbook
// false = don't update the workbook, just do calculations
var persistChanges = true;
try
{
var wrkbk = await graphClient.Users[strUserId].Drive.Items[strItemId].Workbook
.CreateSession(persistChanges)
.Request()
.PostAsync();
var result = wrkbk;
return result;
}
catch (Exception ex)
{
Console.WriteLine($"Error getting items: {ex.Message}");
return null;
}
}
And you are returned a WorkbookSessionInfo object, which includes the SessionId for use in subsequent calls. This way, it keeps all your calls in the same session!
https://learn.microsoft.com/en-us/graph/api/resources/workbooksessioninfo?view=graph-rest-1.0
So for the YouTube Channel Mindless Self Indulgence it has 4 sections on the home tab first section is they're music videos playlist, the 2nd section is albums which is a group of different playlists, then another playlist section and the last section is there uploads.
But when I do a channelSections api call I get like 20 different items and it has me scratching my head why.
Here's the api response https://notepad.pw/raw/w27ot290s
https://www.googleapis.com/youtube/v3/channelSections?key={KEYHERE}&channelId=UChS8bULfMVx10SiyZyeTszw&part=snippet,contentDetails
So I figured this out finally, I neglected to read the documentation on the channelSections api 😅
here: https://developers.google.com/youtube/v3/docs/channelSections
I was getting channel sections for all the regions where channel like music may more often have region specific sections... To filter these you need to also include the targeting object in the part parameter. If the section is region free (or atleast i assume) it won't have the targeting object so something to take into condertation when handling your api response and filter sectoins based on regions.
Here's my code just trying to get the data filtered in react app, not the most practical maybe but I fumbled through it:
const data = response2.data.items;
console.log("response2 data", data);
const filtered = data.filter(item => {
if (item.targeting === undefined) return true;
let test = false;
item.targeting.countries.forEach(i => {
if (i === "US") test = true;
});
return test;
});
I want to use youtube video:list api to get details of multiple videos in single request. As per the api documentation, I can send comma separated videoId list as id parameter. But what is the maximum length possible?
I know the GET request limit is dependent on both the server and the client. In my case I am making the request from server-side and not from browser. Hence the maximum length could be configured on my end. But what is the maximum length acceptable for youtube?
UPDATE: Though official documentation couldn't find, current limit is 50 ids from the tests performed as explained by Tempus. I am adding a code below with 51 different video ids (1 is commented) for those who want to check this in future.
var key = prompt("Please enter your key here");
if (!key) {
alert("No key entered");
} else {
var videoIds = ["RgKAFK5djSk",
"fRh_vgS2dFE",
"OPf0YbXqDm0",
"KYniUCGPGLs",
"e-ORhEE9VVg",
"nfWlot6h_JM",
"NUsoVlDFqZg",
"YqeW9_5kURI",
"YQHsXMglC9A",
"CevxZvSJLk8",
"09R8_2nJtjg",
"HP-MbfHFUqs",
"7PCkvCPvDXk",
"0KSOMA3QBU0",
"hT_nvWreIhg",
"kffacxfA7G4",
"DK_0jXPuIr0",
"2vjPBrBU-TM",
"lp-EO5I60KA",
"5GL9JoH4Sws",
"kOkQ4T5WO9E",
"AJtDXIazrMo",
"RBumgq5yVrA",
"pRpeEdMmmQ0",
"YBHQbu5rbdQ",
"PT2_F-1esPk",
"uelHwf8o7_U",
"KQ6zr6kCPj8",
"IcrbM1l_BoI",
"vjW8wmF5VWc",
"PIh2xe4jnpk",
"QFs3PIZb3js",
"TapXs54Ah3E",
"uxpDa-c-4Mc",
"oyEuk8j8imI",
"ebXbLfLACGM",
"kHSFpGBFGHY",
"CGyEd0aKWZE",
"rYEDA3JcQqw",
"fLexgOxsZu0",
"450p7goxZqg",
"ASO_zypdnsQ",
"t4H_Zoh7G5A",
"QK8mJJJvaes",
"QcIy9NiNbmo",
"yzTuBuRdAyA",
"L0MK7qz13bU",
"uO59tfQ2TbA",
"kkx-7fsiWgg",
"EgqUJOudrcM",
// "60ItHLz5WEA" // 51st VideoID. Uncomment it to see error
];
var url = "https://www.googleapis.com/youtube/v3/videos?part=statistics&key=" + key + "&id=" + videoIds.join(",");
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
(xmlHttp.readyState == 4) && alert("HTTP Status code: " + xmlHttp.status);
}
xmlHttp.open("GET", url, true);
xmlHttp.send(null);
}
The answer is 50. Reason being, is that is all you will get back.
As some calls can have quite a few results depending on search criteria and available results, they have capped the "maxResults" at 50.
Acception to this is the CommentThreads which are up to 100.
This is (as you can work out) to speed page loads and call times.
EDIT:
This can be tested out HERE in the "Try api" part.
You will need to put 50 videoID's into the "id" field separated by coma's.
Then ad one more ID to get 51 and test again. You should receive a "400" response.
P.S. they do not need to be unique ID's. So have a few and then copy and paste as many times as needed ;-)
I'm writing some code where, most commonly, no results will be returned from a query against Entity Framework. This request has been submitted by some jQuery code, and if I reply with "no results", it's just going to turn around and make the same request again - so I'd like to not respond until either some results are available, or a reasonable amount of time (e.g. 30 seconds) have passed (however, I don't want to cache results for 30 seconds - 30 seconds is a reasonable amount of time to not send a response to the query - if results become available, I want them available "immediately")
How do I best go about this. I tried sleeping between re-querying, but it a) doesn't seem to be working (every request that starts with no results waits the full 30 seconds), and b) will tie up an asp.net thread.
So how do I convert my code to not tie up asp.net threads, and to respond once results are available?
[HttpGet]
public ActionResult LoadEventsSince(Guid lastEvent, int maxEvents)
{
maxEvents = Math.Min(50, maxEvents); //No more than 50
using (var dbctxt = new DbContext())
{
var evt = dbctxt.Events.Find(lastEvent);
var afterEvents = (from et in evt.Session.Events
where et.OccurredAt > evt.OccurredAt
orderby et.OccurredAt
select new { EventId = et.EventId, EventType = et.EventType, Control = et.Control, Value = et.Value }).Take(maxEvents);
var cycles = 30;
while (afterEvents.Count() == 0 && cycles-- > 0)
{
System.Threading.Thread.Sleep(1000);
}
return Json(afterEvents.ToArray(), JsonRequestBehavior.AllowGet);
}
}
check out this mix 11 session: "Pragmatic JavaScript jQuery & AJAX with ASP.NET".
At the very end of it (about 40-45 minutes into the session) there is a demo right for you.
I'm prety sure you'll say wow..
Damian Edwards promissed to post more about the technique on his blog, but we are yet to see it..
See > Reverse ajax Comet/Polling implementation for ASP.NET MVC?.
You need to go with long polling. It basically sends a request to the server and the server just keeps it in the queue. It accumulates all the queries and as soon as it receives some data it sends the response to each of the queued requests.
EDIT: Also this is interesting > Comet implementation for ASP.NET?