Coverage for torrentfile\interactive.py: 100%

121 statements  

« prev     ^ index     » next       coverage.py v7.3.0, created at 2023-08-27 21:50 -0700

1#! /usr/bin/python3 

2# -*- coding: utf-8 -*- 

3 

4############################################################################## 

5# Copyright (C) 2021-current alexpdev 

6# 

7# Licensed under the Apache License, Version 2.0 (the "License"); 

8# you may not use this file except in compliance with the License. 

9# You may obtain a copy of the License at 

10# 

11# http://www.apache.org/licenses/LICENSE-2.0 

12# 

13# Unless required by applicable law or agreed to in writing, software 

14# distributed under the License is distributed on an "AS IS" BASIS, 

15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

16# See the License for the specific language governing permissions and 

17# limitations under the License. 

18############################################################################## 

19""" 

20Module contains the procedures used for Interactive Mode. 

21 

22## This module has been deprecated. 

23""" 

24 

25import os 

26import sys 

27import shutil 

28 

29import pyben 

30 

31from torrentfile.edit import edit_torrent 

32from torrentfile.recheck import Checker 

33from torrentfile.torrent import TorrentFile, TorrentFileHybrid, TorrentFileV2 

34 

35 

36def get_input(*args: tuple): # pragma: no cover 

37 """ 

38 Determine appropriate input function to call. 

39 

40 @Deprecated 

41 

42 Parameters 

43 ---------- 

44 *args : tuple 

45 Arbitrary number of args to pass to next function 

46 

47 Returns 

48 ------- 

49 str 

50 The results of the function call. 

51 """ 

52 if len(args) == 2: 

53 return _get_input_loop(*args) 

54 return _get_input(*args) 

55 

56 

57def _get_input(txt: str): # pragma: no cover 

58 """ 

59 Gather information needed from user. 

60 

61 @Deprecated 

62 

63 Parameters 

64 ---------- 

65 txt : str 

66 The message usually containing instructions for the user. 

67 

68 Returns 

69 ------- 

70 str 

71 The text input received from the user. 

72 """ 

73 value = input(txt) 

74 return value 

75 

76 

77def _get_input_loop(txt: str, func): # pragma: no cover 

78 """ 

79 Gather information needed from user. 

80 

81 @Deprecated 

82 

83 Parameters 

84 ---------- 

85 txt : str 

86 The message usually containing instructions for the user. 

87 func : function 

88 Validate/Check user input data, failure = retry, success = continue. 

89 

90 Returns 

91 ------- 

92 str 

93 The text input received from the user. 

94 """ 

95 while True: 

96 value = input(txt) 

97 if func and func(value): 

98 return value 

99 if not func or value == "": 

100 return value 

101 showtext(f"Invalid input {value}: try again") 

102 

103 

104def showtext(txt): 

105 """ 

106 Print contents of txt to screen. 

107 

108 @Deprecated 

109 

110 Parameters 

111 ---------- 

112 txt : str 

113 text to print to terminal. 

114 """ 

115 sys.stdout.write(txt) 

116 

117 

118def showcenter(txt: str): 

119 """ 

120 Print text to screen in the center position of the terminal. 

121 

122 @Deprecated 

123 

124 Parameters 

125 ---------- 

126 txt : str 

127 the preformated message to send to stdout. 

128 """ 

129 termlen = shutil.get_terminal_size().columns 

130 padding = " " * int(((termlen - len(txt)) / 2)) 

131 string = "".join(["\n", padding, txt, "\n"]) 

132 showtext(string) 

133 

134 

135def select_action(): 

136 """ 

137 Operate TorrentFile program interactively through terminal. 

138 

139 DEPRECATION WARNING: The interactive CLI feature will be deprecated 

140 in the future. 

141 """ 

142 showcenter("TorrentFile: Starting Interactive Mode") 

143 showcenter("DEPRECATION WARNING: The interactive feature will be" 

144 "deprecated in the near future.") 

145 action = get_input("Enter the action you wish to perform.\n" 

146 "Action ( Create (c) | Edit (e) | Recheck (r) ): ") 

147 action = action.lower() 

148 

149 if "create" in action or action == "c": 

150 return create_torrent() 

151 

152 if "check" in action or action == "r": 

153 return recheck_torrent() 

154 

155 if "edit" in action or action == "e": 

156 return edit_action() 

157 print("Unable to recognize input. Please try again.") # pragma: nocover 

158 return select_action() # pragma: nocover 

159 

160 

161def recheck_torrent(): 

162 """ 

163 Check torrent download completed percentage. 

164 

165 @Deprecated 

166 """ 

167 showcenter("Check Torrent") 

168 msg = "Enter path to torrent contents, and corresponding torrent metafile." 

169 showtext(msg) 

170 metafile = get_input("Conent Path (downloads/complete/torrentname):", 

171 os.path.exists) 

172 contents = get_input("Metafile (*.torrent): ", os.path.exists) 

173 checker = Checker(metafile, contents) 

174 results = checker.results() 

175 showtext(f"Completion for {metafile} is {results}%") 

176 return results 

177 

178 

179def create_torrent(): 

180 """ 

181 Create new torrent file interactively. 

182 

183 @Deprecated 

184 """ 

185 showcenter("Create Torrent") 

186 showtext( 

187 "\nEnter values for each of the options for the torrent creator, " 

188 "or leave blank for program defaults.\nSpaces are considered item " 

189 "seperators for options that accept a list of values.\nValues " 

190 "enclosed in () indicate the default value, while {} holds all " 

191 "valid choices available for the option.\n\n") 

192 creator = InteractiveCreator() 

193 return creator 

194 

195 

196def edit_action(): 

197 """ 

198 Edit the editable values of the torrent meta file. 

199 

200 @Deprecated 

201 """ 

202 showcenter("Edit Torrent") 

203 metafile = get_input("Metafile(.torrent): ", os.path.exists) 

204 dialog = InteractiveEditor(metafile) 

205 dialog.show_current() 

206 dialog.edit_props() 

207 

208 

209class InteractiveEditor: 

210 """ 

211 Interactive dialog class for torrent editing. 

212 

213 @Deprecated 

214 """ 

215 

216 def __init__(self, metafile: str): 

217 """ 

218 Initialize the Interactive torrent editor guide. 

219 

220 @Deprecated 

221 

222 Parameters 

223 ---------- 

224 metafile : str 

225 user input string identifying the path to a torrent meta file. 

226 """ 

227 self.metafile = metafile 

228 self.meta = pyben.load(metafile) 

229 self.info = self.meta["info"] 

230 

231 self.args = { 

232 "url-list": self.meta.get("url-list", None), 

233 "httpseeds": self.meta.get("httpseeds", None), 

234 "announce": self.meta.get("announce-list", None), 

235 "source": self.info.get("source", None), 

236 "private": self.info.get("private", None), 

237 "comment": self.info.get("comment", None), 

238 } 

239 

240 def show_current(self): 

241 """ 

242 Display the current met file information to screen. 

243 

244 @Deprecated 

245 """ 

246 out = "Current properties and values:\n" 

247 longest = max(len(label) for label in self.args) + 3 

248 for key, val in self.args.items(): 

249 txt = (key.title() + ":").ljust(longest) + str(val) 

250 out += f"\t{txt}\n" 

251 showtext(out) 

252 

253 def sanatize_response(self, key, response): 

254 """ 

255 Convert the input data into a form recognizable by the program. 

256 

257 @ Deprecated 

258 

259 Parameters 

260 ---------- 

261 key : str 

262 name of the property and attribute being eddited. 

263 response : str 

264 User input value the property is being edited to. 

265 """ 

266 if key in ["announce", "url-list", "httpseeds"]: 

267 val = response.split() 

268 else: 

269 val = response 

270 self.args[key] = val 

271 

272 def edit_props(self): 

273 """ 

274 Loop continuosly for edits until user signals DONE. 

275 

276 @Deprecated 

277 """ 

278 while True: 

279 showcenter("Choose the number for a propert the needs editing." 

280 "Enter DONE when all editing has been completed.") 

281 

282 props = { 

283 1: "comment", 

284 2: "source", 

285 3: "private", 

286 4: "tracker", 

287 5: "web-seed", 

288 6: "httpseeds", 

289 } 

290 

291 args = { 

292 1: "comment", 

293 2: "source", 

294 3: "private", 

295 4: "announce", 

296 5: "url-list", 

297 6: "httpseeds", 

298 } 

299 

300 txt = ", ".join((str(k) + ": " + v) for k, v in props.items()) 

301 prop = get_input(txt) 

302 if prop.lower() == "done": 

303 break 

304 

305 if prop.isdigit() and 0 < int(prop) < 6: 

306 key = props[int(prop)] 

307 key2 = args[int(prop)] 

308 val = self.args.get(key2) 

309 showtext( 

310 "Enter new property value or leave empty for no value.") 

311 response = get_input(f"{key.title()} ({val}): ") 

312 self.sanatize_response(key2, response) 

313 

314 else: 

315 showtext("Invalid input: Try again.") 

316 edit_torrent(self.metafile, self.args) 

317 

318 

319class InteractiveCreator: 

320 """ 

321 Class namespace for interactive program options. 

322 

323 @Deprecated 

324 """ 

325 

326 def __init__(self): 

327 """ 

328 Initialize interactive meta file creator dialog. 

329 

330 @Deprecated 

331 """ 

332 self.kwargs = { 

333 "announce": None, 

334 "url_list": None, 

335 "private": None, 

336 "source": None, 

337 "comment": None, 

338 "piece_length": None, 

339 "outfile": None, 

340 "path": None, 

341 "httpseeds": None, 

342 } 

343 self.outfile, self.meta = self.get_props() 

344 

345 def get_props(self): 

346 """ 

347 Gather details for torrentfile from user. 

348 

349 @Deprecated 

350 """ 

351 piece_length = get_input("Piece Length (empty=auto): ", 

352 lambda x: x.isdigit()) 

353 

354 self.kwargs["piece_length"] = piece_length 

355 announce = get_input("Tracker list (empty): ", 

356 lambda x: isinstance(x, str)) 

357 

358 if announce: 

359 self.kwargs["announce"] = announce.split() 

360 

361 url_list = get_input("Web Seed {GetRight} list (empty): ", 

362 lambda x: isinstance(x, str)) 

363 

364 httpseeds = get_input("Web Seed {Hoffman} list (empty): ", 

365 lambda x: isinstance(x, str)) 

366 

367 if url_list: 

368 self.kwargs["url_list"] = url_list.split() 

369 if httpseeds: 

370 self.kwargs["httpseeds"] = httpseeds.split() 

371 comment = get_input("Comment (empty): ", None) 

372 

373 if comment: 

374 self.kwargs["comment"] = comment 

375 source = get_input("Source (empty): ", None) 

376 

377 if source: 

378 self.kwargs["source"] = source 

379 

380 private = get_input("Private Torrent? {Y/N}: (N)", 

381 lambda x: x in "yYnN") 

382 

383 if private and private.lower() == "y": 

384 self.kwargs["private"] = 1 

385 

386 contents = get_input("Content Path: ", os.path.exists) 

387 self.kwargs["path"] = contents 

388 

389 outfile = get_input( 

390 f"Output Path ({contents}.torrent): ", 

391 lambda x: os.path.exists(os.path.dirname(x)), 

392 ) 

393 

394 if outfile: 

395 self.kwargs["outfile"] = outfile 

396 

397 meta_version = get_input("Meta Version {1,2,3}: (1)", 

398 lambda x: x in "123") 

399 

400 showcenter(f"creating {outfile}") 

401 

402 if meta_version == "3": 

403 torrent = TorrentFileHybrid(**self.kwargs) 

404 elif meta_version == "2": 

405 torrent = TorrentFileV2(**self.kwargs) 

406 else: 

407 torrent = TorrentFile(**self.kwargs) 

408 return torrent.write()