Merge pull request #222 from arabcoders/dev

Minor README & EventBus update
This commit is contained in:
Abdulmohsen 2025-03-23 00:55:16 +03:00 committed by GitHub
commit 28d902b2a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 41 additions and 19 deletions

View file

@ -4,31 +4,30 @@
Web GUI for [yt-dlp](https://github.com/yt-dlp/yt-dlp) with playlist & channel support.
YTPTube started as a fork of [meTube](https://github.com/alexta69/metube), Since then it went under heavy changes, and it supports many new features.
[![Short screenshot](https://raw.githubusercontent.com/ArabCoders/ytptube/master/sc_short.png)](https://raw.githubusercontent.com/ArabCoders/ytptube/master/sc_full.png)
# YTPTube Features.
* Multi-downloads support.
* Handle live streams.
* Schedule channels or playlists to be downloaded automatically at a specified time.
* Can Handle live streams.
* Scheduler to queue channels or playlists to be downloaded automatically at a specified time.
* Send notification to targets based on selected events.
* Support per link `yt-dlp JSON config or cli options`, `cookies` & `output format`.
* Queue multiple URLs separated by comma.
* A built in video player that can play any video file regardless of the format. **With support for sidecar external subtitles**.
* New `POST /api/history` endpoint that allow one or multiple links to be sent at the same time.
* New `GET /api/history/add?url=http://..` endpoint that allow to add single item via GET request.
* Completely redesigned the frontend UI.
* Switched out of binary file storage in favor of SQLite.
* Modern frontend UI.
* SQLite as database backend.
* Basic Authentication support.
* Support for curl_cffi, see [yt-dlp documentation](https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#impersonation)
* Support for both advanced and basic mode for WebUI.
* Bundled tools in container: curl-cffi, ffmpeg, ffprobe, aria2, rtmpdump, mkvtoolsnix, mp4box.
[![Short screenshot](https://raw.githubusercontent.com/ArabCoders/ytptube/master/sc_short.png)](https://raw.githubusercontent.com/ArabCoders/ytptube/master/sc_full.png)
# Recommended basic `ytdlp.json` file settings
Your `ytdlp.json` config should include the following basic options for optimal working conditions.
Your `/config/ytdlp.json` config should include the following basic options for optimal working conditions.
```json
{
@ -291,6 +290,20 @@ Disables the `Information` button.
For API endpoints, please refer to the [API documentation](API.md). it's somewhat outdated, but it's a good starting point.
# How to autoload yt-dlp plugins?
Loading yt-dlp plugin in YTPTube is is quite simple, we already have everything setup for you. simply, create a folder
inside the `/config` directory named `yt-dlp` so, the path will be `/config/yt-dlp`. then follow
[yt-dlp plugins docs](https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#plugins) to know how to install the plugins.
Once you have installed the plugins, restart the container and the plugins will be auto-loaded on demand.
# The origin of the project.
The project first started as a fork [meTube](https://github.com/alexta69/metube), since then it has been completely
rewritten and redesigned. The original project was a great starting point, but it didn't align with my vision for the
project and what i wanted to achieve with it.
# Social contact
If you have short or quick questions, you are free to join my [discord server](https://discord.gg/G3GpVR8xpb) and ask

View file

@ -160,7 +160,7 @@ class Event:
id: str = field(default_factory=lambda: str(uuid.uuid4()), init=False)
"""The id of the event."""
created_at: str = field(default_factory=lambda: str(datetime.datetime.now(tz=datetime.timezone.utc).isoformat()))
created_at: str = field(default_factory=lambda: str(datetime.datetime.now(tz=datetime.UTC).isoformat()))
"""The time the event was created."""
event: str
@ -209,8 +209,8 @@ class EventListener:
async def handle(self, event: Event, **kwargs):
if self.is_coroutine:
return self.call_back(event, self.name, **kwargs)
else:
return asyncio.create_task(self.call_back(event, self.name, **kwargs))
return asyncio.create_task(self.call_back(event, self.name, **kwargs))
class EventBus(metaclass=Singleton):

View file

@ -2,8 +2,9 @@ import asyncio
import json
import logging
import os
from collections.abc import Awaitable
from dataclasses import dataclass, field
from datetime import UTC, datetime
from datetime import datetime
from typing import Any
import httpx
@ -11,7 +12,7 @@ from aiohttp import web
from .config import Config
from .encoder import Encoder
from .Events import EventBus, Event, Events
from .Events import Event, EventBus, Events
from .ItemDTO import ItemDTO
from .Singleton import Singleton
from .Utils import ag, validate_uuid
@ -323,7 +324,7 @@ class Notification(metaclass=Singleton):
return True
async def send(self, ev: Event) -> list[dict]:
async def send(self, ev: Event, wait: bool = True) -> list[dict] | Awaitable[list[dict]]:
if len(self._targets) < 1:
return []
@ -339,7 +340,10 @@ class Notification(metaclass=Singleton):
tasks.append(self._send(target, ev))
return await asyncio.gather(*tasks)
if wait:
return await asyncio.gather(*tasks)
return tasks
async def _send(self, target: Target, ev: Event) -> dict:
try:
@ -367,6 +371,7 @@ class Notification(metaclass=Singleton):
if "form" == target.request.type.lower():
reqBody["data"]["data"] = self._encoder.encode(reqBody["data"]["data"])
logging.getLogger("httpx").setLevel(logging.WARNING)
response = await self._client.request(**reqBody)
respData = {"url": target.request.url, "status": response.status_code, "text": response.text}
@ -384,11 +389,8 @@ class Notification(metaclass=Singleton):
return {"url": target.request.url, "status": 500, "text": str(ev)}
def emit(self, e: Event, _, **kwargs): # noqa: ARG002
if len(self._targets) < 1:
return []
if not NotificationEvents.is_valid(e.event):
return []
if len(self._targets) < 1 or not NotificationEvents.is_valid(e.event):
return asyncio.sleep(0)
return self.send(e)

View file

@ -345,6 +345,13 @@ const checkInfo = async () => {
}
}
// trim all fields in copy only if they are strings
for (const key in copy) {
if (typeof copy[key] === 'string') {
copy[key] = copy[key].trim()
}
}
emitter('submit', { reference: toRaw(props.reference), preset: toRaw(copy) });
}