Skip to content

Instantly share code, notes, and snippets.

@dantonnoriega
Last active April 14, 2016 18:14
Show Gist options
  • Save dantonnoriega/4060f994f81e064a821287b893be7a10 to your computer and use it in GitHub Desktop.
Save dantonnoriega/4060f994f81e064a821287b893be7a10 to your computer and use it in GitHub Desktop.
A ruby script to convert a Scrivener project (.scriv) into a single Markdown (.md). NOTE: This is my first experience with Ruby. Don't ask me anything too complicated because I just took the code, inferred the needed pieces, trimmed the fat, and adjust until I got the result!
#!/usr/local/bin/ruby
# SOURCE: scrivwatch.rb by Brett Terpstra, 2011, https://gist.github.com/ttscoff/1676667
# REPURPOSED AND RENAMED BY: Danton Noriega, April 2016
# Instructions:
# 1. place 'scriv2md.rd' in same directory as .scriv project
# 2. give permissions to 'scriv2md.rd':
# chmod u+x ./scriv2md.rd
# 3. use!
# ./scriv2md.rd /path/to/document.scriv
require 'fileutils'
require 'rexml/document'
require 'optparse'
def usage
puts "Usage: scriv2md.rb /path/to/document.scriv"
exit
end
if ARGV[0] =~ /\.scriv\/?$/
path = File.expand_path(ARGV[0].gsub(/\/$/,''))
else
usage
end
# FUNCTIONS
def get_children(ele,path,files,depth)
ele.elements.each('*/BinderItem') do |child|
# Ignore docs set to not include in compile
includetag = REXML::XPath.first( child, "MetaData/IncludeInCompile" )
if !includetag.nil? && includetag.text == "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
# BEGIN CONVERSION
sw_name = File.basename(path,".scriv")
sw_note = "./#{sw_name}.md"
# Create cache
sw_cache_dir = File.expand_path("./#{sw_name}-cache")
Dir.mkdir(sw_cache_dir) unless File.exists?(sw_cache_dir)
sw_cache = File.expand_path("#{sw_cache_dir}")
# Clear cache
if File.exists?(sw_cache)
File.delete(*Dir["#{sw_cache}/*"]) if Dir.glob("#{sw_cache}/*").length > 0
Dir.rmdir(sw_cache)
end
Dir.mkdir(sw_cache)
files = get_rtf_file_array(path)
# loop through files and create copies. store in cache.
cachefiles = []
files.each{ |f|
if f['path']
cachefile = sw_cache + "/" + File.basename(f['path'],'.rtf') + ".md"
note = f['path'] ? %x{/usr/bin/textutil -convert txt -stdout "#{f['path']}"} : ''
leader = ""
notetext = leader + note + "\n\n"
File.open(cachefile,"w"){|cf| cf.puts notetext }
cachefiles.push(cachefile)
end
}
# export text into .md file
File.open(sw_note,"w"){|f| f.puts cachefiles.map{|s| IO.read(s)} }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment