Hello World

We will use the test plugin, to show how to create a plugin for unidown.

Basics

First of all we subclass from unidown.plugin.a_plugin.APlugin, to have all essential variables and functions for free.

from unidown.plugin import APlugin

class Plugin(APlugin):

Next part is to add basic information about the plugin like name, version and server host.

class Plugin(APlugin):
    _INFO = PluginInfo('test', '0.1.0', 'raw.githubusercontent.com')

Next we will hook into the options loading to set default options if nothing was passed.

class Plugin(APlugin):
    _INFO = PluginInfo('test', '0.1.0', 'raw.githubusercontent.com')

def _load_default_options(self):
    super(Plugin, self)._load_default_options()
    if 'username' not in self._options:
        self._options['username'] = ''

Now as the username is in the options for sure we can just get it safely.

class Plugin(APlugin):
    _INFO = PluginInfo('test', '0.1.0', 'raw.githubusercontent.com')

def __init__(self, settings: Settings, options: Dict[str, Any] = None):
    super().__init__(settings, options)
    self._username: str = self._options['username']

def _load_default_options(self):
    super(Plugin, self)._load_default_options()
    if 'username' not in self._options:
        self._options['username'] = ''

To get an overall update time for the complete data set, create the _create_last_update_time method. If the time generated by this function is older compared to the savestate it will run the individual link generating, if not it will skip the rest.

class Plugin(APlugin):
    _INFO = PluginInfo('test', '0.1.0', 'raw.githubusercontent.com')

def __init__(self, settings: Settings, options: Dict[str, Any] = None):
    super().__init__(settings, options)
    self._username: str = self._options['username']

def _create_last_update_time(self) -> datetime:
    self.download_as_file('/IceflowRE/unidown/main/tests/last_update_time.txt', self._temp_dir, 'last_update_time.txt')
    with self._temp_dir.joinpath('last_update_time.txt').open(encoding='utf8') as reader:
        return datetime.strptime(reader.read(), LinkItem.TIME_FORMAT)

def _load_default_options(self):
    super(Plugin, self)._load_default_options()
    if 'username' not in self._options:
        self._options['username'] = ''

So if the generate update time is newer, the application continues to _create_download_links. In the example we just download a json with preconfigured values and create the LinkItemDict from it.

class Plugin(APlugin):
    _INFO = PluginInfo('test', '0.1.0', 'raw.githubusercontent.com')

def __init__(self, settings: Settings, options: Dict[str, Any] = None):
    super().__init__(settings, options)
    self._username: str = self._options['username']

def _create_last_update_time(self) -> datetime:
    self.download_as_file('/IceflowRE/unidown/main/tests/last_update_time.txt', self._temp_dir, 'last_update_time.txt')
    with self._temp_dir.joinpath('last_update_time.txt').open(encoding='utf8') as reader:
        return datetime.strptime(reader.read(), LinkItem.TIME_FORMAT)

def _create_download_data(self) -> LinkItemDict:
    self.download_as_file('/IceflowRE/unidown/main/tests/item_dict.json', self._temp_dir, 'item_dict.json')
    with self._temp_dir.joinpath('item_dict.json').open(encoding='utf8') as reader:
        data = json.loads(reader.read())
    result = LinkItemDict()
    for link, item in data.items():
        result[link] = LinkItem(item['name'], datetime.strptime(item['time'], LinkItem.TIME_FORMAT))
    return result

def _load_default_options(self):
    super(Plugin, self)._load_default_options()
    if 'username' not in self._options:
        self._options['username'] = ''

Final step includes that we create a python package out of the generate file.

unidown_test/
├── __init__.py
├── plugin.py  «the plugin file»
└── setup.py

The most important part is the entry point, otherwise unidown cannot recognize it.

'unidown.plugin': "test = unidown_test.plugin:Plugin" Where as test is the name of the plugin and after that the import path the plugin class.

setup.py
from setuptools import find_packages, setup

setup(
    name="unidown_test",
    version="0.1.0",
    description="Test plugin for unidown.",
    author="Iceflower S",
    author_email="iceflower@gmx.de",
    license='MIT',
    packages=find_packages(include=['unidown_test', 'unidown_test.*']),
    python_requires='>=3.7',
    install_requires=[
        'unidown',
    ],
    entry_points={
        'unidown.plugin': "test = unidown_test.plugin:Plugin"
    },
)

Advanced

The advanced part will show how to use a custom savestate.

First of all we have to create the custom savestate. It is required to subclass from unidown.plugin.savestate.SaveState.

from unidown.plugin.savestate import SaveState

class MySaveState(SaveState):

In our example we want to permanently store a username.

class MySaveState(SaveState):

    def __init__(self, plugin_info: PluginInfo, last_update: datetime, link_items: LinkItemDict, username: str = ''):
        super().__init__(plugin_info, last_update, link_items)
        self.username: str = username

Furthermore to get it loaded from a custom json file we have to override from_json, the same goes with saving with to_json.

class MySaveState(SaveState):

    def __init__(self, plugin_info: PluginInfo, last_update: datetime, link_items: LinkItemDict, username: str = ''):
        super().__init__(plugin_info, last_update, link_items)
        self.username: str = username

    @classmethod
    def from_json(cls, data: dict) -> SaveState:
        savestate = super(MySaveState, cls).from_json(data)
        if 'username' in data:
            savestate.username = data['username']
        return savestate

    def to_json(self) -> dict:
        data = super().to_json()
        data['username'] = self.username
        return data

Additionally it’s required to provide an own upgrade method, in case the format changes and as the super method may erase your own set variables while upgrading.

class MySaveState(SaveState):

    def __init__(self, plugin_info: PluginInfo, last_update: datetime, link_items: LinkItemDict, username: str = ''):
        super().__init__(plugin_info, last_update, link_items)
        self.username: str = username

    @classmethod
    def from_json(cls, data: dict) -> SaveState:
        savestate = super(MySaveState, cls).from_json(data)
        if 'username' in data:
            savestate.username = data['username']
        return savestate

    def to_json(self) -> dict:
        data = super().to_json()
        data['username'] = self.username
            return data

    def upgrade(self) -> SaveState:
        new_savestate = super(MySaveState, self).upgrade()
        new_savestate.username = self.username
        return new_savestate

The next step is to register your own custom savestate in the plugin, so it will be used, by setting _SAVESTATE_CLS.

from unidown_test.savestate import MySaveState

class Plugin(APlugin):
    _INFO = PluginInfo('test', '0.1.0', 'raw.githubusercontent.com')
    _SAVESTATE_CLS = MySaveState

To get the username loaded into your plugin, after the savestate was loaded override load_savestate.

from unidown_test.savestate import MySaveState

class Plugin(APlugin):
    _INFO = PluginInfo('test', '0.1.0', 'raw.githubusercontent.com')
    _SAVESTATE_CLS = MySaveState

# ... all other stuff in between

def load_savestate(self):
    super(Plugin, self).load_savestate()
    # do not override set username by options
    if self._username == '':
        self._username = self.savestate.username

The last step is to hook into updating the savestate. To get the current username saved into a new savestate.

from unidown_test.savestate import MySaveState

class Plugin(APlugin):
    _INFO = PluginInfo('test', '0.1.0', 'raw.githubusercontent.com')
    _SAVESTATE_CLS = MySaveState

# ... all other stuff in between

def load_savestate(self):
    super(Plugin, self).load_savestate()
    # do not override set username by options
    if self._username == '':
        self._username = self.savestate.username

def update_savestate(self, new_items: LinkItemDict):
    super(Plugin, self).update_savestate(new_items)
    self._savestate.username = self._username