ovdashboard/api/ovdashboard_api/dav_file.py

92 lines
2.4 KiB
Python
Raw Normal View History

2022-08-31 10:20:54 +00:00
import asyncio
import functools
2022-08-29 11:27:18 +00:00
import logging
from io import BytesIO
2022-08-31 01:29:44 +00:00
from threading import Lock
2022-08-31 08:42:17 +00:00
from typing import Optional
2022-08-29 11:27:18 +00:00
2022-08-31 01:29:44 +00:00
from apscheduler.schedulers.asyncio import AsyncIOScheduler
2022-08-31 08:42:17 +00:00
from webdav3.client import Resource
2022-08-29 11:27:18 +00:00
_logger = logging.getLogger(__name__)
2022-08-31 10:20:54 +00:00
def run_in_executor(f):
"""
Decorator to make blocking function call asyncio compatible
https://stackoverflow.com/questions/41063331/how-to-use-asyncio-with-existing-blocking-library/
"""
@functools.wraps(f)
def inner(*args, **kwargs):
loop = asyncio.get_running_loop()
2022-08-31 10:21:55 +00:00
return loop.run_in_executor(
None,
functools.partial(f, *args, **kwargs),
)
2022-08-31 10:20:54 +00:00
return inner
2022-08-29 11:27:18 +00:00
class DavFile:
2022-08-31 02:22:57 +00:00
__instances: Optional[list["DavFile"]] = None
2022-08-31 02:15:48 +00:00
__scheduler = None
2022-08-31 01:29:44 +00:00
2022-08-31 08:42:17 +00:00
def __init__(self, resource: Resource, refresh: bool = True) -> None:
self.__resource: Resource = resource
2022-08-29 11:27:18 +00:00
self.__buffer = BytesIO()
2022-08-31 01:29:44 +00:00
self.__lock = Lock()
# register
if DavFile.__instances is None:
DavFile.__instances = []
2022-08-31 08:42:17 +00:00
if refresh:
DavFile.__instances.append(self)
2022-08-29 11:27:18 +00:00
2022-08-31 10:20:54 +00:00
async def download(self) -> None:
@run_in_executor
def download_inner() -> None:
self.__resource.write_to(self.__buffer)
2022-08-31 01:29:44 +00:00
_logger.info(f"updating {self.__resource}")
2022-08-29 11:27:18 +00:00
with self.__lock:
self.__buffer.seek(0)
self.__buffer.truncate(0)
2022-08-31 10:20:54 +00:00
await download_inner()
2022-08-29 11:27:18 +00:00
2022-08-31 01:29:44 +00:00
@classmethod
2022-08-31 02:22:57 +00:00
def refresh(cls, refresh_interval: int = 60) -> None:
if cls.__scheduler is not None:
2022-08-31 02:15:48 +00:00
cls.__scheduler.reschedule_job(
job_id=cls.__name__,
trigger="interval",
seconds=refresh_interval,
)
return
2022-08-29 11:27:18 +00:00
2022-08-31 10:20:54 +00:00
async def tick() -> None:
2022-08-31 01:29:44 +00:00
for davfile in DavFile.__instances:
2022-08-31 10:20:54 +00:00
await davfile.download()
2022-08-29 11:27:18 +00:00
2022-08-31 02:15:48 +00:00
cls.__scheduler = AsyncIOScheduler()
cls.__scheduler.start()
cls.__scheduler.add_job(tick)
2022-08-31 02:22:57 +00:00
cls.__scheduler.add_job(
2022-08-31 02:15:48 +00:00
tick,
id=cls.__name__,
trigger="interval",
2022-08-31 01:29:44 +00:00
seconds=refresh_interval,
)
2022-08-29 11:27:18 +00:00
@property
def bytes(self) -> bytes:
with self.__lock:
self.__buffer.seek(0)
return self.__buffer.read()
def __str__(self) -> str:
return self.bytes.decode(encoding="utf-8")