Skip to content

Commit

Permalink
Add plugin support
Browse files Browse the repository at this point in the history
  • Loading branch information
summerkirakira committed Apr 17, 2024
1 parent ac25965 commit 0011748
Show file tree
Hide file tree
Showing 15 changed files with 676 additions and 22 deletions.
40 changes: 40 additions & 0 deletions backend/app/core/extension_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from stevedore import extension
from ..plugins import get_plugins
from typing import Optional, Type
from ..core.interface.requester import RequesterInterface


class Extension:
def __init__(self, name, plugin):
self.name = name
self.plugin = plugin


def get_extensions() -> list[Extension]:
external_extensions = extension.ExtensionManager(
namespace='myapp.extensions',
invoke_on_load=False
)

internal_extensions = [Extension(ext.namespace, ext) for ext in get_plugins()]
external_extensions = [Extension(ext.name, ext.plugin) for ext in external_extensions]

return internal_extensions + external_extensions


def get_extension(name: str) -> Optional[Extension]:
for ext in get_extensions():
if ext.name == name:
return ext
return None


def get_plugin_by_type(plugin_type: str) -> list[Extension]:
return [ext for ext in get_extensions() if ext.name.startswith(plugin_type)]


def get_requester(name: str) -> Optional[Type[RequesterInterface]]:
ext = get_extension(name)
if ext is not None:
return ext.plugin
return None
Empty file.
24 changes: 24 additions & 0 deletions backend/app/core/interface/downloader_middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import abc
import typing


class DownloaderMiddleware(abc.ABC):
@staticmethod
def namespace(cls) -> str:
pass

@staticmethod
def name(cls) -> str:
pass

@abc.abstractmethod
def download(self, url: str, **kwargs) -> typing.Any:
pass

@abc.abstractmethod
def status(self) -> typing.Tuple[int, str]:
pass

@abc.abstractmethod
def __str__(self) -> str:
pass
19 changes: 19 additions & 0 deletions backend/app/core/interface/engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import abc


class EngineInterface(abc.ABC):
@staticmethod
def namespace(cls) -> str:
pass

@staticmethod
def name(cls) -> str:
pass

@abc.abstractmethod
def status(self):
pass

@abc.abstractmethod
def parse(self, data):
pass
11 changes: 11 additions & 0 deletions backend/app/core/interface/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from pydantic import BaseModel


class UserConfigEntry(BaseModel):
name = str
description = str
placeholder = str


class UserConfigReq(BaseModel):
entries = [UserConfigEntry]
25 changes: 25 additions & 0 deletions backend/app/core/interface/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import abc
from typing import Tuple


class ParserInterface(metaclass=abc.ABCMeta):
@staticmethod
def namespace(cls) -> str:
pass

@staticmethod
def name(cls) -> str:
pass

@abc.abstractmethod
async def parse(self, data):
pass

@abc.abstractmethod
def status(self) -> Tuple[int, str]:
pass

@abc.abstractmethod
def cancel(self) -> bool:
pass

16 changes: 16 additions & 0 deletions backend/app/core/interface/requester.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import abc
import typing


class RequesterInterface(abc.ABC):
@abc.abstractmethod
def name(self) -> str:
pass

@abc.abstractmethod
async def request(self, url: str, **kwargs) -> typing.Any:
pass

@abc.abstractmethod
def status(self) -> typing.Tuple[int, str]:
pass
2 changes: 2 additions & 0 deletions backend/app/plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def get_plugins():
return []
4 changes: 4 additions & 0 deletions backend/app/plugins/engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from ..core.extension_manager import get_requester
from ..core.interface.engine import EngineInterface


45 changes: 45 additions & 0 deletions backend/app/plugins/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from typing import Tuple

from ..core.extension_manager import get_requester
from ..core.interface.parser import ParserInterface

import xml.etree.ElementTree as ET
from pydantic import BaseModel


class TorrentMeta(BaseModel):
id: str
title: str
link: str
description: str
pubDate: str
contentLength: int


class MikanParser(ParserInterface):

@staticmethod
def namespace(cls) -> str:
return 'parser.default.mikan'

@staticmethod
def name(cls) -> str:
return 'A Mikan project Parser'

async def parse(self, data: str):
mikan_requester = get_requester('requester.default.mikan')
if mikan_requester is None:
raise Exception('Requester not found')
mikan_requester = mikan_requester()
rss_data = await mikan_requester.request(data)
root = ET.fromstring(rss_data)





def status(self) -> Tuple[int, str]:
pass

def cancel(self) -> bool:
pass
34 changes: 34 additions & 0 deletions backend/app/plugins/requester.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import typing
import aiohttp

from app.core.interface.requester import RequesterInterface


async def fetch(session: aiohttp.ClientSession, url: str) -> str:
async with session.get(url) as response:
return await response.text()


class MikanRequester(RequesterInterface):

def __init__(self):
self._status = 'Waiting for start...'

@classmethod
def namespace(cls) -> str:
return 'requester.default.mikan'

def name(self) -> str:
return 'A Mikan Project Requester'

async def request(self, url: str, **kwargs) -> typing.Any:
self._status = 'Requesting...'
async with aiohttp.ClientSession() as session:
self._status = 'Fetching...'
result = await fetch(session, url)
self._status = 'Done'
return result

def status(self) -> typing.Tuple[int, str]:
return 100, self._status

Loading

0 comments on commit 0011748

Please sign in to comment.