]> www.wagner.pp.ru Git - oss/ljdump.git/blob - convertdump.py
now replace lj comm as well as lj-cut tags
[oss/ljdump.git] / convertdump.py
1 #!/usr/bin/python
2
3 # Copyright 2009, Sean M. Graham (www.sean-graham.com)
4 # All rights reserved.
5
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are
8 # met:
9
10 # - Redistributions of source code must retain the above copyright notice,
11 #   this list of conditions and the following disclaimer.
12
13 # - Redistributions in binary form must reproduce the above copyright notice,
14 #   this list of conditions and the following disclaimer in the documentation
15 #   and/or other materials provided with the distribution.
16
17 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18 # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20 # EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
23 # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26 # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
27
28 import xml.dom.minidom 
29 import os
30 import codecs
31 import sys
32 import getopt
33 import re
34
35 from time import strptime, strftime
36
37 def getNodeText(doc, nodename):
38     rc = ""
39
40     try:
41         nodelist = doc.getElementsByTagName(nodename)[0].childNodes
42     except:
43         return ""
44
45     for node in nodelist:
46         if node.nodeType == node.TEXT_NODE:
47             rc = rc + node.data
48
49     return rc
50
51 def appendTextNode(doc, parent, nodename, value):
52     nodeValue = value
53
54     # make sure value is properly encoded
55     try:
56         bytes = nodeValue.encode("UTF-8")
57     except:
58         bytes = nodeValue.encode("cp1252")
59         nodeValue = unicode(bytes, "UTF-8")
60
61     element = doc.createElement(nodename)
62
63     if( nodeValue != "" ): 
64         textNode = doc.createTextNode(nodeValue)
65         element.appendChild(textNode)
66
67     parent.appendChild(element)
68
69
70 def addEntryForId(outDoc, element, username, id, includeSecure):
71     entryFile = open("%s/L-%s" % (username,id), "r")
72     inDoc = xml.dom.minidom.parse(entryFile)
73
74     # Create an entry element
75     entry = outDoc.createElement("entry")
76
77     # Create an itemid element
78     appendTextNode(outDoc, entry, "itemid", getNodeText(inDoc,"itemid"))
79
80     # Create an eventtime element
81     appendTextNode(outDoc, entry, "eventtime", getNodeText(inDoc, "eventtime"))
82
83     # Create an subject element
84     appendTextNode(outDoc, entry, "subject", getNodeText(inDoc, "subject"))
85
86     # Create an event node (special case because for some reason there are two
87     # 'event' elements in the pydump output, which is probably LJ's fault)
88     event = inDoc.getElementsByTagName("event")[0]
89     eventText = getNodeText(event, "event")
90
91     appendTextNode(outDoc, entry, "event", replaceLJTags(eventText))
92
93     security = getNodeText(inDoc, "security")
94
95     if(security != ""):
96         # don't append this entry unless the user provided the argument
97         if(includeSecure == False):
98             print("omitting secure entry: L-%s" % id)
99             return 
100         else:
101             if(security == "usemask"):
102                 print("including allowmask entry: L-%s" % id)
103
104                 # Create an allowmask element 
105                 maskText = getNodeText(inDoc, "allowmask")
106
107                 if(maskText != ""):
108                     appendTextNode(outDoc, entry, "allowmask", maskText)
109                 else:
110                     appendTextNode(outDoc, entry, "allowmask", "0")
111             else:
112                 print("including private entry: L-%s" % id)
113
114         appendTextNode(outDoc, entry, "security", security)
115
116     # Create a taglist element
117     appendTextNode(outDoc, entry, "taglist", getNodeText(inDoc, "taglist"))
118
119     # XXXSMG: make sure there is a comment file before trying to do anything
120     # with it
121     addCommentsForId(outDoc, entry, username, id)
122
123     element.appendChild(entry)
124
125 def addCommentsForId(outDoc, entry, username, id):
126     try: 
127         commentFile = open("%s/C-%s" % (username,id), "r")
128     except IOError:  # there are no comments for this entry
129         return
130
131     inDoc = xml.dom.minidom.parse(commentFile)
132
133     comments = inDoc.getElementsByTagName("comment")
134
135     for comment in comments:
136         outComment = outDoc.createElement("comment")
137         entry.appendChild(outComment)
138
139         # add the item id for the comment
140         appendTextNode(outDoc, outComment, "itemid", 
141             getNodeText(comment, "id"))
142
143         # convert the time string
144         timeString = getNodeText(comment, "date")
145         if( timeString != "" ):
146             inDate = strptime(timeString, "%Y-%m-%dT%H:%M:%SZ")
147             outDate = strftime("%Y-%m-%d %H:%M:%S", inDate)
148             appendTextNode(outDoc, outComment, "eventtime", outDate)
149         else:
150             emptyTime = outDoc.createElement("eventtime")
151             outComment.appendChild(emptyTime)
152
153         # Create an subject element
154         appendTextNode(outDoc, outComment, "subject", 
155             getNodeText(comment, "subject"))
156
157         # Create an event element
158         bodyText = getNodeText(comment, "body")
159         appendTextNode(outDoc, outComment, "event", replaceLJTags(bodyText))
160
161         # Create the author element
162         author = outDoc.createElement("author")
163         outComment.appendChild(author)
164
165         try:
166             cUser = getNodeText(comment, "user")
167         except:
168             cUser = "anonymous"
169
170         appendTextNode(outDoc, author, "name", cUser)
171         appendTextNode(outDoc, author, "email", cUser + "@livejournal.com")
172         
173         # Create the parent_itemid
174         parentId = getNodeText(comment, "parentid")
175         if(parentId != ""): 
176             appendTextNode(outDoc, outComment, "parent_itemid", parentId)
177
178 def replaceLJTags(entry):
179     rv = entry
180
181     # replace lj user tags
182     userRE = re.compile('<lj user="(.*?)" ?/?>', re.IGNORECASE)
183     rv = re.sub(userRE, '<a href="http://\\1.livejournal.com/" class="lj-user">\\1</a>', rv) 
184
185     # replace lj comm tags
186     commRE = re.compile('<lj comm="(.*?)" ?/?>', re.IGNORECASE)
187     rv = re.sub(commRE, '<a href="http://community.livejournal.com/\\1/" class="lj-comm">\\1</a>', rv) 
188
189     # replace lj-cut tags
190     namedCutRE = re.compile('<lj-cut +text="(.*?)" ?/?>', 
191                             re.IGNORECASE|re.DOTALL)
192     rv = re.sub(namedCutRE, '<!--more \\1-->', rv)
193     
194     cutRE = re.compile('<lj-cut>', re.IGNORECASE)
195     rv = re.sub(cutRE, '<!--more-->', rv)
196
197     cutRE = re.compile('</lj-cut>', re.IGNORECASE)
198     rv = re.sub(cutRE, '', rv)
199
200     return rv
201
202
203 def usage():
204     print( "Usage: convertdump.py [arguments]" )
205     print( """
206 This will convert a pydump archive into something compatible with the
207 WordPress LiveJournal importer.  This is the same format used by the Windows
208 ljArchive exporter.
209
210 Arguments:
211     -u  --user      username of archive to process [required]
212     -l  --limit     limit the number of entries in each xml file (default 250)
213     -i  --insecure  include private and protected entries in the output
214     -h  --help      show this help page
215
216 Example:
217     ./convertdump.py --user stevemartin --limit 200 --insecure
218 """)
219
220
221 def main(argv): 
222     username = ""
223     entryLimit = 250
224     includeSecure = False;
225
226     if( len(argv) == 0 ):
227         usage()
228         sys.exit(2)
229
230     try:
231         opts, args = getopt.getopt(sys.argv[1:], "hu:l:i", ["help",
232                                                             "user=",
233                                                             "limit=",
234                                                             "insecure"])
235     except getopt.GetoptError, err:
236         # print help information and exit:
237         print str(err) # will print something like "option -a not recognized"
238         usage()
239         sys.exit(2)
240
241     for o, a in opts:
242         if o == "-v":
243             verbose = True
244         elif o in ("-u", "--user"):
245             username = a
246         elif o in ("-l", "--limit"):
247             entryLimit = int(a)
248         elif o in ("-i", "--insecure"):
249             print( "Warning:  Including secure entries in XML output" )
250             includeSecure = True
251         elif o in ("-h", "--help"):
252             usage()
253             sys.exit()
254         else:
255             assert False, "unhandled option"
256
257     userDir = os.listdir(username)
258
259     highNum = -1
260     entryArray = []
261
262     # get the list of entries
263     for file in userDir:
264         if file.startswith("L-"):
265             entryNum = int(file.replace("L-",""))
266
267             entryArray.append(entryNum)
268
269             if( highNum < entryNum ):
270                 highNum = entryNum
271
272     entryArray.sort()
273
274     # Create the minidom document
275     outDoc = xml.dom.minidom.Document()
276
277     # Create the <livejournal> base element
278     ljElement = outDoc.createElement("livejournal")
279     outDoc.appendChild(ljElement)
280
281     currentFileEntry = 0
282
283     # start processing entries
284     for entry in entryArray:
285         addEntryForId(outDoc, ljElement, username, entry, includeSecure)
286
287         currentFileEntry += 1
288
289         if( currentFileEntry == entryLimit or entry == entryArray[-1] ):
290
291             f = open("%s - %s.xml" % (username, entry), "w")
292             tempXML = outDoc.toxml("UTF-8")
293             f.write(tempXML)
294             
295             currentFileEntry = 0
296
297             # Create the minidom document
298             outDoc = xml.dom.minidom.Document()
299
300             # Create the <livejournal> base element
301             ljElement = outDoc.createElement("livejournal")
302             outDoc.appendChild(ljElement)
303
304 if __name__ == "__main__":
305     main(sys.argv[1:])
306