js_api.py 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. from __future__ import annotations
  2. import json
  3. import os.path
  4. from typing import Iterator
  5. from uuid import uuid4
  6. from functools import partial
  7. import webview
  8. import platformdirs
  9. from plyer import camera
  10. from plyer import filechooser
  11. app_storage_path = platformdirs.user_pictures_dir
  12. user_select_image = partial(
  13. filechooser.open_file,
  14. path=platformdirs.user_pictures_dir(),
  15. filters=[["Image", "*.jpg", "*.jpeg", "*.png", "*.webp", "*.svg"]],
  16. )
  17. try:
  18. from android.runnable import run_on_ui_thread
  19. import android.permissions
  20. from android.permissions import Permission
  21. from android.permissions import _RequestPermissionsManager
  22. _RequestPermissionsManager.register_callback()
  23. from .android_gallery import user_select_image
  24. has_android = True
  25. except:
  26. run_on_ui_thread = lambda a : a
  27. has_android = False
  28. from .api import Api
  29. class JsApi(Api):
  30. def get_conversation(self, options: dict, **kwargs) -> Iterator:
  31. window = webview.windows[0]
  32. if hasattr(self, "image") and self.image is not None:
  33. kwargs["image"] = open(self.image, "rb")
  34. for message in self._create_response_stream(
  35. self._prepare_conversation_kwargs(options, kwargs),
  36. options.get("conversation_id"),
  37. options.get('provider')
  38. ):
  39. if not window.evaluate_js(f"if (!this.abort) this.add_message_chunk({json.dumps(message)}); !this.abort && !this.error;"):
  40. break
  41. self.image = None
  42. self.set_selected(None)
  43. @run_on_ui_thread
  44. def choose_image(self):
  45. self.request_permissions()
  46. user_select_image(
  47. on_selection=self.on_image_selection
  48. )
  49. @run_on_ui_thread
  50. def take_picture(self):
  51. self.request_permissions()
  52. filename = os.path.join(app_storage_path(), f"chat-{uuid4()}.png")
  53. camera.take_picture(filename=filename, on_complete=self.on_camera)
  54. def on_image_selection(self, filename):
  55. filename = filename[0] if isinstance(filename, list) and filename else filename
  56. if filename and os.path.exists(filename):
  57. self.image = filename
  58. else:
  59. self.image = None
  60. self.set_selected(None if self.image is None else "image")
  61. def on_camera(self, filename):
  62. if filename and os.path.exists(filename):
  63. self.image = filename
  64. else:
  65. self.image = None
  66. self.set_selected(None if self.image is None else "camera")
  67. def set_selected(self, input_id: str = None):
  68. window = webview.windows[0]
  69. if window is not None:
  70. window.evaluate_js(
  71. f"document.querySelector(`.image-label.selected`)?.classList.remove(`selected`);"
  72. )
  73. if input_id is not None and input_id in ("image", "camera"):
  74. window.evaluate_js(
  75. f'document.querySelector(`label[for="{input_id}"]`)?.classList.add(`selected`);'
  76. )
  77. def request_permissions(self):
  78. if has_android:
  79. android.permissions.request_permissions([
  80. Permission.CAMERA,
  81. Permission.READ_EXTERNAL_STORAGE,
  82. Permission.WRITE_EXTERNAL_STORAGE
  83. ])