Major notification update to add initial support for apprise URLs.
This commit is contained in:
parent
c1763f9e3b
commit
112da01105
15 changed files with 415 additions and 32 deletions
61
app/library/BackgroundWorker.py
Normal file
61
app/library/BackgroundWorker.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import asyncio
|
||||
import inspect
|
||||
import logging
|
||||
import threading
|
||||
from queue import Empty, Queue
|
||||
|
||||
from .Singleton import Singleton
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BackgroundWorker(metaclass=Singleton):
|
||||
_instance = None
|
||||
"""The instance of the Notification class."""
|
||||
|
||||
def __init__(self):
|
||||
self.queue = Queue()
|
||||
self.running = True
|
||||
self.thread = threading.Thread(target=self._run, daemon=True)
|
||||
self.thread.start()
|
||||
|
||||
@staticmethod
|
||||
def get_instance() -> "BackgroundWorker":
|
||||
if BackgroundWorker._instance is None:
|
||||
BackgroundWorker._instance = BackgroundWorker()
|
||||
|
||||
return BackgroundWorker._instance
|
||||
|
||||
def _run(self):
|
||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
# Start loop in a background thread
|
||||
def _loop_runner():
|
||||
try:
|
||||
loop.run_forever()
|
||||
except Exception as e:
|
||||
LOG.exception("Loop error: %s", e)
|
||||
|
||||
threading.Thread(target=_loop_runner, daemon=True).start()
|
||||
|
||||
while self.running:
|
||||
try:
|
||||
fn, args, kwargs = self.queue.get(timeout=1)
|
||||
try:
|
||||
LOG.debug("Running background task: %s", fn.__name__)
|
||||
result = fn(*args, **kwargs)
|
||||
if inspect.iscoroutine(result):
|
||||
loop.call_soon_threadsafe(loop.create_task, result)
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
LOG.error(f"Error in background task: {fn.__name__}")
|
||||
except Empty:
|
||||
continue
|
||||
|
||||
def submit(self, fn, *args, **kwargs):
|
||||
self.queue.put((fn, args, kwargs))
|
||||
|
||||
def shutdown(self):
|
||||
self.running = False
|
||||
self.thread.join()
|
||||
|
|
@ -491,7 +491,12 @@ class Download:
|
|||
|
||||
if "error" == self.info.status and "error" in status:
|
||||
self.info.error = status.get("error")
|
||||
await self._notify.emit(Events.ERROR, data={"message": self.info.error, "data": self.info})
|
||||
await self._notify.emit(
|
||||
Events.ERROR,
|
||||
data={"message": self.info.error, "data": self.info},
|
||||
title="Download Error",
|
||||
message=f"Error in download task '{self.info.title}': {self.info.error}",
|
||||
)
|
||||
|
||||
if "downloaded_bytes" in status and status.get("downloaded_bytes") > 0:
|
||||
self.info.downloaded_bytes = status.get("downloaded_bytes")
|
||||
|
|
|
|||
|
|
@ -461,7 +461,12 @@ class DownloadQueue(metaclass=Singleton):
|
|||
NotifyEvent = Events.COMPLETED
|
||||
dlInfo.info.status = "not_live"
|
||||
dlInfo.info.msg = f"{'Premiere video' if is_premiere else 'Stream' } is not available yet." + text_logs
|
||||
await self._notify.emit(Events.LOG_INFO, data=event_info(dlInfo.info.msg, {"lowPriority": True}))
|
||||
await self._notify.emit(
|
||||
Events.LOG_INFO,
|
||||
data=event_info(dlInfo.info.msg, {"lowPriority": True}),
|
||||
title="Premiere video" if is_premiere else "Live Stream",
|
||||
message=f"Item '{dlInfo.info.title}' is not available yet. {dlInfo.info.msg}",
|
||||
)
|
||||
itemDownload: Download = self.done.put(dlInfo)
|
||||
elif len(entry.get("formats", [])) < 1:
|
||||
availability: str = entry.get("availability", "public")
|
||||
|
|
@ -473,7 +478,12 @@ class DownloadQueue(metaclass=Singleton):
|
|||
dlInfo.info.status = "error"
|
||||
itemDownload = self.done.put(dlInfo)
|
||||
NotifyEvent = Events.COMPLETED
|
||||
await self._notify.emit(Events.LOG_WARNING, data=event_warning(f"No formats found for '{dl.title}'."))
|
||||
await self._notify.emit(
|
||||
Events.LOG_WARNING,
|
||||
data=event_warning(f"No formats found for '{dl.title}'."),
|
||||
title="No Formats Found",
|
||||
message=f"No formats found for '{dl.title}'. {dlInfo.info.error}",
|
||||
)
|
||||
elif is_premiere and self.config.prevent_live_premiere:
|
||||
dlInfo.info.error = "Premiering right now."
|
||||
|
||||
|
|
@ -507,6 +517,8 @@ class DownloadQueue(metaclass=Singleton):
|
|||
await self._notify.emit(
|
||||
Events.LOG_INFO,
|
||||
data=event_info(f"'{dl.title}' is {dlInfo.info.error}.", {"lowPriority": True}),
|
||||
title="Item Not Live",
|
||||
message=f"Item '{dl.title}' is not live. {dlInfo.info.error}",
|
||||
)
|
||||
else:
|
||||
NotifyEvent = Events.ADDED
|
||||
|
|
@ -516,7 +528,12 @@ class DownloadQueue(metaclass=Singleton):
|
|||
else:
|
||||
LOG.debug(f"Item {itemDownload.info.name()} is not set to auto-start.")
|
||||
|
||||
await self._notify.emit(NotifyEvent, data=itemDownload.info.serialize())
|
||||
await self._notify.emit(
|
||||
NotifyEvent,
|
||||
data=itemDownload.info.serialize(),
|
||||
title="Item Added" if Events.ADDED == NotifyEvent else None,
|
||||
message=f"Added '{itemDownload.info.title}'." if NotifyEvent == Events.ADDED else None,
|
||||
)
|
||||
|
||||
return {"status": "ok"}
|
||||
except Exception as e:
|
||||
|
|
@ -721,7 +738,12 @@ class DownloadQueue(metaclass=Singleton):
|
|||
await item.close()
|
||||
LOG.debug(f"Deleting from queue {item_ref}")
|
||||
self.queue.delete(id)
|
||||
await self._notify.emit(Events.CANCELLED, data=item.info.serialize())
|
||||
await self._notify.emit(
|
||||
Events.CANCELLED,
|
||||
data=item.info.serialize(),
|
||||
title="Download Cancelled",
|
||||
message=f"Download '{item.info.title}' has been cancelled.",
|
||||
)
|
||||
item.info.status = "cancelled"
|
||||
# item.info.error = "Cancelled by user."
|
||||
self.done.put(item)
|
||||
|
|
@ -793,7 +815,12 @@ class DownloadQueue(metaclass=Singleton):
|
|||
LOG.error(f"Unable to remove '{itemRef}' local file '{filename}'. {e!s}")
|
||||
|
||||
self.done.delete(id)
|
||||
await self._notify.emit(Events.CLEARED, data=item.info.serialize())
|
||||
await self._notify.emit(
|
||||
Events.CLEARED,
|
||||
data=item.info.serialize(),
|
||||
title="Download Cleared",
|
||||
message=f"Cleared download '{item.info.title}' from history.",
|
||||
)
|
||||
|
||||
msg = f"Deleted completed download '{itemRef}'."
|
||||
if removed_files > 0:
|
||||
|
|
@ -905,12 +932,24 @@ class DownloadQueue(metaclass=Singleton):
|
|||
self.queue.delete(key=id)
|
||||
|
||||
if entry.is_cancelled() is True:
|
||||
await self._notify.emit(Events.CANCELLED, data=entry.info.serialize())
|
||||
await self._notify.emit(
|
||||
Events.CANCELLED,
|
||||
data=entry.info.serialize(),
|
||||
title="Download Cancelled",
|
||||
message=f"Download '{entry.info.title}' has been cancelled.",
|
||||
)
|
||||
entry.info.status = "cancelled"
|
||||
# entry.info.error = "Cancelled by user."
|
||||
|
||||
self.done.put(value=entry)
|
||||
await self._notify.emit(Events.COMPLETED, data=entry.info.serialize())
|
||||
await self._notify.emit(
|
||||
Events.COMPLETED,
|
||||
data=entry.info.serialize(),
|
||||
title="Download Completed" if entry.info.status == "finished" else None,
|
||||
message=f"Download '{entry.info.title}' has been completed."
|
||||
if entry.info.status == "finished"
|
||||
else None,
|
||||
)
|
||||
else:
|
||||
LOG.warning(f"Download '{id}' not found in queue.")
|
||||
|
||||
|
|
|
|||
|
|
@ -203,6 +203,12 @@ class Event:
|
|||
event: str
|
||||
"""The event that was emitted."""
|
||||
|
||||
title: str | None = None
|
||||
"""The title of the event, if any."""
|
||||
|
||||
message: str | None = None
|
||||
"""The message of the event, if any."""
|
||||
|
||||
data: any
|
||||
"""The data that was passed to the event."""
|
||||
|
||||
|
|
@ -214,10 +220,17 @@ class Event:
|
|||
dict: The serialized event.
|
||||
|
||||
"""
|
||||
return {"id": self.id, "created_at": self.created_at, "event": self.event, "data": self.data}
|
||||
return {
|
||||
"id": self.id,
|
||||
"created_at": self.created_at,
|
||||
"event": self.event,
|
||||
"title": self.title,
|
||||
"message": self.message,
|
||||
"data": self.data,
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
return f"Event(id={self.id}, created_at={self.created_at}, event={self.event}, data={self.data})"
|
||||
return f"Event(id={self.id}, created_at={self.created_at}, event={self.event}, title={self.title}, message={self.message} data={self.data})"
|
||||
|
||||
def datatype(self) -> str:
|
||||
"""
|
||||
|
|
@ -230,7 +243,7 @@ class Event:
|
|||
return type(self.data).__name__
|
||||
|
||||
def __str__(self):
|
||||
return f"Event(id={self.id}, created_at={self.created_at}, event={self.event})"
|
||||
return f"Event(id={self.id}, created_at={self.created_at}, event={self.event}, title={self.title}, message={self.message})"
|
||||
|
||||
|
||||
class EventListener:
|
||||
|
|
@ -414,13 +427,17 @@ class EventBus(metaclass=Singleton):
|
|||
fut = asyncio.run_coroutine_threadsafe(emit_all(), loop)
|
||||
return fut.result() if wait else fut
|
||||
|
||||
async def emit(self, event: str, data: Any, **kwargs) -> Awaitable:
|
||||
async def emit(
|
||||
self, event: str, data: Any, title: str | None = None, message: str | None = None, **kwargs
|
||||
) -> Awaitable:
|
||||
"""
|
||||
Emit an event.
|
||||
|
||||
Args:
|
||||
event (str): The event to emit.
|
||||
data (Any): The data to pass to the event.
|
||||
title (str | None): The title of the event, if any.
|
||||
message (str | None): The message of the event, if any.
|
||||
**kwargs: The keyword arguments to pass to the event.
|
||||
|
||||
Returns:
|
||||
|
|
@ -430,7 +447,7 @@ class EventBus(metaclass=Singleton):
|
|||
if event not in self._listeners:
|
||||
return []
|
||||
|
||||
ev = Event(event=event, data=data)
|
||||
ev = Event(event=event, data=data, title=title, message=message)
|
||||
|
||||
if self.debug or event not in Events.only_debug():
|
||||
LOG.debug(f"Emitting event '{ev.id}: {ev.event}'.", extra={"data": data})
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import httpx
|
|||
from aiohttp import web
|
||||
|
||||
from .ag_utils import ag
|
||||
from .BackgroundWorker import BackgroundWorker
|
||||
from .config import Config
|
||||
from .encoder import Encoder
|
||||
from .Events import Event, EventBus, Events
|
||||
|
|
@ -129,6 +130,7 @@ class Notification(metaclass=Singleton):
|
|||
client: httpx.AsyncClient | None = None,
|
||||
encoder: Encoder | None = None,
|
||||
config: Config | None = None,
|
||||
background_worker: BackgroundWorker | None = None,
|
||||
):
|
||||
Notification._instance = self
|
||||
config: Config = config or Config.get_instance()
|
||||
|
|
@ -138,6 +140,7 @@ class Notification(metaclass=Singleton):
|
|||
self._client: httpx.AsyncClient = client or httpx.AsyncClient()
|
||||
self._encoder: Encoder = encoder or Encoder()
|
||||
self._version = config.app_version
|
||||
self._offload: BackgroundWorker = background_worker or BackgroundWorker.get_instance()
|
||||
|
||||
if self._file.exists() and "600" != self._file.stat().st_mode:
|
||||
try:
|
||||
|
|
@ -335,17 +338,54 @@ class Notification(metaclass=Singleton):
|
|||
|
||||
tasks = []
|
||||
|
||||
apprise_targets: list[Target] = []
|
||||
|
||||
for target in self._targets:
|
||||
if len(target.on) > 0 and ev.event not in target.on and "test" != ev.event:
|
||||
continue
|
||||
|
||||
tasks.append(self._send(target, ev))
|
||||
if not target.request.url.startswith("http"):
|
||||
apprise_targets.append(target)
|
||||
else:
|
||||
tasks.append(self._send(target, ev))
|
||||
|
||||
if len(apprise_targets) > 0:
|
||||
tasks.append(self._apprise(apprise_targets, ev))
|
||||
|
||||
if wait:
|
||||
return await asyncio.gather(*tasks)
|
||||
|
||||
return tasks
|
||||
|
||||
async def _apprise(self, target: list[Target], ev: Event) -> dict:
|
||||
if not target or not isinstance(target, list):
|
||||
return {}
|
||||
|
||||
import apprise
|
||||
|
||||
try:
|
||||
notify = apprise.Apprise()
|
||||
apr_config = Path(Config.get_instance().apprise_config)
|
||||
if apr_config.exists():
|
||||
apprise_config = notify.AppriseConfig()
|
||||
apprise_config.add(apr_config)
|
||||
notify.add(apprise_config)
|
||||
|
||||
for t in target:
|
||||
notify.add(t.request.url)
|
||||
|
||||
notify.notify(
|
||||
body=ev.message or json.dumps(ev.serialize(), sort_keys=False, ensure_ascii=False),
|
||||
title=ev.title or f"YTPTube Event: {ev.event}",
|
||||
notify_type=ev.event,
|
||||
)
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
LOG.error(f"Error sending Apprise notification: {e!s}")
|
||||
return {"error": str(e), "event": ev.event, "id": ev.id, "targets": [t.name for t in target]}
|
||||
|
||||
return {}
|
||||
|
||||
async def _send(self, target: Target, ev: Event) -> dict:
|
||||
try:
|
||||
LOG.info(f"Sending Notification event '{ev.event}: {ev.id}' to '{target.name}'.")
|
||||
|
|
@ -397,11 +437,13 @@ class Notification(metaclass=Singleton):
|
|||
LOG.error(f"Error sending Notification event '{ev.event}: {ev.id}' to '{target.name}'. '{err_msg!s}'.")
|
||||
return {"url": target.request.url, "status": 500, "text": str(ev)}
|
||||
|
||||
def emit(self, e: Event, _, **kwargs): # noqa: ARG002
|
||||
def emit(self, e: Event, _, **__):
|
||||
if len(self._targets) < 1 or not NotificationEvents.is_valid(e.event):
|
||||
return asyncio.sleep(0)
|
||||
return self.noop()
|
||||
|
||||
return self.send(e)
|
||||
self._offload.submit(self.send, e)
|
||||
|
||||
return self.noop()
|
||||
|
||||
def _deep_unpack(self, data: dict) -> dict:
|
||||
for k, v in data.items():
|
||||
|
|
@ -415,3 +457,6 @@ class Notification(metaclass=Singleton):
|
|||
data[k] = v.serialize()
|
||||
|
||||
return data
|
||||
|
||||
async def noop(self) -> None:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -325,6 +325,8 @@ class Tasks(metaclass=Singleton):
|
|||
"template": template,
|
||||
"cli": cli,
|
||||
},
|
||||
title=f"Task '{task.name}' started",
|
||||
message=f"Task '{task.name}' started at '{timeNow}'",
|
||||
id=task.id,
|
||||
)
|
||||
|
||||
|
|
@ -338,11 +340,17 @@ class Tasks(metaclass=Singleton):
|
|||
data=success(
|
||||
f"Task '{task.name}' completed in '{ended - started:.2f}' seconds.", data={"lowPriority": True}
|
||||
),
|
||||
title=f"Task '{task.name}' completed",
|
||||
message=f"Task '{task.name}' completed at '{timeNow}'",
|
||||
id=task.id,
|
||||
)
|
||||
except Exception as e:
|
||||
LOG.error(f"Failed to execute '{task.name}' at '{timeNow}'. '{e!s}'.")
|
||||
await self._notify.emit(
|
||||
Events.ERROR, data=error(f"Failed to execute '{task.name}' at '{timeNow}'. '{e!s}'.")
|
||||
Events.ERROR,
|
||||
data=error(f"Failed to execute '{task.name}' at '{timeNow}'. '{e!s}'."),
|
||||
title=f"Task '{task.name}' failed",
|
||||
message=str(e),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ def extract_info(
|
|||
archive_id = f".{idDict['id']}" if idDict.get("id") else None
|
||||
|
||||
log_wrapper.add_target(
|
||||
target=logging.getLogger(f"yt-dlp{archive_id}"),
|
||||
target=logging.getLogger(f"yt-dlp{archive_id if archive_id else '.extract_info'}"),
|
||||
level=logging.DEBUG if debug else logging.WARNING,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -103,6 +103,9 @@ class Config:
|
|||
manual_archive: str = "{config_path}/archive.manual.log"
|
||||
"""The path to the manual archive file."""
|
||||
|
||||
apprise_config: str = "{config_path}/apprise.yml"
|
||||
"""The path to the Apprise configuration file."""
|
||||
|
||||
ui_update_title: bool = True
|
||||
"""Update the title of the browser tab with the current status."""
|
||||
|
||||
|
|
@ -454,8 +457,9 @@ class Config:
|
|||
|
||||
self.started = time.time()
|
||||
|
||||
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||
logging.getLogger("httpcore").setLevel(logging.INFO)
|
||||
for _tool in ("httpx", "urllib3.connectionpool", "apprise"):
|
||||
logging.getLogger(_tool).setLevel(logging.WARNING)
|
||||
|
||||
# check env
|
||||
if self.app_env not in ("production", "development"):
|
||||
|
|
|
|||
10
app/main.py
10
app/main.py
|
|
@ -16,6 +16,7 @@ import caribou
|
|||
import magic
|
||||
from aiohttp import web
|
||||
|
||||
from app.library.BackgroundWorker import BackgroundWorker
|
||||
from app.library.conditions import Conditions
|
||||
from app.library.config import Config
|
||||
from app.library.DownloadQueue import DownloadQueue
|
||||
|
|
@ -39,8 +40,10 @@ class Main:
|
|||
self._config = Config.get_instance(is_native=is_native)
|
||||
self._app = web.Application()
|
||||
self._app.on_shutdown.append(self.on_shutdown)
|
||||
self._background_worker = BackgroundWorker()
|
||||
|
||||
Services.get_instance().add("app", self._app)
|
||||
Services.get_instance().add("background_worker", self._background_worker)
|
||||
|
||||
self._check_folders()
|
||||
|
||||
|
|
@ -94,7 +97,12 @@ class Main:
|
|||
raise
|
||||
|
||||
async def on_shutdown(self, _: web.Application):
|
||||
await EventBus.get_instance().emit(Events.SHUTDOWN, data={"app": self._app})
|
||||
await EventBus.get_instance().emit(
|
||||
Events.SHUTDOWN,
|
||||
data={"app": self._app},
|
||||
title="Application Shutdown",
|
||||
message="The application is shutting down.",
|
||||
)
|
||||
|
||||
def start(self, host: str | None = None, port: int | None = None, cb=None):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -106,6 +106,6 @@ async def notification_test(encoder: Encoder, notify: EventBus) -> Response:
|
|||
"""
|
||||
data = message("test", "This is a test notification.")
|
||||
|
||||
await notify.emit(Events.TEST, data=data)
|
||||
await notify.emit(Events.TEST, data=data, title="Test Notification", message="This is a test notification.")
|
||||
|
||||
return web.json_response(data=data, status=web.HTTPOk.status_code, dumps=encoder.encode)
|
||||
|
|
|
|||
|
|
@ -19,13 +19,23 @@ LOG: logging.Logger = logging.getLogger(__name__)
|
|||
@route(RouteType.SOCKET, "pause", "pause_downloads")
|
||||
async def pause(notify: EventBus, queue: DownloadQueue):
|
||||
queue.pause()
|
||||
await notify.emit(Events.PAUSED, data={"paused": True, "at": time.time()})
|
||||
await notify.emit(
|
||||
Events.PAUSED,
|
||||
data={"paused": True, "at": time.time()},
|
||||
title="Downloads Paused",
|
||||
message="Download pool has been paused.",
|
||||
)
|
||||
|
||||
|
||||
@route(RouteType.SOCKET, "resume", "resume_downloads")
|
||||
async def resume(notify: EventBus, queue: DownloadQueue):
|
||||
queue.resume()
|
||||
await notify.emit(Events.PAUSED, data={"paused": False, "at": time.time()})
|
||||
await notify.emit(
|
||||
Events.PAUSED,
|
||||
data={"paused": False, "at": time.time()},
|
||||
title="Downloads Resumed",
|
||||
message="Download pool has been resumed.",
|
||||
)
|
||||
|
||||
|
||||
@route(RouteType.SOCKET, "add_url", "add_url")
|
||||
|
|
@ -76,7 +86,9 @@ async def item_delete(queue: DownloadQueue, notify: EventBus, sid: str, data: di
|
|||
status = await queue.clear([id], remove_file=bool(data.get("remove_file", False)))
|
||||
status.update({"identifier": id})
|
||||
|
||||
await notify.emit(Events.ITEM_DELETE, data=status)
|
||||
await notify.emit(
|
||||
Events.ITEM_DELETE, data=status, title="Item Deleted", message=f"Item with ID '{id}' has been deleted."
|
||||
)
|
||||
|
||||
|
||||
@route(RouteType.SOCKET, "archive_item", "archive_item")
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ dependencies = [
|
|||
"dateparser>=1.2.1",
|
||||
"defusedxml>=0.7.1",
|
||||
"zipstream-ng>=1.8.0",
|
||||
"apprise>=1.9.3",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
|
|
|
|||
|
|
@ -83,12 +83,13 @@
|
|||
</div>
|
||||
<span class="help">
|
||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||
<span>The URL to send the notification to.</span>
|
||||
<span class="is-bold">The URL to send the notification to. It can be regular http/https endpoint.
|
||||
or <NuxtLink target="blank" href="https://github.com/caronc/apprise?tab=readme-ov-file#readme">Apprise</NuxtLink> URL.</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="column is-6-tablet is-12-mobile" v-if="!isApprise">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="method">
|
||||
Request method
|
||||
|
|
@ -113,7 +114,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="column is-6-tablet is-12-mobile" v-if="!isApprise">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="type">
|
||||
Request Type
|
||||
|
|
@ -138,7 +139,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="column is-12-mobile" :class="{ 'is-6-tablet': !isApprise, 'is-12': isApprise }">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="on">
|
||||
Select Events
|
||||
|
|
@ -167,7 +168,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-6-tablet is-12-mobile">
|
||||
<div class="column is-6-tablet is-12-mobile" v-if="!isApprise">
|
||||
<div class="field">
|
||||
<label class="label is-inline" for="data_key">
|
||||
Data field
|
||||
|
|
@ -187,7 +188,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-12">
|
||||
<div class="column is-12" v-if="!isApprise">
|
||||
<div class="field">
|
||||
<label class="label is-inline is-unselectable">
|
||||
Optional Headers - <button type="button" class="has-text-link"
|
||||
|
|
@ -382,4 +383,6 @@ const importItem = async () => {
|
|||
toast.error(`Failed to import task. ${e.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
const isApprise = computed(() => form.request.url && !form.request.url.startsWith('http'))
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -130,10 +130,12 @@ div.is-centered {
|
|||
The other keys <code>id</code>, <code>event</code> and <code>created_at</code> will be sent as they are.
|
||||
</li>
|
||||
<li>We also send two special headers <code>X-Event-ID</code> and <code>X-Event</code> with the request.</li>
|
||||
<li>Support for <code>Apprise URLs</code> is in beta and subject to many changes to come, currently the
|
||||
message field fallback to JSON encoded string of the event if there there is custom message set by us for
|
||||
that particular event.</li>
|
||||
</ul>
|
||||
</Message>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
178
uv.lock
178
uv.lock
|
|
@ -129,6 +129,23 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "apprise"
|
||||
version = "1.9.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "click" },
|
||||
{ name = "markdown" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "requests" },
|
||||
{ name = "requests-oauthlib" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f8/1e/fe19c88c3e1ff96f4ea757bae9f6350060ac28be523507053347aa5d67db/apprise-1.9.3.tar.gz", hash = "sha256:f583667ea35b8899cd46318c6cb26f0faf6a4605b119174c2523a012590c65a6", size = 1795515 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/88/25a6459e3a8ec9ceb7af2a103e0b2a3efbc3ffd16cb15ea024b0008c678d/apprise-1.9.3-py3-none-any.whl", hash = "sha256:e9b5abb73244c21a30ee493860f8d4ae80665d225b1b436179d48db4f6fc5b9e", size = 1352539 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-timeout"
|
||||
version = "5.0.1"
|
||||
|
|
@ -299,6 +316,66 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "charset-normalizer"
|
||||
version = "3.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794 },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846 },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350 },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657 },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571 },
|
||||
{ url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030 },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015 },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790 },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924 },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567 },
|
||||
{ url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408 },
|
||||
{ url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537 },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565 },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622 },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435 },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442 },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454 },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174 },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166 },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641 },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.2.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/60/6c/8ca2efa64cf75a977a0d7fac081354553ebe483345c734fb6b6515d96bbc/click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", size = 286342 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/85/32/10bb5764d90a8eee674e9dc6f4db6a0ab47c8c4d0d83c27f7c39ac415a4d/click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b", size = 102215 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clr-loader"
|
||||
version = "0.2.7.post0"
|
||||
|
|
@ -311,6 +388,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/9c/c0/06e64a54bced4e8b885c1e7ec03ee1869e52acf69e87da40f92391a214ad/clr_loader-0.2.7.post0-py3-none-any.whl", hash = "sha256:e0b9fcc107d48347a4311a28ffe3ae78c4968edb216ffb6564cb03f7ace0bb47", size = 50649 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coloredlogs"
|
||||
version = "15.0.1"
|
||||
|
|
@ -545,6 +631,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/d1/5d/c059c180c84f7962db0aeae7c3b9303ed1d73d76f2bfbc32bc231c8be314/macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c", size = 38094 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markdown"
|
||||
version = "3.8.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/c2/4ab49206c17f75cb08d6311171f2d65798988db4360c4d1485bd0eedd67c/markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45", size = 362071 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/96/2b/34cc11786bc00d0f04d0f5fdc3a2b1ae0b6239eef72d3d345805f9ad92a1/markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24", size = 106827 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multidict"
|
||||
version = "6.5.1"
|
||||
|
|
@ -627,6 +722,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/b0/7a/620f945b96be1f6ee357d211d5bf74ab1b7fe72a9f1525aafbfe3aee6875/mutagen-1.47.0-py3-none-any.whl", hash = "sha256:edd96f50c5907a9539d8e5bba7245f62c9f520aef333d13392a79a4f70aca719", size = 194391 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oauthlib"
|
||||
version = "3.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/0b/5f/19930f824ffeb0ad4372da4812c50edbd1434f678c90c2733e1188edfc63/oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9", size = 185918 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "25.0"
|
||||
|
|
@ -1132,6 +1236,41 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qtpy"
|
||||
version = "2.4.3"
|
||||
|
|
@ -1197,6 +1336,34 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests"
|
||||
version = "2.32.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "urllib3" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e1/0a/929373653770d8a0d7ea76c37de6e41f11eb07559b103b1c02cafb3f7cf8/requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422", size = 135258 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "requests-oauthlib"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "oauthlib" },
|
||||
{ name = "requests" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "80.9.0"
|
||||
|
|
@ -1275,6 +1442,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wsproto"
|
||||
version = "1.2.0"
|
||||
|
|
@ -1386,6 +1562,7 @@ dependencies = [
|
|||
{ name = "aiocron" },
|
||||
{ name = "aiohttp" },
|
||||
{ name = "anyio" },
|
||||
{ name = "apprise" },
|
||||
{ name = "async-timeout" },
|
||||
{ name = "brotli" },
|
||||
{ name = "brotlicffi" },
|
||||
|
|
@ -1425,6 +1602,7 @@ requires-dist = [
|
|||
{ name = "aiocron", specifier = ">=1.8" },
|
||||
{ name = "aiohttp", specifier = ">=3.9.3" },
|
||||
{ name = "anyio" },
|
||||
{ name = "apprise", specifier = ">=1.9.3" },
|
||||
{ name = "async-timeout" },
|
||||
{ name = "brotli" },
|
||||
{ name = "brotlicffi" },
|
||||
|
|
|
|||
Loading…
Reference in a new issue