Skip to content

Commit

Permalink
Fix command line arguments and detect duplicate sessions (#8089)
Browse files Browse the repository at this point in the history
  • Loading branch information
egbertbouman authored Aug 13, 2024
2 parents 3240c60 + f740f77 commit fd56b6c
Show file tree
Hide file tree
Showing 18 changed files with 217 additions and 113 deletions.
63 changes: 47 additions & 16 deletions src/run_tribler.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import pystray
import tribler
from aiohttp import ClientSession
from PIL import Image
from tribler.core.session import Session
from tribler.tribler_config import TriblerConfigManager
Expand Down Expand Up @@ -50,6 +51,19 @@ def get_root_state_directory(requested_path: os.PathLike | None) -> Path:
return root_state_dir


async def start_download(config: TriblerConfigManager, server_url: str, torrent_uri: str) -> None:
"""
Start a download by calling the REST API.
"""
async with ClientSession() as client, client.put(server_url + "/api/downloads",
headers={"X-Api-Key": config.get("api/key")},
json={"uri": torrent_uri}) as response:
if response.status == 200:
logger.info("Successfully started torrent %s", torrent_uri)
else:
logger.warning("Failed to start torrent %s: %s", torrent_uri, await response.text())


async def main() -> None:
"""
The main script entry point.
Expand All @@ -60,38 +74,55 @@ async def main() -> None:

root_state_dir = get_root_state_directory(os.environ.get('TSTATEDIR', 'state_directory'))
logger.info("Root state dir: %s", root_state_dir)

api_port, api_key = int(os.environ.get('CORE_API_PORT', '0')), os.environ.get('CORE_API_KEY')

config = TriblerConfigManager(root_state_dir / "configuration.json")
config.set("state_dir", str(root_state_dir))

if config.get("api/refresh_port_on_start"):
config.set("api/http_port", 0)
config.set("api/https_port", 0)

if api_key is None and config.get("api/key") is None:
api_key = os.urandom(16).hex()

if api_key is not None and api_key != config.get("api/key"):
config.set("api/key", api_key)
if "CORE_API_PORT" in os.environ:
config.set("api/http_port", int(os.environ.get("CORE_API_PORT")))
config.write()

if api_port is not None and api_port != config.get("api/http_port"):
config.set("api/http_port", api_port)
if "CORE_API_KEY" in os.environ:
config.set("api/key", os.environ.get("CORE_API_KEY"))
config.write()

logger.info("Start tribler core. API port: %d. API key: %s.", api_port, config.get("api/key"))
if config.get("api/key") is None:
config.set("api/key", os.urandom(16).hex())
config.write()

logger.info("Creating session. API port: %d. API key: %s.", config.get("api/http_port"), config.get("api/key"))
session = Session(config)

torrent_uri = parsed_args.get('torrent')
if torrent_uri and os.path.exists(torrent_uri):
if torrent_uri.endswith(".torrent"):
torrent_uri = Path(torrent_uri).as_uri()
if torrent_uri.endswith(".magnet"):
torrent_uri = Path(torrent_uri).read_text()
server_url = await session.find_api_server()

if server_url:
logger.info("Core already running at %s", server_url)
if torrent_uri:
logger.info("Starting torrent using existing core")
await start_download(config, server_url, torrent_uri)
webbrowser.open_new_tab(server_url + f"?key={config.get('api/key')}")
logger.info("Shutting down")
return

await session.start()

server_url = await session.find_api_server()
if server_url and torrent_uri:
await start_download(config, server_url, torrent_uri)

image_path = Path(tribler.__file__).parent / "ui/public/tribler.png"
image = Image.open(image_path.resolve())
url = f"http://localhost:{session.rest_manager.get_api_port()}/ui/#/downloads/all?key={config.get('api/key')}"
api_port = session.rest_manager.get_api_port()
url = f"http://{config.get('api/http_host')}:{api_port}/ui/#/downloads/all?key={config.get('api/key')}"
menu = (pystray.MenuItem('Open', lambda: webbrowser.open_new_tab(url)),
pystray.MenuItem('Quit', lambda: session.shutdown_event.set()))
icon = pystray.Icon("Tribler", icon=image, title="Tribler", menu=menu)
webbrowser.open_new_tab(url)
threading.Thread(target=icon.run).start()

await session.shutdown_event.wait()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,10 @@ def from_defaults(settings: TriblerConfigManager) -> DownloadConfig:
defaults.validate(Validator())
config = DownloadConfig(defaults)

config.set_hops(int(settings.get("libtorrent/download_defaults/number_hops")))
if settings.get("libtorrent/download_defaults/anonymity_enabled"):
config.set_hops(int(settings.get("libtorrent/download_defaults/number_hops")))
else:
config.set_hops(0)
config.set_safe_seeding(settings.get("libtorrent/download_defaults/safeseeding_enabled"))
config.set_dest_dir(settings.get("libtorrent/download_defaults/saveas"))

Expand Down
12 changes: 6 additions & 6 deletions src/tribler/core/libtorrent/restapi/downloads_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,14 @@ def create_dconfig_from_params(self, parameters: dict) -> tuple[DownloadConfig,
"""
download_config = DownloadConfig.from_defaults(self.download_manager.config)

anon_hops = parameters.get('anon_hops', 0)
anon_hops = parameters.get('anon_hops')
safe_seeding = bool(parameters.get('safe_seeding', 0))

if anon_hops > 0 and not safe_seeding:
return None, "Cannot set anonymous download without safe seeding enabled"

if anon_hops >= 0:
download_config.set_hops(anon_hops)
if anon_hops is not None:
if anon_hops > 0 and not safe_seeding:
return None, "Cannot set anonymous download without safe seeding enabled"
if anon_hops >= 0:
download_config.set_hops(anon_hops)

if safe_seeding:
download_config.set_safe_seeding(True)
Expand Down
8 changes: 8 additions & 0 deletions src/tribler/core/restapi/rest_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@ async def start_http_site(self, runner: web.AppRunner) -> None:
str(e))
raise

current_port = api_port or self.site._server.sockets[0].getsockname()[1] # noqa: SLF001
self.config.set("api/http_port_running", current_port)
self.config.write()

self._logger.info("HTTP REST API server started on port %d", self.get_api_port())

async def start_https_site(self, runner: web.AppRunner) -> None:
Expand All @@ -257,6 +261,10 @@ async def start_https_site(self, runner: web.AppRunner) -> None:
await self.site_https.start()
self._logger.info("Started HTTPS REST API: %s", self.site_https.name)

current_port = port or self.site_https._server.sockets[0].getsockname()[1] # noqa: SLF001
self.config.set("api/https_port_running", current_port)
self.config.write()

async def stop(self) -> None:
"""
Clean up all the REST endpoints and connections.
Expand Down
27 changes: 27 additions & 0 deletions src/tribler/core/session.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from __future__ import annotations

import asyncio
import logging
from asyncio import Event
from contextlib import contextmanager
from typing import TYPE_CHECKING, Generator

import aiohttp
from ipv8.loader import IPv8CommunityLoader
from ipv8_service import IPv8

Expand Down Expand Up @@ -74,6 +76,15 @@ def rust_enhancements(session: Session) -> Generator[None, None, None]:
if_specs[i]["worker_threads"] = previous_value


async def _is_url_available(url: str, timeout: int=1) -> bool:
async with aiohttp.ClientSession() as session:
try:
async with session.get(url, timeout=timeout):
return True
except asyncio.TimeoutError:
return False


class Session:
"""
A session manager that manages all components.
Expand Down Expand Up @@ -159,6 +170,22 @@ async def start(self) -> None:
if self.config.get("statistics"):
self.rest_manager.get_endpoint("/api/ipv8").endpoints["/overlays"].enable_overlay_statistics(True, None, True)

async def find_api_server(self) -> str | None:
"""
Find the API server, if available.
"""
if port := self.config.get("api/http_port_running"):
http_url = f'http://{self.config.get("api/http_host")}:{port}'
if await _is_url_available(http_url):
return http_url

if port := self.config.get("api/https_port_running"):
https_url = f'https://{self.config.get("api/https_host")}:{port}'
if await _is_url_available(https_url):
return https_url

return None

async def shutdown(self) -> None:
"""
Shut down all connections and components.
Expand Down
5 changes: 3 additions & 2 deletions src/tribler/tribler_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class ApiConfig(TypedDict):
https_enabled: bool
https_host: str
https_port: int
refresh_port_on_start: bool


class ContentDiscoveryCommunityConfig(TypedDict):
Expand Down Expand Up @@ -166,7 +165,9 @@ class TriblerConfig(TypedDict):
"https_host": "127.0.0.1",
"https_port": 0,
"https_certfile": "https_certfile",
"refresh_port_on_start": True
# Ports currently in-use. Used by run_tribler.py to detect duplicate sessions.
"http_port_running": 0,
"https_port_running": 0,
},

"ipv8": ipv8_default_config,
Expand Down
3 changes: 2 additions & 1 deletion src/tribler/ui/public/locales/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,6 @@
"Socks5": "Socks5",
"Socks5Auth": "Socks5 with authentication",
"HTTP": "HTTP",
"HTTPAuth": "HTTP with authentication"
"HTTPAuth": "HTTP with authentication",
"WebServerSettings": "Web server settings"
}
52 changes: 26 additions & 26 deletions src/tribler/ui/public/locales/es_ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
"Infohash": "Información del hash",
"Downloads": "Descargas",
"AddTorrent": "Añadir torrent",
"All": "TODO",
"Downloading": "DESCARGANDO",
"Completed": "COMPLETADO",
"Active": "ACTIVO",
"Inactive": "INACTIVO",
"All": "Todo",
"Downloading": "Descargando",
"Completed": "Completado",
"Active": "Activo",
"Inactive": "Inactivo",
"Popular": "Populares",
"Settings": "Configuración",
"General": "GENERAL",
"Connection": "CONEXIÓN",
"Bandwidth": "ANCHO DE BANDA",
"Seeding": "SEMBRADO",
"Anonymity": "ANONIMATO",
"General": "General",
"Connection": "Conexión",
"Bandwidth": "Ancho de banda",
"Seeding": "Sembrado",
"Anonymity": "Anonimato",
"Debug": "Depurar",
"ImportTorrentFile": "Importar torrent desde archivo",
"ImportTorrentURL": "Importar un torrent desde un magnet/URL",
Expand Down Expand Up @@ -59,16 +59,15 @@
"Size": "Tamaño",
"Created": "Creado",
"Status": "Estado",
"Status": "ESTADO",
"Seeds": "SEMILLAS",
"Peers": "PARES",
"SpeedDown": "VELOCIDAD (BAJADA)",
"SpeedUp": "VELOCIDAD (SUBIDA)",
"Ratio": "RATIO",
"Anonymous": "¿ANÓNIMOS?",
"Hops": "SALTOS",
"ETA": "TIEMPO",
"AddedOn": "AÑADIDO EL",
"Seeds": "Semillas",
"Peers": "Pares",
"SpeedDown": "Velocidad (bajada)",
"SpeedUp": "Velocidad (subida)",
"Ratio": "Ratio",
"Anonymous": "¿Anónimos?",
"Hops": "Saltos",
"ETA": "Tiempo",
"AddedOn": "Añadido el",
"ForceRecheck": "Forzar nueva verificación",
"ExportTorrent": "Exportar archivo torrent",
"MoveStorage": "Establecer destino",
Expand All @@ -87,10 +86,10 @@
"Availability": "Disponibilidad",
"Files": "Archivos",
"Trackers": "Rastreadores",
"PeerIpPort": "PARES (IP/PUERTO)",
"Completed": "COMPLETADO",
"Flags": "BANDERAS",
"Client": "CLIENTE",
"PeerIpPort": "Pares (IP/puerto)",
"Completed": "Completado",
"Flags": "Banderas",
"Client": "Cliente",
"SeedersLeechers": "{{seeders, number}} sembradores, {{leechers, number}} recolectores",
"NoResults": "No hay resultados.",
"GotoFirst": "Ir a la primera página",
Expand Down Expand Up @@ -118,11 +117,12 @@
"ChangeStorageLocation": "Ubicación",
"ChangeStorageButton": "Mover almacenamiento",
"Actions": "Comportamiento",
"CreateTorrentButton": "CREAR TORRENT",
"CreateTorrentButton": "Crear torrent",
"None": "Ninguno",
"Socks4": "Socks4",
"Socks5": "Socks5",
"Socks5Auth": "Socks5 con autenticación",
"HTTP": "HTTP",
"HTTPAuth": "HTTP con autenticación"
"HTTPAuth": "HTTP con autenticación",
"WebServerSettings": "Configurações do servidor web"
}
Loading

0 comments on commit fd56b6c

Please sign in to comment.