Fixed history database

This commit is contained in:
emdee 2022-10-11 16:36:09 +00:00
parent b75aafe638
commit fd7f2620ba
12 changed files with 164 additions and 120 deletions

View file

@ -49,5 +49,7 @@ Toxygen is powerful cross-platform [Tox](https://tox.chat/) client written in pu
This hard-forked from https://github.com/toxygen-project/toxygen This hard-forked from https://github.com/toxygen-project/toxygen
```next_gen``` branch. ```next_gen``` branch.
See ToDo.md to the current ToDo list.
Work on this project is suspended until the Work on this project is suspended until the
[MultiDevice](https://git.plastiras.org/emdee/tox_profile/wiki/MultiDevice-Announcements-POC) problem is solved. Fork me! [MultiDevice](https://git.plastiras.org/emdee/tox_profile/wiki/MultiDevice-Announcements-POC) problem is solved. Fork me!

18
ToDo.md
View file

@ -6,15 +6,16 @@ The code is in there but it's not working.
## Fix Audio ## Fix Audio
The code is in there but it's not working. The code is in there but it's not working. It looks like audio input
I may have broken it trying to wire up the ability to is working but not output. The code is all in there; I may have broken
set the audio device from the command line. it trying to wire up the ability to set the audio device from the
command line.
## Fix Video ## Fix Video
The code is in there but it's not working. The code is in there but it's not working. I may have broken it
I may have broken it trying to wire up the ability to trying to wire up the ability to set the audio device from the command
set the audio device from the command line. line.
## Groups ## Groups
@ -22,9 +23,6 @@ set the audio device from the command line.
```group.peer_id``` The code is broken in places because I have not ```group.peer_id``` The code is broken in places because I have not
seen the path to change from the old API ro the new one. seen the path to change from the old API ro the new one.
2. There is no way to delete a group in the UI
3. Distinguish between Frieds, Groups and Whispers in the UI.
## Plugin system ## Plugin system
@ -34,6 +32,8 @@ set the audio device from the command line.
3. Should the plugins be in toxygen or a separate repo? 3. Should the plugins be in toxygen or a separate repo?
4. There needs to be a uniform way for plugins to wire into callbacks.
## check toxygen_wrapper ## check toxygen_wrapper
1. I've broken out toxygen_wrapper to be standalone, 1. I've broken out toxygen_wrapper to be standalone,

View file

@ -86,29 +86,29 @@ iNODES=8
def setup_logging(oArgs): def setup_logging(oArgs):
global LOG global LOG
logging._defaultFormatter = logging.Formatter(datefmt='%m-%d %H:%M:%S',
fmt='%(levelname)s:%(name)s %(message)s')
logging._defaultFormatter.default_time_format = '%m-%d %H:%M:%S'
logging._defaultFormatter.default_msec_format = ''
if coloredlogs: if coloredlogs:
aKw = dict(level=oArgs.loglevel, aKw = dict(level=oArgs.loglevel,
logger=LOG, logger=LOG,
fmt='%(name)s %(levelname)s %(message)s') fmt='%(name)s %(levelname)s %(message)s')
if oArgs.logfile: aKw['stream'] = sys.stdout
oFd = open(oArgs.logfile, 'wt')
setattr(oArgs, 'log_oFd', oFd)
aKw['stream'] = oFd
coloredlogs.install(**aKw) coloredlogs.install(**aKw)
else: else:
aKw = dict(level=oArgs.loglevel, aKw = dict(level=oArgs.loglevel,
format='%(name)s %(levelname)-4s %(message)s') format='%(name)s %(levelname)-4s %(message)s')
if oArgs.logfile: aKw['stream'] = sys.stdout
aKw['filename'] = oArgs.logfile
logging.basicConfig(**aKw) logging.basicConfig(**aKw)
if not oArgs.logfile:
oHandler = logging.StreamHandler(stream=sys.stdout)
LOG.addHandler(oHandler)
logging._defaultFormatter = logging.Formatter(datefmt='%m-%d %H:%M:%S') if oArgs.logfile:
logging._defaultFormatter.default_time_format = '%m-%d %H:%M:%S' oFd = open(oArgs.logfile, 'wt')
logging._defaultFormatter.default_msec_format = '' setattr(oArgs, 'log_oFd', oFd)
oHandler = logging.StreamHandler(stream=oFd)
LOG.addHandler(oHandler)
LOG.setLevel(oArgs.loglevel) LOG.setLevel(oArgs.loglevel)
LOG.trace = lambda l: LOG.log(0, repr(l)) LOG.trace = lambda l: LOG.log(0, repr(l))
@ -124,7 +124,6 @@ logging.getLogger('PyQt5.uic').setLevel(logging.ERROR)
logging.getLogger('PyQt5.uic.uiparser').setLevel(logging.ERROR) logging.getLogger('PyQt5.uic.uiparser').setLevel(logging.ERROR)
logging.getLogger('PyQt5.uic.properties').setLevel(logging.ERROR) logging.getLogger('PyQt5.uic.properties').setLevel(logging.ERROR)
global iI global iI
iI = 0 iI = 0
@ -155,7 +154,7 @@ class App:
def __init__(self, version, oArgs): def __init__(self, version, oArgs):
global LOG global LOG
self._args = oArgs self._args = oArgs
self.oArgs = oArgs self._oArgs = oArgs
self._path = path_to_profile = oArgs.profile self._path = path_to_profile = oArgs.profile
uri = oArgs.uri uri = oArgs.uri
logfile = oArgs.logfile logfile = oArgs.logfile
@ -179,18 +178,21 @@ class App:
self._group_factory = self._groups_service = self._profile = None self._group_factory = self._groups_service = self._profile = None
if uri is not None and uri.startswith('tox:'): if uri is not None and uri.startswith('tox:'):
self._uri = uri[4:] self._uri = uri[4:]
self._history = None
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Public methods # Public methods
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
def set_trace(self): def set_trace(self):
"""unused"""
LOG.debug('pdb.set_trace ') LOG.debug('pdb.set_trace ')
sys.stdin = sys.__stdin__ sys.stdin = sys.__stdin__
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
import pdb; pdb.set_trace() import pdb; pdb.set_trace()
def ten(self, i=0): def ten(self, i=0):
"""unused"""
global iI global iI
iI += 1 iI += 1
if logging.getLogger('app').getEffectiveLevel() != 10: if logging.getLogger('app').getEffectiveLevel() != 10:
@ -219,11 +221,11 @@ class App:
# this throws everything as errors # this throws everything as errors
if not self._select_and_load_profile(): if not self._select_and_load_profile():
return 2 return 2
if hasattr(self._args, 'update') and self._args.update: if hasattr(self._oArgs, 'update') and self._oArgs.update:
if self._try_to_update(): return 3 if self._try_to_update(): return 3
self._load_app_styles() self._load_app_styles()
if self._args.language != 'English': if self._oArgs.language != 'English':
# > /var/local/src/toxygen/toxygen/app.py(303)_load_app_translations()->None # > /var/local/src/toxygen/toxygen/app.py(303)_load_app_translations()->None
# -> self._app.translator = translator # -> self._app.translator = translator
# (Pdb) Fatal Python error: Segmentation fault # (Pdb) Fatal Python error: Segmentation fault
@ -271,26 +273,34 @@ class App:
def quit(self, retval=0): def quit(self, retval=0):
LOG.debug("quit") LOG.debug("quit")
oArgs = self._args self._stop_app()
if hasattr(oArgs, 'log_oFd'):
oArgs.log_oFd.close()
delattr(oArgs, 'log_oFd')
# failsafe: segfaults on exit # failsafe: segfaults on exit
if hasattr(self, '_tox'): if hasattr(self, '_tox'):
if self._tox and hasattr(self._tox, 'kill'): if self._tox and hasattr(self._tox, 'kill'):
LOG.debug(f"quit: Killing {self._tox}")
self._tox.kill() self._tox.kill()
del self._tox del self._tox
self._stop_app()
if hasattr(self, '_app'): if hasattr(self, '_app'):
self._app.quit() self._app.quit()
del self._app.quit del self._app.quit
del self._app del self._app
sys.stderr.write('quit raising SystemExit' +'\n')
# hanging on gevents
# Thread 1 "python3.9" received signal SIGSEGV, Segmentation fault.
#44 0x00007ffff7fb2f93 in () at /usr/lib/python3.9/site-packages/greenlet/_greenlet.cpython-39-x86_64-linux-gnu.so
#45 0x00007ffff7fb31ef in () at /usr/lib/python3.9/site-packages/greenlet/_greenlet.cpython-39-x86_64-linux-gnu.so
#46 0x00007ffff452165c in hb_shape_plan_create_cached2 () at /usr/lib64/libharfbuzz.so.0
raise SystemExit(retval) raise SystemExit(retval)
def _stop_app(self): def _stop_app(self):
LOG.debug("_stop_app") LOG.debug("_stop_app")
self._save_profile()
#? self._history.save_history()
self._plugin_loader.stop() self._plugin_loader.stop()
try: try:
self._stop_threads(is_app_closing=True) self._stop_threads(is_app_closing=True)
@ -299,31 +309,38 @@ class App:
pass pass
if hasattr(self, '_tray') and self._tray: if hasattr(self, '_tray') and self._tray:
self._tray.hide() self._tray.hide()
self._save_profile()
self._settings.close() self._settings.close()
LOG.debug(f"stop_app: Killing {self._tox}")
self._kill_toxav() self._kill_toxav()
self._kill_tox() self._kill_tox()
sys.stderr.write('_stop_app end' +'\n') del self._tox
oArgs = self._oArgs
if hasattr(oArgs, 'log_oFd'):
LOG.debug(f"Closing {oArgs.log_oFd}")
oArgs.log_oFd.close()
delattr(oArgs, 'log_oFd')
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# App loading # App loading
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
def _load_base_style(self): def _load_base_style(self):
if self._args.theme in ['', 'default']: return if self._oArgs.theme in ['', 'default']: return
if qdarkstyle: if qdarkstyle:
LOG.debug("_load_base_style qdarkstyle " +self._args.theme) LOG.debug("_load_base_style qdarkstyle " +self._oArgs.theme)
# QDarkStyleSheet # QDarkStyleSheet
if self._args.theme == 'light': if self._oArgs.theme == 'light':
from qdarkstyle.light.palette import LightPalette from qdarkstyle.light.palette import LightPalette
style = qdarkstyle.load_stylesheet(palette=LightPalette) style = qdarkstyle.load_stylesheet(palette=LightPalette)
else: else:
from qdarkstyle.dark.palette import DarkPalette from qdarkstyle.dark.palette import DarkPalette
style = qdarkstyle.load_stylesheet(palette=DarkPalette) style = qdarkstyle.load_stylesheet(palette=DarkPalette)
else: else:
LOG.debug("_load_base_style qss " +self._args.theme) LOG.debug("_load_base_style qss " +self._oArgs.theme)
name = self._args.theme + '.qss' name = self._oArgs.theme + '.qss'
with open(util.join_path(util.get_styles_directory(), name)) as fl: with open(util.join_path(util.get_styles_directory(), name)) as fl:
style = fl.read() style = fl.read()
style += '\n' +sSTYLE style += '\n' +sSTYLE
@ -337,9 +354,9 @@ class App:
if self._settings['theme'] != theme: if self._settings['theme'] != theme:
continue continue
if qdarkstyle: if qdarkstyle:
LOG.debug("_load_base_style qdarkstyle " +self._args.theme) LOG.debug("_load_base_style qdarkstyle " +self._oArgs.theme)
# QDarkStyleSheet # QDarkStyleSheet
if self._args.theme == 'light': if self._oArgs.theme == 'light':
from qdarkstyle.light.palette import LightPalette from qdarkstyle.light.palette import LightPalette
style = qdarkstyle.load_stylesheet(palette=LightPalette) style = qdarkstyle.load_stylesheet(palette=LightPalette)
else: else:
@ -356,7 +373,7 @@ class App:
LOG.debug('_load_app_styles: loading theme file ' + file_path) LOG.debug('_load_app_styles: loading theme file ' + file_path)
style += '\n' +sSTYLE style += '\n' +sSTYLE
self._app.setStyleSheet(style) self._app.setStyleSheet(style)
LOG.info('_load_app_styles: loaded theme ' +self._args.theme) LOG.info('_load_app_styles: loaded theme ' +self._oArgs.theme)
break break
def _load_login_screen_translations(self): def _load_login_screen_translations(self):
@ -503,7 +520,7 @@ class App:
def _select_profile(self): def _select_profile(self):
LOG.debug("_select_profile") LOG.debug("_select_profile")
if self._args.language != 'English': if self._oArgs.language != 'English':
self._load_login_screen_translations() self._load_login_screen_translations()
ls = LoginScreen() ls = LoginScreen()
profiles = ProfileManager.find_profiles() profiles = ProfileManager.find_profiles()
@ -542,7 +559,7 @@ class App:
util_ui.tr('Error')) util_ui.tr('Error'))
return False return False
name = profile_name or 'toxygen_user' name = profile_name or 'toxygen_user'
assert self._args assert self._oArgs
self._path = profile_path self._path = profile_path
if result.password: if result.password:
self._toxes.set_password(result.password) self._toxes.set_password(result.password)
@ -644,7 +661,7 @@ class App:
def _create_dependencies(self): def _create_dependencies(self):
LOG.info(f"_create_dependencies toxygen version {self._version}") LOG.info(f"_create_dependencies toxygen version {self._version}")
if hasattr(self._args, 'update') and self._args.update: if hasattr(self._oArgs, 'update') and self._oArgs.update:
self._backup_service = BackupService(self._settings, self._backup_service = BackupService(self._settings,
self._profile_manager) self._profile_manager)
self._smiley_loader = SmileyLoader(self._settings) self._smiley_loader = SmileyLoader(self._settings)
@ -697,9 +714,11 @@ class App:
self._ms, self._ms,
self._profile_manager, self._profile_manager,
self._contacts_provider, self._contacts_provider,
history, self._tox_dns, history,
self._tox_dns,
messages_items_factory) messages_items_factory)
history.set_contacts_manager(self._contacts_manager) history.set_contacts_manager(self._contacts_manager)
self._history = history
self._calls_manager = CallsManager(self._tox.AV, self._calls_manager = CallsManager(self._tox.AV,
self._settings, self._settings,
self._ms, self._ms,
@ -754,14 +773,14 @@ class App:
self._ms.show() self._ms.show()
# FixMe: # FixMe:
self._log = lambda line: LOG.log(self._args.loglevel, self._log = lambda line: LOG.log(self._oArgs.loglevel,
self._ms.status(line)) self._ms.status(line))
self._ms._log = self._log # used in callbacks.py self._ms._log = self._log # used in callbacks.py
self.LOG = self._log # backwards self.LOG = self._log # backwards
if False: if False:
self.status_handler = logging.Handler() self.status_handler = logging.Handler()
self.status_handler.setLevel(logging.INFO) # self._args.loglevel self.status_handler.setLevel(logging.INFO) # self._oArgs.loglevel
self.status_handler.handle = self._ms.status self.status_handler.handle = self._ms.status
self._init_callbacks() self._init_callbacks()
@ -780,9 +799,9 @@ class App:
def _create_tox(self, data, settings_): def _create_tox(self, data, settings_):
LOG.info("_create_tox calling tox_factory") LOG.info("_create_tox calling tox_factory")
assert self._args assert self._oArgs
retval = tox_factory(data=data, settings=settings_, retval = tox_factory(data=data, settings=settings_,
args=self._args, app=self) args=self._oArgs, app=self)
LOG.debug("_create_tox succeeded") LOG.debug("_create_tox succeeded")
self._tox = retval self._tox = retval
return retval return retval
@ -807,12 +826,12 @@ class App:
self._profile.reset_avatar(self._settings['identicons']) self._profile.reset_avatar(self._settings['identicons'])
def _kill_toxav(self): def _kill_toxav(self):
LOG.debug("_kill_toxav") # LOG_debug("_kill_toxav")
self._calls_manager.set_toxav(None) self._calls_manager.set_toxav(None)
self._tox.AV.kill() self._tox.AV.kill()
def _kill_tox(self): def _kill_tox(self):
LOG.debug("_kill_tox") # LOG.debug("_kill_tox")
self._tox.kill() self._tox.kill()
def loop(self, n): def loop(self, n):
@ -832,17 +851,17 @@ class App:
def test_net(self, lElts=None, oThread=None, iMax=4): def test_net(self, lElts=None, oThread=None, iMax=4):
LOG.debug("test_net " +self._args.network) LOG.debug("test_net " +self._oArgs.network)
# bootstrap # bootstrap
LOG.debug('Calling generate_nodes: udp') LOG.debug('Calling generate_nodes: udp')
lNodes = ts.generate_nodes(oArgs=self._args, lNodes = ts.generate_nodes(oArgs=self._oArgs,
ipv='ipv4', ipv='ipv4',
udp_not_tcp=True) udp_not_tcp=True)
self._settings['current_nodes_udp'] = lNodes self._settings['current_nodes_udp'] = lNodes
if not lNodes: if not lNodes:
LOG.warn('empty generate_nodes udp') LOG.warn('empty generate_nodes udp')
LOG.debug('Calling generate_nodes: tcp') LOG.debug('Calling generate_nodes: tcp')
lNodes = ts.generate_nodes(oArgs=self._args, lNodes = ts.generate_nodes(oArgs=self._oArgs,
ipv='ipv4', ipv='ipv4',
udp_not_tcp=False) udp_not_tcp=False)
self._settings['current_nodes_tcp'] = lNodes self._settings['current_nodes_tcp'] = lNodes
@ -850,8 +869,8 @@ class App:
LOG.warn('empty generate_nodes tcp') LOG.warn('empty generate_nodes tcp')
# if oThread and oThread._stop_thread: return # if oThread and oThread._stop_thread: return
LOG.debug("test_net network=" +self._args.network +' iMax=' +str(iMax)) LOG.debug("test_net network=" +self._oArgs.network +' iMax=' +str(iMax))
if self._args.network not in ['local', 'localnew', 'newlocal']: if self._oArgs.network not in ['local', 'localnew', 'newlocal']:
b = ts.bAreWeConnected() b = ts.bAreWeConnected()
if b is None: if b is None:
i = os.system('ip route|grep ^def') i = os.system('ip route|grep ^def')
@ -860,22 +879,22 @@ class App:
else: else:
b = True b = True
if not b: if not b:
LOG.warn("No default route for network " +self._args.network) LOG.warn("No default route for network " +self._oArgs.network)
text = 'You have no default route - are you connected?' text = 'You have no default route - are you connected?'
reply = util_ui.question(text, "Are you connected?") reply = util_ui.question(text, "Are you connected?")
if not reply: return if not reply: return
iMax = 1 iMax = 1
else: else:
LOG.debug("Have default route for network " +self._args.network) LOG.debug("Have default route for network " +self._oArgs.network)
LOG.debug(f"test_net {self._args.network} iMax= {iMax}") LOG.debug(f"test_net {self._oArgs.network} iMax= {iMax}")
i = 0 i = 0
while i < iMax: while i < iMax:
# if oThread and oThread._stop_thread: return # if oThread and oThread._stop_thread: return
i = i + 1 i = i + 1
LOG.debug(f"bootstrapping status # {i}") LOG.debug(f"bootstrapping status # {i}")
self._test_bootstrap(self._settings['current_nodes_udp']) self._test_bootstrap(self._settings['current_nodes_udp'])
if hasattr(self._args, 'proxy_type') and self._args.proxy_type > 0: if hasattr(self._oArgs, 'proxy_type') and self._oArgs.proxy_type > 0:
LOG.debug(f"relaying status # {i}") LOG.debug(f"relaying status # {i}")
self._test_relays(self._settings['current_nodes_tcp']) self._test_relays(self._settings['current_nodes_tcp'])
status = self._tox.self_get_connection_status() status = self._tox.self_get_connection_status()
@ -906,16 +925,14 @@ class App:
+_settings['proxy_host'] +':' \ +_settings['proxy_host'] +':' \
+str(_settings['proxy_port'])) +str(_settings['proxy_port']))
lElts = _settings['current_nodes_tcp'] lElts = _settings['current_nodes_tcp']
LOG.debug(f"test_env {len(lElts)}") # LOG.debug(f"test_env {len(lElts)}")
return env return env
def _test_bootstrap(self, lElts=None): def _test_bootstrap(self, lElts=None):
env = self._test_env()
if lElts is None: if lElts is None:
lElts = self._settings['current_nodes_udp'] lElts = self._settings['current_nodes_udp']
shuffle(lElts) shuffle(lElts)
LOG.debug(f"_test_bootstrap #Elts={len(lElts)}") LOG.debug(f"_test_bootstrap #Elts={len(lElts)}")
shuffle(lElts)
ts.bootstrap_good(lElts[:iNODES], [self._tox]) ts.bootstrap_good(lElts[:iNODES], [self._tox])
LOG.info("Connected status: " +repr(self._tox.self_get_connection_status())) LOG.info("Connected status: " +repr(self._tox.self_get_connection_status()))
@ -962,10 +979,10 @@ class App:
text = 'Run the Extended Test Suite?\nThe program may freeze for 20-60 minutes.' text = 'Run the Extended Test Suite?\nThe program may freeze for 20-60 minutes.'
reply = util_ui.question(text, title) reply = util_ui.question(text, title)
if reply: if reply:
if hasattr(self._args, 'proxy_type') and self._args.proxy_type: if hasattr(self._oArgs, 'proxy_type') and self._oArgs.proxy_type:
lArgs = ['--proxy_host', self._args.proxy_host, lArgs = ['--proxy_host', self._oArgs.proxy_host,
'--proxy_port', str(self._args.proxy_port), '--proxy_port', str(self._oArgs.proxy_port),
'--proxy_type', str(self._args.proxy_type), ] '--proxy_type', str(self._oArgs.proxy_type), ]
else: else:
lArgs = list() lArgs = list()
try: try:

View file

@ -18,6 +18,18 @@ from middleware.threads import BaseThread
global LOG global LOG
import logging import logging
LOG = logging.getLogger('app.'+__name__) LOG = logging.getLogger('app.'+__name__)
# callbacks can be called in any thread so were being careful
def LOG_ERROR(l): print('EROR< '+l)
def LOG_WARN(l): print('WARN< '+l)
def LOG_INFO(l):
bIsVerbose = hasattr(__builtins__, 'app') and app.oArgs.loglevel <= 20-1
if bIsVerbose: print('INFO< '+l)
def LOG_DEBUG(l):
bIsVerbose = hasattr(__builtins__, 'app') and app.oArgs.loglevel <= 10-1
if bIsVerbose: print('DBUG< '+l)
def LOG_TRACE(l):
bIsVerbose = hasattr(__builtins__, 'app') and app.oArgs.loglevel < 10-1
pass # print('TRACE+ '+l)
TIMER_TIMEOUT = 30.0 TIMER_TIMEOUT = 30.0
bSTREAM_CALLBACK = False bSTREAM_CALLBACK = False
@ -27,6 +39,7 @@ class AV(common.tox_save.ToxAvSave):
def __init__(self, toxav, settings): def __init__(self, toxav, settings):
super().__init__(toxav) super().__init__(toxav)
self._toxav = toxav
self._settings = settings self._settings = settings
self._running = True self._running = True
s = settings s = settings
@ -62,7 +75,10 @@ class AV(common.tox_save.ToxAvSave):
self._video_width = 320 self._video_width = 320
self._video_height = 240 self._video_height = 240
iOutput = self._settings._args.audio['output'] # was iOutput = self._settings._args.audio['output']
iInput = self._settings['audio']['input']
self.lPaSampleratesI = ts.lSdSamplerates(iInput)
iOutput = self._settings['audio']['output']
self.lPaSampleratesO = ts.lSdSamplerates(iOutput) self.lPaSampleratesO = ts.lSdSamplerates(iOutput)
global oPYA global oPYA
oPYA = self._audio = pyaudio.PyAudio() oPYA = self._audio = pyaudio.PyAudio()
@ -180,33 +196,35 @@ class AV(common.tox_save.ToxAvSave):
def start_audio_thread(self): def start_audio_thread(self):
""" """
Start audio sending Start audio sending
from a callback
""" """
global oPYA global oPYA
iInput = self._settings._args.audio['input'] # was iInput = self._settings._args.audio['input']
iInput = self._settings['audio']['input']
if self._audio_thread is not None: if self._audio_thread is not None:
LOG.warn(f"start_audio_thread device={iInput}") LOG_WARN(f"start_audio_thread device={iInput}")
return return
iInput = self._settings._args.audio['input'] LOG_DEBUG(f"start_audio_thread device={iInput}")
LOG.debug(f"start_audio_thread device={iInput}")
lPaSamplerates = ts.lSdSamplerates(iInput) lPaSamplerates = ts.lSdSamplerates(iInput)
if not(len(lPaSamplerates)): if not(len(lPaSamplerates)):
e = f"No supported sample rates for device: audio[input]={iInput!r}" e = f"No supported sample rates for device: audio[input]={iInput!r}"
LOG.error(f"No supported sample rates {e}") LOG_ERROR(f"start_audio_thread {e}")
raise RuntimeError(e) #?? dunno - cancel call?
return
if not self._audio_rate_pa in lPaSamplerates: if not self._audio_rate_pa in lPaSamplerates:
LOG.warn(f"{self._audio_rate_pa} not in {lPaSamplerates!r}") LOG_WARN(f"{self._audio_rate_pa} not in {lPaSamplerates!r}")
if False: if False:
self._audio_rate_pa = oPYA.get_device_info_by_index(iInput)['defaultSampleRate'] self._audio_rate_pa = oPYA.get_device_info_by_index(iInput)['defaultSampleRate']
else: else:
LOG.warn(f"Setting audio_rate to: {lPaSamplerates[0]}") LOG_WARN(f"Setting audio_rate to: {lPaSamplerates[0]}")
self._audio_rate_pa = lPaSamplerates[0] self._audio_rate_pa = lPaSamplerates[0]
try: try:
LOG.debug( f"start_audio_thread framerate: {self._audio_rate_pa}" \ LOG_DEBUG( f"start_audio_thread framerate: {self._audio_rate_pa}" \
+f" device: {iInput}" +f" device: {iInput}"
+f" supported: {lPaSamplerates!r}") +f" supported: {lPaSamplerates!r}")
if self._audio_rate_pa not in lPaSamplerates: if self._audio_rate_pa not in lPaSamplerates:
LOG.warn(f"PAudio sampling rate was {self._audio_rate_pa} changed to {lPaSamplerates[0]}") LOG_WARN(f"PAudio sampling rate was {self._audio_rate_pa} changed to {lPaSamplerates[0]}")
self._audio_rate_pa = lPaSamplerates[0] self._audio_rate_pa = lPaSamplerates[0]
if bSTREAM_CALLBACK: if bSTREAM_CALLBACK:
@ -244,10 +262,12 @@ class AV(common.tox_save.ToxAvSave):
input=True, input=True,
input_device_index=iInput, input_device_index=iInput,
frames_per_buffer=self._audio_sample_count_pa * 10))) frames_per_buffer=self._audio_sample_count_pa * 10)))
# catcher in place in calls_manager # catcher in place in calls_manager? not if from a callback
raise RuntimeError(e) # calls_manager._call.toxav_call_state_cb(friend_number, mask)
# raise RuntimeError(e)
return
else: else:
LOG.debug(f"start_audio_thread {self._audio_stream!r}") LOG_DEBUG(f"start_audio_thread {self._audio_stream!r}")
def stop_audio_thread(self): def stop_audio_thread(self):
@ -339,7 +359,8 @@ class AV(common.tox_save.ToxAvSave):
""" """
if self._out_stream is None: if self._out_stream is None:
iOutput = self._settings._args.audio['output'] # was iOutput = self._settings._args.audio['output']
iOutput = self._settings['audio']['output']
if not rate in self.lPaSampleratesO: if not rate in self.lPaSampleratesO:
LOG.warn(f"{rate} not in {self.lPaSampleratesO!r}") LOG.warn(f"{rate} not in {self.lPaSampleratesO!r}")
if False: if False:
@ -362,7 +383,8 @@ class AV(common.tox_save.ToxAvSave):
self.stop() self.stop()
return return
LOG.debug(f"audio_chunk output_device_index={self._settings._args.audio['input']} rate={rate} channels={channels_count}") iOutput = self._settings['audio']['output']
LOG.debug(f"audio_chunk output_device_index={iOutput} rate={rate} channels={channels_count}")
self._out_stream.write(samples) self._out_stream.write(samples)
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------

View file

@ -16,7 +16,8 @@ LOG = logging.getLogger('app.'+__name__)
class CallsManager: class CallsManager:
def __init__(self, toxav, settings, main_screen, contacts_manager, app=None): def __init__(self, toxav, settings, main_screen, contacts_manager, app=None):
self._call = av.calls.AV(toxav, settings) # object with data about calls self._callav = av.calls.AV(toxav, settings) # object with data about calls
self._call = self._callav
self._call_widgets = {} # dict of incoming call widgets self._call_widgets = {} # dict of incoming call widgets
self._incoming_calls = set() self._incoming_calls = set()
self._settings = settings self._settings = settings
@ -27,7 +28,7 @@ class CallsManager:
self._app = app self._app = app
def set_toxav(self, toxav): def set_toxav(self, toxav):
self._call.set_toxav(toxav) self._callav.set_toxav(toxav)
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Events # Events
@ -52,13 +53,13 @@ class CallsManager:
num = self._contacts_manager.get_active_number() num = self._contacts_manager.get_active_number()
if not self._contacts_manager.is_active_a_friend(): if not self._contacts_manager.is_active_a_friend():
return return
if num not in self._call and self._contacts_manager.is_active_online(): # start call if num not in self._callav and self._contacts_manager.is_active_online(): # start call
if not self._settings['audio']['enabled']: if not self._settings['audio']['enabled']:
return return
self._call(num, audio, video) self._callav(num, audio, video)
self._main_screen.active_call() self._main_screen.active_call()
self._call_started_event(num, audio, video, True) self._call_started_event(num, audio, video, True)
elif num in self._call: # finish or cancel call if you call with active friend elif num in self._callav: # finish or cancel call if you call with active friend
self.stop_call(num, False) self.stop_call(num, False)
def incoming_call(self, audio, video, friend_number): def incoming_call(self, audio, video, friend_number):
@ -89,7 +90,7 @@ class CallsManager:
sys.stdout.flush() sys.stdout.flush()
try: try:
self._call.call_accept_call(friend_number, audio, video) self._callav.call_accept_call(friend_number, audio, video)
except Exception as e: except Exception as e:
LOG.error(f"accept_call _call.accept_call ERROR for {friend_number} {e}") LOG.error(f"accept_call _call.accept_call ERROR for {friend_number} {e}")
self._main_screen.call_finished() self._main_screen.call_finished()
@ -139,14 +140,14 @@ class CallsManager:
else: else:
is_declined = False is_declined = False
self._main_screen.call_finished() self._main_screen.call_finished()
self._call.finish_call(friend_number, by_friend) # finish or decline call self._callav.finish_call(friend_number, by_friend) # finish or decline call
if friend_number in self._call_widgets: if friend_number in self._call_widgets:
self._call_widgets[friend_number].close() self._call_widgets[friend_number].close()
del self._call_widgets[friend_number] del self._call_widgets[friend_number]
def destroy_window(): def destroy_window():
#??? FixMed #??? FixMed
is_video = self._call.is_video_call(friend_number) is_video = self._callav.is_video_call(friend_number)
if is_video: if is_video:
import cv2 import cv2
cv2.destroyWindow(str(friend_number)) cv2.destroyWindow(str(friend_number))
@ -155,8 +156,8 @@ class CallsManager:
self._call_finished_event(friend_number, is_declined) self._call_finished_event(friend_number, is_declined)
def friend_exit(self, friend_number): def friend_exit(self, friend_number):
if friend_number in self._call: if friend_number in self._callav:
self._call.finish_call(friend_number, True) self._callav.finish_call(friend_number, True)
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Private methods # Private methods

View file

@ -28,7 +28,7 @@ class BaseContact:
self._kind = kind self._kind = kind
self._status, self._widget = None, widget self._status, self._widget = None, widget
self._tox_id = tox_id self._tox_id = tox_id
self._name_changed_event = event.Event() self._name_changed_event = event.Event()
self._status_message_changed_event = event.Event() self._status_message_changed_event = event.Event()
self._status_changed_event = event.Event() self._status_changed_event = event.Event()

View file

@ -93,7 +93,7 @@ class ContactsManager(ToxSave):
return False return False
if not self._contacts[self._active_contact]: if not self._contacts[self._active_contact]:
return False return False
return self._contacts[self._active_contact].tox_id == contact.tox_id return self._contacts[self._active_contact].tox_id == contact.tox_id
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
@ -198,7 +198,7 @@ class ContactsManager(ToxSave):
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Filtration # Filtration
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
def filtration_and_sorting(self, sorting=0, filter_str=''): def filtration_and_sorting(self, sorting=0, filter_str=''):
""" """
Filtration of friends list Filtration of friends list
@ -245,7 +245,7 @@ class ContactsManager(ToxSave):
else: else:
self._contacts = sorted(self._contacts, key=lambda x: x.name.lower()) self._contacts = sorted(self._contacts, key=lambda x: x.name.lower())
# change item widgets # change item widgets
for index, contact in enumerate(self._contacts): for index, contact in enumerate(self._contacts):
list_item = self._screen.friends_list.item(index) list_item = self._screen.friends_list.item(index)

View file

@ -53,7 +53,7 @@ class GroupsService(tox_save.ToxSave):
try: try:
group_number = self._tox.group_join(chat_id, password, nick, status) group_number = self._tox.group_join(chat_id, password, nick, status)
assert type(group_number) == int, group_number assert type(group_number) == int, group_number
assert group_number < UINT32_MAX, group_number assert group_number < UINT32_MAX, group_number
except Exception as e: except Exception as e:
# gui # gui
title = f"join_gc_by_id {chat_id}" title = f"join_gc_by_id {chat_id}"
@ -73,7 +73,7 @@ class GroupsService(tox_save.ToxSave):
return return
group.status = constants.TOX_USER_STATUS['NONE'] group.status = constants.TOX_USER_STATUS['NONE']
self._contacts_manager.update_filtration() self._contacts_manager.update_filtration()
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Groups reconnect and leaving # Groups reconnect and leaving
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------

View file

@ -6,20 +6,16 @@ import utils.util as util
# LOG=util.log # LOG=util.log
global LOG global LOG
import logging import logging
LOG = logging.getLogger('app.'+__name__) LOG = logging.getLogger('app.db')
log = lambda x: LOG.info(x)
TIMEOUT = 11 TIMEOUT = 11
SAVE_MESSAGES = 500 SAVE_MESSAGES = 500
MESSAGE_AUTHOR = { MESSAGE_AUTHOR = {
'ME': 0, 'ME': 0,
'FRIEND': 1, 'FRIEND': 1,
'NOT_SENT': 2, 'NOT_SENT': 2,
'GC_PEER': 3 'GC_PEER': 3
} }
CONTACT_TYPE = { CONTACT_TYPE = {
'FRIEND': 0, 'FRIEND': 0,
'GC_PEER': 1, 'GC_PEER': 1,
@ -54,6 +50,7 @@ class Database:
except Exception as ex: except Exception as ex:
LOG.error('Db writing error: ' +path +' ' + str(ex)) LOG.error('Db writing error: ' +path +' ' + str(ex))
os.remove(path) os.remove(path)
LOG.info('Db opened: ' +path)
# ----------------------------------------------------------------------------------------------------------------- # -----------------------------------------------------------------------------------------------------------------
# Public methods # Public methods
@ -75,6 +72,7 @@ class Database:
data = self._toxes.pass_encrypt(data) data = self._toxes.pass_encrypt(data)
with open(new_path, 'wb') as fout: with open(new_path, 'wb') as fout:
fout.write(data) fout.write(data)
LOG.info('Db exported: ' +new_path)
def add_friend_to_db(self, tox_id): def add_friend_to_db(self, tox_id):
db = self._connect() db = self._connect()
@ -91,11 +89,12 @@ class Database:
db.commit() db.commit()
return True return True
except Exception as e: except Exception as e:
LOG("ERROR: " +self._name +' Database exception! ' +str(e)) LOG.error("dd_friend_to_db " +self._name +' Database exception! ' +str(e))
db.rollback() db.rollback()
return False return False
finally: finally:
db.close() db.close()
LOG.debug(f"add_friend_to_db {tox_id}")
def delete_friend_from_db(self, tox_id): def delete_friend_from_db(self, tox_id):
db = self._connect() db = self._connect()
@ -105,11 +104,12 @@ class Database:
db.commit() db.commit()
return True return True
except Exception as e: except Exception as e:
LOG("ERROR: " +self._name +' Database exception! ' +str(e)) LOG.error("delete_friend_from_db " +self._name +' Database exception! ' +str(e))
db.rollback() db.rollback()
return False return False
finally: finally:
db.close() db.close()
LOG.debug(f"delete_friend_from_db {tox_id}")
def save_messages_to_db(self, tox_id, messages_iter): def save_messages_to_db(self, tox_id, messages_iter):
db = self._connect() db = self._connect()
@ -117,15 +117,16 @@ class Database:
cursor = db.cursor() cursor = db.cursor()
cursor.executemany('INSERT INTO id' + tox_id + cursor.executemany('INSERT INTO id' + tox_id +
'(message, author_name, author_type, unix_time, message_type) ' + '(message, author_name, author_type, unix_time, message_type) ' +
'VALUES (?, ?, ?, ?, ?, ?);', messages_iter) 'VALUES (?, ?, ?, ?, ?);', messages_iter)
db.commit() db.commit()
return True return True
except Exception as e: except Exception as e:
LOG("ERROR: " +self._name +' Database exception! ' +str(e)) LOG.error("" +self._name +' Database exception! ' +str(e))
db.rollback() db.rollback()
return False return False
finally: finally:
db.close() db.close()
LOG.debug(f"save_messages_to_db {tox_id}")
def update_messages(self, tox_id, message_id): def update_messages(self, tox_id, message_id):
db = self._connect() db = self._connect()
@ -136,11 +137,12 @@ class Database:
db.commit() db.commit()
return True return True
except Exception as e: except Exception as e:
LOG("ERROR: " +self._name +' Database exception! ' +str(e)) LOG.error("" +self._name +' Database exception! ' +str(e))
db.rollback() db.rollback()
return False return False
finally: finally:
db.close() db.close()
LOG.debug(f"update_messages {tox_id}")
def delete_message(self, tox_id, unique_id): def delete_message(self, tox_id, unique_id):
db = self._connect() db = self._connect()
@ -150,11 +152,12 @@ class Database:
db.commit() db.commit()
return True return True
except Exception as e: except Exception as e:
LOG("ERROR: " +self._name +' Database exception! ' +str(e)) LOG.error("" +self._name +' Database exception! ' +str(e))
db.rollback() db.rollback()
return False return False
finally: finally:
db.close() db.close()
LOG.debug(f"delete_message {tox_id}")
def delete_messages(self, tox_id): def delete_messages(self, tox_id):
db = self._connect() db = self._connect()
@ -164,11 +167,12 @@ class Database:
db.commit() db.commit()
return True return True
except Exception as e: except Exception as e:
LOG("ERROR: " +self._name +' Database exception! ' +str(e)) LOG.error("" +self._name +' Database exception! ' +str(e))
db.rollback() db.rollback()
return False return False
finally: finally:
db.close() db.close()
LOG.debug(f"delete_messages {tox_id}")
def messages_getter(self, tox_id): def messages_getter(self, tox_id):
self.add_friend_to_db(tox_id) self.add_friend_to_db(tox_id)

View file

@ -1,7 +1,6 @@
# -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- # -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-
from history.history_logs_generators import * from history.history_logs_generators import *
class History: class History:
def __init__(self, contact_provider, db, settings, main_screen, messages_items_factory): def __init__(self, contact_provider, db, settings, main_screen, messages_items_factory):
@ -28,7 +27,7 @@ class History:
Save history to db Save history to db
""" """
# me a mistake? was _db not _history # me a mistake? was _db not _history
if self._settings['save_history'] or self._settings['save_db']: if self._settings['save_history']:
for friend in self._contact_provider.get_all_friends(): for friend in self._contact_provider.get_all_friends():
self._db.add_friend_to_db(friend.tox_id) self._db.add_friend_to_db(friend.tox_id)
if not self._settings['save_unsent_only']: if not self._settings['save_unsent_only']:

View file

@ -21,7 +21,7 @@ iMAX = 70
try: try:
# https://github.com/pyqtconsole/pyqtconsole # https://github.com/pyqtconsole/pyqtconsole
from pyqtconsole.console import PythonConsole from pyqtconsole.console import PythonConsole
import pyqtconsole.highlighter as hl import pyqtconsole.highlighter as hl
except Exception as e: except Exception as e:
LOG.warn(e) LOG.warn(e)
PythonConsole = None PythonConsole = None
@ -39,12 +39,12 @@ else:
_format.setFontWeight(QFont.Bold) _format.setFontWeight(QFont.Bold)
if 'italic' in style: if 'italic' in style:
_format.setFontItalic(True) _format.setFontItalic(True)
_fgcolor = QColor() _fgcolor = QColor()
_fgcolor.setNamedColor('white') _fgcolor.setNamedColor('white')
_format.setForeground(_fgcolor) _format.setForeground(_fgcolor)
return _format return _format
aFORMATS = { aFORMATS = {
'keyword': hl.format('blue', 'bold'), 'keyword': hl.format('blue', 'bold'),
'operator': hl.format('red'), 'operator': hl.format('red'),
@ -59,7 +59,7 @@ else:
'outprompt': hl.format('darkRed', 'bold'), 'outprompt': hl.format('darkRed', 'bold'),
} }
class QTextEditLogger(logging.Handler): class QTextEditLogger(logging.Handler):
def __init__(self, parent, app): def __init__(self, parent, app):
super().__init__() super().__init__()
@ -194,7 +194,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.actionAdd_friend = QtWidgets.QAction(window) self.actionAdd_friend = QtWidgets.QAction(window)
self.actionAdd_friend.setObjectName("actionAdd_friend") self.actionAdd_friend.setObjectName("actionAdd_friend")
self.actionProfile_settings = QtWidgets.QAction(window) self.actionProfile_settings = QtWidgets.QAction(window)
self.actionProfile_settings.setObjectName("actionProfile_settings") self.actionProfile_settings.setObjectName("actionProfile_settings")
self.actionPrivacy_settings = QtWidgets.QAction(window) self.actionPrivacy_settings = QtWidgets.QAction(window)
@ -621,10 +621,10 @@ class MainWindow(QtWidgets.QMainWindow):
else: else:
size = 12 size = 12
font_name = "Courier New" font_name = "Courier New"
size = font_width = 10 size = font_width = 10
font_name = "DejaVu Sans Mono" font_name = "DejaVu Sans Mono"
try: try:
if not self._pe: if not self._pe:
self._pe = PythonConsole(sFont=font_name, self._pe = PythonConsole(sFont=font_name,

View file

@ -360,7 +360,6 @@ class Settings(dict):
'y': 400, 'y': 400,
'message_font_size': 14, 'message_font_size': 14,
'unread_color': 'red', 'unread_color': 'red',
'save_unsent_only': False,
'compact_mode': False, 'compact_mode': False,
'identicons': True, 'identicons': True,
'show_welcome_screen': True, 'show_welcome_screen': True,