Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/6700 #6738

Merged
merged 5 commits into from
Jan 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 10 additions & 109 deletions src/run_tribler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@
# A fix for "LookupError: unknown encoding: idna" error.
# Adding encodings.idna to hiddenimports is not enough.
# https://github.com/pyinstaller/pyinstaller/issues/1113
# noinspection PyUnresolvedReferences
import encodings.idna # pylint: disable=unused-import
import logging.config
import os
import sys

from tribler_common.logger import load_logger_config
from tribler_common.process_checker import ProcessChecker
from tribler_common.sentry_reporter.sentry_reporter import SentryStrategy
from tribler_common.sentry_reporter.sentry_scrubber import SentryScrubber
from tribler_common.version_manager import VersionHistory

from tribler_core import start_core
from tribler_core.components.reporter.exception_handler import default_core_exception_handler

logger = logging.getLogger(__name__)
Expand All @@ -25,6 +23,7 @@ class RunTriblerArgsParser(argparse.ArgumentParser):
def __init__(self, *args, **kwargs):
kwargs['description'] = 'Run Tribler BitTorrent client'
super().__init__(*args, **kwargs)
self.add_argument('torrent', help='torrent file to download', default='', nargs='?')
self.add_argument('--core', action="store_true")
self.add_argument('--gui-test-mode', action="store_true")

Expand Down Expand Up @@ -65,6 +64,7 @@ def init_boot_logger():
init_sentry_reporter()

parsed_args = RunTriblerArgsParser().parse_args()
logger.info(f'Run Tribler: {parsed_args}')

# Get root state directory (e.g. from environment variable or from system default)
from tribler_common.osutils import get_root_state_directory
Expand All @@ -77,109 +77,10 @@ def init_boot_logger():

# Check whether we need to start the core or the user interface
if parsed_args.core:
from tribler_core.check_os import should_kill_other_tribler_instances

should_kill_other_tribler_instances(root_state_dir)
logger.info('Running Core' + ' in gui_test_mode' if parsed_args.gui_test_mode else '')
load_logger_config('tribler-core', root_state_dir)

# Check if we are already running a Tribler instance
process_checker = ProcessChecker(root_state_dir)
if process_checker.already_running:
logger.info('Core is already running, exiting')
sys.exit(1)
process_checker.create_lock_file()
version_history = VersionHistory(root_state_dir)
state_dir = version_history.code_version.directory
try:
start_core.run_tribler_core(api_port, api_key, state_dir, gui_test_mode=parsed_args.gui_test_mode)
finally:
logger.info('Remove lock file')
process_checker.remove_lock_file()
from tribler_core.start_core import run_core

else:
from PyQt5.QtCore import QSettings
from tribler_gui.utilities import get_translator

logger.info('Running GUI' + ' in gui_test_mode' if parsed_args.gui_test_mode else '')

# Workaround for macOS Big Sur, see https://github.com/Tribler/tribler/issues/5728
if sys.platform == "darwin":
logger.info('Enabling a workaround for macOS Big Sur')
os.environ["QT_MAC_WANTS_LAYER"] = "1"

# Set up logging
load_logger_config('tribler-gui', root_state_dir)

from tribler_core.check_os import (
check_and_enable_code_tracing,
check_environment,
check_free_space,
enable_fault_handler,
error_and_exit,
)
from tribler_core.exceptions import TriblerException

try:
# Enable tracer using commandline args: --trace-debug or --trace-exceptions
trace_logger = check_and_enable_code_tracing('gui', root_state_dir)

enable_fault_handler(root_state_dir)

# Exit if we cant read/write files, etc.
check_environment()

check_free_space()

from tribler_gui.tribler_app import TriblerApplication
from tribler_gui.tribler_window import TriblerWindow

app_name = os.environ.get('TRIBLER_APP_NAME', 'triblerapp')
app = TriblerApplication(app_name, sys.argv)

# ACHTUNG! translator MUST BE created and assigned to a separate variable
# BEFORE calling installTranslator on app. Otherwise, it won't work for some reason
settings = QSettings('nl.tudelft.tribler')
translator = get_translator(settings.value('translation', None))
app.installTranslator(translator)

if app.is_running():
logger.info('Application is running')
for arg in sys.argv[1:]:
if os.path.exists(arg) and arg.endswith(".torrent"):
app.send_message(f"file:{arg}")
elif arg.startswith('magnet'):
app.send_message(arg)

sys.exit(1)

logger.info('Start Tribler Window')
window = TriblerWindow(settings,
root_state_dir,
api_port=api_port,
api_key=api_key,
run_core=True)
window.setWindowTitle("Tribler")
app.set_activation_window(window)
app.parse_sys_args(sys.argv)
sys.exit(app.exec_())

except ImportError as ie:
logger.exception(ie)
error_and_exit("Import Error", f"Import error: {ie}")

except TriblerException as te:
logger.exception(te)
error_and_exit("Tribler Exception", f"{te}")

except SystemExit:
logger.info("Shutting down Tribler")
if trace_logger:
trace_logger.close()

# Flush all the logs to make sure it is written to file before it exits
for handler in logging.getLogger().handlers:
handler.flush()

default_core_exception_handler.sentry_reporter.global_strategy = SentryStrategy.SEND_SUPPRESSED
raise
run_core(api_port, api_key, root_state_dir, parsed_args)
else: # GUI
from tribler_gui.start_gui import run_gui

run_gui(api_port, api_key, root_state_dir, parsed_args)
45 changes: 45 additions & 0 deletions src/tribler-common/tribler_common/rest_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import os
from typing import Any, Union

from yarl import URL

MAGNET_SCHEME = 'magnet'
FILE_SCHEME = 'file'
HTTP_SCHEME = 'http'
HTTPS_SCHEME = 'https'


def path_to_uri(file_path: Union[str, Any]) -> str:
"""Convert path to url

Example:
'/path/to/file' -> 'file:///path/to/file'
"""
if not isinstance(file_path, str):
file_path = str(file_path)
return str(URL().build(scheme=FILE_SCHEME, path=file_path))


def uri_to_path(file_uri: str) -> str:
"""Convert uri to path

Example:
'file:///path/to/file' -> '/path/to/file'
"""
path = URL(file_uri).path
if os.name == 'nt':
# Removes first slash for win OS
# see https://github.com/aio-libs/yarl/issues/674
return path.lstrip('/')
return path


def scheme_from_uri(uri: str) -> str:
"""Get scheme from URI

Examples:
'file:///some/file' -> 'file'
'magnet:link' -> 'magnet'
'http://en.wikipedia.org' -> 'http'
"""
return URL(uri).scheme
53 changes: 53 additions & 0 deletions src/tribler-common/tribler_common/tests/test_rest_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from unittest.mock import patch

import pytest

from tribler_common.rest_utils import path_to_uri, scheme_from_uri, uri_to_path

NIX_PATHS = [
('/path/to/file', 'file:///path/to/file'),
('/path/to/file with space', 'file:///path/to/file%20with%20space'),
('/path/to/%20%21file', 'file:///path/to/%2520%2521file'), # See: https://github.com/Tribler/tribler/issues/6700
]

WIN_PATHS = [
('C:\\path\\to\\file', 'file:///C:%5Cpath%5Cto%5Cfile'),
('C:\\path\\to\\file with space', 'file:///C:%5Cpath%5Cto%5Cfile%20with%20space'),
('C:\\path\\to\\%20%21file', 'file:///C:%5Cpath%5Cto%5C%2520%2521file'),
]

SCHEMES = [
('file:///path/to/file', 'file'),
('magnet:link', 'magnet'),
('http://en.wikipedia.org', 'http'),
]

# posix
@pytest.mark.parametrize('path, uri', NIX_PATHS)
@patch('os.name', 'posix')
def test_path_to_uri(path, uri):
assert path_to_uri(path) == uri


@pytest.mark.parametrize('path, uri', NIX_PATHS)
@patch('os.name', 'posix')
def test_uri_to_path(path, uri):
assert uri_to_path(uri) == path


# win
@pytest.mark.parametrize('path, uri', WIN_PATHS)
@patch('os.name', 'nt')
def test_path_to_uri_win(path, uri):
assert path_to_uri(path) == uri


@pytest.mark.parametrize('path, uri', WIN_PATHS)
@patch('os.name', 'nt')
def test_uri_to_path_win(path, uri):
assert uri_to_path(uri) == path


@pytest.mark.parametrize('path, scheme', SCHEMES)
def test_scheme_from_uri(path, scheme):
assert scheme_from_uri(path) == scheme
8 changes: 1 addition & 7 deletions src/tribler-common/tribler_common/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
from pathlib import Path
from unittest.mock import MagicMock, patch

from tribler_common.patch_import import patch_import
from tribler_common.utilities import Query, extract_tags, parse_query, show_system_popup, to_fts_query, uri_to_path
from tribler_common.utilities import Query, extract_tags, parse_query, show_system_popup, to_fts_query

# pylint: disable=import-outside-toplevel, import-error
# fmt: off

def test_uri_to_path():
path = Path(__file__).parent / "bla%20foo.bar"
uri = path.as_uri()
assert uri_to_path(uri) == path


def test_to_fts_query():
assert to_fts_query(None) is None
Expand Down
11 changes: 0 additions & 11 deletions src/tribler-common/tribler_common/utilities.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import itertools
import os
import platform
import re
import sys
from dataclasses import dataclass, field
from typing import Set, Tuple
from urllib.parse import urlparse
from urllib.request import url2pathname

from tribler_core.utilities.path_util import Path


def is_frozen():
Expand All @@ -23,12 +18,6 @@ def is_frozen():
return True


def uri_to_path(uri):
parsed = urlparse(uri)
host = "{0}{0}{mnt}{0}".format(os.path.sep, mnt=parsed.netloc)
return Path(host) / url2pathname(parsed.path)


fts_query_re = re.compile(r'\w+', re.UNICODE)
tags_re = re.compile(r'#[^\s^#]{3,50}(?=[#\s]|$)')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@
from ipv8.taskmanager import TaskManager, task

from tribler_common.network_utils import default_network_utils
from tribler_common.rest_utils import (
FILE_SCHEME,
HTTPS_SCHEME,
HTTP_SCHEME,
MAGNET_SCHEME,
scheme_from_uri,
uri_to_path,
)
from tribler_common.simpledefs import DLSTATUS_SEEDING, MAX_LIBTORRENT_RATE_LIMIT, NTFY, STATEDIR_CHECKPOINT_DIR
from tribler_common.utilities import uri_to_path

from tribler_core.components.libtorrent.download_manager.dht_health_manager import DHTHealthManager
from tribler_core.components.libtorrent.download_manager.download import Download
Expand Down Expand Up @@ -505,10 +512,12 @@ def _map_call_on_ltsessions(self, hops, funcname, *args, **kwargs):
getattr(self.get_session(hops), funcname)(*args, **kwargs)

async def start_download_from_uri(self, uri, config=None):
if uri.startswith("http"):
scheme = scheme_from_uri(uri)

if scheme in (HTTP_SCHEME, HTTPS_SCHEME):
tdef = await TorrentDef.load_from_url(uri)
return self.start_download(tdef=tdef, config=config)
if uri.startswith("magnet:"):
if scheme == MAGNET_SCHEME:
name, infohash, _ = parse_magnetlink(uri)
if infohash is None:
raise RuntimeError("Missing infohash")
Expand All @@ -517,9 +526,9 @@ async def start_download_from_uri(self, uri, config=None):
else:
tdef = TorrentDefNoMetainfo(infohash, "Unknown name" if name is None else name, url=uri)
return self.start_download(tdef=tdef, config=config)
if uri.startswith("file:"):
argument = uri_to_path(uri)
return self.start_download(torrent_file=argument, config=config)
if scheme == FILE_SCHEME:
file = uri_to_path(uri)
return self.start_download(torrent_file=file, config=config)
raise Exception("invalid uri")

def start_download(self, torrent_file=None, tdef=None, config=None, checkpoint_disabled=False, hidden=False):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from tribler_common.rest_utils import path_to_uri

from tribler_core.components.base import Component
from tribler_core.components.key.key_component import KeyComponent
from tribler_core.components.libtorrent.download_manager.download_manager import DownloadManager
Expand Down Expand Up @@ -33,7 +35,7 @@ async def run(self):

if config.gui_test_mode:
from tribler_core.tests.tools.common import TORRENT_WITH_DIRS # pylint: disable=import-outside-toplevel
uri = f"file:{TORRENT_WITH_DIRS}"
uri = path_to_uri(TORRENT_WITH_DIRS)
await self.download_manager.start_download_from_uri(uri)

async def shutdown(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,10 @@ def get_files_info_json(download):
"and should only be used in situations where this data is required. "
)
async def get_downloads(self, request):
get_peers = request.query.get('get_peers', '0') == '1'
get_pieces = request.query.get('get_pieces', '0') == '1'
get_files = request.query.get('get_files', '0') == '1'
params = request.query
get_peers = params.get('get_peers', '0') == '1'
get_pieces = params.get('get_pieces', '0') == '1'
get_files = params.get('get_files', '0') == '1'

downloads_json = []
downloads = self.download_manager.get_downloads()
Expand Down Expand Up @@ -373,16 +374,17 @@ async def get_downloads(self, request):
'location, a magnet link or a HTTP(S) url.'),
}))
async def add_download(self, request):
parameters = await request.json()
if not parameters.get('uri'):
params = await request.json()
uri = params.get('uri')
if not uri:
return RESTResponse({"error": "uri parameter missing"}, status=HTTP_BAD_REQUEST)

download_config, error = DownloadsEndpoint.create_dconfig_from_params(parameters)
download_config, error = DownloadsEndpoint.create_dconfig_from_params(params)
if error:
return RESTResponse({"error": error}, status=HTTP_BAD_REQUEST)

try:
download = await self.download_manager.start_download_from_uri(parameters['uri'], config=download_config)
download = await self.download_manager.start_download_from_uri(uri, config=download_config)
except Exception as e:
return RESTResponse({"error": str(e)}, status=HTTP_INTERNAL_SERVER_ERROR)

Expand Down
Loading