Advertisement
Guest User

cRPG update 1.03

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