views.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. __package__ = 'archivebox.plugantic'
  2. import inspect
  3. from typing import Any
  4. from django.http import HttpRequest
  5. from django.utils.html import format_html, mark_safe
  6. from admin_data_views.typing import TableContext, ItemContext
  7. from admin_data_views.utils import render_with_table_view, render_with_item_view, ItemLink
  8. from plugantic.plugins import LOADED_PLUGINS
  9. from django.conf import settings
  10. def obj_to_yaml(obj: Any, indent: int=0) -> str:
  11. indent_str = " " * indent
  12. if isinstance(obj, dict):
  13. if not obj:
  14. return "{}"
  15. result = "\n"
  16. for key, value in obj.items():
  17. result += f"{indent_str}{key}:{obj_to_yaml(value, indent + 1)}\n"
  18. return result
  19. elif isinstance(obj, list):
  20. if not obj:
  21. return "[]"
  22. result = "\n"
  23. for item in obj:
  24. result += f"{indent_str}- {obj_to_yaml(item, indent + 1).lstrip()}\n"
  25. return result.rstrip()
  26. elif isinstance(obj, str):
  27. if "\n" in obj:
  28. return f" |\n{indent_str} " + obj.replace("\n", f"\n{indent_str} ")
  29. else:
  30. return f" {obj}"
  31. elif isinstance(obj, (int, float, bool)):
  32. return f" {str(obj)}"
  33. elif callable(obj):
  34. source = '\n'.join(
  35. '' if 'def ' in line else line
  36. for line in inspect.getsource(obj).split('\n')
  37. if line.strip()
  38. ).split('lambda: ')[-1].rstrip(',')
  39. return f" {indent_str} " + source.replace("\n", f"\n{indent_str} ")
  40. else:
  41. return f" {str(obj)}"
  42. @render_with_table_view
  43. def binaries_list_view(request: HttpRequest, **kwargs) -> TableContext:
  44. assert request.user.is_superuser, 'Must be a superuser to view configuration settings.'
  45. rows = {
  46. "Binary": [],
  47. "Found Version": [],
  48. "From Plugin": [],
  49. "Provided By": [],
  50. "Found Abspath": [],
  51. "Related Configuration": [],
  52. "Overrides": [],
  53. # "Description": [],
  54. }
  55. relevant_configs = {
  56. key: val
  57. for key, val in settings.CONFIG.items()
  58. if '_BINARY' in key or '_VERSION' in key
  59. }
  60. for plugin in LOADED_PLUGINS:
  61. for binary in plugin.binaries:
  62. binary = binary.load_or_install()
  63. rows['Binary'].append(ItemLink(binary.name, key=binary.name))
  64. rows['Found Version'].append(binary.loaded_version)
  65. rows['From Plugin'].append(plugin.name)
  66. rows['Provided By'].append(binary.loaded_provider)
  67. rows['Found Abspath'].append(binary.loaded_abspath)
  68. rows['Related Configuration'].append(mark_safe(', '.join(
  69. f'<a href="/admin/environment/config/{config_key}/">{config_key}</a>'
  70. for config_key, config_value in relevant_configs.items()
  71. if binary.name.lower().replace('-', '').replace('_', '').replace('ytdlp', 'youtubedl') in config_key.lower()
  72. # or binary.name.lower().replace('-', '').replace('_', '') in str(config_value).lower()
  73. )))
  74. rows['Overrides'].append(obj_to_yaml(binary.provider_overrides))
  75. # rows['Description'].append(binary.description)
  76. return TableContext(
  77. title="Binaries",
  78. table=rows,
  79. )
  80. @render_with_item_view
  81. def binary_detail_view(request: HttpRequest, key: str, **kwargs) -> ItemContext:
  82. assert request.user.is_superuser, 'Must be a superuser to view configuration settings.'
  83. binary = None
  84. plugin = None
  85. for loaded_plugin in LOADED_PLUGINS:
  86. for loaded_binary in loaded_plugin.binaries:
  87. if loaded_binary.name == key:
  88. binary = loaded_binary
  89. plugin = loaded_plugin
  90. assert plugin and binary, f'Could not find a binary matching the specified name: {key}'
  91. binary = binary.load_or_install()
  92. return ItemContext(
  93. slug=key,
  94. title=key,
  95. data=[
  96. {
  97. "name": binary.name,
  98. "description": binary.description,
  99. "fields": {
  100. 'plugin': plugin.name,
  101. 'binprovider': binary.loaded_provider,
  102. 'abspath': binary.loaded_abspath,
  103. 'version': binary.loaded_version,
  104. 'overrides': obj_to_yaml(binary.provider_overrides),
  105. 'providers': obj_to_yaml(binary.providers_supported),
  106. },
  107. "help_texts": {
  108. # TODO
  109. },
  110. },
  111. ],
  112. )
  113. @render_with_table_view
  114. def plugins_list_view(request: HttpRequest, **kwargs) -> TableContext:
  115. assert request.user.is_superuser, 'Must be a superuser to view configuration settings.'
  116. rows = {
  117. "Name": [],
  118. "binaries": [],
  119. "extractors": [],
  120. "replayers": [],
  121. "configs": [],
  122. "description": [],
  123. }
  124. for plugin in LOADED_PLUGINS:
  125. plugin = plugin.load_or_install()
  126. rows['Name'].append(ItemLink(plugin.name, key=plugin.name))
  127. rows['binaries'].append(mark_safe(', '.join(
  128. f'<a href="/admin/environment/binaries/{binary.name}/">{binary.name}</a>'
  129. for binary in plugin.binaries
  130. )))
  131. rows['extractors'].append(', '.join(extractor.name for extractor in plugin.extractors))
  132. rows['replayers'].append(', '.join(replayer.name for replayer in plugin.replayers))
  133. rows['configs'].append(mark_safe(', '.join(
  134. f'<a href="/admin/environment/config/{config_key}/">{config_key}</a>'
  135. for configset in plugin.configs
  136. for config_key in configset.__fields__.keys()
  137. if config_key != 'section' and config_key in settings.CONFIG
  138. )))
  139. rows['description'].append(str(plugin.description))
  140. return TableContext(
  141. title="Installed plugins",
  142. table=rows,
  143. )
  144. @render_with_item_view
  145. def plugin_detail_view(request: HttpRequest, key: str, **kwargs) -> ItemContext:
  146. assert request.user.is_superuser, 'Must be a superuser to view configuration settings.'
  147. plugin = None
  148. for loaded_plugin in LOADED_PLUGINS:
  149. if loaded_plugin.name == key:
  150. plugin = loaded_plugin
  151. assert plugin, f'Could not find a plugin matching the specified name: {key}'
  152. plugin = plugin.load_or_install()
  153. return ItemContext(
  154. slug=key,
  155. title=key,
  156. data=[
  157. {
  158. "name": plugin.name,
  159. "description": plugin.description,
  160. "fields": {
  161. 'configs': plugin.configs,
  162. 'binaries': plugin.binaries,
  163. 'extractors': plugin.extractors,
  164. 'replayers': plugin.replayers,
  165. },
  166. "help_texts": {
  167. # TODO
  168. },
  169. },
  170. ],
  171. )