How do I use captions with GTLYouTubeVideoContentDetails and GTLYouTubeVideo? - objective-c

I have successfully implemented YouTube video upload. Since I am also dealing with text that can be used as subtitles, I'd like to upload these, as well.
GTLYouTubeVideoContentDetails’s header does not mention what format "caption" has to be in, so I tried SRT, but that didn't work. The SRT file itself seems to be valid, I uploaded it manually to a demo video and it looked fine.
My approach was this:
GTLYouTubeVideo *video = [GTLYouTubeVideo object];
if (nil != captionString)
{
GTLYouTubeVideoContentDetails *details = [[GTLYouTubeVideoContentDetails alloc] init];
details.caption = captionString;
video.contentDetails = details;
[details release];
}
This was the only alteration to the previously working code. The result now is HTTP status 501 when trying to upload.
Any ideas?

Related

YouTube PlayListitemInsert code not working API

I have code that executes a youtube upload that's working fine, when it's done, it then attempts to move the video to a specific playlist.
For testing, I have hardcoded the playlist (it's correct and re-verified) and the videoid (it does exist in my channel) into the code.
I cannot get the video to to the playlist I specify and get the output below from the NSlog query1, but no errors.
Query1: GTLQueryYouTube 0x1700b0860: {method:youtube.playlistItems.insert params:(part) bodyObject:GTLYouTubePlaylistItem}
Any ideas what I am missing?
// Enter PlaylistItem Code
GTLYouTubePlaylistItem *playlistitem = [[GTLYouTubePlaylistItem alloc] init];
GTLYouTubePlaylistItemSnippet *playlistitemSnippet = [[GTLYouTubePlaylistItemSnippet alloc] init];
playlistitemSnippet.playlistId = #"PL4YcQc6s41BjKOoAPAQ_B-KNC2JBB3gl2";
playlistitemSnippet.resourceId.kind = #"youtube#video";
playlistitemSnippet.resourceId.videoId = #"4frmxoGMOcQ";
GTLQueryYouTube *query1 = [GTLQueryYouTube queryForPlaylistItemsInsertWithObject:playlistitem part:#"snippet"];
UIAlertView *waitIndicator1 = [Utils showWaitIndicator:#"Moving Video to Playlist"];
NSLog(#"Query1: %#", query1);
[service executeQuery:query1
completionHandler:^(GTLServiceTicket *ticket,
GTLYouTubePlaylistItem *resource, NSError *error) {
[waitIndicator1 dismissWithClickedButtonIndex:0 animated:YES];
Ended up scrapping this and used the playlist auto add feature in youtube. Way easier.

Objective-C RSS Reader - XML reading error

I am new to obj-C and i'm trying to make an easy RSS Reader. It works with an external RSS file but not when i load the same file in my server based on AWS services.
This is the snippet code:
[super viewDidLoad];
feeds = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:#"http://images.apple.com/main/rss/hotnews/hotnews.rss"];
parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
[parser setDelegate:self];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
If i read that rss via web, i see this:
When I upload that file on my server, i see this:
I tried to avoid manual upload via Filezilla but using wget command and i also tried ascii mode transfer, but still not work. RSS Reader show an empty list.
It is the problem with URL. I had the crash with the same code with error
Unable to read data
I replaced code with
feedURL = NSURL(string:"http://images.apple.com/main/rss/hotnews/hotnews.rss#sthash.fuVEonEt.dpuf")!
parser = XMLParser(contentsOf:feedURL as URL)!
parser.delegate = self
parser.parse()
It work fine now.
As suggested by #Larme, it was the App Transport Security Issue How do I load an HTTP URL with App Transport Security enabled in iOS 9? .
This solved my issue!

How do I verify a file's existence in iCloud?

I know that the file exists, because I can download it, but I need to check to see whether it exists. I have tried using
[NSFileManager contentsOfDirectoryAtPath:error:]
but it gives me null. I don't understand why that is because I can still download the files that I'm looking for. Maybe it's an incorrect URL, but the URL that I'm using is the one that I printed at creation of my UIDocument that I'm looking for. Maybe I'm using the wrong method?
EDIT:
I have also tried using NSMetadataQuery, and I can get it to give back notifications, but it doesn't ever have results even though I can explicitly download the files I'm looking for.
To find files in iCloud, you use NSMetadataQuery. That will find both files that have already been downloaded as well as files that are in the user's account but which haven't been downloaded to the local device yet. Using NSFileManager will, at best, only find files that have already been downloaded.
You set it up with something like this:
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
[self setMetadataQuery:query];
[query setSearchScopes:#[NSMetadataQueryUbiquitousDataScope]];
[query setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE '*'", NSMetadataItemFSNameKey]];
You'll want to observe NSMetadataQueryDidStartGatheringNotification, NSMetadataQueryDidUpdateNotification, and probably NSMetadataQueryDidFinishGatheringNotification. Then start the query:
[query startQuery];
With that done, you'll get notifications as the query discovers iCloud files. The notifications will include instances of NSMetadataItem, which you can use to get information like file size, download status, etc.
Use a metadata query - here is some sample code
/*! Creates and starts a metadata query for iCloud files
*/
- (void)createFileQuery {
[_query stopQuery];
if (_query) {
[_query startQuery];
}
else {
_query = [[NSMetadataQuery alloc] init];
[_query setSearchScopes:[NSArray arrayWithObjects:NSMetadataQueryUbiquitousDocumentsScope, NSMetadataQueryUbiquitousDataScope, nil]];
// NSString * str = [NSString stringWithFormat:#"*.%#",_fileExtension];
NSString *str = #"*";
[_query setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE %#", NSMetadataItemFSNameKey, str]];
NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self selector:#selector(fileListReceived) name:NSMetadataQueryDidFinishGatheringNotification object:_query];
[notificationCenter addObserver:self selector:#selector(fileListReceived) name:NSMetadataQueryDidUpdateNotification object:_query];
[_query startQuery];
}
}
/*! Gets called by the metadata query any time files change. We need to be able to flag files that
we have created so as to not think it has been deleted from iCloud.
*/
- (void)fileListReceived {
LOG(#"fileListReceived called.");
NSArray* results = [[_query results] sortedArrayUsingComparator:^(NSMetadataItem* firstObject, NSMetadataItem* secondObject) {
NSString* firstFileName = [firstObject valueForAttribute:NSMetadataItemFSNameKey];
NSString* secondFileName = [secondObject valueForAttribute:NSMetadataItemFSNameKey];
NSComparisonResult result = [firstFileName.pathExtension compare:secondFileName.pathExtension];
return result == NSOrderedSame ? [firstFileName compare:secondFileName] : result;
}];
//FLOG(#" results of query are %#", results);
for (NSMetadataItem* result in results) {
NSURL* fileURL = [result valueForAttribute:NSMetadataItemURLKey];
NSString* fileName = [result valueForAttribute:NSMetadataItemDisplayNameKey];
NSNumber* percentDownloaded = [result valueForAttribute:NSMetadataUbiquitousItemPercentDownloadedKey];
NSNumber *isDownloaded = nil;
NSNumber *isDownloading = nil;
NSError *er;
[fileURL getResourceValue: &isDownloaded forKey:NSURLUbiquitousItemIsDownloadedKey error:&er];
[fileURL getResourceValue: &isDownloading forKey:NSURLUbiquitousItemIsDownloadingKey error:&er];
FLOG(#" Found file %#", fileName);
}
}
From the docs:
In iOS, actively download files when required. Items in iCloud but not
yet local are not automatically downloaded by iOS; only their metadata
is automatically downloaded. The initial download of new iCloud-based
documents requires your attention and careful design in your app.
After you explicitly download such an item, the system takes care of
downloading changes arriving from iCloud.
Consider keeping track of
file download status as part of your iOS app’s model layer. Having
this information lets you provide a better user experience: you can
design your app to not surprise users with long delays when they want
to open a document that is not yet local. For each file (or file
package) URL provided by your app’s metadata query, get the value of
the NSURLUbiquitousItemIsDownloadedKeykey by calling the NSURL method
getResourceValue:forKey:error:. Reading a file that has not been
downloaded can take a long time, because the coordinated read
operation blocks until the file finishes downloading (or fails to
download).
For a file (or file package) that is not yet local, you can initiate
download either when, or before, the user requests it. If your app’s
user files are not large or great in number, one strategy to consider
is to actively download all the files indicated by your metadata
query. For each file (or file package) URL provided by the query, make
the corresponding item local by calling the NSFileManager method
startDownloadingUbiquitousItemAtURL:error:. If you pass this method a
URL for an item that is already local, the method performs no work and
returns YES.
Update: iOS7 should use NSURLUbiquitousItemDownloadingStatusKey instead of NSURLUbiquitousItemIsDownloadedKey.

Post photo to Instagram using their iOS hooks

I use the following code in my iOS app to use Instagram iPhone hooks to post a photo to Instagram. I only want the "Open In..." menu to have Instagram app, no other apps. But in my case Camera+ also shows up. How can I restrict to Instagram?
Also, can I directly open Instagram instead of showing Open In menu?
NSURL *instagramURL = [NSURL URLWithString:#"instagram://app"];
if ([[UIApplication sharedApplication] canOpenURL:instagramURL]) {
//imageToUpload is a file path with .ig file extension
self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:imageToUpload]];
self.documentInteractionController.UTI = #"com.instagram.photo";
self.documentInteractionController.annotation = [NSDictionary dictionaryWithObject:#"my caption" forKey:#"InstagramCaption"];
[self.documentInteractionController presentOpenInMenuFromBarButtonItem:self.exportBarButtonItem animated:YES];
}
BTW Instagram added an exclusive file extention (ig) and UTI (com.instagram.exclusivegram) for this. It still opens the Open with... menu but the only option is Instagram.
More info here: https://instagram.com/developer/mobile-sharing/iphone-hooks/
You can get the solution from this link.
Save image with the .igo extension instead of .ig. This is the "exclusive" version of the filetype.
Create a UIDocumentInteractionController, then assign the value com.instagram.exclusivegram to the property UTI.
Present your UIDocumentInteractionController with presentOpenInMenuFromRect:inView:animated.
This worked for me, do it like this and you will have only Instagram as the exclusive app to open your image.
NSString *documentDirectory = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
// *.igo is exclusive to instagram
NSString *saveImagePath = [documentDirectory stringByAppendingPathComponent:#"Image.igo"];
NSData *imageData = UIImagePNGRepresentation(filteredImage);
[imageData writeToFile:saveImagePath atomically:YES];
NSURL *imageURL=[NSURL fileURLWithPath:saveImagePath];
_docController=[[UIDocumentInteractionController alloc]init];
_docController.delegate=self;
_docController.UTI=#"com.instagram.photo";
[_docController setURL:imageURL];
_docController.annotation=[NSDictionary dictionaryWithObjectsAndKeys:#"#yourHashTagGoesHere",#"InstagramCaption", nil];
[_docController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES];
To answer only your first question: you may probably be able to restrict the "Open in ..." menu to just showing Instagram for your device (by deleting the Camera+ App, for example), but you won't be able to restrict users that install your app to their devices. And that's because the iPhone recognizes which applications are able to open a specific kind of files and it automatically show every one that does.
self.documentInteractionController = [self setupControllerWithURL:imgurl usingDelegate:self];
self.documentInteractionController=[UIDocumentInteractionController interactionControllerWithURL:imgurl];
self.documentInteractionController.UTI = #"com.instagram.exclusivegram";
use this code in same sequence .
here documentInteractionController is object of UIDocumentInteractionController.just for your knowledge.
you will get instagram only in "open in" window.

How to make QTMovie play file from URL with forced (MP3) type?

I'm using QTKit to progressively download and play an MP3 from a URL. According to this documentation, this is the code I should use to accomplish that:
NSURL *mp3URL = [NSURL URLWithString:#"http://foo.com/bar.mp3"];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithURL:mp3URL error:&error];
[sound play];
This works, and does exactly what I want — the MP3 URL is lazily downloaded and starts playing immediately. However, if the URL does not have the ".mp3" path extension, it fails:
NSURL *mp3URL = [NSURL URLWithString:#"http://foo.com/bar"];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithURL:mp3URL error:&error];
[sound play];
No error is given, no exception is raised; the duration of the sound is just set to zero, and nothing plays.
The only way I have found to work around this is to force a type by loading the data manually and using a QTDataReference:
NSURL *mp3URL = [NSURL URLWithString:#"http://foo.com/bar"];
NSData *mp3Data = [NSData dataWithContentsOfURL:mp3URL];
QTDataReference *dataReference =
[QTDataReference dataReferenceWithReferenceToData:mp3Data
name:#"bar.mp3"
MIMEType:nil];
NSError *error = nil;
QTMovie *sound = [[QTMovie alloc] initWithDataReference:dataReference error:&error];
[sound play];
However, this forces me to completely download ALL of the MP3 synchronously before I can start playing it, which is obviously undesirable. Is there any way around this?
Thanks.
Edit
Actually, it seems that the path extension has nothing to do with it; the Content-Type is simply not being set in the HTTP header. Even so, the latter code works and the former does not. Anyone know of a way to fix this, without having access to the server?
Edit 2
Anyone? I can't find information about this anywhere, and Google frustratingly now shows this page as the top result for most of my queries...
Two ideas. (The first one being a bit hacky):
To work around the missing content type, you could embed a small Cocoa webserver that supplements the missing header field and route your NSURL over that "proxy".
Some Cocoa http server implementations:
http://code.google.com/p/cocoahttpserver/
http://cocoawithlove.com/2009/07/simple-extensible-http-server-in-cocoa.html
http://culturedcode.com/cocoa/
The second one would be, to switch to a lower level framework (From QTKit to AudioToolbox).
You'd need more code, but there are some very good resources out there on how to stream mp3 using AudioToolbox.
e.g.:
http://cocoawithlove.com/2008/09/streaming-and-playing-live-mp3-stream.html
Personally I'd go with the second option. AudioToolbox isn't as straightforward as QTKit but it offers a clean solution to your problem. It's also available on both - iOS and Mac OS - so you will find plenty of information.
Update:
Did you try to use another initializer? e.g.
+ (id)movieWithAttributes:(NSDictionary *)attributes error:(NSError **)errorPtr
You can insert your URL for the key QTMovieURLAttribute and maybe you can compensate the missing content type by providing other attributes in that dictionary.
This open source project has a QTMovie category that contains methods to accomplish similar things:
http://vidnik.googlecode.com/svn-history/r63/trunk/Source/Categories/QTMovie+Async.m
If you thought weichsel's first solution was hacky, you're going to love this one:
The culprit is the Content-Type header, as you have determined. Had QTKit.framework used Objective-C internally, this would be a trivial matter of overriding -[NSHTTPURLResponse allHeaderFields] with a category of your choosing. However, QTKit.framework (for better or worse) uses Core Foundation (and Core Services) internally. These are both C-based frameworks and there is no elegant way of overriding functions in C.
That said, there is a method, just not a pretty one. Function interposition is even documented by Apple, but seems to be a bit behind the times, compared to the remainder of their documentation.
In essence, you want something along the following lines:
typedef struct interpose_s {
void *new_func;
void *orig_func;
} interpose_t;
CFStringRef myCFHTTPMessageCopyHeaderFieldValue (
CFHTTPMessageRef message,
CFStringRef headerField
);
static const interpose_t interposers[] __attribute__ ((section("__DATA, __interpose"))) = {
{ (void *)myCFHTTPMessageCopyHeaderFieldValue, (void *)CFHTTPMessageCopyHeaderFieldValue }
};
CFStringRef myCFHTTPMessageCopyHeaderFieldValue (
CFHTTPMessageRef message,
CFStringRef headerField
) {
if (CFStringCompare(headerField, CFSTR("Content-Type"), 0) == kCFCompareEqualTo) {
return CFSTR("audio/x-mpeg");
} else {
return CFHTTPMessageCopyHeaderFieldValue(message, headerField);
}
}
You might want to add logic specific to your application in terms of handling the Content-Type field lest your application break in weird and wonderful ways when every HTTP request is determined to be an audio file.
Try replacing http:// with icy://.
Just create an instance like this...
QTMovie *aPlayer = [QTMovie movieWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
fileUrl, QTMovieURLAttribute,
[NSNumber numberWithBool:YES], QTMovieOpenForPlaybackAttribute,
/*[NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute,*/
nil] error:error];