Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
ead54fce3f | |||
ae93efb17a | |||
d95dfb18fd | |||
4e3c397ce5 | |||
4e6126405f | |||
8cdec543e1 | |||
4810cbe138 | |||
8ec7fbb49e | |||
7d52ed8802 | |||
9d44d063eb | |||
d5eb9ed478 | |||
40afb6cdc2 | |||
74234476ab | |||
82c71a3bd8 | |||
5161e1c045 | |||
40052dbf3f | |||
e73d92754e | |||
6df1b80941 | |||
a9e5963b74 | |||
95671966cd | |||
a8ce7216e1 | |||
5c6829165b | |||
91eebfd895 | |||
72cc04342f | |||
75267294f9 | |||
f81d302c9a | |||
c3ab0406e4 | |||
fdcaf4e596 | |||
ca7b3eff29 | |||
f1eb640564 | |||
194ca2d380 | |||
976e90f0a0 | |||
2c836f473d | |||
f834b6046e | |||
e6087f3818 | |||
20c3df6be2 | |||
b914e4b548 | |||
f336489609 | |||
4ce2e88568 | |||
a917a6a9cd | |||
a01eabea91 | |||
605f0da751 | |||
6bd0c10ef2 | |||
a9032602bf | |||
8c03601bd2 |
@@ -21,6 +21,27 @@ test_py3:
|
||||
- '%PYTHON3% -m coverage report --omit="test*"'
|
||||
coverage: '/TOTAL.+ ([0-9]{1,3}%)/'
|
||||
|
||||
documentation:
|
||||
type: deploy
|
||||
tags:
|
||||
- windows10
|
||||
before_script:
|
||||
- '%PYTHON3% -v'
|
||||
script:
|
||||
- copy changelog.md doc\changelog.md
|
||||
- cd doc
|
||||
- '%PYTHON2% documentation_importer.py'
|
||||
- cd ..\src
|
||||
- '%PYTHON2% ..\doc\generator.py'
|
||||
- 'move documentation ..\'
|
||||
only:
|
||||
- master
|
||||
artifacts:
|
||||
paths:
|
||||
- documentation
|
||||
name: socializer_documentation
|
||||
expire_in: 1 day
|
||||
|
||||
alpha_python3:
|
||||
type: deploy
|
||||
tags:
|
||||
|
28
changelog.md
28
changelog.md
@@ -4,6 +4,34 @@
|
||||
|
||||
### New additions
|
||||
|
||||
* Added "post in groups" feature. In order to do so, you need to load the posts for the group where you want to send something. Once loaded, go to the post button in the group's wall and select whether you want to post in the group's behalf or as yourself.
|
||||
* In all audio buffers, it is possible to select individual tracks to be played together. In order to do so, you need to press space to start the selection of items. When selected, the item will emit a sound to indicate the change. Press space in all items you want to select/unselect. When you're focusing an already selected item it will play a sound to indicate that it is already selected. Once you're done with your selection, pressing enter in the list of tracks will start the playback of the list of items you have selected. This is a very experimental feature. More actions can be supported based in this selection method if it proves to be useful.
|
||||
* In conversation buffers, it is possible to display and open wall posts sent as attachments in messages.
|
||||
* In people buffers, it is possible to create a new timeline by using the context menu while focusing an user. This method will create the buffer for the selected user, as opposed to creating the buffer from the menu bar, where you have to type the username or find it in a list.
|
||||
|
||||
### bugfixes
|
||||
|
||||
* Fixed an error with two factor authentication in the recent socializer version. Now it works reliably again.
|
||||
* Fixed an error when trying to attach a photo to a wall post. The error was fixed in the [vk_api](https://github.com/python273/vk_api) module and the fix was sent to the developer of the library, so he will be able to merge it in the next version. In the meantime, socializer already includes the fix for this method, so you can upload photos to wall posts normally.
|
||||
* Fixed an error retrieving some group information for the current session.
|
||||
* When posting in a topic, links will be posted properly.
|
||||
|
||||
|
||||
### Changes
|
||||
|
||||
* the audio player module has received some improvements:
|
||||
- When there is something being played, the label of the "play" button, located in all audio buffers, will change automatically to "pause". When pressed, it will pause the song instead of starting the playback again since the beginning.
|
||||
- The last change will work also in the dialog for displaying audio information. Now it's possible to play and pause an audio from such dialog.
|
||||
- When playing a voice message, if a song is playing already socializer will decrease the volume so you can hear the voice message well enough. Some seconds after the message has finished, the song's volume will be restored.
|
||||
* In audio buffers, you will play the focused audio when pressing enter in the item, instead of opening the audio information dialog.
|
||||
* Removed some old keystrokes in socializer buffers due to better alternatives. The reason for this change is that currently you don't need to be focusing an item in a buffer for being able to use the alternative keystrokes, which makes those a better implementation.
|
||||
- Control+Enter and control+Shift+Enter: superseeded by Control+P for playing (and pausing) the currently focused audio.
|
||||
- F5 and F6: superseeded by Alt+down and up arrows for modifying volume.
|
||||
|
||||
## changes in version 0.20 (25.04.2019)
|
||||
|
||||
### New additions
|
||||
|
||||
* For users with multiple soundcards, there is a new tab in the preferences dialogue of Socializer, called sound. From there, you can define which soundcard will be used for input and output. [#25,](https://code.manuelcortez.net/manuelcortez/socializer/issues/25)
|
||||
* the audio player can seek positions in the currently playing track. You can use the menu items (located in the main menu) or use the new available keystrokes dedicated to the actions. Seeking will be made in 5 second intervals.
|
||||
* Alt+Shift+Left arrow: Seek 5 seconds backwards.
|
||||
|
@@ -3,7 +3,10 @@ wxpython==4.0.3
|
||||
pywin32
|
||||
pyenchant
|
||||
markdown
|
||||
vk_api
|
||||
# For the moment we will use the modified vk_api version that does not try to include enum34 and
|
||||
# already fixed the caption for wall uploaded photos.
|
||||
# Hopefully I can uncomment this in the near future when my changes will get merged upstream.
|
||||
#vk_api
|
||||
bs4
|
||||
configobj
|
||||
pypubsub
|
||||
@@ -18,4 +21,6 @@ mock
|
||||
git+https://code.manuelcortez.net/manuelcortez/libloader
|
||||
git+https://code.manuelcortez.net/manuelcortez/platform_utils
|
||||
git+https://github.com/chrisnorman7/sound_lib
|
||||
git+https://code.manuelcortez.net/manuelcortez/accessible_output2
|
||||
git+https://code.manuelcortez.net/manuelcortez/accessible_output2
|
||||
# Modified vk_api.
|
||||
git+https://github.com/manuelcortez/vk_api
|
@@ -55,11 +55,12 @@ def get_non_refreshed(login, password, scope=scope):
|
||||
url = "https://oauth.vk.com/token"
|
||||
params = dict(grant_type="password",
|
||||
client_id=client_id, client_secret=client_secret, username=login,
|
||||
password=password, v=api_ver, scope=scope, lang="en", device_id=device_id)
|
||||
password=password, v=api_ver, scope=scope, lang="en", device_id=device_id, force_sms=1)
|
||||
# Add two factor auth later due to python's syntax.
|
||||
params["2fa_supported"] = 1
|
||||
headers = {'User-Agent': user_agent}
|
||||
r = requests.get(url, params=params, headers=headers)
|
||||
log.exception(r.json())
|
||||
# If a 401 error is raised, we need to use 2FA here.
|
||||
# see https://vk.com/dev/auth_direct (switch lang to russian, english docs are very incomplete in the matter)
|
||||
# ToDo: this needs testing after implemented official VK tokens.
|
||||
|
@@ -4,24 +4,14 @@ import time
|
||||
import wx
|
||||
import widgetUtils
|
||||
|
||||
code = None
|
||||
remember = True
|
||||
|
||||
def two_factor_auth():
|
||||
global code, remember
|
||||
wx.CallAfter(get_code)
|
||||
while code == None:
|
||||
time.sleep(0.5)
|
||||
return (code, remember)
|
||||
|
||||
def get_code():
|
||||
global code, remember
|
||||
code = None
|
||||
dlg = wx.TextEntryDialog(None, _("Please provide the authentication code you have received from VK."), _("Two factor authentication code"))
|
||||
response = dlg.ShowModal()
|
||||
if response == widgetUtils.OK:
|
||||
code = dlg.GetValue()
|
||||
dlg.Destroy()
|
||||
dlg.Destroy()
|
||||
return (code, True)
|
||||
|
||||
def bad_password():
|
||||
return wx.MessageDialog(None, _("Your password or email address are incorrect. Please fix the mistakes and try it again."), _("Wrong data"), wx.ICON_ERROR).ShowModal()
|
@@ -19,6 +19,7 @@ from requests.exceptions import ReadTimeout, ConnectionError
|
||||
from mutagen.id3 import ID3
|
||||
from presenters import player
|
||||
from wxUI.tabs import home
|
||||
from wxUI.dialogs import timeline
|
||||
from sessionmanager import session, renderers, utils
|
||||
from mysc.thread_utils import call_threaded
|
||||
from wxUI import commonMessages, menus
|
||||
@@ -79,7 +80,7 @@ class baseBuffer(object):
|
||||
def insert(self, item, reversed=False):
|
||||
""" Add a new item to the list. Uses renderers.composefunc for parsing the dictionary and create a valid result for putting it in the list."""
|
||||
item_ = getattr(renderers, self.compose_function)(item, self.session)
|
||||
self.tab.list.insert_item(reversed, *item_)
|
||||
wx.CallAfter(self.tab.list.insert_item, reversed, *item_)
|
||||
|
||||
def get_items(self, show_nextpage=False):
|
||||
""" Retrieve items from the VK API. This function is called repeatedly by the main controller and users could call it implicitly as well with the update buffer option.
|
||||
@@ -304,11 +305,7 @@ class baseBuffer(object):
|
||||
|
||||
def get_event(self, ev):
|
||||
""" Parses keyboard input in the ListCtrl and executes the event associated with user keypresses."""
|
||||
if ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown() and ev.ShiftDown(): event = "pause_audio"
|
||||
elif ev.GetKeyCode() == wx.WXK_RETURN and ev.ControlDown(): event = "play_audio"
|
||||
elif ev.GetKeyCode() == wx.WXK_RETURN: event = "open_post"
|
||||
elif ev.GetKeyCode() == wx.WXK_F5: event = "volume_down"
|
||||
elif ev.GetKeyCode() == wx.WXK_F6: event = "volume_up"
|
||||
if ev.GetKeyCode() == wx.WXK_RETURN: event = "open_post"
|
||||
else:
|
||||
event = None
|
||||
ev.Skip()
|
||||
@@ -377,7 +374,7 @@ class baseBuffer(object):
|
||||
else:
|
||||
return [post["source_id"]]
|
||||
|
||||
def onFocus(self, *args,**kwargs):
|
||||
def onFocus(self, event, *args,**kwargs):
|
||||
""" Function executed when the item in a list is selected.
|
||||
For this buffer it updates the date of posts in the list."""
|
||||
post = self.get_post()
|
||||
@@ -386,6 +383,7 @@ class baseBuffer(object):
|
||||
original_date = arrow.get(post["date"])
|
||||
created_at = original_date.humanize(locale=languageHandler.curLang[:2])
|
||||
self.tab.list.list.SetItem(self.tab.list.get_selected(), 2, created_at)
|
||||
event.Skip()
|
||||
|
||||
def open_in_browser(self, *args, **kwargs):
|
||||
post = self.get_post()
|
||||
@@ -478,12 +476,15 @@ class feedBuffer(baseBuffer):
|
||||
|
||||
class communityBuffer(feedBuffer):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(communityBuffer, self).__init__(*args, **kwargs)
|
||||
self.group_id = self.kwargs["owner_id"]
|
||||
|
||||
def create_tab(self, parent):
|
||||
self.tab = home.communityTab(parent)
|
||||
self.connect_events()
|
||||
self.tab.name = self.name
|
||||
if hasattr(self, "can_post") and self.can_post == False and hasattr(self.tab, "post"):
|
||||
self.tab.post.Enable(False)
|
||||
self.tab.post.Enable(False)
|
||||
|
||||
def connect_events(self):
|
||||
super(communityBuffer, self).connect_events()
|
||||
@@ -499,21 +500,41 @@ class communityBuffer(feedBuffer):
|
||||
""" This method retrieves community information, useful to show different parts of the community itself."""
|
||||
if self.can_get_items:
|
||||
# Strangely, groups.get does not return counters so we need those to show options for loading specific posts for communities.
|
||||
self.group_info = self.session.vk.client.groups.getById(group_ids=-1*self.kwargs["owner_id"], fields="counters")[0]
|
||||
# print(self.group_info["counters"])
|
||||
group_info = self.session.vk.client.groups.getById(group_ids=-1*self.kwargs["owner_id"], fields="counters")[0]
|
||||
self.session.db["group_info"][self.group_id].update(group_info)
|
||||
if "can_post" in self.session.db["group_info"][self.group_id] and self.session.db["group_info"][self.group_id]["can_post"] == True:
|
||||
self.tab.post.Enable(True)
|
||||
super(communityBuffer, self).get_items(*args, **kwargs)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
menu = wx.Menu()
|
||||
user1 = self.session.get_user(self.session.user_id)
|
||||
user2 = self.session.get_user(self.kwargs["owner_id"])
|
||||
user = menu.Append(wx.NewId(), _("Post as {user1_nom}").format(**user1))
|
||||
group = menu.Append(wx.NewId(), _("Post as {user1_nom}").format(**user2))
|
||||
menu.Bind(widgetUtils.MENU, lambda evt: self._post(evt, 1), group)
|
||||
menu.Bind(widgetUtils.MENU, lambda evt: self._post(evt, 0), user)
|
||||
self.tab.post.PopupMenu(menu, self.tab.post.GetPosition())
|
||||
|
||||
def _post(self, event, from_group):
|
||||
owner_id = self.kwargs["owner_id"]
|
||||
user = self.session.get_user(owner_id, key="user1")
|
||||
title = _("Post to {user1_nom}'s wall").format(**user)
|
||||
p = presenters.createPostPresenter(session=self.session, interactor=interactors.createPostInteractor(), view=views.createPostDialog(title=title, message="", text=""))
|
||||
if hasattr(p, "text") or hasattr(p, "privacy"):
|
||||
call_threaded(self.do_last, p=p, owner_id=owner_id, from_group=from_group)
|
||||
|
||||
class topicBuffer(feedBuffer):
|
||||
|
||||
def create_tab(self, parent):
|
||||
self.tab = home.topicTab(parent)
|
||||
self.connect_events()
|
||||
self.tab.name = self.name
|
||||
if hasattr(self, "can_post") and self.can_post == False and hasattr(self.tab, "post"):
|
||||
if "can_create_topic" not in self.session.db["group_info"][self.kwargs["group_id"]*-1] or ("can_create_topic" in self.session.db["group_info"][self.kwargs["group_id"]*-1] and self.session.db["group_info"][self.kwargs["group_id"]*-1]["can_create_topic"] != True):
|
||||
self.tab.post.Enable(False)
|
||||
|
||||
def onFocus(self, *args, **kwargs):
|
||||
pass
|
||||
def onFocus(self, event, *args, **kwargs):
|
||||
event.Skip()
|
||||
|
||||
def open_post(self, *args, **kwargs):
|
||||
""" Opens the currently focused post."""
|
||||
@@ -524,7 +545,6 @@ class topicBuffer(feedBuffer):
|
||||
|
||||
def open_in_browser(self, *args, **kwargs):
|
||||
post = self.get_post()
|
||||
print(post)
|
||||
if post == None:
|
||||
return
|
||||
# In order to load the selected topic we firstly have to catch the group_id, which is present in self.kwargs
|
||||
@@ -533,6 +553,43 @@ class topicBuffer(feedBuffer):
|
||||
url = "https://vk.com/topic{group_id}_{topic_id}".format(group_id=group_id, topic_id=post["id"])
|
||||
webbrowser.open_new_tab(url)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
menu = wx.Menu()
|
||||
user1 = self.session.get_user(self.session.user_id)
|
||||
user2 = self.session.get_user(-1*self.kwargs["group_id"])
|
||||
user = menu.Append(wx.NewId(), _("Post as {user1_nom}").format(**user1))
|
||||
group = menu.Append(wx.NewId(), _("Post as {user1_nom}").format(**user2))
|
||||
menu.Bind(widgetUtils.MENU, lambda evt: self._post(evt, 1), group)
|
||||
menu.Bind(widgetUtils.MENU, lambda evt: self._post(evt, 0), user)
|
||||
self.tab.post.PopupMenu(menu, self.tab.post.GetPosition())
|
||||
|
||||
def _post(self, event, from_group):
|
||||
owner_id = self.kwargs["group_id"]
|
||||
user = self.session.get_user(-1*owner_id, key="user1")
|
||||
title = _("Create topic in {user1_nom}").format(**user)
|
||||
p = presenters.createPostPresenter(session=self.session, interactor=interactors.createPostInteractor(), view=views.createTopicDialog(title=title, message="", text=""))
|
||||
if hasattr(p, "text") or hasattr(p, "privacy"):
|
||||
call_threaded(self.do_last, p=p, group_id=owner_id, from_group=from_group)
|
||||
|
||||
def do_last(self, p, *args, **kwargs):
|
||||
title = p.view.title
|
||||
msg = p.text
|
||||
attachments = ""
|
||||
if hasattr(p, "attachments"):
|
||||
attachments = self.upload_attachments(p.attachments)
|
||||
urls = utils.find_urls_in_text(msg)
|
||||
if len(urls) != 0:
|
||||
if len(attachments) == 0: attachments = urls[0]
|
||||
else: attachments += urls[0]
|
||||
msg = msg.replace(urls[0], "")
|
||||
if msg != "":
|
||||
kwargs.update(text=msg, title=title.GetValue())
|
||||
if attachments != "":
|
||||
kwargs.update(attachments=attachments)
|
||||
# Determines the correct functions to call here.
|
||||
post = self.session.vk.client.board.addTopic(**kwargs)
|
||||
pub.sendMessage("posted", buffer=self.name)
|
||||
|
||||
class documentBuffer(feedBuffer):
|
||||
can_get_items = False
|
||||
|
||||
@@ -543,13 +600,14 @@ class documentBuffer(feedBuffer):
|
||||
if hasattr(self, "can_post") and self.can_post == False and hasattr(self.tab, "post"):
|
||||
self.tab.post.Enable(False)
|
||||
|
||||
def onFocus(self, *args,**kwargs):
|
||||
def onFocus(self, event, *args,**kwargs):
|
||||
post = self.get_post()
|
||||
if post == None:
|
||||
return
|
||||
original_date = arrow.get(post["date"])
|
||||
created_at = original_date.humanize(locale=languageHandler.curLang[:2])
|
||||
self.tab.list.list.SetItem(self.tab.list.get_selected(), 4, created_at)
|
||||
event.Skip()
|
||||
|
||||
def connect_events(self):
|
||||
super(documentBuffer, self).connect_events()
|
||||
@@ -619,9 +677,6 @@ class documentCommunityBuffer(documentBuffer):
|
||||
self.tab.post.Enable(False)
|
||||
|
||||
class audioBuffer(feedBuffer):
|
||||
""" this buffer was supposed to be used with audio elements
|
||||
but is deprecated as VK removed its audio support for third party apps."""
|
||||
|
||||
def create_tab(self, parent):
|
||||
self.tab = home.audioTab(parent)
|
||||
self.tab.name = self.name
|
||||
@@ -629,16 +684,38 @@ class audioBuffer(feedBuffer):
|
||||
if self.name == "me_audio":
|
||||
self.tab.post.Enable(True)
|
||||
|
||||
def get_event(self, ev):
|
||||
if ev.GetKeyCode() == wx.WXK_RETURN:
|
||||
if len(self.tab.list.get_multiple_selection()) < 2:
|
||||
event = "play_all"
|
||||
else:
|
||||
event = "play_audio"
|
||||
else:
|
||||
event = None
|
||||
ev.Skip()
|
||||
if event != None:
|
||||
try:
|
||||
getattr(self, event)(skip_pause=True)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def connect_events(self):
|
||||
widgetUtils.connect_event(self.tab.play, widgetUtils.BUTTON_PRESSED, self.play_audio)
|
||||
widgetUtils.connect_event(self.tab.play_all, widgetUtils.BUTTON_PRESSED, self.play_all)
|
||||
pub.subscribe(self.change_label, "playback-changed")
|
||||
super(audioBuffer, self).connect_events()
|
||||
|
||||
def play_audio(self, *args, **kwargs):
|
||||
selected = self.tab.list.get_selected()
|
||||
if selected == -1:
|
||||
selected = 0
|
||||
pub.sendMessage("play", object=self.session.db[self.name]["items"][selected])
|
||||
if player.player.check_is_playing() and not "skip_pause" in kwargs:
|
||||
return pub.sendMessage("pause")
|
||||
selected = self.tab.list.get_multiple_selection()
|
||||
if len(selected) == 0:
|
||||
return
|
||||
elif len(selected) == 1:
|
||||
pub.sendMessage("play", object=self.session.db[self.name]["items"][selected[0]])
|
||||
else:
|
||||
selected_audios = [self.session.db[self.name]["items"][item] for item in selected]
|
||||
pub.sendMessage("play-all", list_of_songs=selected_audios)
|
||||
return True
|
||||
|
||||
def play_next(self, *args, **kwargs):
|
||||
@@ -696,8 +773,8 @@ class audioBuffer(feedBuffer):
|
||||
# Translators: Some buffers can't use the get previous item feature due to API limitations.
|
||||
output.speak(_("This buffer doesn't support getting more items."))
|
||||
|
||||
def onFocus(self, *args, **kwargs):
|
||||
pass
|
||||
def onFocus(self, event, *args, **kwargs):
|
||||
event.Skip()
|
||||
|
||||
def add_to_library(self, *args, **kwargs):
|
||||
post = self.get_post()
|
||||
@@ -778,6 +855,16 @@ class audioBuffer(feedBuffer):
|
||||
url = "https://vk.com/audio{user_id}_{post_id}".format(user_id=post["owner_id"], post_id=post["id"])
|
||||
webbrowser.open_new_tab(url)
|
||||
|
||||
def change_label(self, stopped):
|
||||
if hasattr(self.tab, "play"):
|
||||
if stopped == False:
|
||||
self.tab.play.SetLabel(_("P&ause"))
|
||||
else:
|
||||
self.tab.play.SetLabel(_("P&lay"))
|
||||
|
||||
def __del__(self):
|
||||
pub.unsubscribe(self.change_label, "playback-changed")
|
||||
|
||||
class audioAlbum(audioBuffer):
|
||||
""" this buffer was supposed to be used with audio albums
|
||||
but is deprecated as VK removed its audio support for third party apps."""
|
||||
@@ -852,8 +939,8 @@ class videoBuffer(feedBuffer):
|
||||
# Translators: Some buffers can't use the get previous item feature due to API limitations.
|
||||
output.speak(_("This buffer doesn't support getting more items."))
|
||||
|
||||
def onFocus(self, *args, **kwargs):
|
||||
pass
|
||||
def onFocus(self, event, *args, **kwargs):
|
||||
event.Skip()
|
||||
|
||||
def add_to_library(self, *args, **kwargs):
|
||||
post = self.get_post()
|
||||
@@ -1056,9 +1143,9 @@ class chatBuffer(baseBuffer):
|
||||
if show_nextpage == False:
|
||||
if self.tab.history.GetValue() != "" and num > 0:
|
||||
v = [i for i in self.session.db[self.name]["items"][:num]]
|
||||
[self.insert(i, False) for i in v]
|
||||
[wx.CallAfter(self.insert, i, False) for i in v]
|
||||
else:
|
||||
[self.insert(i) for i in self.session.db[self.name]["items"][:num]]
|
||||
[wx.CallAfter(self.insert, i) for i in self.session.db[self.name]["items"][:num]]
|
||||
else:
|
||||
if num > 0:
|
||||
# At this point we save more CPU and mathematical work if we just delete everything in the chat history and readd all messages.
|
||||
@@ -1068,7 +1155,7 @@ class chatBuffer(baseBuffer):
|
||||
self.chats = dict()
|
||||
self.tab.history.SetValue("")
|
||||
v = [i for i in self.session.db[self.name]["items"]]
|
||||
[self.insert(i) for i in v]
|
||||
[wx.CallAfter(self.insert, i) for i in v]
|
||||
# Now it's time to set back the focus in the post.
|
||||
for i in self.chats.keys():
|
||||
if self.chats[i] == focused_post["id"]:
|
||||
@@ -1186,8 +1273,7 @@ class chatBuffer(baseBuffer):
|
||||
a = presenters.displayAudioPresenter(session=self.session, postObject=[attachment["audio"]], interactor=interactors.displayAudioInteractor(), view=views.displayAudio())
|
||||
elif attachment["type"] == "audio_message":
|
||||
link = attachment["audio_message"]["link_mp3"]
|
||||
output.speak(_("Playing..."))
|
||||
pub.sendMessage("play", object=dict(url=link), set_info=False)
|
||||
pub.sendMessage("play-message", message_url=link)
|
||||
elif attachment["type"] == "link":
|
||||
output.speak(_("Opening URL..."), True)
|
||||
webbrowser.open_new_tab(attachment["link"]["url"])
|
||||
@@ -1217,6 +1303,8 @@ class chatBuffer(baseBuffer):
|
||||
break
|
||||
if url != "":
|
||||
webbrowser.open_new_tab(url)
|
||||
if attachment["type"] == "wall":
|
||||
pub.sendMessage("open-post", post_object=attachment["wall"], controller_="displayPost")
|
||||
else:
|
||||
log.debug("Unhandled attachment: %r" % (attachment,))
|
||||
|
||||
@@ -1279,7 +1367,14 @@ class peopleBuffer(feedBuffer):
|
||||
self.tab.list.list.SetItem(self.tab.list.get_selected(), 1, online_status)
|
||||
|
||||
def open_timeline(self, *args, **kwargs):
|
||||
pass
|
||||
user = self.get_post()
|
||||
if user == None:
|
||||
return
|
||||
a = timeline.timelineDialog([self.session.get_user(user["id"])["user1_gen"]], show_selector=False)
|
||||
if a.get_response() == widgetUtils.OK:
|
||||
buffer_type = a.get_buffer_type()
|
||||
user_id = user["id"]
|
||||
pub.sendMessage("create-timeline", user_id=user_id, buffer_type=buffer_type)
|
||||
|
||||
def get_menu(self, *args, **kwargs):
|
||||
""" display menu for people buffers (friends and requests)"""
|
||||
|
@@ -200,13 +200,14 @@ class Controller(object):
|
||||
if self.session.settings["load_at_startup"]["communities"] == False and force_action == False:
|
||||
return
|
||||
log.debug("Create community buffers...")
|
||||
groups= self.session.vk.client.groups.get(user_id=user_id, extended=1, count=1000)
|
||||
groups= self.session.vk.client.groups.get(user_id=user_id, extended=1, count=1000, fields="can_post,can_create_topic")
|
||||
self.session.groups=groups["items"]
|
||||
# Let's feed the local database cache with new groups coming from here.
|
||||
data= dict(profiles=[], groups=self.session.groups)
|
||||
self.session.process_usernames(data)
|
||||
if create_buffers:
|
||||
for i in self.session.groups:
|
||||
self.session.db["group_info"][i["id"]*-1] = i
|
||||
wx.CallAfter(pub.sendMessage, "create_buffer", buffer_type="communityBuffer", buffer_title=i["name"], parent_tab="communities", loadable=True, get_items=True, kwargs=dict(parent=self.window.tb, name="{0}_community".format(i["id"],), composefunc="render_status", session=self.session, endpoint="get", parent_endpoint="wall", extended=1, count=self.session.settings["buffers"]["count_for_wall_buffers"], owner_id=-1*i["id"]))
|
||||
time.sleep(0.15)
|
||||
|
||||
@@ -296,6 +297,7 @@ class Controller(object):
|
||||
pub.subscribe(self.create_buffer, "create_buffer")
|
||||
pub.subscribe(self.user_typing, "user-typing")
|
||||
pub.subscribe(self.get_chat, "order-sent-message")
|
||||
pub.subscribe(self.create_timeline, "create-timeline")
|
||||
|
||||
def disconnect_events(self):
|
||||
log.debug("Disconnecting some events...")
|
||||
@@ -307,6 +309,7 @@ class Controller(object):
|
||||
pub.unsubscribe(self.user_online, "user-online")
|
||||
pub.unsubscribe(self.user_offline, "user-offline")
|
||||
pub.unsubscribe(self.notify, "notify")
|
||||
pub.subscribe(self.create_timeline, "create-timeline")
|
||||
|
||||
def in_post(self, buffer):
|
||||
""" This event is triggered whenever an user requires an update in their buffers. For example after sending a post successfully.
|
||||
@@ -408,7 +411,7 @@ class Controller(object):
|
||||
if user == None:
|
||||
log.exception("Getting user manually...")
|
||||
user = self.session.vk.client.users.get(user_ids=event.user_id, fields="last_seen")[0]
|
||||
online_buffer.add_person(user)
|
||||
wx.CallAfter(online_buffer.add_person, user)
|
||||
|
||||
def user_offline(self, event):
|
||||
""" Sends a notification of an user logging off in VK.
|
||||
@@ -421,7 +424,7 @@ class Controller(object):
|
||||
sound = "friend_offline.ogg"
|
||||
self.notify(msg, sound, self.session.settings["chat"]["notifications"])
|
||||
online_friends = self.search("online_friends")
|
||||
online_friends.remove_person(event.user_id)
|
||||
wx.CallAfter(online_friends.remove_person, event.user_id)
|
||||
|
||||
def notify(self, message="", sound="", type="native"):
|
||||
""" display a notification in Socializer.
|
||||
@@ -509,7 +512,7 @@ class Controller(object):
|
||||
# Let's add this to the buffer.
|
||||
# ToDo: Clean this code and test how is the database working with this set to True.
|
||||
buffer.session.db[buffer.name]["items"].append(message)
|
||||
buffer.insert(self.session.db[buffer.name]["items"][-1], False)
|
||||
wx.CallAfter(buffer.insert, self.session.db[buffer.name]["items"][-1], False)
|
||||
self.session.soundplayer.play("message_received.ogg")
|
||||
wx.CallAfter(self.reorder_buffer, buffer)
|
||||
# Check if we have to read the message aloud
|
||||
@@ -517,13 +520,34 @@ class Controller(object):
|
||||
rendered_message = renderers.render_message(message, self.session)
|
||||
output.speak(rendered_message[0])
|
||||
|
||||
def create_timeline(self, user_id, buffer_type, user=""):
|
||||
if user_id == "":
|
||||
user_data = self.session.vk.client.utils.resolveScreenName(screen_name=user)
|
||||
if type(user_data) == list:
|
||||
commonMessages.no_user_exist()
|
||||
return
|
||||
user_id = user_data["object_id"]
|
||||
if buffer_type == "audio":
|
||||
buffer = buffers.audioBuffer(parent=self.window.tb, name="{0}_audio".format(user_id,), composefunc="render_audio", session=self.session, create_tab=False, endpoint="get", parent_endpoint="audio", owner_id=user_id)
|
||||
user = self.session.get_user(user_id, key="user1")
|
||||
name_ = _("{user1_nom}'s audios").format(**user)
|
||||
elif buffer_type == "wall":
|
||||
buffer = buffers.feedBuffer(parent=self.window.tb, name="{0}_feed".format(user_id,), composefunc="render_status", session=self.session, create_tab=False, endpoint="get", parent_endpoint="wall", extended=1, count=self.session.settings["buffers"]["count_for_wall_buffers"], owner_id=user_id)
|
||||
user = self.session.get_user(user_id, key="user1")
|
||||
name_ = _("{user1_nom}'s posts").format(**user)
|
||||
elif buffer_type == "friends":
|
||||
buffer = buffers.peopleBuffer(parent=self.window.tb, name="friends_{0}".format(user_id,), composefunc="render_person", session=self.session, create_tab=False, endpoint="get", parent_endpoint="friends", count=5000, fields="uid, first_name, last_name, last_seen", user_id=user_id)
|
||||
user = self.session.get_user(user_id, key="user1")
|
||||
name_ = _("{user1_nom}'s friends").format(**user)
|
||||
wx.CallAfter(self.complete_buffer_creation, buffer=buffer, name_=name_, position=self.window.search("timelines"))
|
||||
|
||||
### GUI events
|
||||
# These functions are connected to GUI elements such as menus, buttons and so on.
|
||||
def connect_gui_events(self):
|
||||
widgetUtils.connect_event(self.window, widgetUtils.CLOSE_EVENT, self.exit)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.update_buffer, menuitem=self.window.update_buffer)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.check_for_updates, menuitem=self.window.check_for_updates)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.window.about_dialog, menuitem=self.window.about)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.on_about, menuitem=self.window.about)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.search_audios, menuitem=self.window.search_audios)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU, self.search_videos, menuitem=self.window.search_videos)
|
||||
widgetUtils.connect_event(self.window, widgetUtils.MENU,self.remove_buffer, menuitem=self.window.remove_buffer_)
|
||||
@@ -574,6 +598,10 @@ class Controller(object):
|
||||
if update == False:
|
||||
commonMessages.no_update_available()
|
||||
|
||||
def on_about(self, *args, **kwargs):
|
||||
channel = self.session.settings["general"]["update_channel"]
|
||||
self.window.about_dialog(channel)
|
||||
|
||||
def search_audios(self, *args, **kwargs):
|
||||
dlg = searchDialogs.searchAudioDialog()
|
||||
if dlg.get_response() == widgetUtils.OK:
|
||||
@@ -656,25 +684,7 @@ class Controller(object):
|
||||
for i in d:
|
||||
if i[1] == user:
|
||||
user_id = i[0]
|
||||
if user_id == "":
|
||||
user_data = self.session.vk.client.utils.resolveScreenName(screen_name=user)
|
||||
if type(user_data) == list:
|
||||
commonMessages.no_user_exist()
|
||||
return
|
||||
user_id = user_data["object_id"]
|
||||
if buffertype == "audio":
|
||||
buffer = buffers.audioBuffer(parent=self.window.tb, name="{0}_audio".format(user_id,), composefunc="render_audio", session=self.session, create_tab=False, endpoint="get", parent_endpoint="audio", owner_id=user_id)
|
||||
user = self.session.get_user(user_id, key="user1")
|
||||
name_ = _("{user1_nom}'s audios").format(**user)
|
||||
elif buffertype == "wall":
|
||||
buffer = buffers.feedBuffer(parent=self.window.tb, name="{0}_feed".format(user_id,), composefunc="render_status", session=self.session, create_tab=False, endpoint="get", parent_endpoint="wall", extended=1, count=self.session.settings["buffers"]["count_for_wall_buffers"], owner_id=user_id)
|
||||
user = self.session.get_user(user_id, key="user1")
|
||||
name_ = _("{user1_nom}'s posts").format(**user)
|
||||
elif buffertype == "friends":
|
||||
buffer = buffers.peopleBuffer(parent=self.window.tb, name="friends_{0}".format(user_id,), composefunc="render_person", session=self.session, create_tab=False, endpoint="get", parent_endpoint="friends", count=5000, fields="uid, first_name, last_name, last_seen", user_id=user_id)
|
||||
user = self.session.get_user(user_id, key="user1")
|
||||
name_ = _("{user1_nom}'s friends").format(**user)
|
||||
wx.CallAfter(self.complete_buffer_creation, buffer=buffer, name_=name_, position=self.window.search("timelines"))
|
||||
pub.sendMessage("create-timeline", user_id=user_id, buffer_type=buffertype)
|
||||
|
||||
def create_audio_album(self, *args, **kwargs):
|
||||
d = creation.audio_album()
|
||||
@@ -821,19 +831,19 @@ class Controller(object):
|
||||
# 2. If the group_info does not have counters for such items, which would indicate there are no items posted yet.
|
||||
if self.search(current_buffer.name+"_audios") != False:
|
||||
menu.load_audios.Enable(False)
|
||||
elif hasattr(current_buffer, "group_info") and "audios" not in current_buffer.group_info["counters"]:
|
||||
elif "counters" in self.session.db["group_info"][current_buffer.group_id] and "audios" not in self.session.db["group_info"][current_buffer.group_id]["counters"]:
|
||||
menu.load_audios.Enable(False)
|
||||
if self.search(current_buffer.name+"_videos") != False:
|
||||
menu.load_videos.Enable(False)
|
||||
elif hasattr(current_buffer, "group_info") and "videos" not in current_buffer.group_info["counters"]:
|
||||
elif "counters" in self.session.db["group_info"][current_buffer.group_id] and "videos" not in self.session.db["group_info"][current_buffer.group_id]["counters"]:
|
||||
menu.load_videos.Enable(False)
|
||||
if self.search(current_buffer.name+"_topics") != False:
|
||||
menu.load_topics.Enable(False)
|
||||
elif hasattr(current_buffer, "group_info") and "topics" not in current_buffer.group_info["counters"]:
|
||||
elif "counters" in self.session.db["group_info"][current_buffer.group_id] and "topics" not in self.session.db["group_info"][current_buffer.group_id]["counters"]:
|
||||
menu.load_topics.Enable(False)
|
||||
if self.search(current_buffer.name+"_documents") != False:
|
||||
menu.load_documents.Enable(False)
|
||||
elif hasattr(current_buffer, "group_info") and "docs" not in current_buffer.group_info["counters"]:
|
||||
elif "counters" in self.session.db["group_info"][current_buffer.group_id] and "docs" not in self.session.db["group_info"][current_buffer.group_id]["counters"]:
|
||||
menu.load_documents.Enable(False)
|
||||
# Connect the rest of the functions.
|
||||
widgetUtils.connect_event(menu, widgetUtils.MENU, self.load_community_posts, menuitem=menu.load_posts)
|
||||
@@ -888,10 +898,10 @@ class Controller(object):
|
||||
""" Load community audios if they are not loaded already."""
|
||||
current_buffer = self.get_current_buffer()
|
||||
# Get group_info if the community buffer does not have it already, so future menus will be able to use it.
|
||||
if not hasattr(current_buffer, "group_info"):
|
||||
group_info = self.session.vk.client.groups.getById(group_ids=-1*current_buffer.kwargs["owner_id"], fields="counters")[0]
|
||||
current_buffer.group_info = group_info
|
||||
if "audios" not in current_buffer.group_info["counters"]:
|
||||
if current_buffer.group_id not in self.session.db["group_info"] or "counters" not in self.session.db["group_info"][current_buffer.group_id]:
|
||||
group_info = self.session.vk.client.groups.getById(group_ids=-1*current_buffer.kwargs["owner_id"], fields="counters,can_create_topics,can_post")[0]
|
||||
self.session.db["group_info"][current_buffer.kwargs["owner_id"]].update(group_info)
|
||||
if "audios" not in self.session.db["group_info"][current_buffer.group_id]["counters"]:
|
||||
commonMessages.community_no_items()
|
||||
return
|
||||
new_name = current_buffer.name+"_audios"
|
||||
@@ -901,10 +911,10 @@ class Controller(object):
|
||||
""" Load community videos if they are not loaded already."""
|
||||
current_buffer = self.get_current_buffer()
|
||||
# Get group_info if the community buffer does not have it already, so future menus will be able to use it.
|
||||
if not hasattr(current_buffer, "group_info"):
|
||||
group_info = self.session.vk.client.groups.getById(group_ids=-1*current_buffer.kwargs["owner_id"], fields="counters")[0]
|
||||
current_buffer.group_info = group_info
|
||||
if "videos" not in current_buffer.group_info["counters"]:
|
||||
if current_buffer.group_id not in self.session.db["group_info"] or "counters" not in self.session.db["group_info"][current_buffer.group_id]:
|
||||
group_info = self.session.vk.client.groups.getById(group_ids=-1*current_buffer.kwargs["owner_id"], fields="counters,can_create_topics,can_post")[0]
|
||||
self.session.db["group_info"][current_buffer.kwargs["owner_id"]].update(group_info)
|
||||
if "videos" not in self.session.db["group_info"][current_buffer.group_id]["counters"]:
|
||||
commonMessages.community_no_items()
|
||||
return
|
||||
new_name = current_buffer.name+"_videos"
|
||||
@@ -914,10 +924,10 @@ class Controller(object):
|
||||
""" Load community topics."""
|
||||
current_buffer = self.get_current_buffer()
|
||||
# Get group_info if the community buffer does not have it already, so future menus will be able to use it.
|
||||
if not hasattr(current_buffer, "group_info"):
|
||||
group_info = self.session.vk.client.groups.getById(group_ids=-1*current_buffer.kwargs["owner_id"], fields="counters")[0]
|
||||
current_buffer.group_info = group_info
|
||||
if "topics" not in current_buffer.group_info["counters"]:
|
||||
if current_buffer.group_id not in self.session.db["group_info"] or "counters" not in self.session.db["group_info"][current_buffer.group_id]:
|
||||
group_info = self.session.vk.client.groups.getById(group_ids=-1*current_buffer.kwargs["owner_id"], fields="counters,can_create_topic,can_post")[0]
|
||||
self.session.db["group_info"][current_buffer.kwargs["owner_id"]].update(group_info)
|
||||
if "topics" not in self.session.db["group_info"][current_buffer.group_id]["counters"]:
|
||||
commonMessages.community_no_items()
|
||||
return
|
||||
new_name = current_buffer.name+"_topics"
|
||||
@@ -926,10 +936,10 @@ class Controller(object):
|
||||
def load_community_documents(self, *args, **kwargs):
|
||||
current_buffer = self.get_current_buffer()
|
||||
# Get group_info if the community buffer does not have it already, so future menus will be able to use it.
|
||||
if not hasattr(current_buffer, "group_info"):
|
||||
group_info = self.session.vk.client.groups.getById(group_ids=-1*current_buffer.kwargs["owner_id"], fields="counters")[0]
|
||||
current_buffer.group_info = group_info
|
||||
if "docs" not in current_buffer.group_info["counters"]:
|
||||
if current_buffer.group_id not in self.session.db["group_info"] or "counters" not in self.session.db["group_info"][current_buffer.group_id]:
|
||||
group_info = self.session.vk.client.groups.getById(group_ids=-1*current_buffer.kwargs["owner_id"], fields="counters,can_create_topics,can_post")[0]
|
||||
self.session.db["group_info"][current_buffer.kwargs["owner_id"]].update(group_info)
|
||||
if "docs" not in self.session.db["group_info"][current_buffer.group_id]["counters"]:
|
||||
commonMessages.community_no_items()
|
||||
return
|
||||
new_name = current_buffer.name+"_documents"
|
||||
|
@@ -59,8 +59,6 @@ class configurationInteractor(base.baseInteractor):
|
||||
self.presenter.update_setting(section="general", setting="update_channel", value=update_channel)
|
||||
self.presenter.update_setting(section="chat", setting="notify_online", value=self.view.get_value("chat", "notify_online"))
|
||||
self.presenter.update_setting(section="chat", setting="notify_offline", value=self.view.get_value("chat", "notify_offline"))
|
||||
self.presenter.update_setting(section="chat", setting="open_unread_conversations", value=self.view.get_value("chat", "open_unread_conversations"))
|
||||
self.presenter.update_setting(section="chat", setting="automove_to_conversations", value=self.view.get_value("chat", "automove_to_conversations"))
|
||||
self.presenter.update_setting(section="chat", setting="notifications", value=self.presenter.get_notification_type(self.view.get_value("chat", "notifications")))
|
||||
self.presenter.update_setting(section="load_at_startup", setting="audio_albums", value=self.view.get_value("startup", "audio_albums"))
|
||||
self.presenter.update_setting(section="load_at_startup", setting="video_albums", value=self.view.get_value("startup", "video_albums"))
|
||||
|
@@ -171,6 +171,13 @@ class displayAudioInteractor(base.baseInteractor):
|
||||
getattr(self.view, control).Append(i)
|
||||
getattr(self.view, control).SetSelection(0)
|
||||
|
||||
def change_label(self, stopped):
|
||||
|
||||
if stopped == False:
|
||||
self.view.play.SetLabel(_("P&ause"))
|
||||
else:
|
||||
self.view.play.SetLabel(_("P&lay"))
|
||||
|
||||
def install(self, *args, **kwargs):
|
||||
super(displayAudioInteractor, self).install(*args, **kwargs)
|
||||
widgetUtils.connect_event(self.view.list, widgetUtils.LISTBOX_CHANGED, self.on_change)
|
||||
@@ -180,11 +187,13 @@ class displayAudioInteractor(base.baseInteractor):
|
||||
widgetUtils.connect_event(self.view.remove, widgetUtils.BUTTON_PRESSED, self.on_remove_from_library)
|
||||
pub.subscribe(self.set, self.modulename+"_set")
|
||||
pub.subscribe(self.add_items, self.modulename+"_add_items")
|
||||
pub.subscribe(self.change_label, "playback-changed")
|
||||
|
||||
def uninstall(self):
|
||||
super(displayAudioInteractor, self).uninstall()
|
||||
pub.unsubscribe(self.set, self.modulename+"_set")
|
||||
pub.unsubscribe(self.add_items, self.modulename+"_add_items")
|
||||
pub.unsubscribe(self.change_label, "playback-changed")
|
||||
|
||||
def on_change(self, *args, **kwargs):
|
||||
post = self.view.get_audio()
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -59,8 +59,6 @@ class configurationPresenter(base.basePresenter):
|
||||
self.send_message("create_tab", tab="chat")
|
||||
self.send_message("set", tab="chat", setting="notify_online", value=self.session.settings["chat"]["notify_online"])
|
||||
self.send_message("set", tab="chat", setting="notify_offline", value=self.session.settings["chat"]["notify_offline"])
|
||||
self.send_message("set", tab="chat", setting="open_unread_conversations", value=self.session.settings["chat"]["open_unread_conversations"])
|
||||
self.send_message("set", tab="chat", setting="automove_to_conversations", value=self.session.settings["chat"]["automove_to_conversations"])
|
||||
self.send_message("set", tab="chat", setting="notifications", value=self.get_notification_label(self.session.settings["chat"]["notifications"]))
|
||||
self.send_message("create_tab", tab="startup_options")
|
||||
self.send_message("set", tab="startup", setting="audio_albums", value=self.session.settings["load_at_startup"]["audio_albums"])
|
||||
|
@@ -3,7 +3,7 @@ import logging
|
||||
from sessionmanager import utils
|
||||
from pubsub import pub
|
||||
from mysc.thread_utils import call_threaded
|
||||
from presenters import base
|
||||
from presenters import base, player
|
||||
|
||||
log = logging.getLogger(__file__)
|
||||
|
||||
@@ -82,6 +82,8 @@ class displayAudioPresenter(base.basePresenter):
|
||||
|
||||
def play(self, audio_index):
|
||||
post = self.post[audio_index]
|
||||
if player.player.check_is_playing() == True:
|
||||
return pub.sendMessage("stop")
|
||||
pub.sendMessage("play", object=post)
|
||||
|
||||
def load_audios(self):
|
||||
|
@@ -115,11 +115,6 @@ class displayTopicPresenter(basePost.displayPostPresenter):
|
||||
attachments = ""
|
||||
if hasattr(comment, "attachments"):
|
||||
attachments = self.upload_attachments(comment.attachments)
|
||||
urls = utils.find_urls_in_text(msg)
|
||||
if len(urls) != 0:
|
||||
if len(attachments) == 0: attachments = urls[0]
|
||||
else: attachments += urls[0]
|
||||
msg = msg.replace(urls[0], "")
|
||||
if msg != "":
|
||||
kwargs.update(message=msg)
|
||||
if attachments != "":
|
||||
|
@@ -3,6 +3,7 @@
|
||||
As this player does not have (still) an associated GUI, I have decided to place here the code for the interactor, which connects a bunch of pubsub events, and the presenter itself.
|
||||
"""
|
||||
import sys
|
||||
import time
|
||||
import random
|
||||
import logging
|
||||
import sound_lib
|
||||
@@ -19,7 +20,6 @@ from sessionmanager import utils
|
||||
player = None
|
||||
log = logging.getLogger("player")
|
||||
|
||||
# This function will be deprecated when the player works with pubsub events, as will no longer be needed to instantiate and import the player directly.
|
||||
def setup():
|
||||
global player
|
||||
if player == None:
|
||||
@@ -34,6 +34,7 @@ class audioPlayer(object):
|
||||
self.is_playing = False
|
||||
# This will be the URLStream handler
|
||||
self.stream = None
|
||||
self.message = None
|
||||
self.vol = config.app["sound"]["volume"]
|
||||
# this variable is set to true when the URLPlayer is decoding something, thus it will block other calls to the play method.
|
||||
self.is_working = False
|
||||
@@ -41,13 +42,18 @@ class audioPlayer(object):
|
||||
self.queue = []
|
||||
# Index of the currently playing track.
|
||||
self.playing_track = 0
|
||||
self.playing_all = False
|
||||
self.worker = RepeatingTimer(5, self.player_function)
|
||||
self.worker.start()
|
||||
# Status of the player.
|
||||
self.stopped = True
|
||||
# Modify some default settings present in Bass so it will increase timeout connection, thus causing less "connection timed out" errors when playing.
|
||||
bassconfig = BassConfig()
|
||||
# Set timeout connection to 30 seconds.
|
||||
bassconfig["net_timeout"] = 30000
|
||||
# subscribe all pubsub events.
|
||||
pub.subscribe(self.play, "play")
|
||||
pub.subscribe(self.play_message, "play-message")
|
||||
pub.subscribe(self.play_all, "play-all")
|
||||
pub.subscribe(self.pause, "pause")
|
||||
pub.subscribe(self.stop, "stop")
|
||||
@@ -55,6 +61,18 @@ class audioPlayer(object):
|
||||
pub.subscribe(self.play_previous, "play-previous")
|
||||
pub.subscribe(self.seek, "seek")
|
||||
|
||||
# Stopped has a special function here, hence the decorator
|
||||
# when stopped will be set to True, it will send a pubsub event to inform other parts of the application about the status change.
|
||||
# this is useful for changing labels between play and pause, and so on, in buttons.
|
||||
@property
|
||||
def stopped(self):
|
||||
return self._stopped
|
||||
|
||||
@stopped.setter
|
||||
def stopped(self, value):
|
||||
self._stopped = value
|
||||
pub.sendMessage("playback-changed", stopped=value)
|
||||
|
||||
def play(self, object, set_info=True, fresh=False):
|
||||
""" Play an URl Stream.
|
||||
@object dict: typically an audio object as returned by VK, with a "url" component which must be a valid URL to a media file.
|
||||
@@ -70,9 +88,7 @@ class audioPlayer(object):
|
||||
log.exception("error when stopping the file")
|
||||
self.stream = None
|
||||
self.stopped = True
|
||||
if fresh == True and hasattr(self, "worker") and self.worker != None:
|
||||
self.worker.cancel()
|
||||
self.worker = None
|
||||
if fresh == True:
|
||||
self.queue = []
|
||||
# Make sure that there are no other sounds trying to be played.
|
||||
if self.is_working == False:
|
||||
@@ -94,16 +110,41 @@ class audioPlayer(object):
|
||||
self.stopped = False
|
||||
self.is_working = False
|
||||
|
||||
def play_message(self, message_url):
|
||||
if self.message != None and (self.message.is_playing == True or self.message.is_stalled == True):
|
||||
return self.stop_message()
|
||||
output.speak(_("Playing..."))
|
||||
url_ = utils.transform_audio_url(message_url)
|
||||
url_ = bytes(url_, "utf-8")
|
||||
try:
|
||||
self.message = URLStream(url=url_)
|
||||
except:
|
||||
log.error("Unable to play URL %s" % (url_))
|
||||
return
|
||||
self.message.volume = self.vol/100.0
|
||||
self.message.play()
|
||||
volume_percent = self.volume*0.25
|
||||
volume_step = self.volume*0.15
|
||||
while self.stream.volume*100 > volume_percent:
|
||||
self.stream.volume = self.stream.volume-(volume_step/100)
|
||||
time.sleep(0.1)
|
||||
|
||||
def stop(self):
|
||||
""" Stop audio playback. """
|
||||
if self.stream != None and self.stream.is_playing == True:
|
||||
self.stream.stop()
|
||||
self.stopped = True
|
||||
if hasattr(self, "worker") and self.worker != None:
|
||||
self.worker.cancel()
|
||||
self.worker = None
|
||||
self.queue = []
|
||||
|
||||
def stop_message(self):
|
||||
if hasattr(self, "message") and self.message != None and self.message.is_playing == True:
|
||||
self.message.stop()
|
||||
volume_step = self.volume*0.15
|
||||
while self.stream.volume*100 < self.volume:
|
||||
self.stream.volume = self.stream.volume+(volume_step/100)
|
||||
time.sleep(0.1)
|
||||
self.message = None
|
||||
|
||||
def pause(self):
|
||||
""" pause the current playback, without destroying the queue or the current stream. If the stream is already paused this function will resume the playback. """
|
||||
if self.stream != None:
|
||||
@@ -116,6 +157,8 @@ class audioPlayer(object):
|
||||
self.stopped = False
|
||||
except BassError:
|
||||
pass
|
||||
if self.playing_all == False and len(self.queue) > 0:
|
||||
self.playing_all = True
|
||||
|
||||
@property
|
||||
def volume(self):
|
||||
@@ -130,7 +173,11 @@ class audioPlayer(object):
|
||||
elif vol > 100:
|
||||
self.vol = 100
|
||||
if self.stream != None:
|
||||
self.stream.volume = self.vol/100.0
|
||||
if self.message != None and self.message.is_playing:
|
||||
self.stream.volume = (self.vol*0.25)/100.0
|
||||
self.message.volume = self.vol/100.0
|
||||
else:
|
||||
self.stream.volume = self.vol/100.0
|
||||
|
||||
def play_all(self, list_of_songs, shuffle=False):
|
||||
""" Play all passed songs and adds all of those to the queue.
|
||||
@@ -145,16 +192,24 @@ class audioPlayer(object):
|
||||
if shuffle:
|
||||
random.shuffle(self.queue)
|
||||
call_threaded(self.play, self.queue[self.playing_track])
|
||||
self.worker = RepeatingTimer(5, self.player_function)
|
||||
self.worker.start()
|
||||
self.playing_all = True
|
||||
|
||||
def player_function(self):
|
||||
""" Check if the stream has reached the end of the file so it will play the next song. """
|
||||
if self.message != None and self.message.is_playing == False and len(self.message) == self.message.position:
|
||||
volume_step = self.volume*0.15
|
||||
while self.stream.volume*100 < self.volume:
|
||||
self.stream.volume = self.stream.volume+(volume_step/100)
|
||||
time.sleep(0.1)
|
||||
if self.stream != None and self.stream.is_playing == False and self.stopped == False and len(self.stream) == self.stream.position:
|
||||
if len(self.queue) == 0 or self.playing_track >= len(self.queue):
|
||||
self.worker.cancel()
|
||||
if self.playing_track >= len(self.queue):
|
||||
self.stopped = True
|
||||
self.playing_all = False
|
||||
return
|
||||
if self.playing_track < len(self.queue):
|
||||
elif self.playing_all == False:
|
||||
self.stopped = True
|
||||
return
|
||||
elif self.playing_track < len(self.queue):
|
||||
self.playing_track += 1
|
||||
self.play(self.queue[self.playing_track])
|
||||
|
||||
|
@@ -20,8 +20,6 @@ count_for_audio_buffers = integer(default=1000)
|
||||
[chat]
|
||||
notify_online = boolean(default=True)
|
||||
notify_offline = boolean(default=True)
|
||||
open_unread_conversations = boolean(default=True)
|
||||
automove_to_conversations = boolean(default=False)
|
||||
notifications = string(default="custom")
|
||||
|
||||
[load_at_startup]
|
||||
|
@@ -75,6 +75,14 @@ def add_attachment(attachment):
|
||||
elif attachment["type"] == "poll":
|
||||
tpe = _("Poll")
|
||||
msg = attachment["poll"]["question"]
|
||||
elif attachment["type"] == "wall":
|
||||
tpe = _("Post")
|
||||
user = attachment["wall"]["from"]["name"]
|
||||
if len(attachment["wall"]["text"]) > 140:
|
||||
text = attachment["wall"]["text"][:145]+"..."
|
||||
else:
|
||||
text = attachment["wall"]["text"]
|
||||
msg = _("{user}: {post}").format(user=user, post=text)
|
||||
else:
|
||||
print(attachment)
|
||||
return [tpe, msg]
|
||||
|
@@ -40,7 +40,8 @@ def find_item(list, item):
|
||||
break
|
||||
if identifier == None:
|
||||
# if there are objects that can't be processed by lack of identifier, let's print keys for finding one.
|
||||
log.exception("Can't find an identifier for the following object: %r" % (list(item.keys()),))
|
||||
log.exception("Can't find an identifier for the following object: %r" % (item.keys(),))
|
||||
return False
|
||||
for i in list:
|
||||
if identifier in i and i[identifier] == item[identifier]:
|
||||
return True
|
||||
@@ -53,7 +54,7 @@ class vkSession(object):
|
||||
""" Put new items on the local cache database.
|
||||
@name str: The name for the buffer stored in the dictionary.
|
||||
@data list: A list with items and some information about cursors.
|
||||
returns the number of items that has been added in this execution"""
|
||||
returns the number of items that have been added in this execution"""
|
||||
global post_types
|
||||
# When this method is called by friends.getOnlyne, it gives only friend IDS so we need to retrieve full objects from VK.
|
||||
# ToDo: It would be nice to investigate whether reusing some existing objects would be a good idea, whenever possible.
|
||||
@@ -101,6 +102,7 @@ class vkSession(object):
|
||||
self.db = {}
|
||||
self.db["users"] = {}
|
||||
self.db["groups"] = {}
|
||||
self.db["group_info"] = {}
|
||||
|
||||
@property
|
||||
def is_logged(self):
|
||||
@@ -115,9 +117,13 @@ class vkSession(object):
|
||||
log.debug("Creating config file %s" % (file_,))
|
||||
self.settings = Configuration(os.path.join(paths.config_path(), file_), os.path.join(paths.app_path(), "session.defaults"))
|
||||
self.soundplayer = sound.soundSystem(config.app["sound"])
|
||||
pub.subscribe(self.play_sound, "play-sound")
|
||||
# except:
|
||||
# log.exception("The session configuration has failed.")
|
||||
|
||||
def play_sound(self, sound):
|
||||
self.soundplayer.play(sound)
|
||||
|
||||
def login(self):
|
||||
""" Logging in VK.com. This is basically the first method interacting with VK. """
|
||||
# If user is already logged in, we should skip this method.
|
||||
@@ -258,6 +264,10 @@ class vkSession(object):
|
||||
k = "{key}_{case}".format(key=key, case=i)
|
||||
v = self.db["groups"][abs(user_id)][i]
|
||||
user_data[k] = v
|
||||
else:
|
||||
group = self.vk.client.groups.getById(group_ids=-1*user_id)[0]
|
||||
self.process_usernames(data=dict(profiles=[], groups=[group]))
|
||||
return self.get_user(user_id=user_id, key=key)
|
||||
return user_data
|
||||
|
||||
def process_usernames(self, data):
|
||||
|
@@ -78,19 +78,16 @@ def transform_audio_url(url):
|
||||
""" Transforms the URL offered by VK to the unencrypted stream so we can still play it.
|
||||
This function will be updated every time VK decides to change something in their Audio API'S.
|
||||
Changelog:
|
||||
16/04/2019: Implemented this function. For now it replaces /index.m3u8 by .mp3, also removes the path component before "/audios" if the URL contains the word /audios, or the last path component before the filename if doesn't.
|
||||
17/04/2019: Updated function. Now it is not required to strip anything, just replacing /index.m3u8 with .mp3 should be enough.
|
||||
30/04/2019: Re-enabled old methods as VK changed everything as how it was working on 16.04.2019.
|
||||
17.04.2019: Updated function. Now it is not required to strip anything, just replacing /index.m3u8 with .mp3 should be enough.
|
||||
16.04.2019: Implemented this function. For now it replaces /index.m3u8 by .mp3, also removes the path component before "/audios" if the URL contains the word /audios, or the last path component before the filename if doesn't.
|
||||
"""
|
||||
if "vkuseraudio.net" not in url and "index.m3u8" not in url:
|
||||
return url
|
||||
url = url.replace("/index.m3u8", ".mp3")
|
||||
return url
|
||||
### The following code was useful for VK audio methods prior to 17/04/2019.
|
||||
# I just left this here because they may enable such change any time soon.
|
||||
### basically this method was requiring us to strip a part of the full URL.
|
||||
# parts = url.split("/")
|
||||
# if "/audio" not in url:
|
||||
# url = url.replace("/"+parts[-2], "")
|
||||
# else:
|
||||
# url = url.replace("/"+parts[-3], "")
|
||||
# return url
|
||||
parts = url.split("/")
|
||||
if "/audios" not in url:
|
||||
url = url.replace("/"+parts[-2], "")
|
||||
else:
|
||||
url = url.replace("/"+parts[-3], "")
|
||||
return url
|
BIN
src/sounds/default/checked.ogg
Normal file
BIN
src/sounds/default/checked.ogg
Normal file
Binary file not shown.
BIN
src/sounds/default/selected.ogg
Normal file
BIN
src/sounds/default/selected.ogg
Normal file
Binary file not shown.
BIN
src/sounds/default/unchecked.ogg
Normal file
BIN
src/sounds/default/unchecked.ogg
Normal file
Binary file not shown.
@@ -46,10 +46,6 @@ class chat(wx.Panel, widgetUtils.BaseDialog):
|
||||
sizer.Add(self.notify_online, 0, wx.ALL, 5)
|
||||
self.notify_offline = wx.CheckBox(self, wx.NewId(), _("Show notifications when users are offline"))
|
||||
sizer.Add(self.notify_offline, 0, wx.ALL, 5)
|
||||
self.open_unread_conversations = wx.CheckBox(self, wx.NewId(), _("Open unread conversations at startup"))
|
||||
sizer.Add(self.open_unread_conversations, 0, wx.ALL, 5)
|
||||
self.automove_to_conversations = wx.CheckBox(self, wx.NewId(), _("Move focus to new conversations"))
|
||||
sizer.Add(self.automove_to_conversations, 0, wx.ALL, 5)
|
||||
lbl = wx.StaticText(self, wx.NewId(), _("Notification type"))
|
||||
self.notifications = wx.ComboBox(self, wx.NewId(), choices=[_("Native"), _("Custom"),], value=_("Native"), style=wx.CB_READONLY)
|
||||
nbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
@@ -121,3 +121,51 @@ class createCommentDialog(createTextMessage):
|
||||
super(createCommentDialog, self).__init__()
|
||||
self.createControls(message, title, text)
|
||||
self.SetClientSize(self.mainBox.CalcMin())
|
||||
self.SetTitle(title)
|
||||
|
||||
class createTopicDialog(createCommentDialog):
|
||||
def createTextArea(self, message="", text=""):
|
||||
self.panel = wx.Panel(self)
|
||||
label = wx.StaticText(self.panel, -1, _("Title"))
|
||||
self.title = wx.TextCtrl(self.panel, wx.NewId())
|
||||
label2 = wx.StaticText(self.panel, -1, _("Message"))
|
||||
self.text = wx.TextCtrl(self.panel, -1, text, size=(439, -1), style=wx.TE_MULTILINE)
|
||||
self.title.SetFocus()
|
||||
self.textBox = wx.BoxSizer(wx.VERTICAL)
|
||||
titleb = wx.BoxSizer(wx.HORIZONTAL)
|
||||
titleb.Add(label, 0, wx.ALL, 5)
|
||||
titleb.Add(self.title, 0, wx.ALL, 5)
|
||||
self.textBox.Add(titleb, 0, wx.ALL, 5)
|
||||
textb = wx.BoxSizer(wx.HORIZONTAL)
|
||||
textb.Add(label2, 0, wx.ALL, 5)
|
||||
textb.Add(self.text, 0, wx.ALL, 5)
|
||||
self.textBox.Add(textb, 0, wx.ALL, 5)
|
||||
|
||||
def createControls(self, title, message, text):
|
||||
self.mainBox = wx.BoxSizer(wx.VERTICAL)
|
||||
self.createTextArea(message, text)
|
||||
self.mainBox.Add(self.textBox, 0, wx.ALL, 5)
|
||||
self.attach = wx.Button(self.panel, -1, _("Attach"), size=wx.DefaultSize)
|
||||
self.mention = wx.Button(self.panel, wx.NewId(), _("Tag a friend"))
|
||||
self.spellcheck = wx.Button(self.panel, -1, _("Spelling &correction"), size=wx.DefaultSize)
|
||||
self.translateButton = wx.Button(self.panel, -1, _("&Translate message"), size=wx.DefaultSize)
|
||||
self.okButton = wx.Button(self.panel, wx.ID_OK, _("Send"), size=wx.DefaultSize)
|
||||
self.okButton.SetDefault()
|
||||
cancelButton = wx.Button(self.panel, wx.ID_CANCEL, _("Close"), size=wx.DefaultSize)
|
||||
self.buttonsBox1 = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.buttonsBox1.Add(self.attach, 0, wx.ALL, 10)
|
||||
self.buttonsBox1.Add(self.mention, 0, wx.ALL, 10)
|
||||
self.buttonsBox1.Add(self.spellcheck, 0, wx.ALL, 10)
|
||||
self.buttonsBox1.Add(self.translateButton, 0, wx.ALL, 10)
|
||||
self.mainBox.Add(self.buttonsBox1, 0, wx.ALL, 10)
|
||||
self.ok_cancelSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.ok_cancelSizer.Add(self.okButton, 0, wx.ALL, 10)
|
||||
self.ok_cancelSizer.Add(cancelButton, 0, wx.ALL, 10)
|
||||
self.mainBox.Add(self.ok_cancelSizer)
|
||||
selectId = wx.NewId()
|
||||
self.Bind(wx.EVT_MENU, self.onSelect, id=selectId)
|
||||
self.accel_tbl = wx.AcceleratorTable([
|
||||
(wx.ACCEL_CTRL, ord('A'), selectId),])
|
||||
self.SetAcceleratorTable(self.accel_tbl)
|
||||
self.panel.SetSizer(self.mainBox)
|
||||
|
||||
|
@@ -4,7 +4,9 @@ from __future__ import unicode_literals
|
||||
import languageHandler
|
||||
import paths
|
||||
import wx
|
||||
import wx.lib.mixins.listctrl as listmix
|
||||
from builtins import range
|
||||
from pubsub import pub
|
||||
|
||||
toolkit = "wx"
|
||||
|
||||
@@ -134,51 +136,91 @@ class mainLoopObject(wx.App):
|
||||
def run(self):
|
||||
self.app.MainLoop()
|
||||
|
||||
class multiselectionBaseList(wx.ListCtrl, listmix.CheckListCtrlMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
wx.ListCtrl.__init__(self, *args, **kwargs)
|
||||
listmix.CheckListCtrlMixin.__init__(self)
|
||||
self.Bind(wx.EVT_CHAR_HOOK, self.on_keydown)
|
||||
self.Bind(wx.EVT_LIST_ITEM_FOCUSED, self.on_focus)
|
||||
|
||||
def on_focus(self, event):
|
||||
currentItem = self.GetFocusedItem()
|
||||
if self.IsChecked(currentItem):
|
||||
pub.sendMessage("play-sound", sound="selected.ogg")
|
||||
event.Skip()
|
||||
|
||||
def OnCheckItem(self, index, flag):
|
||||
if flag == True:
|
||||
pub.sendMessage("play-sound", sound="checked.ogg")
|
||||
else:
|
||||
pub.sendMessage("play-sound", sound="unchecked.ogg")
|
||||
|
||||
def on_keydown(self, event):
|
||||
if event.GetKeyCode() == wx.WXK_SPACE:
|
||||
self.ToggleItem(self.GetFocusedItem())
|
||||
event.Skip()
|
||||
|
||||
class list(object):
|
||||
def __init__(self, parent, *columns, **listArguments):
|
||||
self.columns = columns
|
||||
self.listArguments = listArguments
|
||||
self.create_list(parent)
|
||||
def __init__(self, parent, *columns, **listArguments):
|
||||
self.columns = columns
|
||||
self.listArguments = listArguments
|
||||
self.create_list(parent)
|
||||
|
||||
def set_windows_size(self, column, characters_max):
|
||||
self.list.SetColumnWidth(column, characters_max*2)
|
||||
def set_windows_size(self, column, characters_max):
|
||||
self.list.SetColumnWidth(column, characters_max*2)
|
||||
|
||||
def set_size(self):
|
||||
self.list.SetSize((self.list.GetBestSize()[0], 1000))
|
||||
def set_size(self):
|
||||
self.list.SetSize((self.list.GetBestSize()[0], 1000))
|
||||
|
||||
def create_list(self, parent):
|
||||
self.list = wx.ListCtrl(parent, -1, **self.listArguments)
|
||||
for i in range(0, len(self.columns)):
|
||||
self.list.InsertColumn(i, "%s" % (self.columns[i]))
|
||||
def create_list(self, parent):
|
||||
self.list = wx.ListCtrl(parent, -1, **self.listArguments)
|
||||
for i in range(0, len(self.columns)):
|
||||
self.list.InsertColumn(i, "%s" % (self.columns[i]))
|
||||
|
||||
def insert_item(self, reversed, *item):
|
||||
""" Inserts an item on the list."""
|
||||
if reversed == False: items = self.list.GetItemCount()
|
||||
else: items = 0
|
||||
self.list.InsertItem(items, item[0])
|
||||
for i in range(1, len(self.columns)):
|
||||
self.list.SetItem(items, i, item[i])
|
||||
def insert_item(self, reversed, *item):
|
||||
""" Inserts an item on the list."""
|
||||
if reversed == False: items = self.list.GetItemCount()
|
||||
else: items = 0
|
||||
self.list.InsertItem(items, item[0])
|
||||
for i in range(1, len(self.columns)):
|
||||
self.list.SetItem(items, i, item[i])
|
||||
|
||||
def remove_item(self, pos):
|
||||
""" Deletes an item from the list."""
|
||||
if pos > 0: self.list.Focus(pos-1)
|
||||
self.list.DeleteItem(pos)
|
||||
def remove_item(self, pos):
|
||||
""" Deletes an item from the list."""
|
||||
if pos > 0: self.list.Focus(pos-1)
|
||||
self.list.DeleteItem(pos)
|
||||
|
||||
def clear(self):
|
||||
self.list.DeleteAllItems()
|
||||
def clear(self):
|
||||
self.list.DeleteAllItems()
|
||||
|
||||
def get_selected(self):
|
||||
return self.list.GetFocusedItem()
|
||||
def get_selected(self):
|
||||
return self.list.GetFocusedItem()
|
||||
|
||||
def select_item(self, pos):
|
||||
self.list.Focus(pos)
|
||||
def select_item(self, pos):
|
||||
self.list.Focus(pos)
|
||||
|
||||
def get_count(self):
|
||||
selected = self.list.GetItemCount()
|
||||
if selected == -1:
|
||||
return 0
|
||||
else:
|
||||
return selected
|
||||
def get_count(self):
|
||||
selected = self.list.GetItemCount()
|
||||
if selected == -1:
|
||||
return 0
|
||||
else:
|
||||
return selected
|
||||
|
||||
def Enable(self, value):
|
||||
return self.list.Enable(value)
|
||||
def Enable(self, value):
|
||||
return self.list.Enable(value)
|
||||
|
||||
class multiselectionList(list):
|
||||
|
||||
def create_list(self, parent):
|
||||
self.list = multiselectionBaseList(parent, -1, **self.listArguments)
|
||||
for i in range(0, len(self.columns)):
|
||||
self.list.InsertColumn(i, "%s" % (self.columns[i]))
|
||||
|
||||
def get_multiple_selection(self):
|
||||
selected = []
|
||||
for item in range(0, self.list.GetItemCount()):
|
||||
if self.list.IsChecked(item):
|
||||
selected.append(item)
|
||||
if len(selected) == 0 and self.list.GetFocusedItem() != -1:
|
||||
selected.append(self.list.GetFocusedItem())
|
||||
return selected
|
@@ -5,16 +5,18 @@ import widgetUtils
|
||||
|
||||
class timelineDialog(widgetUtils.BaseDialog):
|
||||
|
||||
def __init__(self, users=[]):
|
||||
def __init__(self, users=[], show_selector=True):
|
||||
super(timelineDialog, self).__init__(parent=None, title=_("New timeline for {0}").format(users[0],))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
userLabel = wx.StaticText(panel, -1, _("User"))
|
||||
self.cb = wx.ComboBox(panel, -1, choices=users, value=users[0])
|
||||
self.cb.SetFocus()
|
||||
userSizer = wx.BoxSizer()
|
||||
userSizer.Add(userLabel, 0, wx.ALL, 5)
|
||||
userSizer.Add(self.cb, 0, wx.ALL, 5)
|
||||
if show_selector:
|
||||
userLabel = wx.StaticText(panel, -1, _("User"))
|
||||
self.cb = wx.ComboBox(panel, -1, choices=users, value=users[0])
|
||||
self.cb.SetFocus()
|
||||
userSizer = wx.BoxSizer()
|
||||
userSizer.Add(userLabel, 0, wx.ALL, 5)
|
||||
userSizer.Add(self.cb, 0, wx.ALL, 5)
|
||||
sizer.Add(userSizer, 0, wx.ALL, 5)
|
||||
actionsSizer = wx.StaticBoxSizer(parent=panel, orient=wx.VERTICAL, label=_("Buffer type"))
|
||||
self.wall = wx.RadioButton(actionsSizer.GetStaticBox(), wx.NewId(), _("&Wall posts"), style=wx.RB_GROUP)
|
||||
self.audio = wx.RadioButton(actionsSizer.GetStaticBox(), wx.NewId(), _("Audio"))
|
||||
|
@@ -133,10 +133,14 @@ class mainWindow(wx.Frame):
|
||||
def advance_selection(self, forward):
|
||||
self.tb.AdvanceSelection(forward)
|
||||
|
||||
def about_dialog(self, *args, **kwargs):
|
||||
def about_dialog(self, channel="stable", *args, **kwargs):
|
||||
if channel == "stable":
|
||||
version = _("{version} (stable)").format(version=application.version)
|
||||
else:
|
||||
version = _("{version} (alpha)").format(version=application.update_next_version)
|
||||
info = wx.adv.AboutDialogInfo()
|
||||
info.SetName(application.name)
|
||||
info.SetVersion(application.version)
|
||||
info.SetVersion(version)
|
||||
info.SetDescription(application.description)
|
||||
info.SetCopyright(application.copyright)
|
||||
info.SetTranslators(application.translators)
|
||||
|
@@ -57,14 +57,14 @@ class communityTab(feedTab):
|
||||
def create_post_buttons(self):
|
||||
self.postBox = wx.StaticBoxSizer(parent=self, orient=wx.HORIZONTAL, label=_("Actions"))
|
||||
self.load = wx.Button(self.postBox.GetStaticBox(), wx.NewId(), _("Load buffer"))
|
||||
self.post = wx.Button(self.postBox.GetStaticBox(), -1, _("&Post"))
|
||||
self.post = wx.Button(self.postBox.GetStaticBox(), -1, _("&Post in group"))
|
||||
self.postBox.Add(self.load, 0, wx.ALL, 5)
|
||||
self.postBox.Add(self.post, 0, wx.ALL, 5)
|
||||
|
||||
class audioTab(homeTab):
|
||||
def create_list(self):
|
||||
self.lbl = wx.StaticText(self, wx.NewId(), _("Mu&sic"))
|
||||
self.list = widgetUtils.list(self, *[_("Title"), _("Artist"), _("Duration")], style=wx.LC_REPORT)
|
||||
self.list = widgetUtils.multiselectionList(self, *[_("Title"), _("Artist"), _("Duration")], style=wx.LC_REPORT)
|
||||
self.list.set_windows_size(0, 160)
|
||||
self.list.set_windows_size(1, 380)
|
||||
self.list.set_windows_size(2, 80)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
{"current_version": "0.19",
|
||||
{"current_version": "0.20",
|
||||
"description": ".",
|
||||
"downloads":
|
||||
{"Windows32": "https://code.manuelcortez.net/manuelcortez/socializer/-/jobs/artifacts/v0.19/raw/socializer.zip?job=stable"}}
|
||||
{"Windows32": "https://code.manuelcortez.net/manuelcortez/socializer/-/jobs/artifacts/v0.20/raw/socializer.zip?job=stable"}}
|
Reference in New Issue
Block a user