Guest User

cRPG update 1.02

a guest
Apr 18th, 2016
318
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.76 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. # Python 3
  3. # μτf±8
  4. # ___________________________________________________________________________
  5. #
  6. #   Name:        crpg_update
  7. #   Description: -
  8. #   Version:     1.02
  9. #   Project:     cRPG
  10. #
  11. #   Version Last to First: yyyy/mm/dd x.xx Author: Description
  12. #   NB: Change __version__
  13. #
  14. #   2016/04/12 1.02 Élio:
  15. #       - Time sleep calls replaced by registration of sleep at exit, 3 s instead of 5
  16. #       - Minor fixes on duplicated exception print removed
  17. #   2016/04/11 1.01 Élio:
  18. #       - Major fixes on reference to retrieve std_banners_custom files
  19. #       - Rearrangement of the code, creation of a function to check and download list of files
  20. #       - Add a time sleep of 5 s before closing application
  21. #   2016/04/10 1.00 Élio: Creation
  22. # ___________________________________________________________________________
  23.  
  24. """cRPG update"""
  25.  
  26. import argparse # Parser for command-line options, arguments and sub-commands
  27. import gzip # Support for gzip files
  28. import hashlib # Secure hashes and message digests
  29. import os.path # Common pathname manipulations
  30. import sys # System-specific parameters and functions
  31. import time # Time access and conversions
  32. import urllib.error # Exception classes raised by urllib.request
  33. import urllib.parse # Parse URLs into components
  34. import urllib.request # Extensible library for opening URLs
  35. import atexit
  36.  
  37. __version__ = '1.02'
  38. APPLICATION_NAME = "cRPG update {}".format(__version__)
  39.  
  40. def binary_prefix_format(value: int) -> str: # {
  41.     """IEC binary prefix format
  42.    IEC (SI) names: Yobi (Yotta), Zebi (Zetta), Exbi (Exa), Pebi (Peta), Tebi (Tera), Gibi (Giga), Mebi (Mega), Kibi (kilo)
  43.    """
  44.     for val, unit in zip(
  45.         (1 << 80, 1 << 70, 1 << 60, 1 << 50, 1 << 40, 1 << 30, 1 << 20, 1 << 10),
  46.         ("Yi", "Zi", "Ei", "Pi", "Ti", "Gi", "Mi", "Ki")
  47.     ): # {
  48.         if value >= val:
  49.             return "{:0.2f} {}".format(value / val, unit)
  50.     # } for
  51.     else: return "{:0.0f}".format(value)
  52. # } binary_prefix_format
  53.  
  54. def url_read(url: str, verbose=True) -> bytes: # {
  55.     """Read URL data file
  56.    """
  57.     url_o = urllib.parse.urlparse(url)
  58.     try: # {
  59.         with urllib.request.urlopen(url) as f: # {
  60.             if f.reason != "OK" or not f.readable(): # { Validation: PASSED 2016/04/10
  61.                 exc_value = Exception("Unable to retrieve file: {}".format(
  62.                     os.path.basename(url_o.path),
  63.                 ))
  64.                 sys.exit(exc_value) # TODO: Handle error
  65.             # }
  66.             meta = f.info()
  67.             data_length = int(meta.get_all('Content-Length')[0])
  68.             index = 0
  69.             progress = 0
  70.             progress_old = -1
  71.             data = bytes()
  72.             while index < data_length: # {
  73.                 data_chunck = f.read(100 << 10) # 100 kB
  74.                 data += data_chunck
  75.  
  76.                 index += len(data_chunck)
  77.                 progress = index / data_length
  78.                 if ((progress - progress_old) >= 0.1) or progress == 1: # {
  79.                     msg = "\tDownloading file... {}: {} / {} bytes".format(
  80.                         "{:0.0%}".format(progress).rjust(4),
  81.                         binary_prefix_format(index),
  82.                         binary_prefix_format(data_length),
  83.                     )
  84.                     if verbose: print(msg)
  85.                     progress_old = progress
  86.                 # } if
  87.             # } while
  88.             if verbose: print("")
  89.         # } with
  90.         return data
  91.     # } try
  92.     except urllib.error.HTTPError as exc_value: # {
  93.         sys.exit(exc_value) # TODO: Handle error
  94.         # os.path.basename(url_o.path)
  95.         # exc_value.code
  96.         # exc_value.reason
  97.     # } except urllib.error.HTTPError
  98. # } url_read
  99.  
  100. def url_unparse(scheme: str, netloc: str, path: str, params: str="", query: str="", fragment: str="") -> str: # {
  101.     return urllib.parse.urlunparse((scheme, netloc, path, params, query, fragment))
  102. # } url_unparse
  103.  
  104. def file_open(file: str) -> bytes: # {
  105.     try: # {
  106.         with open(file, 'rb') as f: # {
  107.             # TODO: Segment packets
  108.             return f.read()
  109.         # }
  110.     # } try
  111.     except (IOError, OSError) as exc_value: # {
  112.         sys.exit(exc_value) # TODO: Handle error
  113.     # } except (IOError, OSError)
  114. # } file_open
  115.  
  116. def compute_file_md5_checksum(file_path): # {
  117.     """Compute file MD5 checksum
  118.    """
  119.     m = hashlib.md5()
  120.     with open(file_path, 'rb') as f: # {
  121.         m.update(f.read())
  122.     # } with
  123.     return m.hexdigest()
  124. # } compute_file_md5_checksum
  125.  
  126. def retrieve_file(url_o, filename, output_path, iscompressed=True, verbose=True): # {
  127.     # Read URL file
  128.     url_path_file = url_unparse(
  129.         url_o.scheme,
  130.         url_o.netloc,
  131.         os.path.normpath(os.path.join(url_o.path, filename + ["", ".gz"][iscompressed])).replace("\\", "/"),
  132.     )
  133.     if verbose: print("Downloading: {}".format(filename))
  134.     data = url_read(url_path_file, verbose)
  135.     if iscompressed: # {
  136.         # Decompress file
  137.         data = gzip.decompress(data)
  138.     # }
  139.     file_path = os.path.normpath(os.path.join(output_path, filename))
  140.     # Create directory
  141.     if not os.path.exists(os.path.dirname(file_path)): os.makedirs(os.path.dirname(file_path))
  142.     # Store file
  143.     with open(file_path, "wb") as f: # {
  144.         f.write(data)
  145.     # }
  146.     return data
  147. # } retrieve_file
  148.  
  149. def update_filelist(url, update_customs_banners, verbose): # {
  150.     filelist = retrieve_file(url, "filelist.txt", input_path, iscompressed=False, verbose=False)
  151.     filelist = filelist.decode('utf-8', errors='ignore').splitlines()
  152.     index = 0
  153.     progress = 0
  154.     progress_old = -1
  155.     for line in filelist: # { for each line of filelist
  156.         try: # {
  157.             try:
  158.                 file_path, file_md5_checksum = line.split(", ")
  159.             except ValueError: continue
  160.  
  161.             if "version.txt" in file_path: continue # Skip
  162.             if "filelist.txt" in file_path: continue # Skip
  163.  
  164.             local_file_path = os.path.normpath(os.path.join(input_path, file_path))
  165.  
  166.             local_file_md5_checksum = None
  167.             if os.path.exists(local_file_path): # {
  168.                 local_file_md5_checksum = compute_file_md5_checksum(local_file_path)
  169.             # }
  170.             if local_file_md5_checksum != file_md5_checksum: # {
  171.                 # print("{}: {}".format(file_path.ljust(50), file_md5_checksum))
  172.                 # print("{}: {}".format("Local".ljust(50), local_file_md5_checksum))
  173.                 if "std_banners_custom" in file_path and not update_customs_banners: continue
  174.                 retrieve_file(url, file_path, input_path, iscompressed=True, verbose=True)
  175.                 # }
  176.             # }
  177.         # } try
  178.         finally: # {
  179.             if verbose: # {
  180.                 index += 1
  181.                 progress = index / len(filelist)
  182.                 if ((progress - progress_old) >= 0.1) or progress == 1: # {
  183.                     msg = "Updating files... {}: {} / {}".format(
  184.                         "{:0.0%}".format(progress).rjust(4),
  185.                         index,
  186.                         len(filelist),
  187.                     )
  188.                     print(msg)
  189.                     progress_old = progress
  190.                 # } if
  191.             # } if verbose
  192.         # } finally
  193.     # } for each line of filelist
  194. # } update_filelist
  195.  
  196. if __name__ == '__main__': # {
  197.     atexit.register(time.sleep, 3)
  198.     input_path_name = "cRPG"
  199.     parser = argparse.ArgumentParser(description=APPLICATION_NAME)
  200.     parser.add_argument( # Positional argument
  201.         action='store',
  202.         # nargs,
  203.         # const,
  204.         # default,
  205.         type=str,
  206.         # choices,
  207.         help="{} directory".format(input_path_name),
  208.         # metavar,
  209.         dest='input_directory'
  210.     )
  211.     arguments = parser.parse_args()
  212.     input_path = arguments.input_directory
  213.     input1_url = "" ## Removed
  214.     input2_url = "" ## Removed
  215.     #
  216.     input_path = os.path.normpath(input_path)
  217.     # Check input path
  218.     if not os.path.isdir(input_path) or not os.path.exists(input_path): # {
  219.         exc_value = Exception("Invalid {} path: {}".format(
  220.             input_path_name,
  221.             input_path,
  222.         ))
  223.         sys.exit(exc_value)
  224.     # }
  225.     print("{} directory: {}".format(input_path_name, input_path))
  226.  
  227.     input_path_version = os.path.join(input_path, "version.txt")
  228.     data = file_open(input_path_version)
  229.     try:
  230.         local_version = data.splitlines()[0].decode('utf-8', errors='ignore')
  231.     except IndexError: # {
  232.         exc_value = Exception("Invalid file: {}".format(
  233.             "version.txt"
  234.         ))
  235.         sys.exit(exc_value)
  236.     # }
  237.     print("Local version:  {}".format(local_version))
  238.  
  239.     input1_url_o = urllib.parse.urlparse(input1_url)
  240.     url_path_version = url_unparse(
  241.         input1_url_o.scheme,
  242.         input1_url_o.netloc,
  243.         os.path.join(input1_url_o.path, "version.txt"),
  244.     )
  245.     data = url_read(url_path_version, False)
  246.     server_version = data.splitlines()[0].decode('utf-8', errors='ignore')
  247.     print("Server version: {}\n".format(server_version))
  248.  
  249.     update_required = (local_version != server_version)
  250.     if update_required: # {
  251.         print("{} update is required\n".format(input_path_name))
  252.         update_filelist(input1_url_o, update_customs_banners=False, verbose=True)
  253.     # }
  254.  
  255.     if update_required: # {
  256.         # At the end update the "critical" files
  257.         retrieve_file(input1_url_o, "version.txt", input_path, iscompressed=False, verbose=False)
  258.         retrieve_file(input1_url_o, "filelist.txt", input_path, iscompressed=False, verbose=False)
  259.     # }
  260.  
  261.     # For banners
  262.     input2_url_o = urllib.parse.urlparse(input2_url)
  263.     update_filelist(input2_url_o, update_customs_banners=True, verbose=False)
  264.     sys.exit(0)
  265. # } __main__
Advertisement
Add Comment
Please, Sign In to add comment