Repository navigation
Expand file tree
/
Copy path__init__.py
More file actions
218 lines (165 loc) · 6.37 KB
/
Copy path__init__.py
File metadata and controls
218 lines (165 loc) · 6.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# SPDX-FileCopyrightText: 2020-2026 Nicotine+ Contributors
# SPDX-License-Identifier: GPL-3.0-or-later
__application_name__ = "Nicotine+"
__application_id__ = "org.nicotine_plus.Nicotine"
__version__ = "3.4.0.dev2"
__author__ = "Nicotine+ Team"
__copyright__ = """© 2004–2026 Nicotine+ Contributors
© 2003–2004 Nicotine Contributors
© 2001–2003 PySoulSeek Contributors"""
__website_url__ = "https://nicotine-plus.org"
__privileges_url__ = "https://www.slsknet.org/qtlogin.php?username=%s"
__port_checker_url__ = "https://www.slsknet.org/porttest.php?port=%s"
__issue_tracker_url__ = "https://github.com/nicotine-plus/nicotine-plus/issues"
__translations_url__ = "https://nicotine-plus.org/doc/TRANSLATIONS"
import io
import os
import sys
from pynicotine.config import config
from pynicotine.core import core
from pynicotine.i18n import apply_translations
def check_arguments():
"""Parse command line arguments specified by the user."""
import argparse
parser = argparse.ArgumentParser(
prog="nicotine", description=_("Graphical client for the Soulseek peer-to-peer network"),
epilog=_("Website: %s") % __website_url__, add_help=False
)
# Visible arguments
parser.add_argument(
"-h", "--help", action="help",
help=_("show this help message and exit")
)
parser.add_argument(
"-c", "--config", metavar=_("file"),
help=_("use non-default configuration file")
)
parser.add_argument(
"-u", "--user-data", metavar=_("folder"),
help=_("alternative folder for user data and plugins")
)
parser.add_argument(
"-s", "--hidden", action="store_true",
help=_("start the program without showing window")
)
parser.add_argument(
"-b", "--bindip", metavar=_("address"),
help=_("bind connections to the given IP address")
)
parser.add_argument(
"-l", "--port", metavar=_("port"), type=int,
help=_("listen on the given port")
)
parser.add_argument(
"-r", "--rescan", action="store_true",
help=_("rescan shared files")
)
parser.add_argument(
"-n", "--headless", action="store_true",
help=_("start the program in headless mode (no GUI)")
)
parser.add_argument(
"-v", "--version", action="version", version=f"{__application_name__} {__version__}",
help=_("display version and exit")
)
# Disables critical error dialog; used for integration tests
parser.add_argument("--ci-mode", action="store_true", help=argparse.SUPPRESS)
# Raw version used for CI builds
parser.add_argument(
"--ci-version", action="version", version=__version__,
help=argparse.SUPPRESS
)
# Disables features that require external applications, useful for e.g. Docker containers
parser.add_argument("--isolated", action="store_true", help=argparse.SUPPRESS)
args, unhandled_args = parser.parse_known_args()
gui_argv = None
multi_instance = False
if args.headless:
# Show error about unrecognized arguments immediately
args = parser.parse_args()
else:
# Pass unhandled arguments to GUI, e.g. --gapplication-service
gui_argv = [sys.argv[0], *unhandled_args]
if args.config:
config.set_config_file(args.config)
# Since a custom config was specified, allow another instance of the application to open
multi_instance = True
if args.user_data:
config.set_data_folder(args.user_data)
core.cli_interface_address = args.bindip
core.cli_listen_port = args.port
core.cli_rescanning = args.rescan
return gui_argv, args.hidden, args.ci_mode, args.isolated, args.rescan, multi_instance
def check_python_version():
# Require minimum Python version
python_version = (3, 10)
if sys.version_info < python_version:
return _("""You are using an unsupported version of Python (%(old_version)s).
You should install Python %(min_version)s or newer.""") % {
"old_version": ".".join(str(x) for x in sys.version_info[:3]),
"min_version": ".".join(str(x) for x in python_version)
}
return None
def set_up_python():
if sys.stdout is not None:
# Always use UTF-8 and enable line buffering
sys.stdout.reconfigure(encoding="utf-8", line_buffering=True)
if sys.stderr is not None:
# Always use UTF-8 and enable line buffering
sys.stderr.reconfigure(encoding="utf-8", line_buffering=True)
# Dump tracebacks for C modules (in addition to pure Python code)
import faulthandler
faulthandler.enable()
def rename_process(new_name, debug_info=False):
errors = []
# Renaming ourselves for pkill et al.
try:
import ctypes
# GNU/Linux style
libc = ctypes.CDLL(None)
libc.prctl(15, new_name, 0, 0, 0)
except Exception as error:
errors.append(error)
errors.append("Failed GNU/Linux style")
try:
import ctypes
# BSD style
libc = ctypes.CDLL(None)
libc.setproctitle(new_name)
except Exception as second_error:
errors.append(second_error)
errors.append("Failed BSD style")
if debug_info and errors:
msg = ["Errors occurred while trying to change process name:"]
for i in errors:
msg.append(str(i))
print("\n".join(msg))
def rescan_shares():
exit_code = 0
core.init_components(enabled_components={"signal_handler", "cli", "shares"})
core.start()
if not core.shares.rescan_shares(use_thread=False):
exit_code = 1
core.quit()
return exit_code
def run():
"""Run application and return its exit code."""
set_up_python()
rename_process(b"nicotine")
gui_argv, hidden, ci_mode, isolated_mode, rescan, multi_instance = check_arguments()
error = check_python_version()
if error:
print(error)
return 1
if rescan:
return rescan_shares()
# Initialize GTK-based GUI
if gui_argv:
from pynicotine import gtkgui as application
exit_code = application.run(gui_argv, hidden, ci_mode, isolated_mode, multi_instance)
if exit_code is not None:
return exit_code
# Run without a GUI
from pynicotine import headless as application
return application.run(isolated_mode=isolated_mode, is_fallback=bool(gui_argv))
apply_translations()