Access macOS Photo Library using Electron - node.js

I am trying to write an Electron application that lets users select photos from their photo library on macOS. My assumption is that the only way to access the photos without exporting them is by going into ~/Pictures/Photos Library.photoslibrary. However, when passing the file path of a photo to an <img> tag, I get net::ERR_ACCESS_DENIED error.
Is there a way to grant Electron application access to the user's photo library?

I've been writing code recently that accesses the PhotosLibrary. I did this by writing a native module that calls the PhotoKit API. If you go that direction there's going to be a steep learning curve as you'll likely be using Objective-C++ with the features and quirks of both C++ and Objective-C while trying to write something in JavaScript.
https://github.com/nodejs/node-addon-api
https://developer.apple.com/documentation/photokit?language=objc
With Apple's recent security features, you'll also need to somehow run your code inside of an app with the correct entitlements and values set in its Info.plist that will allow access to APIs.
In build/entitlements.mac.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- https://github.com/electron/electron-notarize#prerequisites -->
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<!-- https://github.com/electron-userland/electron-builder/issues/3940 -->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<!-- Allow app to access Photos Library using PhotoKit API -->
<key>com.apple.security.personal-information.photos-library</key>
<true/>
</dict>
</plist>
The following will be needed in your App's Info.plist:
<plist version="1.0">
<dict>
...
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs access to the photos library</string>
</dict>
</plist>
This can be done using electron-builder by adding the following value to the extendInfo extendInfo of your mac build settings in package.json.
{
...
"build": {
...
"mac": {
...
"extendInfo": {
...
"NSPhotoLibraryUsageDescription": "This app needs access to the photos library"
}
}
}
}
I hope this is gives you something to start with. Like I said above, this will come with a steep learning curve unless you're already familiar with JavaScript, native module development, Objective-C, C++ and Apple's APIs.

If you just need to read data from it for a one-off project, the photolibrary is really just a directory-like container around your photos, and an sqlite database for the metadata (faces, places and albums/folders). Right click-the photolibrary, show package contents, orig photos are under Masters/YYYY/MM/DD/IMG_XXXX.JPG format, and metadata is in database/photos.db, some tables you can query are RKMaster (filename/uuid of master files), RKAlbum, RKFace, RKMemory, RKPlace, RKFolder, RKVersion, RKKeyword, etc., get any free sqlite browser and you can figure out the rest.
You can also copy the .photolibrary file to Linux and scan its folder/sqlite file using pure node, you don't need any native modules to read it. Writing to it may try to fire some sqlite triggers that seem to belong to some macosx-proprietary extensions, so make backups first, try writes with extensions disabled, or just read and extract the images and metadata to some other format (raw jpeg/json files in a bucket somewhere) that are easy to collate and then (if you have to), reverse the process and write out to another photolibrary file once you get the relation between the tables and filesystem paths inside its container.

Related

How to set SVG file to image view in Android Xamarin?

I am using the below code to set the svg file as image view source.
imageView.SetImageDrawable(Resources.System.GetDrawable(Resource.Drawable.Typogy1));
it throws an exception. Please suggest me how to set the svg file to image view.
This example by James Mundy is really good for SVG usage in xamarin android.
But recently in a chat with him, he said and as well as, in my opinion, the best way of using SVG's in an Android application is using Vector Drawables. (Much better than using any libraries)
Vector drawable's are easy to use, maintain and are very lightweight in comparison to images which makes them even better.
An example of how to use it.
Creating SVG vector drawable's is very easy too, using this
Update:
Vector.xml
<?xml version="1.0" encoding="utf-8" ?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="96dp"
android:width="96dp"
android:viewportHeight="48"
android:viewportWidth="48" >
<group>
<path
android:fillColor="#393939"
android:pathData="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z" />
</group>
</vector>
Place this XML in a folder inside your Resources for eg your drawable folder or your layout folder, then use it as follows :
_yourImageView.SetImageResource(Resource.Drawable.Vector);
Goodluck!
Revert in case of any queries.

Deep-linking Xamarin iOS

I need to add deep linking feature to my app with Xamarin iOS.
My info.plist looks like this:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>domine.somthing/index.html?UV</string>
</array>
<key>CFBundleURLTypes</key>
<string>com.domine.somthing/index.html?UV</string>
</dict>
I seems not working due to "/" char in the URL.
How am I supposed to write CFBundleURLTypes and CFBundleURLSchemes URL values in order to make it work?
Deep links can returns users directly to your application.
After adding URI support to your application like you did, just clicking on a URL consisting of a scheme name for your application will launch your app, but it seems you are filling CFBundleURLSchemes and CFBundleURLTypeswith with wrong values.
CFBundleURLName - Usually, this corresponds to the bundle ID of your
application. Please see Apple's Development doc for more informations.
CFBundleURLSchemes - Array of strings containing URL scheme names,
usually at least your application's name (without spaces). Pay
attentiont to ensure these values are unique to your application.
Look at this sample:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.yourcompany.yourapp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>appname</string>
</array>
</dict>
</array>
Now clicking on a URL consisting of appname:// on the device will launch your app.
If you want to send a user to a specific part of your application, you need to add arguments to this URL, making a deep link. Here is a sample with a path and query:
appname://custompath?customquery=customvalue
When a user clicks a URL containing your scheme (appname://), your app's delegate is called and specifically the method OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)where you can handle schemas an execute correct app routing based on schema arguments (if provided).

Dynamically updating KML file based on certain conditions

I have developed the following network link for dynamic updates :
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<NetworkLink>
<name>Loads TEST.kml</name>
<Link>
<href>/home/desktop/farhan/test.kml</href>
</Link>
</NetworkLink>
</kml>
Test.kml file has two placemarks.
My network link file is getting successfully updated on changes in Test.kml file
Now what I want to do is to place an 'IF' condition so that if a certain thing is true then placemark color is 'Green' Else 'Red'.
What should I do here ?? Is there any Java API I need to follow.
Depends how/where you want to host the KML and what those conditions are.
If the styles and conditions are static then you can generate the KML pre-set with those styles as part of the KML. For example, if you want to generate the KML based on some conditions such as population size > 100K => red, size > 50K => blue, otherwise white where each generated Placemark would have the appropriate shared share or inline style.
One popular Java-API to generate KML is the Java API for KML (or JAK for short).
If the views (based on conditions) are "dynamic" and user-selected then you have a couple of options:
Generate different versions/views of the Placemarks based on different conditions with NetworkLinks and radioFolders to load a particular view. The KML could be statically generated once or dynamically via a web-service, servlet, CGI-script, etc.
For example, the USGS has an earthquake real-time feed with multiple NetworkLinks each with a different view/color/size scheme for the same data (e.g. color by age vs magnitude).
Check out http://earthquake.usgs.gov/earthquakes/feed/earthquakes.kml
Use the Google Earth API to load, create, and modify KML in context of HTML and JavaScript with Google Earth running as a plugin in your web browser. This requires some JavaScript programming.
Use NetworkLinks as shown in your example combined with NetworkLinkControl elements to globally change the shared styles. See NetworkLinkControl tutorial. You can't add NetworkLinkControl elements to your KML directly in Google Earth so you have to author your KML manually or programmatically.
use php , if you are aware of it and generate kml with the help of your table and also you can use various plugins and JAK as told by jason above.
What you can do is :
connect your mysql db ( having latnlong ) through jdbc.
2 .Run a loop i.e while loop which will add placemarks as per the latitude and longitude and add other necessary element of kml like :
Element ristyle = doc.createElement("IconStyle"); // this will create an Icon.
make a new file through :
Result dest = new StreamResult(new File("file location"));
this will create your new kml file
-- place a condition here which will show your condition like a normal loop.
5 Make a network link and refer your kml file in this for manual updation.
I think this should work

How to force network links to refresh, even after they encounter a file error?

I have a program that is constantly writing/updating a KML file, and I have a network link that points to this file. Under heavy load, if the Network Link attempts to access the KML file at the same time as my program is writing to the KML file, Google Earth stops any further auto-refreshing of that Network Link, assuming it to be broken. At this point, I then have to right-click the network link in the Places pane of Google Earth, and hit refresh, for the auto-updating to begin again.
My question is, is there any way to force Google Earth to keep reading from network links, even after a "no file detected" error? Because it is a real hassle having to manually hit refresh for the network link to become active again, when it seems that task could be easily automated.
I have made countless optimizations on my program's part to minimize the time period it spends writing to the KML file, however I have reached a practical limit, and must now figure out a way to fix this network link issue from within Google Earth.
Any replies, comments, or discussions would be greatly appreciated!
We had a similar problem a while ago. In searching the google-earth kml developers forums, a few people recommended using a network-link to a network-link approach like so:
KML file 1 that links to the updating kml file:
<Document>
<NetworkLink>
<Link> my_URL_to_the_updating_kml
<refreshMode>onInterval</refreshMode>
<refreshInterval>my_Inteval</refreshInterval>
</Link>
<name>My_Name</name>
<visibility>1</visibility>
</NetworkLink>
</Document>
KML file 2 that links to the network link file above:
<Document>
<NetworkLink>
<Link>
my_URL_to_the_first_network_link_file
<refreshMode>onInterval</refreshMode>
<refreshInterval>3600</refreshInterval>
</Link>
<name>My_Name</name>
<visibility>1</visibility>
</NetworkLink>
</Document>
If you're writing to a file on the server a simple trick is write to a temp file then swap with the target file like this:
write temp file temp.kml
delete target.kml
rename temp.kml target.kml
The renaming of the files will be nearly instantenous so very unlikely the Google Earth client will fetch it the momemt it's being swapped out. Depending on the OS of the server you could also use symbolic links to change the file reference in one operation.
UPDATE: in whatever is writing the file can you set the filename in a shared variable and serve that file via a "servlet" that opens a stream to the current file. The networklink can point to the servlet rather than the static KML file.

How to properly update Google Earth KML using NetworkLinkControl and the Java API for KML (JAK)?

I'm building an application that serves up data to a standalone Google Earth client. I want to send over an initial set of data, then update it dynamically using <NetworkLinkControl> and <Update><cookie> tags as things change on the server. I'm generating the KML using the Java API for KML (JAK) library. Unfortunately, while I can confirm that GE is refreshing my NetworkLink and pulling down the Updates I'm sending, none of my updates are showing up in GE. After lots of reading, it seems like it might be that the Update's <targetHref> might be the issue, but I'm 99.9% sure I'm sending over the same string.
Part of what has me confused is that I've seen conflicting info on whether or not the <cookie> element's value needs to be appended to <targetHref>. I did actually see one early prototype updating when I was serving up hand-written test KML files from a static server URL, so I suspect it's not. Actually, that's what's frustrating at the moment: I've seen updating work on my own machine, but can't get it working now with what looks like valid and correct KML.
The current setup looks like this (extraneous XML namespaces stripped for clarity; "$CLIENT_ID" is a GUID-like string):
Root KML file served from http://server/kml/${CLIENT_ID}:
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:xal="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"><NetworkLink>
<Link>
<href>http://server/kmlupdates/${CLIENT_ID}</href>
<refreshMode>onInterval</refreshMode>
<refreshInterval>1.0</refreshInterval>
<viewRefreshTime>0.0</viewRefreshTime>
<viewBoundScale>0.0</viewBoundScale>
</Link>
</NetworkLink></kml>
Initial content KML served from http://server/kmlupdates/${CLIENT_ID}:
<kml><NetworkLinkControl>
<minRefreshPeriod>0.0</minRefreshPeriod>
<maxSessionLength>-1.0</maxSessionLength>
<cookie>cookie=0|kmlRequestType=updates|projectID=6|lastUpdateSeenIndex=-1</cookie>
</NetworkLinkControl>
<Document id="myProject">
<name>My ProjectProject</name>
<Placemark id="pm1"><name>point1</name>
<Point><coordinates>-117.0,35.0</coordinates></Point>
</Placemark>
</Document></kml>
Later update KML served from http://server/kmlupdates/${CLIENT_ID}:
<kml><NetworkLinkControl>
<minRefreshPeriod>0.0</minRefreshPeriod>
<maxSessionLength>-1.0</maxSessionLength>
<cookie>cookie=0|kmlRequestType=updates|projectID=6|lastUpdateSeenIndex=0</cookie>
<Update>
<targetHref>http://server/kmlupdates/${CLIENT_ID}</targetHref>
<Change>
<Placemark targetId="pm1">
<name>Name changed by Update Change</name>
</Placemark>
</Change>
</Update>
</NetworkLinkControl></kml>
If anyone has any suggestions on what I'm missing here, I'd greatly appreciate it. Thanks!
My original version of the question left out two facts that turned out to be relevant: 1) I'm using the Java API for KML to generate this, and 2) the XML namespaces inside <kml>. I finally figured this out after reading this Google KML Group post for the umpteenth time.
The problem is the last XML namespace, "xmlns:xal". For some reason, removing that from the KML allows the <Update> tags to actually change the items in Google Earth. JAK doesn't let you change the namespaces, but you can strip it manually from the marshaled string.
Absolutely bizarre, but at least I found a solution.
As per https://developers.google.com/kml/documentation/kmlreference:
<xal:AddressDetails> is used by KML for geocoding in Google Maps only.
Currently, Google Earth does not use this element; use <address> instead.
I have some sample using different approach to do something else here maybe related to yours (as your purpose "send over an initial set of data, then update it dynamically using tags") :
https://sites.google.com/site/canadadennischen888/home/kml/auto-refresh-3d-tracking
Approach is that all changes are from server Restful service. Hope it helps. Details as :
How to make a dynamic Auto refresh 3D Tracking :
prepare a RestFul service to generate KML file from DB (KML sample as inside above link)
My other jsp code will generate a KMZ file which has a link to my Restful service. KMZ file has onInterval ( as in the bottom)
Jsp web page allow user to download KMZ file.
When Google Earth open KMZ file, Google Earth will auto refresh to get new data from that Restful service
Everytime refreshing, server will send the latest update KML data with new data to GE.
KMZ sample:
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<NetworkLink>
<name>Dennis_Chen_Canada#Hotmail.com</name>
<open>1</open>
<Link>
<href>http://localhost:9080/google-earth-project/rest/kml/10001/20002</href>
<refreshMode>onInterval</refreshMode>
</Link>
</NetworkLink>
</kml>

Resources