From 15db6fba10b655f45effc7d7e9d8d4d551518036 Mon Sep 17 00:00:00 2001 From: Pierre-Luc Paour Date: Wed, 26 Nov 2008 06:48:25 +0000 Subject: [PATCH] 2008-11-25 Pierre-Luc Paour (1.5.1-b43) * Fixed finding ImageMagick (convert) on Mac and presumably Linux. * Added support for importing from Aperture: the user must first select the pictures to be imported in Aperture, and GR will use AppleScript to tell Aperture to export them, along with the captions, then GR will load the pictures. --- ApertureToGallery.applescript | 98 ++++++++++ ChangeLog | 8 + build.xml | 2 +- com/gallery/GalleryRemote/GalleryRemote.java | 4 + com/gallery/GalleryRemote/MainFrame.java | 51 +++++ .../prefs/GalleryProperties.java | 10 + .../resources/GRResources.properties | 5 + .../GalleryRemote/util/ImageUtils.java | 177 ++++++++++++++---- defaults.properties | 4 +- 9 files changed, 315 insertions(+), 44 deletions(-) create mode 100644 ApertureToGallery.applescript diff --git a/ApertureToGallery.applescript b/ApertureToGallery.applescript new file mode 100644 index 0000000..723d949 --- /dev/null +++ b/ApertureToGallery.applescript @@ -0,0 +1,98 @@ +on run argv + set destination to "/tmp/" + set outputFile to "ApertureToGallery.txt" + set decision to "" + + try + repeat + do shell script "/bin/ls " & destination & outputFile + tell application "System Events" + activate + set decision to display dialog "The file used to communicate between Aperture and Gallery Remote is already present. This may mean Gallery Remote has not yet finished processing Aperture's output." buttons {"Continue anyway", "Retry", "Cancel"} default button 3 with icon caution + end tell + log decision + set decision to button returned of decision + if decision is not "Retry" then + exit repeat + end if + end repeat + on error errStr number errorNumber + if errorNumber is -128 then + set decision to "Cancel" + return "user cancelled" + end if + end try + + tell application "Aperture" + set myselected to the selection + set captions to {} + repeat with imageversion in myselected + if exists the value of the IPTC tag named "Caption/Abstract" of imageversion then + set myCaption to the value of the IPTC tag named "Caption/Abstract" of imageversion + copy myCaption to the end of captions + else + copy "" to the end of captions + end if + end repeat + export myselected using export setting "JPEG - Original size" to destination + set exportedFiles to the result + + end tell + + set fileRef to open for access (POSIX file (destination & outputFile)) with write permission + set eof fileRef to 0 + try + repeat with i from 1 to count exportedFiles + set tmpFile to POSIX path of item i of exportedFiles + write tmpFile to fileRef + write "\t" & item i of captions to fileRef + write "\r" to fileRef + end repeat + end try + close access fileRef + + return "done" +end run + +(*script gallery + property myGalleryUrl : "http://www.paour.com/gallery2/" + property myGalleryUser : "paour" + property myGalleryPass : "charsome" + property authToken : 0 + + to login() + set myCommand to "curl -c /tmp/curlCookies -b /tmp/curlCookies -g \"" & myGalleryUrl & "main.php?g2_controller=remote:GalleryRemote&g2_form[cmd]=login&g2_form[protocol_version]=2.0&g2_form[uname]=" & myGalleryUser & "&g2_form[password]=" & myGalleryPass & "\"" + log myCommand + + do shell script myCommand + set loginResult to the result + + set AppleScript's text item delimiters to "=" + repeat with p in paragraphs of loginResult + if word 1 of p = "status" then + set loginStatus to word 3 of p + else if word 1 of p = "auth_token" then + set authToken to word 3 of p + end if + end repeat + + if loginStatus ­ "0" then + display alert "Login failed" + end if + + return loginStatus + end login + + to upload of galleryPicturePath into galleryAlbum + log galleryPicturePath + log authToken + end upload + + to listAlbums() + set myCommand to "curl -c /tmp/curlCookies -b /tmp/curlCookies -g \"" & myGalleryUrl & "main.php?g2_controller=remote:GalleryRemote&g2_form[cmd]=fetch-albums-prune&g2_form[protocol_version]=2.0&g2_form[no_perms]=yes&g2_form[auth_token]=" & authToken & "\"" + log myCommand + + do shell script myCommand + set listAlbumResult to the result + end listAlbums +end script*) \ No newline at end of file diff --git a/ChangeLog b/ChangeLog index b58c1d2..f6c8331 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2008-11-25 Pierre-Luc Paour (1.5.1-b43) + + * Fixed finding ImageMagick (convert) on Mac and presumably Linux. + * Added support for importing from Aperture: the user must first + select the pictures to be imported in Aperture, and GR will use + AppleScript to tell Aperture to export them, along with the captions, + then GR will load the pictures. + 2008-09-16 Pierre-Luc Paour (1.5.1-b42) * Fixed blinking when dropping to the album tree. diff --git a/build.xml b/build.xml index f644bce..3887962 100644 --- a/build.xml +++ b/build.xml @@ -79,7 +79,7 @@ - + diff --git a/com/gallery/GalleryRemote/GalleryRemote.java b/com/gallery/GalleryRemote/GalleryRemote.java index 31c2a9c..a67d51f 100644 --- a/com/gallery/GalleryRemote/GalleryRemote.java +++ b/com/gallery/GalleryRemote/GalleryRemote.java @@ -33,6 +33,7 @@ import java.util.Enumeration; import java.util.Arrays; import java.util.Iterator; +import java.util.Properties; import java.lang.reflect.Method; /** @@ -100,6 +101,9 @@ protected void initializeGR() { // log system properties new GalleryProperties(System.getProperties()).logProperties(Log.LEVEL_INFO, "SysProps"); + // log system environment + new GalleryProperties(System.getenv()).logProperties(Log.LEVEL_INFO, "SysEnv"); + // log properties properties.logProperties(Log.LEVEL_TRACE, "UsrProps"); diff --git a/com/gallery/GalleryRemote/MainFrame.java b/com/gallery/GalleryRemote/MainFrame.java index 1e903eb..1cc889b 100644 --- a/com/gallery/GalleryRemote/MainFrame.java +++ b/com/gallery/GalleryRemote/MainFrame.java @@ -110,6 +110,7 @@ public class MainFrame extends JFrame JSplitPane jAlbumPictureDivider = new JSplitPane(); JButton jUploadButton = new JButton(); JButton jBrowseButton = new JButton(); + JButton jApertureImport = new JButton(); JButton jSortButton = new JButton(); JComboBox jSortCombo = new JComboBox(); JButton jNewAlbumButton = new JButton(); @@ -157,6 +158,7 @@ public class MainFrame extends JFrame public static final String CARD_PICTURE = "picture"; public static final String CARD_ALBUM = "album"; + private File source; public void initMainFrame() { macOSXRegistration(); @@ -499,6 +501,7 @@ public void run() { // if the selected album is uploading, disable everything boolean enabled = !inProgress && currentAlbum != null && jAlbumTree.getModel().getChildCount(jAlbumTree.getModel().getRoot()) >= 1; jBrowseButton.setEnabled(enabled && currentAlbum.getCanAdd()); + jApertureImport.setEnabled(enabled && currentAlbum.getCanAdd()); jPictureInspector.setEnabled(enabled); jPicturesList.setEnabled(enabled && currentAlbum.getCanAdd()); jNewAlbumButton.setEnabled(!inProgress && currentGallery != null && currentGallery.hasComm() @@ -620,6 +623,45 @@ public void browseAddPictures() { } } + public void importApertureSelection() { + jStatusBar.startProgress(StatusUpdate.LEVEL_UNINTERUPTIBLE, 0, 100, GRI18n.getString(MODULE, "apertureStartImport"), true); + jStatusBar.setInProgress(true); + new Thread() { + public void run() { + ArrayList resultList = ImageUtils.importApertureSelection(); + if (resultList == null || resultList.size() == 0) { + jStatusBar.stopProgress(StatusUpdate.LEVEL_UNINTERUPTIBLE, GRI18n.getString(MODULE, "apertureCancelImport")); + jStatusBar.setInProgress(false); + return; + } + + ArrayList pictures = new ArrayList(); + + Iterator i = resultList.iterator(); + while (i.hasNext()) { + String line = (String) i.next(); + int j = line.indexOf('\t'); + if (j != -1) { + String imagePath = line.substring(0, j); + String caption = line.substring(j + 1); + + source = new File(imagePath); + ImageUtils.addToDelete(source); + Picture p = new Picture(getCurrentGallery(), source); + p.setCaption(caption); + pictures.add(p); + } + } + + getCurrentAlbum().addPictures(pictures); + preloadThumbnails(pictures.iterator()); + + jStatusBar.stopProgress(StatusUpdate.LEVEL_UNINTERUPTIBLE, GRI18n.getString(MODULE, "apertureDoneImport")); + jStatusBar.setInProgress(false); + } + }.start(); + } + public void addPictures(File[] files, boolean select) { addPictures(null, files, -1, select); } @@ -975,6 +1017,9 @@ private void jbInit() jBrowseButton.setText(GRI18n.getString(MODULE, "brwsBtnTxt")); jBrowseButton.setActionCommand("Browse"); jBrowseButton.setToolTipText(GRI18n.getString(MODULE, "brwsBtnTip")); + jApertureImport.setText(GRI18n.getString(MODULE, "apertureBtnTxt")); + jApertureImport.setActionCommand("ApertureImport"); + jApertureImport.setToolTipText(GRI18n.getString(MODULE, "apertureBtnTip")); //jSortAlternativesButton.setText(GRI18n.getString(MODULE, "sortBtnTxt")); jSortCombo.setActionCommand("SortAlternative"); jSortCombo.setToolTipText(GRI18n.getString(MODULE, "sortAlternativesBtnTip")); @@ -1092,6 +1137,9 @@ private void jbInit() this.getContentPane().add(jBottomPanel, new GridBagConstraints(0, 2, 1, 1, 1.0, 0.0 , GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 5, 5, 5), 0, 0)); jBottomPanel.add(jBrowseButton, null); + if (GalleryRemote.IS_MAC_OS_X) { + jBottomPanel.add(jApertureImport, null); + } JPanel sortPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0)); sortPanel.add(jSortButton); sortPanel.add(jSortCombo); @@ -1173,6 +1221,7 @@ private void jbInitEvents() { jSortButton.addActionListener(this); jSortCombo.addActionListener(this); jBrowseButton.addActionListener(this); + jApertureImport.addActionListener(this); jNewGalleryButton.addActionListener(this); //jGalleryCombo.addActionListener( this ); jAlbumTree.addTreeSelectionListener(this); @@ -1390,6 +1439,8 @@ public void actionPerformed(ActionEvent e) { newAlbum(); } else if (command.equals("Browse")) { browseAddPictures(); + } else if (command.equals("ApertureImport")) { + importApertureSelection(); } else if (command.equals("Upload")) { uploadPictures(); } else if (command.equals("SortAlternative")) { diff --git a/com/gallery/GalleryRemote/prefs/GalleryProperties.java b/com/gallery/GalleryRemote/prefs/GalleryProperties.java index d3a5d6b..ec3806f 100644 --- a/com/gallery/GalleryRemote/prefs/GalleryProperties.java +++ b/com/gallery/GalleryRemote/prefs/GalleryProperties.java @@ -55,6 +55,16 @@ public GalleryProperties(Properties p) { super(p); } + public GalleryProperties(Map p) { + super(); + + Iterator names = p.keySet().iterator(); + while (names.hasNext()) { + String name = (String) names.next(); + super.setProperty(name, (String) p.get(name)); + } + } + public GalleryProperties() { } diff --git a/com/gallery/GalleryRemote/resources/GRResources.properties b/com/gallery/GalleryRemote/resources/GRResources.properties index d5b136d..4a27829 100644 --- a/com/gallery/GalleryRemote/resources/GRResources.properties +++ b/com/gallery/GalleryRemote/resources/GRResources.properties @@ -21,6 +21,11 @@ MainFrame.albums = Albums MainFrame.panel1 = Destination Gallery MainFrame.brwsBtnTxt = Add pictures... MainFrame.brwsBtnTip = Find images to add to the currently selected album. This button is disabled, if the currently selected album is read-only or you're not logged in. +MainFrame.apertureBtnTxt = Aperture Import +MainFrame.apertureBtnTip = Import the current selection of pictures in Aperture. Aperture must be started and you must have selected the pictures you want to import. +MainFrame.apertureStartImport = Importing from Aperture +MainFrame.apertureCancelImport = Cancelled Aperture import +MainFrame.apertureDoneImport = Aperture Import successful MainFrame.sortBtnTxt = Sort MainFrame.sortBtnTip = Resort images in the current album MainFrame.sortAlternativesBtnTip = Choose the sort order criteria diff --git a/com/gallery/GalleryRemote/util/ImageUtils.java b/com/gallery/GalleryRemote/util/ImageUtils.java index 3149929..74e96c7 100644 --- a/com/gallery/GalleryRemote/util/ImageUtils.java +++ b/com/gallery/GalleryRemote/util/ImageUtils.java @@ -96,6 +96,7 @@ public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, in return false; } }; + public static final String APERTURE_TO_GALLERY_SCPT = "ApertureToGallery.applescript"; public static Image load(String filename, Dimension d, int usage) { return load(filename, d, usage, false); @@ -848,6 +849,90 @@ public static Dimension getPictureDimension(Picture p) { } } + public static ArrayList importApertureSelection() { + File script = new File(System.getProperty("java.io.tmpdir"), APERTURE_TO_GALLERY_SCPT); + + if (!script.exists()) { + // copy the script file from our bundle to /tmp + InputStream scriptResource = GalleryRemote.class.getResourceAsStream("/" + APERTURE_TO_GALLERY_SCPT); + if (scriptResource == null) { + try { + scriptResource = new FileInputStream(APERTURE_TO_GALLERY_SCPT); + } catch (FileNotFoundException e) { + Log.logException(Log.LEVEL_ERROR, MODULE, e); + } + + if (scriptResource == null) { + Log.log(Log.LEVEL_ERROR, MODULE, "Can't find " + APERTURE_TO_GALLERY_SCPT); + return null; + } + } + + BufferedReader scriptInStream = new BufferedReader(new InputStreamReader( + scriptResource)); + BufferedWriter scriptOutStream = null; + + try { + String line; + scriptOutStream = new BufferedWriter(new FileWriter(script)); + while ((line = scriptInStream.readLine()) != null) { + scriptOutStream.write(line); + scriptOutStream.write("\n"); + } + } catch (IOException e) { + Log.logException(Log.LEVEL_ERROR, MODULE, e); + } finally { + try { + scriptInStream.close(); + if (scriptOutStream != null) { + scriptOutStream.close(); + } + } catch (IOException f) {} + } + + toDelete.add(script); + } + + // run script + if (exec("osascript " + script.getAbsolutePath()) != 0) { + return null; + } + + // load results + File resultFile = new File(System.getProperty("java.io.tmpdir"), "ApertureToGallery.txt"); + ArrayList resultList = new ArrayList(); + + if (!resultFile.exists()) { + return null; + } else { + BufferedReader resultReader = null; + try { + resultReader = new BufferedReader(new FileReader(resultFile)); + String line = null; + + while ((line = resultReader.readLine()) != null) { + resultList.add(line); + } + } catch (IOException e) { + Log.logException(Log.LEVEL_ERROR, MODULE, e); + } finally { + if (resultReader != null) { + try { + resultReader.close(); + resultFile.delete(); + } catch (IOException e) { + } + } + } + } + + return resultList; + } + + public static void addToDelete(File f) { + toDelete.add(f); + } + static { tmpDir = new File(System.getProperty("java.io.tmpdir"), "thumbs-" + System.getProperty("user.name")); @@ -890,61 +975,71 @@ public static Dimension getPictureDimension(Picture p) { imIgnoreErrorCode = p.getBooleanProperty("im.ignoreErrorCode", imIgnoreErrorCode); Log.log(Log.LEVEL_INFO, MODULE, "imIgnoreErrorCode: " + imIgnoreErrorCode); - if (imPath.indexOf('/') == -1 && imPath.indexOf('\\') == -1 - && System.getProperty("os.name").toLowerCase().indexOf("windows") != -1) { + if (imPath.indexOf('/') == -1 && imPath.indexOf('\\') == -1) { + // unqualified path, let's investigate + + if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1) { // we're on Windows with an abbreviated path: look up IM in the registry - StringBuffer output = new StringBuffer(); - int retval = exec("reg query HKLM\\Software\\ImageMagick\\Current /v BinPath", output); - - if (retval == 0) { - Pattern pat = Pattern.compile("^\\s*BinPath\\s*REG_SZ\\s*(.*)", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); - //Pattern pat = Pattern.compile("BinPath", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); - Matcher m = pat.matcher(output.toString()); - if (m.find()) { - imPath = m.group(1) + "\\" + imPath; - - if (!imPath.endsWith(".exe")) { - imPath += ".exe"; + + StringBuffer output = new StringBuffer(); + int retval = exec("reg query HKLM\\Software\\ImageMagick\\Current /v BinPath", output); + + if (retval == 0) { + Pattern pat = Pattern.compile("^\\s*BinPath\\s*REG_SZ\\s*(.*)", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + //Pattern pat = Pattern.compile("BinPath", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE); + Matcher m = pat.matcher(output.toString()); + if (m.find()) { + imPath = m.group(1) + "\\" + imPath; + + if (!imPath.endsWith(".exe")) { + imPath += ".exe"; + } + + Log.log(Log.LEVEL_INFO, MODULE, "Found ImageMagick in registry. imPath is now " + imPath); } + } else { + // most likely, we don't have reg.exe, try regedit.exe - Log.log(Log.LEVEL_INFO, MODULE, "Found ImageMagick in registry. imPath is now " + imPath); - } - } else { - // most likely, we don't have reg.exe, try regedit.exe + File tempFile = File.createTempFile("gr_regdump", null); - File tempFile = File.createTempFile("gr_regdump", null); + retval = exec("regedit /E \"" + tempFile.getPath() + "\" \"HKEY_LOCAL_MACHINE\\Software\\ImageMagick\\Current\"", output); - retval = exec("regedit /E \"" + tempFile.getPath() + "\" \"HKEY_LOCAL_MACHINE\\Software\\ImageMagick\\Current\"", output); + if (retval == 0) { + BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(tempFile), "UTF-16")); + String line; + Pattern pat = Pattern.compile("^\\\"BinPath\\\"=\\\"(.*)\\\"", Pattern.CASE_INSENSITIVE); + while ((line = br.readLine()) != null) { + Matcher m = pat.matcher(line); + if (m.find()) { + imPath = m.group(1) + "\\" + imPath; - if (retval == 0) { - BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(tempFile), "UTF-16")); - String line; - Pattern pat = Pattern.compile("^\\\"BinPath\\\"=\\\"(.*)\\\"", Pattern.CASE_INSENSITIVE); - while ((line = br.readLine()) != null) { - Matcher m = pat.matcher(line); - if (m.find()) { - imPath = m.group(1) + "\\" + imPath; - - if (!imPath.endsWith(".exe")) { - imPath += ".exe"; - } + if (!imPath.endsWith(".exe")) { + imPath += ".exe"; + } - Log.log(Log.LEVEL_INFO, MODULE, "Found ImageMagick in registry. imPath is now " + imPath); + Log.log(Log.LEVEL_INFO, MODULE, "Found ImageMagick in registry. imPath is now " + imPath); - break; + break; + } } + + br.close(); } - br.close(); + tempFile.delete(); } - - tempFile.delete(); } - } - if (imPath.indexOf('/') == -1 && imPath.indexOf('\\') == -1) { - Log.log(Log.LEVEL_CRITICAL, MODULE, "ImageMagick path is not fully qualified, " + - "presence won't be tested until later"); + // try to validate that IM works + int exitValue = exec(new String[] {imPath}); + + if ((exitValue != 0 && !imIgnoreErrorCode)) { + if (exitValue != -1) { + // don't kill IM if it's just an InterruptedException + Log.log(Log.LEVEL_CRITICAL, MODULE, "ImageMagick doesn't seem to be working. Disabling"); + stopUsingIM(); + } + } } else if (!new File(imPath).exists()) { Log.log(Log.LEVEL_CRITICAL, MODULE, "Can't find ImageMagick Convert at the above path"); stopUsingIM(); diff --git a/defaults.properties b/defaults.properties index d709e3f..60cfe00 100644 --- a/defaults.properties +++ b/defaults.properties @@ -322,6 +322,6 @@ updateUrlBeta=http://gallery.sourceforge.net/gallery_remote_version_check_beta.p # # --- Do not edit below this line --- # -version=1.5.1-b42 -releaseDate=2008/09/16 +version=1.5.1-b43 +releaseDate=2008/11/25 aboutText=Gallery Remote\n \n \nA part of the Gallery Open-Source Project\nhttp://gallery.sourceforge.net\n \n \nMaintained by:\n \nPierre-Luc Paour\n \n \nInitial version by Chris Smith\n \n \nContributors:\n \nTim Miller\nDolan Halbrook\nMarkus Cozowicz\nScott Gartner\nAmedeo Paglione\nChris Schwerdt\nSeth Ladd\n \n \nArtwork by Ross A. Reyman\n \n \nBundled software:\n \nImageMagick\nJava look and feel Graphics Repository icons\njpegtran, Guido Vollbeding's version\nMetadataExtractor\nSaverBeans