mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-05 18:58:53 -05:00
Generate default LL server config and attach it to GH release (#6430)
This commit is contained in:
parent
68f2806204
commit
90691ba2b9
40
.github/workflows/publish_release.yml
vendored
40
.github/workflows/publish_release.yml
vendored
@ -80,10 +80,38 @@ jobs:
|
|||||||
name: build-output
|
name: build-output
|
||||||
path: ./dist
|
path: ./dist
|
||||||
|
|
||||||
|
generate_default_ll_server_config:
|
||||||
|
name: Generate default application.yml
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: '3.8'
|
||||||
|
|
||||||
|
- name: Install script's dependencies
|
||||||
|
run: python -m pip install PyYAML
|
||||||
|
|
||||||
|
- name: Generate default application.yml
|
||||||
|
env:
|
||||||
|
APP_YML_FILE: "Red-DiscordBot-${{ github.ref_name }}-default-lavalink-application.yml"
|
||||||
|
run: |
|
||||||
|
mkdir -p dist
|
||||||
|
python .github/workflows/scripts/get_default_ll_server_config.py "dist/$APP_YML_FILE"
|
||||||
|
|
||||||
|
- name: Upload default application.yml
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ll-default-server-config
|
||||||
|
path: ./dist
|
||||||
|
|
||||||
release_to_pypi:
|
release_to_pypi:
|
||||||
needs:
|
needs:
|
||||||
- release_information
|
- release_information
|
||||||
- build
|
- build
|
||||||
|
- generate_default_ll_server_config
|
||||||
environment: Release
|
environment: Release
|
||||||
name: Release to PyPI
|
name: Release to PyPI
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -96,6 +124,18 @@ jobs:
|
|||||||
name: build-output
|
name: build-output
|
||||||
path: dist/
|
path: dist/
|
||||||
|
|
||||||
|
- name: Download default application.yml
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ll-default-server-config
|
||||||
|
path: dist/
|
||||||
|
|
||||||
|
- name: Upload dists to GitHub Release
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: "${{ github.token }}"
|
||||||
|
run: |
|
||||||
|
gh release upload "$GITHUB_REF_NAME" dist/*
|
||||||
|
|
||||||
- name: Publish package distributions to PyPI
|
- name: Publish package distributions to PyPI
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
with:
|
with:
|
||||||
|
|||||||
31
.github/workflows/scripts/get_default_ll_server_config.py
vendored
Normal file
31
.github/workflows/scripts/get_default_ll_server_config.py
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
ROOT_FOLDER = Path(__file__).parents[3].absolute()
|
||||||
|
AUDIO_FOLDER = ROOT_FOLDER / "redbot/cogs/audio"
|
||||||
|
|
||||||
|
# We want to import `redbot.cogs.audio.managed_node` package as if it were top-level package
|
||||||
|
# so we have to the `redbot/cogs/audio` directory to Python's path.
|
||||||
|
sys.path.insert(0, str(AUDIO_FOLDER))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
try:
|
||||||
|
output_file = sys.argv[1]
|
||||||
|
except IndexError:
|
||||||
|
print("Usage:", sys.argv[0], "<output_file>", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
import managed_node
|
||||||
|
|
||||||
|
server_config = managed_node.get_default_server_config()
|
||||||
|
with open(output_file, "w", encoding="utf-8") as fp:
|
||||||
|
yaml.safe_dump(server_config, fp)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
16
redbot/cogs/audio/managed_node/__init__.py
Normal file
16
redbot/cogs/audio/managed_node/__init__.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Note: contents of this package are meant to be self-contained
|
||||||
|
# and should not depend on anything in Red or on external dependencies.
|
||||||
|
|
||||||
|
from .ll_server_config import generate_server_config, get_default_server_config
|
||||||
|
from .ll_version import LAVALINK_BUILD_LINE, LavalinkOldVersion, LavalinkVersion
|
||||||
|
from .version_pins import JAR_VERSION, YT_PLUGIN_VERSION
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"generate_server_config",
|
||||||
|
"get_default_server_config",
|
||||||
|
"LAVALINK_BUILD_LINE",
|
||||||
|
"LavalinkOldVersion",
|
||||||
|
"LavalinkVersion",
|
||||||
|
"JAR_VERSION",
|
||||||
|
"YT_PLUGIN_VERSION",
|
||||||
|
)
|
||||||
119
redbot/cogs/audio/managed_node/ll_server_config.py
Normal file
119
redbot/cogs/audio/managed_node/ll_server_config.py
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Dict, Final
|
||||||
|
|
||||||
|
from . import version_pins
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"DEFAULT_LAVALINK_YAML",
|
||||||
|
"get_default_server_config",
|
||||||
|
"generate_server_config",
|
||||||
|
"change_dict_naming_convention",
|
||||||
|
)
|
||||||
|
|
||||||
|
YT_PLUGIN_REPOSITORY: Final[str] = "https://maven.lavalink.dev/releases"
|
||||||
|
|
||||||
|
DEFAULT_LAVALINK_YAML = {
|
||||||
|
# The nesting structure of this dict is very important, it's a 1:1 mirror of application.yaml in JSON
|
||||||
|
"yaml__server__address": "localhost",
|
||||||
|
"yaml__server__port": 2333,
|
||||||
|
"yaml__lavalink__server__password": "youshallnotpass",
|
||||||
|
"yaml__lavalink__server__sources__http": True,
|
||||||
|
"yaml__lavalink__server__sources__bandcamp": True,
|
||||||
|
"yaml__lavalink__server__sources__local": True,
|
||||||
|
"yaml__lavalink__server__sources__soundcloud": True,
|
||||||
|
"yaml__lavalink__server__sources__youtube": True,
|
||||||
|
"yaml__lavalink__server__sources__twitch": True,
|
||||||
|
"yaml__lavalink__server__sources__vimeo": True,
|
||||||
|
"yaml__lavalink__server__bufferDurationMs": 400,
|
||||||
|
"yaml__lavalink__server__frameBufferDurationMs": 1000,
|
||||||
|
# 100 pages - 100 entries per page = 10,000 tracks which is the Audio Limit for a single playlist.
|
||||||
|
"yaml__lavalink__server__youtubePlaylistLoadLimit": 100,
|
||||||
|
"yaml__lavalink__server__playerUpdateInterval": 1,
|
||||||
|
"yaml__lavalink__server__youtubeSearchEnabled": True,
|
||||||
|
"yaml__lavalink__server__soundcloudSearchEnabled": True,
|
||||||
|
"yaml__lavalink__server__gc_warnings": True,
|
||||||
|
"yaml__metrics__prometheus__enabled": False,
|
||||||
|
"yaml__metrics__prometheus__endpoint": "/metrics",
|
||||||
|
"yaml__sentry__dsn": "",
|
||||||
|
"yaml__sentry__environment": "",
|
||||||
|
"yaml__logging__file__path": "./logs/",
|
||||||
|
"yaml__logging__level__root": "INFO",
|
||||||
|
"yaml__logging__level__lavalink": "INFO",
|
||||||
|
"yaml__logging__logback__rollingpolicy__max_history": 15,
|
||||||
|
"yaml__logging__logback__rollingpolicy__max_size": "10MB",
|
||||||
|
# plugin configuration - note that the plugin may be disabled by the manager
|
||||||
|
"yaml__plugins__youtube__enabled": True,
|
||||||
|
"yaml__plugins__youtube__allowSearch": True,
|
||||||
|
"yaml__plugins__youtube__allowDirectVideoIds": True,
|
||||||
|
"yaml__plugins__youtube__allowDirectPlaylistIds": True,
|
||||||
|
"yaml__plugins__youtube__clients": [
|
||||||
|
"MUSIC",
|
||||||
|
"WEB",
|
||||||
|
"ANDROID_TESTSUITE",
|
||||||
|
"TVHTML5EMBEDDED",
|
||||||
|
"ANDROID_LITE",
|
||||||
|
"MEDIA_CONNECT",
|
||||||
|
"IOS",
|
||||||
|
],
|
||||||
|
"yaml__plugins__youtube__WEB__playback": True,
|
||||||
|
"yaml__plugins__youtube__TVHTML5EMBEDDED__playlistLoading": False,
|
||||||
|
"yaml__plugins__youtube__TVHTML5EMBEDDED__videoLoading": False,
|
||||||
|
"yaml__plugins__youtube__TVHTML5EMBEDDED__searching": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _unflatten_config_defaults(config_defaults: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
ret: Dict[str, Any] = {}
|
||||||
|
|
||||||
|
# based on Config._get_defaults_dict()
|
||||||
|
for flat_key, value in config_defaults.items():
|
||||||
|
keys = flat_key.split("__")
|
||||||
|
partial = ret
|
||||||
|
for idx, key in enumerate(keys, start=1):
|
||||||
|
if idx == len(keys):
|
||||||
|
partial[key] = value
|
||||||
|
else:
|
||||||
|
partial = partial.setdefault(key, {})
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_server_config() -> Dict[str, Any]:
|
||||||
|
return generate_server_config(_unflatten_config_defaults(DEFAULT_LAVALINK_YAML)["yaml"])
|
||||||
|
|
||||||
|
|
||||||
|
def generate_server_config(config_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
data = change_dict_naming_convention(config_data)
|
||||||
|
ll_config = data["lavalink"]
|
||||||
|
sources = ll_config["server"]["sources"]
|
||||||
|
plugins = ll_config.setdefault("plugins", [])
|
||||||
|
|
||||||
|
enable_yt_plugin = sources["youtube"]
|
||||||
|
if enable_yt_plugin:
|
||||||
|
sources["youtube"] = False
|
||||||
|
yt_plugin = {
|
||||||
|
"dependency": f"dev.lavalink.youtube:youtube-plugin:{version_pins.YT_PLUGIN_VERSION}",
|
||||||
|
"repository": YT_PLUGIN_REPOSITORY,
|
||||||
|
}
|
||||||
|
plugins.append(yt_plugin)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
# This assumes all keys with `_` should be converted from `part1_part2` to `part1-part2`
|
||||||
|
def _convert_function(key: str) -> str:
|
||||||
|
return key.replace("_", "-")
|
||||||
|
|
||||||
|
|
||||||
|
def change_dict_naming_convention(data: Any) -> Any:
|
||||||
|
ret: Any = data
|
||||||
|
if isinstance(data, dict):
|
||||||
|
ret = {}
|
||||||
|
for key, value in data.items():
|
||||||
|
ret[_convert_function(key)] = change_dict_naming_convention(value)
|
||||||
|
elif isinstance(data, list):
|
||||||
|
ret = []
|
||||||
|
for value in data:
|
||||||
|
ret.append(change_dict_naming_convention(value))
|
||||||
|
return ret
|
||||||
204
redbot/cogs/audio/managed_node/ll_version.py
Normal file
204
redbot/cogs/audio/managed_node/ll_version.py
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from typing import Final, Optional, Pattern, Tuple
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"LAVALINK_BUILD_LINE",
|
||||||
|
"LavalinkOldVersion",
|
||||||
|
"LavalinkVersion",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# present until Lavalink 3.5-rc4
|
||||||
|
LAVALINK_BUILD_LINE: Final[Pattern] = re.compile(rb"^Build:\s+(?P<build>\d+)$", re.MULTILINE)
|
||||||
|
# we don't actually care about what the version format before 3.5-rc4 is exactly
|
||||||
|
# as the comparison is based entirely on the build number
|
||||||
|
_LAVALINK_VERSION_LINE_PRE35: Final[Pattern] = re.compile(
|
||||||
|
rb"^Version:\s+(?P<version>\S+)$", re.MULTILINE | re.VERBOSE
|
||||||
|
)
|
||||||
|
# used for LL versions >=3.5-rc4 but below 3.6.
|
||||||
|
# Since this only applies to historical version, this regex is based only on
|
||||||
|
# version numbers that actually existed, not ones that technically could.
|
||||||
|
_LAVALINK_VERSION_LINE_PRE36: Final[Pattern] = re.compile(
|
||||||
|
rb"""
|
||||||
|
^
|
||||||
|
Version:\s+
|
||||||
|
(?P<version>
|
||||||
|
(?P<major>3)\.(?P<minor>[0-5])
|
||||||
|
# Before LL 3.6, when patch version == 0, it was stripped from the version string
|
||||||
|
(?:\.(?P<patch>[1-9]\d*))?
|
||||||
|
# Before LL 3.6, the dot in rc.N was optional
|
||||||
|
(?:-rc\.?(?P<rc>0|[1-9]\d*))?
|
||||||
|
# additional build metadata, can be used by our downstream Lavalink
|
||||||
|
# if we need to alter an upstream release
|
||||||
|
(?:\+red\.(?P<red>[1-9]\d*))?
|
||||||
|
)
|
||||||
|
$
|
||||||
|
""",
|
||||||
|
re.MULTILINE | re.VERBOSE,
|
||||||
|
)
|
||||||
|
# used for LL 3.6 and newer
|
||||||
|
# This regex is limited to the realistic usage in the LL version number,
|
||||||
|
# not everything that could be a part of it according to the spec.
|
||||||
|
# We can easily release an update to this regex in the future if it ever becomes necessary.
|
||||||
|
_LAVALINK_VERSION_LINE: Final[Pattern] = re.compile(
|
||||||
|
rb"""
|
||||||
|
^
|
||||||
|
Version:\s+
|
||||||
|
(?P<version>
|
||||||
|
(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)
|
||||||
|
(?:-rc\.(?P<rc>0|[1-9]\d*))?
|
||||||
|
# additional build metadata, can be used by our downstream Lavalink
|
||||||
|
# if we need to alter an upstream release
|
||||||
|
(?:\+red\.(?P<red>[1-9]\d*))?
|
||||||
|
)
|
||||||
|
$
|
||||||
|
""",
|
||||||
|
re.MULTILINE | re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LavalinkOldVersion:
|
||||||
|
def __init__(self, raw_version: str, *, build_number: int) -> None:
|
||||||
|
self.raw_version = raw_version
|
||||||
|
self.build_number = build_number
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"{self.raw_version}_{self.build_number}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_version_output(cls, output: bytes) -> LavalinkOldVersion:
|
||||||
|
build_match = LAVALINK_BUILD_LINE.search(output)
|
||||||
|
if build_match is None:
|
||||||
|
raise ValueError(
|
||||||
|
"Could not find 'Build' line in the given `--version` output,"
|
||||||
|
" or invalid build number given."
|
||||||
|
)
|
||||||
|
version_match = _LAVALINK_VERSION_LINE_PRE35.search(output)
|
||||||
|
if version_match is None:
|
||||||
|
raise ValueError(
|
||||||
|
"Could not find 'Version' line in the given `--version` output,"
|
||||||
|
" or invalid version number given."
|
||||||
|
)
|
||||||
|
return cls(
|
||||||
|
raw_version=version_match["version"].decode(),
|
||||||
|
build_number=int(build_match["build"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, LavalinkOldVersion):
|
||||||
|
return self.build_number == other.build_number
|
||||||
|
if isinstance(other, LavalinkVersion):
|
||||||
|
return False
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __lt__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, LavalinkOldVersion):
|
||||||
|
return self.build_number < other.build_number
|
||||||
|
if isinstance(other, LavalinkVersion):
|
||||||
|
return True
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __le__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, LavalinkOldVersion):
|
||||||
|
return self.build_number <= other.build_number
|
||||||
|
if isinstance(other, LavalinkVersion):
|
||||||
|
return True
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __gt__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, LavalinkOldVersion):
|
||||||
|
return self.build_number > other.build_number
|
||||||
|
if isinstance(other, LavalinkVersion):
|
||||||
|
return False
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __ge__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, LavalinkOldVersion):
|
||||||
|
return self.build_number >= other.build_number
|
||||||
|
if isinstance(other, LavalinkVersion):
|
||||||
|
return False
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
|
class LavalinkVersion:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
major: int,
|
||||||
|
minor: int,
|
||||||
|
patch: int = 0,
|
||||||
|
*,
|
||||||
|
rc: Optional[int] = None,
|
||||||
|
red: int = 0,
|
||||||
|
) -> None:
|
||||||
|
self.major = major
|
||||||
|
self.minor = minor
|
||||||
|
self.patch = patch
|
||||||
|
self.rc = rc
|
||||||
|
self.red = red
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
version = f"{self.major}.{self.minor}.{self.patch}"
|
||||||
|
if self.rc is not None:
|
||||||
|
version += f"-rc.{self.rc}"
|
||||||
|
if self.red:
|
||||||
|
version += f"+red.{self.red}"
|
||||||
|
return version
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_version_output(cls, output: bytes) -> LavalinkVersion:
|
||||||
|
match = _LAVALINK_VERSION_LINE.search(output)
|
||||||
|
if match is None:
|
||||||
|
# >=3.5-rc4, <3.6
|
||||||
|
match = _LAVALINK_VERSION_LINE_PRE36.search(output)
|
||||||
|
if match is None:
|
||||||
|
raise ValueError(
|
||||||
|
"Could not find 'Version' line in the given `--version` output,"
|
||||||
|
" or invalid version number given."
|
||||||
|
)
|
||||||
|
return cls(
|
||||||
|
major=int(match["major"]),
|
||||||
|
minor=int(match["minor"]),
|
||||||
|
patch=int(match["patch"] or 0),
|
||||||
|
rc=int(match["rc"]) if match["rc"] is not None else None,
|
||||||
|
red=int(match["red"] or 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_comparison_tuple(self) -> Tuple[int, int, int, bool, int, int]:
|
||||||
|
return self.major, self.minor, self.patch, self.rc is None, self.rc or 0, self.red
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, LavalinkVersion):
|
||||||
|
return self._get_comparison_tuple() == other._get_comparison_tuple()
|
||||||
|
if isinstance(other, LavalinkOldVersion):
|
||||||
|
return False
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __lt__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, LavalinkVersion):
|
||||||
|
return self._get_comparison_tuple() < other._get_comparison_tuple()
|
||||||
|
if isinstance(other, LavalinkOldVersion):
|
||||||
|
return False
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __le__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, LavalinkVersion):
|
||||||
|
return self._get_comparison_tuple() <= other._get_comparison_tuple()
|
||||||
|
if isinstance(other, LavalinkOldVersion):
|
||||||
|
return False
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __gt__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, LavalinkVersion):
|
||||||
|
return self._get_comparison_tuple() > other._get_comparison_tuple()
|
||||||
|
if isinstance(other, LavalinkOldVersion):
|
||||||
|
return True
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __ge__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, LavalinkVersion):
|
||||||
|
return self._get_comparison_tuple() >= other._get_comparison_tuple()
|
||||||
|
if isinstance(other, LavalinkOldVersion):
|
||||||
|
return True
|
||||||
|
return NotImplemented
|
||||||
9
redbot/cogs/audio/managed_node/version_pins.py
Normal file
9
redbot/cogs/audio/managed_node/version_pins.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from typing import Final
|
||||||
|
|
||||||
|
from .ll_version import LavalinkVersion
|
||||||
|
|
||||||
|
__all__ = ("JAR_VERSION", "YT_PLUGIN_VERSION")
|
||||||
|
|
||||||
|
|
||||||
|
JAR_VERSION: Final[LavalinkVersion] = LavalinkVersion(3, 7, 11, red=3)
|
||||||
|
YT_PLUGIN_VERSION: Final[str] = "1.5.2"
|
||||||
@ -22,6 +22,7 @@ from red_commons.logging import getLogger
|
|||||||
from redbot.core import data_manager, Config
|
from redbot.core import data_manager, Config
|
||||||
from redbot.core.i18n import Translator
|
from redbot.core.i18n import Translator
|
||||||
|
|
||||||
|
from . import managed_node
|
||||||
from .errors import (
|
from .errors import (
|
||||||
LavalinkDownloadFailed,
|
LavalinkDownloadFailed,
|
||||||
InvalidArchitectureException,
|
InvalidArchitectureException,
|
||||||
@ -35,8 +36,8 @@ from .errors import (
|
|||||||
NoProcessFound,
|
NoProcessFound,
|
||||||
NodeUnhealthy,
|
NodeUnhealthy,
|
||||||
)
|
)
|
||||||
|
from .managed_node.ll_version import LAVALINK_BUILD_LINE, LavalinkVersion, LavalinkOldVersion
|
||||||
from .utils import (
|
from .utils import (
|
||||||
change_dict_naming_convention,
|
|
||||||
get_max_allocation_size,
|
get_max_allocation_size,
|
||||||
replace_p_with_prefix,
|
replace_p_with_prefix,
|
||||||
)
|
)
|
||||||
@ -55,7 +56,7 @@ _LL_PLUGIN_LOG: Final[Pattern[bytes]] = re.compile(
|
|||||||
)
|
)
|
||||||
_FAILED_TO_START: Final[Pattern[bytes]] = re.compile(rb"Web server failed to start\. (.*)")
|
_FAILED_TO_START: Final[Pattern[bytes]] = re.compile(rb"Web server failed to start\. (.*)")
|
||||||
|
|
||||||
# Version regexes
|
# Java version regexes
|
||||||
#
|
#
|
||||||
# We expect the output to look something like:
|
# We expect the output to look something like:
|
||||||
# $ java -version
|
# $ java -version
|
||||||
@ -103,210 +104,14 @@ LAVALINK_LAVAPLAYER_LINE: Final[Pattern] = re.compile(
|
|||||||
LAVALINK_BUILD_TIME_LINE: Final[Pattern] = re.compile(
|
LAVALINK_BUILD_TIME_LINE: Final[Pattern] = re.compile(
|
||||||
rb"^Build time:\s+(?P<build_time>\d+[.\d+]*).*$", re.MULTILINE
|
rb"^Build time:\s+(?P<build_time>\d+[.\d+]*).*$", re.MULTILINE
|
||||||
)
|
)
|
||||||
# present until Lavalink 3.5-rc4
|
|
||||||
LAVALINK_BUILD_LINE: Final[Pattern] = re.compile(rb"^Build:\s+(?P<build>\d+)$", re.MULTILINE)
|
|
||||||
# we don't actually care about what the version format before 3.5-rc4 is exactly
|
|
||||||
# as the comparison is based entirely on the build number
|
|
||||||
LAVALINK_VERSION_LINE_PRE35: Final[Pattern] = re.compile(
|
|
||||||
rb"^Version:\s+(?P<version>\S+)$", re.MULTILINE | re.VERBOSE
|
|
||||||
)
|
|
||||||
# used for LL versions >=3.5-rc4 but below 3.6.
|
|
||||||
# Since this only applies to historical version, this regex is based only on
|
|
||||||
# version numbers that actually existed, not ones that technically could.
|
|
||||||
LAVALINK_VERSION_LINE_PRE36: Final[Pattern] = re.compile(
|
|
||||||
rb"""
|
|
||||||
^
|
|
||||||
Version:\s+
|
|
||||||
(?P<version>
|
|
||||||
(?P<major>3)\.(?P<minor>[0-5])
|
|
||||||
# Before LL 3.6, when patch version == 0, it was stripped from the version string
|
|
||||||
(?:\.(?P<patch>[1-9]\d*))?
|
|
||||||
# Before LL 3.6, the dot in rc.N was optional
|
|
||||||
(?:-rc\.?(?P<rc>0|[1-9]\d*))?
|
|
||||||
# additional build metadata, can be used by our downstream Lavalink
|
|
||||||
# if we need to alter an upstream release
|
|
||||||
(?:\+red\.(?P<red>[1-9]\d*))?
|
|
||||||
)
|
|
||||||
$
|
|
||||||
""",
|
|
||||||
re.MULTILINE | re.VERBOSE,
|
|
||||||
)
|
|
||||||
# used for LL 3.6 and newer
|
|
||||||
# This regex is limited to the realistic usage in the LL version number,
|
|
||||||
# not everything that could be a part of it according to the spec.
|
|
||||||
# We can easily release an update to this regex in the future if it ever becomes necessary.
|
|
||||||
LAVALINK_VERSION_LINE: Final[Pattern] = re.compile(
|
|
||||||
rb"""
|
|
||||||
^
|
|
||||||
Version:\s+
|
|
||||||
(?P<version>
|
|
||||||
(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)
|
|
||||||
(?:-rc\.(?P<rc>0|[1-9]\d*))?
|
|
||||||
# additional build metadata, can be used by our downstream Lavalink
|
|
||||||
# if we need to alter an upstream release
|
|
||||||
(?:\+red\.(?P<red>[1-9]\d*))?
|
|
||||||
)
|
|
||||||
$
|
|
||||||
""",
|
|
||||||
re.MULTILINE | re.VERBOSE,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class LavalinkOldVersion:
|
|
||||||
def __init__(self, raw_version: str, *, build_number: int) -> None:
|
|
||||||
self.raw_version = raw_version
|
|
||||||
self.build_number = build_number
|
|
||||||
|
|
||||||
def __str__(self) -> None:
|
|
||||||
return f"{self.raw_version}_{self.build_number}"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_version_output(cls, output: bytes) -> Self:
|
|
||||||
build_match = LAVALINK_BUILD_LINE.search(output)
|
|
||||||
if build_match is None:
|
|
||||||
raise ValueError(
|
|
||||||
"Could not find 'Build' line in the given `--version` output,"
|
|
||||||
" or invalid build number given."
|
|
||||||
)
|
|
||||||
version_match = LAVALINK_VERSION_LINE_PRE35.search(output)
|
|
||||||
if version_match is None:
|
|
||||||
raise ValueError(
|
|
||||||
"Could not find 'Version' line in the given `--version` output,"
|
|
||||||
" or invalid version number given."
|
|
||||||
)
|
|
||||||
return cls(
|
|
||||||
raw_version=version_match["version"].decode(),
|
|
||||||
build_number=int(build_match["build"]),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __eq__(self, other: object) -> bool:
|
|
||||||
if isinstance(other, LavalinkOldVersion):
|
|
||||||
return self.build_number == other.build_number
|
|
||||||
if isinstance(other, LavalinkVersion):
|
|
||||||
return False
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def __lt__(self, other: object) -> bool:
|
|
||||||
if isinstance(other, LavalinkOldVersion):
|
|
||||||
return self.build_number < other.build_number
|
|
||||||
if isinstance(other, LavalinkVersion):
|
|
||||||
return True
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def __le__(self, other: object) -> bool:
|
|
||||||
if isinstance(other, LavalinkOldVersion):
|
|
||||||
return self.build_number <= other.build_number
|
|
||||||
if isinstance(other, LavalinkVersion):
|
|
||||||
return True
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def __gt__(self, other: object) -> bool:
|
|
||||||
if isinstance(other, LavalinkOldVersion):
|
|
||||||
return self.build_number > other.build_number
|
|
||||||
if isinstance(other, LavalinkVersion):
|
|
||||||
return False
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def __ge__(self, other: object) -> bool:
|
|
||||||
if isinstance(other, LavalinkOldVersion):
|
|
||||||
return self.build_number >= other.build_number
|
|
||||||
if isinstance(other, LavalinkVersion):
|
|
||||||
return False
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
|
|
||||||
class LavalinkVersion:
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
major: int,
|
|
||||||
minor: int,
|
|
||||||
patch: int = 0,
|
|
||||||
*,
|
|
||||||
rc: Optional[int] = None,
|
|
||||||
red: int = 0,
|
|
||||||
) -> None:
|
|
||||||
self.major = major
|
|
||||||
self.minor = minor
|
|
||||||
self.patch = patch
|
|
||||||
self.rc = rc
|
|
||||||
self.red = red
|
|
||||||
|
|
||||||
def __str__(self) -> None:
|
|
||||||
version = f"{self.major}.{self.minor}.{self.patch}"
|
|
||||||
if self.rc is not None:
|
|
||||||
version += f"-rc.{self.rc}"
|
|
||||||
if self.red:
|
|
||||||
version += f"+red.{self.red}"
|
|
||||||
return version
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_version_output(cls, output: bytes) -> Self:
|
|
||||||
match = LAVALINK_VERSION_LINE.search(output)
|
|
||||||
if match is None:
|
|
||||||
# >=3.5-rc4, <3.6
|
|
||||||
match = LAVALINK_VERSION_LINE_PRE36.search(output)
|
|
||||||
if match is None:
|
|
||||||
raise ValueError(
|
|
||||||
"Could not find 'Version' line in the given `--version` output,"
|
|
||||||
" or invalid version number given."
|
|
||||||
)
|
|
||||||
return LavalinkVersion(
|
|
||||||
major=int(match["major"]),
|
|
||||||
minor=int(match["minor"]),
|
|
||||||
patch=int(match["patch"] or 0),
|
|
||||||
rc=int(match["rc"]) if match["rc"] is not None else None,
|
|
||||||
red=int(match["red"] or 0),
|
|
||||||
)
|
|
||||||
|
|
||||||
def _get_comparison_tuple(self) -> Tuple[int, int, int, bool, int, int]:
|
|
||||||
return self.major, self.minor, self.patch, self.rc is None, self.rc or 0, self.red
|
|
||||||
|
|
||||||
def __eq__(self, other: object) -> bool:
|
|
||||||
if isinstance(other, LavalinkVersion):
|
|
||||||
return self._get_comparison_tuple() == other._get_comparison_tuple()
|
|
||||||
if isinstance(other, LavalinkOldVersion):
|
|
||||||
return False
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def __lt__(self, other: object) -> bool:
|
|
||||||
if isinstance(other, LavalinkVersion):
|
|
||||||
return self._get_comparison_tuple() < other._get_comparison_tuple()
|
|
||||||
if isinstance(other, LavalinkOldVersion):
|
|
||||||
return False
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def __le__(self, other: object) -> bool:
|
|
||||||
if isinstance(other, LavalinkVersion):
|
|
||||||
return self._get_comparison_tuple() <= other._get_comparison_tuple()
|
|
||||||
if isinstance(other, LavalinkOldVersion):
|
|
||||||
return False
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def __gt__(self, other: object) -> bool:
|
|
||||||
if isinstance(other, LavalinkVersion):
|
|
||||||
return self._get_comparison_tuple() > other._get_comparison_tuple()
|
|
||||||
if isinstance(other, LavalinkOldVersion):
|
|
||||||
return True
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
def __ge__(self, other: object) -> bool:
|
|
||||||
if isinstance(other, LavalinkVersion):
|
|
||||||
return self._get_comparison_tuple() >= other._get_comparison_tuple()
|
|
||||||
if isinstance(other, LavalinkOldVersion):
|
|
||||||
return True
|
|
||||||
return NotImplemented
|
|
||||||
|
|
||||||
|
|
||||||
class ServerManager:
|
class ServerManager:
|
||||||
JAR_VERSION: Final[str] = LavalinkVersion(3, 7, 11, red=3)
|
|
||||||
YT_PLUGIN_VERSION: Final[str] = "1.5.2"
|
|
||||||
|
|
||||||
LAVALINK_DOWNLOAD_URL: Final[str] = (
|
LAVALINK_DOWNLOAD_URL: Final[str] = (
|
||||||
"https://github.com/Cog-Creators/Lavalink-Jars/releases/download/"
|
"https://github.com/Cog-Creators/Lavalink-Jars/releases/download/"
|
||||||
f"{JAR_VERSION}/"
|
f"{managed_node.JAR_VERSION}/"
|
||||||
"Lavalink.jar"
|
"Lavalink.jar"
|
||||||
)
|
)
|
||||||
YT_PLUGIN_REPOSITORY: Final[str] = "https://maven.lavalink.dev/releases"
|
|
||||||
|
|
||||||
_java_available: ClassVar[Optional[bool]] = None
|
_java_available: ClassVar[Optional[bool]] = None
|
||||||
_java_version: ClassVar[Optional[Tuple[int, int]]] = None
|
_java_version: ClassVar[Optional[Tuple[int, int]]] = None
|
||||||
@ -429,19 +234,7 @@ class ServerManager:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
async def process_settings(self):
|
async def process_settings(self):
|
||||||
data = change_dict_naming_convention(await self._config.yaml.all())
|
data = managed_node.generate_server_config(await self._config.yaml.all())
|
||||||
ll_config = data["lavalink"]
|
|
||||||
sources = ll_config["server"]["sources"]
|
|
||||||
plugins = ll_config.setdefault("plugins", [])
|
|
||||||
|
|
||||||
enable_yt_plugin = sources["youtube"]
|
|
||||||
if enable_yt_plugin:
|
|
||||||
sources["youtube"] = False
|
|
||||||
yt_plugin = {
|
|
||||||
"dependency": f"dev.lavalink.youtube:youtube-plugin:{self.YT_PLUGIN_VERSION}",
|
|
||||||
"repository": self.YT_PLUGIN_REPOSITORY,
|
|
||||||
}
|
|
||||||
plugins.append(yt_plugin)
|
|
||||||
|
|
||||||
with open(self.lavalink_app_yml, "w", encoding="utf-8") as f:
|
with open(self.lavalink_app_yml, "w", encoding="utf-8") as f:
|
||||||
yaml.safe_dump(data, f)
|
yaml.safe_dump(data, f)
|
||||||
@ -584,7 +377,8 @@ class ServerManager:
|
|||||||
# A 404 means our LAVALINK_DOWNLOAD_URL is invalid, so likely the jar version
|
# A 404 means our LAVALINK_DOWNLOAD_URL is invalid, so likely the jar version
|
||||||
# hasn't been published yet
|
# hasn't been published yet
|
||||||
raise LavalinkDownloadFailed(
|
raise LavalinkDownloadFailed(
|
||||||
f"Lavalink jar version {self.JAR_VERSION} hasn't been published yet",
|
f"Lavalink jar version {managed_node.JAR_VERSION}"
|
||||||
|
" hasn't been published yet",
|
||||||
response=response,
|
response=response,
|
||||||
should_retry=False,
|
should_retry=False,
|
||||||
)
|
)
|
||||||
@ -670,7 +464,7 @@ class ServerManager:
|
|||||||
self._jvm = java["jvm"].decode()
|
self._jvm = java["jvm"].decode()
|
||||||
self._lavaplayer = lavaplayer["lavaplayer"].decode()
|
self._lavaplayer = lavaplayer["lavaplayer"].decode()
|
||||||
self._buildtime = date
|
self._buildtime = date
|
||||||
self._up_to_date = self._lavalink_version >= self.JAR_VERSION
|
self._up_to_date = self._lavalink_version >= managed_node.JAR_VERSION
|
||||||
return self._up_to_date
|
return self._up_to_date
|
||||||
|
|
||||||
async def maybe_download_jar(self):
|
async def maybe_download_jar(self):
|
||||||
@ -690,7 +484,7 @@ class ServerManager:
|
|||||||
log.info(
|
log.info(
|
||||||
"Lavalink version outdated, triggering update from %s to %s...",
|
"Lavalink version outdated, triggering update from %s to %s...",
|
||||||
self._lavalink_version,
|
self._lavalink_version,
|
||||||
self.JAR_VERSION,
|
managed_node.JAR_VERSION,
|
||||||
)
|
)
|
||||||
await self._download_jar()
|
await self._download_jar()
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,8 @@ from redbot.core import commands
|
|||||||
from redbot.core.bot import Red
|
from redbot.core.bot import Red
|
||||||
from redbot.core.i18n import Translator
|
from redbot.core.i18n import Translator
|
||||||
|
|
||||||
|
from .managed_node.ll_server_config import DEFAULT_LAVALINK_YAML, change_dict_naming_convention
|
||||||
|
|
||||||
log = getLogger("red.cogs.Audio.task.callback")
|
log = getLogger("red.cogs.Audio.task.callback")
|
||||||
_ = Translator("Audio", Path(__file__))
|
_ = Translator("Audio", Path(__file__))
|
||||||
|
|
||||||
@ -54,55 +56,6 @@ def get_jar_ram_defaults() -> Tuple[str, str]:
|
|||||||
|
|
||||||
MIN_JAVA_RAM, MAX_JAVA_RAM = get_jar_ram_defaults()
|
MIN_JAVA_RAM, MAX_JAVA_RAM = get_jar_ram_defaults()
|
||||||
|
|
||||||
DEFAULT_LAVALINK_YAML = {
|
|
||||||
# The nesting structure of this dict is very important, it's a 1:1 mirror of application.yaml in JSON
|
|
||||||
"yaml__server__address": "localhost",
|
|
||||||
"yaml__server__port": 2333,
|
|
||||||
"yaml__lavalink__server__password": "youshallnotpass",
|
|
||||||
"yaml__lavalink__server__sources__http": True,
|
|
||||||
"yaml__lavalink__server__sources__bandcamp": True,
|
|
||||||
"yaml__lavalink__server__sources__local": True,
|
|
||||||
"yaml__lavalink__server__sources__soundcloud": True,
|
|
||||||
"yaml__lavalink__server__sources__youtube": True,
|
|
||||||
"yaml__lavalink__server__sources__twitch": True,
|
|
||||||
"yaml__lavalink__server__sources__vimeo": True,
|
|
||||||
"yaml__lavalink__server__bufferDurationMs": 400,
|
|
||||||
"yaml__lavalink__server__frameBufferDurationMs": 1000,
|
|
||||||
# 100 pages - 100 entries per page = 10,000 tracks which is the Audio Limit for a single playlist.
|
|
||||||
"yaml__lavalink__server__youtubePlaylistLoadLimit": 100,
|
|
||||||
"yaml__lavalink__server__playerUpdateInterval": 1,
|
|
||||||
"yaml__lavalink__server__youtubeSearchEnabled": True,
|
|
||||||
"yaml__lavalink__server__soundcloudSearchEnabled": True,
|
|
||||||
"yaml__lavalink__server__gc_warnings": True,
|
|
||||||
"yaml__metrics__prometheus__enabled": False,
|
|
||||||
"yaml__metrics__prometheus__endpoint": "/metrics",
|
|
||||||
"yaml__sentry__dsn": "",
|
|
||||||
"yaml__sentry__environment": "",
|
|
||||||
"yaml__logging__file__path": "./logs/",
|
|
||||||
"yaml__logging__level__root": "INFO",
|
|
||||||
"yaml__logging__level__lavalink": "INFO",
|
|
||||||
"yaml__logging__logback__rollingpolicy__max_history": 15,
|
|
||||||
"yaml__logging__logback__rollingpolicy__max_size": "10MB",
|
|
||||||
# plugin configuration - note that the plugin may be disabled by the manager
|
|
||||||
"yaml__plugins__youtube__enabled": True,
|
|
||||||
"yaml__plugins__youtube__allowSearch": True,
|
|
||||||
"yaml__plugins__youtube__allowDirectVideoIds": True,
|
|
||||||
"yaml__plugins__youtube__allowDirectPlaylistIds": True,
|
|
||||||
"yaml__plugins__youtube__clients": [
|
|
||||||
"MUSIC",
|
|
||||||
"WEB",
|
|
||||||
"ANDROID_TESTSUITE",
|
|
||||||
"TVHTML5EMBEDDED",
|
|
||||||
"ANDROID_LITE",
|
|
||||||
"MEDIA_CONNECT",
|
|
||||||
"IOS",
|
|
||||||
],
|
|
||||||
"yaml__plugins__youtube__WEB__playback": True,
|
|
||||||
"yaml__plugins__youtube__TVHTML5EMBEDDED__playlistLoading": False,
|
|
||||||
"yaml__plugins__youtube__TVHTML5EMBEDDED__videoLoading": False,
|
|
||||||
"yaml__plugins__youtube__TVHTML5EMBEDDED__searching": False,
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFAULT_LAVALINK_SETTINGS = {
|
DEFAULT_LAVALINK_SETTINGS = {
|
||||||
"host": DEFAULT_LAVALINK_YAML["yaml__server__address"],
|
"host": DEFAULT_LAVALINK_YAML["yaml__server__address"],
|
||||||
"rest_port": DEFAULT_LAVALINK_YAML["yaml__server__port"],
|
"rest_port": DEFAULT_LAVALINK_YAML["yaml__server__port"],
|
||||||
@ -122,24 +75,6 @@ def sizeof_fmt(num: Union[float, int]) -> str:
|
|||||||
return f"{num:.1f}Y"
|
return f"{num:.1f}Y"
|
||||||
|
|
||||||
|
|
||||||
# This assumes all keys with `_` should be converted from `part1_part2` to `part1-part2`
|
|
||||||
def convert_function(key: str) -> str:
|
|
||||||
return key.replace("_", "-")
|
|
||||||
|
|
||||||
|
|
||||||
def change_dict_naming_convention(data) -> dict:
|
|
||||||
ret: Any = data
|
|
||||||
if isinstance(data, dict):
|
|
||||||
ret = {}
|
|
||||||
for key, value in data.items():
|
|
||||||
ret[convert_function(key)] = change_dict_naming_convention(value)
|
|
||||||
elif isinstance(data, list):
|
|
||||||
ret = []
|
|
||||||
for value in data:
|
|
||||||
ret.append(change_dict_naming_convention(value))
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
class CacheLevel:
|
class CacheLevel:
|
||||||
__slots__ = ("value",)
|
__slots__ = ("value",)
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from redbot.cogs.audio.manager import LavalinkOldVersion, LavalinkVersion
|
from redbot.cogs.audio.managed_node.ll_version import LavalinkOldVersion, LavalinkVersion
|
||||||
|
|
||||||
|
|
||||||
ORDERED_VERSIONS = [
|
ORDERED_VERSIONS = [
|
||||||
Loading…
x
Reference in New Issue
Block a user