Java - YouTube API - setPublishAt on video leads to "400 Bad Request - invalidVideoMetadata" - java

I'm trying to save a video via the YouTube API. Basically it is already working fine, just for one exception: If I try to set the status.setPublishAt() I get
400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "youtube.video",
"location" : "body",
"locationType" : "other",
"message" : "The request metadata is invalid.",
"reason" : "invalidVideoMetadata"
} ],
"message" : "The request metadata is invalid."
}
The code is as follows:
YouTube.Videos.List listVideosRequest = M_YOUTUBE.videos().list("snippet,status").setId(_dbVideo.getYoutubeId());
VideoListResponse listResponse = listVideosRequest.execute();
List<Video> videoList = listResponse.getItems();
if (videoList.isEmpty()) {
return false;
}
Video video = videoList.get(0);
VideoStatus status = video.getStatus();
status.setPrivacyStatus(_dbVideo.getPrivacyStatus()); // "private"
String sPublishedAt = _dbVideo.getPublishedAt();
// sPublishAt is in ISO 8106: "2016-10-28T10:01:00.000+02:00"
if (sPublishedAt != null && sPublishedAt != "") {
// this line leads to the bad request.
status.setPublishAt(new DateTime(sPublishedAt));
} else {
status.setPublishAt(null);
}
VideoSnippet snippet = video.getSnippet();
String sTitle = _dbVideo.getTitle();
String sDescription = _dbVideo.getDescription();
String sTags = _dbVideo.getTags();
...
snippet.setTitle(sTitle);
snippet.setDescription(sDescription);
snippet.setTags(tagList);
snippet.setCategoryId(_dbVideo.getCategoryId());
YouTube.Videos.Update updateVideosRequest = M_YOUTUBE.videos().update("snippet,status", video);
updateVideosRequest.execute();
For the uploading process I am using mostly the same code and it is working there. If I decide not to set the publishAt property with a date and instead set it to null it is working fine as well. Am I missing something?
Update:
The beheviour only occurs if the video was set to public once. If i decide to set it back to private and set a new publishAt i get the BadRequest.

You have to sent all the status values, otherwise the update method will think you are trying to empty them. You have send setPrivacyStatus and publishat. But you should send all other parameter to your request. Also, if you send publishat time too close to current time it will show badRequest (400) invalidPublishAt. try to put 60 mins. It works for me.

Related

Difficulties sending multipart/form-data request via Postman

please, this is y concern: I'll like to know how to query a web service defined as in the below code using postman for test purposes.
PS: I can't change the method signature
I have a web service like this :
#POST
#Path("/uploadOpenAccountRequestFiles/{requestId}")
#Consumes({MediaType.APPLICATION_JSON,MediaType.MULTIPART_FORM_DATA})
public Response uploadOpenAccountRequestFiles(#PathParam("requestId") String requestId,
MultipartFormDataInput multipartFormDataInput)
throws JsonGenerationException, JsonMappingException, IOException {
initContext();
logger.info("DIGIBANK : uploadOpenAccountRequestFiles END: ");
String msge = "";
try {
digiBean.saveToserver(requestId, multipartFormDataInput);
} catch (Exception e) {
msge = e.getMessage();
e.printStackTrace();
}
org.af.webservice.Response resp = new org.af.webservice.Response(
request.getSession().getId(), "uploadOpenAccountRequestFiles", "",
msge.equalsIgnoreCase("OK") ? true : false, msge.equalsIgnoreCase("OK") ? false : true, "",
msge.equalsIgnoreCase("OK") ? true : false, "Boolean", msge);
logger.info("DIGIBANK : uploadOpenAccountRequestFiles END: ");
return Response.ok().entity(mapper.writeValueAsString(resp)).build();
}
this is are images of my configurations:
enter image description here
enter image description here
For calling that service, you need to pass requestId like below:
http://localhost:8080/uploadOpenAccountRequestFiles/requestId-value-here
For sending MultiPart data such as a file, you need to select form-data option in the body section in Postman and select the file by selecting the File dropdown. Also, make sure to set the appropriate headers for the request.
Check the below stackoverflow answer for more details:
Tool for sending multipart/form-data request
The steps of uploading a file through postman along with passing some input data along with the multipart request is very well discussed in below blog along with the screenshot. In this blog, the api code is written in node js. You can go through it once. It may give some information.
https://jksnu.blogspot.com/2021/09/how-to-create-post-request-with.html

check nested key existence in video object in youtube-api to avoid NULLPOINTEREXCEPTION

I want to check the existence of nested key in Video object returned as a Json response from youtube video search by using below code:-
YouTube.Videos.List searchvideostats = youtube.videos().list("snippet,statistics");
searchvideostats.setKey(apiKey);
Video v = searchvideostats.execute().getItems().get(0);
System.out.println(v.toPrettyString());
I got output like this:-
{
"etag" : "\"m2yskBQFythfE4irbTIeOgYYfBU/-TONXAYMx_10Caihcoac4XCyb4I\"",
"id" : "4Aa9GwWaRv0",
"kind" : "youtube#video",
"snippet" : {
"categoryId" : "10",
...................
.........
MY goal is:- how to check whether categoryId key is present in this response or not. coz if do v.getSnippet().getCategoryId() it gives NullPointerException if categotyId is not present in Json.
Tried:-
if (v.containsKey("id")) {
System.out.println("contains");
} else {
System.out.println("doesnt contains");
}
this returns contains as expected.
if (v.containsKey("categoryId")) {
System.out.println("contains");
} else {
System.out.println("doesnt contains");
}
This returns doesnt contains.. which is not expected. How would I check if this nested key is available?
P.S. -> I have to check many nested such keys.
Thanks for help.
You don't need String manipulations. Just use Youtube library.
if(video.getSnippet()!=null && video.getSnippet().getCategoryId()!=null)
will do the stuff.
note: checking for zero length categoryid might be necessary also.

Gmail api scope & format mismatch

I'm trying to write tiny gmail client for android as training.
I took gmail api guide sample from https://developers.google.com/gmail/api/quickstart/android modified it a little to get messages with headers & body by threads. I set scopes to GmailScopes.Gmail_modify and edited main request function as this:
private List<String> getDataFromApi() throws IOException {
// Get the labels in the user's account.
String user = "me";
List<String> labels = new ArrayList<String>();
ListLabelsResponse listResponse =
mService.users().labels().list(user).execute();
ListThreadsResponse listThreads = null;
try {
listThreads = mService.users().threads().list(user).execute();
} catch (IOException ioex){
Log.e(LOG_TAG, "First: " + ioex.toString());
}
for (Thread thread : listThreads.getThreads()) {
try {
thread = mService.users().threads().get(user, thread.getId()).setFormat("full").execute();
} catch (IOException ioex){
Log.e(LOG_TAG, "Second: " + ioex.toString());
}
for(Message message : thread.getMessages()){
labels.add(message.getId());
}
}
return labels;
}
But I always get
Second: GoogleJsonResponseException: 403 Forbidden {
"code" : 403,
"errors" : [ {
"domain" : "global",
"message" : "Metadata scope doesn't allow format FULL",
"reason" : "forbidden"
} ],
"message" : "Metadata scope doesn't allow format FULL"
}
I tried different scopes configurations but seems like service scope is always set to GmailScopes.GMAIL_METADATA
This is exactly my problem these day when playing with Google APIs Explorer. And here is what I did to solve it:
Go to https://security.google.com/settings/security/permissions
Choose the app you are playing with, mine is Google APIs Explorer
Click Remove > OK
Next time, just request exactly which permissions you need.
Hope it help :)
you should remove the "metadata" scope.
check app permissions to make sure you have only these 3 scopes:
https://mail.google.com/
gmail.modify
readonly
, or else remove the permissions and add them again.
After getting permissions to device Contacts you have to approve chosen copes. So first time I approved metadata scope. Next times when I needed to approve readonly scope, there was no window to do it. So you need to delete scopes permissions from google account and reinstall app.
got the same error when a service account which i was using had only these scopes ( Security > API Controls > Domain-wide Delegation ):
https://www.googleapis.com/auth/gmail.readonly
https://www.googleapis.com/auth/gmail.metadata
and addition of
https://mail.google.com/
solved the issue

Google API Create Instance - Invalid value for field 'resource.machineType'

I am trying to create an instance with the Google Java API however I am getting an error:
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"message" : "Invalid value for field 'resource.machineType': 'https://www.googleapis.com/compute/v1/projects/...snip.../zones/europe-west1-a/machineTypes/n1-standard-1'. Cross-project references for this resource type are not allowed.",
"reason" : "invalid"
} ],
"message" : "Invalid value for field 'resource.machineType': 'https://www.googleapis.com/compute/v1/projects/...snip.../zones/europe-west1-a/machineTypes/n1-standard-1'. Cross-project references for this resource type are not allowed."
}
The code I am using:
Compute compute = getComputeService();
// uri reveals: https://www.googleapis.com/compute/v1/projects/...projectid.../zones/europe-west1-a/machineTypes/n1-standard-1
String uri = GOOGLE_BASE + this.project + "/zones/" + zone + INSTANCETYPES + type;
Instance instance = new Instance();
instance.setName(name);
instance.setZone(zone);
instance.setMachineType(uri);
List<AttachedDisk> disks = new ArrayList<AttachedDisk>();
AttachedDisk boot = new AttachedDisk();
boot.setAutoDelete(false);
boot.setType(disk.getType());
boot.setBoot(true);
boot.setDeviceName(disk.getName());
disks.add(boot);
List<NetworkInterface> interfaces = new ArrayList<NetworkInterface>();
NetworkInterface nat = new NetworkInterface();
nat.setName("External NAT");
nat.setNetwork("ONE_TO_ONE_NAT");
instance.setNetworkInterfaces(interfaces);
instance.setDisks(disks);
Instances.Insert request = compute.instances().insert(name, zone, instance);
Operation result = request.execute();
As per this link, I believe that your request should look like:
Instances.Insert request = compute.instances().insert(this.project, zone, instance);

Call 2 Ajax calls in one using Jquery Ajax

I have a problem in posting the selected values to the database with mismatching values.Here is the scenario where the ajax call maps to the respective Java function. The problem is, I am not getting correct selected values in the DB when the user logs in for the first time. Kindly provide a solution....
Here is my Jquery Ajax
if(selectedproductIds != '')
{
$.ajax({
url : "selectedProducts",
data : "selectedproducts="+selectedproductIds,
type : "POST",
success : function(data) {
}
});
$.ajax({
url : "<c:out value= "${saveDemoURL}"/>",
data : request,
type : "POST",
success : function(data) {
showNotification({
message : "",
type : "success",
autoClose : true,
duration : 5
});
resetForm();
alert("Demo Request Saved Successfully");
}
});
The first ajax call maps to this java function
#RequestMapping(value = "/selectedProducts")
public #ResponseBody
String getSelectedProducts(
#RequestParam(value = "selectedproducts") String[] selectedproducts,
Map<Object, Object> map) {
List<Product> selectedProd = new ArrayList<Product>();
for (String prod : selectedproducts) {
Product product = new Product();
product.setId(Integer.parseInt(prod));
selectedProd.add(product);
}
if (!Util.isEmpty(selectedProd)) {
map.put("selectedproducts", selectedProd);
}
for (Product product: selectedProd) {
LOGGER.info("Demo ID:"+ " List of selected products:"+product.getId());
}
selectedProdList = selectedProd;
return "success";
}
The second Ajax call maps to this java function
#RequestMapping(value = "/saveDemo")
public #ResponseBody
Map<Object, Object> saveDemo(#ModelAttribute("demoBean") DemoBean demoBean,
Model model, Map<Object, Object> map) {
Map<Object, Object> output = null;
Demo demo = new Demo();
try{
......
}
catch{.....}
return output;
}
Change your code to this format: $.ajax().ajax;:
if(selectedproductIds != '')
{
$.ajax({
url : "selectedProducts",
data : "selectedproducts="+selectedproductIds,
async: false,
type : "POST",
success : function(data) {
}
}).ajax({
url : "<c:out value= "${saveDemoURL}"/>",
data : request,
async: false,
type : "POST",
success : function(data) {
showNotification({
message : "",
type : "success",
autoClose : true,
duration : 5
});
resetForm();
alert("Demo Request Saved Successfully");
}
});
I'm not 100% certain I understand what your two functions are doing, but if I'm right and you're trying to perform something which would normally be wrapped in a transaction then you do not want to coordinate that from the front-end at all. A classic example of a transaction is taking money out of one person's account and putting it into another. If that process gets interrupted in the middle due to power failure, laptop reboot, network problems, etc. then the whole thing needs to go back to its original state, otherwise money can just disappear from one account and never go anywhere.
I want to refer you to this question: jQuery deferred chaining problems
It's a little different because the author has already figured out that promises may be a key to getting things to happen together. But notice that I answer it twice. The second time is to specifically to explain that transactions should never be coordinated from the front-end. If you have two database operations that must always complete together then you must have only one API call which receives all the data needed for both calls and you do them together on the back-end. Technically, even then you should use something like database transactions on the back-end to wrap them so a power failure back there won't leave you with half an operation completed. But failures in the middle of your multi-step processes are 1000% more likely if you try to execute them across the network between a browser and a server. Get that logic into the server where it belongs.

Categories