I am trying to parse youtube link and play on MPMoviePlayerViewController.
I am just following this link and I can parse successfully with their example youtube link. https://github.com/hellozimi/HCYoutubeParser
http://www.youtube.com/watch?v=8To-6VIJZRE
However, the problem is that I can't parse other youtube URL like this
http://www.youtube.com/watch?v=Ndy1J859n5I
Is it because of video duration? I would like to know how to solve this.
Code
// Gets an dictionary with each available youtube url
NSDictionary *videos = [HCYoutubeParser h264videosWithYoutubeURL:[NSURL URLWithString:#"http://www.youtube.com/watch?v=8To-6VIJZRE"]];
// Presents a MoviePlayerController with the youtube quality medium
MPMoviePlayerViewController *mp = [[[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL URLWithString:[videos objectForKey:#"medium"]]] autorelease];
[self presentModalViewController:mp animated:YES];
// To get a thumbnail for an image there is now a async method for that
[HCYoutubeParser thumbnailForYoutubeURL:url
thumbnailSize:YouTubeThumbnailDefaultHighQuality
completeBlock:^(UIImage *image, NSError *error) {
if (!error) {
self.thumbailImageView.image = image;
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[alert show];
}
}];
I have managed to successfully send videos from a class in Parse.com to an app. Is this what you are trying to do? If so, the above code is completely incorrect.
Related
In my app I have to implement save image feature. I have managed saving like this:
UIImage *image = [UIImage imageNamed:actualBackground];
UIImageWriteToSavedPhotosAlbum(
image, self,
#selector(thisImage:hasBeenSavedInPhotoAlbumWithError:usingContextInfo:),
nil);
/* ... */
- (void)thisImage:(UIImage *)image
hasBeenSavedInPhotoAlbumWithError:(NSError *)error
usingContextInfo:(void *)ctxInfo {
if (!error){
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
[self presentViewController:picker animated:YES completion:nil];
}
}
Unfortunately I have to check if file already exists to prevent redundant saving. Also this is necessary because saving multiple times the same image don't override one file, but it creates copy of it...
Do you know how to solve that problem?
SOLUTION:
According to Shravya Boggarapu answer I'm storing assetUrl in my NSUserDefaults. Complete code:
- (IBAction)onDownloadClick:(UIButton *)sender {
UIImage *image = [UIImage imageNamed:actualBackground];
NSString *key = [NSString stringWithFormat:#"assetsUrl %#", actualBackground];
NSString *savedValue =
[[NSUserDefaults standardUserDefaults] stringForKey:key];
NSURL *url = [NSURL URLWithString:savedValue];
if (url != nil) {
PHFetchResult *fetch =
[PHAsset fetchAssetsWithALAssetURLs:[NSArray arrayWithObject:url]
options:nil];
if ([fetch count]) {
UIAlertView *myAlert = [[UIAlertView alloc]
initWithTitle:nil
message:NSLocalizedString(#"Already downloaded", nil)
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[myAlert show];
return;
}
}
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library
writeImageToSavedPhotosAlbum:image.CGImage
orientation:(ALAssetOrientation)image.imageOrientation
completionBlock:^(NSURL *assetURL, NSError *error) {
[library assetForURL:assetURL
resultBlock:^(ALAsset *asset) {
NSLog(#"assetURL %#", assetURL);
NSString *ass = [assetURL absoluteString];
[[NSUserDefaults standardUserDefaults]
setObject:ass
forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
UIAlertView *myAlert = [[UIAlertView alloc]
initWithTitle:nil
message:NSLocalizedString(
#"Image downloaded", nil)
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[myAlert show];
}
failureBlock:^(NSError *error){
}];
}];
}
Hope it will help somebody.
I have a solution but not one that applies to every situation.
The thing about saving image to camera roll is that there is an assetURL created when you add the image to camera roll. Also, this asset URL is new every time, so if you save to camera roll, it will create a copy. The name of the image is also not retained.
If the image is added to the camera roll through your app in the first place, then you can store the assetURL as part of image information.
In my app, a dictionary is maintained for every image containing some critical info. This includes the assetURL of the image if it is saved to camera roll.
Once you have the URL, you can check its existence by using fetchAssetsWithALAssetURLs:options: function.
If the URL is nil or the asset when fetched is nil, it means that the image does not exist in the camera roll. So you can add anew.
There is no easy way to compare two images and if you found one that will surly a heavy task, instead of this we can compare image's meta data which we can write it to image and also read it from images.
You can use this repo for that
And ALAssetLibrary also have some properties to access an images meta info. If you google with this you will surly get some.
Like this one
I am having some trouble in sharing videos with airdrop. So, i am using the AssetLibrary like this :
else if (conformsToVideo) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Asset Loaded" message:#"Video One Loaded"
delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
self.mediaAsset = [AVAsset assetWithURL:[info objectForKey:UIImagePickerControllerMediaURL]];
UIActivityViewController * controller = [[UIActivityViewController alloc] initWithActivityItems:#[self.mediaAsset] applicationActivities:nil];
[self presentViewController:controller animated:YES completion:nil];
}
Maybe this is just not the way to do it, I don't know and I didn't find any tutorial about it so if you have one, I will gladly take it.
Now my problem is that when i select a video everything works fine until the UIActivityViewController pops out , i don't have any error but I can't use airdrop (nor any other service BTW) the only thing I can do is press the Cancel button of the UIAVController.
I am using iOS 8.3.
Thanks for your help
Ok, eventually I found an answer, maybe not the best but I put it there in case it helps someone cause I didn't find much on sharing video with airdrop :
NSURL * path = [info objectForKey:UIImagePickerControllerReferenceURL];
BOOL conformsToVideo = (UTTypeConformsTo((__bridge_retained CFStringRef) mediaType, kUTTypeAudiovisualContent));
if (conformsToVideo) {
[self.library assetForURL:path
resultBlock:^(ALAsset *asset) {
ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
NSString* outputDirectory = [NSString stringWithFormat:#"%#", NSTemporaryDirectory()];
NSString* pathToCopy = [outputDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#", assetRepresentation.filename]];
NSUInteger size = (NSUInteger)assetRepresentation.size;
NSMutableData* data = [NSMutableData dataWithLength:size];
NSUInteger bytesRead = [assetRepresentation getBytes:data.mutableBytes fromOffset:0 length:size error:nil];
NSURL * url = nil;
if ([data writeToFile:pathToCopy atomically:YES])
{
NSLog(#"Ok");
url = [NSURL fileURLWithPath:pathToCopy];
UIActivityViewController * controller = [[UIActivityViewController alloc] initWithActivityItems:#[url] applicationActivities:nil];
[self presentViewController:controller animated:YES completion:nil];
}
} failureBlock:^(NSError *error) {
NSLog(#"Failure to use the ALAsset");
}
];
}
I found the solution using this question : Share a video from asset library with AirDrop fails
I am merely adding the previous steps to the code given there, hope it can be useful.
My app takes pictures, saves them to the camera roll, and allows the modification of the EXIF metadata.
I use the AssetsLibrary to save and modify the photo (I can't use the new PhotoKit api because I need to modify the underlying EXIF, plus it's a legacy app and would require a lot of refactoring to change it).
I am using Xcode 6.3.1, with the iOS 8.3 SDK, deployment target of 6.1.
In iOS 8.2, all this worked.
In iOS 8.3, the editing of the metadata fails.
The app has permission in the Privacy settings to access Photos.
When a user modifies the photos, and the app tries to rewrite the photo, iOS 8.3 now shows the "Allow App to modify this photo" dialog. This dialog did not appear in iOS 8.2. If I click "Modify", the photo is saved, but the metadata is wiped. There is also no error returned from setImageData, and I check if the photo is editable.
Here is the code:
-(void)savePhoto:(ALAsset*)asset
{
ALAssetRepresentation* rep = [asset defaultRepresentation];
CGImageRef imageRef = [rep fullResolutionImage];
UIImage *image = [UIImage imageWithCGImage:imageRef];
NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
if ([asset isEditable])
{
[asset setImageData:imageData metadata:self.getMetadataDictionary completionBlock:^(NSURL *assetURL, NSError *error)
{
if (error!=nil)
[self showErrorDialog:error title:#"Error saving photo" ];
[self closeSaveDialog];
}];
}
else
{
[self closeSaveDialog];
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"Error saving photo" message:#"Photo is not editable" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}
When debugging this, I noticed a bug in my code where the photo size was being increased by 4x by the UIImageJPEGRepresentation, because it was resampling the photo, so I changed the code to grab the original image bytes, and just rewrite the metadata. But interestingly, I get a different error back, this time setImageData returns this error.
Description : "User denied access".
Underlying error : "ALAssetsLibraryErrorDomain" Code -3311.
FailureReason : "The user has denied the application access to their media".
Which is strange, because the app created the asset, and it has acccess to the camera roll.
Again, this code works in iOS 8.2:
-(void)savePhoto:(ALAsset*)asset
{
ALAssetRepresentation* rep = [asset defaultRepresentation];
// New way handling updating photos, doesn't modify image data at all, only metadata
Byte *buffer = (Byte*)malloc((unsigned long)rep.size);
NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:(NSUInteger)rep.size error:nil];
NSData *imageData = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
if ([asset isEditable])
{
[asset setImageData:imageData metadata:self.getMetadataDictionary completionBlock:^(NSURL *assetURL, NSError *error)
{
if (error!=nil)
[self showErrorDialog:error title:#"Error saving photo" ];
[self closeSaveDialog];
}];
}
else
{
[self closeSaveDialog];
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"Error saving photo" message:#"Photo is not editable" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}
I submitted a bug report to Apple, but havn't heard anything back.
This is similar to this question : setImageData fails in iOS 8.3
Does anyone know of a fix for this?
Thanks,
i've been using an html string for a long period for playing youtube videos through an UIWebView, the problem is i want to get notifications with playbackstate changed. i've decided to create an MPMoviePlayerController and play the youtube video through this, but cant seem to make it work. i'm using following code in viewdidload:
NSString *urlAddress = #"http://www.youtube.com/watch?v=m01MYOpbdIk";
NSURL *url = [NSURL URLWithString:urlAddress];
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGFloat height = [UIScreen mainScreen].bounds.size.height;
movie = [[MPMoviePlayerController alloc] initWithContentURL:url];
movie.scalingMode=MPMovieScalingModeAspectFill;
movie.view.frame = CGRectMake(0.0, 0.0, width, height);
[self.view addSubview:movie.view];
[movie play];
Gives me this error:
_itemFailedToPlayToEnd: {
kind = 1;
new = 2;
old = 0;
}
For me this library did work perfectly! https://github.com/hellozimi/HCYoutubeParser
moviePlayer = [[MPMoviePlayerController alloc] init];
moviePlayer.shouldAutoplay = YES;
moviePlayer.fullscreen = YES;
moviePlayer.repeatMode = MPMovieRepeatModeNone;
moviePlayer.controlStyle = MPMovieControlStyleDefault;
moviePlayer.movieSourceType = MPMovieSourceTypeFile;
moviePlayer.scalingMode = MPMovieScalingModeAspectFit;
After this call method.
[self callYouTubeURL:[NSString stringWithFormat:#"http://www.youtube.com/embed/%#",_urlcode]];
In this method parse youtube link.
- (void)callYouTubeURL:(NSString *)urlLink
{
NSURL *url = [NSURL URLWithString:urlLink];
actvity.hidden = NO;
[HCYoutubeParser thumbnailForYoutubeURL:url thumbnailSize:YouTubeThumbnailDefaultHighQuality completeBlock:^(UIImage *image, NSError *error) {
if (!error) {
[HCYoutubeParser h264videosWithYoutubeURL:url completeBlock:^(NSDictionary *videoDictionary, NSError *error) {
NSDictionary *qualities = videoDictionary;
NSString *URLString = nil;
if ([qualities objectForKey:#"small"] != nil) {
URLString = [qualities objectForKey:#"small"];
}
else if ([qualities objectForKey:#"live"] != nil) {
URLString = [qualities objectForKey:#"live"];
}
else {
[[[UIAlertView alloc] initWithTitle:#"Error" message:#"Couldn't find youtube video" delegate:nil cancelButtonTitle:#"Close" otherButtonTitles: nil] show];
return;
}
_urlToLoad = [NSURL URLWithString:URLString];
[self urlLoadintoPlayer];
}];
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"Dismiss" otherButtonTitles:nil];
[alert show];
}
}];
}
After this load newly parse url into player.
-(void)urlLoadintoPlayer
{
moviePlayer.contentURL = _urlToLoad;
}
Make use of custom LBYouTubePlayerViewController
It is a subclass of MPMoviePlayerViewController.
LBYouTubeView is just a small view that is able to display YouTube videos in a MPMoviePlayerController. You even have the choice between high-quality and standard quality stream.
It just loads the HTML code of YouTube's mobile website and looks for the data in the script tag.
LBYouTubeView doesn't use UIWebView which makes it faster and look cleaner.
Official way to play youtube videos: 1) UIWebView with the embed tag from Youtube the UIWebView's content.2) Using youtube-ios-player-helper[Google official way]
Unfortunately, there's no way to directly play a youtube video with MPMoviePlayerController because youtube does not expose direct links to the video files.
There are library which runs youtube videos through MPMoviePlayerController, but they are against the TOC of youtube. Hence simplest method is to go for youtube-ios-player-helper.
In case youtube-ios-player-helper pod doesn't work you can add YTPlayer.h/.m and assets folder to your project and write a bridge header with #import "YTPlayerView.h" and rest procedure you can follow on https://github.com/youtube/youtube-ios-player-helper
This definitely worked for me!
I'm asking the question like that because a UIAlertView will go off. Just not the AVAudioPlayer. Here's my code:
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
NSLog(#"MapViewController - didEnterRegion");
LocationReminder *theLocationReminder = [self.locationReminders objectForKey:region.identifier];
NSError *error;
NSURL *theUrl = [NSURL URLWithString:theLocationReminder.audioURI];
NSLog(#"theUrl = %#",theUrl);
AVAudioPlayer *thePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:theUrl error:&error];
NSLog(#"thePlayer.url = %#",thePlayer.url);
[thePlayer play];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"entered region..." message:#"You have Entered the Location." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
alert.tag = 2;
[alert show];
}
I checked the logs on the device after testing and while the Alert View does indeed show while I'm walking around testing it, the AVAudioPlayer wont play even though it has the correct URL.
I should mention that while I have enabled audio in the plist under Required Background Modes, I'm still in the app (so backgrounding wouldn't be an issue).
You are not maintaining a reference to thePlayer so it is getting deallocated (assuming you are using ARC). Possibly add a property in your class #interface block like
#property (nonatomic, strong) AVAudioPlayer *player;
and then instead of the line you have do
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:theUrl error:&error];
Hope this helps!