Get YouTube url from UIWebView - url

How do I get the url from a youtube video that is being played in a UIWebView?

Once the YouTube video was loaded into the webView, I created a button that the user can tap to copy the URL to a text field on the same page. The trick is to get the innerHTML of the page and then look for the YouTube ID. With this ID I then put it into a full url so that the user can save the link and play back the video at a later date without having to look for that video again on the YouTube site. (BTW, I have my own displayAlert that takes the given message and displays it to the user when there isn't a video loaded in the UIWebView or there are many videos loaded) Here is the code:
-(IBAction)copyURLAction {
if (webView) {
NSString* htmlString = [webView stringByEvaluatingJavaScriptFromString:#"document.documentElement.innerHTML"];
if (htmlString) {
NSString *youTubeID = #"data-youtube-id=\"";
int position = [htmlString rangeOfString:youTubeID].location + [youTubeID length];
if (position > 1) {
htmlString = [htmlString substringFromIndex:position];
position = [htmlString rangeOfString:#"\""].location;
htmlString = [htmlString substringToIndex:position];
NSString* tempPrefix = #"http://m.youtube.com/watch?v=";
NSString *youTubeString = [tempPrefix stringByAppendingString:htmlString];
textFieldView.text = youTubeString;
} else {
[[SharedObjectsSingleton sharedInstance] displayAlert:#"Can't Copy URL" :#"Can only copy the URL when a single video is being displayed."];
}
} else {
[[SharedObjectsSingleton sharedInstance] displayAlert:#"Can't get URL" :#"Nothing to copy."];
}
}
}

The code above used to work but i think youtube have changed the script so now in order for it to work you need to replace
#"data-youtube-id=\"" with #"i.ytimg.com/vi/"

Related

WKWebView media controls title displays Url

Is it possible to change the iOS media controls (lock screen, command center media) title from the Url when playing a video from WKWebView?
I have tryied the following and cannot get Now Playing info to change:
DispatchQueue.main.async() {
var nowPlayingInfo = [String : Any]()
nowPlayingInfo[MPMediaItemPropertyTitle] = "Some title"
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
Using https://www.youtube.com/iframe_api onYouTubeIframeAPIReady I can get the title and send it back to the app, but the issue is getting the now applying controls to update the title from the video Url.
The MPNowPlayingInfoCenter displays the title of the webpage as title of the currently played audio/video.
Since the homepage title is never visible in the webview, you can freely change it via Javascript as soon as a new audio or video file is played:
document.title = "your title";
The new homepage title will now be displayed on your NowPlayingInfoCenter as currently playing audio/video.
Works great for me.
You could also do it inside the app code (Objective-C):
[webView evaluateJavaScript:#"document.title = \"your title\";" completionHandler:nil];

How to get input value before WKWebView changes with Swift?

I have a basic iOS app developed in Swift. I'm using a WkWebView to load my website and require to get an inputs value before the page is changed so that I can save it into NSUserDefaults.
I can use the following to simply populate the inputs value:
webView.evaluateJavaScript("document.getElementById('myInput').value = 'Test';") { (result, error) in
if error != nil {
print(result)
}
Although how can I get the value of an input from a WKWebView?
Try adding a java script to capture the changes in webview and send the data asynchronously to wkconfiguration delegate to collect the data.
Adding JS to wkwebkit means lot of control, even you can add jquery as well.
This is just a hint where you can create your ideas.
-
(void) addJSScriptToWebConfig
{
NSString *JS = ...;
WKWebViewConfiguration *webViewConfig = [[SBWKWebViewConfiguration sharedInstance] configuration];
WKUserScript *script3 = [[WKUserScript alloc] initWithSource:JS injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
[webViewConfig.userContentController addUserScript:script3];
[webViewConfig.userContentController addScriptMessageHandler:self
name:#"callbackFunc"];

How to convert a video NSURL to an ALAsset?

Facebook sharing requires an ALAsset such as the following:
let content = FBSDKShareVideoContent()
//The videos must be less than 12MB in size.
let bundle = NSBundle.mainBundle()
let path = bundle.URLForResource("a", withExtension: "mp4")
let video = FBSDKShareVideo()
// doesn't work; needs to be an "asset url" (ALAsset)
//video.videoURL = path
content.video = video
let dialog = FBSDKShareDialog()
dialog.shareContent = content
dialog.show()
How is it possible to take a local bundle document, or an NSData object, and convert it to an ALAsset?
(My initial thinking was saving the video to the local camera roll, and then loading the list and selecting it, but that is unnecessary interface steps)
The documentation for an ALAsset states that
An ALAsset object represents a photo or a video managed by the Photo application.
so I'm pretty sure that you have to write the video to the camera roll before using it as an ALAsset. However, you don't need to open the camera roll and have a user pick the asset in order to use it. When writing to the ALAssetLibrary using
library.writeVideoAtPathToSavedPhotosAlbum(movieURL, completionBlock: { (newURL, error) -> Void
you get the asset url in that newUrl completion block variable. Use it in the Facebook sharing call
let content = FBSDKShareVideoContent()
content.video = FBSDKShareVideo(videoURL: newURL)
FBSDKShareAPI.shareWithContent(content, delegate: self)
NSLog("Facebook content shared \(content.video.videoURL)")
You can do this sharing inside the completion block if you so desire, or you can save the newUrl from the completion block and use it somewhere else.

Printing PDF on iOS, background image not printing

I'm attempting to print a PDF file in my Cordova application on iOS.
The file is generated using jsPDF in the Cordova app and then I've modified the katzer cordova-plugin-printer to accept the raw PDF data as a string, convert it to NSData and print it out.
- (void) printPDFFromData:(CDVInvokedUrlCommand*)command
{
if (!self.isPrintingAvailable)
{
return;
}
NSArray* arguments = [command arguments];
NSString* documentData = [arguments objectAtIndex:0];
NSData* pdfData = [documentData dataUsingEncoding:NSUTF8StringEncoding];
UIPrintInteractionController* controller = printController;
[self adjustSettingsForPrintController:controller];
controller.printingItem = pdfData;
[self openPrintController:controller];
[self commandDelegate];
}
Using the iOS print simulator (I don't have access to an AirPrint printer), the PDF appears to print out, except that the background image is not printed, just the vector drawings overlaying it.
The same raw output data when saved to a PDF file will display the background image and when you print that file, the background image is printed.
Is this just an anomaly of the printer simulator or do I need to somehow set the print controller to be able to print the image in the document?
I found a solution to the issue. Something was getting lost in the decoding of the string data from JavaScript into Objective-C.
To get around this I Base64 encoded the PDF document in my JS side before sending it off to the plugin:
var startIndexOfBase64Data = 28;
var base64Document = doc.output('dataurlstring').substring(startIndexOfBase64Data);
window.plugin.printer.printPDFFromData(base64Document);
Then I needed to add
NSData+Base64.m and NSData+Base64.h
from this sample project into my plugins directory to allow this line of code to convert the Base64 string into NSData:
NSData* pdfData = [NSData dataFromBase64String:documentData];
Then the document then printed out untainted.
Now I'm off to see if I can get it working with Android.

iOS Monotouch UIImagePickerController multiple photos / videos from camera

We're experiencing a strange problem with a UIImagePickerController. In our application users are able to fill out a series of forms and also attach images and videos within these forms.
We allow users to add multiple photos / videos either from the camera roll or to be captured at the time of filling the form out.
We're using the UIImagePickerController to do this. The problem occurs when 1 or 2 images / videos are taken with the camera.
Once 1 or 2 images / videos are captured when the camera screen is re-entered for a third time the image is static and doesn't update. The view is stuck at the last frame of whatever was captured last.
If the capture button is pressed then the image / video suddenly updates and has captured what the camera was pointing at. From then on the picker is good for another go behaving normally. Additionally selecting a picture / video from the camera roll appears to make everything behave again for another picture / video. Finally when the screen isn't responding and the user has selected to take a picture the view will shrink to a small rectangle within the view. The controller is being setup as follows:
private void SourceChosen(EventHandler<UIImagePickerMediaPickedEventArgs> captureEvent, int buttonIndex, string[] mediaTypes)
{
var picker = ConfigurePicker(mediaTypes, captureEvent);
if (CameraAvailable && buttonIndex == 0)
{
picker.SourceType = UIImagePickerControllerSourceType.Camera;
picker.CameraDevice = UIImagePickerControllerCameraDevice.Rear;
this.NavigationController.PresentViewController(picker, true, () => { });
}
if ((!CameraAvailable && buttonIndex == 0) || (CameraAvailable && buttonIndex == 1))
{
picker.SourceType = UIImagePickerControllerSourceType.PhotoLibrary;
this.NavigationController.PresentViewController(picker, false, () => { });
}
}
private UIImagePickerController ConfigurePicker(string[] mediaTypes, EventHandler<UIImagePickerMediaPickedEventArgs> captureEvent)
{
var mediaPicker = new UIImagePickerController();
mediaPicker.FinishedPickingMedia += captureEvent;
mediaPicker.Canceled += (sender, args) => mediaPicker.DismissViewController(true, () => { });
mediaPicker.SetBarDefaults();
mediaPicker.MediaTypes = mediaTypes;
return mediaPicker;
}
An example of a captureEvent is as follows:
void PhotoChosen(object sender, UIImagePickerMediaPickedEventArgs e)
{
UIImage item = e.OriginalImage;
string fileName = string.Format("{0}.{1}", Guid.NewGuid(), "png");
string path = Path.Combine(IosConstants.UserPersonalFolder, fileName);
NSData imageData = item.AsPNG();
CopyData(imageData, path, fileName, ViewModel.Images, ((UIImagePickerController)sender));
}
private void CopyData(NSData imageData, string path, string fileName, List<AssociatedItem> collectionToAddTo, UIImagePickerController picker)
{
byte[] imageBytes = new byte[imageData.Length];
System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, imageBytes, 0, Convert.ToInt32(imageData.Length));
File.WriteAllBytes(path, imageBytes);
AssociatedItem item = new AssociatedItem
{
StorageKey = fileName
};
collectionToAddTo.Add(item);
picker.DismissViewController(true, ReloadTables);
}
At the moment as you can see we're not holding a reference to the picker but we have tried variations of this code where we store a reference to the picker and dispose it after the CopyData method, we've added picker.Release(); after copydata and before the dispose (results in subsequent pickers crashing the application when displayed) and pretty much every other variation on the theme.
Does anyone have any idea why this might be occurring and how to fix it? It was my assumption that we might be running low on memory but neither disposing of it each time / only ever creating one instance and changing its mode from pictures to videos has any affect and we always see the same behaviour.
EDIT
Thanks to Kento and the below answer what we needed to get it all working as intended was something along the lines of:
public class PickerDelegate : UIImagePickerControllerDelegate
{
private readonly Action<UIImagePickerController, NSDictionary> _captureEvent;
public PickerDelegate(Action<UIImagePickerController, NSDictionary> captureEvent)
{
_captureEvent = captureEvent;
}
public override void FinishedPickingMedia(UIImagePickerController picker, NSDictionary info)
{
_captureEvent(picker, info);
}
}
Then to get an image
void PhotoChosen(UIImagePickerController picker, NSDictionary info)
{
UIImage item = (UIImage)info.ObjectForKey(UIImagePickerController.OriginalImage);
string fileName = string.Format("{0}.{1}", Guid.NewGuid(), "png");
string path = Path.Combine(IosConstants.UserPersonalFolder, fileName);
NSData imageData = item.AsPNG();
CopyData(imageData, path, fileName, ViewModel.Images, picker);
}
Or to get a video
void VideoChosen(UIImagePickerController picker, NSDictionary info)
{
var videoURL = (NSUrl)info.ObjectForKey(UIImagePickerController.MediaURL);
NSData videoData = NSData.FromUrl(videoURL);
string fileName = string.Format("{0}.{1}", Guid.NewGuid(), "mov");
string path = Path.Combine(IosConstants.UserPersonalFolder, fileName);
CopyData(videoData, path, fileName, ViewModel.Videos, picker);
}
I had this same problem.
The post here is not marked as the answer but it did solve it for me: https://stackoverflow.com/a/20035698/2514318
I'm guessing this is a bug w/ MonoTouch when using the FinishedPickingMedia event. I have read that there are leaks with using UIImagePickerController (regardless of using obj c or Mono) so I prefer to keep the instance around and re-use it. If you do re-create it each time, I would recommend disposing the previous instance.
Can anyone from Xamarin weigh in on if this is a bug or not?
This post helped me a lot, so I decided to make a very simples sample and post on github for anyone that may need it: https://github.com/GiusepeCasagrande/XamarinSimpleCameraSample

Resources