October 2008 Archives
Sat 18 Oct 2008 @ 18:46 (1224348396)
delicious backup
A slightly improved version of
this ruby script
you can use to backup your delicious bookmarks to an sqlite3 database.
#!/usr/bin/env ruby require 'rexml/document' require 'net/http' require 'net/https' require 'sqlite3' require 'json' dbfile_base = 'delicious_bkp_' rcfile_base = '.delicious_bkp.rc' agent = 'del.icio.us backup v0.2.1' schema = <<EOF create table bookmarks ( hash char(32) primary key, url varchar(1024), title varchar(1024), note varchar(2048), time timestamp ); create table tags (hash char(32), tag varchar(1024)); create index ix_tags_hash on tags (hash); create index ix_tags_tag on tags (tag); EOF insert_url = 'insert into bookmarks (hash, url, title, note, time) ' + 'values (?, ?, ?, ?, ?);' insert_tag = 'insert into tags (hash, tag) values (?, ?);' rcfile_path = File.join(ENV['HOME'], rcfile_base) if (not FileTest.exist?(rcfile_path)) puts "\nERROR: rc file not found!\n\n" puts "Create a file " + rcfile_path + " containing the following line:" puts "{ \"user\": \"username\", \"pass\": \"password\", \"dir\": \"/path/to/bkp\", \"max_bkp\": 0}\n\n" exit -1 end rcfile = File.open(rcfile_path) perms = sprintf("%o", rcfile.stat().mode)[-4..-1] if (perms != "0600") puts "\nERROR: rc file permissions are not safe!\n\n" exit -1 end begin j = JSON.parse(rcfile.read()) rescue JSON::ParserError puts "\nERROR: bad syntax in rc file\n\n" exit -1 end ['user', 'pass', 'dir', 'max_bkp'].each { |key| if (not j.has_key?(key)) puts "\nERROR: field " + key + " doesn't exist in rc file!\n\n" exit -1 end } if (not FileTest.directory?(j['dir'])) puts "\nERROR: Specified directory does not exist!\n\n" exit -1 end dbfile_base = dbfile_base + j['user'] + "_" dbfile_name = dbfile_base + Time.now.strftime("%Y-%m-%d.db") dbfile_path = File.join(j['dir'], dbfile_name) if (FileTest.exist?(dbfile_path)) puts "Backup " + dbfile_name + " exists, exiting." exit 0 end http = Net::HTTP.new('api.del.icio.us', 443) http.use_ssl = true xml = http.start { |http| req = Net::HTTP::Get.new('/v1/posts/all', {'User-Agent' => agent}) req.basic_auth(j['user'], j['pass']) http.request(req).body } doc = REXML::Document.new(xml) SQLite3::Database.open(dbfile_path).transaction { |db| db.execute_batch(schema) db.prepare(insert_url) { |url_stmt| db.prepare(insert_tag) { |tag_stmt| doc.elements.each('posts/post') { |el| url_stmt.execute(el.attributes['hash'], el.attributes['href'], el.attributes['description'], el.attributes['extended'], el.attributes['time']) el.attributes['tag'].split(' ').each { |tag| tag_stmt.execute(el.attributes['hash'], tag) } } } } } if (j['max_bkp'] > 0) d = Dir.new(j['dir']) b = [] re = Regexp.new(dbfile_base + "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\.db") d.each {|entry| if re.match(entry) then b.push(entry) end } if b.nitems > j['max_bkp'] b.sort()[0, (b.nitems - j['max_bkp'])].each {|olddb| File.unlink(File.join(j['dir'], olddb)) } end end