-
Notifications
You must be signed in to change notification settings - Fork 12
/
wanted.py
executable file
·314 lines (276 loc) · 11.2 KB
/
wanted.py
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
#!/usr/bin/env python
import sys
import os
try:
import configparser
import urllib.request as urllib
except ImportError:
import ConfigParser as configparser
import urllib
import json
import argparse
import time
# Default values that will be used if not found in CFG
default_host = 'localhost'
default_port = 5050
default_ssl = False
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
def validateConf(config, section, item):
try:
# Special check for ssl
if item == 'ssl':
try:
# Specific to CP-backuptool CFG
return config.getboolean(section, item)
except:
# Specific to CP settings.conf
if config.get(section, "ssl_key"):
return True
else:
return False
else:
return config.get(section, item)
except:
if item == 'host':
print("No '%s' found in config, using default: '%s'" % (item, default_host))
return default_host
elif item == 'port':
print("No '%s' found in config, using default: '%s'" % (item, default_port))
return default_port
elif item == 'api_key':
raise Exception("No API key found in configfile")
def writeConf(config, confFile):
with open(confFile, "w") as conf:
config.write(conf)
conf.close()
def apiCall(url, verbose = True):
if verbose:
print("Opening URL:", url)
try:
urlObj = urllib.urlopen(url)
except:
print("Failed to open URL:", url)
print("Caught following:")
raise
result = json.load(urlObj)
if result:
return result
else:
return None
def listWanted(baseurl):
api_call = "movie.list/?status=active"
url = baseurl + api_call
result = apiCall(url)
return result
def listDone(baseurl):
api_call = "movie.list/?status=done"
url = baseurl + api_call
result = apiCall(url)
return result
def listLimitedDone(baseurl):
api_call = "movie.list/?status=manage&limit_offset=50"
url = baseurl + api_call
result = apiCall(url)
return result
def process(type, backup = None):
config = configparser.ConfigParser()
if args.cfg:
configFilename = args.cfg
else:
configFilename = os.path.join(os.path.dirname(sys.argv[0]), "couch.cfg")
print("Loading config from:", configFilename)
with open(configFilename, "r") as conf:
config.readfp(conf)
sections = config.sections()
host = validateConf(config, sections[0], "host")
port = validateConf(config, sections[0], "port")
apikey = validateConf(config, sections[0], "api_key")
ssl = validateConf(config, sections[0], "ssl")
web_root = validateConf(config, sections[0], "url_base")
if ssl:
protocol = "https://"
else:
protocol = "http://"
# Add '/' to beginning of web_root if missing
if web_root and not web_root[0] == '/':
web_root = '/' + web_root
# Remove '/' from end of web_root if present
if web_root and web_root[-1] == '/':
web_root = web_root[:-1]
# The base URL
baseurl = protocol + host + ":" + str(port) + web_root + "/api/" + apikey + "/"
if type == "backup":
result = listWanted(baseurl)
backup_list = []
# Check if backup is necessary (i.e skip if no movies found)
if result['total'] > 0:
for item in result["movies"]:
if not ("info" in item or "identifiers" in item):
continue
movie_list = []
try:
# Try the current data structure
movie_list.append(item["identifiers"]["imdb"])
except:
# Use old data structure for backward compatibility
movie_list.append(item["info"]["imdb"])
# If the profile ID is found (optional)
if item["profile_id"]:
movie_list.append(item["profile_id"])
# Append the movie list to backup list
backup_list.append(movie_list)
print("found %s wanted movies, writing file..." % len(backup_list))
with open(backup, 'w') as f:
json.dump(backup_list, f)
f.close()
print("Backup file completed:", backup)
else:
print("No wanted movies found")
elif type == "restore":
# Do a managed search prior to restoring
print("Doing a full managed scan...")
api_call = "manage.update/?full=1"
url = baseurl + api_call
result = apiCall(url)
# Check progress
api_call = "manage.progress"
url = baseurl + api_call
result = apiCall(url)
while result['progress'] != False:
result = apiCall(url, verbose=False)
time.sleep(1)
print("Managed scan completed")
with open(backup, 'r') as f:
movie_list = json.load(f)
f.close()
for movie in movie_list:
# Add movies along with profile id (if not found or empty; default will be used)
if len(movie) == 1:
movie.append("")
api_call = "movie.add/?identifier=%s&profile_id=%s" %(movie[0], movie[1])
url = baseurl + api_call
result = apiCall(url)
elif type == "add":
with open(backup, 'r') as f:
movie_list = json.load(f)
f.close()
for movie in movie_list:
# Add movies along with profile id (if not found or empty; default will be used)
if len(movie) == 1:
movie.append("")
api_call = "movie.add/?identifier=%s&profile_id=%s" %(movie[0], movie[1])
url = baseurl + api_call
result = apiCall(url)
elif type == "clear":
result = listLimitedDone(baseurl)
print("Clearing Movie Library...")
if not result['empty']:
while not result['empty']:
for item in result["movies"]:
print("Clearing movie '%s' from Library" % item["title"])
api_call = "movie.delete/?delete_from=manage&id=%s" % item["_id"]
url = baseurl + api_call
apiCall(url, verbose = False)
result = listLimitedDone(baseurl)
else:
print("No movies in Library to clear")
elif type == "delete":
result = listWanted(baseurl)
if result['total'] > 0:
print("Deleting wanted movies...")
for item in result["movies"]:
print("Deleting movie '%s'" % item["title"])
api_call = "movie.delete/?delete_from=wanted&id=%s" % item["_id"]
url = baseurl + api_call
apiCall(url, verbose = False)
else:
print("No wanted movies to delete")
elif type == "export":
result = listDone(baseurl)
export_list = []
# Check if export is necessary (i.e skip if no movies found)
if result['total'] > 0:
for item in result["movies"]:
if not ("info" in item or "identifiers" in item):
continue
movie_list = []
try:
# Try the current data structure
movie_list.append(item["identifiers"]["imdb"])
except:
# Use old data structure for backward compatibility
movie_list.append(item["info"]["imdb"])
# Check releases for files
for release in item["releases"]:
if not ("files" in release):
continue
if not ("movie" in release["files"]):
continue
# Get the path of the movie file
movie_list.append(release["files"]["movie"][0])
# Append separator character (optional)
movie_list.append("\n")
# Append the movie list to export list
export_list.append(movie_list)
print("found %s library movies, writing file..." % len(export_list))
with open(backup, 'w') as f:
json.dump(export_list, f)
f.close()
print("Export file completed:", backup)
else:
print("No library movies found")
elif type == "check":
result = listDone(baseurl)
export_list = []
# Check if export is necessary (i.e skip if no movies found)
if result['total'] > 0:
for item in result["movies"]:
#print "item found: %s " % json.dumps(item)
#print "------------------------------------------------"
title = item["info"]["original_title"]
#print "Title: %s" % title
# Check releases for files
if not item["releases"]:
continue
for release in item["releases"]:
if not ("files" in release):
continue
if not ("movie" in release["files"]):
continue
# Get the path of the movie file
fileondisk = os.path.isfile(release["files"]["movie"][0])
if not fileondisk:
if backup is None:
print("=====================================================")
print("Title: %s" % title)
print("File check for file %s is not found on disk " % (release["files"]["movie"][0]))
else:
export_list.append(release["files"]["movie"][0])
print("found %s library movies, writing file..." % len(export_list))
if not backup is None:
with open(backup, 'w') as f:
json.dump(export_list, f)
f.close()
print("File check completed:", backup)
else:
print("File check completed")
else:
print("No library movies found")
parser = argparse.ArgumentParser(description='Backup/Restore/Delete/Export Couchpotato wanted/library list',
formatter_class=argparse.RawTextHelpFormatter)
# Require this option
parser.add_argument('--type', metavar='backup/restore/delete/add/export/clear/check', choices=['backup', 'restore', 'delete', 'add', 'export', 'clear', 'check'],
required=True, help='''backup: Writes the wanted movies to file.
restore: Adds wanted movies from file.
delete: Delete all your wanted movies
add: Adds wanted movies from file skipping manage scan.
export: Writes the library movies to file.
check: Checks done movie list to filesystem''')
parser.add_argument('--file', help='', required=False)
parser.add_argument('--cfg', metavar='cfg-file', help='Specify an alternative cfg file')
args = parser.parse_args()
if args.type == 'backup' or args.type == 'restore' or args.type == 'export':
if not args.file:
parser.error('You must specify a file when using %s' % args.type)
process(args.type, args.file)