Merge pull request #222 from arabcoders/dev
Minor README & EventBus update
This commit is contained in:
commit
28d902b2a9
4 changed files with 41 additions and 19 deletions
27
README.md
27
README.md
|
|
@ -4,31 +4,30 @@
|
||||||
|
|
||||||
Web GUI for [yt-dlp](https://github.com/yt-dlp/yt-dlp) with playlist & channel support.
|
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.
|
[](https://raw.githubusercontent.com/ArabCoders/ytptube/master/sc_full.png)
|
||||||
|
|
||||||
# YTPTube Features.
|
# YTPTube Features.
|
||||||
|
|
||||||
* Multi-downloads support.
|
* Multi-downloads support.
|
||||||
* Handle live streams.
|
* Can Handle live streams.
|
||||||
* Schedule channels or playlists to be downloaded automatically at a specified time.
|
* Scheduler to queue channels or playlists to be downloaded automatically at a specified time.
|
||||||
* Send notification to targets based on selected events.
|
* Send notification to targets based on selected events.
|
||||||
* Support per link `yt-dlp JSON config or cli options`, `cookies` & `output format`.
|
* Support per link `yt-dlp JSON config or cli options`, `cookies` & `output format`.
|
||||||
* Queue multiple URLs separated by comma.
|
* 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**.
|
* 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 `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.
|
* New `GET /api/history/add?url=http://..` endpoint that allow to add single item via GET request.
|
||||||
* Completely redesigned the frontend UI.
|
* Modern frontend UI.
|
||||||
* Switched out of binary file storage in favor of SQLite.
|
* SQLite as database backend.
|
||||||
* Basic Authentication support.
|
* 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 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.
|
* Support for both advanced and basic mode for WebUI.
|
||||||
* Bundled tools in container: curl-cffi, ffmpeg, ffprobe, aria2, rtmpdump, mkvtoolsnix, mp4box.
|
* Bundled tools in container: curl-cffi, ffmpeg, ffprobe, aria2, rtmpdump, mkvtoolsnix, mp4box.
|
||||||
|
|
||||||
[](https://raw.githubusercontent.com/ArabCoders/ytptube/master/sc_full.png)
|
|
||||||
|
|
||||||
# Recommended basic `ytdlp.json` file settings
|
# 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
|
```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.
|
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
|
# Social contact
|
||||||
|
|
||||||
If you have short or quick questions, you are free to join my [discord server](https://discord.gg/G3GpVR8xpb) and ask
|
If you have short or quick questions, you are free to join my [discord server](https://discord.gg/G3GpVR8xpb) and ask
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@ class Event:
|
||||||
id: str = field(default_factory=lambda: str(uuid.uuid4()), init=False)
|
id: str = field(default_factory=lambda: str(uuid.uuid4()), init=False)
|
||||||
"""The id of the event."""
|
"""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."""
|
"""The time the event was created."""
|
||||||
|
|
||||||
event: str
|
event: str
|
||||||
|
|
@ -209,8 +209,8 @@ class EventListener:
|
||||||
async def handle(self, event: Event, **kwargs):
|
async def handle(self, event: Event, **kwargs):
|
||||||
if self.is_coroutine:
|
if self.is_coroutine:
|
||||||
return self.call_back(event, self.name, **kwargs)
|
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):
|
class EventBus(metaclass=Singleton):
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,9 @@ import asyncio
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from collections.abc import Awaitable
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from datetime import UTC, datetime
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
|
@ -11,7 +12,7 @@ from aiohttp import web
|
||||||
|
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .encoder import Encoder
|
from .encoder import Encoder
|
||||||
from .Events import EventBus, Event, Events
|
from .Events import Event, EventBus, Events
|
||||||
from .ItemDTO import ItemDTO
|
from .ItemDTO import ItemDTO
|
||||||
from .Singleton import Singleton
|
from .Singleton import Singleton
|
||||||
from .Utils import ag, validate_uuid
|
from .Utils import ag, validate_uuid
|
||||||
|
|
@ -323,7 +324,7 @@ class Notification(metaclass=Singleton):
|
||||||
|
|
||||||
return True
|
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:
|
if len(self._targets) < 1:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
@ -339,7 +340,10 @@ class Notification(metaclass=Singleton):
|
||||||
|
|
||||||
tasks.append(self._send(target, ev))
|
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:
|
async def _send(self, target: Target, ev: Event) -> dict:
|
||||||
try:
|
try:
|
||||||
|
|
@ -367,6 +371,7 @@ class Notification(metaclass=Singleton):
|
||||||
if "form" == target.request.type.lower():
|
if "form" == target.request.type.lower():
|
||||||
reqBody["data"]["data"] = self._encoder.encode(reqBody["data"]["data"])
|
reqBody["data"]["data"] = self._encoder.encode(reqBody["data"]["data"])
|
||||||
|
|
||||||
|
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||||
response = await self._client.request(**reqBody)
|
response = await self._client.request(**reqBody)
|
||||||
|
|
||||||
respData = {"url": target.request.url, "status": response.status_code, "text": response.text}
|
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)}
|
return {"url": target.request.url, "status": 500, "text": str(ev)}
|
||||||
|
|
||||||
def emit(self, e: Event, _, **kwargs): # noqa: ARG002
|
def emit(self, e: Event, _, **kwargs): # noqa: ARG002
|
||||||
if len(self._targets) < 1:
|
if len(self._targets) < 1 or not NotificationEvents.is_valid(e.event):
|
||||||
return []
|
return asyncio.sleep(0)
|
||||||
|
|
||||||
if not NotificationEvents.is_valid(e.event):
|
|
||||||
return []
|
|
||||||
|
|
||||||
return self.send(e)
|
return self.send(e)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) });
|
emitter('submit', { reference: toRaw(props.reference), preset: toRaw(copy) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue