diff --git a/toxygen/av/calls.py b/toxygen/av/calls.py index b290798..92f7611 100644 --- a/toxygen/av/calls.py +++ b/toxygen/av/calls.py @@ -21,6 +21,7 @@ LOG = logging.getLogger('app.'+__name__) TIMER_TIMEOUT = 30.0 bSTREAM_CALLBACK = False +iFPS = 25 class AV(common.tox_save.ToxAvSave): @@ -56,7 +57,7 @@ class AV(common.tox_save.ToxAvSave): self._video = None self._video_thread = None - self._video_running = False + self._video_running = None self._video_width = 320 self._video_height = 240 @@ -278,12 +279,7 @@ class AV(common.tox_save.ToxAvSave): self._video_width = s['video']['width'] self._video_height = s['video']['height'] - LOG.info("start_video_thread " \ - +f" device: {s['video']['device']}" \ - +f" supported: {s['video']['width']} {s['video']['height']}") - - s['video']['device'] = -1 - if s['video']['device'] == -1: + if True or s['video']['device'] == -1: self._video = screen_sharing.DesktopGrabber(s['video']['x'], s['video']['y'], s['video']['width'], @@ -291,11 +287,24 @@ class AV(common.tox_save.ToxAvSave): else: with ts.ignoreStdout(): import cv2 + if s['video']['device'] == 0: + # webcam + self._video = cv2.VideoCapture(s['video']['device'], cv2.DSHOW) + else: self._video = cv2.VideoCapture(s['video']['device']) - self._video.set(cv2.CAP_PROP_FPS, 25) - self._video.set(cv2.CAP_PROP_FRAME_WIDTH, self._video_width) - self._video.set(cv2.CAP_PROP_FRAME_HEIGHT, self._video_height) - + self._video.set(cv2.CAP_PROP_FPS, iFPS) + self._video.set(cv2.CAP_PROP_FRAME_WIDTH, self._video_width) + self._video.set(cv2.CAP_PROP_FRAME_HEIGHT, self._video_height) +# self._video.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')) + if self._video is None: + LOG.error("start_video_thread " \ + +f" device: {s['video']['device']}" \ + +f" supported: {s['video']['width']} {s['video']['height']}") + return + LOG.info("start_video_thread " \ + +f" device: {s['video']['device']}" \ + +f" supported: {s['video']['width']} {s['video']['height']}") + self._video_running = True self._video_thread = BaseThread(target=self.send_video, name='_video_thread') @@ -345,15 +354,15 @@ class AV(common.tox_save.ToxAvSave): output_device_index=iOutput, output=True) except Exception as e: - LOG.error(f"Error playing audio_chunk creating self._out_stream {e}") - LOG.debug(f"audio_chunk output_device_index={self._settings._args.audio['input']} rate={rate} channels={channels_count}") - invoke_in_main_thread(util_ui.message_box, + LOG.error(f"Error playing audio_chunk creating self._out_stream {e}") + invoke_in_main_thread(util_ui.message_box, str(e), util_ui.tr("Error Chunking audio")) - # dunno - self.stop() - return + # dunno + self.stop() + return + LOG.debug(f"audio_chunk output_device_index={self._settings._args.audio['input']} rate={rate} channels={channels_count}") self._out_stream.write(samples) # ----------------------------------------------------------------------------------------------------------------- @@ -410,29 +419,42 @@ class AV(common.tox_save.ToxAvSave): """ This method sends video to friends """ - LOG.debug(f"send_video thread={threading.current_thread()}" + LOG.debug(f"send_video thread={threading.current_thread().name}" +f" self._video_running={self._video_running}" +f" device: {self._settings['video']['device']}" ) while self._video_running: try: result, frame = self._video.read() - if result: - LOG.warn(f"send_video video_send_frame _video.read") + if not result: + LOG.warn(f"send_video video_send_frame _video.read result={result}") + break + if frame is None: + LOG.warn(f"send_video video_send_frame _video.read result={result} frame={frame}") + continue else: + LOG.debug(f"send_video video_send_frame _video.read result={result}") height, width, channels = frame.shape + friends = [] for friend_num in self._calls: if self._calls[friend_num].out_video: - try: - y, u, v = self.convert_bgr_to_yuv(frame) - self._toxav.video_send_frame(friend_num, width, height, y, u, v) - except Exception as e: - LOG.debug(f"send_video video_send_frame ERROR {e}") - pass + friends.append(friend_num) + if len(friends) == 0: + LOG.warn(f"send_video video_send_frame no friends") + else: + LOG.debug(f"send_video video_send_frame {friends}") + friend_num = friends[0] + try: + y, u, v = self.convert_bgr_to_yuv(frame) + self._toxav.video_send_frame(friend_num, width, height, y, u, v) + except Exception as e: + LOG.debug(f"send_video video_send_frame ERROR {e}") + pass - except: + except Exception as e: + LOG.error(f"send_video video_send_frame {e}") pass - sleep(0.1) + sleep( 1.0/iFPS) def convert_bgr_to_yuv(self, frame): """ diff --git a/toxygen/contacts/contacts_manager.py b/toxygen/contacts/contacts_manager.py index c0ec665..1c6c0f7 100644 --- a/toxygen/contacts/contacts_manager.py +++ b/toxygen/contacts/contacts_manager.py @@ -1,4 +1,7 @@ # -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- + +import traceback + from contacts.friend import Friend from contacts.group_chat import GroupChat from messenger.messages import * @@ -11,6 +14,8 @@ import logging LOG = logging.getLogger('app.'+__name__) log = lambda x: LOG.info(x) +UINT32_MAX = 2 ** 32 -1 + class ContactsManager(ToxSave): """ Represents contacts list. @@ -21,6 +26,7 @@ class ContactsManager(ToxSave): super().__init__(tox) self._settings = settings self._screen = screen + self._ms = screen self._profile_manager = profile_manager self._contact_provider = contact_provider self._tox_dns = tox_dns @@ -34,6 +40,8 @@ class ContactsManager(ToxSave): self._history = history self._load_contacts() + def _log(self, s): self._ms(s) + def get_contact(self, num): if num < 0 or num >= len(self._contacts): return None @@ -106,6 +114,7 @@ class ContactsManager(ToxSave): current_contact.curr_text = self._screen.messageEdit.toPlainText() except: pass + # IndexError: list index out of range contact = self._contacts[value] self._subscribe_to_events(contact) contact.remove_invalid_unsent_files() @@ -137,7 +146,7 @@ class ContactsManager(ToxSave): except Exception as ex: # no friend found. ignore LOG.warn(f"no friend found. Friend value: {value!s}") LOG.error('in set active: ' + str(ex)) - raise + # gulp raise active_contact = property(get_active, set_active) @@ -322,7 +331,7 @@ class ContactsManager(ToxSave): Block user with specified tox id (or public key) - delete from friends list and ignore friend requests """ tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] - if tox_id == self._tox.self_get_address[:TOX_PUBLIC_KEY_SIZE * 2]: + if tox_id == self._tox.self_get_address()[:TOX_PUBLIC_KEY_SIZE * 2]: return if tox_id not in self._settings['blocked']: self._settings['blocked'].append(tox_id) @@ -424,26 +433,36 @@ class ContactsManager(ToxSave): """ try: message = message or 'Hello! Add me to your contact list please' - if '@' in tox_id: # value like groupbot@toxme.io - tox_id = self._tox_dns.lookup(tox_id) - if tox_id is None: - raise Exception('TOX DNS lookup failed') if len(tox_id) == TOX_PUBLIC_KEY_SIZE * 2: # public key self.add_friend(tox_id) - title = util_ui.tr('Friend added') - text = util_ui.tr('Friend added without sending friend request') - util_ui.message_box(text, title) + title = 'Friend added' + text = 'Friend added without sending friend request' else: - self._tox.friend_add(tox_id, message.encode('utf-8')) - tox_id = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] - self._add_friend(tox_id) - self.update_filtration() - self.save_profile() - return True + num = self._tox.friend_add(tox_id, message.encode('utf-8')) + if num < UINT32_MAX: + tox_pk = tox_id[:TOX_PUBLIC_KEY_SIZE * 2] + self._add_friend(tox_pk) + self.update_filtration() + title = 'Friend added' + text = 'Friend added by sending friend request' + self.save_profile() + retval = True + else: + title = 'Friend failed' + text = 'Friend failed sending friend request' + retval = text + except Exception as ex: # wrong data - LOG.error('Friend request failed with ' + str(ex)) - return str(ex) - + title = 'Friend add exception' + text = 'Friend request exception with ' + str(ex) + self._log(text) + LOG.error(traceback.format_exc()) + retval = str(ex) + title = util_ui.tr(title) + text = util_ui.tr(text) + util_ui.message_box(text, title) + return retval + def process_friend_request(self, tox_id, message): """ Accept or ignore friend request diff --git a/toxygen/images/icon.xcf b/toxygen/images/icon.xcf new file mode 100644 index 0000000..b9fae66 Binary files /dev/null and b/toxygen/images/icon.xcf differ diff --git a/toxygen/main.py b/toxygen/main.py index a9785c9..027e3d6 100644 --- a/toxygen/main.py +++ b/toxygen/main.py @@ -29,7 +29,6 @@ from user_data.settings import * from user_data.settings import Settings from user_data import settings import utils.util as util -from tests import omain with ts.ignoreStderr(): import pyaudio @@ -95,7 +94,7 @@ def setup_audio(oArgs): global oPYA audio = setup_default_audio() for k,v in audio['input_devices'].items(): - if v == 'default' and 'input' not in audio : + if v == 'default' and 'input' not in audio: audio['input'] = k if v == getattr(oArgs, 'audio_input'): audio['input'] = k @@ -272,7 +271,7 @@ def main_parser(): parser.add_argument('--download_nodes_url', type=str, default='https://nodes.tox.chat/json') parser.add_argument('--network', type=str, - choices=['main', 'new', 'local', 'newlocal'], + choices=['old', 'new', 'local', 'newlocal'], default='new') parser.add_argument('--video_input', type=str, default=-1, @@ -303,43 +302,34 @@ def main_parser(): # clean out the unchanged settings so these can override the profile lKEEP_SETTINGS = ['uri', - 'profile', - 'loglevel', - 'logfile', - 'mode', - 'audio', - 'video', - 'ipv6_enabled', - 'udp_enabled', - 'local_discovery_enabled', - 'theme', - 'network', - 'message_font_size', - 'font', - 'save_history', - 'language', - 'update', - 'proxy_host', - 'proxy_type', - 'proxy_port', - 'core_logging', - 'audio', - 'video' - ] # , 'nodes_json' -lBOOLEANS = [ - 'local_discovery_enabled', - 'udp_enabled', - 'ipv6_enabled', - 'compact_mode', - 'allow_inline', - 'notifications', - 'sound_notifications', - 'hole_punching_enabled', - 'dht_announcements_enabled', - 'save_history', - 'download_nodes_list' - 'core_logging', - ] + 'profile', + 'loglevel', + 'logfile', + 'mode', + + # dunno + 'audio_input', + 'audio_output', + 'audio', + 'video', + + 'ipv6_enabled', + 'udp_enabled', + 'local_discovery_enabled', + 'theme', + 'network', + 'message_font_size', + 'font', + 'save_history', + 'language', + 'update', + 'proxy_host', + 'proxy_type', + 'proxy_port', + 'core_logging', + 'audio', + 'video' + ] # , 'nodes_json' class A(): pass @@ -373,7 +363,7 @@ def main(lArgs): if getattr(default_ns, key) == getattr(oArgs, key): delattr(oArgs, key) - for key in lBOOLEANS: + for key in ts.lBOOLEANS: if not hasattr(oArgs, key): continue val = getattr(oArgs, key) if type(val) == bool: continue @@ -385,15 +375,15 @@ def main(lArgs): aArgs = A() for key in oArgs.__dict__.keys(): setattr(aArgs, key, getattr(oArgs, key)) - setattr(aArgs, 'video', setup_video(oArgs)) + #setattr(aArgs, 'video', setup_video(oArgs)) aArgs.video = setup_video(oArgs) assert 'video' in aArgs.__dict__ - - setattr(aArgs, 'audio', setup_audio(oArgs)) + + #setattr(aArgs, 'audio', setup_audio(oArgs)) aArgs.audio = setup_audio(oArgs) assert 'audio' in aArgs.__dict__ - oArgs = aArgs + toxygen = app.App(__version__, oArgs) global oAPP oAPP = toxygen diff --git a/toxygen/middleware/callbacks.py b/toxygen/middleware/callbacks.py index 9bc9d9d..9119b4f 100644 --- a/toxygen/middleware/callbacks.py +++ b/toxygen/middleware/callbacks.py @@ -1,4 +1,5 @@ # -*- mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*- +import sys import os import threading from PyQt5 import QtGui @@ -13,10 +14,10 @@ from notifications.sound import * from datetime import datetime iMAX_INT32 = 4294967295 -def LOG_ERROR(l): print('ERRORc: '+l) -def LOG_WARN(l): print('WARNc: '+l) -def LOG_INFO(l): print('INFOc: '+l) -def LOG_DEBUG(l): print('DEBUGc: '+l) +def LOG_ERROR(l): print('EROR< '+l) +def LOG_WARN(l): print('WARN< '+l) +def LOG_INFO(l): print('INFO< '+l) +def LOG_DEBUG(l): print('DBUG< '+l) def LOG_TRACE(l): pass # print('TRACE+ '+l) global aTIMES @@ -45,6 +46,7 @@ def bTooSoon(key, sSlot, fSec=10.0): global iBYTES iBYTES=0 def sProcBytes(sFile=None): + if sys.platform == 'win32': return '' global iBYTES if sFile is None: pid = os.getpid() @@ -69,13 +71,11 @@ def self_connection_status(tox, profile): """ Current user changed connection status (offline, TCP, UDP) """ - pid = os.getpid() - sFile = '/proc/'+str(pid) +'/net/softnet_stat' sSlot = 'self connection status' def wrapped(tox_link, connection, user_data): key = f"connection {connection}" if bTooSoon(key, sSlot, 10): return - s = sProcBytes(sFile) + s = sProcBytes() try: status = tox.self_get_status() if connection != TOX_CONNECTION['NONE'] else None if status: @@ -148,10 +148,10 @@ def friend_name(contacts_provider, messenger): """ key = f"friend_number={friend_number}" if bTooSoon(key, sSlot, 60): return - LOG_DEBUG(f'New name friend #' + str(friend_number)) friend = contacts_provider.get_friend_by_number(friend_number) old_name = friend.name new_name = str(name, 'utf-8') + LOG_DEBUG(f"get_friend_by_number #{friend_number} {new_name}") invoke_in_main_thread(friend.set_name, new_name) invoke_in_main_thread(messenger.new_friend_name, friend, old_name, new_name) @@ -364,7 +364,7 @@ def callback_audio(calls_manager): New audio chunk """ LOG_DEBUG(f"callback_audio #{friend_number}") - # guessing was .call + # dunno was .call calls_manager._call.audio_chunk( bytes(samples[:audio_samples_per_channel * 2 * audio_channels_count]), audio_channels_count, @@ -401,7 +401,7 @@ def video_receive_frame(toxav, friend_number, width, height, y, u, v, ystride, u It can be created from initial y, u, v using slices """ - LOG_DEBUG(f"video_receive_frame from {friend_number}") + LOG_DEBUG(f"video_receive_frame from toxav_video_receive_frame_cb={friend_number}") import cv2 import numpy as np try: @@ -480,7 +480,8 @@ def group_private_message(window, tray, tox, messenger, settings, profile): if settings['sound_notifications'] and bl and profile.status != TOX_USER_STATUS['BUSY']: sound_notification(SOUND_NOTIFICATION['MESSAGE']) icon = util.join_path(util.get_images_directory(), 'icon_new_messages.png') - invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) + if tray and hasattr(tray, 'setIcon'): + invoke_in_main_thread(tray.setIcon, QtGui.QIcon(icon)) return wrapped @@ -507,7 +508,10 @@ def group_invite(window, settings, tray, profile, groups_service, contacts_provi def group_self_join(contacts_provider, contacts_manager, groups_service): + sSlot = 'group_self_join' def wrapped(tox, group_number, user_data): + key = f"group_number {group_number}" + if bTooSoon(key, sSlot, 10): return LOG_DEBUG(f"group_self_join #{group_number}") group = contacts_provider.get_group_by_number(group_number) invoke_in_main_thread(group.set_status, TOX_USER_STATUS['NONE']) @@ -535,11 +539,18 @@ def group_peer_join(contacts_provider, groups_service): def group_peer_exit(contacts_provider, groups_service, contacts_manager): - def wrapped(tox, group_number, peer_id, message, length, user_data): - LOG_DEBUG(f"group_peer_exit #{group_number} peer_id={peer_id}") + def wrapped(tox, + group_number, peer_id, + exit_type, name, name_length, + message, length, + user_data): group = contacts_provider.get_group_by_number(group_number) - group.remove_peer(peer_id) - invoke_in_main_thread(groups_service.generate_peers_list) + if group: + LOG_DEBUG(f"group_peer_exit #{group_number} peer_id={peer_id} exit_type={exit_type}") + group.remove_peer(peer_id) + invoke_in_main_thread(groups_service.generate_peers_list) + else: + LOG_WARN(f"group_peer_exit group not found #{group_number} peer_id={peer_id}") return wrapped @@ -700,9 +711,6 @@ def init_callbacks(tox, profile, settings, plugin_loader, contacts_manager, :param groups_service: GroupsService instance :param contacts_provider: ContactsProvider instance """ - global LOG - import logging - LOG = logging.getLogger('app.'+__name__) # self callbacks tox.callback_self_connection_status(self_connection_status(tox, profile)) diff --git a/toxygen/middleware/threads.py b/toxygen/middleware/threads.py index faffbb3..ca3dbbb 100644 --- a/toxygen/middleware/threads.py +++ b/toxygen/middleware/threads.py @@ -30,10 +30,10 @@ import logging LOG = logging.getLogger('app.'+'threads') # log = lambda x: LOG.info(x) -def LOG_ERROR(l): print('ERRORt: '+l) -def LOG_WARN(l): print('WARNt: '+l) -def LOG_INFO(l): print('INFOt: '+l) -def LOG_DEBUG(l): print('DEBUGt: '+l) +def LOG_ERROR(l): print('EROR+ '+l) +def LOG_WARN(l): print('WARN+ '+l) +def LOG_INFO(l): print('INFO+ '+l) +def LOG_DEBUG(l): print('DBUG+ '+l) def LOG_TRACE(l): pass # print('TRACE+ '+l) # ----------------------------------------------------------------------------------------------------------------- @@ -148,8 +148,9 @@ class ToxIterateThread(BaseQThread): self._tox.iterate() except Exception as e: # Fatal Python error: Segmentation fault - LOG_ERROR('ToxIterateThread run: {e}') - sleep(iMsec / 1000) + LOG_ERROR(f"ToxIterateThread run: {e}") + else: + sleep(iMsec / 1000) class ToxAVIterateThread(BaseQThread): diff --git a/toxygen/middleware/tox_factory.py b/toxygen/middleware/tox_factory.py index 8addad4..60535c1 100644 --- a/toxygen/middleware/tox_factory.py +++ b/toxygen/middleware/tox_factory.py @@ -9,8 +9,9 @@ import os global LOG import logging LOG = logging.getLogger('app.'+'tox_factory') -def LOG_DEBUG(l): print('DEBUGf: '+l) -def LOG_LOG(l): print('TRACf: '+l) +def LOG_INFO(l): print('DBUG> '+l) +def LOG_DEBUG(l): print('DBUG> '+l) +def LOG_LOG(l): print('TRAC> '+l) from ctypes import * from utils import util @@ -32,8 +33,7 @@ def tox_log_cb(iTox, level, file, line, func, message, *args): # root WARNING 3network.c#944:b'send_packet'attempted to send message with network family 10 (probably IPv6) on IPv4 socket if file == 'network.c' and line == 944: return message = f"{file}#{line}:{func} {message}" - LOG_LOG(# 'TRAC: ' + - message) + LOG_LOG(message) def tox_factory(data=None, settings=None, args=None, app=None): """ diff --git a/toxygen/ui/main_screen.py b/toxygen/ui/main_screen.py index d1dcfc6..2b3414d 100644 --- a/toxygen/ui/main_screen.py +++ b/toxygen/ui/main_screen.py @@ -12,7 +12,7 @@ from user_data.settings import Settings iMAX = 70 global LOG -LOG = logging.getLogger('app.'+__name__) +LOG = logging.getLogger('app.'+'mains') class QTextEditLogger(logging.Handler): def __init__(self, parent, app): @@ -190,7 +190,7 @@ class MainWindow(QtWidgets.QMainWindow): self.menuSettings.addAction(self.actionNetwork) self.menuSettings.addAction(self.audioSettings) self.menuSettings.addAction(self.videoSettings) - self.menuSettings.addAction(self.updateSettings) +## self.menuSettings.addAction(self.updateSettings) self.menuPlugins.addAction(self.pluginData) self.menuPlugins.addAction(self.importPlugin) self.menuPlugins.addAction(self.reloadPlugins) @@ -221,7 +221,7 @@ class MainWindow(QtWidgets.QMainWindow): self.actionNotifications.triggered.connect(self.notification_settings) self.audioSettings.triggered.connect(self.audio_settings) self.videoSettings.triggered.connect(self.video_settings) - self.updateSettings.triggered.connect(self.update_settings) +## self.updateSettings.triggered.connect(self.update_settings) self.pluginData.triggered.connect(self.plugins_menu) self.lockApp.triggered.connect(self.lock_app) self.importPlugin.triggered.connect(self.import_plugin) @@ -514,7 +514,7 @@ class MainWindow(QtWidgets.QMainWindow): self.peers_list.setGeometry(width * 3 // 4, 0, width - width * 3 // 4, self.height() - 155) invites_button_visible = self.groupInvitesPushButton.isVisible() - LOG.debug(f"invites_button_visible={invites_button_visible}") +# LOG.debug(f"invites_button_visible={invites_button_visible}") self.friends_list.setGeometry(0, 125 if invites_button_visible else 100, 270, self.height() - 150 if invites_button_visible else self.height() - 125) diff --git a/toxygen/user_data/settings.py b/toxygen/user_data/settings.py index 66fb93f..b5faf21 100644 --- a/toxygen/user_data/settings.py +++ b/toxygen/user_data/settings.py @@ -400,6 +400,9 @@ class Settings(dict): if key not in aArgs.__dict__: continue val = aArgs.__dict__[key] if val in ['0.0.0.0']: continue + if key in aArgs.__dict__ and key not in info: + # dunno = network + continue if key in aArgs.__dict__ and info[key] != val: aRet[key] = val return aRet