Coverage for torrentfile\interactive.py: 100%
121 statements
« prev ^ index » next coverage.py v7.3.0, created at 2023-08-27 21:50 -0700
« prev ^ index » next coverage.py v7.3.0, created at 2023-08-27 21:50 -0700
1#! /usr/bin/python3
2# -*- coding: utf-8 -*-
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.
22## This module has been deprecated.
23"""
25import os
26import sys
27import shutil
29import pyben
31from torrentfile.edit import edit_torrent
32from torrentfile.recheck import Checker
33from torrentfile.torrent import TorrentFile, TorrentFileHybrid, TorrentFileV2
36def get_input(*args: tuple): # pragma: no cover
37 """
38 Determine appropriate input function to call.
40 @Deprecated
42 Parameters
43 ----------
44 *args : tuple
45 Arbitrary number of args to pass to next function
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)
57def _get_input(txt: str): # pragma: no cover
58 """
59 Gather information needed from user.
61 @Deprecated
63 Parameters
64 ----------
65 txt : str
66 The message usually containing instructions for the user.
68 Returns
69 -------
70 str
71 The text input received from the user.
72 """
73 value = input(txt)
74 return value
77def _get_input_loop(txt: str, func): # pragma: no cover
78 """
79 Gather information needed from user.
81 @Deprecated
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.
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")
104def showtext(txt):
105 """
106 Print contents of txt to screen.
108 @Deprecated
110 Parameters
111 ----------
112 txt : str
113 text to print to terminal.
114 """
115 sys.stdout.write(txt)
118def showcenter(txt: str):
119 """
120 Print text to screen in the center position of the terminal.
122 @Deprecated
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)
135def select_action():
136 """
137 Operate TorrentFile program interactively through terminal.
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()
149 if "create" in action or action == "c":
150 return create_torrent()
152 if "check" in action or action == "r":
153 return recheck_torrent()
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
161def recheck_torrent():
162 """
163 Check torrent download completed percentage.
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
179def create_torrent():
180 """
181 Create new torrent file interactively.
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
196def edit_action():
197 """
198 Edit the editable values of the torrent meta file.
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()
209class InteractiveEditor:
210 """
211 Interactive dialog class for torrent editing.
213 @Deprecated
214 """
216 def __init__(self, metafile: str):
217 """
218 Initialize the Interactive torrent editor guide.
220 @Deprecated
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"]
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 }
240 def show_current(self):
241 """
242 Display the current met file information to screen.
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)
253 def sanatize_response(self, key, response):
254 """
255 Convert the input data into a form recognizable by the program.
257 @ Deprecated
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
272 def edit_props(self):
273 """
274 Loop continuosly for edits until user signals DONE.
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.")
282 props = {
283 1: "comment",
284 2: "source",
285 3: "private",
286 4: "tracker",
287 5: "web-seed",
288 6: "httpseeds",
289 }
291 args = {
292 1: "comment",
293 2: "source",
294 3: "private",
295 4: "announce",
296 5: "url-list",
297 6: "httpseeds",
298 }
300 txt = ", ".join((str(k) + ": " + v) for k, v in props.items())
301 prop = get_input(txt)
302 if prop.lower() == "done":
303 break
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)
314 else:
315 showtext("Invalid input: Try again.")
316 edit_torrent(self.metafile, self.args)
319class InteractiveCreator:
320 """
321 Class namespace for interactive program options.
323 @Deprecated
324 """
326 def __init__(self):
327 """
328 Initialize interactive meta file creator dialog.
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()
345 def get_props(self):
346 """
347 Gather details for torrentfile from user.
349 @Deprecated
350 """
351 piece_length = get_input("Piece Length (empty=auto): ",
352 lambda x: x.isdigit())
354 self.kwargs["piece_length"] = piece_length
355 announce = get_input("Tracker list (empty): ",
356 lambda x: isinstance(x, str))
358 if announce:
359 self.kwargs["announce"] = announce.split()
361 url_list = get_input("Web Seed {GetRight} list (empty): ",
362 lambda x: isinstance(x, str))
364 httpseeds = get_input("Web Seed {Hoffman} list (empty): ",
365 lambda x: isinstance(x, str))
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)
373 if comment:
374 self.kwargs["comment"] = comment
375 source = get_input("Source (empty): ", None)
377 if source:
378 self.kwargs["source"] = source
380 private = get_input("Private Torrent? {Y/N}: (N)",
381 lambda x: x in "yYnN")
383 if private and private.lower() == "y":
384 self.kwargs["private"] = 1
386 contents = get_input("Content Path: ", os.path.exists)
387 self.kwargs["path"] = contents
389 outfile = get_input(
390 f"Output Path ({contents}.torrent): ",
391 lambda x: os.path.exists(os.path.dirname(x)),
392 )
394 if outfile:
395 self.kwargs["outfile"] = outfile
397 meta_version = get_input("Meta Version {1,2,3}: (1)",
398 lambda x: x in "123")
400 showcenter(f"creating {outfile}")
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()