main.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import os
  2. import json
  3. import logging
  4. import distutils.spawn
  5. from ulauncher.api.client.Extension import Extension
  6. from ulauncher.api.client.EventListener import EventListener
  7. from ulauncher.api.shared.event import KeywordQueryEvent, ItemEnterEvent
  8. from ulauncher.api.shared.item.ExtensionResultItem import ExtensionResultItem
  9. from ulauncher.api.shared.item.SmallResultItem import SmallResultItem
  10. from ulauncher.api.shared.action.RenderResultListAction import RenderResultListAction
  11. from ulauncher.api.shared.action.RunScriptAction import RunScriptAction
  12. from ulauncher.api.shared.action.ExtensionCustomAction import ExtensionCustomAction
  13. logging.basicConfig()
  14. logger = logging.getLogger(__name__)
  15. global usage_cache
  16. usage_cache = {}
  17. # Usage tracking
  18. script_directory = os.path.dirname(os.path.realpath(__file__))
  19. usage_db = os.path.join(script_directory, "usage.json")
  20. if os.path.exists(usage_db):
  21. with open(usage_db, 'r') as db:
  22. # Read JSON string
  23. raw = db.read()
  24. # JSON to dict
  25. usage_cache = json.loads(raw)
  26. # Initialize items cache and x2goclient sessions file path
  27. x2goclient_bin = ""
  28. # Locate x2goclient profiles and binary
  29. x2go_sessions_path = ["{}/.x2goclient/sessions".format(os.environ.get('HOME'))]
  30. x2goclient_bin = distutils.spawn.find_executable('x2goclient')
  31. # This extension is useless without x2goclient
  32. if x2goclient_bin is None or x2goclient_bin == "":
  33. logger.error("x2goclient executable path could not be determined")
  34. exit()
  35. # Check if x2goclient sessions file exists
  36. x2go_sessions_path_exists = None
  37. # Check default paths first
  38. if os.path.isfile(x2go_sessions_path):
  39. x2go_sessions_path_exists = True
  40. class x2goclientExtension(Extension):
  41. def __init__(self):
  42. super(x2goclientExtension, self).__init__()
  43. self.subscribe(KeywordQueryEvent, KeywordQueryEventListener())
  44. self.subscribe(ItemEnterEvent, ItemEnterEventListener())
  45. def list_sessions(self, query):
  46. items = []
  47. with open('/home/bryan/.x2goclient/sessions') as lines:
  48. for line in lines:
  49. if line.startswith('host=') or line.startswith('name='):
  50. line = line.rstrip()
  51. line = line.split('=',1)
  52. item = line[1]
  53. items.append(item)
  54. it_items = iter(items)
  55. sessions = list(zip(it_items, it_items))
  56. for session in sessions:
  57. host = session[0]
  58. name = session[1]
  59. # Search for query inside filename and profile description
  60. # Multiple strings can be used to search in description
  61. # all() is used to achieve a AND search (include all keywords)
  62. keywords = query.split(" ")
  63. # if (query in base.lower()) or (query in desc.lower()):
  64. if (query.lower() in host.lower()) or (query.lower() in name.lower()):
  65. items_cache.append(create_item(host, name))
  66. items_cache = sorted(items_cache, key=sort_by_usage, reverse=True)
  67. return items_cache
  68. class KeywordQueryEventListener(EventListener):
  69. def on_event(self, event, extension):
  70. global x2go_sessions_path
  71. if extension.preferences["sessions"] is not "" or not x2go_sessions_path:
  72. # Tilde (~) won't work alone, need expanduser()
  73. x2go_sessions_path = os.path.expanduser(extension.preferences["sessions"])
  74. # pref_sessions_path = extension.preferences['sessions']
  75. logger.debug("x2goclient sessions path: {}".format(x2go_sessions_path))
  76. # Get query
  77. term = (event.get_argument() or "").lower()
  78. # Display all items when query empty
  79. sessions_list = extension.list_sessions(term)
  80. return RenderResultListAction(sessions_list[:8])
  81. class ItemEnterEventListener(EventListener):
  82. def on_event(self, event, extension):
  83. global usage_cache
  84. # Get query
  85. data = event.get_data()
  86. on_enter = data["id"]
  87. # The profilefile name is the ID
  88. base = os.path.basename(on_enter)
  89. b = os.path.splitext(base)[0]
  90. # Check usage and increment
  91. if b in usage_cache:
  92. usage_cache[b] = usage_cache[b]+1
  93. else:
  94. usage_cache[b] = 1
  95. # Update usage JSON
  96. with open(usage_db, 'w') as db:
  97. db.write(json.dumps(usage_cache, indent=2))
  98. return RunScriptAction('#!/usr/bin/env bash\n{} --session {}\n'.format(x2goclient_bin, on_enter), None).run()
  99. def create_item(host, name):
  100. return ExtensionResultItem(
  101. name=name,
  102. description=host,
  103. icon="images/x2goclient.svg",
  104. on_enter=ExtensionCustomAction(
  105. {"id": name})
  106. )
  107. def sort_by_usage(i):
  108. global usage_cache
  109. # Convert item name to ID format
  110. j = i._name.lower()
  111. # Return score according to usage
  112. if j in usage_cache:
  113. return usage_cache[j]
  114. # Default is 0 (no usage rank / unused)
  115. return 0
  116. if __name__ == "__main__":
  117. x2goclientExtension().run()