I've "formally" released the new version of my YouTube CFC to it's RIAForge home. This new version doesn't have a one to one relation to the older API so I've included the old code in the zip as well. Right now it supports:
- Various 'top' video feeds (most popular, etc)
- Videos by category, keyword, or both
- Videos by user
- Comments for a video
- Playlists for a user
- Videos for a playlist
And of course the big one - video upload.
The major things left to do include posting new comments and updating video information. Hopefully I can get the YouTube API Blog folks to notice.
Archived Comments
Hey Ray, this is great - thank you! I was wondering if you planned to add a GetVideo function akin to the one in the CFC with the first API, so that you could grab all of the data about a specific video.
Regardless, this is really helpful - thank you!
Great CFC Ray! I have a quick question about error trapping on cfhttp.
I'm always concerned about timeouts / problems connecting to third party services - how would getVideos() tell the calling app "sorry, I couldn't contact the youtube website"
Could (should?) you change getVideos() to return a struct - one key has the results recordset, and one key has a "statuscode" message maybe?
Then put a timeout on the cfhttp call, and try/catch it?
just a thought...
I should have more error handling, but no, I don't believe in returning structs like that. I know some folks do - but in my mind, if an error occurs, I should cfthrow. However, I like to throw my own error, using a common type and message format, so you can look for it in the code that calls my cfc.
@Bob - the odd thing is that what getVideo used to do is mostly contained in the get* feeds. Ie, when you get a list of videos, it contains the same info, per video, that a singular getVideo would contain. I believe. I don't believe - and I have to research this more - that you can get just ONE video based on ID. I meant to actually look into that last night but forgot. I'll make that my next priority.
Thanks Ray. This morning I just used CFHTTP to grab and then XMLParse() to parse the individual video XML page at http://gdata.youtube.com/fe.... It's doing the job.
I didn't read the whole spec, but I do remember trying to find docs for getitng one video. Where did you find that? (I'm sure it works and I'll confirm/add it later, but I'd like to be sure it is SUPPOSED to work if you know what I mean.)
I actually used your CFC to pull the list of videos for the user. The ID column contained the feed URL for each video, which I pulled up in Firefox and found the detailed XML. So no, I can't confirm it's SUPPOSED to work.
I did some more looking last night, and it does seem to be kinda documented - when you look specifically at the Entry field it does talk about the links.
So I will add this later today.
hi, great tool, but i can't seem to make it work.
trying the test2.cfm file in the youtubeapi1 folder and keep getting "Bad, unknown, or disabled dev_id specified."
i want to test the upload function and can't get started.
the cfc is installed and i have a valid dev key.
any advice or other documentation? where to start with uploads?
thanks!
Don't use the youtubeapie1 folder. That's the older code. I include it for folks who may insist upon it - but in general don't support it anymore. Make sense?
Hey Ray, excellent work on the api, i have one question though. I wanted to order the videos by the newest to the oldest. Here is the code that I'm using
<cfset yt = createObject("component", "assets.cfcs.youtube")>
<cfset pl = yt.getVideosByUser('marjenningstv')>
any suggestions?
My CFC returns a query. If you want to re-order it, you can use a query of query. I can investigate, later on, adding sorting.
FYI - upload is currently broken. I hope to have it fixed w/n a week. It turns out there is a bug in CF that only gets exposed with the YT api.
Hi Ray, Do you have any documentation or examples on uploading videos? This is what I have at this point and the results follow: (thanks for your help and this .cfc)
<cfinvoke component="youtube" method="init" returnvariable="yt"><cfinvokeargument name="devkey" value="#request.YouTubeDeveloperKey#"></cfinvoke>
<cfdump var="#yt.upload(video='#cffile.SERVERDIRECTORY#\#cffile.SERVERFILE#',title='Upload Test',description='Testing',categories='Education',keywords='YouTube ColdFusion')#">
Upload Result:
--------------
ErrorDetail: I/O Exception: Software caused connection abort: socket write error
Filecontent: Connection Failure
Mimetype: Unable to determine MIME type of file.
Statuscode: Connection Failure. Status code unavailable.
Greg - video uploads is currently broken. I have worked w/ Adobe and there IS a fix, but the fix involves a hot fix that is not yet released to the public. I'm going to bug them again about that and see when it will go public.
Long story short: You can't do the upload now- period. But you will be able to with a hotfix sometime in the (I would assume!) near future.
i found the hotfix:
http://kb.adobe.com/selfser...
but i get an error when trying to upload:
the error is in your cfc, trying to read an error.
can you give an example of the code needed to upload a video?
Element ERRORS.ERROR.DOMAIN.XMLTEXT is undefined in RESXML.
The error occurred in /home/mylifesp/dev_html/my-account/youtube.cfc: line 691
689 : <cfset resxml = xmlParse(result.fileContent)>
690 :
691 : <cfthrow message="YouTubeCFC Upload Error: Domain=#resxml.errors.error.domain.xmlText#, Code=#resxml.errors.error.code.xmlText#">
692 : <cfelse>
The only real 'doc' for upload is by reading the CFC method and looking at the arguments. Can you show me how you tried to do an upload?
I noticed it says you need a dev key and YT username and pwd, but in the api docs it says
Both the Client ID and Developer Key should be provided in every API request (see Using a developer key and client ID). Each application/product should have its own unique Developer Key and Client ID.
http://code.google.com/apis...
Has it changed since the last update?
I think ClientID is the name of the 'tool' using the service, which in this case is my tool, and is hard coded in the CFC. So basically, you don't need to worry about it.
ahh..ok so they aren't tied together?
No, it's like your cell phone. Your phone number is unique to you, but your brand name is iPhone. So my code's "brand" is, um, whatever I used (probably something with Camden in it since I'm an egotistical twit), and the dev key is what you are responsible for.
ok, back again. This issue is related to just retrieving videos.
Everything was working at first. Then I started getting connection failures. I did some testing and by removing the V2 param, I got it working again.
So now the past couple of days it is no longer working on the prod server which is 7,0,2,142559 , but works fine on 8 from my home dev box.
Charset [empty string]
ErrorDetail Unknown host: gdata.youtube.com: gdata.youtube.com
Filecontent Connection Failure
Header [undefined struct element]
Mimetype Unable to determine MIME type of file.
Responseheader
struct [empty]
Statuscode Connection Failure. Status code unavailable.
Text YES
here is a test page I am using
http://justs10.com/videos/t...
If anyone could help, be mucho appreciated.
Thanks,
D
Unfortunately this sounds like a Google/YT issue. I'm not sure what I can recommend for that. Removing the v2 param isn't a solution for me as I know I'm relying on some v2 stuff in some calls. You may want to check the network settings on production box.
Ray - trying to use the upload capability but am a little clueless as to where to start. Any sample code you have for uploading?
Wasn't there one in the zip?
Here is an example, with login/key info changed:
<cfset yt = createObject("component", "youtube")>
<cfset yt.setDeveloperKey("AI39n2n17CzpcDAt6GMrXc8z6DA")>
<cfset yt.login("cfjedimaster", "password")>
<cfset r = yt.upload(expandPath('./actual.mov'),'Test/Upload Jack-O-Latern','Playing/with Jack-O-Latern youtube aug29','Music','testing fun')>
<cfoutput>id=#r#</cfoutput>
Hi Ray - great component.
Everything is working just as it should with the exception of the upload. I used the example upload code you supplied (altered to my id, login and video location) and I'm getting the:
Element ERRORS.ERROR.DOMAIN.XMLTEXT is undefined in RESXML.
Any thoughts?
So I just had it return resXML and it appears it does not like my DeveloperKeyID...
Looks like my error handling could be nicer. Did you get a new dev id though?
Yep - working great. Great component and allows me to put off the purchase of a media encoder... for now anyways.
Thanks!
Ok! Very Nice Work Ray!
One last question does not it return rows more than 25 either try to fetch by category or keywords:
Just curious
According to the getVideosBySearch function, there is a max attribute which defaults to 25, but can go up to 50. You can paginate by using the start attribute.
Hi ray! when i try to use getplaylist, it just keeps on loading:
i am just using getplaylist, not getplaylists
is there any example to that
Are you saying getPlaylist() never finishes? If so, can you tell me the playlist ID you tried?
well i never did tried any ID:
i just used #yt.getplaylist#
and it keeps on loading. how can i know in youtube playlists
Thanks Ray! but for all functions it only returns 25, it something needs to be reworked to make it work to fetch all. and once it fetches all, pagination can be donr
Regards
@Shaveta: Errr - that's not how you call a method. Have you used CFCs before?
@Misty: It only returns 25 if you don't set a max attribute. You can set it to 50. Also, look at the result query. It tells you how many total items there are. With that, you can build your own pagination.
Yes! i have used CFC's Before but when i see you playlists example: first you are calling a getplaylists function with pasing yourname and then u did the recordcount and then purl=[1] so the second part is bit ambigious as to u passed it 1. will playlists be visible only for a specific user and not for all.
Alright:
i am fetching the gettopratedvideos, when i run it, it just returns 25 results, if u can show me a viable example here, I shall be nice. i wanna get all results and show like 1 to 10 of 3773438 results:, then i can add my pagination. currently i am bit confused as there seems to me no result query in cfc which does that stuff
I don't think you are listening Misty. :) Did you look at the query result? Did you see the total number of results returned? It is in the column: total. So if you have the total _possible_, and you have the ability to pass in a start argument, then you have everything you need.
@Shaveta: My demo code first gets all of them - this is a query. So when I then call getPlaylist, I'm simply passing in the first URL. I don't pass 1, I pass the value of the purl column, row 1. If this doesn't work for you, please share the code you used.
in this first i tried my code which apparently did not worked. then i used you example and same did a copypaste. i did returned 2 results by your names but after that it gave me error
the error i encountered is
The web site you are accessing has experienced an unexpected error.
Please contact the website administrator.
The following information is meant for the website developer for debugging purposes.
Error Occurred While Processing Request
Element ENTRY.CONTENT.XMLTEXT is undefined in ARGUMENTS.
The error occurred in C:\inetpub\wwwroot\project2\admin\com\youtube.cfc: line 392
Called from C:\inetpub\wwwroot\project2\admin\com\youtube.cfc: line 167
Called from C:\inetpub\wwwroot\project2\admin\com\youtube.cfc: line 83
Called from C:\inetpub\wwwroot\project2\playlist.cfm: line 8
390 : <cfset s.categories = categoryList>
391 : <cfset s.title = arguments.entry.title.xmlText>
392 : <cfset s.content = arguments.entry.content.xmlText>
393 : <cfset s.author = arguments.entry.author.name.xmlText>
394 : <cfset s.authorurl = entry.author.uri.xmlText>
Having a hard time understanding you. Did you say one of my samples failed? Or did you modify it? If it was my sample, tell me the filename. If you modified it, please email me (ray at camdenfamily dot com) the script.
I noted one thing i used this and it gave me another error:
<cfset yt = createObject("component", "youtube")>
<cfset pl = yt.getPlaylists("PhilipDeFranco")>
<cfdump var="#pl#">
<cfif pl.recordCount>
<cfset plurl = pl.url[1]>
<cfdump var="#yt.getPlaylist(plurl)#">
</cfif>
Error i got
Element FEED.ENTRY is undefined in RESULT.
The error occurred in C:\inetpub\wwwroot\project2\admin\com\youtube.cfc: line 98
96 : <cfhttp url="#baseurl#" result="result">
97 : <cfset result = xmlParse(result.filecontent)>
98 : <cfif not arrayLen(result.feed.entry)>
99 : <cfreturn results>
100 : </cfif>
I just coped from you demo file:
test_getplaylists.cfm and ran it but encountered above errors. if i change the users' name it shows
Element FEED.ENTRY is undefined in RESULT. error.
i have changed nothing in youtube.cfc code. i am just running it as copied from your demo folder and that's it
I confirmed this bug. The issue was that the playlist had no videos, and the check for that was wrong. I also found another bug as well. I've uploaded the new CFC to:
http://www.coldfusionjedi.c....
Please download and confirm. Once you do, I will update the main project.
I am unable to download it
Please check the url again for download
Regards
Getting this Error:
404 - File or directory not found.
The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable.
Try
http://www.coldfusionjedi.c...
Awesome! Thanks
Please be sure to let me know if it works. Then I can update the official release.
Hi! I tried your way saying use of Max attribute but really I am lost how do i make it work to fetch all videos not only limited to 25 only. plz plz.
just let me know one example like gettopratedtags video or any other. it should all videos not only 25 please
Misty, I'm not going to write the entire thing for you. I gave you the basics. You have a start argument and you know the total # of results. Your code basically needs to use pagination. Here is an example that is similar: http://www.coldfusionjedi.c...
I successfully build up the pagination. it has been a no worry. the only issues which is making me think again & gain is that when i do like gettoprated or other it defaults to 25 only. i added the pagination showing 1 to 10 of 25. so my question is hardly it matters to paginate it returns only 25 not more.
while i am trying to get all related to that function gettoprated
@Misty: Um.... are you saying you have pagination working, but you want to show 50 per page, not 25?
When i fetch results, It shows to me only 25. I use cfdump but it restricts itself to only 25 while i am looking forward to fetch all the topratedvideos.
suppose 100 topratedvideos are listed on youtube, i ran query it just returned 25. i have added pagination but it has restricted itself to 25 only, while i want the ful youtube 100 listings as on youtube. The same is the case with all queries.
Maybe i am able to clarify what exactly my point is
Ah, I think I see what you mean. Only the Search API now supports max/start. My funcs for 'get so and so' do not. I'll look at adding in that as well, but no promises yet.
No Problem!
I am happy i am able to pinpoint my message clearly to you
Cheers
Please download
http://www.coldfusionjedi.c...
It contains updates to support max/start for the 'top' video type searches. Note max still has a MAX value of 100.
Thanks Ray! Looking forward to this to gets more support for this youtube CFC. Its an Awesome tag you have done a Great Great job.
I noted one thing. It shows 1 to 10 of total of 100. but it counts only to 50.
i mean API does not go beyong 50 records for every function
Did you try the start argument?
Yes Id did. i passsed it like:
getTopratedvideos("jshjsdsd",1,50)
it retur 50 recs
if try
getTopratedvideos("jshjsdsd",1,100)
it automatically comes to 25
so i have checked it does fetch more than 50, maybe issue with youtube api fetching results
Right, because the MAX is 50. When you set it to 100 it was too much. Keep it at 50 max.
am i skipping something big, you aid start argument. do i need to pass the start dynamically at the end of first 50 records and then start the next from 51st. is that so
When you run the query the first time, you should START with 1 and ask for 50 records. Check the TOTAL column in the query. If it is more than 50, then you need to call the CFC again, this time with 1+50, but the same max. Ie, start with 51 and get 50 rows. This will return the next 50.
I have this when i searched: Displaying 1 to 12 of 1000000 Records.
now if i use like this:
<cfset query = #yt.getVideosBySearch("song",1,50)#>
Element FEED is undefined in PACKET.
if i remove 1,50, it shows records but only 25. i think i have very hard time locating this that if i return total of 1000000. i can use it can get max of 50 in one shift and that shift also needs pagination to say 10 in a row.
question arises. when it reached end of the 50, how does it know to run th cfc again to get another. i think i am doing wrong
error, i found and removed but below the error the confusion still remains
The CFC doesn't do it for you. You have to do it. As I described - you check the TOTAL value (not the number of records in the column, but the TOTAL column) and if you SEE that more exist, you build links to load the next page. It is a manual process.
Folks, please test with the version at:
http://youtubecfc.riaforge.org
I've updated it there.
Thanks For pointing, it is a manual task. a thought of a solution like it should feych 10 records at a time and every next prev link, the cfc should again to fetch next 10 and do it until it reaches 100.
i am correct, but exactly i do not know how this can be done
Misty, I'm sorry, but I'm not going to write this code for you. I linked to an article on doing pagination above. Here is that link again:
http://www.coldfusionjedi.c...
If you google for ColdFusion and Pagination, you will see other examples. This is not a Youtube specific issue. Pagination can be done to any set of data, and there are many examples out there, not just the blog entry I linked to above.
Ray! i never said you write code for me of pagination, pagination is easy and i have done it. my issue comes where i told you already that run the cfc first time and fetc the first 10 records. next page, rerun the cfc and fetch the next 10 records.
i tried to make it simple by means that rather than fetching 50 first time and paginate, why not fetch 10 at a time and then paginate, but issue persists, how to fetch the next 10 after first 10.
Misty, I'm just at a loss. I simply cannot understand what you are asking here. It sounds like you are saying you want 2 calls to the same method to paginate automatically. Ie, first call gets 1-10, second gets 11-20. The CFC can't do that. You have to manually change the start parameter yourself.
If I'm still not reading you right... well, I hate to say I give up, but the language barrier is difficult here.
This time you read me right, Language barrier is a issue here but as you said, it is not possible to do like this. i cannot browse all 100 records. only first 50.
But you can! Unless there is a bug in the code, you just pass a start value of 51. Again, my CFC won't do this for you. You have to a) determine you have to page and b) write the code to fetch the next page.
Ok lets leave that point for the time being:
suppose there is a method which does not have start and max attributes, can i add to it/
Ok! I am not as experienced as you are but i think you cannot browse all records of youtube. like if founded are 1025. it is nearly no way i found that it can go beyond 50 records. might be you cold find a way instead
Regards
Misty - let me ask you this. Did you try passing a start value higher than 51? I did. It works.
Hmmmm! passing 51 to 100, it works but that i have to pass manually. i mean i have to tamper the file to fetch the next 50.
so i tried the start and max in the url also but still no luck as:
abc.cfm?start=1&max=10&pagenumber=2
but it did not worked either way too
I think you are still not getting a core concept of pagination in CF. Did you read my article? In the article, I use links that point back to the script. The starting value defaults to 1, and when I link back, I pass in a new start value. So the page basically links to itself to allow for pagination, all via URL parameters which define where to start.
I really think you need to look at the blog entry I mentioned before as it _does_ explain this concept.
Ok! i am using another pagination but i seems i should try yours. ok let me try with your pagingnation tool and see what i gets
I used Your pagination system which is quite simple to which i am using. it creates link with the start of 1 and then 11 and then 21 and it stops at 25.
how i rerun the cfc again to fetch next 25 or 50 recs,
I am going my blow my balls away with a shotgun! i am too much confused
:((
You rerun the code when you link to yourself. I honestly do not think you have read the article I linked to. In that article, I show how you create a link back to the current document and pass in a new START value.
_Thats_ how the CFC is run the 2nd time. The first time the user hits the page, url.start doesn't exist, so it defaults to 1, right? You determine there are more than 50 records so you build a link (again, look at the article), and now url.start is 51 and that is passed to the CFC.
Ok! i am doing it like this:
<cfset perpage = 10>
<cfparam name="url.start" default="1">
<cfif not isNumeric(url.start) or url.start lt 1 or url.start gt toprated.recordCount or round(url.start) neq url.start>
<cfset url.start = 1>
<cfif StructKeyExists(URL,'time')>
<cfset tr = #yt.getTopRatedVideos("#url.time#",#url.start#,50)#>
<cfelse>
<cfset tr = #yt.getTopRatedVideos("")#>
</cfif>
</cfif>
but it does seems to work, i think i am missing one big step, i am near the end but can't find the end
recordcount returns 25 and if parameters are used it returns 50.
now running pagination on 25 or 50 is easy and it works, how it should fetch next 50.
because pagination has toprated.recordcount which is count to every time 50 or 25, so it is possible it keeps on fetching the very next records. seems impossible to me instead
What i have noted, cfc cannot fetch more than 50.
That all
I tried this way:
http://gdata.youtube.com/fe...
it clearly indicated that it cannot go and fetch the records, max 50 allowed, so i think there is no way how it can done.
Hi ray! any Update on this
It is 50 per time. Just change it to 50. Your starting at 51, so you will get 51-101. One arg says where to start, one says how many to get. You will ALWAYS ask for 50 at a time, your just changing the starting position.
Hi Ray. First I must say thank you for your endless contributions to the CF community. Your CFCs for youtube and amazon s3 have really saved my butt this week. In regards to the youtube CFC, I found some minor issues with the delete method. 1. 'http://gdata.youtube.com/fe... must be stripped from the videoID before it is passed to the delete method. Other methods in the CFC include this in the code. The delete method does not. 2. The first cfhttp param should be "Host" with a value of "gdata.youtube.com". Ex: "<cfhttpparam type="header" name="Host" value="gdata.youtube.com">" before the content-type param. I discovered this by reviewing the youTube API documentation here: http://code.google.com/apis...
I downloaded version 6 from RIAForge early last week, I'm wondering if I have the latest version. Thanks again for the great CFC's.
Cheers,
Eric Wilkinson
Thank you for the kind words, but I'm not the person behind Amazon S3 support. :)
Anyway, I took your changes as is and pushed it live. Download and check it out please.
Hello Ray,
I'm trying to use the "update" function of youtube.cfc. When I do, I get a 400 / Explanatioin: Bad Request error.
Here is the code that I'm using:
<cfset yt = createObject("component", "/yt/youtube")>
<cfset yt.setDeveloperKey("mykey")>
<cfset yt.login("uname", "pword")>
<cfset r = yt.update('#form.cause_name#','#form.before_description#','Music','#cause.tags#','#cause.before_video_ytid#')>
Here are the values to the above:
form.cause_name = sample cause
form.before_description = This is a test description.
cause.tags = Online, Contribution
cause.before_video_ytid = my video id displays here...
I checked the URLs and they seem to be correct, not sure what else to try. Any help is greatly appreciated!
- Anthony
Google is super anal about tags. I've tried to 'massage' the tags value to make them Google happy. I don't have time to test this second - but can you run a quick test where you send one value for tags, something simple like: "Testing". No periods, commas, or spaces. Just one word - and make sure it is at least 4 characters or so long.
Thanks Ray. I updated the code to generate all static simple values and I'm still getting the same error. Here is the new code:
<cfset yt = createObject("component", "/yt/youtube")>
<cfset yt.setDeveloperKey("mykey")>
<cfset yt.login("uname", "pword")>
<cfset r = yt.update('Test Name','This is a description','Music','Testing','videoidvalue')>
Oh shoot - you are doing an UPDATE, not an Upload. Sorry. Testing now.
I fixed it. Please get the latest from Subversion on RIAForge, not the zip, confirm, and then I'll release it.
Hi Ray, I hope you had a great Thanksgiving! I downloaded the new upload code (youtube.cfc), tried the same code with the static values and get the same error.
Can you maybe provide me with a sample code? I may be missing something.
Thanks again!
Please ping me directlyu and pass along your credentials - if you feel comfortable. Obviously it works for me so I need to test exactly as you have it.
Hi Ray,
Thanks for being such an active and professional commentator and teacher over the years. I know I have certainly benefited from your examples and posts.
You are probably tired of talking about this CFC, but...
I am trying to use the YouTubeCFC, and like many above, can get everything working except the upload.
I think I may be missing something conceptually about the authorization process, but here is what I am trying.
I had to modify your CFC to work in openBlueDragon (ver 1.2)- basically replace all =& and also the ReMatch function in the keywords wasn't happy. For this example I am just passing a single word to avoid YT formatting issues with keywords.
<cfset yt = createObject("component", "youtube")>
<cfset yt.login("whittonr", "mypassword")>
<cfset r = yt.upload('E:\Video_Files\B\Cyprinus_carpio_Common_carp_Wide_Angle_742_S5Q5_ID~33085.m2t','Koi','Test description','Tech','Koi')>
<cfoutput>id=#r#</cfoutput>
I keep getting "YouTubeCFC Upload Error: Status: 415 / Explanation: Unsupported Media Type". I have gone and manually added the same clip to YouTube with not problem, so I do not believe that the file is bad.
The authtoken... where is that coming from? My username and password? Is that the session authorization required to then submit the uploaded file?
The DevKey - that is your specific key to go with your YouTubeCFC tool, correct? I don't need to use my Google Dev Key. correct?
Thanks in advance for your help,
Rob
The authtoken is made from the login. You don't need to recreate it. Basically the login command does it and stores it. It uses for the next calls.
DevKey - don't need to change it.
So - I'd like you to try something. I know that m2t may work for you on the public site, but the API may be more anal about what it requires. Try one of the more common types and see if it works ok.
Aloha Ray,
Sorry for the delay. I just tried both an mpg and an mp4, and had the same error: YouTubeCFC Upload Error: Status: 415 / Explanation: Unsupported Media Type.
Both uploads took several minutes before failing, as both files were large - around 50mb. I then tried a smaller mp4 - 2.7MB and received the same error.
Here is the code I am using:
<cfset filename="M.grandoculis640x480.mp4">
<cfset fileLocation = "D:\Explorers_Log\webapps\log\Temp\">
<cfset yt = createObject("component", "youtube")>
<cfset yt.login("whittonr", "mypassword")>
<cfset r = yt.upload('#fileLocation##filename#','Angelfish','Test description','Tech','Shark,Reef,Whitton')>
<cfoutput>id=#r#</cfoutput>
<cfdump var="#yt.getVideosByUser('whittonr')#" top="5">
<cfabort>
Any other ideas on things I should try to narrow down the issue?
Thanks again for your help,
Rob
Robert, would you feel comfortable sending me a test mp4 and your login credentials so I can recreate here? Obviously NOT in a blog comment. ;)
Hi Ray, this is a fantastic CFC. I have had it running on my dev server for the the past 5 months but last week every thing went a bit wrong.
When ever i try and upload a video im getting this error, is this anything you have seen before?
Thanks for taking the time to read this.
Jason
Element XMLATTRIBUTES.REASONCODE is undefined in a Java object of type class coldfusion.xml.XmlNodeMap referenced as ''
The error occurred in F:\sports\webroot\youtube.cfc: line 816
Called from F:\sports\webroot\youtube.cfc: line 388
Called from F:\sports\webroot\video-view.cfm: line 150
Called from F:\sports\webroot\video-view.cfm: line 138
Called from F:\sports\webroot\video-view.cfm: line 1
814 : <cfset s.videostatus = "public">
815 : <cfif structKeyExists(arguments.entry, "app:control")>
816 : <cfset s.videostatus = arguments.entry["app:control"]["yt:state"].xmlAttributes.reasonCode>
817 : </cfif>
818 :
It looks like maybe I made an assumption about the xml result. Add a quick cfdump to arguments.entry. Tell me what you see under app:control.
I don't seem to be able to dump it?! Despite the error the videos seem to be passing through & saving to youtube fine.
Jason
You need to add an abort after the dump probably.
I did need that, thanks, I am still fairly new to CF.
I am getting:
<app:control>
<app:draft>yes</app:draft>
<yt:state name='processing'/>
</app:control>
I have worked out that the problem is that once a user uploads a video I relocate them back to the video page where it displays the latest video & if it has not been processed yet then it throws up the error. I have a feeling youtube might be taking a little longer to process video than the were when I originally put the page together.
I shall check yt:state before displaying the video and get round it that way.
Ok - so to be clear though, this is a bug in my code though. It isn't you 'doing it too quickly'. I should update my code to NOT assume the code is there all the time. If I work on a fix, would you be willing to test it out?
yeah, there is, I have got round it rather than say using:
<cfset yt = createObject("component", "youtube")>
<cfset foo = "http://gdata.youtube.com/fe...">
<cfset z = yt.getVideo(foo)>
I have used:
<cfhttp url="http://gdata.youtube.com/fe...">
<cfif #cfhttp.Statuscode# contains '200' AND #cfhttp.Filecontent# DOES NOT CONTAIN "<yt:state name='processing'/>">
<cfset yt = createObject("component", "youtube")>
<cfset foo = "http://gdata.youtube.com/fe...">
<cfset z = yt.getVideo(foo)>
<cfdump var="#z#">
<cfelse>
#errorMSG#
</cfif>
If you do work on a fix id be more than happy to test it.
Please try this version: http://pastebin.com/VFVbdizL
By changing:
<cfset result = result.filecontent>
<cfif result is "Invalid id">
<cfthrow message="YouTubeCFC: Invalid id">
</cfif>
to:
<cfset statuscode = result.Statuscode>
<cfset result = result.filecontent>
<cfif result is "Invalid id" OR statuscode EQ "403 Forbidden">
<cfthrow message="YouTubeCFC: Invalid id">
</cfif>
It is now throwing up the message when I pass it the ID of a invalid/deleted/duplicate video.
Sorry - what block of code is that?
From line 378 in here http://pastebin.com/VFVbdizL
Ok, seems smart. I used
<cfhttp url="#arguments.myid#" result="result">
<cfset resulttext = result.filecontent>
<cfif resulttext is "Invalid id" OR result.statuscode EQ "403 Forbidden">
<cfthrow message="YouTubeCFC: Invalid id">
</cfif>
Hi Raymond,
It seems like there is another bug. Please review the error below. It seems like the code is making an assumption that app:control existence will result in status attribute. If you have fixed it thats great. If you are going to explore a solution my suggestion would be to add to the conditional statement where you check for the attribute before assigning the value. I will be happy to test this for FYEGUY.com site.
Thanks for your help
Element yt:state is undefined in a Java object of type class coldfusion.xml.XmlNodeList.
The error occurred in E:\fyeguy.com\youtube2\youtubecfc\youtube.cfc: line 799
Called from E:\fyeguy.com\youtube2\youtubecfc\youtube.cfc: line 400
Called from E:\fyeguy.com\youtube2\youtubecfc\youtube.cfc: line 650
Called from E:\fyeguy.com\promotions\lolla\lookup.cfm: line 5
797 : <cfset s.videostatus = "public">
798 : <cfif structKeyExists(arguments.entry, "app:control")>
799 : <cfset s.videostatus = arguments.entry["app:control"]["yt:state"].xmlAttributes.reasonCode>
800 : </cfif>
I fixed this in source already. Do you have the latest from RIAForge?
Hi Raymond,
I just saw the latest file and indeed that is what I was exploring and I can confirm that its fixed.
Thanks,
Das
This is one of the best CFCs out there. I have been using it for quite some time now (1.5 years) in production, but for some reason starting yesterday or the day before, I started getting the following error:
<b>Element SUMMARY.XMLTEXT is undefined in ENTRY.</b>
The error occurred in youtube.cfc: line 249
247 : <cfset querySetCell(results, "updated", handleDate(entry.updated.xmlText))>
248 : <cfset querySetCell(results, "title", entry.title.xmlText)>
249 : <cfset querySetCell(results, "summary", entry.summary.xmlText)>
250 : <cfset querySetCell(results, "url", entry["gd:feedLink"].xmlAttributes.href)>
251 : <cfset querySetCell(results, "videocount", entry["gd:feedLink"].xmlAttributes.countHint)>
I just finished spending about 30 minutes looking into this and can't for the life of me figure out why the entry.summary.xmlText value is not available in the output. I dumped the entire "result" and sure enough it's not there. Interestingly, neither is the videocount.
Can anyone else duplicate this? The only way I was able to get my code working again was to comment out those two lines in the CFC, which if fine for now, but I hate when things all of the sudden break (after 1.5 years of no trouble) and I can't figure out why. Any idea? Thanks.
My guess would be that the code should check for their existence now. Perhaps Youtube changed something in their API.
Anyway - it would be trivial to check for this. Do you have a reproducable case though so I can confirm it here? If your case involves NDA crap, you can send it to me directly and I'll delete when done.
Thanks for the quick reply! I just sent you the code by replying to the blog notification email.
YouTubeCFC has been updated to handle cases where summary/countHint do not exist. You can get the latest at http://youtubecfc.riaforge.org.
Ray, does youtube.cfc support Browser Based Uploading.. ( http://code.google.com/apis... ). Ideally I'd like to have the user upload straight to YouTube versus the pain of uploading to my webserver and then uploading to Youtube. By the looks of things, that might not be available in this version?
Right, this is not available in my CFC.
I have been trying to figure out how to remove a video from playlist. I keep getting 400 errors. I see an authtoken in the cfc, where does that come from and is that needed to do this? I can use all the other functions just fine. Does anyone have an idea on how to this based on Rays code?
here is the link to the api info:
http://code.google.com/apis...
400 is coming from Google, it means the call is done wrong. I checked, and that functionality is not in my CFC. So I assumed you added it yourself? Can you share your version?
Thanks for responding Ray. I have been trying various different methods based on the code you have there. I guess i really don't see where the "delete" part comes in to play in their code examples. This code below was really just based on one of your other functions, i just made sure that i was passing in the playlistID and the videoID
<cffunction name="deleteFromPlaylist" access="public" returnType="any" output="false" hint="I delete a video.">
<cfargument name="playlistId" type="string" required="true">
<cfargument name="videoId" type="string" required="true">
<cfset var theurl = "http://gdata.youtube.com/fe...">
<cfset var result = "">
<cfset var resxml = "">
<!--- Video ID may include the URL, strip it --->
<cfset arguments.videoid = replace(arguments.videoid, "http://gdata.youtube.com/fe...","")>
<cfset arguments.videoid = listFirst(arguments.videoid, "&")>
<cfset theurl &= arguments.videoId />
<cfhttp url="#theurl#" method="delete" result="result">
<cfhttpparam type="header" name="Host" value="gdata.youtube.com">
<cfhttpparam type="header" name="Content-Type" value="application/atom+xml">
<cfhttpparam type="header" name="Authorization" value="GoogleLogin auth=#variables.authtoken#">
<cfhttpparam type="header" name="X-GData-Client" value="youtubecfc">
<cfhttpparam type="header" name="X-GData-Key" value="key=#variables.devkey#">
</cfhttp>
<cfif result.responseheader.explanation is "OK">
<cfreturn "Video successfully removed.">
<cfelse>
<cfif isXml(result.filecontent)>
<cfset resxml = xmlParse(result.fileContent)>
<cfthrow message="YouTubeCFC Upload Error: Domain=#resxml.errors.error.domain.xmlText#, Code=#resxml.errors.error.code.xmlText#">
<cfelse>
<cfthrow message="YouTubeCFC Upload Error: Status: #result.responseheader.status_code# / Explanation: #result.responseheader.explanation#">
</cfif>
</cfif>
</cffunction>
Acutally i posted the wrong one..
<cffunction name="deleteFromPlaylist" access="public" returnType="any" output="false" hint="I delete a video from playlist.">
<cfargument name="playlistId" type="string" required="true">
<cfargument name="videoId" type="string" required="true">
<cfset var theurl = "http://gdata.youtube.com/fe...">
<cfset var result = "">
<cfhttp url="#theurl#" method="delete" result="result">
<cfhttpparam type="header" name="Host" value="gdata.youtube.com">
<cfhttpparam type="header" name="Content-Type" value="application/atom+xml">
<cfhttpparam type="header" name="Authorization" value="GoogleLogin auth=#variables.authtoken#">
<cfhttpparam type="header" name="X-GData-Client" value="youtubecfc">
<cfhttpparam type="header" name="X-GData-Key" value="key=#variables.devkey#">
</cfhttp>
<cfreturn result>
</cffunction>
This is what i was using, the return i get with this is:
401 Token invalid
So i think has to do with the second part of my original question about the authtoken in the cfc, where would i get that?
The Authtoken is generated when you login. My example code should show you how you first authenticate, then you can do methods like that.
Ok, so i see your login and i can confirm its giving me a token. I am now back to 400 error. This is how i am calling it
<cfset yt = createObject("component", "youtube")>
<cfset yt.login("username", "password")>
<cfset dp = yt.deleteFromPlaylist('62D2AB95BFB9F362','http://gdata.youtube.com/fe...
<cfdump var="#dp#">
Add a debug dump, ie, <cfdump var="#result#" abort> before your cfreturn. Let's look at more of the result.
i think i got it, even though the ID in the playlist is the big long url which i was using, i tried just making it like the call below and that returned a good status 200, and it was removed from my list..
<cfset dp = yt.deleteFromPlaylist('62D2AB95BFB9F362','tEsesCpfljvXjvB_LBoDK-aBCrzoWfdG')>
So for anyone looking to do this, here is the function i added to the cfc.
<cffunction name="deleteFromPlaylist" access="public" returnType="any" output="false" hint="I delete a video.">
<cfargument name="playlistId" type="string" required="true">
<cfargument name="videoId" type="string" required="true">
<cfset var theurl = "http://gdata.youtube.com/fe...">
<cfset var result = "">
<cfhttp url="#theurl#" method="delete" result="result">
<cfhttpparam type="header" name="Host" value="gdata.youtube.com">
<cfhttpparam type="header" name="Content-Type" value="application/atom+xml">
<cfhttpparam type="header" name="Authorization" value="GoogleLogin auth=#variables.authtoken#">
<cfhttpparam type="header" name="X-GData-Client" value="youtubecfc">
<cfhttpparam type="header" name="X-GData-Key" value="key=#variables.devkey#">
</cfhttp>
<cfreturn result>
</cffunction>
Ray, as always, thank you for you time and masterful knowledge. The new site template looks fantastic by the way.
Hey Ray was able to get addPlaylist to work by changing the function a little
<cffunction name="addPlaylist" access="public" returntype="any" output="false" hint="Adds a playlist for a user.">
<cfargument name="title" type="string" required="true">
<cfargument name="description" type="string" required="true">
<cfset var theurl = "http://gdata.youtube.com/fe...">
<cfset var result = "">
<cfset var meta = "">
<cfset var tmpFile = expandPath("./#replace(createUUID(),'-','_','all')#")>
<cfset var resxml = "">
<cfsavecontent variable="meta">
<cfoutput>
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:yt="http://gdata.youtube.com/sc...">
<title type="text">#xmlFormat(arguments.title)#</title>
<summary>#XMLFormat(arguments.description)#</summary>
</entry>
</cfoutput>
</cfsavecontent>
<cfset meta = trim(meta)>
<cfhttp url="#theurl#" method="post" result="result">
<cfhttpparam type="header" name="Host" value="gdata.youtube.com">
<cfhttpparam type="header" name="Content-Type" value="application/atom+xml">
<cfhttpparam type="header" name="Content-Length" value="#len(meta)#">
<cfhttpparam type="header" name="Authorization" value="GoogleLogin auth=#variables.authtoken#">
<cfhttpparam type="header" name="X-GData-Client" value="youtubecfc">
<cfhttpparam type="header" name="X-GData-Key" value="key=#variables.devkey#">
<cfhttpparam type="body" value="#meta#">
</cfhttp>
<cfdump var="#result#"><cfabort>
</cffunction>