add the ability to prevent downloading youtube premiere initial stream.
This commit is contained in:
parent
a478e8e58d
commit
bb41eadde7
8 changed files with 240 additions and 153 deletions
75
README.md
75
README.md
|
|
@ -274,41 +274,42 @@ If you feel like donating and appreciate my work, you can do so by donating to c
|
||||||
|
|
||||||
Certain configuration values can be set via environment variables, using the `-e` parameter on the docker command line, or the `environment:` section in `compose.yaml` file.
|
Certain configuration values can be set via environment variables, using the `-e` parameter on the docker command line, or the `environment:` section in `compose.yaml` file.
|
||||||
|
|
||||||
| Environment Variable | Description | Default |
|
| Environment Variable | Description | Default |
|
||||||
| ------------------------ | ---------------------------------------------------------------- | ---------------------------------- |
|
| ------------------------- | ------------------------------------------------------------------ | ---------------------------------- |
|
||||||
| YTP_OUTPUT_TEMPLATE | The template for the filenames of the downloaded videos | `%(title)s.%(ext)s` |
|
| YTP_OUTPUT_TEMPLATE | The template for the filenames of the downloaded videos | `%(title)s.%(ext)s` |
|
||||||
| YTP_DEFAULT_PRESET | The default preset to use for the download | `default` |
|
| YTP_DEFAULT_PRESET | The default preset to use for the download | `default` |
|
||||||
| YTP_INSTANCE_TITLE | The title of the instance | `empty string` |
|
| YTP_INSTANCE_TITLE | The title of the instance | `empty string` |
|
||||||
| YTP_FILE_LOGGING | Whether to log to file | `false` |
|
| YTP_FILE_LOGGING | Whether to log to file | `false` |
|
||||||
| YTP_DOWNLOAD_PATH | Path to where the downloads will be saved | `/downloads` |
|
| YTP_DOWNLOAD_PATH | Path to where the downloads will be saved | `/downloads` |
|
||||||
| YTP_MAX_WORKERS | How many works to use for downloads | `1` |
|
| YTP_MAX_WORKERS | How many works to use for downloads | `1` |
|
||||||
| YTP_AUTH_USERNAME | Username for basic authentication | `empty string` |
|
| YTP_AUTH_USERNAME | Username for basic authentication | `empty string` |
|
||||||
| YTP_AUTH_PASSWORD | Password for basic authentication | `empty string` |
|
| YTP_AUTH_PASSWORD | Password for basic authentication | `empty string` |
|
||||||
| YTP_CONSOLE_ENABLED | Whether to enable the console | `false` |
|
| YTP_CONSOLE_ENABLED | Whether to enable the console | `false` |
|
||||||
| YTP_REMOVE_FILES | Remove the actual file when clicking the remove button | `false` |
|
| YTP_REMOVE_FILES | Remove the actual file when clicking the remove button | `false` |
|
||||||
| YTP_CONFIG_PATH | Path to where the config files will be stored. | `/config` |
|
| YTP_CONFIG_PATH | Path to where the config files will be stored. | `/config` |
|
||||||
| YTP_TEMP_PATH | Path to where tmp files are stored. | `/tmp` |
|
| YTP_TEMP_PATH | Path to where tmp files are stored. | `/tmp` |
|
||||||
| YTP_TEMP_KEEP | Whether to keep the Individual video temp directory or remove it | `false` |
|
| YTP_TEMP_KEEP | Whether to keep the Individual video temp directory or remove it | `false` |
|
||||||
| YTP_KEEP_ARCHIVE | Keep history of downloaded videos | `true` |
|
| YTP_KEEP_ARCHIVE | Keep history of downloaded videos | `true` |
|
||||||
| YTP_YTDL_DEBUG | Whether to turn debug logging for the internal `yt-dlp` package | `false` |
|
| YTP_YTDL_DEBUG | Whether to turn debug logging for the internal `yt-dlp` package | `false` |
|
||||||
| YTP_HOST | Which IP address to bind to | `0.0.0.0` |
|
| YTP_HOST | Which IP address to bind to | `0.0.0.0` |
|
||||||
| YTP_PORT | Which port to bind to | `8081` |
|
| YTP_PORT | Which port to bind to | `8081` |
|
||||||
| YTP_LOG_LEVEL | Log level | `info` |
|
| YTP_LOG_LEVEL | Log level | `info` |
|
||||||
| YTP_STREAMER_VCODEC | The video codec to use for in-browser streaming | `libx264` |
|
| YTP_STREAMER_VCODEC | The video codec to use for in-browser streaming | `libx264` |
|
||||||
| YTP_STREAMER_ACODEC | The audio codec to use for in-browser streaming | `aac` |
|
| YTP_STREAMER_ACODEC | The audio codec to use for in-browser streaming | `aac` |
|
||||||
| YTP_ACCESS_LOG | Whether to log access to the web server | `true` |
|
| YTP_ACCESS_LOG | Whether to log access to the web server | `true` |
|
||||||
| YTP_DEBUG | Whether to turn on debug mode | `false` |
|
| YTP_DEBUG | Whether to turn on debug mode | `false` |
|
||||||
| YTP_DEBUGPY_PORT | The port to use for the debugpy debugger | `5678` |
|
| YTP_DEBUGPY_PORT | The port to use for the debugpy debugger | `5678` |
|
||||||
| YTP_SOCKET_TIMEOUT | The timeout for the yt-dlp socket connection variable | `30` |
|
| YTP_SOCKET_TIMEOUT | The timeout for the yt-dlp socket connection variable | `30` |
|
||||||
| YTP_EXTRACT_INFO_TIMEOUT | The timeout for extracting video information | `70` |
|
| YTP_EXTRACT_INFO_TIMEOUT | The timeout for extracting video information | `70` |
|
||||||
| YTP_DB_FILE | The path to the SQLite database file | `{config_path}/ytptube.db` |
|
| YTP_DB_FILE | The path to the SQLite database file | `{config_path}/ytptube.db` |
|
||||||
| YTP_MANUAL_ARCHIVE | The path to the manual archive file | `{config_path}/manual_archive.log` |
|
| YTP_MANUAL_ARCHIVE | The path to the manual archive file | `{config_path}/manual_archive.log` |
|
||||||
| YTP_UI_UPDATE_TITLE | Whether to update the title of the page with the current stats | `true` |
|
| YTP_UI_UPDATE_TITLE | Whether to update the title of the page with the current stats | `true` |
|
||||||
| YTP_PIP_PACKAGES | A space separated list of pip packages to install | `empty string` |
|
| YTP_PIP_PACKAGES | A space separated list of pip packages to install | `empty string` |
|
||||||
| YTP_PIP_IGNORE_UPDATES | Do not update the custom pip packages | `false` |
|
| YTP_PIP_IGNORE_UPDATES | Do not update the custom pip packages | `false` |
|
||||||
| YTP_BASIC_MODE | Whether to run WebUI in basic mode | `false` |
|
| YTP_BASIC_MODE | Whether to run WebUI in basic mode | `false` |
|
||||||
| YTP_PICTURES_BACKENDS | A comma separated list of pictures urls to use. | `empty string` |
|
| YTP_PICTURES_BACKENDS | A comma separated list of pictures urls to use. | `empty string` |
|
||||||
| YTP_BROWSER_ENABLED | Whether to enable the file browser | `false` |
|
| YTP_BROWSER_ENABLED | Whether to enable the file browser | `false` |
|
||||||
| YTP_YTDLP_AUTO_UPDATE | Whether to enable the auto update for yt-dlp | `true` |
|
| YTP_YTDLP_AUTO_UPDATE | Whether to enable the auto update for yt-dlp | `true` |
|
||||||
| YTP_BASE_PATH | Set this if you are serving YTPTube from sub-folder | `/` |
|
| YTP_BASE_PATH | Set this if you are serving YTPTube from sub-folder | `/` |
|
||||||
|
| YTP_PREVENT_LIVE_PREMIERE | Prevents the initial youtube premiere stream from being downloaded | `false` |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ from .config import Config
|
||||||
from .DataStore import DataStore
|
from .DataStore import DataStore
|
||||||
from .Download import Download
|
from .Download import Download
|
||||||
from .Events import EventBus, Events
|
from .Events import EventBus, Events
|
||||||
|
from .Events import info as event_info
|
||||||
from .Events import warning as event_warning
|
from .Events import warning as event_warning
|
||||||
from .ItemDTO import Item, ItemDTO
|
from .ItemDTO import Item, ItemDTO
|
||||||
from .Presets import Presets
|
from .Presets import Presets
|
||||||
|
|
@ -239,6 +240,7 @@ class DownloadQueue(metaclass=Singleton):
|
||||||
options: dict = {}
|
options: dict = {}
|
||||||
error: str | None = None
|
error: str | None = None
|
||||||
live_in: str | None = None
|
live_in: str | None = None
|
||||||
|
is_premiere: bool = bool(entry.get("is_premiere", False))
|
||||||
|
|
||||||
# check if the video is live stream.
|
# check if the video is live stream.
|
||||||
if "live_status" in entry and "is_upcoming" == entry.get("live_status"):
|
if "live_status" in entry and "is_upcoming" == entry.get("live_status"):
|
||||||
|
|
@ -246,7 +248,7 @@ class DownloadQueue(metaclass=Singleton):
|
||||||
live_in = formatdate(entry.get("release_timestamp"), usegmt=True)
|
live_in = formatdate(entry.get("release_timestamp"), usegmt=True)
|
||||||
item.extras.update({"live_in": live_in})
|
item.extras.update({"live_in": live_in})
|
||||||
else:
|
else:
|
||||||
error = "Live stream not yet started. And no date is set."
|
error = f"No start time is set for {'premiere' if is_premiere else 'live stream'}."
|
||||||
else:
|
else:
|
||||||
error = entry.get("msg")
|
error = entry.get("msg")
|
||||||
|
|
||||||
|
|
@ -274,7 +276,7 @@ class DownloadQueue(metaclass=Singleton):
|
||||||
is_live = bool(entry.get("is_live") or live_in or entry.get("live_status") in live_status)
|
is_live = bool(entry.get("is_live") or live_in or entry.get("live_status") in live_status)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
download_dir = calc_download_path(base_path=self.config.download_path, folder=item.folder)
|
download_dir: str = calc_download_path(base_path=self.config.download_path, folder=item.folder)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(e)
|
LOG.exception(e)
|
||||||
return {"status": "error", "msg": str(e)}
|
return {"status": "error", "msg": str(e)}
|
||||||
|
|
@ -287,6 +289,14 @@ class DownloadQueue(metaclass=Singleton):
|
||||||
if isinstance(key, str) and key.startswith("playlist") and entry.get(key):
|
if isinstance(key, str) and key.startswith("playlist") and entry.get(key):
|
||||||
item.extras[key] = entry.get(key)
|
item.extras[key] = entry.get(key)
|
||||||
|
|
||||||
|
item.extras["duration"] = entry.get("duration", item.extras.get("duration"))
|
||||||
|
|
||||||
|
if not item.extras.get("live_in") and live_in:
|
||||||
|
item.extras["live_in"] = live_in
|
||||||
|
|
||||||
|
if not item.extras.get("live_in") and is_premiere:
|
||||||
|
item.extras["is_premiere"] = is_premiere
|
||||||
|
|
||||||
dl = ItemDTO(
|
dl = ItemDTO(
|
||||||
id=str(entry.get("id")),
|
id=str(entry.get("id")),
|
||||||
title=str(entry.get("title")),
|
title=str(entry.get("title")),
|
||||||
|
|
@ -310,18 +320,20 @@ class DownloadQueue(metaclass=Singleton):
|
||||||
try:
|
try:
|
||||||
dlInfo: Download = Download(info=dl, info_dict=entry, logs=logs)
|
dlInfo: Download = Download(info=dl, info_dict=entry, logs=logs)
|
||||||
|
|
||||||
text_logs = ""
|
text_logs: str = ""
|
||||||
if filtered_logs := extract_ytdlp_logs(logs):
|
if filtered_logs := extract_ytdlp_logs(logs):
|
||||||
text_logs = f" Logs: {', '.join(filtered_logs)}"
|
text_logs = f" Logs: {', '.join(filtered_logs)}"
|
||||||
|
|
||||||
if live_in or "is_upcoming" == entry.get("live_status"):
|
if live_in or "is_upcoming" == entry.get("live_status"):
|
||||||
NotifyEvent = Events.COMPLETED
|
NotifyEvent = Events.COMPLETED
|
||||||
dlInfo.info.status = "not_live"
|
dlInfo.info.status = "not_live"
|
||||||
dlInfo.info.msg = "Stream is not live yet." + text_logs
|
dlInfo.info.msg = (
|
||||||
itemDownload = self.done.put(dlInfo)
|
f"{'Premiere video' if is_premiere else 'Live Stream' } is not available yet." + text_logs
|
||||||
|
)
|
||||||
|
itemDownload: Download = self.done.put(dlInfo)
|
||||||
elif len(entry.get("formats", [])) < 1:
|
elif len(entry.get("formats", [])) < 1:
|
||||||
availability = entry.get("availability", "public")
|
availability: str = entry.get("availability", "public")
|
||||||
msg = "No formats found."
|
msg: str = "No formats found."
|
||||||
if availability and availability not in ("public",):
|
if availability and availability not in ("public",):
|
||||||
msg += f" Availability is set for '{availability}'."
|
msg += f" Availability is set for '{availability}'."
|
||||||
|
|
||||||
|
|
@ -329,7 +341,27 @@ class DownloadQueue(metaclass=Singleton):
|
||||||
dlInfo.info.status = "error"
|
dlInfo.info.status = "error"
|
||||||
itemDownload = self.done.put(dlInfo)
|
itemDownload = self.done.put(dlInfo)
|
||||||
NotifyEvent = Events.COMPLETED
|
NotifyEvent = Events.COMPLETED
|
||||||
await self._notify.emit(Events.LOG_WARNING, data=event_warning(msg))
|
await self._notify.emit(Events.LOG_WARNING, data=event_warning(f"No formats found for '{dl.title}'."))
|
||||||
|
elif is_premiere and self.config.prevent_live_premiere:
|
||||||
|
dlInfo.info.error = (
|
||||||
|
f"Premiering right now. Delaying download by '{300+dl.extras.get('duration',0)}' seconds."
|
||||||
|
)
|
||||||
|
_live_in = live_in or item.extras.get("live_in", None)
|
||||||
|
if _live_in:
|
||||||
|
starts_in = parsedate_to_datetime(live_in)
|
||||||
|
starts_in = starts_in.replace(tzinfo=UTC) if starts_in.tzinfo is None else starts_in.astimezone(UTC)
|
||||||
|
starts_in = starts_in + timedelta(minutes=5, seconds=dl.extras.get("duration", 0))
|
||||||
|
dlInfo.info.error += f" Starts in {starts_in.isoformat()}."
|
||||||
|
|
||||||
|
dlInfo.info.status = "not_live"
|
||||||
|
itemDownload = self.done.put(dlInfo)
|
||||||
|
NotifyEvent = Events.COMPLETED
|
||||||
|
await self._notify.emit(
|
||||||
|
Events.LOG_INFO,
|
||||||
|
data=event_info(
|
||||||
|
f"'{dl.title}' is premiering. Download delayed by '{300+dl.extras.get('duration')}'s."
|
||||||
|
),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
NotifyEvent = Events.ADDED
|
NotifyEvent = Events.ADDED
|
||||||
itemDownload = self.queue.put(dlInfo)
|
itemDownload = self.queue.put(dlInfo)
|
||||||
|
|
@ -470,12 +502,10 @@ class DownloadQueue(metaclass=Singleton):
|
||||||
if not entry:
|
if not entry:
|
||||||
return {"status": "error", "msg": "Unable to extract info." + "\n".join(logs)}
|
return {"status": "error", "msg": "Unable to extract info." + "\n".join(logs)}
|
||||||
|
|
||||||
if not item.requeued:
|
if not item.requeued and (condition := Conditions.get_instance().match(info=entry)):
|
||||||
condition = Conditions.get_instance().match(info=entry)
|
already.pop()
|
||||||
if condition is not None:
|
LOG.info(f"Condition '{condition.name}' matched for '{item.url}'.")
|
||||||
already.pop()
|
return await self.add(item=item.new_with(requeued=True, cli=condition.cli), already=already)
|
||||||
LOG.info(f"Condition '{condition}' matched for '{item.url}'.")
|
|
||||||
return await self.add(item=item.new_with(requeued=True, cli=condition.cli), already=already)
|
|
||||||
|
|
||||||
end_time = time.perf_counter() - started
|
end_time = time.perf_counter() - started
|
||||||
LOG.debug(f"extract_info: for 'URL: {item.url}' is done in '{end_time:.3f}'. Length: '{len(entry)}/keys'.")
|
LOG.debug(f"extract_info: for 'URL: {item.url}' is done in '{end_time:.3f}'. Length: '{len(entry)}/keys'.")
|
||||||
|
|
@ -833,32 +863,44 @@ class DownloadQueue(metaclass=Singleton):
|
||||||
if self.is_paused() or self.done.empty():
|
if self.is_paused() or self.done.empty():
|
||||||
return
|
return
|
||||||
|
|
||||||
LOG.debug("Checking for live stream items in the history queue.")
|
LOG.debug("Checking history queue for queued live stream links.")
|
||||||
|
|
||||||
time_now = datetime.now(tz=UTC)
|
time_now = datetime.now(tz=UTC)
|
||||||
|
|
||||||
status = ["not_live", "is_upcoming", "is_live"]
|
status: list[str] = ["not_live", "is_upcoming", "is_live"]
|
||||||
|
|
||||||
for id, item in list(self.done.items()):
|
for id, item in list(self.done.items()):
|
||||||
if item.info.status not in status:
|
if item.info.status not in status:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
item_ref = f"{id=} {item.info.id=} {item.info.title=}"
|
item_ref: str = f"{id=} {item.info.id=} {item.info.title=}"
|
||||||
if not item.is_live:
|
if not item.is_live:
|
||||||
LOG.debug(f"Item '{item_ref}' is not a live stream.")
|
LOG.debug(f"Item '{item_ref}' is not a live stream.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not item.info.live_in:
|
live_in: str | None = item.info.live_in or item.info.extras.get("live_in", None)
|
||||||
|
if not live_in:
|
||||||
LOG.debug(f"Item '{item_ref}' marked as live stream, but no date is set.")
|
LOG.debug(f"Item '{item_ref}' marked as live stream, but no date is set.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
starts_in = parsedate_to_datetime(item.info.live_in)
|
starts_in = parsedate_to_datetime(live_in)
|
||||||
starts_in = starts_in.replace(tzinfo=UTC) if starts_in.tzinfo is None else starts_in.astimezone(UTC)
|
starts_in = starts_in.replace(tzinfo=UTC) if starts_in.tzinfo is None else starts_in.astimezone(UTC)
|
||||||
|
|
||||||
if time_now < (starts_in + timedelta(minutes=1)):
|
if time_now < (starts_in + timedelta(minutes=1)):
|
||||||
LOG.debug(f"Item '{item_ref}' is not yet live. will start in '{dt_delta(starts_in-time_now)}'.")
|
LOG.debug(f"Item '{item_ref}' is not yet live. will start in '{dt_delta(starts_in-time_now)}'.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
duration: int | None = item.info.extras.get("duration", None)
|
||||||
|
is_premiere: bool = item.info.extras.get("is_premiere", False)
|
||||||
|
|
||||||
|
if is_premiere and duration and self.config.prevent_live_premiere:
|
||||||
|
premiere_ends: datetime = starts_in + timedelta(minutes=5, seconds=duration)
|
||||||
|
if time_now < premiere_ends:
|
||||||
|
LOG.debug(
|
||||||
|
f"Item '{item_ref}' is premiering and download is delayed by '{300+duration}' seconds. Will start at '{premiere_ends.isoformat()}'"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
LOG.info(f"Re-queuing item '{item_ref} {item.info.extras=}' for download.")
|
LOG.info(f"Re-queuing item '{item_ref} {item.info.extras=}' for download.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -868,17 +910,18 @@ class DownloadQueue(metaclass=Singleton):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
info = item.info
|
await self.add(
|
||||||
new_queue = Item(
|
item=Item(
|
||||||
url=info.url,
|
url=item.info.url,
|
||||||
preset=info.preset,
|
preset=item.info.preset,
|
||||||
folder=info.folder,
|
folder=item.info.folder,
|
||||||
cookies=info.cookies,
|
cookies=item.info.cookies,
|
||||||
template=info.template,
|
template=item.info.template,
|
||||||
cli=item.info.cli,
|
cli=item.info.cli,
|
||||||
extras=item.info.extras,
|
extras=item.info.extras,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
await self.add(item=new_queue)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(f"Failed to re-queue item '{item_ref}'. {e!s}")
|
self.done.put(item)
|
||||||
LOG.exception(e)
|
LOG.exception(e)
|
||||||
|
LOG.error(f"Failed to re-queue item '{item_ref}'. {e!s}")
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ from typing import TypeVar
|
||||||
|
|
||||||
import yt_dlp
|
import yt_dlp
|
||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
|
from yt_dlp.utils import match_str
|
||||||
|
|
||||||
from .LogWrapper import LogWrapper
|
from .LogWrapper import LogWrapper
|
||||||
|
|
||||||
|
|
@ -59,7 +60,8 @@ FILES_TYPE: list = [
|
||||||
{"rx": re.compile(r"\.(nfo|json|jpg|torrent|\.info\.json)$", re.IGNORECASE), "type": "metadata"},
|
{"rx": re.compile(r"\.(nfo|json|jpg|torrent|\.info\.json)$", re.IGNORECASE), "type": "metadata"},
|
||||||
]
|
]
|
||||||
|
|
||||||
DATETIME_PATTERN = re.compile(r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:[+-]\d{2}:\d{2}))\s?")
|
TAG_REGEX: re.Pattern[str] = re.compile(r"%{([^:}]+)(?::([^}]*))?}c")
|
||||||
|
DT_PATTERN: re.Pattern[str] = re.compile(r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:[+-]\d{2}:\d{2}))\s?")
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
@ -204,6 +206,10 @@ def extract_info(
|
||||||
if not data:
|
if not data:
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
data["is_premiere"] = match_str("media_type=video & duration & is_live", data)
|
||||||
|
if not data["is_premiere"]:
|
||||||
|
data["is_premiere"] = "video" == data.get("media_type") and "is_upcoming" == data.get("live_status")
|
||||||
|
|
||||||
return yt_dlp.YoutubeDL.sanitize_info(data) if sanitize_info else data
|
return yt_dlp.YoutubeDL.sanitize_info(data) if sanitize_info else data
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -963,7 +969,7 @@ async def read_logfile(file: Path, offset: int = 0, limit: int = 50) -> dict:
|
||||||
for line in lines[-(offset + limit) : -offset] if offset else lines[-limit:]:
|
for line in lines[-(offset + limit) : -offset] if offset else lines[-limit:]:
|
||||||
line_bytes = line if isinstance(line, bytes) else line.encode()
|
line_bytes = line if isinstance(line, bytes) else line.encode()
|
||||||
msg = line.decode(errors="replace")
|
msg = line.decode(errors="replace")
|
||||||
dt_match = DATETIME_PATTERN.match(msg)
|
dt_match = DT_PATTERN.match(msg)
|
||||||
result.append(
|
result.append(
|
||||||
{
|
{
|
||||||
"id": sha256(line_bytes).hexdigest(),
|
"id": sha256(line_bytes).hexdigest(),
|
||||||
|
|
@ -1005,7 +1011,7 @@ async def tail_log(file: Path, emitter: callable, sleep_time: float = 0.5):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
msg = line.decode(errors="replace")
|
msg = line.decode(errors="replace")
|
||||||
dt_match = DATETIME_PATTERN.match(msg)
|
dt_match = DT_PATTERN.match(msg)
|
||||||
|
|
||||||
await emitter(
|
await emitter(
|
||||||
{
|
{
|
||||||
|
|
@ -1228,3 +1234,25 @@ def load_modules(root_path: Path, directory: Path):
|
||||||
importlib.import_module(full_name)
|
importlib.import_module(full_name)
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
LOG.error(f"Failed to import module '{full_name}': {e}")
|
LOG.error(f"Failed to import module '{full_name}': {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_tags(text: str) -> tuple[str, dict[str, str | bool]]:
|
||||||
|
"""
|
||||||
|
Parse tags from a string formatted with %{tag_name[:value]}c.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text (str): The input string containing tags.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple[str, dict[str, str | bool]]: A tuple containing the string with tags removed and a dictionary of tags.
|
||||||
|
|
||||||
|
"""
|
||||||
|
tags: dict[str, str | bool] = {}
|
||||||
|
|
||||||
|
def replacer(match: re.Match) -> str:
|
||||||
|
name = match.group(1)
|
||||||
|
value = match.group(2)
|
||||||
|
tags[name] = value if value is not None else True
|
||||||
|
return ""
|
||||||
|
|
||||||
|
return TAG_REGEX.sub(replacer, text).strip(), tags
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,9 @@ class Config:
|
||||||
is_native: bool = False
|
is_native: bool = False
|
||||||
"Is the application running in webview."
|
"Is the application running in webview."
|
||||||
|
|
||||||
|
prevent_live_premiere: bool = False
|
||||||
|
"""Prevent downloading of the initial premiere live broadcast."""
|
||||||
|
|
||||||
pictures_backends: list[str] = [
|
pictures_backends: list[str] = [
|
||||||
"https://unsplash.it/1920/1080?random",
|
"https://unsplash.it/1920/1080?random",
|
||||||
"https://picsum.photos/1920/1080",
|
"https://picsum.photos/1920/1080",
|
||||||
|
|
@ -209,6 +212,7 @@ class Config:
|
||||||
"console_enabled",
|
"console_enabled",
|
||||||
"browser_enabled",
|
"browser_enabled",
|
||||||
"ytdlp_auto_update",
|
"ytdlp_auto_update",
|
||||||
|
"prevent_premiere_live",
|
||||||
)
|
)
|
||||||
"The variables that are booleans."
|
"The variables that are booleans."
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -537,6 +537,9 @@ const setIcon = item => {
|
||||||
if (!item.filename) {
|
if (!item.filename) {
|
||||||
return 'fa-solid fa-exclamation'
|
return 'fa-solid fa-exclamation'
|
||||||
}
|
}
|
||||||
|
if (item.extras?.is_premiere) {
|
||||||
|
return 'fa-solid fa-star'
|
||||||
|
}
|
||||||
return item.is_live ? 'fa-solid fa-globe' : 'fa-solid fa-circle-check'
|
return item.is_live ? 'fa-solid fa-globe' : 'fa-solid fa-circle-check'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -549,7 +552,7 @@ const setIcon = item => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('not_live' === item.status) {
|
if ('not_live' === item.status) {
|
||||||
return 'fa-solid fa-headset'
|
return item.extras?.is_premiere ? 'fa-solid fa-star' : 'fa-solid fa-headset'
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'fa-solid fa-circle'
|
return 'fa-solid fa-circle'
|
||||||
|
|
@ -579,6 +582,11 @@ const setStatus = item => {
|
||||||
if (!item.filename) {
|
if (!item.filename) {
|
||||||
return 'Skipped?'
|
return 'Skipped?'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item.extras?.is_premiere) {
|
||||||
|
return 'Premiere'
|
||||||
|
}
|
||||||
|
|
||||||
return item.is_live ? 'Live Ended' : 'Completed'
|
return item.is_live ? 'Live Ended' : 'Completed'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -591,6 +599,9 @@ const setStatus = item => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('not_live' === item.status) {
|
if ('not_live' === item.status) {
|
||||||
|
if (item.extras?.is_premiere){
|
||||||
|
return 'Premiere'
|
||||||
|
}
|
||||||
return display_style.value === 'cards' ? 'Live Stream' : 'Live'
|
return display_style.value === 'cards' ? 'Live Stream' : 'Live'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
<hr class="navbar-divider">
|
<hr class="navbar-divider">
|
||||||
</template>
|
</template>
|
||||||
<div class="notification-list">
|
<div class="notification-list">
|
||||||
<div v-for="n in store.notifications" :key="n.id" class="navbar-item is-flex is-align-items-start"
|
<div v-for="n in store.notifications" :key="n.id" class="pr-1 pl-1 navbar-item is-flex is-align-items-start"
|
||||||
:class="['notification-item', 'notification-' + n.level]">
|
:class="['notification-item', 'notification-' + n.level]">
|
||||||
<div class="is-flex-grow-1">
|
<div class="is-flex-grow-1">
|
||||||
<p class="is-size-7 mb-1 notification-message" :class="{ expanded: expandedId === n.id }"
|
<p class="is-size-7 mb-1 notification-message" :class="{ expanded: expandedId === n.id }"
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
"web-types": "./web-types.json",
|
"web-types": "./web-types.json",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pinia/nuxt": "^0.11.1",
|
"@pinia/nuxt": "^0.11.1",
|
||||||
"@sentry/nuxt": "^9.28.1",
|
"@sentry/nuxt": "^9.29.0",
|
||||||
"@vueuse/core": "^13.3.0",
|
"@vueuse/core": "^13.3.0",
|
||||||
"@vueuse/nuxt": "^13.3.0",
|
"@vueuse/nuxt": "^13.3.0",
|
||||||
"@xterm/addon-fit": "^0.10.0",
|
"@xterm/addon-fit": "^0.10.0",
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ importers:
|
||||||
specifier: ^0.11.1
|
specifier: ^0.11.1
|
||||||
version: 0.11.1(magicast@0.3.5)(pinia@3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))
|
version: 0.11.1(magicast@0.3.5)(pinia@3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))
|
||||||
'@sentry/nuxt':
|
'@sentry/nuxt':
|
||||||
specifier: ^9.28.1
|
specifier: ^9.29.0
|
||||||
version: 9.28.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)(magicast@0.3.5)(nuxt@3.17.5(@parcel/watcher@2.5.1)(@types/node@24.0.1)(db0@0.3.2)(ioredis@5.6.1)(magicast@0.3.5)(rollup@4.43.0)(terser@5.42.0)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(terser@5.42.0)(yaml@2.8.0))(yaml@2.8.0))(pinia@3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))(rollup@4.43.0)(vue@3.5.16(typescript@5.8.3))
|
version: 9.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)(magicast@0.3.5)(nuxt@3.17.5(@parcel/watcher@2.5.1)(@types/node@24.0.1)(db0@0.3.2)(ioredis@5.6.1)(magicast@0.3.5)(rollup@4.43.0)(terser@5.42.0)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(terser@5.42.0)(yaml@2.8.0))(yaml@2.8.0))(pinia@3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))(rollup@4.43.0)(vue@3.5.16(typescript@5.8.3))
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: ^13.3.0
|
specifier: ^13.3.0
|
||||||
version: 13.3.0(vue@3.5.16(typescript@5.8.3))
|
version: 13.3.0(vue@3.5.16(typescript@5.8.3))
|
||||||
|
|
@ -1066,40 +1066,40 @@ packages:
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
|
|
||||||
'@sentry-internal/browser-utils@9.28.1':
|
'@sentry-internal/browser-utils@9.29.0':
|
||||||
resolution: {integrity: sha512-P/FEZkT7UqTw9P/2n/Y4Aa1OtGP6dnCvyqzPPkjiRdVa7Ep7S5ElBJloGv7077TLLBtAfCsEUVRlM1F6/jQoaA==}
|
resolution: {integrity: sha512-Wp6UJCDVV2KVK+TG8GwdLZyDy4GtUYDmVhGMpHKPS3G/Qgpf36cY/XHwChwaHZ5P9Bk1sjS9Ok698J59S8L2nw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@sentry-internal/feedback@9.28.1':
|
'@sentry-internal/feedback@9.29.0':
|
||||||
resolution: {integrity: sha512-HOk/c26D3nlClO/xEefev8fIJzRA621PFQvNFPu/y0Z5HujEqSmIsrff0cXszPPYD95h4Mwk63E0ZYdspdeXcw==}
|
resolution: {integrity: sha512-ADvetGrtr+RfYcQKrQxah4fHs/xDJ/VjbStVMSuaNllzwWPYNkWIGFE6YjQ7wZszj0DQIu5/H+B6lZKsFYk4xw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@sentry-internal/replay-canvas@9.28.1':
|
'@sentry-internal/replay-canvas@9.29.0':
|
||||||
resolution: {integrity: sha512-RtkogfcIpXLFCyV8CTnXmVTH2QauT/KwmUAXBbeOz3rRWsM19yjN1moHrsjxn7OdjTv+D4qWSCA8Ka1aKSpr7g==}
|
resolution: {integrity: sha512-TrQYhSAVPhyenvu0fNkon7BznFibu1mzS5bCudxhgOWajZluUVrXcbp8Q3WZ3R+AogrcgA3Vy6aumP/+fMKdwg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@sentry-internal/replay@9.28.1':
|
'@sentry-internal/replay@9.29.0':
|
||||||
resolution: {integrity: sha512-Tv9pkfAX+1bmhxF42TL0c4uTiK2+rp5LMYEPdz6JBfpfvG/Z1unPGsuB7fQmHYKyfHBQJmi92DZV+smljm7w/g==}
|
resolution: {integrity: sha512-we/1JPRje8sNowQCyogOV1OYWuDOP/3XmDi48XoFG2HB0XMl2HfL5LI8AvgAvC/5nrqVAAo4ktbjoVLm1fb7rg==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@sentry/babel-plugin-component-annotate@3.2.4':
|
'@sentry/babel-plugin-component-annotate@3.2.4':
|
||||||
resolution: {integrity: sha512-yBzRn3GEUSv1RPtE4xB4LnuH74ZxtdoRJ5cmQ9i6mzlmGDxlrnKuvem5++AolZTE9oJqAD3Tx2rd1PqmpWnLoA==}
|
resolution: {integrity: sha512-yBzRn3GEUSv1RPtE4xB4LnuH74ZxtdoRJ5cmQ9i6mzlmGDxlrnKuvem5++AolZTE9oJqAD3Tx2rd1PqmpWnLoA==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
'@sentry/babel-plugin-component-annotate@3.4.0':
|
'@sentry/babel-plugin-component-annotate@3.5.0':
|
||||||
resolution: {integrity: sha512-tSzfc3aE7m0PM0Aj7HBDet5llH9AB9oc+tBQ8AvOqUSnWodLrNCuWeQszJ7mIBovD3figgCU3h0cvI6U5cDtsg==}
|
resolution: {integrity: sha512-s2go8w03CDHbF9luFGtBHKJp4cSpsQzNVqgIa9Pfa4wnjipvrK6CxVT4icpLA3YO6kg5u622Yoa5GF3cJdippw==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
'@sentry/browser@9.28.1':
|
'@sentry/browser@9.29.0':
|
||||||
resolution: {integrity: sha512-XAS46iQSq8lXTnv9udQP025JTf3PwSVRE9ePJVQhx25QBWxedqGhEOv5qqX9b1Ijf8KiZYXXhBWMQxBBXVzUaw==}
|
resolution: {integrity: sha512-+GFX/yb+rh6V1fSgTYM6ttAgledl2aUR3T3Rg86HNuegbdX8ym6lOtUOIZ0j9jPK015HR47KIPyIZVZZJ7Rj9g==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@sentry/bundler-plugin-core@3.2.4':
|
'@sentry/bundler-plugin-core@3.2.4':
|
||||||
resolution: {integrity: sha512-YMj9XW5W2JA89EeweE7CPKLDz245LBsI1JhCmqpt/bjSvmsSIAAPsLYnvIJBS3LQFm0OhtG8NB54PTi96dAcMA==}
|
resolution: {integrity: sha512-YMj9XW5W2JA89EeweE7CPKLDz245LBsI1JhCmqpt/bjSvmsSIAAPsLYnvIJBS3LQFm0OhtG8NB54PTi96dAcMA==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
'@sentry/bundler-plugin-core@3.4.0':
|
'@sentry/bundler-plugin-core@3.5.0':
|
||||||
resolution: {integrity: sha512-X1Q41AsQ6xcT6hB4wYmBDBukndKM/inT4IsR7pdKLi7ICpX2Qq6lisamBAEPCgEvnLpazSFguaiC0uiwMKAdqw==}
|
resolution: {integrity: sha512-zDzPrhJqAAy2VzV4g540qAZH4qxzisstK2+NIJPZUUKztWRWUV2cMHsyUtdctYgloGkLyGpZJBE3RE6dmP/xqQ==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
'@sentry/cli-darwin@2.42.2':
|
'@sentry/cli-darwin@2.42.2':
|
||||||
|
|
@ -1148,22 +1148,22 @@ packages:
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
'@sentry/core@9.28.1':
|
'@sentry/core@9.29.0':
|
||||||
resolution: {integrity: sha512-6q59r/71MeE+4StkvwdKAAyhBBNpWcii0HeiWBZ3l1gaFYQlb6bChjZJRZmxSzF5dnvkdF4duQbAC3JmjeIbPA==}
|
resolution: {integrity: sha512-wDyNe45PM+RCGtUn1tK7LzJ08ksv8i8KRUHrst7lsinEfRm83YH+wbWrPmwkVNEngUZvYkHwGLbNXM7xgFUuDQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@sentry/node@9.28.1':
|
'@sentry/node@9.29.0':
|
||||||
resolution: {integrity: sha512-1bC8ywFPs1EIKzRlUhLoreTR/fUU45O+ezVNvOu0I6JURMwjyC8uT23f0Xkiolx4WjKZXER+d0el3HojPaYkYw==}
|
resolution: {integrity: sha512-oABipgC/fClRuvyMeK43rigv9F+OAaoR84UaMKB7aPXN6iz634wBRVsaoZAwiR3xLL+R7MafEPPA/s9XqlG7ag==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@sentry/nuxt@9.28.1':
|
'@sentry/nuxt@9.29.0':
|
||||||
resolution: {integrity: sha512-+QhmFywTq5BL9tK7Yq7j6o6/B31l2fAn/ZULmIlJLAlgnKsexVS/Dbhlbal/deMi0tLLQPxbEtOtZ86PbQpKdg==}
|
resolution: {integrity: sha512-bstQwiqgB6n4/23NYDb8pl7B7wICSJaZoIbTjAhceiog1yVtdQZsQa8iRyCwDpdzU7NyOhfCT1ECQ98EdwDJog==}
|
||||||
engines: {node: '>=18.19.1'}
|
engines: {node: '>=18.19.1'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
nuxt: '>=3.7.0 || 4.x'
|
nuxt: '>=3.7.0 || 4.x'
|
||||||
|
|
||||||
'@sentry/opentelemetry@9.28.1':
|
'@sentry/opentelemetry@9.29.0':
|
||||||
resolution: {integrity: sha512-1xxriB2diNSlNpkMosdgIKzPOPwAiyFg7XgEWKpLtDYWj7WVbLNyLkUWriFr4JgU40uZp3LW+sGgnshA48/zQQ==}
|
resolution: {integrity: sha512-QTUmre8i5+832RjzQW+g8IQ3UmBe5fbQXGbCF5hQ0UNuHle9r3Z8UZcIff5W8tm5AXMxPqvptTnDEZUUXHgBiA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@opentelemetry/api': ^1.9.0
|
'@opentelemetry/api': ^1.9.0
|
||||||
|
|
@ -1173,8 +1173,8 @@ packages:
|
||||||
'@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.0.0
|
'@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.0.0
|
||||||
'@opentelemetry/semantic-conventions': ^1.34.0
|
'@opentelemetry/semantic-conventions': ^1.34.0
|
||||||
|
|
||||||
'@sentry/rollup-plugin@3.4.0':
|
'@sentry/rollup-plugin@3.5.0':
|
||||||
resolution: {integrity: sha512-oqDcjV+aaTZZ7oOadk90KlShOYfKEEQsvbZtzHl7HPHNt5kmtTaQyWphPIDt2Z9OCK8QF5T8GLsr1MCOXJ6vqA==}
|
resolution: {integrity: sha512-aMPCvdNMkv//LZYjYCJsEcNiNiaQFinBO75+9NJVEe1OrKNdGqDi3hky2ll7zuY+xozEtZCZcUKJJz/aAYAS8A==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
rollup: '>=3.2.0'
|
rollup: '>=3.2.0'
|
||||||
|
|
@ -1183,8 +1183,8 @@ packages:
|
||||||
resolution: {integrity: sha512-ZRn5TLlq5xtwKOqaWP+XqS1PYVfbBCgsbMk7wW2Ly6EgF9wYePvtLqKgYnE3hwPg2LpBnRPR2ti1ohlUkR+wXA==}
|
resolution: {integrity: sha512-ZRn5TLlq5xtwKOqaWP+XqS1PYVfbBCgsbMk7wW2Ly6EgF9wYePvtLqKgYnE3hwPg2LpBnRPR2ti1ohlUkR+wXA==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
'@sentry/vue@9.28.1':
|
'@sentry/vue@9.29.0':
|
||||||
resolution: {integrity: sha512-o6b2rQpbEjvNgyUDd7dVjjKwFfembf0ltd4MW78F2CoAYMS/YkHAum/iZ/5wOB4pYhutPbeDDZf0s6aM5KCOZQ==}
|
resolution: {integrity: sha512-wwKHgW2RFpn91UQuClYiI6Tt8VkQ36dGxiVT8l98uGC0uPXzwz40ikdBgnOMt3qwbRZX8ziEXHjjTtAxdETe3w==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
pinia: 2.x || 3.x
|
pinia: 2.x || 3.x
|
||||||
|
|
@ -1595,8 +1595,8 @@ packages:
|
||||||
caniuse-api@3.0.0:
|
caniuse-api@3.0.0:
|
||||||
resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
|
resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
|
||||||
|
|
||||||
caniuse-lite@1.0.30001722:
|
caniuse-lite@1.0.30001723:
|
||||||
resolution: {integrity: sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA==}
|
resolution: {integrity: sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==}
|
||||||
|
|
||||||
chokidar@3.6.0:
|
chokidar@3.6.0:
|
||||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||||
|
|
@ -2337,8 +2337,8 @@ packages:
|
||||||
image-meta@0.2.1:
|
image-meta@0.2.1:
|
||||||
resolution: {integrity: sha512-K6acvFaelNxx8wc2VjbIzXKDVB0Khs0QT35U6NkGfTdCmjLNcO2945m7RFNR9/RPVFm48hq7QPzK8uGH18HCGw==}
|
resolution: {integrity: sha512-K6acvFaelNxx8wc2VjbIzXKDVB0Khs0QT35U6NkGfTdCmjLNcO2945m7RFNR9/RPVFm48hq7QPzK8uGH18HCGw==}
|
||||||
|
|
||||||
import-in-the-middle@1.14.0:
|
import-in-the-middle@1.14.2:
|
||||||
resolution: {integrity: sha512-g5zLT0HaztRJWysayWYiUq/7E5H825QIiecMD2pI5QO7Wzr847l6GDvPvmZaDIdrDtS2w7qRczywxiK6SL5vRw==}
|
resolution: {integrity: sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==}
|
||||||
|
|
||||||
impound@1.0.0:
|
impound@1.0.0:
|
||||||
resolution: {integrity: sha512-8lAJ+1Arw2sMaZ9HE2ZmL5zOcMnt18s6+7Xqgq2aUVy4P1nlzAyPtzCDxsk51KVFwHEEdc6OWvUyqwHwhRYaug==}
|
resolution: {integrity: sha512-8lAJ+1Arw2sMaZ9HE2ZmL5zOcMnt18s6+7Xqgq2aUVy4P1nlzAyPtzCDxsk51KVFwHEEdc6OWvUyqwHwhRYaug==}
|
||||||
|
|
@ -4090,8 +4090,8 @@ packages:
|
||||||
resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
|
resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
|
||||||
engines: {node: '>= 14'}
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
zod@3.25.63:
|
zod@3.25.64:
|
||||||
resolution: {integrity: sha512-3ttCkqhtpncYXfP0f6dsyabbYV/nEUW+Xlu89jiXbTBifUfjaSqXOG6JnQPLtqt87n7KAmnMqcjay6c0Wq0Vbw==}
|
resolution: {integrity: sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g==}
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
|
|
@ -4532,7 +4532,7 @@ snapshots:
|
||||||
unixify: 1.0.0
|
unixify: 1.0.0
|
||||||
urlpattern-polyfill: 8.0.2
|
urlpattern-polyfill: 8.0.2
|
||||||
yargs: 17.7.2
|
yargs: 17.7.2
|
||||||
zod: 3.25.63
|
zod: 3.25.64
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
- rollup
|
- rollup
|
||||||
|
|
@ -4966,7 +4966,7 @@ snapshots:
|
||||||
'@opentelemetry/api': 1.9.0
|
'@opentelemetry/api': 1.9.0
|
||||||
'@opentelemetry/api-logs': 0.57.2
|
'@opentelemetry/api-logs': 0.57.2
|
||||||
'@types/shimmer': 1.2.0
|
'@types/shimmer': 1.2.0
|
||||||
import-in-the-middle: 1.14.0
|
import-in-the-middle: 1.14.2
|
||||||
require-in-the-middle: 7.5.2
|
require-in-the-middle: 7.5.2
|
||||||
semver: 7.7.2
|
semver: 7.7.2
|
||||||
shimmer: 1.2.1
|
shimmer: 1.2.1
|
||||||
|
|
@ -5264,35 +5264,35 @@ snapshots:
|
||||||
'@rollup/rollup-win32-x64-msvc@4.43.0':
|
'@rollup/rollup-win32-x64-msvc@4.43.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@sentry-internal/browser-utils@9.28.1':
|
'@sentry-internal/browser-utils@9.29.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry/core': 9.28.1
|
'@sentry/core': 9.29.0
|
||||||
|
|
||||||
'@sentry-internal/feedback@9.28.1':
|
'@sentry-internal/feedback@9.29.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry/core': 9.28.1
|
'@sentry/core': 9.29.0
|
||||||
|
|
||||||
'@sentry-internal/replay-canvas@9.28.1':
|
'@sentry-internal/replay-canvas@9.29.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry-internal/replay': 9.28.1
|
'@sentry-internal/replay': 9.29.0
|
||||||
'@sentry/core': 9.28.1
|
'@sentry/core': 9.29.0
|
||||||
|
|
||||||
'@sentry-internal/replay@9.28.1':
|
'@sentry-internal/replay@9.29.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry-internal/browser-utils': 9.28.1
|
'@sentry-internal/browser-utils': 9.29.0
|
||||||
'@sentry/core': 9.28.1
|
'@sentry/core': 9.29.0
|
||||||
|
|
||||||
'@sentry/babel-plugin-component-annotate@3.2.4': {}
|
'@sentry/babel-plugin-component-annotate@3.2.4': {}
|
||||||
|
|
||||||
'@sentry/babel-plugin-component-annotate@3.4.0': {}
|
'@sentry/babel-plugin-component-annotate@3.5.0': {}
|
||||||
|
|
||||||
'@sentry/browser@9.28.1':
|
'@sentry/browser@9.29.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry-internal/browser-utils': 9.28.1
|
'@sentry-internal/browser-utils': 9.29.0
|
||||||
'@sentry-internal/feedback': 9.28.1
|
'@sentry-internal/feedback': 9.29.0
|
||||||
'@sentry-internal/replay': 9.28.1
|
'@sentry-internal/replay': 9.29.0
|
||||||
'@sentry-internal/replay-canvas': 9.28.1
|
'@sentry-internal/replay-canvas': 9.29.0
|
||||||
'@sentry/core': 9.28.1
|
'@sentry/core': 9.29.0
|
||||||
|
|
||||||
'@sentry/bundler-plugin-core@3.2.4':
|
'@sentry/bundler-plugin-core@3.2.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -5308,10 +5308,10 @@ snapshots:
|
||||||
- encoding
|
- encoding
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@sentry/bundler-plugin-core@3.4.0':
|
'@sentry/bundler-plugin-core@3.5.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.27.4
|
'@babel/core': 7.27.4
|
||||||
'@sentry/babel-plugin-component-annotate': 3.4.0
|
'@sentry/babel-plugin-component-annotate': 3.5.0
|
||||||
'@sentry/cli': 2.42.2
|
'@sentry/cli': 2.42.2
|
||||||
dotenv: 16.5.0
|
dotenv: 16.5.0
|
||||||
find-up: 5.0.0
|
find-up: 5.0.0
|
||||||
|
|
@ -5362,9 +5362,9 @@ snapshots:
|
||||||
- encoding
|
- encoding
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@sentry/core@9.28.1': {}
|
'@sentry/core@9.29.0': {}
|
||||||
|
|
||||||
'@sentry/node@9.28.1':
|
'@sentry/node@9.29.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@opentelemetry/api': 1.9.0
|
'@opentelemetry/api': 1.9.0
|
||||||
'@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0)
|
'@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0)
|
||||||
|
|
@ -5396,23 +5396,23 @@ snapshots:
|
||||||
'@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0)
|
'@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0)
|
||||||
'@opentelemetry/semantic-conventions': 1.34.0
|
'@opentelemetry/semantic-conventions': 1.34.0
|
||||||
'@prisma/instrumentation': 6.8.2(@opentelemetry/api@1.9.0)
|
'@prisma/instrumentation': 6.8.2(@opentelemetry/api@1.9.0)
|
||||||
'@sentry/core': 9.28.1
|
'@sentry/core': 9.29.0
|
||||||
'@sentry/opentelemetry': 9.28.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)
|
'@sentry/opentelemetry': 9.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)
|
||||||
import-in-the-middle: 1.14.0
|
import-in-the-middle: 1.14.2
|
||||||
minimatch: 9.0.5
|
minimatch: 9.0.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@sentry/nuxt@9.28.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)(magicast@0.3.5)(nuxt@3.17.5(@parcel/watcher@2.5.1)(@types/node@24.0.1)(db0@0.3.2)(ioredis@5.6.1)(magicast@0.3.5)(rollup@4.43.0)(terser@5.42.0)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(terser@5.42.0)(yaml@2.8.0))(yaml@2.8.0))(pinia@3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))(rollup@4.43.0)(vue@3.5.16(typescript@5.8.3))':
|
'@sentry/nuxt@9.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)(magicast@0.3.5)(nuxt@3.17.5(@parcel/watcher@2.5.1)(@types/node@24.0.1)(db0@0.3.2)(ioredis@5.6.1)(magicast@0.3.5)(rollup@4.43.0)(terser@5.42.0)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(terser@5.42.0)(yaml@2.8.0))(yaml@2.8.0))(pinia@3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))(rollup@4.43.0)(vue@3.5.16(typescript@5.8.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nuxt/kit': 3.17.5(magicast@0.3.5)
|
'@nuxt/kit': 3.17.5(magicast@0.3.5)
|
||||||
'@sentry/browser': 9.28.1
|
'@sentry/browser': 9.29.0
|
||||||
'@sentry/core': 9.28.1
|
'@sentry/core': 9.29.0
|
||||||
'@sentry/node': 9.28.1
|
'@sentry/node': 9.29.0
|
||||||
'@sentry/opentelemetry': 9.28.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)
|
'@sentry/opentelemetry': 9.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)
|
||||||
'@sentry/rollup-plugin': 3.4.0(rollup@4.43.0)
|
'@sentry/rollup-plugin': 3.5.0(rollup@4.43.0)
|
||||||
'@sentry/vite-plugin': 3.2.4
|
'@sentry/vite-plugin': 3.2.4
|
||||||
'@sentry/vue': 9.28.1(pinia@3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3))
|
'@sentry/vue': 9.29.0(pinia@3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3))
|
||||||
nuxt: 3.17.5(@parcel/watcher@2.5.1)(@types/node@24.0.1)(db0@0.3.2)(ioredis@5.6.1)(magicast@0.3.5)(rollup@4.43.0)(terser@5.42.0)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(terser@5.42.0)(yaml@2.8.0))(yaml@2.8.0)
|
nuxt: 3.17.5(@parcel/watcher@2.5.1)(@types/node@24.0.1)(db0@0.3.2)(ioredis@5.6.1)(magicast@0.3.5)(rollup@4.43.0)(terser@5.42.0)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.1)(jiti@2.4.2)(terser@5.42.0)(yaml@2.8.0))(yaml@2.8.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@opentelemetry/api'
|
- '@opentelemetry/api'
|
||||||
|
|
@ -5428,7 +5428,7 @@ snapshots:
|
||||||
- supports-color
|
- supports-color
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
'@sentry/opentelemetry@9.28.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)':
|
'@sentry/opentelemetry@9.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@opentelemetry/api': 1.9.0
|
'@opentelemetry/api': 1.9.0
|
||||||
'@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0)
|
'@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0)
|
||||||
|
|
@ -5436,11 +5436,11 @@ snapshots:
|
||||||
'@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0)
|
'@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0)
|
||||||
'@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0)
|
'@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0)
|
||||||
'@opentelemetry/semantic-conventions': 1.34.0
|
'@opentelemetry/semantic-conventions': 1.34.0
|
||||||
'@sentry/core': 9.28.1
|
'@sentry/core': 9.29.0
|
||||||
|
|
||||||
'@sentry/rollup-plugin@3.4.0(rollup@4.43.0)':
|
'@sentry/rollup-plugin@3.5.0(rollup@4.43.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry/bundler-plugin-core': 3.4.0
|
'@sentry/bundler-plugin-core': 3.5.0
|
||||||
rollup: 4.43.0
|
rollup: 4.43.0
|
||||||
unplugin: 1.0.1
|
unplugin: 1.0.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
|
@ -5455,10 +5455,10 @@ snapshots:
|
||||||
- encoding
|
- encoding
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@sentry/vue@9.28.1(pinia@3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3))':
|
'@sentry/vue@9.29.0(pinia@3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3)))(vue@3.5.16(typescript@5.8.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sentry/browser': 9.28.1
|
'@sentry/browser': 9.29.0
|
||||||
'@sentry/core': 9.28.1
|
'@sentry/core': 9.29.0
|
||||||
vue: 3.5.16(typescript@5.8.3)
|
vue: 3.5.16(typescript@5.8.3)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
pinia: 3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3))
|
pinia: 3.0.3(typescript@5.8.3)(vue@3.5.16(typescript@5.8.3))
|
||||||
|
|
@ -5864,7 +5864,7 @@ snapshots:
|
||||||
autoprefixer@10.4.21(postcss@8.5.5):
|
autoprefixer@10.4.21(postcss@8.5.5):
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.25.0
|
browserslist: 4.25.0
|
||||||
caniuse-lite: 1.0.30001722
|
caniuse-lite: 1.0.30001723
|
||||||
fraction.js: 4.3.7
|
fraction.js: 4.3.7
|
||||||
normalize-range: 0.1.2
|
normalize-range: 0.1.2
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
|
|
@ -5900,7 +5900,7 @@ snapshots:
|
||||||
|
|
||||||
browserslist@4.25.0:
|
browserslist@4.25.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001722
|
caniuse-lite: 1.0.30001723
|
||||||
electron-to-chromium: 1.5.167
|
electron-to-chromium: 1.5.167
|
||||||
node-releases: 2.0.19
|
node-releases: 2.0.19
|
||||||
update-browserslist-db: 1.1.3(browserslist@4.25.0)
|
update-browserslist-db: 1.1.3(browserslist@4.25.0)
|
||||||
|
|
@ -5956,11 +5956,11 @@ snapshots:
|
||||||
caniuse-api@3.0.0:
|
caniuse-api@3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.25.0
|
browserslist: 4.25.0
|
||||||
caniuse-lite: 1.0.30001722
|
caniuse-lite: 1.0.30001723
|
||||||
lodash.memoize: 4.1.2
|
lodash.memoize: 4.1.2
|
||||||
lodash.uniq: 4.5.0
|
lodash.uniq: 4.5.0
|
||||||
|
|
||||||
caniuse-lite@1.0.30001722: {}
|
caniuse-lite@1.0.30001723: {}
|
||||||
|
|
||||||
chokidar@3.6.0:
|
chokidar@3.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -6708,7 +6708,7 @@ snapshots:
|
||||||
|
|
||||||
image-meta@0.2.1: {}
|
image-meta@0.2.1: {}
|
||||||
|
|
||||||
import-in-the-middle@1.14.0:
|
import-in-the-middle@1.14.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.15.0
|
acorn: 8.15.0
|
||||||
acorn-import-attributes: 1.9.5(acorn@8.15.0)
|
acorn-import-attributes: 1.9.5(acorn@8.15.0)
|
||||||
|
|
@ -8592,4 +8592,4 @@ snapshots:
|
||||||
compress-commons: 6.0.2
|
compress-commons: 6.0.2
|
||||||
readable-stream: 4.7.0
|
readable-stream: 4.7.0
|
||||||
|
|
||||||
zod@3.25.63: {}
|
zod@3.25.64: {}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue