pywaybackup 1.2.3__tar.gz → 1.2.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/PKG-INFO +5 -1
  2. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/README.md +4 -0
  3. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup/Exception.py +45 -7
  4. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup/SnapshotCollection.py +7 -1
  5. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup/Verbosity.py +1 -1
  6. pywaybackup-1.2.5/pywaybackup/__version__.py +1 -0
  7. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup/archive.py +23 -20
  8. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup/helper.py +12 -1
  9. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup/main.py +2 -0
  10. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup.egg-info/PKG-INFO +5 -1
  11. pywaybackup-1.2.3/pywaybackup/__version__.py +0 -1
  12. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/LICENSE +0 -0
  13. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup/__init__.py +0 -0
  14. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup/arguments.py +0 -0
  15. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup.egg-info/SOURCES.txt +0 -0
  16. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup.egg-info/dependency_links.txt +0 -0
  17. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup.egg-info/entry_points.txt +0 -0
  18. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup.egg-info/requires.txt +0 -0
  19. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/pywaybackup.egg-info/top_level.txt +0 -0
  20. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/setup.cfg +0 -0
  21. {pywaybackup-1.2.3 → pywaybackup-1.2.5}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pywaybackup
3
- Version: 1.2.3
3
+ Version: 1.2.5
4
4
  Summary: Download snapshots from the Wayback Machine
5
5
  Home-page: https://github.com/bitdruid/python-wayback-machine-downloader
6
6
  Author: bitdruid
@@ -26,6 +26,10 @@ Internet-archive is a nice source for several OSINT-information. This tool is a
26
26
 
27
27
  This tool allows you to download content from the Wayback Machine (archive.org). You can use it to download either the latest version or all versions of web page snapshots within a specified range.
28
28
 
29
+ ## Info
30
+
31
+ Linux recommended: On windows machines, the path length is limited. It can only be overcome by editing the registry. Files which exceed the path length will not be downloaded.
32
+
29
33
  ## Installation
30
34
 
31
35
  ### Pip
@@ -11,6 +11,10 @@ Internet-archive is a nice source for several OSINT-information. This tool is a
11
11
 
12
12
  This tool allows you to download content from the Wayback Machine (archive.org). You can use it to download either the latest version or all versions of web page snapshots within a specified range.
13
13
 
14
+ ## Info
15
+
16
+ Linux recommended: On windows machines, the path length is limited. It can only be overcome by editing the registry. Files which exceed the path length will not be downloaded.
17
+
14
18
  ## Installation
15
19
 
16
20
  ### Pip
@@ -5,6 +5,10 @@ from datetime import datetime
5
5
  import linecache
6
6
  import traceback
7
7
 
8
+ import re
9
+
10
+ from pywaybackup.__version__ import __version__
11
+
8
12
  class Exception:
9
13
 
10
14
  new_debug = True
@@ -22,19 +26,20 @@ class Exception:
22
26
  @classmethod
23
27
  def exception(cls, message: str, e: Exception, tb=None):
24
28
  custom_tb = sys.exc_info()[-1] if tb is None else tb
25
- original_tb = "".join(traceback.format_exception(type(e), e, e.__traceback__))
29
+ original_tb = cls.relativate_path("".join(traceback.format_exception(type(e), e, e.__traceback__)))
26
30
  exception_message = (
27
31
  "-------------------------\n"
28
32
  f"!-- Exception: {message}\n"
29
33
  )
30
34
  if custom_tb is not None:
31
- while custom_tb.tb_next: # loop to last traceback frame
32
- custom_tb = custom_tb.tb_next
35
+ while custom_tb.tb_next: # loop to last traceback frame
36
+ custom_tb = custom_tb.tb_next
33
37
  tb_frame = custom_tb.tb_frame
34
38
  tb_line = custom_tb.tb_lineno
35
39
  func_name = tb_frame.f_code.co_name
36
- filename = tb_frame.f_code.co_filename
40
+ filename = cls.relativate_path(tb_frame.f_code.co_filename)
37
41
  codeline = linecache.getline(filename, tb_line).strip()
42
+ local_vars = tb_frame.f_locals
38
43
  exception_message += (
39
44
  f"!-- File: {filename}\n"
40
45
  f"!-- Function: {func_name}\n"
@@ -45,24 +50,57 @@ class Exception:
45
50
  exception_message += "!-- Traceback is None\n"
46
51
  exception_message += (
47
52
  f"!-- Description: {e}\n"
48
- "-------------------------")
53
+ "-------------------------"
54
+ )
49
55
  print(exception_message)
50
56
  if cls.debug:
51
57
  debug_file = os.path.join(cls.output, "waybackup_error.log")
52
58
  print(f"Exception log: {debug_file}")
53
59
  print("-------------------------")
54
60
  print(f"Full traceback:\n{original_tb}")
55
- if cls.new_debug: # new run, overwrite file
61
+ if cls.new_debug: # new run, overwrite file
56
62
  cls.new_debug = False
57
63
  f = open(debug_file, "w")
58
64
  f.write("-------------------------\n")
65
+ f.write(f"Version: {__version__}\n")
66
+ f.write("-------------------------\n")
59
67
  f.write(f"Command: {cls.command}\n")
60
68
  f.write("-------------------------\n\n")
61
- else: # current run, append to file
69
+ else: # current run, append to file
62
70
  f = open(debug_file, "a")
63
71
  f.write(datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "\n")
64
72
  f.write(exception_message + "\n")
73
+ f.write("!-- Local Variables:\n")
74
+ for var_name, value in local_vars.items():
75
+ if var_name in ["status_message", "headers"]:
76
+ continue
77
+ value = cls.relativate_path(str(value))
78
+ value = value[:666] + " ... " if len(value) > 666 else value
79
+ f.write(f" -- {var_name} = {value}\n")
80
+ f.write("-------------------------\n")
65
81
  f.write(original_tb + "\n")
82
+ f.close()
83
+
84
+ @classmethod
85
+ def relativate_path(cls, input: str) -> str:
86
+ try:
87
+ path_pattern = re.compile(r'File "([^"]+)"')
88
+ if os.path.isfile(input): # case single path
89
+ return os.path.relpath(input, os.getcwd())
90
+ input_modified = ""
91
+ input_lines = input.split('\n')
92
+ if len(input_lines) == 1: # case single line
93
+ return input
94
+ for line in input.split('\n'): # case multiple lines
95
+ match = path_pattern.search(line)
96
+ if match:
97
+ original_path = match.group(1)
98
+ relative_path = os.path.relpath(original_path, os.getcwd())
99
+ line = line.replace(original_path, relative_path)
100
+ input_modified += line + "\n"
101
+ return input_modified
102
+ except ValueError:
103
+ return input
66
104
 
67
105
  @staticmethod
68
106
  def exception_handler(exception_type, exception, traceback):
@@ -31,7 +31,13 @@ class SnapshotCollection:
31
31
 
32
32
 
33
33
  @classmethod
34
- def count_list(cls):
34
+ def count(cls, collection=False, success=False, fail=False):
35
+ if collection:
36
+ return len(cls.SNAPSHOT_COLLECTION)
37
+ if success:
38
+ return len([entry for entry in cls.SNAPSHOT_COLLECTION if entry["file"]])
39
+ if fail:
40
+ return len([entry for entry in cls.SNAPSHOT_COLLECTION if not entry["file"]])
35
41
  return len(cls.SNAPSHOT_COLLECTION)
36
42
 
37
43
 
@@ -38,7 +38,7 @@ class Verbosity:
38
38
  def write(cls, message: str = None, progress: int = None):
39
39
  if cls.mode == "progress":
40
40
  if cls.pbar is None and progress == 0:
41
- maxval = sc.count_list()
41
+ maxval = sc.count(collection=True)
42
42
  cls.pbar = tqdm.tqdm(total=maxval, desc="Downloading", unit=" snapshot", ascii="░▒█")
43
43
  if cls.pbar is not None and progress is not None and progress > 0 :
44
44
  cls.pbar.update(progress)
@@ -0,0 +1 @@
1
+ __version__ = "1.2.5"
@@ -13,7 +13,7 @@ from datetime import datetime, timezone
13
13
 
14
14
  from socket import timeout
15
15
 
16
- from pywaybackup.helper import url_get_timestamp, url_split, move_index, sanitize_filename
16
+ from pywaybackup.helper import url_get_timestamp, url_split, move_index, sanitize_filename, check_nt
17
17
 
18
18
  from pywaybackup.SnapshotCollection import SnapshotCollection as sc
19
19
 
@@ -83,7 +83,7 @@ def save_page(url: str):
83
83
 
84
84
  def print_list():
85
85
  vb.write("")
86
- count = sc.count_list()
86
+ count = sc.count(collection=True)
87
87
  if count == 0:
88
88
  vb.write("\nNo snapshots found")
89
89
  else:
@@ -104,7 +104,8 @@ def query_list(url: str, range: int, start: int, end: int, explicit: bool, mode:
104
104
  cdxResult = cdxResult.read()
105
105
  vb.write(f"\n-----> {len(cdxResult)} snapshots injected")
106
106
  except FileNotFoundError as e:
107
- ex.exception("Could not inject CDX data", e)
107
+ vb.write("\nFILE NOT FOUND -> could not inject CDX data")
108
+ os._exit(1)
108
109
  else:
109
110
  vb.write("\nQuerying snapshots...")
110
111
  query_range = ""
@@ -134,7 +135,6 @@ def query_list(url: str, range: int, start: int, end: int, explicit: bool, mode:
134
135
  except requests.exceptions.ConnectionError as e:
135
136
  vb.write("\nCONNECTION REFUSED -> could not query cdx server (max retries exceeded)\n")
136
137
  os._exit(1)
137
- #ex.exception("Could not query snapshots", e)
138
138
 
139
139
  if cdxbackup:
140
140
  os.makedirs(cdxbackup, exist_ok=True)
@@ -144,7 +144,7 @@ def query_list(url: str, range: int, start: int, end: int, explicit: bool, mode:
144
144
 
145
145
  cdxResult = json.loads(cdxResult)
146
146
  sc.create_list(cdxResult, mode)
147
- vb.write(f"\n-----> {sc.count_list()} snapshots to utilize")
147
+ vb.write(f"\n-----> {sc.count(collection=True)} snapshots to utilize")
148
148
 
149
149
 
150
150
 
@@ -156,7 +156,7 @@ def download_list(output, retry, no_redirect, workers, skipset: set = None):
156
156
  """
157
157
  Download a list of urls in format: [{"timestamp": "20190815104545", "url": "https://www.google.com/"}]
158
158
  """
159
- if sc.count_list() == 0:
159
+ if sc.count(collection=True) == 0:
160
160
  vb.write("\nNothing to download");
161
161
  return
162
162
  vb.write("\nDownloading snapshots...", progress=0)
@@ -188,8 +188,8 @@ def download_list(output, retry, no_redirect, workers, skipset: set = None):
188
188
  thread.start()
189
189
  for thread in threads:
190
190
  thread.join()
191
- successed = len([snapshot for snapshot in sc.SNAPSHOT_COLLECTION if "file" in snapshot and snapshot["file"]])
192
- failed = len([snapshot for snapshot in sc.SNAPSHOT_COLLECTION if "file" in snapshot and not snapshot["file"]])
191
+ successed = sc.count(success=True)
192
+ failed = sc.count(fail=True)
193
193
  vb.write(f"\nFiles downloaded: {successed}")
194
194
  vb.write(f"Not downloaded: {failed}\n")
195
195
 
@@ -214,7 +214,7 @@ def download_loop(snapshot_queue, output, worker, retry, no_redirect, skipset=No
214
214
  while not snapshot_queue.empty():
215
215
  snapshot = snapshot_queue.get()
216
216
  status = f"\n-----> Attempt: [{attempt}/{max_attempt}] Snapshot [{sc.SNAPSHOT_COLLECTION.index(snapshot)+1}/{len(sc.SNAPSHOT_COLLECTION)}] - Worker: {worker}"
217
- download_status = download(output, snapshot, connection, status, no_redirect, skipset)
217
+ download_status = download(output, snapshot, connection, status, no_redirect)
218
218
  if not download_status:
219
219
  if snapshot not in failed_urls:
220
220
  failed_urls.append(snapshot)
@@ -236,7 +236,7 @@ def download_loop(snapshot_queue, output, worker, retry, no_redirect, skipset=No
236
236
 
237
237
 
238
238
 
239
- def download(output, snapshot_entry, connection, status_message, no_redirect=False, skipset=None):
239
+ def download(output, snapshot_entry, connection, status_message, no_redirect=False):
240
240
  """
241
241
  Download a single URL and save it to the specified filepath.
242
242
  If there is a redirect, the function will follow the redirect and update the download URL.
@@ -284,6 +284,15 @@ def download(output, snapshot_entry, connection, status_message, no_redirect=Fal
284
284
  output_file = sc.create_output(download_url, snapshot_entry["timestamp"], output)
285
285
  output_path = os.path.dirname(output_file)
286
286
 
287
+ # if output_file is too long for windows, skip download
288
+ if check_nt() and len(output_file) > 255:
289
+ status_message = f"{status_message}\n" + \
290
+ f"PATH TOO LONG TO SAVE FILE -> HTTP: {response_status} - {response_status_message}\n" + \
291
+ f" -> URL: {download_url}"
292
+ sc.snapshot_entry_modify(snapshot_entry, "file", "PATH TOO LONG TO SAVE FILE")
293
+ vb.write(status_message)
294
+ return True
295
+
287
296
  # case if output_path is a file, move file to temporary name, create output_path and move file into output_path
288
297
  if os.path.isfile(output_path):
289
298
  move_index(existpath=output_path)
@@ -293,14 +302,6 @@ def download(output, snapshot_entry, connection, status_message, no_redirect=Fal
293
302
  if os.path.isdir(output_file):
294
303
  output_file = move_index(existfile=output_file, filebuffer=response_data)
295
304
 
296
- # if filename is too long, skip download
297
- if len(os.path.basename(output_file)) > 255:
298
- status_message = f"{status_message}\n" + \
299
- f"FILENAME TOO LONG -> HTTP: {response_status} - {response_status_message}\n" + \
300
- f" -> URL: {download_url}"
301
- vb.write(status_message)
302
- return True
303
-
304
305
  if not os.path.isfile(output_file):
305
306
  with open(output_file, 'wb') as file:
306
307
  if response.getheader('Content-Encoding') == 'gzip':
@@ -378,7 +379,7 @@ def csv_close(csv_path: str, url: str):
378
379
  """
379
380
  try:
380
381
  csv_path = csv_filepath(csv_path, url)
381
- if sc.count_list() > 0:
382
+ if sc.count(collection=True) > 0:
382
383
  if os.path.exists(csv_path): # append to existing file
383
384
  existing_rows = set()
384
385
  with open(csv_path, mode='r') as file: # read existing rows
@@ -425,11 +426,13 @@ def skip_open(csv_path: str, url: str) -> tuple:
425
426
  """
426
427
  try:
427
428
  csv_path = csv_filepath(csv_path, url)
428
- if os.path.exists(csv_path) and os.path.getsize(csv_path) > 0:
429
+ if os.path.isfile(csv_path) and os.path.getsize(csv_path) > 0:
429
430
  csv_file = open(csv_path, mode='r')
430
431
  skipset = set(csv_read(csv_file))
432
+ csv_file.close()
431
433
  return skipset
432
434
  else:
435
+ vb.write("\nNo CSV-file or content found to load skipable URLs")
433
436
  return None
434
437
  except Exception as e:
435
438
  ex.exception("Could not open CSV-file", e)
@@ -3,6 +3,14 @@ import os
3
3
  import shutil
4
4
  import magic
5
5
 
6
+
7
+ def check_nt():
8
+ """
9
+ Check if the OS is Windows.
10
+ """
11
+ return os.name == "nt"
12
+
13
+
6
14
  def sanitize_filename(input: str) -> str:
7
15
  """
8
16
  Sanitize a string to be used as (part of) a filename.
@@ -21,6 +29,7 @@ def url_get_timestamp(url):
21
29
  timestamp = url.split("id_/")[0].split("/")[-1]
22
30
  return timestamp
23
31
 
32
+
24
33
  def url_split(url, index=False):
25
34
  """
26
35
  Split a URL into domain, subdir, and filename.
@@ -38,7 +47,7 @@ def url_split(url, index=False):
38
47
  filename = "index.html" if index else ""
39
48
  subdir = "/".join(path_parts).strip("/")
40
49
  # sanitize subdir and filename for windows
41
- if os.name == "nt":
50
+ if check_nt():
42
51
  special_chars = [":", "*", "?", "&", "=", "<", ">", "\\", "|"]
43
52
  for char in special_chars:
44
53
  subdir = subdir.replace(char, f"%{ord(char):02x}")
@@ -46,6 +55,7 @@ def url_split(url, index=False):
46
55
  filename = filename.replace("%20", " ")
47
56
  return domain, subdir, filename
48
57
 
58
+
49
59
  def move_index(existpath: str = None, existfile: str = None, filebuffer: bytes = None):
50
60
  """
51
61
  1. If existpath is given but can't be created because a file exists with the same name
@@ -73,6 +83,7 @@ def move_index(existpath: str = None, existfile: str = None, filebuffer: bytes =
73
83
  else:
74
84
  return os.path.join(existfile, "index.html")
75
85
 
86
+
76
87
  def check_index_mime(filebuffer: bytes) -> bool:
77
88
  mime = magic.Magic(mime=True)
78
89
  mime_type = mime.from_buffer(filebuffer)
@@ -14,6 +14,8 @@ def main():
14
14
  if args.output is None:
15
15
  args.output = os.path.join(os.getcwd(), "waybackup_snapshots")
16
16
  os.makedirs(args.output, exist_ok=True)
17
+ else:
18
+ os.makedirs(args.output, exist_ok=True)
17
19
 
18
20
  ex.init(args.debug, args.output, command)
19
21
  vb.init(args.verbosity)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pywaybackup
3
- Version: 1.2.3
3
+ Version: 1.2.5
4
4
  Summary: Download snapshots from the Wayback Machine
5
5
  Home-page: https://github.com/bitdruid/python-wayback-machine-downloader
6
6
  Author: bitdruid
@@ -26,6 +26,10 @@ Internet-archive is a nice source for several OSINT-information. This tool is a
26
26
 
27
27
  This tool allows you to download content from the Wayback Machine (archive.org). You can use it to download either the latest version or all versions of web page snapshots within a specified range.
28
28
 
29
+ ## Info
30
+
31
+ Linux recommended: On windows machines, the path length is limited. It can only be overcome by editing the registry. Files which exceed the path length will not be downloaded.
32
+
29
33
  ## Installation
30
34
 
31
35
  ### Pip
@@ -1 +0,0 @@
1
- __version__ = "1.2.3"
File without changes
File without changes
File without changes