Never been to TextSnippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world (or not, you can keep them private!)

About this user

« Newer Snippets
Older Snippets »
2 total  XML / RSS feed 

Ruby: Apple Aperture API

UPDATED WED Aug 15/07

PLEASE NOTE: requires the following modules "active_record", "rbosa" and "hpricot" and my own Imageshack API to work.

USAGE:

You can include/require this as a module in your own scripts, or as I do, use it directly from irb in the Terminal. Documentation is provided within the script itself, but essentially I use it to get these things done:

With Aperture launched, select the pictures of your choice (one or multiples makes no diff), then entering the following inside irb results in:

1. Aperture.reveal is essentially "Reveal in Finder"

2. Aperture.paths returns the Unix paths to the locations of the pics.

3. Aperture.open opens the pictures with an external default app (usually Preview).

4. If you provide the script with Photoshop's path, then:

Aperture.edit will open the original file directly in Photoshop without creating a separate version inside Aperture.

5. Aperture.ids will give you an array of the unique ID asssigned by Aperture to each pic within the ZRKVERSION table. What this is for is up to you to find out.

6. NEW: Imageshack.us upload/backup

Aperture.upload will upload the pic(s) to Imageshack.us and return the direct link/URL of the pic.

Upon first launch, a new metadata field is created inside Aperture's metadata panel called "Imageshack URL" (if you don't see it, inside Aperture, Ctrl-D --> Others). the uploaded pic's URL will be stored inside this field automatically.

The API directly interrogates Aperture's SQLite DB. Who knows, could be a nice place to start from to building other stuff.



require 'rubygems'
require 'active_record'
require 'rbosa'
require 'hpricot'
require 'open3'
require 'beckett/imageshack'

# --- GLOBALS --- #

HOME = ENV['HOME']
PATH_TO_PHOTOSHOP = "/Applications/Adobe\\ Photoshop\\ CS3/Adobe\\ Photoshop\\ CS3.app/Contents/MacOS/Adobe\\ Photoshop\\ CS3"

Aperture = OSA.app('Aperture')

# --- CONNECT TO CURRENT LIBRARY --- #

class LibraryConnect

  def initialize
    findLibrary
    connect unless $PATH_TO_DB.nil?
  end

  protected

  def findLibrary
  
  aperture_plist = HOME + '/Library/Preferences/com.apple.Aperture.plist'
  plistdump = HOME + '/Library/Caches/' + 'plistdump.tmp'
  plutil_error = Open3.popen3("plutil -convert xml1 -o #{plistdump} #{aperture_plist}") { |stdin,stdout,stderr| stderr.read }

  if plutil_error == ""
      doc = Hpricot(File.open(plistdump)) ; %x( rm #{plistdump} )
      relative_lib_path = (doc/:key).select { |key| key.inner_text == 'LibraryPath' }[0].next_sibling.inner_text
      $PATH_TO_LIB = relative_lib_path =~ /^~/ ? relative_lib_path.gsub(/^~/, HOME) : relative_lib_path
      $PATH_TO_DB = "#{$PATH_TO_LIB}/Aperture.aplib/Library.apdb"
      puts "Current library --> " + $PATH_TO_LIB
  else
      $PATH_TO_DB = nil
      puts "Cannot find library. Goodbye."
  end

  end

  def connect
    ActiveRecord::Base.establish_connection(
      :adapter => "sqlite3",
      :dbfile  => $PATH_TO_DB
    )
  end

end

# --- LIBRARY CLASSES-SQL TABLES MAPPINGS --- #

class Pictures < ActiveRecord::Base         # Contains the majority of non-metadata info on a picture
  set_table_name "ZRKVERSION"
  set_primary_key "ZUUID"
end

class Projects < ActiveRecord::Base         # Every pic only belongs to one project
  set_table_name "ZRKFOLDER"
  set_primary_key "ZUUID"
end

class Masters < ActiveRecord::Base          # Info on master images
  set_table_name "ZRKMASTER"
  set_primary_key "Z_PK"
end

class Files < ActiveRecord::Base            # Info on the actual image file 
  set_table_name "ZRKFILE"
  set_primary_key "Z_PK"
end

class Metadata < ActiveRecord::Base         # Metadata information tagged to every picture in library
  set_table_name "ZRKSEARCHABLEPROPERTY"
  set_primary_key "Z_PK"
end

class Albums < ActiveRecord::Base           # All albums within library
  set_table_name "ZRKPERSISTENTALBUM"
  set_primary_key "Z_PK"
end

class AlbumVersions < ActiveRecord::Base    # Foreign key that links a picture's Z_PK (from ZRKVERSION) to an
  set_table_name "Z_11VERSIONS"             # album's Z_PK in ZRKPERSISTENTALBUM
end

class Properties < ActiveRecord::Base       # Metadata categories
  set_table_name "ZRKPROPERTYIDENTIFIER"
  set_primary_key "Z_PK"
end

class Picture                               # Custom class that contains all relevant info on a picture

attr_reader :id, :project_id, :master_id, :file_id, :version_id, :width, :height, :filesize, :name, :moddate

def initialize(pic_id)

  # Query ZRKVERSION
  
  @id = pic_id
  pic = Pictures.find(pic_id)
  @project_id = pic.ZPROJECTUUID
  @master_id = pic.ZMASTER
  @file_id = pic.ZFILE
  @version_id = pic.Z_PK

  # Query ZRKFILE

  file = Files.find(@file_id)
  @width = pic.ZPIXELWIDTH
  @height = pic.ZPIXELHEIGHT
  @filesize = file.ZFILESIZE
  @name = file.ZNAME
  @moddate = realtime(file.ZFILEMODIFICATIONDATE_before_type_cast)

end

def filepath
    path = $PATH_TO_LIB + '/' + self.projectPath + '/' + self.importGroup + '/' + self.folderName + '/' + self.filename
end

def nixpath
    self.filepath.gsub(/ /,'\ ')
end

def open
  `open #{self.nixpath}`
end

def edit
  if PATH_TO_PHOTOSHOP != ""
    `open -a #{$PATH_TO_PHOTOSHOP} #{self.nixpath}`
  else
    `open #{self.nixpath}`
  end
end

def reveal
    puts nixpath
    `open #{File.dirname(self.nixpath)}`
end

def shack_uri
  pic_meta = Metadata.find( :first, :conditions => { :ZVERSION => @version_id, :ZPROPERTYIDENTIFIER => $SHACKURLPROP } )
  unless pic_meta == nil
    pic_meta.ZPROPERTYSPECIFICSTRING
  else
    return 'Does not exist.'
  end
end

def storeURI(url)
records = Metadata.find( :all, :conditions => { :ZVERSION => @version_id, :ZPROPERTYIDENTIFIER => $SHACKURLPROP } )
if  records.empty? == true
    new_uri = Metadata.create(
        :Z_ENT => 13,
        :Z_OPT => 1,
        :ZPROPERTYSPECIFICNUMBER =>  nil,
        :ZPROPERTYSPECIFICSTRING => url,
        :ZVALUETYPE => 3,
        :ZVERSION => @version_id,
        :ZPROPERTYIDENTIFIER => $SHACKURLPROP )
  new_uri.Z_PK
else
  records.each { |entry| Metadata.destroy(entry.Z_PK) }
  self.storeURI(url)
end

end

def realtime(sqtime)
  t = Time.at(sqtime.to_i + 978307200)      # => # Derives from: Time.utc(2001,"jan",1,0,0,0).to_i
  Time.utc(t.year,t.month,t.day,t.hour,t.min)
end

protected

def projectPath
    Projects.find(@project_id).ZLIBRARYRELATIVEPATH
end

def importGroup
   Masters.find(@master_id).ZIMPORTGROUP
end

def folderName
   Masters.find(@master_id).ZNAME
end

def filename
  Files.find(@file_id).ZNAME
end

end

class Album

def initialize(zpk)
  @album = Albums.find(zpk)
end

def add_count(n=0)
  @album.update_attributes(   :ZDATELASTSAVEDINDATABASE => (Time.now.to_f + 978307200.0),
                              :ZMETADATACHANGEDATE => (Time.now.to_f + 978307200.0),
                              :ZVERSIONCOUNT => @album.ZVERSIONCOUNT + n,
                              :ZCREATEDATE => @album.ZCREATEDATE_before_type_cast.to_f )
end

end

# --- INTERACTING WITH APERTURE FROM IRB --- #
# All operations are performed recursively on image(s) within the array returned by Aperture

class OSA::Aperture::Application

def new_prop(fieldName)

Properties.create(  :Z_ENT => 12,
                    :Z_OPT => 1,
                    :ZPROPERTYKEY => fieldName,
                    :ZPROPERTYTYPE => 7)
                    
return Properties.find_by_ZPROPERTYKEY(fieldName).Z_PK

end

def ids     # Returns the IDs used to query the ZRKVERSION table
  self.selection.collect { |pic| pic.id2 }
end

def paths   # Returns the filepaths of selected images
  self.ids.collect { |id| Picture.new(id).filepath }
end

def reveal  # 'Reveal in Finder' selected images
  self.ids.each { |id| Picture.new(id).reveal }
end

def open    # Opens selected images in Preview
  self.ids.each { |id| Picture.new(id).open }
end

def edit    # Opens the original file in Photoshop CS3 (if path provided or default)
  self.ids.each { |id| Picture.new(id).edit }
end

def upload(reveal=false)  # Uploads the image to Imageshack and returns the direct link to the pic, if called with true, then opens it in browser after upload

  self.ids.each do |id|
    pic = Picture.new(id)
    pic_mirror = ShackMirror.new(pic.filepath)
    pic.storeURI(pic_mirror.url)
    `open #{pic_mirror.url}` if reveal
    puts pic_mirror.url
  end

end

def print(name)
  self.ids.each { |id| puts Picture.new(id).send(name) }
end

end

# --- INITIALIZE --- #

LibraryConnect.new

$SHACKURLPROP = Properties.find_by_ZPROPERTYKEY("Imageshack URL") == nil ? Aperture.new_prop("Imageshack URL") : Properties.find_by_ZPROPERTYKEY("Imageshack URL").Z_PK

Ruby: Barebones Ruby Imageshack API

Usage:

pic_online = ShackMirror.new(local_path_of_pic)
pic_online.url # => returns direct link on Imageshack.

NOTE: if you give it a path beginning with http, then it will transload the image directly to Imageshack. Cool!

require 'rubygems'
require 'hpricot'
require 'net/http'
require 'uri'
require 'cgi'
require 'mime/types'

class ShackMirror

SHACK_ID = "REPLACE WITH YOUR OWN IMAGESHACK ID"
USER_AGENT = "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en) AppleWebKit/419 (KHTML, like Gecko) Safari/419.3"
BOUNDARY = '----------PuSHerInDaBUSH_$'

attr_reader :url

def initialize(img)
    raise NonImageTypeError, 'Expected image file.' unless img =~ /jpe?g|png|gif|bmp|tif|tiff|swf$/
    @img = img
    @url, @hosturi, @res = "","",""
    @header, @params = {}, {}
    @header['Cookie'] = "myimages=#{SHACK_ID}"
    @header['User-Agent'] = USER_AGENT
    @params['uploadtype'] = 'on'  
    @params['brand'] = ''
    @params['refer'] = ''
    @params['MAX_FILE_SIZE'] = '13145728'
    @params['optimage'] = '0'
    @params['rembar'] = '1'
    transfer
    getdirect
end

protected

def prepare_multipart ( params )
    fp = []
    params.each do |k,v|
    if v.respond_to?(:read)
      fp.push(FileParam.new(k,v.path,v.read))
      else fp.push(Param.new(k,v)) 
    end
  end
    query = fp.collect {|p| "--" + BOUNDARY + "\r\n" + p.to_multipart }.join("") + "--" + BOUNDARY + "--"
    return query
end

def prepFile(path_to_file)

  file = File.new(path_to_file)

  @header['Content-Type'] = "multipart/form-data, boundary=" + BOUNDARY + " "

  @params['url'] = 'paste image url here'
  @params['fileupload'] = file
  
  $query = prepare_multipart(@params)
  file.close

end

def locate(path)
  path !~ /^http/ ? "local" : "remote"
end

def upload( query, headers={} )
  Net::HTTP.start(@hosturi.host) do | http |
    http.post(@hosturi.path, query, headers);
  end
end

def transload(url)

  @header['Content-Type'] = 'form-data'

  @params['url'] = url
  @params['fileupload'] = ''
  
  postreq = Net::HTTP::Post.new(@hosturi.path, @header)
  postreq.set_form_data(@params)

  return Net::HTTP.new(@hosturi.host, @hosturi.port).start { |http| http.request(postreq) }

end

def transfer

case locate(@img)
  when "local"
    @hosturi = URI.parse('http://load.imageshack.us/index.php')
    prepFile(@img)
    @res = upload($query,@header)
  when "remote"
    @hosturi = URI.parse('http://imageshack.us/transload.php')
    @res = transload(@img)
end

end

def getdirect
  doc = Hpricot(@res.body)
  @url = (doc/"//input").last['value']
end

end

class Param

  attr_accessor :k, :v

  def initialize( k, v )
    @k = k
    @v = v
  end
  
  def to_multipart
    return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"\r\n\r\n#{v}\r\n"
  end

end

class FileParam

  attr_accessor :k, :filename, :content

  def initialize( k, filename, content )
    @k = k
    @filename = filename
    @content = content
  end
  
  def to_multipart
    return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"; filename=\"#{filename}\"\r\n" +
    "Content-Type: #{MIME::Types.type_for(@filename)}\r\n\r\n" + content + "\r\n"
  end

end
« Newer Snippets
Older Snippets »
2 total  XML / RSS feed