diff --git a/app/YtManagerApp/management/video_provider_manager.py b/app/YtManagerApp/management/video_provider_manager.py index e411105..c848310 100644 --- a/app/YtManagerApp/management/video_provider_manager.py +++ b/app/YtManagerApp/management/video_provider_manager.py @@ -1,21 +1,20 @@ +import json import logging -from typing import List, Dict, Union, Iterable +from typing import List, Dict, Union, Iterable, Optional + +from django.db import transaction from YtManagerApp.models import VideoProviderConfig, Video, Subscription -from YtManagerApp.providers.video_provider import VideoProvider, InvalidURLError -import json -from collections import namedtuple +from YtManagerApp.providers.video_provider import VideoProvider, InvalidURLError, VideoProviderState log = logging.getLogger("VideoProviderManager") -VideoProviderInfo = namedtuple('VideoProviderInfo', ['id', 'name', 'is_configured', 'description']) - class VideoProviderManager(object): def __init__(self, registered_providers: List[VideoProvider]): self._registered_providers: Dict[str, VideoProvider] = {} - self._configured_providers: Dict[str, VideoProvider] = {} self._pending_configs: Dict[str, VideoProviderConfig] = {} + for rp in registered_providers: self.register_provider(rp) self._load() @@ -39,6 +38,28 @@ class VideoProviderManager(object): self._configure(provider, self._pending_configs[provider.id]) del self._pending_configs[provider.id] + def configure_provider(self, provider_id: str, config: Optional[Dict[str, any]]): + provider = self.get(provider_id) + + if config is not None: + provider.configure(config) + with transaction.atomic(): + cfg, _ = VideoProviderConfig.objects.get_or_create(provider_id=provider_id) + cfg.settings = json.dumps(config) + cfg.save() + provider.state = VideoProviderState.OK + + else: + provider.unconfigure() + VideoProviderConfig.objects.filter(provider_id=provider_id).delete() + provider.state = VideoProviderState.NOT_CONFIGURED + + def get_provider_config(self, provider_id: str): + cfg = VideoProviderConfig.objects.filter(provider_id=provider_id).first() + if cfg is not None: + return json.loads(cfg.settings) + return None + def _load(self) -> None: # Loads configuration from database for config in VideoProviderConfig.objects.all(): @@ -56,8 +77,8 @@ class VideoProviderManager(object): def _configure(self, provider, config): settings = json.loads(config.settings) provider.configure(settings) + provider.state = VideoProviderState.OK log.info(f"Configured video provider {provider.id}") - self._configured_providers[provider.id] = provider def get(self, item: Union[str, Subscription, Video]): """ @@ -79,12 +100,13 @@ class VideoProviderManager(object): :param url: :return: """ - for provider in self._configured_providers.values(): - try: - provider.validate_subscription_url(url) - return - except InvalidURLError: - pass + for provider in self._registered_providers.values(): + if provider.state == VideoProviderState.OK: + try: + provider.validate_subscription_url(url) + return + except InvalidURLError: + pass raise InvalidURLError("The given URL is not valid for any of the supported sites!") @@ -94,23 +116,16 @@ class VideoProviderManager(object): :param url: :return: """ - for provider in self._configured_providers.values(): - try: - provider.validate_subscription_url(url) - # Found the right provider - return provider.fetch_subscription(url) - except InvalidURLError: - pass + for provider in self._registered_providers.values(): + if provider.state == VideoProviderState.OK: + try: + provider.validate_subscription_url(url) + # Found the right provider + return provider.fetch_subscription(url) + except InvalidURLError: + pass raise InvalidURLError("The given URL is not valid for any of the supported sites!") - def get_available_providers(self) -> Iterable[VideoProviderInfo]: - """ - Gets a list of available providers and some basic information about them. - :return: List of dictionary entries - """ - for key, provider in self._registered_providers.items(): - yield VideoProviderInfo(id=key, - name=provider.name, - description=provider.description, - is_configured=(key in self._configured_providers)) + def get_available_providers(self) -> Iterable[VideoProvider]: + return self._registered_providers.values() diff --git a/app/YtManagerApp/providers/dummy_video_provider.py b/app/YtManagerApp/providers/dummy_video_provider.py index 8fc1c12..c3b8670 100644 --- a/app/YtManagerApp/providers/dummy_video_provider.py +++ b/app/YtManagerApp/providers/dummy_video_provider.py @@ -20,6 +20,9 @@ class DummyVideoProvider(VideoProvider): def configure(self, configuration: Dict[str, Any]) -> None: print(configuration) + def unconfigure(self): + pass + def validate_configuration(self, configuration: Dict[str, Any]): print("Validating...") if configuration["number_of_something"] >= 10: diff --git a/app/YtManagerApp/providers/video_provider.py b/app/YtManagerApp/providers/video_provider.py index f377fe4..41a72cc 100644 --- a/app/YtManagerApp/providers/video_provider.py +++ b/app/YtManagerApp/providers/video_provider.py @@ -1,4 +1,5 @@ from abc import abstractmethod, ABC +from enum import Enum from typing import Dict, Iterable, List, Any from django.forms import Field @@ -6,6 +7,12 @@ from django.forms import Field from YtManagerApp.models import Subscription, Video +class VideoProviderState(Enum): + NOT_CONFIGURED = 0 + OK = 1 + ERROR = 2 + + class ProviderValidationError(ValueError): """ Exception type thrown when validating configurations. @@ -44,6 +51,9 @@ class VideoProvider(ABC): """ settings: Dict[str, Field] = {} + def __init__(self): + self.state = VideoProviderState.NOT_CONFIGURED + @abstractmethod def configure(self, configuration: Dict[str, Any]) -> None: """ @@ -53,6 +63,14 @@ class VideoProvider(ABC): """ pass + @abstractmethod + def unconfigure(self) -> None: + """ + Destroys video provider configuration + :return: + """ + pass + @abstractmethod def validate_configuration(self, configuration: Dict[str, Any]) -> None: """ diff --git a/app/YtManagerApp/providers/ytapi_video_provider.py b/app/YtManagerApp/providers/ytapi_video_provider.py index 538b394..56a979f 100644 --- a/app/YtManagerApp/providers/ytapi_video_provider.py +++ b/app/YtManagerApp/providers/ytapi_video_provider.py @@ -25,6 +25,10 @@ class YouTubeApiVideoProvider(VideoProvider): self.__api_key = configuration['api_key'] self.__api = yt.YouTube(key=self.__api_key) + def unconfigure(self): + self.__api_key = None + self.__api = None + def validate_configuration(self, configuration: Dict[str, Any]): # TODO: implement pass diff --git a/app/YtManagerApp/static/YtManagerApp/css/style.scss b/app/YtManagerApp/static/YtManagerApp/css/style.scss index cfca912..254a50f 100644 --- a/app/YtManagerApp/static/YtManagerApp/css/style.scss +++ b/app/YtManagerApp/static/YtManagerApp/css/style.scss @@ -199,4 +199,8 @@ img.muted { font-size: 3rem; text-align: center; line-height: 4rem; +} + +.provider-status { + font-size: 10rem; } \ No newline at end of file diff --git a/app/YtManagerApp/static/YtManagerApp/js/settings_providers.js b/app/YtManagerApp/static/YtManagerApp/js/settings_providers.js new file mode 100644 index 0000000..4ae5f4a --- /dev/null +++ b/app/YtManagerApp/static/YtManagerApp/js/settings_providers.js @@ -0,0 +1,6 @@ + +jQuery(function() { + + + +}); \ No newline at end of file diff --git a/app/YtManagerApp/templates/YtManagerApp/controls/provider_config_modal.html b/app/YtManagerApp/templates/YtManagerApp/controls/provider_config_modal.html new file mode 100644 index 0000000..b7dd60e --- /dev/null +++ b/app/YtManagerApp/templates/YtManagerApp/controls/provider_config_modal.html @@ -0,0 +1,22 @@ +{% extends 'YtManagerApp/controls/modal.html' %} +{% load crispy_forms_tags %} + +{% block modal_title %} + {{ provider.name }} configuration +{% endblock modal_title %} + +{% block modal_content %} +
+{% endblock %} + +{% block modal_body %} + {{ form | crispy }} +{% endblock modal_body %} + +{% block modal_footer %} + + +{% endblock modal_footer %} \ No newline at end of file diff --git a/app/YtManagerApp/templates/YtManagerApp/settings/providers.html b/app/YtManagerApp/templates/YtManagerApp/settings/providers.html index 5489540..4d8189a 100644 --- a/app/YtManagerApp/templates/YtManagerApp/settings/providers.html +++ b/app/YtManagerApp/templates/YtManagerApp/settings/providers.html @@ -3,34 +3,48 @@ {% block body %} -