public
Last active

Watch a Scrivener project for changes and update Marked.app

  • Download Gist
scrivwatcher.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
#!/usr/bin/env ruby
# scrivwatch.rb by Brett Terpstra, 2011
# Modifications to merge into one file for Markdown viewing in Marked by BooneJS, 2012
# Modified to use REXML and add titles by Brett Terpstra, 2012 <https://gist.github.com/1676667>
#
# Watch a Scrivener project for changes and update a preview file for Marked
# Usage: scrivwatch.rb /path/to/document.scriv
# <http://markedapp.com>
 
require 'fileutils'
require 'rexml/document'
debug = false
droplet = true
 
def check_running()
newpid = %x{ps ax|grep Marked|grep -v grep|awk '{print $1}'}
return newpid.empty? ? false : true
end
 
def get_children(ele,path,files,depth)
ele.elements.each('*/BinderItem') do |child|
# Ignore docs set to not include in compile
included = REXML::XPath.first( child, "MetaData/IncludeInCompile" ).text
if included == "Yes"
id = child.attributes["ID"]
# passing type, would eventually use to control header/title output
type = child.attributes["Type"]
title = child.elements.to_a[0].text
file = "#{path}/Files/Docs/#{id}.rtf"
filepath = File.exists?(file) ? file : false
files << { 'path' => filepath, 'title' => title, 'depth' => depth, 'type' => type }
end
get_children(child,path,files,depth+1)
end
end
 
# Take the path to the scrivener file and open the internal XML file.
def get_rtf_file_array(scrivpath)
scrivpath = File.expand_path(scrivpath)
scrivx = File.basename("#{scrivpath}", ".scriv") + ".scrivx"
scrivxpath = "#{scrivpath}/#{scrivx}"
# handle cases where the package has been renamed after creation
unless File.exists?(scrivxpath)
scrivxpath = %x{ls -1 #{scrivpath}/*.scrivx|head -n 1}.strip
end
files = []
doc = REXML::Document.new File.new(scrivxpath)
doc.elements.each('ScrivenerProject/Binder/BinderItem') do |ele|
if ele.attributes['Type'] == "DraftFolder"
get_children(ele,scrivpath,files,1)
end
end
 
return files
end
 
trap("SIGINT") { exit }
 
unless droplet || debug
if (ARGV.length != 1 || ARGV[0] !~ /\.scriv\/?$/)
puts "Usage: scrivwatch.rb /path/to/document.scriv"
exit
end
end
 
if debug
path = File.expand_path("~/Dropbox/ScrivTest.scriv")
else
path = File.expand_path(ARGV[0].gsub(/\/$/,''))
end
 
sw_name = path.gsub(/.*?\/([^\/]+)\.scriv$/,"\\1")
sw_target = File.expand_path("~/ScrivWatcher")
Dir.mkdir(sw_target) unless File.exists?(sw_target)
sw_note = "#{sw_target}/ScrivWatcher - #{sw_name}.md"
FileUtils.touch(sw_note)
%x{open -a Scrivener "#{path}" && open -a /Applications/Marked.app "#{sw_note}"}
 
first = true
 
while true do # repeat infinitely
unless check_running
puts "Marked quit, exiting"
exit
end
notetext = ""
files = get_rtf_file_array(path)
# track any file changes in folder
new_hash = files.collect {|f| [ f['path'], File.stat(f['path']).mtime.to_i ] if f['path'] } # create a hash of timestamps
hash ||= new_hash
diff_hash = new_hash - hash # check for changes
# tracking the xml file for ordering changes as well
new_xml_time = File.stat(path).mtime.to_i
xml_time ||= new_xml_time
diff_xml_time = new_xml_time - xml_time
unless first == false && diff_hash.empty? && diff_xml_time == 0 # if changes were found in the timestamps
first = false
hash = new_hash
xml_time = new_xml_time
# All attempts to pass more than 1 file to textutil failed. Do it one-at-a-time.
files.each{ |f|
p f if debug
note = f['path'] ? %x{/usr/bin/textutil -convert txt -stdout "#{f['path']}"} : ''
leader = ""
f['depth'].times { leader += "#" } unless f['depth'].nil?
notetext = notetext + "#{leader} #{f['title']}\n\n" + note + "\n\n"
}
# write the result to the preview file
watch_note = File.new("#{sw_note}",'w')
watch_note.puts notetext
watch_note.close
end
sleep 1
end

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.