Skip to content

Commit 0efb216

Browse files
committed
Version 0.8 of vk-backup
* A number of speed optimizations * Generated self appid with many permissions requirements * Added known error return codes, like access restrictions * Enabled json database sort keys * Fixed copy_history processing * Added request comments for photos, wall, videos * Rewrited update of media * Added user updated filed to know, if it already updated this session
1 parent 22b9fb9 commit 0efb216

File tree

5 files changed

+97
-25
lines changed

5 files changed

+97
-25
lines changed

lib/Api.py

+19-3
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616

1717
c.log('debug', 'Init Api')
1818

19+
# Session start time
20+
_START_TIME = long(time.time())
21+
1922
# Vk application ID
20-
_CLIENT_ID = '2951857'
23+
_CLIENT_ID = '4603710'
2124

2225
# Get token & user_id by login
23-
(_TOKEN, _USER_ID) = vk_auth.auth(c.cfg('user'), c.cfg('password'), _CLIENT_ID, "messages")
26+
(_TOKEN, _USER_ID) = vk_auth.auth(c.cfg('user'), c.cfg('password'), _CLIENT_ID, "messages,audio,docs,video,photos,wall,friends")
2427

2528
# Last time api call to prevent service overloading
2629
_LAST_API_CALL = 0
@@ -39,7 +42,16 @@ def request(method, params):
3942
url = "https://api.vk.com/method/%s?%s" % (method, urlencode(params))
4043
data = json.loads(urllib2.urlopen(url, None, 30).read())
4144
if 'response' not in data:
42-
raise Exception('no correct response while calling api method "%s", data: %s' % (method, data))
45+
if 'error' in data:
46+
c.log('warning', 'Api responded error: %s' % data['error']['error_msg'])
47+
if data['error']['error_code'] in [7, 15, 212]:
48+
return
49+
elif data['error']['error_code'] in [10]:
50+
continue
51+
else:
52+
raise Exception('unknown error code %i, "%s", data: %s' % (data['error']['error_code'], method, data))
53+
else:
54+
raise Exception('no correct response while calling api method "%s", data: %s' % (method, data))
4355
break
4456
except Exception as e:
4557
c.log('warning', 'Retry request %i (3): %s' % (retry, str(e)))
@@ -55,3 +67,7 @@ def getUserId():
5567
global _USER_ID
5668
return str(_USER_ID)
5769

70+
def getStartTime():
71+
global _START_TIME
72+
return _START_TIME
73+

lib/Database.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def store(self):
3030
if not os.path.isdir(os.path.dirname(path)):
3131
os.makedirs(os.path.dirname(path))
3232
with codecs.open(path, 'w', 'utf-8') as outfile:
33-
json.dump(self.data[i], outfile, indent=1, ensure_ascii=False)
33+
json.dump(self.data[i], outfile, indent=1, ensure_ascii=False, sort_keys=True)
3434

3535
def load(self, subdir = None):
3636
path = self.path if subdir == None else os.path.join(self.path, subdir)

lib/Media.py

+70-16
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
from Database import Database
1919

20+
import Api
21+
2022
class Media(Database):
2123
class Downloader(threading.Thread):
2224
def __init__(self, queue, report):
@@ -83,7 +85,7 @@ def download(self):
8385
return self.success
8486

8587
def stopDownloads(self):
86-
c.log('debug', 'Stopping download threads')
88+
c.log('debug', 'Stopping download threads (%i)' % len(self.threads))
8789
for i in self.threads:
8890
i.stop()
8991

@@ -125,14 +127,15 @@ def loadAttachments(self, data):
125127
if 'attachment' in data:
126128
attachments.append(data['attachment'])
127129
if 'copy_history' in data:
128-
self.loadAttachments(data['copy_history'])
130+
for subdata in data['copy_history']:
131+
self.loadAttachments(subdata)
129132
for attach in attachments:
130133
c.log('debug', 'Processing %s' % attach['type'])
131134
funcname = 'process' + attach['type'].title()
132135
if funcname in dir(self):
133136
getattr(self, funcname)(attach[attach['type']])
134137
else:
135-
c.log('error', ' unable to find attachment processing function "Media.%s"' % funcname)
138+
c.log('error', ' media processing function "Media.%s" is not implemented' % funcname)
136139
c.log('debug', str(attach))
137140

138141
def addDownload(self, url, path = None):
@@ -165,15 +168,59 @@ def preprocess(self, data, data_type):
165168
path = os.path.join(data_type, str(mydata['id']))
166169

167170
if path in self.data:
168-
return None
171+
return path
172+
169173
self.data[path] = mydata
170174

171175
return path
172176

177+
def requestComments(self, data, data_type, owner_id):
178+
if str(owner_id) != Api.getUserId():
179+
return
180+
181+
c.log('debug', 'Requesting comments for %s %i' % (data_type, data['id']))
182+
183+
if data_type == 'photo':
184+
api_method = 'photos.getComments'
185+
api_id_name = 'photo_id'
186+
elif data_type == 'video':
187+
api_method = 'video.getComments'
188+
api_id_name = 'video_id'
189+
elif data_type == 'wall':
190+
api_method = 'wall.getComments'
191+
api_id_name = 'post_id'
192+
else:
193+
c.log('warning', 'Unable to request comments for %s %i - not implemented' % (data_type, data['id']))
194+
return
195+
196+
if 'comments' not in data:
197+
data['comments'] = {}
198+
if not isinstance(data['comments'], dict):
199+
data['comments'] = {}
200+
201+
req_data = {'owner_id': int(owner_id), api_id_name: int(data['id']), 'count': 100, 'offset': 0}
202+
203+
while True:
204+
subdata = Api.request(api_method, req_data)
205+
if subdata == None:
206+
return
207+
count = subdata['count']
208+
subdata = subdata['items']
209+
for d in subdata:
210+
data['comments'][str(d['date'])] = d
211+
self.loadAttachments(data['comments'][str(d['date'])])
212+
213+
req_data['offset'] += 100
214+
if req_data['offset'] >= count:
215+
break
216+
173217
def processPhoto(self, data):
218+
c.log('debug', 'Processing photo media')
174219
path = self.preprocess(data, 'photo')
175-
if path != None:
220+
if 'localpath' not in self.data[path]:
176221
url = None
222+
if 'url' in self.data[path]:
223+
url = self.data[path]['url']
177224
size = 0
178225
for key in self.data[path].keys():
179226
if key.startswith('photo_'):
@@ -188,46 +235,53 @@ def processPhoto(self, data):
188235

189236
self.data[path]['url'] = url
190237
self.data[path]['localpath'] = self.addDownload(self.data[path]['url'])
238+
self.requestComments(self.data[path], 'photo', self.data[path]['owner_id'])
191239

192240
def processDoc(self, data):
241+
c.log('debug', 'Processing doc media')
193242
path = self.preprocess(data, 'doc')
194-
if path != None:
243+
if 'localpath' not in self.data[path]:
195244
self.data[path]['localpath'] = self.addDownload(self.data[path]['url'])
196245

197246
def processAudio(self, data):
247+
c.log('debug', 'Processing audio media')
198248
path = self.preprocess(data, 'audio')
199-
if path != None:
249+
if 'localpath' not in self.data[path]:
200250
self.data[path]['localpath'] = self.addDownload(self.data[path]['url'])
201251

202252
def processWall(self, data):
203-
c.log('debug', 'Processing wall attachments')
253+
c.log('debug', 'Processing wall media')
254+
data['comments'].pop('count', None)
255+
data['comments'].pop('can_post', None)
256+
self.requestComments(data, 'wall', data['from_id'])
204257
self.loadAttachments(data)
205258

206259
def processGeo(self, data):
207260
self.preprocess(data, 'geo')
208-
c.log('debug', 'Skipping geo attachment - no data to download')
261+
c.log('debug', 'Skipping geo media - no data to download')
209262

210263
def processVideo(self, data):
211-
self.preprocess(data, 'video')
212-
c.log('debug', 'Skipping video attachment - size of the file is too big')
264+
path = self.preprocess(data, 'video')
265+
self.requestComments(self.data[path], 'video', self.data[path]['owner_id'])
266+
c.log('debug', 'Skipping video media - size of the file is too big')
213267

214268
def processSticker(self, data):
215269
self.preprocess(data, 'sticker')
216-
c.log('debug', 'Skipping sticker attachment - idiotizm')
270+
c.log('debug', 'Skipping sticker media - idiotizm')
217271

218272
def processLink(self, data):
219-
c.log('debug', 'Skipping link attachment - no data to download')
273+
c.log('debug', 'Skipping link media - no data to download')
220274

221275
def processPoll(self, data):
222276
self.preprocess(data, 'poll')
223-
c.log('debug', 'Skipping poll attachment - no data to download')
277+
c.log('debug', 'Skipping poll media - no data to download')
224278

225279
def processNote(self, data):
226280
self.preprocess(data, 'note')
227-
c.log('debug', 'Skipping note attachment - no data to download')
281+
c.log('debug', 'Skipping note media - no data to download')
228282

229283
def processPresent(self, data):
230284
self.preprocess(data, 'present')
231-
c.log('debug', 'Skipping present attachment - stupid present')
285+
c.log('debug', 'Skipping present media - stupid present')
232286

233287
S = Media()

lib/Users.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,15 @@ def addUser(self, newdata):
4141
'data': { long(time.time()): newdata },
4242
}
4343
else:
44+
if self.data[newdata_id]['updated'] > Api.getStartTime():
45+
return
4446
user_data = self.data[newdata_id]['data']
4547
if len(set(user_data[max(user_data)].items()) & set(newdata.items())) != len(newdata):
4648
c.log('debug', 'Adding new data for user %s' % newdata_id)
4749
user_data[long(time.time())] = newdata
4850

4951
self.requestProfilePhotos(newdata_id)
52+
self.data[newdata_id]['updated'] = long(time.time())
5053

5154
def requestProfilePhotos(self, user_id):
5255
c.log('debug', 'Requesting profile photos')
@@ -102,7 +105,7 @@ def requestBlog(self, user_id):
102105
data = data['items']
103106
for d in data:
104107
self.data[user_id]['blog'][str(d['date'])] = d
105-
Media.loadAttachments(self.data[user_id]['blog'][str(d['date'])])
108+
Media.processWall(self.data[user_id]['blog'][str(d['date'])])
106109

107110
req_data['offset'] += 100
108111
if req_data['offset'] >= count:

vk-backup.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/python
22
# -*- coding: UTF-8 -*-
3-
'''VK-Backup 0.7.0
3+
'''VK-Backup 0.8.0
44
55
Author: Rabit <[email protected]>
66
License: GPL v3
@@ -46,7 +46,7 @@ def store(self):
4646
Chats.store()
4747
Media.store()
4848

49-
with open(os.path.join(self.path, 'backup.json'), 'w') as outfile:
49+
with open(os.path.join(self.path, 'backup.id'), 'w') as outfile:
5050
outfile.write(str(Api.getUserId()))
5151

5252
def process(self):
@@ -69,10 +69,9 @@ def process(self):
6969

7070
# Store data
7171
backup.store()
72-
except Exception as e:
72+
except (Exception, KeyboardInterrupt) as e:
7373
Media.stopDownloads()
7474
c.log('error', 'Exception: %s' % str(e))
75-
raise e
7675

7776
from lib import Api
7877

0 commit comments

Comments
 (0)