Advertisement
FocusedWolf

Python: Software Version Checker

Feb 1st, 2024 (edited)
582
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.78 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. # Version 27
  4.  
  5. # POSTED ONLINE: https://pastebin.com/Dp4dK0Mz
  6.  
  7. # SOURCE: https://stackoverflow.com/questions/3029816/how-do-i-get-a-thread-safe-print-in-python-2-6
  8. # Python uses separate opcodes for object printing and newline printing.
  9. # With multiple threads printing you would encounter newlines printed out of turn separate from the objects.
  10. # Joining the strings before calling print(...) fixes the issue.
  11. def safe_print(*args, sep=' ', end='\n', file=None, flush=False):
  12.     # NOTE: Using end=None, or removing end='', will result in an extra newline in the output.
  13.     print(sep.join(map(str, args)) + end, sep=sep, end='', file=file, flush=flush)
  14.  
  15. # -----
  16.  
  17. import asyncio
  18. class App:
  19.     def __init__(self, name, installedVersion, patternData, urlData):
  20.         self.name = name
  21.         self.installedVersion = installedVersion
  22.         self.__pattern = patternData[0]
  23.         self.__patternMatchIndex = patternData[1]
  24.         self.__url = urlData[0]
  25.         self.__urlEncoding = urlData[1]
  26.  
  27.     async def GetLatestVersion(self):
  28.         loop = asyncio.get_running_loop()
  29.         if 'api.github.com' in self.__url:
  30.             latestVersion = await loop.run_in_executor(None, search_json, self.__pattern, self.__url, [0, 'name'], self.__urlEncoding, self.__patternMatchIndex)
  31.         else:
  32.             latestVersion = await loop.run_in_executor(None, search_url, self.__pattern, self.__url, self.__urlEncoding, self.__patternMatchIndex)
  33.         return latestVersion
  34.  
  35.     async def DisplayStatus(self, skipUpToDate=True):
  36.         latestVersion = await self.GetLatestVersion()
  37.         upToDate = self.installedVersion == latestVersion
  38.         if latestVersion == None:
  39.             safe_print(color(' ! {}: Failed to detect the latest version (Last known version: {} --> ?)\n   URL: {}\n'.format(self.name, self.installedVersion, self.__url), RED))
  40.         elif not upToDate:
  41.             safe_print(color(' * {}: Update detected (Last known version: {} --> {})\n   URL: {}\n'.format(self.name, self.installedVersion, latestVersion, self.__url), YELLOW))
  42.         elif not skipUpToDate:
  43.             safe_print(color(' - {}: No update detected (Last known version: {})\n'.format(self.name, latestVersion), GREEN))
  44.         return not upToDate
  45.  
  46. # -----
  47.  
  48. import json
  49. def search_json(pattern, url, indicies, encoding='UTF-8', matchIndex=0, timeoutSeconds=5):
  50.     response = download_url(url, encoding, timeoutSeconds)
  51.     if response == None:
  52.         return None
  53.     data = json.loads(response)
  54.     for index in indicies:
  55.         data = data[index]
  56.     return regex_search(data, pattern, matchIndex)
  57.  
  58. def search_url(pattern, url, encoding='UTF-8', matchIndex=0, timeoutSeconds=5):
  59.     response = download_url(url, encoding, timeoutSeconds)
  60.     return regex_search(response, pattern, matchIndex)
  61.  
  62. # -----
  63.  
  64. import urllib.request
  65. from urllib.error import *
  66. import socket
  67. def download_url(url, encoding='UTF-8', timeoutSeconds=5):
  68.     try:
  69.         response = urllib.request.urlopen(url, timeout=timeoutSeconds, context=unverifiedContext).read().decode(encoding)
  70.     except HTTPError as error:
  71.         safe_print('   HTTP-Error: Data not retrieved because {}\n   URL: {}\n'.format(error, url))
  72.     except URLError as error:
  73.         if isinstance(error.reason, socket.timeout):
  74.             safe_print('   Timeout-Error: Data not retrieved because {}\n   URL: {}\n'.format(error, url))
  75.         else:
  76.             safe_print('   URL-Error: Data not retrieved because {}\n   URL: {}\n'.format(error, url))
  77.     else:
  78.         return response
  79.  
  80. # Disable certificate verification with unverified SSL context.
  81. # SOURCE: https://support.chainstack.com/hc/en-us/articles/9117198436249-Common-SSL-Issues-on-Python-and-How-to-Fix-it
  82. import ssl
  83. unverifiedContext = ssl._create_unverified_context()
  84.  
  85. # -----
  86.  
  87. import re
  88. def regex_search(string, pattern, matchIndex=0):
  89.     if string == None:
  90.         return None
  91.     matches = re.findall(pattern, string)
  92.     if matches == None or len(matches) == 0:
  93.         return None
  94.     return matches[matchIndex]
  95.  
  96. # -----
  97.  
  98. # BLACK = '\033[30m'
  99. RED = '\033[31m'
  100. GREEN = '\033[32m'
  101. YELLOW = '\033[33m'
  102. BLUE = '\033[34m'
  103. MAGENTA = '\033[35m'
  104. CYAN = '\033[36m'
  105. WHITE = '\033[37m'
  106. UNDERLINE = '\033[4m'
  107. RESET = '\033[0m'
  108.  
  109. def color(var, color):
  110.     return color + str(var) + RESET
  111.  
  112. import platform
  113. system = platform.system()
  114. if system == 'Windows':
  115.     import os
  116.     # Needed to display colored text on Windows for some reason.
  117.     os.system('cls')
  118.  
  119. # -----
  120.  
  121. import socket
  122. async def wait_for_internet(host="www.google.com", port=80, timeout=1):
  123.     try:
  124.         with socket.create_connection((host, port), timeout):
  125.             #  safe_print(color(" Internet connection established . . .\n", GREEN))
  126.             return True
  127.     except OSError:
  128.         safe_print(color(" Waiting for internet connection . . .\n", YELLOW))
  129.  
  130.     while True:
  131.         try:
  132.             with socket.create_connection((host, port), timeout):
  133.                 # safe_print(color(" Internet connection established . . .\n", GREEN))
  134.                 return True
  135.         except OSError:
  136.             await asyncio.sleep(1) # Retry every second.
  137.  
  138. # -----
  139.  
  140. def wait_for_any_keypress():
  141.     import sys
  142.     if sys.platform == 'win32':
  143.         import os
  144.         os.system('pause')
  145.     elif sys.platform.startswith('linux') or sys.platform == 'darwin':
  146.         print('Press any key to continue . . .')
  147.         import termios
  148.         import tty
  149.         stdin_file_desc = sys.stdin.fileno()
  150.         old_stdin_tty_attr = termios.tcgetattr(stdin_file_desc)
  151.         try:
  152.             tty.setraw(stdin_file_desc)
  153.             sys.stdin.read(1)
  154.         finally:
  155.             termios.tcsetattr(stdin_file_desc, termios.TCSADRAIN, old_stdin_tty_attr)
  156.  
  157. # -----
  158.  
  159. # NOTE: You need to update these version numbers manually until a way is found to query them from the installed software directly.
  160. apps = [
  161.     App('Voicemeeter Banana', '2.1.1.9', (r'Version ([0-9.]+)(?: \([\w\s]+\))', 1),          ('https://voicemeeter.com', 'UTF-8')),
  162.   # App('Voicemeeter Banana', '2.0.6.8', (r'Voicemeeter ([0-9.]+) \(ZIP Package\)', 0),      ('https://vb-audio.com/Voicemeeter/banana.htm', 'UTF-8')), # This website keeps dying...
  163.     App('Equalizer APO',      '1.4.1',     (r'EqualizerAPO(?:.*?)-([0-9.]+)\.exe', 0),         ('https://sourceforge.net/projects/equalizerapo/files/', 'UTF-8')),
  164.     App('HeSuVi',             '2.0.0.1', (r'HeSuVi_([0-9.]+)\.exe', 0),                      ('https://sourceforge.net/projects/hesuvi/files/', 'UTF-8')),
  165.     App('IEM Plug-in Suite',  '1.14.1',  (r'Latest version: <strong>v([0-9.]+)', 0),         ('https://plugins.iem.at/download/', 'UTF-8')),
  166.   # App('Vim',                '9.1',     (r'Vim ([0-9.]+) is the latest stable version', 0), ('https://www.vim.org/download.php/', 'ISO-8859-1')), # This website keeps dying...
  167.     App('Vim',                '9.1',     (r'\d+\.\d+', 0),                                   ('https://api.github.com/repos/vim/vim/tags', 'UTF-8')),
  168.     App('Nvidia',             '566.36',  (r'GeForce ([0-9.]+)', 0),                          ('https://www.guru3d.com/files/category/videocards-nvidia-geforce-windows-7-8-10/', 'UTF-8')),
  169.     App('MSI Afterburner',    '4.6.6',   (r'MSI Afterburner ([0-9.]+) Download', 0),         ('https://www.guru3d.com/download/msi-afterburner-beta-download/', 'UTF-8')),
  170. ]
  171.  
  172. async def main():
  173.     import sys
  174.     pauseNeeded = len(sys.argv) == 1 or sys.argv[1] != 'nopause'
  175.  
  176.     await wait_for_internet()
  177.  
  178.     updateNeeded = False
  179.     tasks = [app.DisplayStatus(skipUpToDate=not pauseNeeded) for app in apps]
  180.     for future in asyncio.as_completed(tasks):
  181.         updateNeeded |= await future
  182.  
  183.     if pauseNeeded or updateNeeded:
  184.         wait_for_any_keypress()
  185.  
  186.  
  187. if __name__ == '__main__':
  188.     asyncio.run(main())
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement