mirror of
https://github.com/MCV-Software/TWBlue.git
synced 2025-07-18 06:06:06 -04:00
Putting all the code from the current master branch of TWBlue
This commit is contained in:
3
src/extra/AudioUploader/__init__.py
Normal file
3
src/extra/AudioUploader/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
import gui, transfer, transfer_dialogs, platform
|
||||
if platform.system() != "Darwin":
|
||||
import dropbox
|
113
src/extra/AudioUploader/dropbox_transfer.py
Normal file
113
src/extra/AudioUploader/dropbox_transfer.py
Normal file
@@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import threading
|
||||
import time
|
||||
import os
|
||||
import exceptions
|
||||
import wx
|
||||
import dropbox
|
||||
import config
|
||||
from mysc import event
|
||||
from utils import *
|
||||
from dropbox.rest import ErrorResponse
|
||||
from StringIO import StringIO
|
||||
|
||||
class UnauthorisedError(exceptions.Exception):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(UnauthorisedError, self).__init__(*args, **kwargs)
|
||||
|
||||
class newChunkedUploader(dropbox.client.ChunkedUploader):
|
||||
def __init__(self, client, file_obj, length, callback):
|
||||
super(newChunkedUploader, self).__init__(client, file_obj, length)
|
||||
self.progress_callback = callback
|
||||
|
||||
def upload_chunked(self, chunk_size = 4 * 1024 * 1024):
|
||||
while self.offset < self.target_length:
|
||||
next_chunk_size = min(chunk_size, self.target_length - self.offset)
|
||||
if self.last_block == None:
|
||||
self.last_block = self.file_obj.read(next_chunk_size)
|
||||
|
||||
try:
|
||||
(self.offset, self.upload_id) = self.client.upload_chunk(
|
||||
StringIO(self.last_block), next_chunk_size, self.offset, self.upload_id)
|
||||
self.last_block = None
|
||||
if callable(self.progress_callback): self.progress_callback(self.offset)
|
||||
except ErrorResponse as e:
|
||||
reply = e.body
|
||||
if "offset" in reply and reply['offset'] != 0:
|
||||
if reply['offset'] > self.offset:
|
||||
self.last_block = None
|
||||
self.offset = reply['offset']
|
||||
|
||||
class dropboxLogin(object):
|
||||
def __init__(self):
|
||||
self.logged = False
|
||||
self.app_key = "c8ikm0gexqvovol"
|
||||
self.app_secret = "gvvi6fzfecooast"
|
||||
|
||||
def get_url(self):
|
||||
self.flow = dropbox.client.DropboxOAuth2FlowNoRedirect(self.app_key, self.app_secret)
|
||||
return self.flow.start()
|
||||
|
||||
def authorise(self, code):
|
||||
access_token, user_id = self.flow.finish(code)
|
||||
config.main["services"]["dropbox_token"] = access_token
|
||||
self.logged = True
|
||||
|
||||
class dropboxUploader(object):
|
||||
def __init__(self, filename, completed_callback, wxDialog, short_url=False):
|
||||
if config.main["services"]["dropbox_token"] != "":
|
||||
self.client = dropbox.client.DropboxClient(config.main["services"]["dropbox_token"])
|
||||
else:
|
||||
raise UnauthorisedError("You need authorise TWBlue")
|
||||
self.filename = filename
|
||||
self.short_url = short_url
|
||||
self.wxDialog = wxDialog
|
||||
self.file = open(self.filename, "rb")
|
||||
self.file_size = os.path.getsize(self.filename)
|
||||
self.uploader = newChunkedUploader(client=self.client, file_obj=self.file, length=self.file_size, callback=self.process)
|
||||
self.start_time = None
|
||||
self.completed_callback = completed_callback
|
||||
self.background_thread = None
|
||||
self.current = 0
|
||||
self.transfer_rate = 0
|
||||
|
||||
def elapsed_time(self):
|
||||
if not self.start_time:
|
||||
return 0
|
||||
return time.time() - self.start_time
|
||||
|
||||
def perform_transfer(self):
|
||||
self.start_time = time.time()
|
||||
while self.uploader.offset < self.file_size:
|
||||
self.uploader.upload_chunked(self.file_size/100)
|
||||
self.transfer_completed()
|
||||
|
||||
def process(self, offset):
|
||||
progress = {}
|
||||
self.current = offset
|
||||
progress["total"] = self.file_size
|
||||
progress["current"] = self.current
|
||||
progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100)
|
||||
self.transfer_rate = progress["current"] / self.elapsed_time()
|
||||
progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate)
|
||||
if self.transfer_rate:
|
||||
progress["eta"] = (progress["total"] - progress["current"]) / self.transfer_rate
|
||||
else:
|
||||
progress["eta"] = 0
|
||||
info = event.event(event.EVT_OBJECT, 1)
|
||||
info.SetItem(progress)
|
||||
wx.PostEvent(self.wxDialog, info)
|
||||
|
||||
def perform_threaded(self):
|
||||
self.background_thread = threading.Thread(target=self.perform_transfer)
|
||||
self.background_thread.daemon = True
|
||||
self.background_thread.start()
|
||||
|
||||
def transfer_completed(self):
|
||||
self.uploader.finish(os.path.basename(self.filename))
|
||||
if callable(self.completed_callback):
|
||||
self.completed_callback()
|
||||
|
||||
def get_url(self):
|
||||
original = "%s" % (self.client.share(os.path.basename(self.filename), False)["url"])
|
||||
return original.replace("dl=0", "dl=1")
|
190
src/extra/AudioUploader/gui.py
Normal file
190
src/extra/AudioUploader/gui.py
Normal file
@@ -0,0 +1,190 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
import wx
|
||||
import output
|
||||
import tempfile
|
||||
import sound
|
||||
import os
|
||||
import config
|
||||
from mysc.thread_utils import call_threaded
|
||||
import sound_lib
|
||||
|
||||
class audioDialog(wx.Dialog):
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
wx.Dialog.__init__(self, None, -1, _(u"Attach audio"))
|
||||
self.file = None
|
||||
self.recorded = False
|
||||
self.recording = None
|
||||
self.playing = None
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self.play = wx.Button(panel, -1, _(u"Play"))
|
||||
self.play.Bind(wx.EVT_BUTTON, self.onPlay)
|
||||
self.play.Disable()
|
||||
self.pause = wx.Button(panel, -1, _(u"Pause"))
|
||||
self.pause.Bind(wx.EVT_BUTTON, self.onPause)
|
||||
self.pause.Disable()
|
||||
self.record = wx.Button(panel, -1, _(u"Record"))
|
||||
self.record.Bind(wx.EVT_BUTTON, self.onRecord)
|
||||
self.record.SetFocus()
|
||||
self.attach_exists = wx.Button(panel, -1, _(u"Add an existing file"))
|
||||
self.attach_exists.Bind(wx.EVT_BUTTON, self.onAttach)
|
||||
self.discard = wx.Button(panel, -1, _(u"Discard"))
|
||||
self.discard.Bind(wx.EVT_BUTTON, self.onDiscard)
|
||||
self.discard.Disable()
|
||||
label = wx.StaticText(panel, -1, _(u"Upload to"))
|
||||
self.services = wx.ComboBox(panel, -1, choices=self.get_available_services(), value=self.get_available_services()[0], style=wx.CB_READONLY)
|
||||
servicesBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
servicesBox.Add(label)
|
||||
servicesBox.Add(self.services)
|
||||
self.attach = wx.Button(panel, wx.ID_OK, _(u"Attach"))
|
||||
self.attach.Disable()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL, _(u"Cancel"))
|
||||
sizer.Add(self.play)
|
||||
sizer.Add(self.pause)
|
||||
sizer.Add(self.record)
|
||||
sizer.Add(self.attach_exists)
|
||||
sizer.Add(self.discard)
|
||||
sizer.Add(self.attach)
|
||||
|
||||
def get_available_services(self):
|
||||
services = []
|
||||
if config.main["services"]["dropbox_token"] != "":
|
||||
services.append("Dropbox")
|
||||
services.append("SNDUp")
|
||||
return services
|
||||
|
||||
def onPause(self, ev):
|
||||
if self.pause.GetLabel() == _(u"Pause"):
|
||||
self.recording.pause()
|
||||
self.pause.SetLabel(_(u"Resume"))
|
||||
elif self.pause.GetLabel() == _(u"Resume"):
|
||||
self.recording.play()
|
||||
self.pause.SetLabel(_(U"Pause"))
|
||||
|
||||
def onRecord(self, ev):
|
||||
if self.recording != None:
|
||||
self.stop_recording()
|
||||
self.pause.Disable()
|
||||
else:
|
||||
self.start_recording()
|
||||
self.pause.Enable()
|
||||
|
||||
def start_recording(self):
|
||||
self.attach_exists.Disable()
|
||||
self.file = tempfile.mktemp(suffix='.wav')
|
||||
self.recording = sound.recording(self.file)
|
||||
self.recording.play()
|
||||
self.record.SetLabel(_(u"Stop recording"))
|
||||
output.speak(_(u"Recording"))
|
||||
|
||||
def stop_recording(self):
|
||||
self.recording.stop()
|
||||
self.recording.free()
|
||||
output.speak(_(u"Stopped"))
|
||||
self.recorded = True
|
||||
self.record.SetLabel(_(u"Record"))
|
||||
self.file_attached()
|
||||
|
||||
def file_attached(self):
|
||||
self.pause.SetLabel(_(u"Pause"))
|
||||
self.record.Disable()
|
||||
self.play.Enable()
|
||||
self.discard.Enable()
|
||||
self.attach_exists.Disable()
|
||||
self.attach.Enable()
|
||||
self.play.SetFocus()
|
||||
|
||||
def onDiscard(self, evt):
|
||||
evt.Skip()
|
||||
if self.playing:
|
||||
self._stop()
|
||||
if self.recording != None:
|
||||
self.attach.Disable()
|
||||
self.play.Disable()
|
||||
self.file = None
|
||||
self.record.Enable()
|
||||
self.attach_exists.Enable()
|
||||
self.record.SetFocus()
|
||||
self.discard.Disable()
|
||||
self.recording = None
|
||||
output.speak(_(u"Discarded"))
|
||||
|
||||
def onPlay(self, evt):
|
||||
evt.Skip()
|
||||
if not self.playing:
|
||||
call_threaded(self._play)
|
||||
else:
|
||||
self._stop()
|
||||
|
||||
def _play(self):
|
||||
output.speak(_(u"Playing..."))
|
||||
# try:
|
||||
self.playing = sound_lib.stream.FileStream(file=unicode(self.file), flags=sound_lib.stream.BASS_UNICODE)
|
||||
self.playing.play()
|
||||
self.play.SetLabel(_(u"Stop"))
|
||||
try:
|
||||
while self.playing.is_playing:
|
||||
pass
|
||||
self.play.SetLabel(_(u"Play"))
|
||||
self.playing.free()
|
||||
self.playing = None
|
||||
except:
|
||||
pass
|
||||
|
||||
def _stop(self):
|
||||
output.speak(_(u"Stopped"))
|
||||
self.playing.stop()
|
||||
self.playing.free()
|
||||
self.play.SetLabel(_(u"Play"))
|
||||
self.playing = None
|
||||
|
||||
def postprocess(self):
|
||||
if self.file.lower().endswith('.wav'):
|
||||
output.speak(_(u"Recoding audio..."))
|
||||
sound.recode_audio(self.file)
|
||||
self.wav_file = self.file
|
||||
self.file = '%s.ogg' % self.file[:-4]
|
||||
|
||||
def cleanup(self):
|
||||
if self.playing and self.playing.is_playing:
|
||||
self.playing.stop()
|
||||
if self.recording != None:
|
||||
if self.recording.is_playing:
|
||||
self.recording.stop()
|
||||
try:
|
||||
self.recording.free()
|
||||
except:
|
||||
pass
|
||||
os.remove(self.file)
|
||||
if hasattr(self, 'wav_file'):
|
||||
os.remove(self.wav_file)
|
||||
del(self.wav_file)
|
||||
if hasattr(self, 'wav_file') and os.path.exists(self.file):
|
||||
os.remove(self.file)
|
||||
|
||||
|
||||
def onAttach(self, ev):
|
||||
openFileDialog = wx.FileDialog(self, _(u"Select the audio file to be uploaded"), "", "", _("Audio Files (*.mp3, *.ogg, *.wav)|*.mp3; *.ogg; *.wav"), wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
|
||||
if openFileDialog.ShowModal() == wx.ID_CANCEL:
|
||||
return
|
||||
self.file = openFileDialog.GetPath()
|
||||
self.file_attached()
|
||||
ev.Skip()
|
107
src/extra/AudioUploader/transfer.py
Normal file
107
src/extra/AudioUploader/transfer.py
Normal file
@@ -0,0 +1,107 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pycurl
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import json
|
||||
import wx
|
||||
from mysc import event
|
||||
from utils import *
|
||||
|
||||
#__all__ = ['TransferDialog', 'DownloadDialog', 'UploadDialog']
|
||||
|
||||
class Transfer(object):
|
||||
|
||||
def __init__(self, url=None, filename=None, follow_location=True, completed_callback=None, verbose=False, wxDialog=None, *args, **kwargs):
|
||||
self.url = url
|
||||
self.filename = filename
|
||||
self.curl = pycurl.Curl()
|
||||
self.start_time = None
|
||||
self.completed_callback = completed_callback
|
||||
self.background_thread = None
|
||||
self.transfer_rate = 0
|
||||
self.curl.setopt(self.curl.PROGRESSFUNCTION, self.progress_callback)
|
||||
self.curl.setopt(self.curl.URL, url)
|
||||
self.curl.setopt(self.curl.NOPROGRESS, 0)
|
||||
self.curl.setopt(self.curl.HTTP_VERSION, self.curl.CURL_HTTP_VERSION_1_0)
|
||||
self.curl.setopt(self.curl.FOLLOWLOCATION, int(follow_location))
|
||||
self.curl.setopt(self.curl.VERBOSE, int(verbose))
|
||||
self.wxDialog = wxDialog
|
||||
super(Transfer, self).__init__(*args, **kwargs)
|
||||
|
||||
def elapsed_time(self):
|
||||
if not self.start_time:
|
||||
return 0
|
||||
return time.time() - self.start_time
|
||||
|
||||
def progress_callback(self, down_total, down_current, up_total, up_current):
|
||||
progress = {}
|
||||
progress["total"] = up_total
|
||||
progress["current"] = up_current
|
||||
# else:
|
||||
# print "Killed function"
|
||||
# return
|
||||
if progress["current"] == 0:
|
||||
progress["percent"] = 0
|
||||
self.transfer_rate = 0
|
||||
else:
|
||||
progress["percent"] = int((float(progress["current"]) / progress["total"]) * 100)
|
||||
self.transfer_rate = progress["current"] / self.elapsed_time()
|
||||
progress["speed"] = '%s/s' % convert_bytes(self.transfer_rate)
|
||||
if self.transfer_rate:
|
||||
progress["eta"] = (progress["total"] - progress["current"]) / self.transfer_rate
|
||||
else:
|
||||
progress["eta"] = 0
|
||||
info = event.event(event.EVT_OBJECT, 1)
|
||||
info.SetItem(progress)
|
||||
wx.PostEvent(self.wxDialog, info)
|
||||
|
||||
def perform_transfer(self):
|
||||
self.start_time = time.time()
|
||||
self.curl.perform()
|
||||
self.curl.close()
|
||||
wx.CallAfter(self.complete_transfer)
|
||||
|
||||
def perform_threaded(self):
|
||||
self.background_thread = threading.Thread(target=self.perform_transfer)
|
||||
self.background_thread.daemon = True
|
||||
self.background_thread.start()
|
||||
|
||||
def complete_transfer(self):
|
||||
if callable(self.completed_callback):
|
||||
self.curl.close()
|
||||
self.completed_callback()
|
||||
|
||||
class Upload(Transfer):
|
||||
|
||||
def __init__(self, field=None, filename=None, *args, **kwargs):
|
||||
super(Upload, self).__init__(filename=filename, *args, **kwargs)
|
||||
self.response = dict()
|
||||
self.curl.setopt(self.curl.POST, 1)
|
||||
if isinstance(filename, unicode):
|
||||
local_filename = filename.encode(sys.getfilesystemencoding())
|
||||
else:
|
||||
local_filename = filename
|
||||
self.curl.setopt(self.curl.HTTPPOST, [(field, (self.curl.FORM_FILE, local_filename, self.curl.FORM_FILENAME, filename.encode("utf-8")))])
|
||||
self.curl.setopt(self.curl.HEADERFUNCTION, self.header_callback)
|
||||
self.curl.setopt(self.curl.WRITEFUNCTION, self.body_callback)
|
||||
|
||||
def header_callback(self, content):
|
||||
self.response['header'] = content
|
||||
|
||||
def body_callback(self, content):
|
||||
self.response['body'] = content
|
||||
|
||||
def get_url(self):
|
||||
return json.loads(self.response['body'])['url']
|
||||
|
||||
class Download(Transfer):
|
||||
|
||||
def __init__(self, follow_location=True, *args, **kwargs):
|
||||
super(Download, self).__init__(*args, **kwargs)
|
||||
self.download_file = open(self.filename, 'wb')
|
||||
self.curl.setopt(self.curl.WRITEFUNCTION, self.download_file.write)
|
||||
|
||||
def complete_transfer(self):
|
||||
self.download_file.close()
|
||||
super(DownloadDialog, self).complete_transfer()
|
72
src/extra/AudioUploader/transfer_dialogs.py
Normal file
72
src/extra/AudioUploader/transfer_dialogs.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
from mysc import event
|
||||
from utils import *
|
||||
|
||||
__all__ = ['TransferDialog', 'DownloadDialog', 'UploadDialog']
|
||||
|
||||
class TransferDialog(wx.Dialog):
|
||||
|
||||
def __init__(self, filename, *args, **kwargs):
|
||||
super(TransferDialog, self).__init__(*args, **kwargs)
|
||||
self.pane = wx.Panel(self)
|
||||
self.progress_bar = wx.Gauge(parent=self.pane)
|
||||
fileBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
fileLabel = wx.StaticText(self.pane, -1, _(u"File"))
|
||||
self.file = wx.TextCtrl(self.pane, -1, value=filename, style=wx.TE_READONLY|wx.TE_MULTILINE, size=(200, 100))
|
||||
self.file.SetFocus()
|
||||
fileBox.Add(fileLabel)
|
||||
fileBox.Add(self.file)
|
||||
currentAmountBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
current_amount_label = wx.StaticText(self.pane, -1, _(u"Transferred"))
|
||||
self.current_amount = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||
currentAmountBox.Add(current_amount_label)
|
||||
currentAmountBox.Add(self.current_amount)
|
||||
totalSizeBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
total_size_label = wx.StaticText(self.pane, -1, _(u"Total file size"))
|
||||
self.total_size = wx.TextCtrl(self.pane, -1, value='0', style=wx.TE_READONLY|wx.TE_MULTILINE)
|
||||
totalSizeBox.Add(total_size_label)
|
||||
totalSizeBox.Add(self.total_size)
|
||||
speedBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
speedLabel = wx.StaticText(self.pane, -1, _(u"Transfer rate"))
|
||||
self.speed = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="0 Kb/s")
|
||||
speedBox.Add(speedLabel)
|
||||
speedBox.Add(self.speed)
|
||||
etaBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
etaLabel = wx.StaticText(self.pane, -1, _(u"Time left"))
|
||||
self.eta = wx.TextCtrl(self.pane, -1, style=wx.TE_READONLY|wx.TE_MULTILINE, value="Unknown", size=(200, 100))
|
||||
etaBox.Add(etaLabel)
|
||||
etaBox.Add(self.eta)
|
||||
# self.create_buttons()
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(fileBox)
|
||||
sizer.Add(currentAmountBox)
|
||||
sizer.Add(totalSizeBox)
|
||||
sizer.Add(speedBox)
|
||||
sizer.Add(etaBox)
|
||||
sizer.Add(self.progress_bar)
|
||||
self.pane.SetSizerAndFit(sizer)
|
||||
self.Bind(event.MyEVT_OBJECT, self.update)
|
||||
|
||||
def update(self, ev):
|
||||
data = ev.GetItem()
|
||||
wx.CallAfter(self.progress_bar.SetValue, data["percent"])
|
||||
wx.CallAfter(self.current_amount.SetValue, '%s (%d%%)' % (convert_bytes(data["current"]), data["percent"]))
|
||||
wx.CallAfter(self.total_size.SetValue, convert_bytes(data["total"]))
|
||||
wx.CallAfter(self.speed.SetValue, data["speed"])
|
||||
if data["eta"]:
|
||||
wx.CallAfter(self.eta.SetValue, seconds_to_string(data["eta"]))
|
||||
|
||||
def create_buttons(self):
|
||||
self.cancel_button = wx.Button(parent=self.pane, id=wx.ID_CANCEL)
|
||||
self.cancel_button.Bind(wx.EVT_BUTTON, self.on_cancel)
|
||||
|
||||
class UploadDialog(TransferDialog):
|
||||
|
||||
def __init__(self, filename=None, *args, **kwargs):
|
||||
super(UploadDialog, self).__init__(filename=filename, *args, **kwargs)
|
||||
|
||||
class DownloadDialog(TransferDialog):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Download, self).__init__(*args, **kwargs)
|
42
src/extra/AudioUploader/utils.py
Normal file
42
src/extra/AudioUploader/utils.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
def convert_bytes(n):
|
||||
K, M, G, T, P = 1 << 10, 1 << 20, 1 << 30, 1 << 40, 1 << 50
|
||||
if n >= P:
|
||||
return '%.2fPb' % (float(n) / T)
|
||||
elif n >= T:
|
||||
return '%.2fTb' % (float(n) / T)
|
||||
elif n >= G:
|
||||
return '%.2fGb' % (float(n) / G)
|
||||
elif n >= M:
|
||||
return '%.2fMb' % (float(n) / M)
|
||||
elif n >= K:
|
||||
return '%.2fKb' % (float(n) / K)
|
||||
else:
|
||||
return '%d' % n
|
||||
|
||||
def seconds_to_string(seconds, precision=0):
|
||||
day = seconds // 86400
|
||||
hour = seconds // 3600
|
||||
min = (seconds // 60) % 60
|
||||
sec = seconds - (hour * 3600) - (min * 60)
|
||||
sec_spec = "." + str(precision) + "f"
|
||||
sec_string = sec.__format__(sec_spec)
|
||||
string = ""
|
||||
if day == 1:
|
||||
string += _(u"%d day, ") % day
|
||||
elif day >= 2:
|
||||
string += _(u"%d days, ") % day
|
||||
if (hour == 1):
|
||||
string += _(u"%d hour, ") % hour
|
||||
elif (hour >= 2):
|
||||
string += _("%d hours, ") % hour
|
||||
if (min == 1):
|
||||
string += _(u"%d minute, ") % min
|
||||
elif (min >= 2):
|
||||
string += _(u"%d minutes, ") % min
|
||||
if sec >= 0 and sec <= 2:
|
||||
string += _(u"%s second") % sec_string
|
||||
else:
|
||||
string += _(u"%s seconds") % sec_string
|
||||
return string
|
1
src/extra/SoundsTutorial/__init__.py
Normal file
1
src/extra/SoundsTutorial/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
import gui
|
59
src/extra/SoundsTutorial/gui.py
Normal file
59
src/extra/SoundsTutorial/gui.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import wx
|
||||
import config
|
||||
import os
|
||||
import paths
|
||||
import sound
|
||||
|
||||
class soundsTutorial(wx.Dialog):
|
||||
def __init__(self):
|
||||
self.actions = [
|
||||
_(u"The tweet may contain a playable audio"),
|
||||
_(u"A timeline has been created"),
|
||||
_(u"A timeline has been deleted"),
|
||||
_(u"You've received a direct message"),
|
||||
_(u"You've sent a direct message"),
|
||||
_(u"A bug has happened"),
|
||||
_(u"You've added a tweet to your favourites"),
|
||||
_(u"Someone's favourites have been updated"),
|
||||
_(u"There are no more tweets to read"),
|
||||
_(u"A list has a new tweet"),
|
||||
_(u"You can't add any more characters on the tweet"),
|
||||
_(u"You've been mentioned "),
|
||||
_(u"A new event has happened"),
|
||||
_(u"TW Blue is ready "),
|
||||
_(u"You've replied"),
|
||||
_(u"You've retweeted"),
|
||||
_(u"A search has been updated"),
|
||||
_(u"There's a new tweet in the main buffer"),
|
||||
_(u"You've sent a tweet"),
|
||||
_(u"There's a new tweet in a timeline"),
|
||||
_(u"You have a new follower"),
|
||||
_(u"You've turned the volume up or down")]
|
||||
self.files = os.listdir(paths.sound_path("default"))
|
||||
super(soundsTutorial, self).__init__(None, -1)
|
||||
if len(self.actions) > len(self.files):
|
||||
wx.MessageDialog(None, _(u"It seems as though the currently used sound pack needs an update. %i fails are still be required to use this function. Make sure to obtain the needed lacking sounds or to contact with the sound pack developer.") % (len(self.actions) - len(self.files)), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
self.Destroy()
|
||||
self.SetTitle(_(u"Sounds tutorial"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
label = wx.StaticText(panel, -1, _(u"Press enter to listen to the sound for the selected event"))
|
||||
self.items = wx.ListBox(panel, 1, choices=self.actions, style=wx.LB_SINGLE)
|
||||
self.items.SetSelection(0)
|
||||
listBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
listBox.Add(label)
|
||||
listBox.Add(self.items)
|
||||
play = wx.Button(panel, 1, (u"Play"))
|
||||
play.SetDefault()
|
||||
self.Bind(wx.EVT_BUTTON, self.onPlay, play)
|
||||
close = wx.Button(panel, wx.ID_CANCEL)
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(play)
|
||||
btnBox.Add(close)
|
||||
sizer.Add(listBox)
|
||||
sizer.Add(btnBox)
|
||||
panel.SetSizer(sizer)
|
||||
|
||||
def onPlay(self, ev):
|
||||
sound.player.play(self.files[self.items.GetSelection()])
|
0
src/extra/SpellChecker/__init__.py
Normal file
0
src/extra/SpellChecker/__init__.py
Normal file
105
src/extra/SpellChecker/gui.py
Normal file
105
src/extra/SpellChecker/gui.py
Normal file
@@ -0,0 +1,105 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
import wx
|
||||
import output
|
||||
import config
|
||||
import languageHandler
|
||||
from enchant.checker import SpellChecker
|
||||
from enchant.errors import DictNotFoundError
|
||||
|
||||
class spellCheckerDialog(wx.Dialog):
|
||||
def __init__(self, text, dictionary):
|
||||
super(spellCheckerDialog, self).__init__(None, 1)
|
||||
try:
|
||||
if config.main["general"]["language"] == "system": self.checker = SpellChecker()
|
||||
else: self.checker = SpellChecker(languageHandler.getLanguage())
|
||||
self.checker.set_text(text)
|
||||
except DictNotFoundError:
|
||||
wx.MessageDialog(None, _(u"A bug has happened. There are no dictionaries available for the selected language in TW Blue"), _(u"Error"), wx.ICON_ERROR).ShowModal()
|
||||
self.Destroy()
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
word = wx.StaticText(panel, -1, _(u"Mis-spelled word"))
|
||||
self.word = wx.TextCtrl(panel, -1)
|
||||
wordBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
wordBox.Add(word)
|
||||
wordBox.Add(self.word)
|
||||
context = wx.StaticText(panel, -1, _(u"Context"))
|
||||
self.context = wx.TextCtrl(panel, -1)
|
||||
contextBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
contextBox.Add(context)
|
||||
contextBox.Add(self.context)
|
||||
suggest = wx.StaticText(panel, -1, _(u"Suggestions"))
|
||||
self.suggestions = wx.ListBox(panel, -1, choices=[], style=wx.LB_SINGLE)
|
||||
suggestionsBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
suggestionsBox.Add(suggest)
|
||||
suggestionsBox.Add(self.suggestions)
|
||||
ignore = wx.Button(panel, -1, _(u"Ignore"))
|
||||
self.Bind(wx.EVT_BUTTON, self.onIgnore, ignore)
|
||||
ignoreAll = wx.Button(panel, -1, _(u"Ignore all"))
|
||||
self.Bind(wx.EVT_BUTTON, self.onIgnoreAll, ignoreAll)
|
||||
replace = wx.Button(panel, -1, _(u"Replace"))
|
||||
self.Bind(wx.EVT_BUTTON, self.onReplace, replace)
|
||||
replaceAll = wx.Button(panel, -1, _(u"Replace all"))
|
||||
self.Bind(wx.EVT_BUTTON, self.onReplaceAll, replaceAll)
|
||||
close = wx.Button(panel, wx.ID_CANCEL)
|
||||
btnBox = wx.BoxSizer(wx.HORIZONTAL)
|
||||
btnBox.Add(ignore)
|
||||
btnBox.Add(ignoreAll)
|
||||
btnBox.Add(replace)
|
||||
btnBox.Add(replaceAll)
|
||||
btnBox.Add(close)
|
||||
sizer.Add(wordBox)
|
||||
sizer.Add(contextBox)
|
||||
sizer.Add(suggestionsBox)
|
||||
sizer.Add(btnBox)
|
||||
panel.SetSizerAndFit(sizer)
|
||||
self.check()
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
self.checker.next()
|
||||
textToSay = _(u"Mis-spelled word: %s") % (self.checker.word,)
|
||||
context = u"... %s %s %s" % (self.checker.leading_context(10), self.checker.word, self.checker.trailing_context(10))
|
||||
self.SetTitle(textToSay)
|
||||
output.speak(textToSay)
|
||||
self.word.SetValue(self.checker.word)
|
||||
self.context.ChangeValue(context)
|
||||
self.suggestions.Set(self.checker.suggest())
|
||||
self.suggestions.SetFocus()
|
||||
except StopIteration:
|
||||
wx.MessageDialog(self, _(u"The spelling review has finished."), _("Finished"), style=wx.OK).ShowModal()
|
||||
self.EndModal(wx.ID_OK)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def onIgnore(self, ev):
|
||||
self.check()
|
||||
|
||||
def onIgnoreAll(self, ev):
|
||||
self.checker.ignore_always(word=self.checker.word)
|
||||
self.check()
|
||||
|
||||
def onReplace(self, ev):
|
||||
self.checker.replace(self.suggestions.GetStringSelection())
|
||||
self.check()
|
||||
|
||||
def onReplaceAll(self, ev):
|
||||
self.checker.replace_always(self.suggestions.GetStringSelection())
|
||||
self.check()
|
0
src/extra/__init__.py
Normal file
0
src/extra/__init__.py
Normal file
3
src/extra/translator/__init__.py
Normal file
3
src/extra/translator/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from translator import *
|
||||
import gui
|
44
src/extra/translator/gui.py
Normal file
44
src/extra/translator/gui.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
############################################################
|
||||
# Copyright (c) 2013, 2014 Manuel Eduardo Cortéz Vallejo <manuel@manuelcortez.net>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
############################################################
|
||||
import wx
|
||||
import translator
|
||||
|
||||
class translateDialog(wx.Dialog):
|
||||
def __init__(self):
|
||||
wx.Dialog.__init__(self, None, -1, title=_(u"Translate message"))
|
||||
panel = wx.Panel(self)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
staticSource = wx.StaticText(panel, -1, _(u"Source language"))
|
||||
self.source_lang = wx.ComboBox(panel, -1, choices=[x[1] for x in translator.available_languages()], style = wx.CB_READONLY)
|
||||
self.source_lang.SetFocus()
|
||||
staticDest = wx.StaticText(panel, -1, _(u"Target language"))
|
||||
self.source_lang.SetSelection(0)
|
||||
self.dest_lang = wx.ComboBox(panel, -1, choices=[x[1] for x in translator.available_languages()], style = wx.CB_READONLY)
|
||||
listSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
listSizer.Add(staticSource)
|
||||
listSizer.Add(self.source_lang)
|
||||
listSizer.Add(staticDest)
|
||||
listSizer.Add(self.dest_lang)
|
||||
ok = wx.Button(panel, wx.ID_OK)
|
||||
ok.SetDefault()
|
||||
cancel = wx.Button(panel, wx.ID_CANCEL)
|
||||
self.SetEscapeId(wx.ID_CANCEL)
|
||||
|
||||
def onOk(self, ev):
|
||||
self.EndModal(wx.ID_OK)
|
151
src/extra/translator/translator.py
Normal file
151
src/extra/translator/translator.py
Normal file
@@ -0,0 +1,151 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
try:
|
||||
import urllib2 as request
|
||||
from urllib import quote
|
||||
except:
|
||||
from urllib import request
|
||||
from urllib.parse import quote
|
||||
|
||||
class Translator:
|
||||
string_pattern = r"\"(([^\"\\]|\\.)*)\""
|
||||
match_string =re.compile(
|
||||
r"\,?\["
|
||||
+ string_pattern + r"\,"
|
||||
+ string_pattern + r"\,"
|
||||
+ string_pattern + r"\,"
|
||||
+ string_pattern
|
||||
+r"\]")
|
||||
|
||||
def __init__(self):
|
||||
self.from_lang = ""
|
||||
self.to_lang = ""
|
||||
|
||||
def translate(self, source):
|
||||
json5 = self._get_json5_from_google(source)
|
||||
return self._unescape(self._get_translation_from_json5(json5))
|
||||
|
||||
def _get_translation_from_json5(self, content):
|
||||
result = ""
|
||||
pos = 2
|
||||
while True:
|
||||
m = self.match_string.match(content, pos)
|
||||
if not m:
|
||||
break
|
||||
result += m.group(1)
|
||||
pos = m.end()
|
||||
return result
|
||||
|
||||
def _get_json5_from_google(self, source):
|
||||
escaped_source = quote(source, '')
|
||||
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.168 Safari/535.19'}
|
||||
req = request.Request(
|
||||
url="http://translate.google.com/translate_a/t?client=t&ie=UTF-8&oe=UTF-8"
|
||||
+"&sl=%s&tl=%s&text=%s" % (self.from_lang, self.to_lang, escaped_source)
|
||||
, headers = headers)
|
||||
r = request.urlopen(req)
|
||||
return r.read().decode('utf-8')
|
||||
|
||||
def _unescape(self, text):
|
||||
return re.sub(r"\\.?", lambda x:eval('"%s"'%x.group(0)), text)
|
||||
|
||||
languages = {
|
||||
"af": _(u"Afrikaans"),
|
||||
"sq": _(u"Albanian"),
|
||||
"am": _(u"Amharic"),
|
||||
"ar": _(u"Arabic"),
|
||||
"hy": _(u"Armenian"),
|
||||
"az": _(u"Azerbaijani"),
|
||||
"eu": _(u"Basque"),
|
||||
"be": _(u"Belarusian"),
|
||||
"bn": _(u"Bengali"),
|
||||
"bh": _(u"Bihari"),
|
||||
"bg": _(u"Bulgarian"),
|
||||
"my": _(u"Burmese"),
|
||||
"ca": _(u"Catalan"),
|
||||
"chr": _(u"Cherokee"),
|
||||
"zh": _(u"Chinese"),
|
||||
"zh-CN": _(u"Chinese_simplified"),
|
||||
"zh-TW": _(u"Chinese_traditional"),
|
||||
"hr": _(u"Croatian"),
|
||||
"cs": _(u"Czech"),
|
||||
"da": _(u"Danish"),
|
||||
"dv": _(u"Dhivehi"),
|
||||
"nl": _(u"Dutch"),
|
||||
"en": _(u"English"),
|
||||
"eo": _(u"Esperanto"),
|
||||
"et": _(u"Estonian"),
|
||||
"tl": _(u"Filipino"),
|
||||
"fi": _(u"Finnish"),
|
||||
"fr": _(u"French"),
|
||||
"gl": _(u"Galician"),
|
||||
"ka": _(u"Georgian"),
|
||||
"de": _(u"German"),
|
||||
"el": _(u"Greek"),
|
||||
"gn": _(u"Guarani"),
|
||||
"gu": _(u"Gujarati"),
|
||||
"iw": _(u"Hebrew"),
|
||||
"hi": _(u"Hindi"),
|
||||
"hu": _(u"Hungarian"),
|
||||
"is": _(u"Icelandic"),
|
||||
"id": _(u"Indonesian"),
|
||||
"iu": _(u"Inuktitut"),
|
||||
"ga": _(u"Irish"),
|
||||
"it": _(u"Italian"),
|
||||
"ja": _(u"Japanese"),
|
||||
"kn": _(u"Kannada"),
|
||||
"kk": _(u"Kazakh"),
|
||||
"km": _(u"Khmer"),
|
||||
"ko": _(u"Korean"),
|
||||
"ku": _(u"Kurdish"),
|
||||
"ky": _(u"Kyrgyz"),
|
||||
"lo": _(u"Laothian"),
|
||||
"lv": _(u"Latvian"),
|
||||
"lt": _(u"Lithuanian"),
|
||||
"mk": _(u"Macedonian"),
|
||||
"ms": _(u"Malay"),
|
||||
"ml": _(u"Malayalam"),
|
||||
"mt": _(u"Maltese"),
|
||||
"mr": _(u"Marathi"),
|
||||
"mn": _(u"Mongolian"),
|
||||
"ne": _(u"Nepali"),
|
||||
"no": _(u"Norwegian"),
|
||||
"or": _(u"Oriya"),
|
||||
"ps": _(u"Pashto"),
|
||||
"fa": _(u"Persian"),
|
||||
"pl": _(u"Polish"),
|
||||
"pt-PT": _(u"Portuguese"),
|
||||
"pa": _(u"Punjabi"),
|
||||
"ro": _(u"Romanian"),
|
||||
"ru": _(u"Russian"),
|
||||
"sa": _(u"Sanskrit"),
|
||||
"sr": _(u"Serbian"),
|
||||
"sd": _(u"Sindhi"),
|
||||
"si": _(u"Sinhalese"),
|
||||
"sk": _(u"Slovak"),
|
||||
"sl": _(u"Slovenian"),
|
||||
"es": _(u"Spanish"),
|
||||
"sw": _(u"Swahili"),
|
||||
"sv": _(u"Swedish"),
|
||||
"tg": _(u"Tajik"),
|
||||
"ta": _(u"Tamil"),
|
||||
"tl": _(u"Tagalog"),
|
||||
"te": _(u"Telugu"),
|
||||
"th": _(u"Thai"),
|
||||
"bo": _(u"Tibetan"),
|
||||
"tr": _(u"Turkish"),
|
||||
"uk": _(u"Ukrainian"),
|
||||
"ur": _(u"Urdu"),
|
||||
"uz": _(u"Uzbek"),
|
||||
"ug": _(u"Uighur"),
|
||||
"vi": _(u"Vietnamese"),
|
||||
"cy": _(u"Welsh"),
|
||||
"yi": _(u"Yiddish")
|
||||
}
|
||||
|
||||
def available_languages():
|
||||
l = languages.keys()
|
||||
d = languages.values()
|
||||
l.insert(0, '')
|
||||
d.insert(0, _(u"autodetect"))
|
||||
return sorted(zip(l, d))
|
Reference in New Issue
Block a user