-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbookmarks.lisp
102 lines (90 loc) · 3.1 KB
/
bookmarks.lisp
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
(in-package #:cl-parse-bookmarks)
(defclass bookmark ()
((title
:accessor title
:initarg :title
:documentation "bookmark title")
(url
:accessor url
:initarg :url
:documentation "bookmark URL")
(icon
:accessor icon
:initarg :icon
:documentation "bookmark icon")
(add-date
:accessor add-date
:initarg :add-date
:documentation "bookmark add date")))
(defvar *bookmark-items* ()
"bookmarks instances list")
;; external interface
(defun list-bookmark-items ()
"list bookmarks with title ,url and add-date attributes"
(dolist (item *bookmark-items*)
(format t "Title: ~A~%URL: ~A~%Date: ~A~%~%"
(title item)
(url item)
(add-date item))))
(defun save-bookmark-items (filepath)
"save bookmark lists to a file"
(with-open-file (out (pathname filepath) :direction :output
:if-exists :supersede)
(with-standard-io-syntax
(dolist (item *bookmark-items*)
(print (list :title (title item)
:url (url item)
:icon (icon item)
:add-date (add-date item))
out)))))
(defun parse-bookmarks-from-html (html-path)
"parse exported chrome bookmarks html file"
(let* ((content (uiop:read-file-string (pathname html-path)))
(body (parse-html5 content)))
(traverse-nodes #'standard-recurse-p #'convert-to-bookmark body)))
(defun write-bookmarks-csv (filepath)
"write bookmarks to csv file"
(with-open-file (out (pathname filepath) :direction :output
:if-exists :supersede)
(dolist (item *bookmark-items*)
(cl-csv:write-csv-row
(list (title item)
(url item)
(icon item)
(add-date item))
:always-quote t
:stream out))))
;; internal
(defun traverse-nodes (recurse-p fn node)
"fn is applied to each visited node
recurse-p controls whether to visit the children of node"
(if node
(progn
(funcall fn node)
(if (funcall recurse-p node)
(element-map-children
(lambda (n-node)
(traverse-nodes recurse-p fn n-node))
node)))))
(defun convert-to-bookmark (node)
"visit node and convert attributes to make bookmark instance"
(when (equal (node-name node) "a")
(let ((title (node-value
(node-first-child node)))
(url (element-attribute node "href"))
(icon (element-attribute node "icon"))
(add-date (element-attribute node "add_date")))
(format t "~A~%" title)
(push (make-instance 'bookmark
:title title
:url url
:icon icon
:add-date (local-time:unix-to-timestamp
(parse-integer add-date)))
*bookmark-items*))))
(defun standard-recurse-p (node)
"returns true only if you aren't trying to recurse into a script,
style, or noscript tag."
(not (or (equalp (node-name node) "script")
(equalp (node-name node) "style")
(equalp (node-name node) "noscript"))))